Skip to content

Commit

Permalink
Added message_file option for custom msg in file (#47)
Browse files Browse the repository at this point in the history
Added message_file option for custom msg in file
  • Loading branch information
arbourd authored Sep 20, 2019
2 parents 5fbbc13 + 36bf717 commit 5739ae3
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
-e "BUILD_JOB_NAME=test"
-e "BUILD_NAME=$TRAVIS_BUILD_NUMBER"
concourse-slack-alert-resource-travis:$TRAVIS_BUILD_NUMBER
/opt/resource/out
/opt/resource/out $PWD

branches:
only:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Sends a structured message to Slack based on the alert type.
- `alert_type`: *Optional.* The type of alert to send to Slack. See [Alert Types](#alert-types). Defaults to `default`.
- `channel`: *Optional.* Channel where this message is posted. Defaults to the `channel` setting in Source.
- `message`: *Optional.* The status message at the top of the alert. Defaults to name of alert type.
- `message_file`: *Optional.* File containing text which overrides `message`. If the file cannot be read, `message` will be used instead.
- `color`: *Optional.* The color of the notification bar as a hexadecimal. Defaults to the icon color of the alert type.
- `disable`: *Optional.* Disables the alert. Defaults to `false`.

Expand Down
11 changes: 6 additions & 5 deletions concourse/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ type InResponse struct {

// OutParams are the parameters that can be configured for the out operation.
type OutParams struct {
AlertType string `json:"alert_type"`
Message string `json:"message"`
Color string `json:"color"`
Disable bool `json:"disable"`
Channel string `json:"channel"`
AlertType string `json:"alert_type"`
Channel string `json:"channel"`
Color string `json:"color"`
Message string `json:"message"`
MessageFile string `json:"message_file"`
Disable bool `json:"disable"`
}

// OutRequest is in the input for the out operation.
Expand Down
15 changes: 8 additions & 7 deletions out/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import "github.com/arbourd/concourse-slack-alert-resource/concourse"

// An Alert defines the notification that will be sent to Slack.
type Alert struct {
Type string
Channel string
Color string
IconURL string
Message string
Disabled bool
Type string
Channel string
Color string
IconURL string
Message string
MessageFile string
Disabled bool
}

// NewAlert constructs and returns an Alert.
Expand Down Expand Up @@ -81,6 +82,6 @@ func NewAlert(input *concourse.OutRequest) Alert {
if input.Params.Color != "" {
alert.Color = input.Params.Color
}

alert.MessageFile = input.Params.MessageFile
return alert
}
41 changes: 30 additions & 11 deletions out/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,35 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/arbourd/concourse-slack-alert-resource/concourse"
"github.com/arbourd/concourse-slack-alert-resource/slack"
)

func buildMessage(alert Alert, m concourse.BuildMetadata) *slack.Message {
fallback := fmt.Sprintf("%s -- %s", fmt.Sprintf("%s: %s/%s/%s", alert.Message, m.PipelineName, m.JobName, m.BuildName), m.URL)
func buildMessage(alert Alert, m concourse.BuildMetadata, path string) *slack.Message {
message := alert.Message

// Open and read message file if set
if alert.MessageFile != "" {
file := filepath.Join(path, alert.MessageFile)
f, err := ioutil.ReadFile(file)

if err != nil {
fmt.Fprintf(os.Stderr, "error reading message_file: %v\nwill default to message instead\n", err)
} else {
message = strings.TrimSpace(string(f))
}
}

attachment := slack.Attachment{
Fallback: fallback,
AuthorName: alert.Message,
Fallback: fmt.Sprintf("%s -- %s", fmt.Sprintf("%s: %s/%s/%s", message, m.PipelineName, m.JobName, m.BuildName), m.URL),
AuthorName: message,
Color: alert.Color,
Footer: m.URL,
FooterIcon: alert.IconURL,
Expand Down Expand Up @@ -61,7 +77,7 @@ func previousBuildStatus(input *concourse.OutRequest, m concourse.BuildMetadata)
return previous.Status, nil
}

func out(input *concourse.OutRequest) (*concourse.OutResponse, error) {
func out(input *concourse.OutRequest, path string) (*concourse.OutResponse, error) {
if input.Source.URL == "" {
return nil, errors.New("slack webhook url cannot be blank")
}
Expand All @@ -73,16 +89,16 @@ func out(input *concourse.OutRequest) (*concourse.OutResponse, error) {
if send && (alert.Type == "fixed" || alert.Type == "broke") {
status, err := previousBuildStatus(input, metadata)
if err != nil {
return nil, err
return nil, fmt.Errorf("error getting last build status: %v", err)
}
send = (alert.Type == "fixed" && status != "succeeded") || (alert.Type == "broke" && status == "succeeded")
}

if send {
message := buildMessage(alert, metadata)
message := buildMessage(alert, metadata, path)
err := slack.Send(input.Source.URL, message)
if err != nil {
return nil, err
return nil, fmt.Errorf("error sending slack message: %v", err)
}
}

Expand All @@ -98,19 +114,22 @@ func out(input *concourse.OutRequest) (*concourse.OutResponse, error) {
}

func main() {
// The first argument is the path to the build's sources.
path := os.Args[1]

var input *concourse.OutRequest
err := json.NewDecoder(os.Stdin).Decode(&input)
if err != nil {
log.Fatalln(err)
log.Fatalln(fmt.Errorf("error reading stdin: %v", err))
}

o, err := out(input)
o, err := out(input, path)
if err != nil {
log.Fatalln(err)
}

err = json.NewEncoder(os.Stdout).Encode(o)
if err != nil {
log.Fatalln(err)
log.Fatalln(fmt.Errorf("error writing stdout: %v", err))
}
}
58 changes: 56 additions & 2 deletions out/main_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package main

import (
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"reflect"
"testing"

Expand Down Expand Up @@ -202,7 +204,7 @@ func TestOut(t *testing.T) {
os.Setenv(k, v)
}

got, err := out(c.outRequest)
got, err := out(c.outRequest, "")
if err != nil && !c.err {
t.Fatalf("unexpected error from out:\n\t(ERR): %s", err)
} else if err == nil && c.err {
Expand Down Expand Up @@ -261,6 +263,44 @@ func TestBuildMessage(t *testing.T) {
},
Channel: "general"},
},
"message file": {
alert: Alert{
Type: "default",
Message: "Testing",
MessageFile: "message_file",
},
want: &slack.Message{
Attachments: []slack.Attachment{
{
Fallback: "message file: demo/test/1 -- https://ci.example.com/teams/main/pipelines/demo/jobs/test/builds/1",
AuthorName: "message file",
Fields: []slack.Field{
{Title: "Job", Value: "demo/test", Short: true},
{Title: "Build", Value: "1", Short: true},
},
Footer: "https://ci.example.com/teams/main/pipelines/demo/jobs/test/builds/1", FooterIcon: ""},
},
},
},
"message file failure": {
alert: Alert{
Type: "default",
Message: "Testing",
MessageFile: "bad file",
},
want: &slack.Message{
Attachments: []slack.Attachment{
{
Fallback: "Testing: demo/test/1 -- https://ci.example.com/teams/main/pipelines/demo/jobs/test/builds/1",
AuthorName: "Testing",
Fields: []slack.Field{
{Title: "Job", Value: "demo/test", Short: true},
{Title: "Build", Value: "1", Short: true},
},
Footer: "https://ci.example.com/teams/main/pipelines/demo/jobs/test/builds/1", FooterIcon: ""},
},
},
},
}

metadata := concourse.BuildMetadata{
Expand All @@ -274,7 +314,21 @@ func TestBuildMessage(t *testing.T) {

for name, c := range cases {
t.Run(name, func(t *testing.T) {
got := buildMessage(c.alert, metadata)
path := ""
if c.alert.MessageFile != "" {
dir, err := ioutil.TempDir("", "example")
if err != nil {
t.Fatal(err)
}
path = dir

defer os.RemoveAll(dir)
if err := ioutil.WriteFile(filepath.Join(dir, "message_file"), []byte("message file"), 0666); err != nil {
t.Fatal(err)
}
}

got := buildMessage(c.alert, metadata, path)
if !reflect.DeepEqual(got, c.want) {
t.Fatalf("unexpected slack.Message value from buildSlackMessage:\n\t(GOT): %#v\n\t(WNT): %#v", got, c.want)
}
Expand Down

0 comments on commit 5739ae3

Please sign in to comment.