Last active
December 9, 2020 05:51
-
-
Save jayco/2fafc15487af19ce847516e95dd73a29 to your computer and use it in GitHub Desktop.
EventBridge
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 ( | |
"bytes" | |
"context" | |
"encoding/json" | |
"fmt" | |
"log" | |
"net/http" | |
"os" | |
"strconv" | |
"github.com/aws/aws-lambda-go/lambda" | |
"golang.org/x/oauth2" | |
) | |
// general config values - api token with full access | |
var ( | |
apiToken = os.Getenv("GRAPHQL_API_TOKEN") | |
defaultAnnotationStyle = "ERROR" | |
graphAPI = "https://graphql.buildkite.com/v1" | |
finished = "finished" | |
) | |
// Buildkite job (unused values omitted) | |
type job struct { | |
ID string `json:"graphql_id"` | |
UUID string `json:"uuid"` | |
State string `json:"state"` | |
ExitStatus int `json:"exit_status"` | |
SoftFailed bool `json:"soft_failed"` | |
RetriedInNewJob *string `json:"retried_in_job_id"` | |
} | |
// Buildkite build (unused values omitted) | |
type build struct { | |
ID string `json:"graphql_id"` | |
Number *int `json:"number"` | |
URL string `json:"url"` | |
} | |
// Buildkite pipeline (unused values omitted) | |
type pipeline struct { | |
Slug string `json:"slug"` | |
} | |
// Buildkite organization (unused values omitted) | |
type organization struct { | |
Slug string `json:"slug"` | |
} | |
// Buildkite devent detail | |
type detail struct { | |
Job job `json:"job"` | |
Build build `json:"build"` | |
Pipeline pipeline `json:"pipeline"` | |
Organization pipeline `json:"organization"` | |
} | |
// Buildkite job finished event | |
type event struct { | |
Detail detail `json:"detail"` | |
} | |
// Nothing fancy, lets just post using http client | |
func jsonPost(c *http.Client, jsonData *map[string]string) { | |
jsonValue, _ := json.Marshal(jsonData) | |
payload := bytes.NewBuffer(jsonValue) | |
if _, err := c.Post(graphAPI, "application/json", payload); err != nil { | |
log.Printf("%v", err) | |
} | |
} | |
// cancel build grapthql query | |
func cancel(buildGQLID string) *map[string]string { | |
cancelTemplate := `mutation { | |
buildCancel(input: {id: "%s"}) { | |
build { | |
canceledAt | |
} | |
} | |
}` | |
return &map[string]string{"query": fmt.Sprintf(cancelTemplate, buildGQLID)} | |
} | |
// annotate build grapthql query | |
func annotate(BuildID string, body string, style string) *map[string]string { | |
annotateTemplate := `mutation { | |
buildAnnotate(input: {buildID: "%s", body: "%s", style: %s}) { | |
annotation { | |
uuid | |
body { text } | |
style | |
context | |
} | |
} | |
}` | |
return &map[string]string{"query": fmt.Sprintf(annotateTemplate, BuildID, body, style)} | |
} | |
// create a message with a link to the failed job | |
func buildMessage(orgSlug string, pipelineSlug string, buildNumber int, jobUUID string) string { | |
return fmt.Sprintf(`Canceled because of hard failure at https://buildkite.com/%s/%s/builds/%d#%s`, orgSlug, pipelineSlug, buildNumber, jobUUID) | |
} | |
// handleRequest with a http client - best effort fast failing | |
func handleRequest(ctx context.Context, e event) (string, error) { | |
src := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: apiToken}) | |
httpClient := oauth2.NewClient(context.Background(), src) | |
// call the API and fail fast if the build is hard failing | |
if (e.Detail.Job.State == finished) && (e.Detail.Job.ExitStatus > 0) && (e.Detail.Job.SoftFailed == false) && (e.Detail.Job.RetriedInNewJob == nil) { | |
log.Println("hardfailed job, attempting to cancel build") | |
jsonPost(httpClient, cancel(e.Detail.Build.ID)) | |
log.Printf("Build #%s canceled, %s", strconv.Itoa(*e.Detail.Build.Number), e.Detail.Build.URL) | |
jsonPost(httpClient, annotate(e.Detail.Build.ID, buildMessage(e.Detail.Organization.Slug, e.Detail.Pipeline.Slug, *e.Detail.Build.Number, e.Detail.Job.UUID), defaultAnnotationStyle)) | |
} | |
return "ok", nil | |
} | |
// small server to listen for webhooks | |
func main() { | |
lambda.Start(handleRequest) | |
} |
Select the EventBus and create a rule
Give it a name
Define an event pattern
I use:
{
"detail-type": [
"Job Finished"
],
"detail": {
"job": {
"state": [
"finished"
],
"soft_failed": [
false
]
}
}
}
Match the associated event bus
Select your lambda function to use as a target, I also enable event logging
And then hit create
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Create AWS EventBridge notification in Buildkite.