Skip to content

Commit

Permalink
fix(11164): Advanced Templating using templatePatch
Browse files Browse the repository at this point in the history
Signed-off-by: gmuselli <geoffrey.muselli@gmail.com>
  • Loading branch information
speedfl committed Oct 23, 2023
1 parent 973565e commit 39c3ed8
Show file tree
Hide file tree
Showing 17 changed files with 1,254 additions and 815 deletions.
295 changes: 162 additions & 133 deletions applicationset/controllers/applicationset_controller.go

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions applicationset/controllers/applicationset_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ func (g *generatorMock) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetG
return args.Get(0).([]map[string]interface{}), args.Error(1)
}

func (g *generatorMock) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) {
args := g.Called(tmpl, replaceMap, useGoTemplate, goTemplateOptions)

return args.Get(0).(string), args.Error(1)
}

type rendererMock struct {
mock.Mock
}
Expand All @@ -109,6 +115,12 @@ func (r *rendererMock) RenderTemplateParams(tmpl *v1alpha1.Application, syncPoli

}

func (r *rendererMock) Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) {
args := r.Called(tmpl, replaceMap, useGoTemplate, goTemplateOptions)

return args.Get(0).(string), args.Error(1)
}

func TestExtractApplications(t *testing.T) {
scheme := runtime.NewScheme()
err := v1alpha1.AddToScheme(scheme)
Expand Down
42 changes: 42 additions & 0 deletions applicationset/utils/templatePatch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package utils

import (
"encoding/json"
"fmt"

"k8s.io/apimachinery/pkg/util/strategicpatch"

appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
)

func ApplyPatchTemplate(app *appv1.Application, templatePatch string) (*appv1.Application, error) {

appString, err := json.Marshal(app)
if err != nil {
return nil, fmt.Errorf("error while marhsalling Application %w", err)
}

convertedTemplatePatch, err := ConvertYAMLToJSON(templatePatch)

if err != nil {
return nil, fmt.Errorf("error while converting template to json %q: %w", convertedTemplatePatch, err)
}

if err := json.Unmarshal([]byte(convertedTemplatePatch), &appv1.Application{}); err != nil {
return nil, fmt.Errorf("invalid templatePatch %q: %w", convertedTemplatePatch, err)
}

data, err := strategicpatch.StrategicMergePatch([]byte(appString), []byte(convertedTemplatePatch), appv1.Application{})

if err != nil {
return nil, fmt.Errorf("error while applying templatePatch template to json %q: %w", convertedTemplatePatch, err)
}

finalApp := appv1.Application{}
err = json.Unmarshal(data, &finalApp)
if err != nil {
return nil, fmt.Errorf("error while unmarhsalling patched application %w", err)
}

return &finalApp, err
}
99 changes: 99 additions & 0 deletions applicationset/utils/templatePatch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package utils

import (
"testing"

appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/test/e2e/fixture/applicationsets/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func validate(t *testing.T, patch string) {
app := &appv1.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "argoproj.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-cluster-guestbook",
Namespace: utils.ArgoCDExternalNamespace,
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
},
Spec: argov1alpha1.ApplicationSpec{
Project: "default",
Source: &argov1alpha1.ApplicationSource{
RepoURL: "https://github.com/argoproj/argocd-example-apps.git",
TargetRevision: "HEAD",
Path: "guestbook",
},
Destination: argov1alpha1.ApplicationDestination{
Server: "https://kubernetes.default.svc",
Namespace: "guestbook",
},
},
}

result, err := ApplyPatchTemplate(app, patch)
require.NoError(t, err)
require.Contains(t, result.ObjectMeta.Annotations, "annotation-some-key")
assert.Equal(t, result.ObjectMeta.Annotations["annotation-some-key"], "annotation-some-value")

assert.Equal(t, result.Spec.SyncPolicy.Automated.Prune, true)
require.Contains(t, result.Spec.Source.Helm.ValueFiles, "values.test.yaml")
require.Contains(t, result.Spec.Source.Helm.ValueFiles, "values.big.yaml")
}

func TestWithJson(t *testing.T) {

validate(t, `{
"metadata": {
"annotations": {
"annotation-some-key": "annotation-some-value"
}
},
"spec": {
"source": {
"helm": {
"valueFiles": [
"values.test.yaml",
"values.big.yaml"
]
}
},
"syncPolicy": {
"automated": {
"prune": true
}
}
}
}`)

}

