mirror of
https://github.com/fluxcd/flagger.git
synced 2026-02-14 18:10:00 +00:00
Slack bot token authentication
Signed-off-by: Ricardo Lorenzo <rlorenzo@payfone.com>
This commit is contained in:
@@ -66,6 +66,7 @@ var (
|
||||
msteamsProxyURL string
|
||||
includeLabelPrefix string
|
||||
slackURL string
|
||||
slackToken string
|
||||
slackProxyURL string
|
||||
slackUser string
|
||||
slackChannel string
|
||||
@@ -97,6 +98,7 @@ func init() {
|
||||
flag.StringVar(&logLevel, "log-level", "debug", "Log level can be: debug, info, warning, error.")
|
||||
flag.StringVar(&port, "port", "8080", "Port to listen on.")
|
||||
flag.StringVar(&slackURL, "slack-url", "", "Slack hook URL.")
|
||||
flag.StringVar(&slackToken, "slack-token", "", "Slack bot token.")
|
||||
flag.StringVar(&slackProxyURL, "slack-proxy-url", "", "Slack proxy URL.")
|
||||
flag.StringVar(&slackUser, "slack-user", "flagger", "Slack user name.")
|
||||
flag.StringVar(&slackChannel, "slack-channel", "", "Slack channel.")
|
||||
@@ -365,6 +367,7 @@ func startLeaderElection(ctx context.Context, run func(), ns string, kubeClient
|
||||
|
||||
func initNotifier(logger *zap.SugaredLogger) (client notifier.Interface) {
|
||||
provider := "slack"
|
||||
token := fromEnv("SLACK_TOKEN", slackToken)
|
||||
notifierURL := fromEnv("SLACK_URL", slackURL)
|
||||
notifierProxyURL := fromEnv("SLACK_PROXY_URL", slackProxyURL)
|
||||
if msteamsURL != "" || os.Getenv("MSTEAMS_URL") != "" {
|
||||
@@ -372,7 +375,7 @@ func initNotifier(logger *zap.SugaredLogger) (client notifier.Interface) {
|
||||
notifierURL = fromEnv("MSTEAMS_URL", msteamsURL)
|
||||
notifierProxyURL = fromEnv("MSTEAMS_PROXY_URL", msteamsProxyURL)
|
||||
}
|
||||
notifierFactory := notifier.NewFactory(notifierURL, notifierProxyURL, slackUser, slackChannel)
|
||||
notifierFactory := notifier.NewFactory(notifierURL, token, notifierProxyURL, slackUser, slackChannel)
|
||||
|
||||
var err error
|
||||
client, err = notifierFactory.Notifier(provider)
|
||||
|
||||
@@ -37,6 +37,8 @@ or if the analysis reached the maximum number of failed checks:
|
||||
|
||||

|
||||
|
||||
For using a Slack bot token, you should add `token` to a secret and use **secretRef**.
|
||||
|
||||
### Microsoft Teams
|
||||
|
||||
Flagger can be configured to send notifications to Microsoft Teams:
|
||||
@@ -73,6 +75,7 @@ spec:
|
||||
channel: on-call-alerts
|
||||
username: flagger
|
||||
# webhook address (ignored if secretRef is specified)
|
||||
# or https://slack.com/api/chat.postMessage if you use token in the secret
|
||||
address: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
|
||||
# optional http/s proxy
|
||||
proxy: http://my-http-proxy.com
|
||||
@@ -87,6 +90,7 @@ metadata:
|
||||
namespace: flagger
|
||||
data:
|
||||
address: <encoded-url>
|
||||
token: <encoded-token>
|
||||
```
|
||||
|
||||
The alert provider **type** can be: `slack`, `msteams`, `rocket` or `discord`. When set to `discord`,
|
||||
|
||||
@@ -123,6 +123,10 @@ func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bo
|
||||
// set hook URL address
|
||||
url := provider.Spec.Address
|
||||
|
||||
// set the token which will be sent in the header
|
||||
// https://datatracker.ietf.org/doc/html/rfc6750
|
||||
token := ""
|
||||
|
||||
// extract address from secret
|
||||
if provider.Spec.SecretRef != nil {
|
||||
secret, err := c.kubeClient.CoreV1().Secrets(providerNamespace).Get(context.TODO(), provider.Spec.SecretRef.Name, metav1.GetOptions{})
|
||||
@@ -138,6 +142,10 @@ func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bo
|
||||
Errorf("alert provider %s.%s secret does not contain an address", alert.ProviderRef.Name, providerNamespace)
|
||||
continue
|
||||
}
|
||||
|
||||
if tokenFromSecret, ok := secret.Data["token"]; ok {
|
||||
token = string(tokenFromSecret)
|
||||
}
|
||||
}
|
||||
|
||||
// set defaults
|
||||
@@ -155,7 +163,7 @@ func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bo
|
||||
}
|
||||
|
||||
// create notifier based on provider type
|
||||
f := notifier.NewFactory(url, proxy, username, channel)
|
||||
f := notifier.NewFactory(url, token, proxy, username, channel)
|
||||
n, err := f.Notifier(provider.Spec.Type)
|
||||
if err != nil {
|
||||
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func postMessage(address string, proxy string, payload interface{}) error {
|
||||
func postMessage(address, token, proxy string, payload interface{}) error {
|
||||
var httpClient = &http.Client{}
|
||||
|
||||
if proxy != "" {
|
||||
@@ -64,6 +64,10 @@ func postMessage(address string, proxy string, payload interface{}) error {
|
||||
}
|
||||
req.Header.Set("Content-type", "application/json")
|
||||
|
||||
if token != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(req.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
|
||||
@@ -39,6 +39,6 @@ func Test_postMessage(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
err := postMessage(ts.URL, "", map[string]string{"status": "success"})
|
||||
err := postMessage(ts.URL, "", "", map[string]string{"status": "success"})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ func (s *Discord) Post(workload string, namespace string, message string, fields
|
||||
|
||||
payload.Attachments = []SlackAttachment{a}
|
||||
|
||||
err := postMessage(s.URL, s.ProxyURL, payload)
|
||||
err := postMessage(s.URL, "", s.ProxyURL, payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -22,14 +22,16 @@ import (
|
||||
|
||||
type Factory struct {
|
||||
URL string
|
||||
Token string
|
||||
ProxyURL string
|
||||
Username string
|
||||
Channel string
|
||||
}
|
||||
|
||||
func NewFactory(url string, proxy string, username string, channel string) *Factory {
|
||||
func NewFactory(url, token, proxy, username, channel string) *Factory {
|
||||
return &Factory{
|
||||
URL: url,
|
||||
Token: token,
|
||||
ProxyURL: proxy,
|
||||
Channel: channel,
|
||||
Username: username,
|
||||
@@ -45,7 +47,7 @@ func (f Factory) Notifier(provider string) (Interface, error) {
|
||||
var err error
|
||||
switch provider {
|
||||
case "slack":
|
||||
n, err = NewSlack(f.URL, f.ProxyURL, f.Username, f.Channel)
|
||||
n, err = NewSlack(f.URL, f.Token, f.ProxyURL, f.Username, f.Channel)
|
||||
case "discord":
|
||||
n, err = NewDiscord(f.URL, f.ProxyURL, f.Username, f.Channel)
|
||||
case "rocket":
|
||||
|
||||
@@ -111,7 +111,7 @@ func (s *GChat) Post(workload string, namespace string, message string, fields [
|
||||
},
|
||||
}
|
||||
|
||||
err := postMessage(s.URL, s.ProxyURL, payload)
|
||||
err := postMessage(s.URL, "", s.ProxyURL, payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func (s *Rocket) Post(workload string, namespace string, message string, fields
|
||||
|
||||
payload.Attachments = []SlackAttachment{a}
|
||||
|
||||
err := postMessage(s.URL, s.ProxyURL, payload)
|
||||
err := postMessage(s.URL, "", s.ProxyURL, payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
// Slack holds the hook URL
|
||||
type Slack struct {
|
||||
URL string
|
||||
Token string
|
||||
ProxyURL string
|
||||
Username string
|
||||
Channel string
|
||||
@@ -56,10 +57,10 @@ type SlackField struct {
|
||||
}
|
||||
|
||||
// NewSlack validates the Slack URL and returns a Slack object
|
||||
func NewSlack(hookURL string, proxyURL string, username string, channel string) (*Slack, error) {
|
||||
_, err := url.ParseRequestURI(hookURL)
|
||||
func NewSlack(address, token, proxyURL, username, channel string) (*Slack, error) {
|
||||
_, err := url.ParseRequestURI(address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Slack hook URL %s", hookURL)
|
||||
return nil, fmt.Errorf("invalid Slack hook URL %s", address)
|
||||
}
|
||||
|
||||
if username == "" {
|
||||
@@ -72,7 +73,8 @@ func NewSlack(hookURL string, proxyURL string, username string, channel string)
|
||||
|
||||
return &Slack{
|
||||
Channel: channel,
|
||||
URL: hookURL,
|
||||
URL: address,
|
||||
Token: token,
|
||||
ProxyURL: proxyURL,
|
||||
Username: username,
|
||||
}, nil
|
||||
@@ -106,7 +108,7 @@ func (s *Slack) Post(workload string, namespace string, message string, fields [
|
||||
|
||||
payload.Attachments = []SlackAttachment{a}
|
||||
|
||||
err := postMessage(s.URL, s.ProxyURL, payload)
|
||||
err := postMessage(s.URL, s.Token, s.ProxyURL, payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func TestSlack_Post(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
slack, err := NewSlack(ts.URL, "", "test", "test")
|
||||
slack, err := NewSlack(ts.URL, "", "", "test", "test")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = slack.Post("podinfo", "test", "test", fields, "error")
|
||||
|
||||
@@ -86,7 +86,7 @@ func (s *MSTeams) Post(workload string, namespace string, message string, fields
|
||||
payload.ThemeColor = "FF0000"
|
||||
}
|
||||
|
||||
err := postMessage(s.URL, s.ProxyURL, payload)
|
||||
err := postMessage(s.URL, "", s.ProxyURL, payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user