W zeszłym tygodniu zacząłem opisywać serverless w google a dokładnie cloud functions i co trzeba zrobić by wystartować z projektem/funkcją. Dziś zaś skoncentruje się nad tym jak taką funkcję możemy testować i debugować lokalnie.

Prawie jak Azure (prawie robi różnicę), google udostępnia emulator lokalny funkcji. Pisząc google udostępnia już pewnie naginam prawdę, bo google twierdzi, że to nie jest oficjalny produkt googla, ale jest copyright by Google Inc……. Nigdy takich spraw nie pojmę. Do tego, Azure może działać on premise, zaś google nie. Więc stąd to jest emulator, gdzie w Azure może to być po prostu implementacja serwera funkcji.

Instalacja emulatora

Ale dobra. Co potrzebujemy do tego by się tym wszystkim pobawić? Jedynie NPM albo Yarn (przydałoby się by to w końcu opisać). Dodatkowo albo VS Code albo Google Chrome.

Jeżeli mamy npm to możemy odpalić komendę:

npm install -g @google-cloud/functions-emulator

Jeżeli zaś yarn to:

yarn global add @google-cloud/functions-emulator

Choć u mnie yarn nie zadziałał, dostałem wyjątkiem prosto w oczy:

> yarn global add @google-cloud/functions-emulator
yarn global v0.19.1
[1/4] Resolving packages...
warning @google-cloud/functions-emulator > @google-cloud/storage > gcs-resumable-upload > google-auto-auth > gcp-metadata > retry-request > request > [email protected]: Use uuid module instead
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
⠁
⠁
⠁
⠁
error C:\Users\Gutek\AppData\Local\Yarn\config\global\node_modules\grpc: Command failed.
Exit code: 1
Command: C:\WINDOWS\system32\cmd.exe
Arguments: /d /s /c ./node_modules/.bin/node-pre-gyp install --fallback-to-build --library=static_library
Directory: C:\Users\Gutek\AppData\Local\Yarn\config\global\node_modules\grpc
Output:
'.' is not recognized as an internal or external command, operable program or batch file.

info Visit https://yarnpkg.com/en/docs/cli/global for documentation about this command.

Może to wina windows, nie wiem.

Operacje/konfiguracja

Po instalacji powinniśmy mieć dostępne polecenie functions – polecenie to będzie nam pomagało w deployowaniu jak i debugowaniu naszej testowej funkcji. Możemy się dużo o nim dowiedzieć pisząc:

functions --help

Ogólnie, zasada:

functions COMMAND --help

tutaj działa i to bardzo dobrze.

Ale z jednych takich prostych rzeczy to możemy dowiedzieć się jaka jest konfiguracja naszego systemu za pomocą polecenia:

functions config list

Możemy to porównać z domyślnymi ustawieniami:

functions config default

Jak i możemy za pomocą polecenia functions config edytować parametry. Tutaj functions config --help pomoże.

Start/Stop/Deploy/Execute

By skorzystać z emulatora musimy na początku wystartować nasz system. Możemy to zrobić za pomocą komendy:

$ functions start
Starting Google Cloud Functions Emulator...
Google Cloud Functions Emulator STARTED
┌────────┬────────────┬─────────┬───────────────────────────────────────────────────┐
│ Status │ Name       │ Trigger │ Resource                                          │
├────────┼────────────┼─────────┼───────────────────────────────────────────────────┤
│ READY  │ helloWorld │ HTTP    │ http://localhost:8010/gfun/us-central1/helloWorld │
└────────┴────────────┴─────────┴───────────────────────────────────────────────────┘

Wylistowanie dostępnych funkcji pojawia się wtedy, kiedy nasz emulator ma już jakąś funkcję wgraną.

Przeciwieństwem komendy jest:

$ functions stop
Stopping Google Cloud Functions Emulator...
Google Cloud Functions Emulator STOPPED

Piszę o tym, bo warto pamiętać, że jak coś wystartowaliśmy to warto to ubić. Ogólnie my nie wiemy, że coś działa – gdyż po wystartowaniu, dostajemy kontrolę nad terminalem/konsolą.

Deployowanie i uruchamianie

Dobra, by dalej coś zrobić potrzebujemy banalnego kodu. W jakimś testowym/tymczasowym folderze stwórzmy sobie plik index.js (nie wiem, nie sprawdzałem innych nazw, możliwe, że to nie ma znaczenia) i dodajemy do niego:

exports.helloWorld = (reg, res) => {
  if (req.body.message === undefined) {
    res.status(400).send('No message defined!');
  } else {
    res.status(200).send('Success: ' + req.body.message);
  }
};

Zapiszmy plik i otwórzmy linię poleceń w danym katalogu, w którym jest nasza funkcja. Taką funkcję możemy zdeployować do naszego emulator, w tym celu wykonujemy polecenie deploy wraz z nazwą funkcji exportowaniej jak i typem triggera:

$ functions deploy helloWorld --trigger-http
Copying file://C:\Users\Gutek\AppData\Local\Temp\us-central1-helloWorld-2560qRS5aZBOKymF.zip...
Waiting for operation to finish...done.
Deploying function.......done.
Function helloWorld deployed.

┌────────────┬──────────────────────────────────────────────────────────────────────────────────────┐
│ Property   │ Value                                                                                │
├────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Name       │ helloWorld                                                                           │
├────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Trigger    │ HTTP                                                                                 │
├────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Resource   │ http://localhost:8010/gfun/us-central1/helloWorld                                    │
├────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Timeout    │ 60 seconds                                                                           │
├────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Local path │ D:\test\gfun                                                                         │
├────────────┼──────────────────────────────────────────────────────────────────────────────────────┤
│ Archive    │ file://C:\Users\Gutek\AppData\Local\Temp\us-central1-helloWorld-2560qRS5aZBOKymF.zip │
└────────────┴──────────────────────────────────────────────────────────────────────────────────────┘

