github.com/argoproj/argo-cd/v2@v2.10.9/applicationset/generators/generator_spec_processor.go (about) 1 package generators 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/jeremywohl/flatten" 8 9 "github.com/argoproj/argo-cd/v2/applicationset/utils" 10 11 "k8s.io/apimachinery/pkg/labels" 12 13 argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 14 15 "github.com/imdario/mergo" 16 log "github.com/sirupsen/logrus" 17 ) 18 19 const ( 20 selectorKey = "Selector" 21 ) 22 23 type TransformResult struct { 24 Params []map[string]interface{} 25 Template argoprojiov1alpha1.ApplicationSetTemplate 26 } 27 28 // Transform a spec generator to list of paramSets and a template 29 func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}) ([]TransformResult, error) { 30 // This is a custom version of the `LabelSelectorAsSelector` that is in k8s.io/apimachinery. This has been copied 31 // verbatim from that package, with the difference that we do not have any restrictions on label values. This is done 32 // so that, among other things, we can match on cluster urls. 33 selector, err := utils.LabelSelectorAsSelector(requestedGenerator.Selector) 34 if err != nil { 35 return nil, fmt.Errorf("error parsing label selector: %w", err) 36 } 37 38 res := []TransformResult{} 39 var firstError error 40 interpolatedGenerator := requestedGenerator.DeepCopy() 41 42 generators := GetRelevantGenerators(&requestedGenerator, allGenerators) 43 for _, g := range generators { 44 // we call mergeGeneratorTemplate first because GenerateParams might be more costly so we want to fail fast if there is an error 45 mergedTemplate, err := mergeGeneratorTemplate(g, &requestedGenerator, baseTemplate) 46 if err != nil { 47 log.WithError(err).WithField("generator", g). 48 Error("error generating params") 49 if firstError == nil { 50 firstError = err 51 } 52 continue 53 } 54 var params []map[string]interface{} 55 if len(genParams) != 0 { 56 tempInterpolatedGenerator, err := InterpolateGenerator(&requestedGenerator, genParams, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions) 57 interpolatedGenerator = &tempInterpolatedGenerator 58 if err != nil { 59 log.WithError(err).WithField("genParams", genParams). 60 Error("error interpolating params for generator") 61 if firstError == nil { 62 firstError = err 63 } 64 continue 65 } 66 } 67 params, err = g.GenerateParams(interpolatedGenerator, appSet) 68 if err != nil { 69 log.WithError(err).WithField("generator", g). 70 Error("error generating params") 71 if firstError == nil { 72 firstError = err 73 } 74 continue 75 } 76 var filterParams []map[string]interface{} 77 for _, param := range params { 78 flatParam, err := flattenParameters(param) 79 if err != nil { 80 log.WithError(err).WithField("generator", g). 81 Error("error flattening params") 82 if firstError == nil { 83 firstError = err 84 } 85 continue 86 } 87 88 if requestedGenerator.Selector != nil && !selector.Matches(labels.Set(flatParam)) { 89 continue 90 } 91 filterParams = append(filterParams, param) 92 } 93 94 res = append(res, TransformResult{ 95 Params: filterParams, 96 Template: mergedTemplate, 97 }) 98 } 99 100 return res, firstError 101 } 102 103 func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, generators map[string]Generator) []Generator { 104 var res []Generator 105 106 v := reflect.Indirect(reflect.ValueOf(requestedGenerator)) 107 for i := 0; i < v.NumField(); i++ { 108 field := v.Field(i) 109 if !field.CanInterface() { 110 continue 111 } 112 name := v.Type().Field(i).Name 113 if name == selectorKey { 114 continue 115 } 116 117 if !reflect.ValueOf(field.Interface()).IsNil() { 118 res = append(res, generators[name]) 119 } 120 } 121 122 return res 123 } 124 125 func flattenParameters(in map[string]interface{}) (map[string]string, error) { 126 flat, err := flatten.Flatten(in, "", flatten.DotStyle) 127 if err != nil { 128 return nil, fmt.Errorf("error flatenning parameters: %w", err) 129 } 130 131 out := make(map[string]string, len(flat)) 132 for k, v := range flat { 133 out[k] = fmt.Sprintf("%v", v) 134 } 135 136 return out, nil 137 } 138 139 func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetTemplate argoprojiov1alpha1.ApplicationSetTemplate) (argoprojiov1alpha1.ApplicationSetTemplate, error) { 140 // Make a copy of the value from `GetTemplate()` before merge, rather than copying directly into 141 // the provided parameter (which will touch the original resource object returned by client-go) 142 dest := g.GetTemplate(requestedGenerator).DeepCopy() 143 144 err := mergo.Merge(dest, applicationSetTemplate) 145 146 return *dest, err 147 } 148 149 // InterpolateGenerator allows interpolating the matrix's 2nd child generator with values from the 1st child generator 150 // "params" parameter is an array, where each index corresponds to a generator. Each index contains a map w/ that generator's parameters. 151 func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (argoprojiov1alpha1.ApplicationSetGenerator, error) { 152 render := utils.Render{} 153 interpolatedGenerator, err := render.RenderGeneratorParams(requestedGenerator, params, useGoTemplate, goTemplateOptions) 154 if err != nil { 155 log.WithError(err).WithField("interpolatedGenerator", interpolatedGenerator).Error("error interpolating generator with other generator's parameter") 156 return argoprojiov1alpha1.ApplicationSetGenerator{}, err 157 } 158 159 return *interpolatedGenerator, nil 160 } 161 162 // Fixes https://github.com/argoproj/argo-cd/issues/11982 while ensuring backwards compatibility. 163 // This is only a short-term solution and should be removed in a future major version. 164 func dropDisabledNestedSelectors(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator) bool { 165 var foundSelector bool 166 for i := range generators { 167 if generators[i].Selector != nil { 168 foundSelector = true 169 generators[i].Selector = nil 170 } 171 } 172 return foundSelector 173 }