대망의 Cloud Functions의 Go 대응! Cloud Build의 통지를 Go로 구현!!

tl;dr


  • Cloud Functions Go에서 Cloud Build 결과 알림을 Slack 게시하는 샘플 작성
  • 소스는 이쪽
  • go-cloud-functions-examples/notify-slack at master · iwata/go-cloud-functions-examples


  • Cloud Functions Go



    마지막 날 (2019/1/17) Cloud Functions에서 Go를 사용할 수있게되었습니다
    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-webhook 2을 사용했습니다.
    이제 다음과 같은 명령으로 배포하면 사용할 수 있을 것입니다…입니다! 3
    $ gcloud functions deploy Subscribe --runtime go111 \
      --trigger-topic cloud-builds
    



    소스는 Github에도 올려져 있습니다. 

    확실히 조사한 한 이것 이외 라이브러리 없는 것 같았다 

    실제로이 소스에서 시도하지는 않지만 현재 프로젝트에서 비슷한 코드로 작동합니다. 

    좋은 웹페이지 즐겨찾기