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

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