Skip to content

Commit

Permalink
daemon: scaffold Slack notification delivery (#585)
Browse files Browse the repository at this point in the history
Co-Authored-By: terryz21 <38884362+terryz21@users.noreply.github.com>
  • Loading branch information
terryz21 authored and bobheadxi committed Oct 3, 2019
1 parent b64a81e commit 13389b4
Show file tree
Hide file tree
Showing 10 changed files with 284 additions and 6 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ mocks:
./daemon/inertiad/project/deployment.go Deployer
counterfeiter -o ./daemon/inertiad/build/mocks/builder.go \
./daemon/inertiad/build/builder.go ContainerBuilder
counterfeiter -o ./daemon/inertiad/notify/mocks/notify.go \
./daemon/inertiad/notify/notifier.go Notifier

## scripts: recompile script assets
.PHONY: scripts
Expand Down
4 changes: 2 additions & 2 deletions client/internal/compiled.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

112 changes: 112 additions & 0 deletions daemon/inertiad/notify/mocks/notify.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 54 additions & 0 deletions daemon/inertiad/notify/notifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package notify

import (
"go.uber.org/multierr"
)

// Notifiers is a collection of notification targets
type Notifiers []Notifier

// Notify delivers a notification to all targets
func (n Notifiers) Notify(msg string, opts Options) error {
if len(n) == 0 {
return nil
}

var errs error
for _, notif := range n {
errs = multierr.Append(errs, notif.Notify(msg, opts))
}
return errs
}

// Notifier manages notifications
type Notifier interface {
Notify(string, Options) error
}

// Options is used to configure formatting of notifications
type Options struct {
Color Color
}

// Color is used to represent message color for different states (i.e success, fail)
type Color string

const (
// Green for success messages
Green Color = "good"
// Yellow for warning messages
Yellow Color = "warning"
// Red for error messages
Red Color = "danger"
)

// MessageArray builds the json message to be posted to webhook
type MessageArray struct {
Attachments []Message `json:"attachments"`
}

// Message builds the attachments content of Message
type Message struct {
Text string `json:"text"`
Color string `json:"color"`
}
62 changes: 62 additions & 0 deletions daemon/inertiad/notify/slack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package notify

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
)

// SlackNotifier represents slack notifications
type SlackNotifier struct {
hookURL string
}

// NewSlackNotifier creates a notifier with web hook url to slack channel. Passing
// it an empty url makes it a no-op notifier.
func NewSlackNotifier(webhookURL string) Notifier {
return &SlackNotifier{
hookURL: webhookURL,
}
}

// Notify sends the notification
func (n *SlackNotifier) Notify(text string, options Options) error {
if n.hookURL == "" {
return nil
}

b, err := json.Marshal(MessageArray{
Attachments: []Message{
{
Text: fmt.Sprintf("*%s*", text),
Color: colorToString(options.Color),
},
},
})
if err != nil {
return fmt.Errorf("failed to encode request: %w", err)
}

resp, err := http.Post(n.hookURL, "application/json", bytes.NewBuffer(b))
if err != nil {
return err
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
}
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return errors.New("http request rejected by Slack: " + string(body))
}

return nil
}

func colorToString(color Color) string {
return string(color)
}
22 changes: 20 additions & 2 deletions daemon/inertiad/project/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
docker "github.com/docker/docker/client"
gogit "gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"

"github.com/ubclaunchpad/inertia/api"
"github.com/ubclaunchpad/inertia/common"
"github.com/ubclaunchpad/inertia/daemon/inertiad/build"
"github.com/ubclaunchpad/inertia/daemon/inertiad/containers"
"github.com/ubclaunchpad/inertia/daemon/inertiad/crypto"
"github.com/ubclaunchpad/inertia/daemon/inertiad/git"
gogit "gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
"github.com/ubclaunchpad/inertia/daemon/inertiad/notify"
)

// Deployer manages the deployed user project
Expand Down Expand Up @@ -60,6 +62,8 @@ type Deployment struct {
mux sync.Mutex

dataManager *DeploymentDataManager

notifiers notify.Notifiers
}

// DeploymentConfig is used to configure Deployment
Expand Down Expand Up @@ -148,6 +152,8 @@ func (d *Deployment) SetConfig(cfg DeploymentConfig) {
if cfg.BuildFilePath != "" {
d.buildFilePath = cfg.BuildFilePath
}

// TODO: register notifiers
}

// DeployOptions is used to configure how the deployment handles the deploy
Expand Down Expand Up @@ -196,9 +202,21 @@ func (d *Deployment) Deploy(
// Build project
deploy, err := d.builder.Build(strings.ToLower(d.buildType), *conf, cli, out)
if err != nil {
if notifyErr := d.notifiers.Notify("Build error", notify.Options{
Color: notify.Red,
}); notifyErr != nil {
fmt.Fprintln(out, notifyErr.Error())
}
return func() error { return nil }, err
}

// Send build complete slack notification
if notifyErr := d.notifiers.Notify("Build completed", notify.Options{
Color: notify.Green,
}); notifyErr != nil {
fmt.Fprintln(out, notifyErr.Error())
}

// Deploy
return func() error {
d.active = true
Expand Down
1 change: 1 addition & 0 deletions daemon/inertiad/project/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func TestGetStatusIntegration(t *testing.T) {
builder: fakeBuilder,
}
status, err := deployment.GetStatus(cli)

assert.NoError(t, err)
assert.False(t, status.BuildContainerActive)
assert.Equal(t, "test", status.BuildType)
Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kyokomi/emoji v2.1.1-0.20190303065119-51762253ec2b+incompatible
github.com/mattn/go-isatty v0.0.6 // indirect
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/gox v1.0.1
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
Expand All @@ -42,13 +43,14 @@ require (
github.com/stretchr/testify v1.3.0
github.com/xanzy/ssh-agent v0.2.1 // indirect
go.etcd.io/bbolt v1.3.2
go.uber.org/atomic v1.4.0 // indirect
go.uber.org/multierr v1.2.0
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac
golang.org/x/net v0.0.0-20190311183353-d8887717615a
golang.org/x/net v0.0.0-20190628185345-da137c7871d7
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
google.golang.org/grpc v1.23.1 // indirect
gopkg.in/src-d/go-billy.v4 v4.3.0 // indirect
gopkg.in/src-d/go-git.v4 v4.10.0
gopkg.in/yaml.v2 v2.2.2 // indirect
gotest.tools v2.2.0+incompatible // indirect
)

0 comments on commit 13389b4

Please sign in to comment.