-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding slack integration for posting to channel on applies. Fixes #179 #180
Changes from 12 commits
d5d8e12
5491a30
3be7f8c
3976d46
8c1dc0f
46d9f44
f94cede
e35fd72
4cd775b
ed1d8cd
4e57157
0d39196
b17eec2
66a6f6f
9b680e5
70ad95f
e1d059e
1ebc62b
54e4207
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
// Automatically generated by pegomock. DO NOT EDIT! | ||
// Source: github.com/hootsuite/atlantis/server/events/webhooks (interfaces: SlackClient) | ||
|
||
package mocks | ||
|
||
import ( | ||
webhooks "github.com/hootsuite/atlantis/server/events/webhooks" | ||
pegomock "github.com/petergtz/pegomock" | ||
"reflect" | ||
) | ||
|
||
type MockSlackClient struct { | ||
fail func(message string, callerSkip ...int) | ||
} | ||
|
||
func NewMockSlackClient() *MockSlackClient { | ||
return &MockSlackClient{fail: pegomock.GlobalFailHandler} | ||
} | ||
|
||
func (mock *MockSlackClient) AuthTest() error { | ||
params := []pegomock.Param{} | ||
result := pegomock.GetGenericMockFrom(mock).Invoke("AuthTest", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}) | ||
var ret0 error | ||
if len(result) != 0 { | ||
if result[0] != nil { | ||
ret0 = result[0].(error) | ||
} | ||
} | ||
return ret0 | ||
} | ||
|
||
func (mock *MockSlackClient) ChannelExist(channelName string) (bool, error) { | ||
params := []pegomock.Param{channelName} | ||
result := pegomock.GetGenericMockFrom(mock).Invoke("ChannelExist", params, []reflect.Type{reflect.TypeOf((*bool)(nil)).Elem(), reflect.TypeOf((*error)(nil)).Elem()}) | ||
var ret0 bool | ||
var ret1 error | ||
if len(result) != 0 { | ||
if result[0] != nil { | ||
ret0 = result[0].(bool) | ||
} | ||
if result[1] != nil { | ||
ret1 = result[1].(error) | ||
} | ||
} | ||
return ret0, ret1 | ||
} | ||
|
||
func (mock *MockSlackClient) PostMessage(channel string, applyResult webhooks.ApplyResult) error { | ||
params := []pegomock.Param{channel, applyResult} | ||
result := pegomock.GetGenericMockFrom(mock).Invoke("PostMessage", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}) | ||
var ret0 error | ||
if len(result) != 0 { | ||
if result[0] != nil { | ||
ret0 = result[0].(error) | ||
} | ||
} | ||
return ret0 | ||
} | ||
|
||
func (mock *MockSlackClient) VerifyWasCalledOnce() *VerifierSlackClient { | ||
return &VerifierSlackClient{mock, pegomock.Times(1), nil} | ||
} | ||
|
||
func (mock *MockSlackClient) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierSlackClient { | ||
return &VerifierSlackClient{mock, invocationCountMatcher, nil} | ||
} | ||
|
||
func (mock *MockSlackClient) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierSlackClient { | ||
return &VerifierSlackClient{mock, invocationCountMatcher, inOrderContext} | ||
} | ||
|
||
type VerifierSlackClient struct { | ||
mock *MockSlackClient | ||
invocationCountMatcher pegomock.Matcher | ||
inOrderContext *pegomock.InOrderContext | ||
} | ||
|
||
func (verifier *VerifierSlackClient) AuthTest() *SlackClient_AuthTest_OngoingVerification { | ||
params := []pegomock.Param{} | ||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "AuthTest", params) | ||
return &SlackClient_AuthTest_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} | ||
} | ||
|
||
type SlackClient_AuthTest_OngoingVerification struct { | ||
mock *MockSlackClient | ||
methodInvocations []pegomock.MethodInvocation | ||
} | ||
|
||
func (c *SlackClient_AuthTest_OngoingVerification) GetCapturedArguments() { | ||
} | ||
|
||
func (c *SlackClient_AuthTest_OngoingVerification) GetAllCapturedArguments() { | ||
} | ||
|
||
func (verifier *VerifierSlackClient) ChannelExist(channelName string) *SlackClient_ChannelExist_OngoingVerification { | ||
params := []pegomock.Param{channelName} | ||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "ChannelExist", params) | ||
return &SlackClient_ChannelExist_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} | ||
} | ||
|
||
type SlackClient_ChannelExist_OngoingVerification struct { | ||
mock *MockSlackClient | ||
methodInvocations []pegomock.MethodInvocation | ||
} | ||
|
||
func (c *SlackClient_ChannelExist_OngoingVerification) GetCapturedArguments() string { | ||
channelName := c.GetAllCapturedArguments() | ||
return channelName[len(channelName)-1] | ||
} | ||
|
||
func (c *SlackClient_ChannelExist_OngoingVerification) GetAllCapturedArguments() (_param0 []string) { | ||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) | ||
if len(params) > 0 { | ||
_param0 = make([]string, len(params[0])) | ||
for u, param := range params[0] { | ||
_param0[u] = param.(string) | ||
} | ||
} | ||
return | ||
} | ||
|
||
func (verifier *VerifierSlackClient) PostMessage(channel string, applyResult webhooks.ApplyResult) *SlackClient_PostMessage_OngoingVerification { | ||
params := []pegomock.Param{channel, applyResult} | ||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "PostMessage", params) | ||
return &SlackClient_PostMessage_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} | ||
} | ||
|
||
type SlackClient_PostMessage_OngoingVerification struct { | ||
mock *MockSlackClient | ||
methodInvocations []pegomock.MethodInvocation | ||
} | ||
|
||
func (c *SlackClient_PostMessage_OngoingVerification) GetCapturedArguments() (string, webhooks.ApplyResult) { | ||
channel, applyResult := c.GetAllCapturedArguments() | ||
return channel[len(channel)-1], applyResult[len(applyResult)-1] | ||
} | ||
|
||
func (c *SlackClient_PostMessage_OngoingVerification) GetAllCapturedArguments() (_param0 []string, _param1 []webhooks.ApplyResult) { | ||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) | ||
if len(params) > 0 { | ||
_param0 = make([]string, len(params[0])) | ||
for u, param := range params[0] { | ||
_param0[u] = param.(string) | ||
} | ||
_param1 = make([]webhooks.ApplyResult, len(params[1])) | ||
for u, param := range params[1] { | ||
_param1[u] = param.(webhooks.ApplyResult) | ||
} | ||
} | ||
return | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// Automatically generated by pegomock. DO NOT EDIT! | ||
// Source: github.com/hootsuite/atlantis/server/events/webhooks (interfaces: WebhookSender) | ||
|
||
package mocks | ||
|
||
import ( | ||
webhooks "github.com/hootsuite/atlantis/server/events/webhooks" | ||
pegomock "github.com/petergtz/pegomock" | ||
"reflect" | ||
) | ||
|
||
type MockWebhookSender struct { | ||
fail func(message string, callerSkip ...int) | ||
} | ||
|
||
func NewMockWebhookSender() *MockWebhookSender { | ||
return &MockWebhookSender{fail: pegomock.GlobalFailHandler} | ||
} | ||
|
||
func (mock *MockWebhookSender) Send(_param0 webhooks.ApplyResult) error { | ||
params := []pegomock.Param{_param0} | ||
result := pegomock.GetGenericMockFrom(mock).Invoke("Send", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()}) | ||
var ret0 error | ||
if len(result) != 0 { | ||
if result[0] != nil { | ||
ret0 = result[0].(error) | ||
} | ||
} | ||
return ret0 | ||
} | ||
|
||
func (mock *MockWebhookSender) VerifyWasCalledOnce() *VerifierWebhookSender { | ||
return &VerifierWebhookSender{mock, pegomock.Times(1), nil} | ||
} | ||
|
||
func (mock *MockWebhookSender) VerifyWasCalled(invocationCountMatcher pegomock.Matcher) *VerifierWebhookSender { | ||
return &VerifierWebhookSender{mock, invocationCountMatcher, nil} | ||
} | ||
|
||
func (mock *MockWebhookSender) VerifyWasCalledInOrder(invocationCountMatcher pegomock.Matcher, inOrderContext *pegomock.InOrderContext) *VerifierWebhookSender { | ||
return &VerifierWebhookSender{mock, invocationCountMatcher, inOrderContext} | ||
} | ||
|
||
type VerifierWebhookSender struct { | ||
mock *MockWebhookSender | ||
invocationCountMatcher pegomock.Matcher | ||
inOrderContext *pegomock.InOrderContext | ||
} | ||
|
||
func (verifier *VerifierWebhookSender) Send(_param0 webhooks.ApplyResult) *WebhookSender_Send_OngoingVerification { | ||
params := []pegomock.Param{_param0} | ||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Send", params) | ||
return &WebhookSender_Send_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations} | ||
} | ||
|
||
type WebhookSender_Send_OngoingVerification struct { | ||
mock *MockWebhookSender | ||
methodInvocations []pegomock.MethodInvocation | ||
} | ||
|
||
func (c *WebhookSender_Send_OngoingVerification) GetCapturedArguments() webhooks.ApplyResult { | ||
_param0 := c.GetAllCapturedArguments() | ||
return _param0[len(_param0)-1] | ||
} | ||
|
||
func (c *WebhookSender_Send_OngoingVerification) GetAllCapturedArguments() (_param0 []webhooks.ApplyResult) { | ||
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations) | ||
if len(params) > 0 { | ||
_param0 = make([]webhooks.ApplyResult, len(params[0])) | ||
for u, param := range params[0] { | ||
_param0[u] = param.(webhooks.ApplyResult) | ||
} | ||
} | ||
return | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package webhooks | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
|
||
"github.com/nlopes/slack" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
const ( | ||
successColour = "good" | ||
failureColour = "danger" | ||
) | ||
|
||
//go:generate pegomock generate --use-experimental-model-gen --package mocks -o mocks/mock_slack.go SlackClient | ||
|
||
type SlackClient interface { | ||
AuthTest() error | ||
ChannelExist(channelName string) (bool, error) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. call this |
||
PostMessage(channel string, applyResult ApplyResult) error | ||
} | ||
|
||
type ConcreteSlackClient struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've decided to go with |
||
Slack *slack.Client | ||
} | ||
|
||
type SlackWebhook struct { | ||
Client SlackClient | ||
EnvRegex *regexp.Regexp | ||
Channel string | ||
} | ||
|
||
func NewSlackClient(token string) SlackClient { | ||
return &ConcreteSlackClient{ | ||
Slack: slack.New(token), | ||
} | ||
} | ||
|
||
func NewSlack(r *regexp.Regexp, channel string, client SlackClient) (*SlackWebhook, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. split the slack client and webhook into two files |
||
if err := client.AuthTest(); err != nil { | ||
return nil, errors.Wrap(err, "testing slack authentication") | ||
} | ||
|
||
channelExist, err := client.ChannelExist(channel) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if !channelExist { | ||
return nil, errors.Errorf("slack channel %q doesn't exist", channel) | ||
} | ||
|
||
return &SlackWebhook{ | ||
Client: client, | ||
EnvRegex: r, | ||
Channel: channel, | ||
}, nil | ||
} | ||
|
||
func (c *ConcreteSlackClient) AuthTest() error { | ||
_, err := c.Slack.AuthTest() | ||
return err | ||
} | ||
|
||
func (c *ConcreteSlackClient) ChannelExist(channelName string) (bool, error) { | ||
channels, err := c.Slack.GetChannels(true) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
for _, channel := range channels { | ||
if channel.Name == channelName { | ||
return true, nil | ||
} | ||
} | ||
return false, nil | ||
} | ||
|
||
func (c *ConcreteSlackClient) PostMessage(channel string, applyResult ApplyResult) error { | ||
params := slack.NewPostMessageParameters() | ||
params.Attachments = c.createAttachments(applyResult) | ||
params.AsUser = true | ||
params.EscapeText = false | ||
_, _, err := c.Slack.PostMessage(channel, "", params) | ||
return err | ||
} | ||
|
||
func (c *ConcreteSlackClient) createAttachments(applyResult ApplyResult) []slack.Attachment { | ||
var colour string | ||
if applyResult.Success { | ||
colour = successColour | ||
} else { | ||
colour = failureColour | ||
} | ||
|
||
text := fmt.Sprintf("Applied in <%s|%s>.", applyResult.Pull.URL, applyResult.Repo.FullName) | ||
attachment := slack.Attachment{ | ||
Color: colour, | ||
Text: text, | ||
Fields: []slack.AttachmentField{ | ||
slack.AttachmentField{ | ||
Title: "Environment", | ||
Value: applyResult.Environment, | ||
Short: true, | ||
}, | ||
slack.AttachmentField{ | ||
Title: "User", | ||
Value: applyResult.User.Username, | ||
Short: true, | ||
}, | ||
}, | ||
} | ||
return []slack.Attachment{attachment} | ||
} | ||
|
||
func (s *SlackWebhook) Send(applyResult ApplyResult) error { | ||
if !s.EnvRegex.MatchString(applyResult.Environment) { | ||
return nil | ||
} | ||
return s.Client.PostMessage(s.Channel, applyResult) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use
mocks/mocks_slack_client.go
to match the interface name rather than the name of this file. That way, if we generate a mock for more than one interface in this file, it won't delete the previously generated file (which would have the same name)