From 3185742350c5567d418054f5fbe595b5d4e4af76 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Tue, 10 Mar 2020 15:41:04 +0700 Subject: [PATCH 01/17] init slack --- .gitignore | 1 + bridges/slack-bridge/main.go | 243 +++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 bridges/slack-bridge/main.go diff --git a/.gitignore b/.gitignore index 25c3120..7137f54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea +.vscode bin \ No newline at end of file diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go new file mode 100644 index 0000000..50d7c18 --- /dev/null +++ b/bridges/slack-bridge/main.go @@ -0,0 +1,243 @@ +// +// This is the slack bridge, which should be built like so: +// +// go build . +// +// Once built launch it as follows: +// +// $ ./slack-bridge -slack=slack-webhook-url +// +// When a test fails an slack will sent via SMTP +// +// Eka +// -- +// + +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/go-redis/redis" +) + +type SlackRequestBody struct { + Text string `json:"text,omitempty"` + IconEmoji string `json:"icon_emoji,omitempty"` + Channel string `json:"channel"` + Blocks []SlackBlock `json:"blocks"` +} + +type SlackBlock struct { + Type string `json:"type"` + Text SlackText `json:"text,omitempty"` +} + +type SlackText struct { + Text string `json:"text"` + Type string `json:"type"` +} + +type SlackBrige struct { + slackWebhook string + slackChannel string + + SendTestSuccess bool + SendTestRecovered bool +} + +// +// Given a JSON string decode it and post it via slack if it describes +// a test-failure. +// +func (bridge *SlackBridge) process(msg []byte) { + testResult, err := test.ResultFromJSON(msg) + if err != nil { + panic(err) + } + + // If the test passed then we don't care, unless otherwise defined + shouldSend := true + if testResult.Error == nil { + shouldSend = false + + if bridge.SendTestSuccess { + shouldSend = true + } + + if bridge.SendTestRecovered && testResult.Recovered { + shouldSend = true + } + } + + if !shouldSend { + return + } + + fmt.Printf("Processing result: %+v\n", testResult) + + // Define Title + titleText := SlackText{ + Text: fmt.Sprintf(":warning: *%s %s*", "Error:", testResult.Error), + Type: "mrkdwn", + } + + if testResult.IsDedup { + titleText.Text = fmt.Sprintf(":warning: *%s %s*", "Error (deduplicated):", testResult.Error) + } + + if testResult.Recovered { + titleText.Text = ":white_check_mark: *Error Recovered*" + } + + title := SlackBlock{ + Type: "section", + Text: titleText, + } + + tagText := SlackText{ + Text: "", + Type: "mrkdwn", + } + + // Define Tag + if testResult.Tag != "" { + tagText.Text = fmt.Sprintf("Tag : %s", testResult.Tag) + } else { + tagText.Text = "Tag : None" + } + + tag := SlackBlock{ + Type: "context", + Text: tagText, + } + + divider := SlackBlock{ + Type: "divider", + } + + body := SlackRequestBody{ + Channel: slackChannel, + Blocks: []SlackBlock{ + title, + tag, + divider, + } + } + + if testResult.Details != "" { + detail = SlackBlock{ + Type: "section", + Text: SlackText{ + Text: testResult.Details, + Type: "mrkdwn", + } + } + body.Blocks = append(body.Blocks, detail) + } + + info := SlackBlock{ + Type: "section", + Text: SlackText{ + Type: "mrkdwn", + Text: fmt.Sprintf("Input: %s\nTarget: %s\nType: %s", testResult.Input, testResult.Target, testResult.Type) + }, + } + body.Blocks = append(body.Blocks, info) + + date := SlackBlock{ + Type: "context", + Text: SlackText{ + Type: "mrkdwn", + Text: time.Now().UTC().String() + }, + } + body.Blocks = append(body.Blocks, date) + + slackBody, _ := json.Marshal(body) + req, err := http.NewRequest(http.MethodPost, slackWebhook, bytes.NewBuffer(slackBody)) + if err != nil { + return err + } + + req.Header.Add("Content-Type", "application/json") + + client := &http.Client{Timeout: 10 * time.Second} + resp, err := client.Do(req) + if err != nil { + return err + } + + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + if buf.String() != "ok" { + return errors.New("Non-ok response returned from Slack") + } +} + +// +// Entry Point +// +func main() { + + // + // Parse our flags + // + redisHost := flag.String("redis-host", "127.0.0.1:6379", "Specify the address of the redis queue.") + redisPass := flag.String("redis-pass", "", "Specify the password of the redis queue.") + redisQueueKey := flag.String("redis-queue-key", "overseer.results", "Specify the redis queue key to use.") + + slackWebhook := flag.String("slack-webhook", "https://hooks.slack.com/services/T1234/xxxx/xxx", "Slack Webhook URL") + slackChannel := flag.String("slack-channel", "#my-channel", "Slack Channel Name") + + sendTestSuccess := flag.Bool("send-test-success", false, "Send also test results when successful") + sendTestRecovered := flag.Bool("send-test-recovered", false, "Send also test results when a test recovers from failure (valid only when used together with deduplication rules)") + + flag.Parse() + + // + // Create the redis client + // + r := redis.NewClient(&redis.Options{ + Addr: *redisHost, + Password: *redisPass, + DB: 0, // use default DB + }) + + // + // And run a ping, just to make sure it worked. + // + _, err := r.Ping().Result() + if err != nil { + fmt.Printf("Redis connection failed: %s\n", err.Error()) + os.Exit(1) + } + + bridge := SlackBridge{ + slackWebhook: slackWebhook, + slackChannel: slackChannel, + SendTestRecovered: *sendTestRecovered, + SendTestSuccess: *sendTestSuccess, + } + + for { + + // + // Get test-results + // + msg, _ := r.BLPop(0, *redisQueueKey).Result() + + // + // If they were non-empty, process them. + // + // msg[0] will be "overseer.results" + // + // msg[1] will be the value removed from the list. + // + if len(msg) >= 1 { + bridge.Process([]byte(msg[1])) + } + } +} From 41a7471bd7c656d0182c6f711b35d6b69e305e6f Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Tue, 10 Mar 2020 15:51:37 +0700 Subject: [PATCH 02/17] add docs --- bridges/README.md | 2 + .../overseer-bridge-slack.optional.yaml | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 example-kubernetes/overseer-bridge-slack.optional.yaml diff --git a/bridges/README.md b/bridges/README.md index d40de43..3434adc 100644 --- a/bridges/README.md +++ b/bridges/README.md @@ -19,6 +19,8 @@ The following bridges are distributed with `overseer`: * [webhook-bridge](webhook-bridge/) * Submits tests via webhook (see [Kubernetes usage example](/example-kubernetes/overseer-bridge-webhook-n17.yaml)). +* [slack-bridge](slack-bridge/) + * Submits tests via webhook (see [Kubernetes usage example](/example-kubernetes/overseer-bridge-slack.optional.yaml)). * [email-bridge](email-bridge/) * Submits test-failures via email, using SMTP server (see [Kubernetes usage example](/example-kubernetes/overseer-bridge-email.optional.yaml)). * [queue-bridge](email-bridge/) diff --git a/example-kubernetes/overseer-bridge-slack.optional.yaml b/example-kubernetes/overseer-bridge-slack.optional.yaml new file mode 100644 index 0000000..a193f7c --- /dev/null +++ b/example-kubernetes/overseer-bridge-slack.optional.yaml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: overseer-bridge-slack + namespace: overseer + labels: + app: overseer-bridge-slack +spec: + selector: + matchLabels: + app: overseer-bridge-slack + replicas: 1 + template: + metadata: + labels: + app: overseer-bridge-slack + spec: + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app + operator: In + values: + - overseer-bridge-slack + topologyKey: kubernetes.io/hostname + containers: + - name: overseer-bridge-slack + image: cmaster11/overseer-slack-bridge:1.12.8 + args: + - -redis-host + - redis:6379 + - -slack-webhook + - "https://hooks.slack.com/services/T1234/xxxx/xxx" + - -slack-channel + - "#my-channel" + # If using the webhook queue to clone test results + # - -redis-queue-key + # - "overseer.results.slack" + - -send-test-recovered=true From 9355ee061c3393dce2d0daa37d6cef91a4e077d1 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Tue, 10 Mar 2020 16:12:59 +0700 Subject: [PATCH 03/17] add Dockerfile --- scripts/Dockerfile.slack-bridge | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 scripts/Dockerfile.slack-bridge diff --git a/scripts/Dockerfile.slack-bridge b/scripts/Dockerfile.slack-bridge new file mode 100644 index 0000000..f2444e1 --- /dev/null +++ b/scripts/Dockerfile.slack-bridge @@ -0,0 +1,25 @@ +FROM golang:1.13.1-alpine3.10 as builder + +# Install git +# Git is required for fetching the dependencies. +RUN apk update && apk upgrade && \ + apk add --no-cache gcc g++ git ca-certificates && update-ca-certificates + +WORKDIR /build +ADD . . + +# Build the binary +RUN go build -a -o /go/bin/main ./bridges/slack-bridge + +############################ +# STEP 2 build a small image +############################ +FROM alpine:3.10 + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +# Copy our static executable +COPY --from=builder /go/bin/main /go/bin/main +RUN chmod a+x /go/bin/main + +ENTRYPOINT ["/go/bin/main"] \ No newline at end of file From c4b5e5b023fd8acf9d7c69586efaae9a99888ebe Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Tue, 10 Mar 2020 17:28:34 +0700 Subject: [PATCH 04/17] fix errors --- bridges/slack-bridge/main.go | 42 +++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 50d7c18..0ff6a10 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -16,10 +16,15 @@ package main import ( + "bytes" + "encoding/json" "flag" "fmt" + "net/http" "os" + "time" + "github.com/cmaster11/overseer/test" "github.com/go-redis/redis" ) @@ -32,7 +37,7 @@ type SlackRequestBody struct { type SlackBlock struct { Type string `json:"type"` - Text SlackText `json:"text,omitempty"` + Text SlackText `json:"text,omitempty"` } type SlackText struct { @@ -40,7 +45,7 @@ type SlackText struct { Type string `json:"type"` } -type SlackBrige struct { +type SlackBridge struct { slackWebhook string slackChannel string @@ -119,21 +124,21 @@ func (bridge *SlackBridge) process(msg []byte) { } body := SlackRequestBody{ - Channel: slackChannel, + Channel: bridge.slackChannel, Blocks: []SlackBlock{ title, tag, divider, - } + }, } - if testResult.Details != "" { - detail = SlackBlock{ + if testResult.Details != nil { + detail := SlackBlock{ Type: "section", Text: SlackText{ - Text: testResult.Details, + Text: *testResult.Details, Type: "mrkdwn", - } + }, } body.Blocks = append(body.Blocks, detail) } @@ -142,7 +147,7 @@ func (bridge *SlackBridge) process(msg []byte) { Type: "section", Text: SlackText{ Type: "mrkdwn", - Text: fmt.Sprintf("Input: %s\nTarget: %s\nType: %s", testResult.Input, testResult.Target, testResult.Type) + Text: fmt.Sprintf("Input: %s\nTarget: %s\nType: %s", testResult.Input, testResult.Target, testResult.Type), }, } body.Blocks = append(body.Blocks, info) @@ -151,15 +156,16 @@ func (bridge *SlackBridge) process(msg []byte) { Type: "context", Text: SlackText{ Type: "mrkdwn", - Text: time.Now().UTC().String() + Text: time.Now().UTC().String(), }, } body.Blocks = append(body.Blocks, date) slackBody, _ := json.Marshal(body) - req, err := http.NewRequest(http.MethodPost, slackWebhook, bytes.NewBuffer(slackBody)) + req, err := http.NewRequest(http.MethodPost, bridge.slackWebhook, bytes.NewBuffer(slackBody)) if err != nil { - return err + fmt.Printf("Failed to send req to slack %s\n", err.Error()) + return } req.Header.Add("Content-Type", "application/json") @@ -167,13 +173,15 @@ func (bridge *SlackBridge) process(msg []byte) { client := &http.Client{Timeout: 10 * time.Second} resp, err := client.Do(req) if err != nil { - return err + fmt.Printf("Failed to get response from slack %s\n", err.Error()) + return } buf := new(bytes.Buffer) buf.ReadFrom(resp.Body) if buf.String() != "ok" { - return errors.New("Non-ok response returned from Slack") + fmt.Printf("Non-ok response returned from Slack") + return } } @@ -216,8 +224,8 @@ func main() { } bridge := SlackBridge{ - slackWebhook: slackWebhook, - slackChannel: slackChannel, + slackWebhook: *slackWebhook, + slackChannel: *slackChannel, SendTestRecovered: *sendTestRecovered, SendTestSuccess: *sendTestSuccess, } @@ -237,7 +245,7 @@ func main() { // msg[1] will be the value removed from the list. // if len(msg) >= 1 { - bridge.Process([]byte(msg[1])) + bridge.process([]byte(msg[1])) } } } From 0acc2817c6f6e4f6f407c62944a5aff6e6a70280 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Tue, 10 Mar 2020 18:58:53 +0700 Subject: [PATCH 05/17] fix linter --- bridges/slack-bridge/main.go | 65 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 0ff6a10..4baf720 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -28,23 +28,28 @@ import ( "github.com/go-redis/redis" ) +// SlackRequestBody Slack main struct type SlackRequestBody struct { - Text string `json:"text,omitempty"` - IconEmoji string `json:"icon_emoji,omitempty"` - Channel string `json:"channel"` - Blocks []SlackBlock `json:"blocks"` + Username string `json:"username"` + Text string `json:"text,omitempty"` + IconEmoji string `json:"icon_emoji,omitempty"` + Channel string `json:"channel"` + Blocks []SlackBlock `json:"blocks"` } +// SlackBlock Slack block struct type SlackBlock struct { - Type string `json:"type"` - Text SlackText `json:"text,omitempty"` + Type string `json:"type"` + Text SlackText `json:"text,omitempty"` } +// SlackText Slack text struct type SlackText struct { - Text string `json:"text"` - Type string `json:"type"` + Text string `json:"text"` + Type string `json:"type"` } +// SlackBridge ... type SlackBridge struct { slackWebhook string slackChannel string @@ -85,12 +90,12 @@ func (bridge *SlackBridge) process(msg []byte) { // Define Title titleText := SlackText{ - Text: fmt.Sprintf(":warning: *%s %s*", "Error:", testResult.Error), + Text: fmt.Sprintf(":warning: *%s %s*", "Error:", *testResult.Error), Type: "mrkdwn", } if testResult.IsDedup { - titleText.Text = fmt.Sprintf(":warning: *%s %s*", "Error (deduplicated):", testResult.Error) + titleText.Text = fmt.Sprintf(":warning: *%s %s*", "Error (deduplicated):", *testResult.Error) } if testResult.Recovered { @@ -124,7 +129,9 @@ func (bridge *SlackBridge) process(msg []byte) { } body := SlackRequestBody{ - Channel: bridge.slackChannel, + Username: "Overseer", + IconEmoji: ":godmode:", + Channel: bridge.slackChannel, Blocks: []SlackBlock{ title, tag, @@ -162,27 +169,27 @@ func (bridge *SlackBridge) process(msg []byte) { body.Blocks = append(body.Blocks, date) slackBody, _ := json.Marshal(body) - req, err := http.NewRequest(http.MethodPost, bridge.slackWebhook, bytes.NewBuffer(slackBody)) - if err != nil { + req, err := http.NewRequest(http.MethodPost, bridge.slackWebhook, bytes.NewBuffer(slackBody)) + if err != nil { fmt.Printf("Failed to send req to slack %s\n", err.Error()) - return - } + return + } - req.Header.Add("Content-Type", "application/json") + req.Header.Add("Content-Type", "application/json") - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) - if err != nil { - fmt.Printf("Failed to get response from slack %s\n", err.Error()) - return - } + client := &http.Client{Timeout: 10 * time.Second} + resp, err := client.Do(req) + if err != nil { + fmt.Printf("Failed to get response from slack %s\n", err.Error()) + return + } - buf := new(bytes.Buffer) - buf.ReadFrom(resp.Body) - if buf.String() != "ok" { + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + if buf.String() != "ok" { fmt.Printf("Non-ok response returned from Slack") - return - } + return + } } // @@ -197,7 +204,7 @@ func main() { redisPass := flag.String("redis-pass", "", "Specify the password of the redis queue.") redisQueueKey := flag.String("redis-queue-key", "overseer.results", "Specify the redis queue key to use.") - slackWebhook := flag.String("slack-webhook", "https://hooks.slack.com/services/T1234/xxxx/xxx", "Slack Webhook URL") + slackWebhook := flag.String("slack-webhook", "https://hooks.slack.com/services/T1234/Bxxx/xxx", "Slack Webhook URL") slackChannel := flag.String("slack-channel", "#my-channel", "Slack Channel Name") sendTestSuccess := flag.Bool("send-test-success", false, "Send also test results when successful") @@ -225,7 +232,7 @@ func main() { bridge := SlackBridge{ slackWebhook: *slackWebhook, - slackChannel: *slackChannel, + slackChannel: *slackChannel, SendTestRecovered: *sendTestRecovered, SendTestSuccess: *sendTestSuccess, } From 616398a539b00507d79f1632ab8fb7adc4aff243 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Wed, 11 Mar 2020 15:45:49 +0700 Subject: [PATCH 06/17] add support for redis DB --- bridges/slack-bridge/main.go | 3 ++- example-kubernetes/overseer-bridge-slack.optional.yaml | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 4baf720..7f1db67 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -202,6 +202,7 @@ func main() { // redisHost := flag.String("redis-host", "127.0.0.1:6379", "Specify the address of the redis queue.") redisPass := flag.String("redis-pass", "", "Specify the password of the redis queue.") + redisDB := flag.Int("redis-db", 0, "Specify the database-number for redis.") redisQueueKey := flag.String("redis-queue-key", "overseer.results", "Specify the redis queue key to use.") slackWebhook := flag.String("slack-webhook", "https://hooks.slack.com/services/T1234/Bxxx/xxx", "Slack Webhook URL") @@ -218,7 +219,7 @@ func main() { r := redis.NewClient(&redis.Options{ Addr: *redisHost, Password: *redisPass, - DB: 0, // use default DB + DB: *redisDB, }) // diff --git a/example-kubernetes/overseer-bridge-slack.optional.yaml b/example-kubernetes/overseer-bridge-slack.optional.yaml index a193f7c..d67a699 100644 --- a/example-kubernetes/overseer-bridge-slack.optional.yaml +++ b/example-kubernetes/overseer-bridge-slack.optional.yaml @@ -38,4 +38,7 @@ spec: # If using the webhook queue to clone test results # - -redis-queue-key # - "overseer.results.slack" + # If using redis DB + # - -redis-db + # - 1 - -send-test-recovered=true From a6b5051ca0df16355223476a911a1ae3864204c9 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Wed, 11 Mar 2020 16:02:12 +0700 Subject: [PATCH 07/17] close response --- bridges/slack-bridge/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 7f1db67..79d9b23 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -184,10 +184,12 @@ func (bridge *SlackBridge) process(msg []byte) { return } + defer resp.Body.Close() + buf := new(bytes.Buffer) buf.ReadFrom(resp.Body) if buf.String() != "ok" { - fmt.Printf("Non-ok response returned from Slack") + fmt.Printf("Non-ok response returned from Slack. Code %v, Message %s", resp.StatusCode, resp.Body) return } } From f5862882e0911211ab0db975c586647221e7c371 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Wed, 11 Mar 2020 16:10:38 +0700 Subject: [PATCH 08/17] missing enter --- bridges/slack-bridge/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 79d9b23..1cdc183 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -189,7 +189,7 @@ func (bridge *SlackBridge) process(msg []byte) { buf := new(bytes.Buffer) buf.ReadFrom(resp.Body) if buf.String() != "ok" { - fmt.Printf("Non-ok response returned from Slack. Code %v, Message %s", resp.StatusCode, resp.Body) + fmt.Printf("Non-ok response returned from Slack. Code %v, Message %s\n", resp.StatusCode, resp.Body) return } } From 7c8d07d0569683e708f5cd9aa070f84d10a3b22e Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Wed, 11 Mar 2020 17:02:52 +0700 Subject: [PATCH 09/17] fix error message --- bridges/slack-bridge/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 1cdc183..b17680d 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -189,7 +189,7 @@ func (bridge *SlackBridge) process(msg []byte) { buf := new(bytes.Buffer) buf.ReadFrom(resp.Body) if buf.String() != "ok" { - fmt.Printf("Non-ok response returned from Slack. Code %v, Message %s\n", resp.StatusCode, resp.Body) + fmt.Printf("Non-ok response returned from Slack. Code %v, Message %s\n", resp.StatusCode, buf.String()) return } } From b8682abb9bf43bb4cf534b3c7d257351bf6a6806 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Wed, 11 Mar 2020 17:05:26 +0700 Subject: [PATCH 10/17] add logger for slack req --- bridges/slack-bridge/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index b17680d..5af8bdc 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -169,6 +169,8 @@ func (bridge *SlackBridge) process(msg []byte) { body.Blocks = append(body.Blocks, date) slackBody, _ := json.Marshal(body) + fmt.Printf("%s \n", string(slackBody)) + req, err := http.NewRequest(http.MethodPost, bridge.slackWebhook, bytes.NewBuffer(slackBody)) if err != nil { fmt.Printf("Failed to send req to slack %s\n", err.Error()) From 511a78631f1343a771e26a76abdc96a137361e8a Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Wed, 11 Mar 2020 18:21:12 +0700 Subject: [PATCH 11/17] attmpt to fix bad blocks & mv detail to attachmtn --- bridges/slack-bridge/main.go | 71 ++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 5af8bdc..ae733f5 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -30,23 +30,40 @@ import ( // SlackRequestBody Slack main struct type SlackRequestBody struct { - Username string `json:"username"` - Text string `json:"text,omitempty"` - IconEmoji string `json:"icon_emoji,omitempty"` - Channel string `json:"channel"` - Blocks []SlackBlock `json:"blocks"` + Username string `json:"username"` + Text string `json:"text,omitempty"` + IconEmoji string `json:"icon_emoji,omitempty"` + Channel string `json:"channel"` + Blocks []SlackBlock `json:"blocks"` + Attachments []SlackAttachment `json:"attachments,omitempty"` } // SlackBlock Slack block struct type SlackBlock struct { - Type string `json:"type"` - Text SlackText `json:"text,omitempty"` + Type string `json:"type"` + Text SlackText `json:"text,omitempty"` + Elements []SlackElement `json:"elements,omitempty"` } // SlackText Slack text struct type SlackText struct { - Text string `json:"text"` - Type string `json:"type"` + Text string `json:"text,omitempty"` + Type string `json:"type,omitempty"` +} + +// SlackElement Slack element struct +type SlackElement struct { + Type string `json:"type"` + Emoji bool `json:"emoji"` + Text string `json:"text"` +} + +// SlackAttachment Slack attachment struct +type SlackAttachment struct { + Blocks []SlackBlock `json:"blocks,omitempty"` + Text string `json:"text,omitempty"` + Color string `json:"color"` + Title string `json:"title"` } // SlackBridge ... @@ -107,21 +124,24 @@ func (bridge *SlackBridge) process(msg []byte) { Text: titleText, } - tagText := SlackText{ - Text: "", - Type: "mrkdwn", + tagElement := SlackElement{ + Text: "", + Emoji: true, + Type: "plain_text", } // Define Tag if testResult.Tag != "" { - tagText.Text = fmt.Sprintf("Tag : %s", testResult.Tag) + tagElement.Text = fmt.Sprintf("Tag : %s", testResult.Tag) } else { - tagText.Text = "Tag : None" + tagElement.Text = "Tag : None" } tag := SlackBlock{ Type: "context", - Text: tagText, + Elements: []SlackElement{ + tagElement, + }, } divider := SlackBlock{ @@ -137,6 +157,7 @@ func (bridge *SlackBridge) process(msg []byte) { tag, divider, }, + Attachments: []SlackAttachment{}, } if testResult.Details != nil { @@ -147,7 +168,14 @@ func (bridge *SlackBridge) process(msg []byte) { Type: "mrkdwn", }, } - body.Blocks = append(body.Blocks, detail) + + attachment := SlackAttachment{ + Color: "#a9a9a9", + Title: "Detail", + Blocks: []SlackBlock{detail}, + } + + body.Attachments = append(body.Attachments, attachment) } info := SlackBlock{ @@ -159,11 +187,16 @@ func (bridge *SlackBridge) process(msg []byte) { } body.Blocks = append(body.Blocks, info) + dateElement := SlackElement{ + Type: "plain_text", + Emoji: true, + Text: time.Now().UTC().String(), + } + date := SlackBlock{ Type: "context", - Text: SlackText{ - Type: "mrkdwn", - Text: time.Now().UTC().String(), + Elements: []SlackElement{ + dateElement, }, } body.Blocks = append(body.Blocks, date) From a4426fd13c0107ea86cfe0779148e25fd9306a41 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Wed, 11 Mar 2020 20:08:41 +0700 Subject: [PATCH 12/17] attempt to fix unneeded param --- bridges/slack-bridge/main.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index ae733f5..8410060 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -41,7 +41,7 @@ type SlackRequestBody struct { // SlackBlock Slack block struct type SlackBlock struct { Type string `json:"type"` - Text SlackText `json:"text,omitempty"` + Text *SlackText `json:"text,omitempty"` Elements []SlackElement `json:"elements,omitempty"` } @@ -121,7 +121,7 @@ func (bridge *SlackBridge) process(msg []byte) { title := SlackBlock{ Type: "section", - Text: titleText, + Text: &titleText, } tagElement := SlackElement{ @@ -163,7 +163,7 @@ func (bridge *SlackBridge) process(msg []byte) { if testResult.Details != nil { detail := SlackBlock{ Type: "section", - Text: SlackText{ + Text: &SlackText{ Text: *testResult.Details, Type: "mrkdwn", }, @@ -180,7 +180,7 @@ func (bridge *SlackBridge) process(msg []byte) { info := SlackBlock{ Type: "section", - Text: SlackText{ + Text: &SlackText{ Type: "mrkdwn", Text: fmt.Sprintf("Input: %s\nTarget: %s\nType: %s", testResult.Input, testResult.Target, testResult.Type), }, @@ -243,7 +243,7 @@ func main() { redisQueueKey := flag.String("redis-queue-key", "overseer.results", "Specify the redis queue key to use.") slackWebhook := flag.String("slack-webhook", "https://hooks.slack.com/services/T1234/Bxxx/xxx", "Slack Webhook URL") - slackChannel := flag.String("slack-channel", "#my-channel", "Slack Channel Name") + slackChannel := flag.String("slack-channel", "", "Slack Channel Name") sendTestSuccess := flag.Bool("send-test-success", false, "Send also test results when successful") sendTestRecovered := flag.Bool("send-test-recovered", false, "Send also test results when a test recovers from failure (valid only when used together with deduplication rules)") From 5932932c65909ece851316f016ae1be19bb615d4 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Thu, 12 Mar 2020 10:41:21 +0700 Subject: [PATCH 13/17] change title attachment to block --- bridges/slack-bridge/main.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 8410060..6897419 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -63,7 +63,7 @@ type SlackAttachment struct { Blocks []SlackBlock `json:"blocks,omitempty"` Text string `json:"text,omitempty"` Color string `json:"color"` - Title string `json:"title"` + Title string `json:"title,omitempty"` } // SlackBridge ... @@ -161,6 +161,14 @@ func (bridge *SlackBridge) process(msg []byte) { } if testResult.Details != nil { + title := SlackBlock{ + Type: "section", + Text: &SlackText{ + Text: "*Details*", + Type: "mrkdwn", + }, + } + detail := SlackBlock{ Type: "section", Text: &SlackText{ @@ -170,9 +178,12 @@ func (bridge *SlackBridge) process(msg []byte) { } attachment := SlackAttachment{ - Color: "#a9a9a9", - Title: "Detail", - Blocks: []SlackBlock{detail}, + Color: "#a9a9a9", + Title: "Detail", + Blocks: []SlackBlock{ + title, + detail, + }, } body.Attachments = append(body.Attachments, attachment) From e1f744d89047bfd753153ad50b0ca1e6a2aeee0e Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Thu, 12 Mar 2020 10:48:37 +0700 Subject: [PATCH 14/17] remove title in attachment --- bridges/slack-bridge/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 6897419..ac45345 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -179,7 +179,6 @@ func (bridge *SlackBridge) process(msg []byte) { attachment := SlackAttachment{ Color: "#a9a9a9", - Title: "Detail", Blocks: []SlackBlock{ title, detail, From f78041c0bb623c356dda6c2e88d9d1fc40c4a66f Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Thu, 12 Mar 2020 11:58:25 +0700 Subject: [PATCH 15/17] change emoji --- bridges/slack-bridge/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index ac45345..47d8fef 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -150,7 +150,7 @@ func (bridge *SlackBridge) process(msg []byte) { body := SlackRequestBody{ Username: "Overseer", - IconEmoji: ":godmode:", + IconEmoji: ":eyes:", Channel: bridge.slackChannel, Blocks: []SlackBlock{ title, From 0be0ce6deb8585cfa952272aa50745c91a3bb578 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Tue, 17 Mar 2020 13:30:09 +0700 Subject: [PATCH 16/17] change SMTP mention --- bridges/slack-bridge/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 47d8fef..46d70bb 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -7,7 +7,7 @@ // // $ ./slack-bridge -slack=slack-webhook-url // -// When a test fails an slack will sent via SMTP +// When a test fails an slack will sent via Slack Webhook // // Eka // -- From 0fff35ab163b174438aa5a37abdcb539ce7fec09 Mon Sep 17 00:00:00 2001 From: ekapratama93 Date: Thu, 17 Sep 2020 13:57:15 +0700 Subject: [PATCH 17/17] add message text & update docker image --- bridges/slack-bridge/main.go | 1 + scripts/Dockerfile.slack-bridge | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bridges/slack-bridge/main.go b/bridges/slack-bridge/main.go index 46d70bb..e0f62cc 100644 --- a/bridges/slack-bridge/main.go +++ b/bridges/slack-bridge/main.go @@ -152,6 +152,7 @@ func (bridge *SlackBridge) process(msg []byte) { Username: "Overseer", IconEmoji: ":eyes:", Channel: bridge.slackChannel, + Text: titleText.Text, Blocks: []SlackBlock{ title, tag, diff --git a/scripts/Dockerfile.slack-bridge b/scripts/Dockerfile.slack-bridge index f2444e1..309e05c 100644 --- a/scripts/Dockerfile.slack-bridge +++ b/scripts/Dockerfile.slack-bridge @@ -1,4 +1,4 @@ -FROM golang:1.13.1-alpine3.10 as builder +FROM golang:1.15-alpine3.12 as builder # Install git # Git is required for fetching the dependencies. @@ -14,7 +14,7 @@ RUN go build -a -o /go/bin/main ./bridges/slack-bridge ############################ # STEP 2 build a small image ############################ -FROM alpine:3.10 +FROM alpine:3.12 COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt