diff --git a/docs/spec/v1beta1/provider.md b/docs/spec/v1beta1/provider.md index 8df7da6aa..e290c4a95 100644 --- a/docs/spec/v1beta1/provider.md +++ b/docs/spec/v1beta1/provider.md @@ -339,6 +339,42 @@ The provider will send the following labels for the event. | namespace | The namespace of the involved object associated with the event | +### Slack App + +It is possible to use Slack Apps bot integration to send messages. To obtain bot token, follow the [Slack's guide](https://api.slack.com/bot-users). + +Differences from Slack [webhook method](#notifications): + +* Possible to use single credentials to post into different channels (integration should be added to each channel) +* All messages would be posted from app username, losing the `helm-controller`, `source-controller` usernames + +In that case, Slack secret should contain URL of [chat.postMessage](https://api.slack.com/methods/chat.postMessage) method and your Slack bot token (starts with `xoxb-`): + +```shell +kubectl create secret generic slack-token \ +--from-literal=address=https://slack.com/api/chat.postMessage \ +--from-literal=token=xoxb-YOUR-TOKEN +``` + +Then reference this secret in `spec.secretRef`: + +```yaml +apiVersion: notification.toolkit.fluxcd.io/v1beta1 +kind: Provider +metadata: + name: slack + namespace: default +spec: + type: slack + channel: general + # HTTP(S) proxy (optional) + proxy: https://proxy.corp:8080 + # secret containing Slack API address and token + secretRef: + name: slack-token +``` + + ### Git commit status The GitHub, GitLab, Bitbucket, and Azure DevOps provider will write to the @@ -366,7 +402,7 @@ spec: The provider type can be: `github`, `gitlab`, `bitbucket` or `azuredevops`. -For bitbucket, the token should contain the username and [app password](https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/#Create-an-app-password) +For bitbucket, the token should contain the username and [app password](https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/#Create-an-app-password) in the format `:`. The app password should have `Repositories (Read/Write)` permission. You can create the secret using this command: diff --git a/internal/notifier/factory.go b/internal/notifier/factory.go index cb22802d9..ce66a64ac 100644 --- a/internal/notifier/factory.go +++ b/internal/notifier/factory.go @@ -54,7 +54,7 @@ func (f Factory) Notifier(provider string) (Interface, error) { case v1beta1.GenericProvider: n, err = NewForwarder(f.URL, f.ProxyURL, f.CertPool) case v1beta1.SlackProvider: - n, err = NewSlack(f.URL, f.ProxyURL, f.CertPool, f.Username, f.Channel) + n, err = NewSlack(f.URL, f.ProxyURL, f.Token, f.CertPool, f.Username, f.Channel) case v1beta1.DiscordProvider: n, err = NewDiscord(f.URL, f.ProxyURL, f.Username, f.Channel) case v1beta1.RocketProvider: diff --git a/internal/notifier/slack.go b/internal/notifier/slack.go index d0322b271..2477dd1f1 100644 --- a/internal/notifier/slack.go +++ b/internal/notifier/slack.go @@ -23,12 +23,14 @@ import ( "strings" "github.com/fluxcd/pkg/runtime/events" + "github.com/hashicorp/go-retryablehttp" ) // Slack holds the hook URL type Slack struct { URL string ProxyURL string + Token string Username string Channel string CertPool *x509.CertPool @@ -60,7 +62,7 @@ type SlackField struct { } // NewSlack validates the Slack URL and returns a Slack object -func NewSlack(hookURL string, proxyURL string, certPool *x509.CertPool, username string, channel string) (*Slack, error) { +func NewSlack(hookURL string, proxyURL string, token string, certPool *x509.CertPool, username string, channel string) (*Slack, error) { _, err := url.ParseRequestURI(hookURL) if err != nil { return nil, fmt.Errorf("invalid Slack hook URL %s", hookURL) @@ -71,6 +73,7 @@ func NewSlack(hookURL string, proxyURL string, certPool *x509.CertPool, username Username: username, URL: hookURL, ProxyURL: proxyURL, + Token: token, CertPool: certPool, }, nil } @@ -114,7 +117,11 @@ func (s *Slack) Post(event events.Event) error { payload.Attachments = []SlackAttachment{a} - err := postMessage(s.URL, s.ProxyURL, s.CertPool, payload) + err := postMessage(s.URL, s.ProxyURL, s.CertPool, payload, func(request *retryablehttp.Request) { + if s.Token != "" { + request.Header.Add("Authorization", "Bearer "+s.Token) + } + }) if err != nil { return fmt.Errorf("postMessage failed: %w", err) } diff --git a/internal/notifier/slack_test.go b/internal/notifier/slack_test.go index 81144dbac..a1133d09f 100644 --- a/internal/notifier/slack_test.go +++ b/internal/notifier/slack_test.go @@ -39,7 +39,7 @@ func TestSlack_Post(t *testing.T) { })) defer ts.Close() - slack, err := NewSlack(ts.URL, "", nil, "", "test") + slack, err := NewSlack(ts.URL, "", "", nil, "", "test") require.NoError(t, err) err = slack.Post(testEvent()) @@ -47,7 +47,7 @@ func TestSlack_Post(t *testing.T) { } func TestSlack_PostUpdate(t *testing.T) { - slack, err := NewSlack("http://localhost", "", nil, "", "test") + slack, err := NewSlack("http://localhost", "", "", nil, "", "test") require.NoError(t, err) event := testEvent()