Jak widać nasza funkcja została z deployowana i teraz nawet mamy do niej URL dostępowe:

http://localhost:8010/gfun/us-central1/helloWorld

Jednak adresu nie trzeba znać jeżeli korzystamy z CLI:

functions call helloWorld

Możemy też przejrzeć logi wykonania funkcji za pomocą polecenia:

functions logs read

Ale do nich jeszcze dzisiaj wrócimy.

Debuggowanie z poziomu VS Code

Mając funkcję, która jest z deployowana, możemy otworzyć VS Code w katalogu gdzie nasz plik się z apką znajduje. Majac VS Code otwarte na katalogu możemy wystartować z deubogwaniem – F5. To nam powinno stworzyć plik launch.json zawierający taski do uruchomienia debuggera. Podmieńmy zawartość launch.json na:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "gcloud Gutek Test",
      "type": "node",
      "request": "attach",
      "port": 5858
    }
  ]
}

Możemy też na:

{
   "version": "0.2.0",
   "configurations": [
     {
       "name": "Inspect Function",
       "type": "node2",
       "request": "attach",
       "port": 9229
     }
   ]
 }

Albo ogólnie możemy mieć dwa takie taski. Różnica polega na tym, że ten drugi korzysta z inspektora V8 który umożliwia podłączenie się chrome DevTools do instancji node.js. Także dwa taski różnią się tym co musimy odpalić z linii poleceń by się nimi pobawić. Opcję Inspect Function opisuje w Chrome DevTools (dosłownie to samo trzeba wpisać), tutaj zaś zajmijmy się gcloud Gutek Test.

Mając plik launch.json zaktualizowany, możemy zacząć debuggować naszą funkcję.

W tym celu należy odpalić dosłownie debugowanie za pomocą polecenia:

$ functions debug helloWorld

Debugger for helloWorld listening on port 5858.

Mając informację o tym, że debuggor wystartował, możemy ponownie kliknąć F5 i zacząć debuggowanie. Oczywiście by nasza metoda się wykonała trzeba albo uderzyć do endpointu HTTP albo poprzez functions call. Byśmy mogli coś podejrzeć potrzebujemy breakpoint (F9) :)

Cloud Function VS Code debugger
Cloud Function VS Code debugger

Jedyny problem z debuggowaniem na jaki ja natrafiłem to problem z timeout. W ustawieniach mamy, że funkcja ma działać max przez 60 sekund. A więc za dużo czasu na debugging to nie mamy. Warto więc zmienić tę opcję za pomocą functions config.

Jak widać działa to dość sprawnie. Pozostawię was z VS Code byście się trochę pobawili. Ale jak skończycie chodźcie z powrotem, bo dalej piszemy o debuggowaniu.

Debuggowanie z poziomu Google Chrome

To wiemy jak debuggować z poziomy VS Code, a jak to zrobić z poziomu chrome? Najpierw zresetujmy naszą funkcję (na wszelki wypadek):

functions reset helloWorld

To zakończy sesję debguggowania i na nowo “wystartuje” funkcję. Jeżeli chcemy przy tym restarcie zachować debugging to możemy dodać parametr --keep, jednak dla tego przypadku tego nie róbmy, przejdźmy przez pełny proces debuggowania w chrome.

Jeżeli chcemy skorzystać z najnowszej wersji node.js, to możemy podłączyć się pod inspektora (to samo polecenie trzeba użyć jak chcielibyśmy z drugiego tasku w VS Code skorzystać):

$ functions inspect helloWorld

Debugger for helloWorld listening on port 9229.

Teraz możemy dobrać się do debuggowania naszego kodu tylko nie znamy url z którego mamy skorzystać. Ta część niestety nie jest fajnie rozwiązana. By dowiedzieć się jaki URL mamy użyć musimy odczytać logi:

$ functions logs read --limit=5
Warning: This is an experimental feature and could change at any time.
To start debugging, open the following URL in Chrome:
    chrome-devtools://devtools/remote/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/1a893692-053b-4210-93d3-4199fb497c56

2017-07-12T15:10:13.709Z - info: Debugger (via --inspect) for projects/gfun/locations/us-central1/functions/helloWorld listening on port 9229.

Zastosowałem –limit=X do ograniczenia liczby zwracanych ostatnich linijek. 5 wystarczy. Z logu wynika że nasz link to:

chrome-devtools://devtools/remote/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/1a893692-053b-4210-93d3-4199fb497c56

Odpalamy Chrome, wklejamy url. Teraz trzeba cierpliwości by znaleźć nasz plik index (CTRL+P albo CMD+P). Jak go znajdziemy to ustawmy breakpoint i następnie wykonajmy zapytanie do naszej funkcji (url dostaliśmy przy deploy, jest taki sam jak przy VS Code). I boom:

Cloud Function Chrome DevTools debugger
Cloud Function Chrome DevTools debugger

Podsumowanie

To tyle na dzisiaj, tylko tyle albo aż tyle. Więcej za bardzo się nie da zrobić z emulatorem, ale i tak da się dość dużo. Zarówno łatwo jest go wystartować jak i potem wgrać funkcję i na końcu ją z debuggować. Narzędzie o dziwo bardzo przyjazne czego się po google jakoś nie spodziewałem – nie wiem czemu ;)

Tak czy siak, warto się nim pobawić, pełną dokumentację jak i tutoriale można znaleźć na github. Kończąc życzę miłej zabawy :) do jutra!