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  }