diff --git a/applicationset/controllers/applicationset_controller.go b/applicationset/controllers/applicationset_controller.go index d785ad7289c4a..1914783c4c79e 100644 --- a/applicationset/controllers/applicationset_controller.go +++ b/applicationset/controllers/applicationset_controller.go @@ -597,6 +597,9 @@ func (r *ApplicationSetReconciler) createOrUpdateInCluster(ctx context.Context, appLog := log.WithFields(log.Fields{"app": generatedApp.Name, "appSet": applicationSet.Name}) generatedApp.Namespace = applicationSet.Namespace + // Normalize to avoid fighting with the application controller. + generatedApp.Spec = *argoutil.NormalizeApplicationSpec(&generatedApp.Spec) + found := &argov1alpha1.Application{ ObjectMeta: metav1.ObjectMeta{ Name: generatedApp.Name, diff --git a/applicationset/controllers/applicationset_controller_test.go b/applicationset/controllers/applicationset_controller_test.go index dd934cc7dd53f..9fdc8b0434879 100644 --- a/applicationset/controllers/applicationset_controller_test.go +++ b/applicationset/controllers/applicationset_controller_test.go @@ -373,6 +373,7 @@ func TestCreateOrUpdateInCluster(t *testing.T) { Namespace: "namespace", ResourceVersion: "1", }, + Spec: v1alpha1.ApplicationSpec{Project: "default"}, }, }, }, @@ -900,6 +901,60 @@ func TestCreateOrUpdateInCluster(t *testing.T) { }, }, }, + }, { + name: "Ensure that the app spec is normalized before applying", + appSet: v1alpha1.ApplicationSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + Spec: v1alpha1.ApplicationSetSpec{ + Template: v1alpha1.ApplicationSetTemplate{ + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + Directory: &v1alpha1.ApplicationSourceDirectory{ + Jsonnet: v1alpha1.ApplicationSourceJsonnet{}, + }, + }, + }, + }, + }, + }, + desiredApps: []v1alpha1.Application{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + Directory: &v1alpha1.ApplicationSourceDirectory{ + Jsonnet: v1alpha1.ApplicationSourceJsonnet{}, + }, + }, + }, + }, + }, + expected: []v1alpha1.Application{ + { + TypeMeta: metav1.TypeMeta{ + Kind: "Application", + APIVersion: "argoproj.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "namespace", + ResourceVersion: "1", + }, + Spec: v1alpha1.ApplicationSpec{ + Project: "project", + Source: &v1alpha1.ApplicationSource{ + // Directory and jsonnet block are removed + }, + }, + }, + }, }, } { @@ -1231,13 +1286,15 @@ func TestCreateApplications(t *testing.T) { err = v1alpha1.AddToScheme(scheme) assert.Nil(t, err) - for _, c := range []struct { + testCases := []struct { + name string appSet v1alpha1.ApplicationSet existsApps []v1alpha1.Application apps []v1alpha1.Application expected []v1alpha1.Application }{ { + name: "no existing apps", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -1263,10 +1320,14 @@ func TestCreateApplications(t *testing.T) { Namespace: "namespace", ResourceVersion: "1", }, + Spec: v1alpha1.ApplicationSpec{ + Project: "default", + }, }, }, }, { + name: "existing apps", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -1324,6 +1385,7 @@ func TestCreateApplications(t *testing.T) { }, }, { + name: "existing apps with different project", appSet: v1alpha1.ApplicationSet{ ObjectMeta: metav1.ObjectMeta{ Name: "name", @@ -1380,39 +1442,42 @@ func TestCreateApplications(t *testing.T) { }, }, }, - } { - initObjs := []crtclient.Object{&c.appSet} - for _, a := range c.existsApps { - err = controllerutil.SetControllerReference(&c.appSet, &a, scheme) - assert.Nil(t, err) - initObjs = append(initObjs, &a) - } - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build() + } - r := ApplicationSetReconciler{ - Client: client, - Scheme: scheme, - Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), - } + for _, c := range testCases { + t.Run(c.name, func(t *testing.T) { + initObjs := []crtclient.Object{&c.appSet} + for _, a := range c.existsApps { + err = controllerutil.SetControllerReference(&c.appSet, &a, scheme) + assert.Nil(t, err) + initObjs = append(initObjs, &a) + } - err = r.createInCluster(context.TODO(), c.appSet, c.apps) - assert.Nil(t, err) + client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjs...).Build() - for _, obj := range c.expected { - got := &v1alpha1.Application{} - _ = client.Get(context.Background(), crtclient.ObjectKey{ - Namespace: obj.Namespace, - Name: obj.Name, - }, got) + r := ApplicationSetReconciler{ + Client: client, + Scheme: scheme, + Recorder: record.NewFakeRecorder(len(initObjs) + len(c.expected)), + } - err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) + err = r.createInCluster(context.TODO(), c.appSet, c.apps) assert.Nil(t, err) - assert.Equal(t, obj, *got) - } - } + for _, obj := range c.expected { + got := &v1alpha1.Application{} + _ = client.Get(context.Background(), crtclient.ObjectKey{ + Namespace: obj.Namespace, + Name: obj.Name, + }, got) + err = controllerutil.SetControllerReference(&c.appSet, &obj, r.Scheme) + assert.Nil(t, err) + + assert.Equal(t, obj, *got) + } + }) + } } func TestDeleteInCluster(t *testing.T) { diff --git a/util/argo/argo.go b/util/argo/argo.go index a91b64a1e51d1..9b08d3aeeb847 100644 --- a/util/argo/argo.go +++ b/util/argo/argo.go @@ -856,7 +856,8 @@ func NormalizeApplicationSpec(spec *argoappv1.ApplicationSpec) *argoappv1.Applic for _, source := range spec.Sources { NormalizeSource(&source) } - } else { + } else if spec.Source != nil { + // In practice, spec.Source should never be nil. NormalizeSource(spec.Source) } return spec