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 }