github.com/argoproj/argo-cd/v3@v3.2.1/applicationset/generators/plugin.go (about)

     1  package generators
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/jeremywohl/flatten"
    12  	corev1 "k8s.io/api/core/v1"
    13  	"sigs.k8s.io/controller-runtime/pkg/client"
    14  
    15  	argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    16  	"github.com/argoproj/argo-cd/v3/util/settings"
    17  
    18  	"github.com/argoproj/argo-cd/v3/applicationset/services/plugin"
    19  )
    20  
    21  const (
    22  	DefaultPluginRequeueAfter = 30 * time.Minute
    23  )
    24  
    25  var _ Generator = (*PluginGenerator)(nil)
    26  
    27  type PluginGenerator struct {
    28  	client    client.Client
    29  	namespace string
    30  }
    31  
    32  func NewPluginGenerator(client client.Client, namespace string) Generator {
    33  	g := &PluginGenerator{
    34  		client:    client,
    35  		namespace: namespace,
    36  	}
    37  	return g
    38  }
    39  
    40  func (g *PluginGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
    41  	// Return a requeue default of 30 minutes, if no default is specified.
    42  
    43  	if appSetGenerator.Plugin.RequeueAfterSeconds != nil {
    44  		return time.Duration(*appSetGenerator.Plugin.RequeueAfterSeconds) * time.Second
    45  	}
    46  
    47  	return DefaultPluginRequeueAfter
    48  }
    49  
    50  func (g *PluginGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
    51  	return &appSetGenerator.Plugin.Template
    52  }
    53  
    54  func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
    55  	if appSetGenerator == nil {
    56  		return nil, ErrEmptyAppSetGenerator
    57  	}
    58  
    59  	if appSetGenerator.Plugin == nil {
    60  		return nil, ErrEmptyAppSetGenerator
    61  	}
    62  
    63  	ctx := context.Background()
    64  
    65  	providerConfig := appSetGenerator.Plugin
    66  
    67  	pluginClient, err := g.getPluginFromGenerator(ctx, applicationSetInfo.Name, providerConfig)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("error getting plugin from generator: %w", err)
    70  	}
    71  
    72  	list, err := pluginClient.List(ctx, providerConfig.Input.Parameters)
    73  	if err != nil {
    74  		return nil, fmt.Errorf("error listing params: %w", err)
    75  	}
    76  
    77  	res, err := g.generateParams(appSetGenerator, applicationSetInfo, list.Output.Parameters, appSetGenerator.Plugin.Input.Parameters, applicationSetInfo.Spec.GoTemplate)
    78  	if err != nil {
    79  		return nil, fmt.Errorf("error generating params: %w", err)
    80  	}
    81  
    82  	return res, nil
    83  }
    84  
    85  func (g *PluginGenerator) getPluginFromGenerator(ctx context.Context, appSetName string, generatorConfig *argoprojiov1alpha1.PluginGenerator) (*plugin.Service, error) {
    86  	cm, err := g.getConfigMap(ctx, generatorConfig.ConfigMapRef.Name)
    87  	if err != nil {
    88  		return nil, fmt.Errorf("error fetching ConfigMap: %w", err)
    89  	}
    90  	token, err := g.getToken(ctx, cm["token"])
    91  	if err != nil {
    92  		return nil, fmt.Errorf("error fetching Secret token: %w", err)
    93  	}
    94  
    95  	var requestTimeout int
    96  	requestTimeoutStr, ok := cm["requestTimeout"]
    97  	if ok {
    98  		requestTimeout, err = strconv.Atoi(requestTimeoutStr)
    99  		if err != nil {
   100  			return nil, fmt.Errorf("error set requestTimeout : %w", err)
   101  		}
   102  	}
   103  
   104  	pluginClient, err := plugin.NewPluginService(appSetName, cm["baseUrl"], token, requestTimeout)
   105  	if err != nil {
   106  		return nil, fmt.Errorf("error initializing plugin client: %w", err)
   107  	}
   108  	return pluginClient, nil
   109  }
   110  
   111  func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, objectsFound []map[string]any, pluginParams argoprojiov1alpha1.PluginParameters, useGoTemplate bool) ([]map[string]any, error) {
   112  	res := []map[string]any{}
   113  
   114  	for _, objectFound := range objectsFound {
   115  		params := map[string]any{}
   116  
   117  		if useGoTemplate {
   118  			for k, v := range objectFound {
   119  				params[k] = v
   120  			}
   121  		} else {
   122  			flat, err := flatten.Flatten(objectFound, "", flatten.DotStyle)
   123  			if err != nil {
   124  				return nil, err
   125  			}
   126  			for k, v := range flat {
   127  				params[k] = fmt.Sprintf("%v", v)
   128  			}
   129  		}
   130  
   131  		params["generator"] = map[string]any{
   132  			"input": map[string]argoprojiov1alpha1.PluginParameters{
   133  				"parameters": pluginParams,
   134  			},
   135  		}
   136  
   137  		err := appendTemplatedValues(appSetGenerator.Plugin.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
   138  		if err != nil {
   139  			return nil, err
   140  		}
   141  
   142  		res = append(res, params)
   143  	}
   144  
   145  	return res, nil
   146  }
   147  
   148  func (g *PluginGenerator) getToken(ctx context.Context, tokenRef string) (string, error) {
   149  	if tokenRef == "" || !strings.HasPrefix(tokenRef, "$") {
   150  		return "", fmt.Errorf("token is empty, or does not reference a secret key starting with '$': %v", tokenRef)
   151  	}
   152  
   153  	secretName, tokenKey := plugin.ParseSecretKey(tokenRef)
   154  
   155  	secret := &corev1.Secret{}
   156  	err := g.client.Get(
   157  		ctx,
   158  		client.ObjectKey{
   159  			Name:      secretName,
   160  			Namespace: g.namespace,
   161  		},
   162  		secret)
   163  	if err != nil {
   164  		return "", fmt.Errorf("error fetching secret %s/%s: %w", g.namespace, secretName, err)
   165  	}
   166  
   167  	secretValues := make(map[string]string, len(secret.Data))
   168  
   169  	for k, v := range secret.Data {
   170  		secretValues[k] = string(v)
   171  	}
   172  
   173  	token := settings.ReplaceStringSecret(tokenKey, secretValues)
   174  
   175  	return token, err
   176  }
   177  
   178  func (g *PluginGenerator) getConfigMap(ctx context.Context, configMapRef string) (map[string]string, error) {
   179  	cm := &corev1.ConfigMap{}
   180  	err := g.client.Get(
   181  		ctx,
   182  		client.ObjectKey{
   183  			Name:      configMapRef,
   184  			Namespace: g.namespace,
   185  		},
   186  		cm)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	baseURL, ok := cm.Data["baseUrl"]
   192  	if !ok || baseURL == "" {
   193  		return nil, errors.New("baseUrl not found in ConfigMap")
   194  	}
   195  
   196  	token, ok := cm.Data["token"]
   197  	if !ok || token == "" {
   198  		return nil, errors.New("token not found in ConfigMap")
   199  	}
   200  
   201  	return cm.Data, nil
   202  }