public note

Goで書いたCloud FunctionsをTerraformでデプロイする

春になったし Cloud Workflows の検証でもしようかと思って、その部品として Cloud Functions を作るぞとなったので、最近入門したGoでやってみようと思いました。しかし、検索してもこの組合せのサンプルが全然出てこなかった。

結論

Terraform では、Go で書いた Cloud Functions を PythonRuby と同じ方式でデプロイできます。Go のビルドプロセスは隠蔽されています。

実装

terraform のバージョンは 0.13.4、google provider のバージョンは 3.60.0 (公開時点の最新)です。

Go

これはほぼサンプルのままです。リクエストを受けたら返事をするだけです。

package got

import (
        "encoding/json"
        "fmt"
        "html"
        "net/http"
)

func HelloHTTP(w http.ResponseWriter, r *http.Request) {
        var d struct {
                Name string `json:"name"`
        }
        if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
                fmt.Fprint(w, "Winter is comming!")
                return
        }
        if d.Name == "" {
                fmt.Fprint(w, "Winter is comming!")
                return
        }
        fmt.Fprintf(w, "Hello, %s!", html.EscapeString(d.Name))
}

Terraform

cloudfunctions.invoker の権限をもつアカウントだけが、HTTPリクエストを送信できるようにしています。 Goなので、ビルド関連でPythonRubyと何か違いがあるはずと思ったのですが、そんなことはなく、同じように書けばOKです。この"他の言語と同じように書けばOK"という事実が、検索しても出てこない。初回applyのときはちょっと驚きました。

resource "google_cloudfunctions_function" "got" {
  name                  = "got"
  description           = "Game of Thrones"
  runtime               = "go113"
  source_archive_bucket = google_storage_bucket.zip_bucket.name
  source_archive_object = google_storage_bucket_object.function_got_packages.name
  available_memory_mb   = 128
  timeout               = 30
  entry_point           = "HelloHTTP"
  trigger_http          = true
  service_account_email = google_service_account.sa_functions_got.email
}

data "archive_file" "function_got_archive" {
  type        = "zip"
  source_dir  = "src/go/got"
  output_path = "zip/go/got.zip"
}

resource "google_storage_bucket_object" "function_got_packages" {
  name   = "packages/go/function_got.${data.archive_file.function_got_archive.output_md5}.zip"
  bucket = google_storage_bucket.zip_bucket.name
  source = data.archive_file.function_got_archive.output_path
}

resource "google_cloudfunctions_function_iam_member" "got_member" {
  project        = google_cloudfunctions_function.got.project
  region         = google_cloudfunctions_function.got.region
  cloud_function = google_cloudfunctions_function.got.name
  role           = "roles/cloudfunctions.invoker"
  member         = "serviceAccount:${google_service_account.sa_functions_got.email}"
}

resource "google_project_iam_member" "cloud_storage_admin_got" {
  role   = "roles/storage.objectAdmin"
  member = "serviceAccount:${google_service_account.sa_functions_got.email}"
}

output "function_got_url" {
  value = google_cloudfunctions_function.got.https_trigger_url
}

Cloud Run と同じで、ビルドが隠蔽されているのが心地よいです。どの言語を選んだとしても開発のプロセスが変わらないので、とても素敵だなと思いました。