From 9bde4cb59fc49e68a92d5724ad08d14d559e8f1a Mon Sep 17 00:00:00 2001 From: Jakub Al-Khalili Date: Fri, 2 Aug 2019 14:07:37 +0200 Subject: [PATCH] Improve notification mechanism --- internal/notifier/mailgun.go | 9 +-- internal/notifier/msteams.go | 11 ++-- internal/notifier/sender.go | 55 +++++++++---------- internal/notifier/slack.go | 11 ++-- pkg/apis/jenkins/v1alpha2/jenkins_types.go | 8 +-- .../jenkins/v1alpha2/zz_generated.deepcopy.go | 6 +- 6 files changed, 46 insertions(+), 54 deletions(-) diff --git a/internal/notifier/mailgun.go b/internal/notifier/mailgun.go index 74f524c5..3bf190b9 100644 --- a/internal/notifier/mailgun.go +++ b/internal/notifier/mailgun.go @@ -3,11 +3,10 @@ package notifier import ( "context" "fmt" + "github.com/pkg/errors" "time" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" - "github.com/jenkinsci/kubernetes-operator/pkg/log" - "github.com/mailgun/mailgun-go/v3" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" @@ -44,21 +43,19 @@ type Mailgun struct{} // Send is function for sending directly to API func (m Mailgun) Send(n *Notification, config v1alpha2.Notification) error { - var selector v1alpha2.SecretKeySelector secret := &corev1.Secret{} i := n.Information - selector = config.Mailgun.APIKeySecretKeySelector + selector := config.Mailgun.APIKeySecretKeySelector err := n.K8sClient.Get(context.TODO(), types.NamespacedName{Name: selector.Name, Namespace: n.Jenkins.Namespace}, secret) if err != nil { - n.Logger.V(log.VWarn).Info(fmt.Sprintf("Failed to get secret with name `%s`. %+v", selector.Name, err)) return err } secretValue := string(secret.Data[selector.Name]) if secretValue == "" { - return fmt.Errorf("SecretValue %s is empty", selector.Name) + return errors.Errorf("SecretValue %s is empty", selector.Name) } mg := mailgun.NewMailgun(config.Mailgun.Domain, secretValue) diff --git a/internal/notifier/msteams.go b/internal/notifier/msteams.go index 7ebea378..f1a1b290 100644 --- a/internal/notifier/msteams.go +++ b/internal/notifier/msteams.go @@ -4,12 +4,10 @@ import ( "bytes" "context" "encoding/json" - "fmt" + "github.com/pkg/errors" "net/http" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" - "github.com/jenkinsci/kubernetes-operator/pkg/log" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" ) @@ -40,15 +38,14 @@ type TeamsFact struct { // Send is function for sending directly to API func (t Teams) Send(n *Notification, config v1alpha2.Notification) error { - var selector v1alpha2.SecretKeySelector secret := &corev1.Secret{} i := n.Information - selector = config.Teams.URLSecretKeySelector + selector := config.Teams.URLSecretKeySelector err := n.K8sClient.Get(context.TODO(), types.NamespacedName{Name: selector.Name, Namespace: n.Jenkins.Namespace}, secret) if err != nil { - n.Logger.V(log.VWarn).Info(fmt.Sprintf("Failed to get secret with name `%s`. %+v", selector.Name, err)) + return err } msg, err := json.Marshal(TeamsMessage{ @@ -83,7 +80,7 @@ func (t Teams) Send(n *Notification, config v1alpha2.Notification) error { secretValue := string(secret.Data[selector.Key]) if secretValue == "" { - return fmt.Errorf("SecretValue %s is empty", selector.Name) + return errors.Errorf("SecretValue %s is empty", selector.Name) } if err != nil { diff --git a/internal/notifier/sender.go b/internal/notifier/sender.go index 81e70ca5..9735fcc5 100644 --- a/internal/notifier/sender.go +++ b/internal/notifier/sender.go @@ -2,9 +2,9 @@ package notifier import ( "fmt" - "github.com/go-logr/logr" "net/http" + "github.com/go-logr/logr" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" "github.com/jenkinsci/kubernetes-operator/pkg/log" @@ -70,27 +70,30 @@ type service interface { // Listen is goroutine that listens for incoming messages and sends it func Listen(notification chan *Notification) { for n := range notification { - notificationConfig := n.Jenkins.Spec.Notification - var err error - var svc service + if len(n.Jenkins.Spec.Notifications) > 0 { + for _, notificationConfig := range n.Jenkins.Spec.Notifications { + var err error + var svc service - if notificationConfig.Slack != (v1alpha2.Slack{}) { - svc = Slack{} - } else if notificationConfig.Teams != (v1alpha2.Teams{}) { - svc = Teams{} - } else if notificationConfig.Mailgun != (v1alpha2.Mailgun{}) { - svc = Mailgun{} - } else { - n.Logger.V(log.VWarn).Info(fmt.Sprintf("Notification service in `%s` not found or not defined", notificationConfig.Name)) - continue - } + if notificationConfig.Slack != (v1alpha2.Slack{}) { + svc = Slack{} + } else if notificationConfig.Teams != (v1alpha2.Teams{}) { + svc = Teams{} + } else if notificationConfig.Mailgun != (v1alpha2.Mailgun{}) { + svc = Mailgun{} + } else { + n.Logger.V(log.VWarn).Info(fmt.Sprintf("Notification service in `%s` not found or not defined", notificationConfig.Name)) + continue + } - err = notify(svc, n, notificationConfig) + err = notify(svc, n, notificationConfig) - if err != nil { - n.Logger.V(log.VWarn).Info(fmt.Sprintf("Failed to send notifications. %+v", err)) - } else { - n.Logger.V(log.VDebug).Info("Sent notification") + if err != nil { + n.Logger.V(log.VWarn).Info(fmt.Sprintf("Failed to send notifications. %+v", err)) + } else { + n.Logger.V(log.VDebug).Info("Sent notification") + } + } } } } @@ -129,16 +132,10 @@ func getStatusColor(logLevel LoggingLevel, svc service) StatusColor { } } -func notify(svc service, n *Notification, nc v1alpha2.Notification) error { - var err error - switch s := svc.(type) { - case Slack: - err = s.Send(n, nc) - case Teams: - err = s.Send(n, nc) - case Mailgun: - err = s.Send(n, nc) +func notify(svc service, n *Notification, manifest v1alpha2.Notification) error { + if n.Information.LogLevel == LogInfo && string(manifest.LoggingLevel) == string(LogWarn) { + return nil } - return err + return svc.Send(n, manifest) } diff --git a/internal/notifier/slack.go b/internal/notifier/slack.go index 0a009514..185d3393 100644 --- a/internal/notifier/slack.go +++ b/internal/notifier/slack.go @@ -4,12 +4,10 @@ import ( "bytes" "context" "encoding/json" - "fmt" + "github.com/pkg/errors" "net/http" "github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2" - "github.com/jenkinsci/kubernetes-operator/pkg/log" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" ) @@ -43,15 +41,14 @@ type SlackField struct { // Send is function for sending directly to API func (s Slack) Send(n *Notification, config v1alpha2.Notification) error { - var selector v1alpha2.SecretKeySelector secret := &corev1.Secret{} i := n.Information - selector = config.Slack.URLSecretKeySelector + selector := config.Slack.URLSecretKeySelector err := n.K8sClient.Get(context.TODO(), types.NamespacedName{Name: selector.Name, Namespace: n.Jenkins.Namespace}, secret) if err != nil { - n.Logger.V(log.VWarn).Info(fmt.Sprintf("Failed to get secret with name `%s`. %+v", selector.Name, err)) + return err } slackMessage, err := json.Marshal(SlackMessage{ @@ -94,7 +91,7 @@ func (s Slack) Send(n *Notification, config v1alpha2.Notification) error { secretValue := string(secret.Data[selector.Key]) if secretValue == "" { - return fmt.Errorf("SecretValue %s is empty", selector.Name) + return errors.Errorf("SecretValue %s is empty", selector.Name) } if err != nil { diff --git a/pkg/apis/jenkins/v1alpha2/jenkins_types.go b/pkg/apis/jenkins/v1alpha2/jenkins_types.go index d9634fd6..4afb27b2 100644 --- a/pkg/apis/jenkins/v1alpha2/jenkins_types.go +++ b/pkg/apis/jenkins/v1alpha2/jenkins_types.go @@ -17,9 +17,9 @@ type JenkinsSpec struct { // +optional SeedJobs []SeedJob `json:"seedJobs,omitempty"` - // Notification defines services which are used to inform about Jenkins behavior + // Notifications defines services which are used to inform about Jenkins status // Can be used to integrate chat services like Slack or Email services like Mailgun - Notification Notification `json:"notifications,omitempty"` + Notifications []Notification `json:"notifications,omitempty"` // Service is Kubernetes service of Jenkins master HTTP pod // Defaults to : @@ -66,13 +66,13 @@ type Notification struct { // Slack is handler for Slack type Slack struct { - // The web hook url to Slack App + // The web hook URL to Slack App URLSecretKeySelector SecretKeySelector `json:"urlSecretKeySelector"` } // Teams is handler for Microsoft Teams type Teams struct { - // The web hook url to Teams App + // The web hook URL to Teams App URLSecretKeySelector SecretKeySelector `json:"urlSecretKeySelector"` } diff --git a/pkg/apis/jenkins/v1alpha2/zz_generated.deepcopy.go b/pkg/apis/jenkins/v1alpha2/zz_generated.deepcopy.go index 0ed66359..5d6d2d18 100644 --- a/pkg/apis/jenkins/v1alpha2/zz_generated.deepcopy.go +++ b/pkg/apis/jenkins/v1alpha2/zz_generated.deepcopy.go @@ -366,7 +366,11 @@ func (in *JenkinsSpec) DeepCopyInto(out *JenkinsSpec) { *out = make([]SeedJob, len(*in)) copy(*out, *in) } - out.Notification = in.Notification + if in.Notifications != nil { + in, out := &in.Notifications, &out.Notifications + *out = make([]Notification, len(*in)) + copy(*out, *in) + } in.Service.DeepCopyInto(&out.Service) in.SlaveService.DeepCopyInto(&out.SlaveService) in.Backup.DeepCopyInto(&out.Backup)