Created
December 8, 2021 08:30
-
-
Save lsjostro/295eb3436cf09dda1791acd9fa1d0c0a to your computer and use it in GitHub Desktop.
Cloud function listening on a pubsub cloud-build pubsub topic for GCB run events and notify on slack
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"encoding/base64" | |
"encoding/json" | |
"fmt" | |
"net/http" | |
"strings" | |
"github.com/apsystole/log" | |
"github.com/sethvargo/go-envconfig" | |
"github.com/slack-go/slack" | |
"google.golang.org/api/cloudbuild/v1" | |
"google.golang.org/api/pubsub/v1" | |
) | |
var config struct { | |
Port string `env:"PORT,default=8080"` | |
SlackChannelID string `env:"SLACK_CHANNEL_ID,required"` | |
SlackToken string `env:"SLACK_TOKEN,required"` | |
} | |
type pushRequest struct { | |
Message pubsub.PubsubMessage `json:"message"` | |
Subscription string `json:"subscription"` | |
} | |
func receiveMessage(w http.ResponseWriter, r *http.Request) { | |
var pr pushRequest | |
err := json.NewDecoder(r.Body).Decode(&pr) | |
if err != nil { | |
http.Error(w, fmt.Sprintf("Could not decode body: %v", err), http.StatusBadRequest) | |
return | |
} | |
data := base64.NewDecoder(base64.StdEncoding, strings.NewReader(pr.Message.Data)) | |
var b cloudbuild.Build | |
err = json.NewDecoder(data).Decode(&b) | |
if err != nil { | |
log.Error(err) | |
http.Error(w, err.Error(), http.StatusBadRequest) | |
} | |
if notifyBuild(&b) { | |
err = slackPostMessage(&b) | |
if err != nil { | |
log.Error(err) | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
} | |
} | |
} | |
func notifyBuild(b *cloudbuild.Build) bool { | |
switch b.Status { | |
case | |
"EXPIRED", | |
"FAILURE", | |
"INTERNAL_ERROR", | |
"SUCCESS", | |
"TIMEOUT": | |
return true | |
} | |
return false | |
} | |
func slackPostMessage(b *cloudbuild.Build) error { | |
api := slack.New(config.SlackToken) | |
trigger := b.Substitutions["TRIGGER_NAME"] | |
commit := b.Substitutions["SHORT_SHA"] | |
repo := b.Substitutions["REPO_NAME"] | |
branch := b.Substitutions["BRANCH_NAME"] | |
srcText := ":github: Source" | |
var srcLink string | |
// GitHub Repo | |
if repo != "" { | |
srcLink = fmt.Sprintf("https://github.com/Tocaboca/%s/commit/%s", repo, commit) | |
repo = fmt.Sprintf("<%s|%s:%s@%s>", srcLink, repo, branch, commit) | |
repo = " for " + repo | |
} | |
var message string | |
if b.Status == "SUCCESS" { | |
message = fmt.Sprintf(":large_green_circle: *%s* successful%s :rocket:", trigger, repo) | |
} else { | |
message = fmt.Sprintf( | |
":octagonal_sign: *%s* failed with status `%s` %s", | |
trigger, | |
b.Status, | |
repo, | |
) | |
if b.FailureInfo != nil { | |
message = fmt.Sprintf("%s\n```%s```", message, b.FailureInfo.Detail) | |
} | |
} | |
headerText := slack.NewTextBlockObject("mrkdwn", message, false, false) | |
headerSection := slack.NewSectionBlock(headerText, nil, nil) | |
logsBtn := &slack.ButtonBlockElement{ | |
Type: slack.METButton, | |
Text: slack.NewTextBlockObject("plain_text", ":mag: View Logs", false, false), | |
URL: b.LogUrl, | |
Value: "view_logs", | |
} | |
srcBtn := &slack.ButtonBlockElement{ | |
Type: slack.METButton, | |
Text: slack.NewTextBlockObject("plain_text", srcText, true, false), | |
URL: srcLink, | |
Value: "view_src", | |
} | |
actions := []slack.BlockElement{ | |
logsBtn, | |
srcBtn, | |
} | |
pr := b.Substitutions["_PR_NUMBER"] | |
if pr != "" { | |
prBtn := &slack.ButtonBlockElement{ | |
Type: slack.METButton, | |
Text: slack.NewTextBlockObject("plain_text", ":github: Pull Request", true, false), | |
URL: fmt.Sprintf("https://github.com/Tocaboca/%s/pull/%s", b.Substitutions["REPO_NAME"], pr), | |
Value: "view_pr", | |
} | |
actions = append(actions, prBtn) | |
} | |
actionBlock := slack.NewActionBlock("", actions...) | |
_, _, err := api.PostMessage(config.SlackChannelID, | |
slack.MsgOptionBlocks( | |
headerSection, | |
actionBlock, | |
slack.NewDividerBlock(), | |
), | |
) | |
return err | |
} | |
func main() { | |
ctx := context.Background() | |
if err := envconfig.Process(ctx, &config); err != nil { | |
log.Fatal(err) | |
} | |
http.HandleFunc("/", receiveMessage) | |
log.Printf("Listening on port %s", config.Port) | |
if err := http.ListenAndServe(":"+config.Port, nil); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment