diff --git a/cmd/flagger/main.go b/cmd/flagger/main.go index c84da585..d2d77e4a 100644 --- a/cmd/flagger/main.go +++ b/cmd/flagger/main.go @@ -34,6 +34,7 @@ var ( controlLoopInterval time.Duration logLevel string port string + msteamsURL string slackURL string slackUser string slackChannel string @@ -53,6 +54,7 @@ func init() { flag.DurationVar(&controlLoopInterval, "control-loop-interval", 10*time.Second, "Kubernetes API sync interval.") 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(&msteamsURL, "msteams-url", "", "MS Teams incoming webhook URL.") flag.StringVar(&slackURL, "slack-url", "", "Slack hook URL.") flag.StringVar(&slackUser, "slack-user", "flagger", "Slack user name.") flag.StringVar(&slackChannel, "slack-channel", "", "Slack channel.") @@ -158,13 +160,25 @@ func main() { logger.Errorf("Metrics server %s unreachable %v", metricsServer, err) } - var slack *notifier.Slack + var notifierClient notifier.Interface if slackURL != "" { - slack, err = notifier.NewSlack(slackURL, slackUser, slackChannel) + f := notifier.NewFactory(slackURL, slackUser, slackChannel) + var err error + notifierClient, err = f.Notifier() if err != nil { logger.Errorf("Notifier %v", err) } else { - logger.Infof("Slack notifications enabled for channel %s", slack.Channel) + logger.Infof("Slack notifications enabled for channel %s", slackChannel) + } + } + if msteamsURL != "" { + f := notifier.NewFactory(slackURL, slackUser, slackChannel) + var err error + notifierClient, err = f.Notifier() + if err != nil { + logger.Errorf("Notifier %v", err) + } else { + logger.Infof("Slack notifications enabled for channel %s", slackChannel) } } @@ -179,9 +193,8 @@ func main() { flaggerClient, canaryInformer, controlLoopInterval, - metricsServer, logger, - slack, + notifierClient, routerFactory, observerFactory, meshProvider, diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 4f44adee..f15c6158 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -46,7 +46,7 @@ type Controller struct { jobs map[string]CanaryJob deployer canary.Deployer recorder metrics.Recorder - notifier *notifier.Slack + notifier notifier.Interface routerFactory *router.Factory observerFactory *metrics.Factory meshProvider string @@ -58,9 +58,8 @@ func NewController( flaggerClient clientset.Interface, flaggerInformer flaggerinformers.CanaryInformer, flaggerWindow time.Duration, - metricServer string, logger *zap.SugaredLogger, - notifier *notifier.Slack, + notifier notifier.Interface, routerFactory *router.Factory, observerFactory *metrics.Factory, meshProvider string, @@ -271,26 +270,26 @@ func (c *Controller) sendNotification(cd *flaggerv1.Canary, message string, meta return } - var fields []notifier.SlackField + var fields []notifier.Field if metadata { fields = append(fields, - notifier.SlackField{ - Title: "Target", + notifier.Field{ + Name: "Target", Value: fmt.Sprintf("%s/%s.%s", cd.Spec.TargetRef.Kind, cd.Spec.TargetRef.Name, cd.Namespace), }, - notifier.SlackField{ - Title: "Traffic routing", + notifier.Field{ + Name: "Traffic routing", Value: fmt.Sprintf("Weight step: %v max: %v", cd.Spec.CanaryAnalysis.StepWeight, cd.Spec.CanaryAnalysis.MaxWeight), }, - notifier.SlackField{ - Title: "Failed checks threshold", + notifier.Field{ + Name: "Failed checks threshold", Value: fmt.Sprintf("%v", cd.Spec.CanaryAnalysis.Threshold), }, - notifier.SlackField{ - Title: "Progress deadline", + notifier.Field{ + Name: "Progress deadline", Value: fmt.Sprintf("%vs", cd.GetProgressDeadlineSeconds()), }, ) diff --git a/pkg/notifier/factory.go b/pkg/notifier/factory.go new file mode 100644 index 00000000..482fd736 --- /dev/null +++ b/pkg/notifier/factory.go @@ -0,0 +1,28 @@ +package notifier + +import ( + "strings" +) + +type Factory struct { + URL string + Username string + Channel string +} + +func NewFactory(URL string, username string, channel string) *Factory { + return &Factory{ + URL: URL, + Channel: channel, + Username: username, + } +} + +func (f Factory) Notifier() (Interface, error) { + switch { + case strings.Contains(f.URL, "slack.com"): + return NewSlack(f.URL, f.Username, f.Channel) + } + + return nil, nil +} diff --git a/pkg/notifier/notifier.go b/pkg/notifier/notifier.go new file mode 100644 index 00000000..c014b030 --- /dev/null +++ b/pkg/notifier/notifier.go @@ -0,0 +1,10 @@ +package notifier + +type Interface interface { + Post(workload string, namespace string, message string, fields []Field, warn bool) error +} + +type Field struct { + Name string + Value string +} diff --git a/pkg/notifier/slack.go b/pkg/notifier/slack.go index a97fc0ad..8f247fbb 100644 --- a/pkg/notifier/slack.go +++ b/pkg/notifier/slack.go @@ -67,7 +67,7 @@ func NewSlack(hookURL string, username string, channel string) (*Slack, error) { } // Post Slack message -func (s *Slack) Post(workload string, namespace string, message string, fields []SlackField, warn bool) error { +func (s *Slack) Post(workload string, namespace string, message string, fields []Field, warn bool) error { payload := SlackPayload{ Channel: s.Channel, Username: s.Username, @@ -78,12 +78,17 @@ func (s *Slack) Post(workload string, namespace string, message string, fields [ color = "danger" } + sfields := make([]SlackField, len(fields)) + for _, f := range fields { + sfields = append(sfields, SlackField{f.Name, f.Value, false}) + } + a := SlackAttachment{ Color: color, AuthorName: fmt.Sprintf("%s.%s", workload, namespace), Text: message, MrkdwnIn: []string{"text"}, - Fields: fields, + Fields: sfields, } payload.Attachments = []SlackAttachment{a}