diff --git a/cmd/flagger/main.go b/cmd/flagger/main.go index 023490f3e..6b6766636 100644 --- a/cmd/flagger/main.go +++ b/cmd/flagger/main.go @@ -12,6 +12,7 @@ import ( informers "github.com/stefanprodan/flagger/pkg/client/informers/externalversions" "github.com/stefanprodan/flagger/pkg/controller" "github.com/stefanprodan/flagger/pkg/logging" + "github.com/stefanprodan/flagger/pkg/notifier" "github.com/stefanprodan/flagger/pkg/server" "github.com/stefanprodan/flagger/pkg/version" "k8s.io/client-go/kubernetes" @@ -27,6 +28,9 @@ var ( controlLoopInterval time.Duration logLevel string port string + slackURL string + slackUser string + slackChannel string ) func init() { @@ -36,6 +40,9 @@ func init() { flag.DurationVar(&controlLoopInterval, "control-loop-interval", 10*time.Second, "wait interval between rollouts") 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(&slackUser, "slack-user", "flagger", "Slack user name.") + flag.StringVar(&slackChannel, "slack-channel", "", "Slack channel.") } func main() { @@ -88,6 +95,16 @@ func main() { logger.Errorf("Metrics server %s unreachable %v", metricsServer, err) } + var slack *notifier.Slack + if slackURL != "" { + slack, err = notifier.NewSlack(slackURL, slackUser, slackChannel) + if err != nil { + logger.Errorf("Notifier %v", err) + } else { + logger.Infof("Slack notifications enabled for channel %s", slack.Channel) + } + } + // start HTTP server go server.ListenAndServe(port, 3*time.Second, logger, stopCh) @@ -99,6 +116,7 @@ func main() { controlLoopInterval, metricsServer, logger, + slack, ) flaggerInformerFactory.Start(stopCh) diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 4f62899b1..b17e63051 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -12,6 +12,7 @@ import ( flaggerscheme "github.com/stefanprodan/flagger/pkg/client/clientset/versioned/scheme" flaggerinformers "github.com/stefanprodan/flagger/pkg/client/informers/externalversions/flagger/v1alpha1" flaggerlisters "github.com/stefanprodan/flagger/pkg/client/listers/flagger/v1alpha1" + "github.com/stefanprodan/flagger/pkg/notifier" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -43,6 +44,7 @@ type Controller struct { router CanaryRouter observer CanaryObserver recorder CanaryRecorder + notifier *notifier.Slack } func NewController( @@ -53,6 +55,7 @@ func NewController( flaggerWindow time.Duration, metricServer string, logger *zap.SugaredLogger, + notifier *notifier.Slack, ) *Controller { logger.Debug("Creating event broadcaster") @@ -100,6 +103,7 @@ func NewController( router: router, observer: observer, recorder: recorder, + notifier: notifier, } flaggerInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ @@ -256,6 +260,17 @@ func (c *Controller) recordEventWarningf(r *flaggerv1.Canary, template string, a c.eventRecorder.Event(r, corev1.EventTypeWarning, "Synced", fmt.Sprintf(template, args...)) } +func (c *Controller) sendNotification(workload string, namespace string, message string, warn bool) { + if c.notifier == nil { + return + } + + err := c.notifier.Post(workload, namespace, message, warn) + if err != nil { + c.logger.Error(err) + } +} + func int32p(i int32) *int32 { return &i } diff --git a/pkg/controller/scheduler.go b/pkg/controller/scheduler.go index c0998257b..ff5e7b4ad 100644 --- a/pkg/controller/scheduler.go +++ b/pkg/controller/scheduler.go @@ -110,6 +110,8 @@ func (c *Controller) advanceCanary(name string, namespace string) { return } c.recorder.SetStatus(cd) + c.sendNotification(cd.Spec.TargetRef.Name, cd.Namespace, + "Canary analysis failed, rollback finished.", true) return } @@ -180,6 +182,8 @@ func (c *Controller) advanceCanary(name string, namespace string) { return } c.recorder.SetStatus(cd) + c.sendNotification(cd.Spec.TargetRef.Name, cd.Namespace, + "Canary analysis completed successfully, promotion finished.", false) } } @@ -196,11 +200,15 @@ func (c *Controller) checkCanaryStatus(cd *flaggerv1.Canary, deployer CanaryDepl } c.recorder.SetStatus(cd) c.recordEventInfof(cd, "Initialization done! %s.%s", cd.Name, cd.Namespace) + c.sendNotification(cd.Spec.TargetRef.Name, cd.Namespace, + "New deployment detected, initialization completed.", false) return false } if diff, err := deployer.IsNewSpec(cd); diff { c.recordEventInfof(cd, "New revision detected! Scaling up %s.%s", cd.Spec.TargetRef.Name, cd.Namespace) + c.sendNotification(cd.Spec.TargetRef.Name, cd.Namespace, + "New revision detected, starting canary analysis.", false) if err = deployer.Scale(cd, 1); err != nil { c.recordEventErrorf(cd, "%v", err) return false