github.com/argoproj/argo-cd/v3@v3.2.1/util/notification/settings/legacy.go (about) 1 package settings 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 8 "github.com/argoproj/notifications-engine/pkg/api" 9 "github.com/argoproj/notifications-engine/pkg/services" 10 "github.com/argoproj/notifications-engine/pkg/subscriptions" 11 "github.com/argoproj/notifications-engine/pkg/triggers" 12 "github.com/argoproj/notifications-engine/pkg/util/text" 13 jsonpatch "github.com/evanphx/json-patch" 14 log "github.com/sirupsen/logrus" 15 corev1 "k8s.io/api/core/v1" 16 "sigs.k8s.io/yaml" 17 ) 18 19 type legacyTemplate struct { 20 Name string `json:"name,omitempty"` 21 Title string `json:"subject,omitempty"` 22 Body string `json:"body,omitempty"` 23 services.Notification 24 } 25 26 type legacyTrigger struct { 27 Name string `json:"name,omitempty"` 28 Condition string `json:"condition,omitempty"` 29 Description string `json:"description,omitempty"` 30 Template string `json:"template,omitempty"` 31 Enabled *bool `json:"enabled,omitempty"` 32 } 33 34 type legacyConfig struct { 35 Triggers []legacyTrigger `json:"triggers,omitempty"` 36 Templates []legacyTemplate `json:"templates,omitempty"` 37 Context map[string]string `json:"context,omitempty"` 38 Subscriptions subscriptions.DefaultSubscriptions `json:"subscriptions,omitempty"` 39 } 40 41 type legacyWebhookOptions struct { 42 services.WebhookOptions 43 Name string `json:"name"` 44 } 45 46 type legacyServicesConfig struct { 47 Email *services.EmailOptions `json:"email"` 48 Slack *services.SlackOptions `json:"slack"` 49 Opsgenie *services.OpsgenieOptions `json:"opsgenie"` 50 Grafana *services.GrafanaOptions `json:"grafana"` 51 Webhook []legacyWebhookOptions `json:"webhook"` 52 } 53 54 func mergePatch(orig any, other any) error { 55 origData, err := json.Marshal(orig) 56 if err != nil { 57 return fmt.Errorf("error marshaling json for original: %w", err) 58 } 59 otherData, err := json.Marshal(other) 60 if err != nil { 61 return fmt.Errorf("error marshaling json for patch: %w", err) 62 } 63 64 if string(otherData) == "null" { 65 return nil 66 } 67 68 mergedData, err := jsonpatch.MergePatch(origData, otherData) 69 if err != nil { 70 return fmt.Errorf("error creating merge patch: %w", err) 71 } 72 return json.Unmarshal(mergedData, orig) 73 } 74 75 func (legacy legacyConfig) merge(cfg *api.Config, context map[string]string) error { 76 if err := mergePatch(&context, &legacy.Context); err != nil { 77 return fmt.Errorf("error in merge: %w", err) 78 } 79 if err := mergePatch(&cfg.Subscriptions, &legacy.Subscriptions); err != nil { 80 return fmt.Errorf("error in merge config and legacy subscriptions: %w", err) 81 } 82 83 for _, template := range legacy.Templates { 84 t, ok := cfg.Templates[template.Name] 85 if ok { 86 if err := mergePatch(&t, &template.Notification); err != nil { 87 return err 88 } 89 } 90 if template.Title != "" { 91 if template.Email == nil { 92 template.Email = &services.EmailNotification{} 93 } 94 template.Email.Subject = template.Title 95 } 96 if template.Body != "" { 97 template.Message = template.Body 98 } 99 cfg.Templates[template.Name] = template.Notification 100 } 101 102 for _, trigger := range legacy.Triggers { 103 if trigger.Enabled != nil && *trigger.Enabled { 104 cfg.DefaultTriggers = append(cfg.DefaultTriggers, trigger.Name) 105 } 106 var firstCondition triggers.Condition 107 t, ok := cfg.Triggers[trigger.Name] 108 if !ok || len(t) == 0 { 109 t = []triggers.Condition{firstCondition} 110 } else { 111 firstCondition = t[0] 112 } 113 114 if trigger.Condition != "" { 115 firstCondition.When = trigger.Condition 116 } 117 if trigger.Template != "" { 118 firstCondition.Send = []string{trigger.Template} 119 } 120 if trigger.Description != "" { 121 firstCondition.Description = trigger.Description 122 } 123 t[0] = firstCondition 124 cfg.Triggers[trigger.Name] = t 125 } 126 127 return nil 128 } 129 130 func (c *legacyServicesConfig) merge(cfg *api.Config) { 131 if c.Email != nil { 132 cfg.Services["email"] = func() (services.NotificationService, error) { 133 return services.NewEmailService(*c.Email), nil 134 } 135 } 136 if c.Slack != nil { 137 cfg.Services["slack"] = func() (services.NotificationService, error) { 138 return services.NewSlackService(*c.Slack), nil 139 } 140 } 141 if c.Grafana != nil { 142 cfg.Services["grafana"] = func() (services.NotificationService, error) { 143 return services.NewGrafanaService(*c.Grafana), nil 144 } 145 } 146 if c.Opsgenie != nil { 147 cfg.Services["opsgenie"] = func() (services.NotificationService, error) { 148 return services.NewOpsgenieService(*c.Opsgenie), nil 149 } 150 } 151 for i := range c.Webhook { 152 opts := c.Webhook[i] 153 cfg.Services[opts.Name] = func() (services.NotificationService, error) { 154 return services.NewWebhookService(opts.WebhookOptions), nil 155 } 156 } 157 } 158 159 // ApplyLegacyConfig settings specified using deprecated config map and secret keys 160 func ApplyLegacyConfig(cfg *api.Config, context map[string]string, cm *corev1.ConfigMap, secret *corev1.Secret) error { 161 if notifiersData, ok := secret.Data["notifiers.yaml"]; ok && len(notifiersData) > 0 { 162 log.Warn("Key 'notifiers.yaml' in Secret is deprecated, please migrate to new settings") 163 legacyServices := &legacyServicesConfig{} 164 err := yaml.Unmarshal(notifiersData, legacyServices) 165 if err != nil { 166 return fmt.Errorf("error unmarshaling legacy services config: %w", err) 167 } 168 legacyServices.merge(cfg) 169 } 170 171 if configData, ok := cm.Data["config.yaml"]; ok && configData != "" { 172 log.Warn("Key 'config.yaml' in ConfigMap is deprecated, please migrate to new settings") 173 legacyCfg := &legacyConfig{} 174 err := yaml.Unmarshal([]byte(configData), legacyCfg) 175 if err != nil { 176 return fmt.Errorf("error in unmarshaling legacy config: %w", err) 177 } 178 err = legacyCfg.merge(cfg, context) 179 if err != nil { 180 return fmt.Errorf("error in ApplyLegacyConfig merging config map: %w", err) 181 } 182 } 183 return nil 184 } 185 186 const ( 187 annotationKey = "recipients.argocd-notifications.argoproj.io" 188 ) 189 190 func GetLegacyDestinations(annotations map[string]string, defaultTriggers []string, serviceDefaultTriggers map[string][]string) services.Destinations { 191 dests := services.Destinations{} 192 for k, v := range annotations { 193 if !strings.HasSuffix(k, annotationKey) { 194 continue 195 } 196 197 var triggerNames []string 198 triggerName := strings.TrimRight(k[0:len(k)-len(annotationKey)], ".") 199 if triggerName == "" { 200 triggerNames = defaultTriggers 201 } else { 202 triggerNames = []string{triggerName} 203 } 204 205 for _, recipient := range text.SplitRemoveEmpty(v, ",") { 206 if recipient = strings.TrimSpace(recipient); recipient == "" { 207 continue 208 } 209 parts := strings.Split(recipient, ":") 210 dest := services.Destination{Service: parts[0]} 211 if len(parts) > 1 { 212 dest.Recipient = parts[1] 213 } 214 215 t := triggerNames 216 if v, ok := serviceDefaultTriggers[dest.Service]; ok { 217 t = v 218 } 219 for _, name := range t { 220 dests[name] = append(dests[name], dest) 221 } 222 } 223 } 224 return dests 225 } 226 227 // injectLegacyVar injects legacy variable into context 228 func injectLegacyVar(ctx map[string]string, serviceType string) map[string]string { 229 res := map[string]string{ 230 "notificationType": serviceType, 231 } 232 for k, v := range ctx { 233 res[k] = v 234 } 235 return res 236 }