Skip to content

Commit

Permalink
fix: use field-wise templating for child matrix generators (#11661) (#…
Browse files Browse the repository at this point in the history
…12287)

* fix: use field-wise templating for child matrix generators (#11661)

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* test shouldn't use go template

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* Update applicationset/utils/utils.go

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

---------

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
  • Loading branch information
crenshaw-dev committed Mar 8, 2023
1 parent c9ca3ff commit 1a5bcf8
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 18 deletions.
21 changes: 4 additions & 17 deletions applicationset/generators/generator_spec_processor.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package generators

import (
"encoding/json"
"reflect"

"github.com/argoproj/argo-cd/v2/applicationset/utils"
Expand All @@ -24,7 +23,7 @@ type TransformResult struct {
Template argoprojiov1alpha1.ApplicationSetTemplate
}

//Transform a spec generator to list of paramSets and a template
// Transform a spec generator to list of paramSets and a template
func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}) ([]TransformResult, error) {
selector, err := metav1.LabelSelectorAsSelector(requestedGenerator.Selector)
if err != nil {
Expand Down Expand Up @@ -131,27 +130,15 @@ func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.
return *dest, err
}

// Currently for Matrix Generator. Allows interpolating the matrix's 2nd child generator with values from the 1st child generator
// InterpolateGenerator allows interpolating the matrix's 2nd child generator with values from the 1st child generator
// "params" parameter is an array, where each index corresponds to a generator. Each index contains a map w/ that generator's parameters.
func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool) (argoprojiov1alpha1.ApplicationSetGenerator, error) {
interpolatedGenerator := requestedGenerator.DeepCopy()
tmplBytes, err := json.Marshal(interpolatedGenerator)
if err != nil {
log.WithError(err).WithField("requestedGenerator", interpolatedGenerator).Error("error marshalling requested generator for interpolation")
return *interpolatedGenerator, err
}

render := utils.Render{}
replacedTmplStr, err := render.Replace(string(tmplBytes), params, useGoTemplate)
interpolatedGenerator, err := render.RenderGeneratorParams(requestedGenerator, params, useGoTemplate)
if err != nil {
log.WithError(err).WithField("interpolatedGeneratorString", replacedTmplStr).Error("error interpolating generator with other generator's parameter")
log.WithError(err).WithField("interpolatedGenerator", interpolatedGenerator).Error("error interpolating generator with other generator's parameter")
return *interpolatedGenerator, err
}

err = json.Unmarshal([]byte(replacedTmplStr), interpolatedGenerator)
if err != nil {
log.WithError(err).WithField("requestedGenerator", interpolatedGenerator).Error("error unmarshalling requested generator for interpolation")
return *interpolatedGenerator, err
}
return *interpolatedGenerator, nil
}
55 changes: 55 additions & 0 deletions applicationset/generators/generator_spec_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

Expand Down Expand Up @@ -248,6 +249,60 @@ func TestInterpolateGenerator(t *testing.T) {
Path: "{{server}}",
}

requestedGenerator = &argoprojiov1alpha1.ApplicationSetGenerator{
Git: &argoprojiov1alpha1.GitGenerator{
Files: append([]argoprojiov1alpha1.GitFileGeneratorItem{}, fileNamePath, fileServerPath),
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
},
}
clusterGeneratorParams := map[string]interface{}{
"name": "production_01/west", "server": "https://production-01.example.com",
}
interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, false)
if err != nil {
log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator")
return
}
assert.Equal(t, "production_01/west", interpolatedGenerator.Git.Files[0].Path)
assert.Equal(t, "https://production-01.example.com", interpolatedGenerator.Git.Files[1].Path)
}

func TestInterpolateGenerator_go(t *testing.T) {
requestedGenerator := &argoprojiov1alpha1.ApplicationSetGenerator{
Clusters: &argoprojiov1alpha1.ClusterGenerator{
Selector: metav1.LabelSelector{
MatchLabels: map[string]string{
"argocd.argoproj.io/secret-type": "cluster",
"path-basename": "{{base .path.path}}",
"path-zero": "{{index .path.segments 0}}",
"path-full": "{{.path.path}}",
"kubernetes.io/environment": `{{default "foo" .my_label}}`,
}},
},
}
gitGeneratorParams := map[string]interface{}{
"path": map[string]interface{}{
"path": "p1/p2/app3",
"segments": []string{"p1", "p2", "app3"},
},
}
interpolatedGenerator, err := InterpolateGenerator(requestedGenerator, gitGeneratorParams, true)
require.NoError(t, err)
if err != nil {
log.WithError(err).WithField("requestedGenerator", requestedGenerator).Error("error interpolating Generator")
return
}
assert.Equal(t, "app3", interpolatedGenerator.Clusters.Selector.MatchLabels["path-basename"])
assert.Equal(t, "p1", interpolatedGenerator.Clusters.Selector.MatchLabels["path-zero"])
assert.Equal(t, "p1/p2/app3", interpolatedGenerator.Clusters.Selector.MatchLabels["path-full"])

fileNamePath := argoprojiov1alpha1.GitFileGeneratorItem{
Path: "{{.name}}",
}
fileServerPath := argoprojiov1alpha1.GitFileGeneratorItem{
Path: "{{.server}}",
}

requestedGenerator = &argoprojiov1alpha1.ApplicationSetGenerator{
Git: &argoprojiov1alpha1.GitGenerator{
Files: append([]argoprojiov1alpha1.GitFileGeneratorItem{}, fileNamePath, fileServerPath),
Expand Down
23 changes: 22 additions & 1 deletion applicationset/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (r *Render) deeplyReplace(copy, original reflect.Value, replaceMap map[stri

func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool) (*argoappsv1.Application, error) {
if tmpl == nil {
return nil, fmt.Errorf("application template is empty ")
return nil, fmt.Errorf("application template is empty")
}

if len(params) == 0 {
Expand Down Expand Up @@ -204,6 +204,27 @@ func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *
return replacedTmpl, nil
}

func (r *Render) RenderGeneratorParams(gen *argoappsv1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool) (*argoappsv1.ApplicationSetGenerator, error) {
if gen == nil {
return nil, fmt.Errorf("generator is empty")
}

if len(params) == 0 {
return gen, nil
}

original := reflect.ValueOf(gen)
copy := reflect.New(original.Type()).Elem()

if err := r.deeplyReplace(copy, original, params, useGoTemplate); err != nil {
return nil, fmt.Errorf("failed to replace parameters in generator: %w", err)
}

replacedGen := copy.Interface().(*argoappsv1.ApplicationSetGenerator)

return replacedGen, nil
}

var isTemplatedRegex = regexp.MustCompile(".*{{.*}}.*")

// Replace executes basic string substitution of a template with replacement values.
Expand Down

0 comments on commit 1a5bcf8

Please sign in to comment.