LoginSignup
4
3

More than 5 years have passed since last update.

待望のCloud FunctionsのGo対応! Cloud Buildの通知をGoで実装!!

Last updated at Posted at 2019-01-25

tl;dr

Cloud Functions Go

つい先日(2019/1/17)、Cloud FunctionsでGoが使えるようになりました:tada:
Cloud Functions: Go 1.11 is now a supported language | Google Cloud Blog
いまのところベータ版ですが、GCPのベータはプロダクションレベルなんでいろいろ使っていきたいですね。

Cloud Buildの通知サンプル

Cloud Buildの実行結果を、Cloud Functionsを使ってSlackに通知するサンプルがオフィシャルドキュメントにあります。
Configuring notifications for third-party services  |  Cloud Build Documentation  |  Google Cloud
Node.jsで書かれたものですが、こちらを今回Goで書き直してみました。

Go版のCloud Functions

ソースは以下です。1

function.go
package function

import (
    "context"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "os"

    "cloud.google.com/go/functions/metadata"
    slack "github.com/ashwanthkumar/slack-go-webhook"
    "github.com/pkg/errors"
    "google.golang.org/api/cloudbuild/v1"
)

const SlackWebhookURL = "[SLACK_WEBHOOK]"

var (
    projectID string
    resource  string
    // Skip if the current status is not in the status list.
    // Add additional statues to list if you'd like:
    // QUEUED, WORKING, SUCCESS, FAILURE,
    // INTERNAL_ERROR, TIMEOUT, CANCELLED
    status = map[string]bool{
        "SUCCESS":        true,
        "FAILURE":        true,
        "INTERNAL_ERROR": true,
        "TIMEOUT":        true,
    }
)

func init() {
    projectID = os.Getenv("GCP_PROJECT")
    resource = fmt.Sprintf("projects/%s/topics/cloud-builds", projectID)
}

type PubSubMessage struct {
    Data string `json:"data"`
}

// Subscribe is the main function called by Cloud Functions.
func Subscribe(ctx context.Context, m PubSubMessage) error {
    meta, err := metadata.FromContext(ctx)
    if err != nil {
        return errors.Wrap(err, "Failed to get metadata")
    }
    if meta.Resource.Name != resource {
        fmt.Printf("%s is not wathing resource\n", meta.Resource.Name)
        return nil
    }

    build, err := eventToBuild(m.Data)
    if err != nil {
        return errors.Wrap(err, "Failed to decode event data")
    }

    if _, ok := status[build.Status]; !ok {
        fmt.Printf("%s status is skipped\n", build.Status)
        return nil
    }

    // Send message to Slack.
    message := createSlackMessage(build)
    errs := slack.Send(SlackWebhookURL, "", message)
    if len(errs) > 0 {
        return errors.Errorf("Failed to send a message to Slack: %s", errs)
    }

    return nil
}

// eventToBuild transforms pubsub event message to a Build struct.
func eventToBuild(data string) (*cloudbuild.Build, error) {
    d, err := base64.StdEncoding.DecodeString(data)
    if err != nil {
        return nil, errors.Wrap(err, "Failed to decode base64 data")
    }

    build := cloudbuild.Build{}
    err = json.Unmarshal(d, &build)
    if err != nil {
        return nil, errors.Wrap(err, "Failed to decode to JSON")
    }
    return &build, nil
}

// createSlackMessage creates a message from a build object.
func createSlackMessage(build *cloudbuild.Build) slack.Payload {
    title := "Build Logs"
    a := slack.Attachment{
        Title:     &title,
        TitleLink: &build.LogUrl,
    }
    a.AddField(slack.Field{
        Title: "Status",
        Value: build.Status,
    })
    p := slack.Payload{
        Text:        fmt.Sprintf("Build `%s`", build.Id),
        Markdown:    true,
        Attachments: []slack.Attachment{a},
    }
    return p
}

追い易いように元のNode.jsのソースのコメントほぼそのまま載せました。
Node.js版をみるとわかるように、Cloud Buildから渡ってくるPubSub Messageはdataフィールドをもっており、その中身はJSONをbase64エンコードしたものになっています。
ここからデータを取り出すために、Goでもbase64デコード→JSONデコードの処理をeventToBuildで実装しています。
ビルドデータの構造体はGoogleのcloudbuild.v1パッケージのBuild構造体で定義されているものを使っています。

statusをチェックしているところは、NodeだとArray.prototype.indexOfを使っていますが、Goのスライスだとそういうメソッドはなく、プリミティブにforを書くことになるので、mapのキーとして有効なstatusを定義しました。

SlackにInCommingWebhook経由で通知する部分は、ashwanthkumar/slack-go-webhook2を使いました。
これで以下のようなコマンドでデプロイすれば使えるはず…です!3

$ gcloud functions deploy Subscribe --runtime go111 \
  --trigger-topic cloud-builds

  1. ソースはGithubにもあげてあります。 

  2. パッと調べた限りこれ以外ライブラリなさそうだった 

  3. 実際にこのソースで試してはないですが、いまやっているプロジェクトでは似たようなコードで動いています。 

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3