func TestWithYaml(t *testing.T) {

validate(t, `
metadata:
annotations:
annotation-some-key: annotation-some-value
spec:
source:
helm:
valueFiles:
- values.test.yaml
- values.big.yaml
syncPolicy:
automated:
prune: true`)
}

func TestError(t *testing.T) {
app := &appv1.Application{}

result, err := ApplyPatchTemplate(app, "hello world")
require.Error(t, err)
require.Nil(t, result)
}
1 change: 1 addition & 0 deletions applicationset/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func init() {

type Renderer interface {
RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *argoappsv1.ApplicationSetSyncPolicy, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (*argoappsv1.Application, error)
Replace(tmpl string, replaceMap map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error)
}

type Render struct {
Expand Down
3 changes: 3 additions & 0 deletions assets/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -6140,6 +6140,9 @@
},
"template": {
"$ref": "#/definitions/v1alpha1ApplicationSetTemplate"
},
"templatePatch": {
"type": "string"
}
}
},
Expand Down
57 changes: 57 additions & 0 deletions docs/operator-manual/applicationset/Template.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,60 @@ spec:
(*The full example can be found [here](https://github.com/argoproj/argo-cd/tree/master/applicationset/examples/template-override).*)
In this example, the ApplicationSet controller will generate an `Application` resource using the `path` generated by the List generator, rather than the `path` value defined in `.spec.template`.

## Patch Template

Templating is only available on string type. However some uses cases may require to apply templating on other types.

Example:

- Set the automated sync policy
- Switch prune boolean to true
- Add multiple helm value files

ArgoCD has a `templatePatch` feature to allow advanced templating. It supports both json and yaml


```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: guestbook
spec:
goTemplate: true
generators:
- list:
elements:
- cluster: engineering-dev
url: https://kubernetes.default.svc
autoSync: true
prune: true
valueFiles:
- values.large.yaml
- values.debug.yaml
templatePatch: |
spec:
source:
helm:
valueFiles:
{{- range $valueFile := .valueFiles }}
- {{ $valueFile }}
{{- end }}
{{- if .autoSync }}
syncPolicy:
automated:
prune: {{ .prune }}
{{- end }}
template:
metadata:
name: '{{.cluster}}-deployment'
spec:
project: "default"
source:
repoURL: https://github.com/infra-team/cluster-deployments.git
targetRevision: HEAD
path: guestbook/{{ .cluster }}
destination:
server: '{{.url}}'
namespace: guestbook
```
2 changes: 2 additions & 0 deletions manifests/core-install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19855,6 +19855,8 @@ spec:
- metadata
- spec
type: object
templatePatch:
type: string
required:
- generators
- template
Expand Down
2 changes: 2 additions & 0 deletions manifests/crds/applicationset-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14991,6 +14991,8 @@ spec:
- metadata
- spec
type: object
templatePatch:
type: string
required:
- generators
- template
Expand Down
2 changes: 2 additions & 0 deletions manifests/ha/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19855,6 +19855,8 @@ spec:
- metadata
- spec
type: object
templatePatch:
type: string
required:
- generators
- template
Expand Down
2 changes: 2 additions & 0 deletions manifests/install.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19855,6 +19855,8 @@ spec:
- metadata
- spec
type: object
templatePatch:
type: string
required:
- generators
- template
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/application/v1alpha1/applicationset_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type ApplicationSetSpec struct {
// ApplyNestedSelectors enables selectors defined within the generators of two level-nested matrix or merge generators
ApplyNestedSelectors bool `json:"applyNestedSelectors,omitempty" protobuf:"bytes,8,name=applyNestedSelectors"`
IgnoreApplicationDifferences ApplicationSetIgnoreDifferences `json:"ignoreApplicationDifferences,omitempty" protobuf:"bytes,9,name=ignoreApplicationDifferences"`
TemplatePatch *string `json:"templatePatch,omitempty" protobuf:"bytes,10,name=templatePatch"`
}

type ApplicationPreservedFields struct {
Expand Down
Loading

0 comments on commit 39c3ed8

Please sign in to comment.