From 140e5a0c1e3fbd369a18d2428733d8ae6cefa828 Mon Sep 17 00:00:00 2001 From: Jaskaran Sarkaria Date: Tue, 12 Mar 2024 11:38:15 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20functionality=20to?= =?UTF-8?q?=20message=20user=20when=20build=20fails=20(#548)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 add functionality to message user when build fails * docs(cobra): update auto-generated documentation --------- Co-authored-by: jaskaransarkaria --- Dockerfile | 1 + doc/cloud-platform_environment_apply.md | 1 + go.mod | 10 +++++---- go.sum | 15 +++++++++++++ pkg/commands/environment.go | 11 +++++----- pkg/environment/environmentApply.go | 28 +++++++++++++++++++------ pkg/slack/init.go | 7 +++++++ pkg/slack/notifyUser.go | 26 +++++++++++++++++++++++ pkg/slack/post.go | 21 +++++++++++++++++++ pkg/slack/search.go | 22 +++++++++++++++++++ 10 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 pkg/slack/init.go create mode 100644 pkg/slack/notifyUser.go create mode 100644 pkg/slack/post.go create mode 100644 pkg/slack/search.go diff --git a/Dockerfile b/Dockerfile index 91505e15..492c672b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,6 +54,7 @@ RUN apt install \ openssl \ parallel \ python3 \ + jq \ -y RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-${AWSCLI_VERSION}.zip" -o "awscliv2.zip" diff --git a/doc/cloud-platform_environment_apply.md b/doc/cloud-platform_environment_apply.md index 2aeb3501..daf4b29f 100644 --- a/doc/cloud-platform_environment_apply.md +++ b/doc/cloud-platform_environment_apply.md @@ -41,6 +41,7 @@ $ cloud-platform environment apply -n --all-namespaces Apply all namespaces with -all-namespaces --batch-apply-index int Starting index for Apply to a batch of namespaces --batch-apply-size int Number of namespaces to apply in a batch + --build-url string The concourse apply build url --cluster string cluster context fron kubeconfig file --clusterdir string folder name under namespaces/ inside cloud-platform-environments repo refering to full cluster name --enable-apply-skip Enable skipping apply for a namespace diff --git a/go.mod b/go.mod index 6a426537..3f68e65b 100644 --- a/go.mod +++ b/go.mod @@ -18,10 +18,10 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 github.com/zclconf/go-cty v1.14.1 - golang.org/x/crypto v0.18.0 // indirect + golang.org/x/crypto v0.19.0 // indirect golang.org/x/mod v0.14.0 - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 @@ -83,6 +83,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.4.0 // indirect github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -120,6 +121,7 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 // indirect + github.com/slack-go/slack v0.12.5 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/stretchr/objx v0.5.0 // indirect @@ -130,7 +132,7 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/term v0.16.0 // indirect + golang.org/x/term v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/go.sum b/go.sum index ebf32e85..53012e2a 100644 --- a/go.sum +++ b/go.sum @@ -154,6 +154,8 @@ github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/e github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= +github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= @@ -211,6 +213,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -246,6 +249,8 @@ github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/Q github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -434,6 +439,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/slack-go/slack v0.12.5 h1:ddZ6uz6XVaB+3MTDhoW04gG+Vc/M/X1ctC+wssy2cqs= +github.com/slack-go/slack v0.12.5/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -505,6 +512,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -582,6 +591,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -656,6 +667,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -663,6 +676,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/commands/environment.go b/pkg/commands/environment.go index 999ecae1..b1d0dfc6 100644 --- a/pkg/commands/environment.go +++ b/pkg/commands/environment.go @@ -15,8 +15,10 @@ import ( ) // variables specific to commands package used to store the values of flags of various environment sub commands -var module, moduleVersion string -var optFlags environment.Options +var ( + module, moduleVersion string + optFlags environment.Options +) // skipEnvCheck is a flag to skip the environments repository check. // This is useful for testing. @@ -66,6 +68,7 @@ func addEnvironmentCmd(topLevel *cobra.Command) { environmentApplyCmd.Flags().StringVar(&optFlags.ClusterCtx, "cluster", "", "cluster context fron kubeconfig file") environmentApplyCmd.Flags().StringVar(&optFlags.ClusterDir, "clusterdir", "", "folder name under namespaces/ inside cloud-platform-environments repo refering to full cluster name") environmentApplyCmd.PersistentFlags().BoolVar(&optFlags.RedactedEnv, "redact", true, "Redact the terraform output before printing") + environmentApplyCmd.Flags().StringVar(&optFlags.BuildUrl, "build-url", "", "The concourse apply build url") environmentBumpModuleCmd.Flags().StringVarP(&module, "module", "m", "", "Module to upgrade the version") environmentBumpModuleCmd.Flags().StringVarP(&moduleVersion, "module-version", "v", "", "Semantic version to bump a module to") @@ -92,7 +95,7 @@ func addEnvironmentCmd(topLevel *cobra.Command) { log.Fatal(err) } - // e.g. if this is the Pull rquest to perform the apply: https://github.com/ministryofjustice/cloud-platform-environments/pull/8370, the pr ID is 8370. + // e.g. if this is the Pull request to perform the apply: https://github.com/ministryofjustice/cloud-platform-environments/pull/8370, the pr ID is 8370. environmentPlanCmd.Flags().IntVar(&optFlags.PRNumber, "prNumber", 0, "Pull request ID or number to which you want to perform the plan") environmentPlanCmd.Flags().StringVarP(&optFlags.Namespace, "namespace", "n", "", "Namespace which you want to perform the plan") @@ -102,7 +105,6 @@ func addEnvironmentCmd(topLevel *cobra.Command) { environmentPlanCmd.Flags().StringVar(&optFlags.ClusterCtx, "cluster", "", "cluster context fron kubeconfig file") environmentPlanCmd.Flags().StringVar(&optFlags.ClusterDir, "clusterdir", "", "folder name under namespaces/ inside cloud-platform-environments repo refering to full cluster name") environmentPlanCmd.PersistentFlags().BoolVar(&optFlags.RedactedEnv, "redact", true, "Redact the terraform output before printing") - } var environmentCmd = &cobra.Command{ @@ -242,7 +244,6 @@ var environmentApplyCmd = &cobra.Command{ } return } - }, } diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index 5c5619ec..d4329dfb 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -9,6 +9,7 @@ import ( gogithub "github.com/google/go-github/github" "github.com/kelseyhightower/envconfig" "github.com/ministryofjustice/cloud-platform-cli/pkg/github" + "github.com/ministryofjustice/cloud-platform-cli/pkg/slack" "github.com/ministryofjustice/cloud-platform-cli/pkg/util" "github.com/ministryofjustice/cloud-platform-environments/pkg/authenticate" "github.com/ministryofjustice/cloud-platform-environments/pkg/namespace" @@ -20,6 +21,7 @@ import ( type Options struct { Namespace, KubecfgPath, ClusterCtx, ClusterDir, GithubToken string PRNumber int + BuildUrl string AllNamespaces bool EnableApplySkip, RedactedEnv, SkipProdDestroy bool BatchApplyIndex, BatchApplySize int @@ -33,6 +35,8 @@ type RequiredEnvVars struct { kubernetescluster string `required:"true" envconfig:"TF_VAR_kubernetes_cluster"` githubowner string `required:"true" envconfig:"TF_VAR_github_owner"` githubtoken string `required:"true" envconfig:"TF_VAR_github_token"` + slackBotToken string `required:"false" envconfig:"SLACK_BOT_TOKEN"` + slackWebhookUrl string `required:"false" envconfig:"SLACK_WEBHOOK_URL"` pingdomapitoken string `required:"true" envconfig:"PINGDOM_API_TOKEN"` } @@ -45,6 +49,18 @@ type Apply struct { GithubClient github.GithubIface } +func notifyUserApplyFailed(prNumberInt int, slackToken, webhookUrl, buildUrl string) { + if prNumberInt > 0 && strings.Contains(buildUrl, "http") { + prNumber := fmt.Sprintf("%d", prNumberInt) + + slackErr := slack.Notify(prNumber, slackToken, webhookUrl, buildUrl) + + if slackErr != nil { + fmt.Printf("Warning: Error notifying user of build error %v\n", slackErr) + } + } +} + // NewApply creates a new Apply object and populates its fields with values from options(which are flags), // instantiate Applier object which also checks and sets the Backend config variables to do terraform init, // RequiredEnvVars object which stores the values required for plan/apply of namespace @@ -70,6 +86,7 @@ func (a *Apply) Initialize() { a.RequiredEnvVars.kubernetescluster = reqEnvVars.kubernetescluster a.RequiredEnvVars.githubowner = reqEnvVars.githubowner a.RequiredEnvVars.githubtoken = reqEnvVars.githubtoken + a.RequiredEnvVars.slackBotToken = reqEnvVars.slackBotToken a.RequiredEnvVars.pingdomapitoken = reqEnvVars.pingdomapitoken // Set KUBE_CONFIG_PATH to the path of the kubeconfig file // This is needed for terraform to be able to connect to the cluster when a different kubecfg is passed @@ -84,7 +101,6 @@ func (a *Apply) Initialize() { // else checks for PR number and get the list of changed namespaces in the PR. Then does the `kubectl apply --dry-run=client` and // terraform init and plan of all the namespaces changed in the PR func (a *Apply) Plan() error { - if a.Options.PRNumber == 0 && a.Options.Namespace == "" { return fmt.Errorf("either a PR Id/Number or a namespace is required to perform plan") } @@ -262,7 +278,6 @@ func (a *Apply) ApplyBatch() error { repoPath := "namespaces/" + a.Options.ClusterDir folderChunks, err := util.GetFolderChunks(repoPath, a.Options.BatchApplyIndex, a.Options.BatchApplySize) - if err != nil { return err } @@ -365,7 +380,7 @@ func (a *Apply) applyTerraform() (string, error) { return outputTerraform, nil } -// applyTerraform calls applier -> TerraformInitAndDestroy and prints the output from applier +// destroyTerraform calls applier -> TerraformInitAndDestroy and prints the output from applier func (a *Apply) destroyTerraform() (string, error) { log.Printf("Running Terraform Destroy for namespace: %v", a.Options.Namespace) @@ -467,6 +482,8 @@ func (a *Apply) applyNamespace() error { if util.IsYamlFileExists(repoPath) { outputKubectl, err := applier.applyKubectl() if err != nil { + notifyUserApplyFailed(a.Options.PRNumber, a.RequiredEnvVars.slackBotToken, a.RequiredEnvVars.slackWebhookUrl, a.Options.BuildUrl) + return err } @@ -484,9 +501,9 @@ func (a *Apply) applyNamespace() error { if err == nil && exists { outputTerraform, err := applier.applyTerraform() if err != nil { + notifyUserApplyFailed(a.Options.PRNumber, a.RequiredEnvVars.slackBotToken, a.RequiredEnvVars.slackWebhookUrl, a.Options.BuildUrl) return err } - fmt.Println("\nOutput of terraform:") util.RedactedEnv(os.Stdout, outputTerraform, a.Options.RedactedEnv) } else { @@ -573,7 +590,6 @@ func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]str } } return namespaces, nil - } // nsChangedInPR get the list of changed files for a given PR. checks if the namespaces exists in the given cluster @@ -590,7 +606,7 @@ func nsChangedInPR(files []*gogithub.CommitFile, cluster string, isDeleted bool) // namespaces filepaths are assumed to come in // the format: namespaces/.cloud-platform.service.justice.gov.uk/ s := strings.Split(*file.Filename, "/") - //only get namespaces from the folder that belong to the given cluster and + // only get namespaces from the folder that belong to the given cluster and // ignore changes outside namespace directories if len(s) > 1 && s[1] == cluster { namespaceNames = append(namespaceNames, s[2]) diff --git a/pkg/slack/init.go b/pkg/slack/init.go new file mode 100644 index 00000000..f4b5236f --- /dev/null +++ b/pkg/slack/init.go @@ -0,0 +1,7 @@ +package slack + +import "github.com/slack-go/slack" + +func initSlack(token string) *slack.Client { + return slack.New(token) +} diff --git a/pkg/slack/notifyUser.go b/pkg/slack/notifyUser.go new file mode 100644 index 00000000..cd0c4613 --- /dev/null +++ b/pkg/slack/notifyUser.go @@ -0,0 +1,26 @@ +package slack + +import ( + "fmt" + + "github.com/slack-go/slack" +) + +func Notify(prNumber, token, webhookUrl, buildUrl string) error { + slackClient := initSlack(token) + + defaultSearchParams := slack.NewSearchParameters() + + results, searchErr := search(slackClient, defaultSearchParams, prNumber) + + if searchErr != nil { + fmt.Printf("Failed to find pr in slack %v\n", searchErr) + return nil + } + + // get the user who posted + user := results.Matches[0].User + ts := results.Matches[0].Timestamp + + return post(user, ts, webhookUrl, buildUrl) +} diff --git a/pkg/slack/post.go b/pkg/slack/post.go new file mode 100644 index 00000000..296e235d --- /dev/null +++ b/pkg/slack/post.go @@ -0,0 +1,21 @@ +package slack + +import ( + "fmt" + + "github.com/slack-go/slack" +) + +func post(user, ts, webhookUrl, buildUrl string) error { + // https://pkg.go.dev/github.com/slack-go/slack#PostWebhook + message := fmt.Sprintf("<@%s> <%s|your build failed>, please address immediately or add a to your namespace to prevent our pipelines from being blocked", user, buildUrl) + + webhookMsg := slack.WebhookMessage{ + Channel: "ask-cloud-platform", + Text: message, + ThreadTimestamp: ts, + ReplyBroadcast: true, + } + + return slack.PostWebhook(webhookUrl, &webhookMsg) +} diff --git a/pkg/slack/search.go b/pkg/slack/search.go new file mode 100644 index 00000000..6252b638 --- /dev/null +++ b/pkg/slack/search.go @@ -0,0 +1,22 @@ +package slack + +import ( + "fmt" + + "github.com/slack-go/slack" +) + +func search(client *slack.Client, params slack.SearchParameters, query string) (*slack.SearchMessages, error) { + results, searchErr := client.SearchMessages(query, params) + if searchErr != nil { + fmt.Printf("Error: searching for slack messages: %v\n", searchErr.Error()) + return nil, searchErr + } + + if results == nil { + fmt.Printf("Failed to find a slack message with the PR number %s\n", query) + return nil, nil + } + + return results, nil +}