From 6c6f0d4963a5c85f898a7bed1ae1d8ac9ccd18ca Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 3 Jun 2019 16:03:38 -0700 Subject: [PATCH 01/30] Adds the option to ignore resources when calculating sync status --- controller/state.go | 5 +++- test/e2e/kustomize_test.go | 30 +++++++++++++++++++ .../kustomize-cm-gen/kustomization.yaml | 8 +++++ util/resources/annotations.go | 17 +++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 test/e2e/kustomize_test.go create mode 100644 test/e2e/testdata/kustomize-cm-gen/kustomization.yaml create mode 100644 util/resources/annotations.go diff --git a/controller/state.go b/controller/state.go index 3a66e2de237c4..2e96b3f9bbaa1 100644 --- a/controller/state.go +++ b/controller/state.go @@ -27,6 +27,7 @@ import ( "github.com/argoproj/argo-cd/util/health" hookutil "github.com/argoproj/argo-cd/util/hook" kubeutil "github.com/argoproj/argo-cd/util/kube" + "github.com/argoproj/argo-cd/util/resources" "github.com/argoproj/argo-cd/util/settings" ) @@ -313,7 +314,9 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st } diffResult := diffResults.Diffs[i] - if resState.Hook { + if resources.HasAnnotationOption(obj,"argocd.argoproj.io/sync-status-options","Ignore") { + // ignore annotated resources + } else if resState.Hook { // For resource hooks, don't store sync status, and do not affect overall sync status } else if diffResult.Modified || targetObjs[i] == nil || managedLiveObj[i] == nil { // Set resource state to OutOfSync since one of the following is true: diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go new file mode 100644 index 0000000000000..6e40f5e54c393 --- /dev/null +++ b/test/e2e/kustomize_test.go @@ -0,0 +1,30 @@ +package e2e; + +import ( + "testing" + + . "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" + . "github.com/argoproj/argo-cd/test/e2e/fixture/app" +) + +// when we have a config map generator, AND the ignore annotation, it is ignored in the app's sync status +func TestSyncStatusOptionIgnore(t *testing.T) { + Given(t). + Path("kustomize-cm-gen"). + When(). + Create(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(HealthStatusHealthy)). + When(). + // we now force generation of a second CM + Patch("kustomization.yaml", `[{"op": "replace", "path": "/configMapGenerator/0/literals/0", "value": "foo=baz"}]`). + Refresh(RefreshTypeHard). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(HealthStatusHealthy)) +} diff --git a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml new file mode 100644 index 0000000000000..a9a3213a90337 --- /dev/null +++ b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml @@ -0,0 +1,8 @@ +kind: Kustomization +generatorOptions: + annotations: + argocd.argoproj.io/sync-status-options: Ignore +configMapGenerator: + - name: my-map + literals: + - foo=bar diff --git a/util/resources/annotations.go b/util/resources/annotations.go new file mode 100644 index 0000000000000..dbac868f020d5 --- /dev/null +++ b/util/resources/annotations.go @@ -0,0 +1,17 @@ +package resources + +import ( + "strings" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func HasAnnotationOption(obj *unstructured.Unstructured, key, val string) bool { + for _, item := range strings.Split(obj.GetAnnotations()[key], ",") { + if strings.TrimSpace(item) == val { + return true + } + } + return false +} + From 8aa745d5e52a6958a036dae90d8b4bf478f34373 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 3 Jun 2019 16:19:38 -0700 Subject: [PATCH 02/30] lint --- controller/state.go | 2 +- test/e2e/kustomize_test.go | 2 +- util/resources/annotations.go | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/controller/state.go b/controller/state.go index 2e96b3f9bbaa1..4c3f04fbc2af5 100644 --- a/controller/state.go +++ b/controller/state.go @@ -314,7 +314,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st } diffResult := diffResults.Diffs[i] - if resources.HasAnnotationOption(obj,"argocd.argoproj.io/sync-status-options","Ignore") { + if resources.HasAnnotationOption(obj, "argocd.argoproj.io/sync-status-options", "Ignore") { // ignore annotated resources } else if resState.Hook { // For resource hooks, don't store sync status, and do not affect overall sync status diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index 6e40f5e54c393..58563f5c23c35 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -1,4 +1,4 @@ -package e2e; +package e2e import ( "testing" diff --git a/util/resources/annotations.go b/util/resources/annotations.go index dbac868f020d5..ec45c4f891fdf 100644 --- a/util/resources/annotations.go +++ b/util/resources/annotations.go @@ -14,4 +14,3 @@ func HasAnnotationOption(obj *unstructured.Unstructured, key, val string) bool { } return false } - From 7ba87a9f18f5a46558ec469ea472afe40e30608e Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 3 Jun 2019 17:04:28 -0700 Subject: [PATCH 03/30] ""github.com/argoproj/argo-cd/util/resources"..." to state.go --- controller/state.go | 4 +- util/{resources => resource}/annotations.go | 2 +- util/resource/annotations_test.go | 41 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) rename util/{resources => resource}/annotations.go (94%) create mode 100644 util/resource/annotations_test.go diff --git a/controller/state.go b/controller/state.go index 4c3f04fbc2af5..bb7c626d56c3a 100644 --- a/controller/state.go +++ b/controller/state.go @@ -27,7 +27,7 @@ import ( "github.com/argoproj/argo-cd/util/health" hookutil "github.com/argoproj/argo-cd/util/hook" kubeutil "github.com/argoproj/argo-cd/util/kube" - "github.com/argoproj/argo-cd/util/resources" + "github.com/argoproj/argo-cd/util/resource" "github.com/argoproj/argo-cd/util/settings" ) @@ -314,7 +314,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st } diffResult := diffResults.Diffs[i] - if resources.HasAnnotationOption(obj, "argocd.argoproj.io/sync-status-options", "Ignore") { + if resource.HasAnnotationOption(obj, "argocd.argoproj.io/sync-status-options", "Ignore") { // ignore annotated resources } else if resState.Hook { // For resource hooks, don't store sync status, and do not affect overall sync status diff --git a/util/resources/annotations.go b/util/resource/annotations.go similarity index 94% rename from util/resources/annotations.go rename to util/resource/annotations.go index ec45c4f891fdf..0b6018f4dfa02 100644 --- a/util/resources/annotations.go +++ b/util/resource/annotations.go @@ -1,4 +1,4 @@ -package resources +package resource import ( "strings" diff --git a/util/resource/annotations_test.go b/util/resource/annotations_test.go new file mode 100644 index 0000000000000..1476d76481069 --- /dev/null +++ b/util/resource/annotations_test.go @@ -0,0 +1,41 @@ +package resource + +import ( + "testing" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + "github.com/argoproj/argo-cd/test" +) + +func TestHasAnnotationOption(t *testing.T) { + type args struct { + obj *unstructured.Unstructured + key string + val string + } + tests := []struct { + name string + args args + want bool + }{ + {"Nil", args{test.NewPod(), "foo", "bar"},false}, + {"Empty", args{example(""), "foo", "bar"},false}, + {"Single", args{example("bar"), "foo", "bar"}, true}, + {"Double", args{example("bar,baz"), "foo", "baz"}, true}, + {"Spaces", args{example("bar "), "foo", "bar"}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := HasAnnotationOption(tt.args.obj, tt.args.key, tt.args.val); got != tt.want { + t.Errorf("HasAnnotationOption() = %v, want %v", got, tt.want) + } + }) + } +} + +func example(val string) *unstructured.Unstructured { + obj := test.NewPod() + obj.SetAnnotations(map[string]string {"foo": val}) + return obj +} From f4c7908eea9f1c60bf1c559690229403ab3d4365 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 3 Jun 2019 17:13:02 -0700 Subject: [PATCH 04/30] "// AnnotationSyncStatusOptions is a comma-separate..." to common.go --- common/common.go | 2 ++ controller/state.go | 2 +- util/resource/annotations_test.go | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common/common.go b/common/common.go index 95c47406d2158..8e2542a4fe4e9 100644 --- a/common/common.go +++ b/common/common.go @@ -96,6 +96,8 @@ const ( AnnotationKeyHelmHook = "helm.sh/hook" // AnnotationValueHelmHookCRDInstall is a value of crd helm hook AnnotationValueHelmHookCRDInstall = "crd-install" + // AnnotationSyncStatusOptions is a comma-separated list of options + AnnotationSyncStatusOptions = "argocd.argoproj.io/sync-status-options" // ResourcesFinalizerName the finalizer value which we inject to finalize deletion of an application ResourcesFinalizerName = "resources-finalizer.argocd.argoproj.io" ) diff --git a/controller/state.go b/controller/state.go index bb7c626d56c3a..5974f477e5c23 100644 --- a/controller/state.go +++ b/controller/state.go @@ -314,7 +314,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st } diffResult := diffResults.Diffs[i] - if resource.HasAnnotationOption(obj, "argocd.argoproj.io/sync-status-options", "Ignore") { + if resource.HasAnnotationOption(obj, common.AnnotationSyncStatusOptions, "Ignore") { // ignore annotated resources } else if resState.Hook { // For resource hooks, don't store sync status, and do not affect overall sync status diff --git a/util/resource/annotations_test.go b/util/resource/annotations_test.go index 1476d76481069..a437f3c9b4139 100644 --- a/util/resource/annotations_test.go +++ b/util/resource/annotations_test.go @@ -19,8 +19,8 @@ func TestHasAnnotationOption(t *testing.T) { args args want bool }{ - {"Nil", args{test.NewPod(), "foo", "bar"},false}, - {"Empty", args{example(""), "foo", "bar"},false}, + {"Nil", args{test.NewPod(), "foo", "bar"}, false}, + {"Empty", args{example(""), "foo", "bar"}, false}, {"Single", args{example("bar"), "foo", "bar"}, true}, {"Double", args{example("bar,baz"), "foo", "baz"}, true}, {"Spaces", args{example("bar "), "foo", "bar"}, true}, @@ -36,6 +36,6 @@ func TestHasAnnotationOption(t *testing.T) { func example(val string) *unstructured.Unstructured { obj := test.NewPod() - obj.SetAnnotations(map[string]string {"foo": val}) + obj.SetAnnotations(map[string]string{"foo": val}) return obj } From eed7261932df70221784ab9bc2f0eb7554196db9 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 3 Jun 2019 18:12:09 -0700 Subject: [PATCH 05/30] "testCompareAppStateIgnoreAnnotation(t, common.Anno..." to state_test.go --- controller/state_test.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/controller/state_test.go b/controller/state_test.go index d6c04d45df38f..25202e3ddcec6 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -93,8 +93,17 @@ func TestCompareAppStateExtra(t *testing.T) { // TestCompareAppStateHook checks that hooks are detected during manifest generation, and not // considered as part of resources when assessing Synced status func TestCompareAppStateHook(t *testing.T) { + testCompareAppStateIgnoreAnnotation(t, common.AnnotationKeyHook, "PreSync") +} + +// checks that ignore resources are detected, but excluded from status +func TestCompareAppStateIgnore(t *testing.T) { + testCompareAppStateIgnoreAnnotation(t, common.AnnotationSyncStatusOptions, "Ignore") +} + +func testCompareAppStateIgnoreAnnotation(t *testing.T, key, val string) { pod := test.NewPod() - pod.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync"}) + pod.SetAnnotations(map[string]string{key: val}) podBytes, _ := json.Marshal(pod) app := newFakeApp() data := fakeData{ From c72752ac07210d1309b9ace8115cc5569dd1a105 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Mon, 3 Jun 2019 18:49:13 -0700 Subject: [PATCH 06/30] "if resource.HasAnnotationOption(obj, common.Annota..." to state.go --- controller/state.go | 10 +++-- controller/state_test.go | 37 ++++++++++++++----- docs/user-guide/kustomize.md | 5 ++- docs/user-guide/sync_status_options.md | 27 ++++++++++++++ mkdocs.yml | 1 + test/e2e/kustomize_test.go | 19 +++++++++- .../kustomize-cm-gen/kustomization.yaml | 8 ++-- 7 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 docs/user-guide/sync_status_options.md diff --git a/controller/state.go b/controller/state.go index 5974f477e5c23..9ff67cbbff220 100644 --- a/controller/state.go +++ b/controller/state.go @@ -314,9 +314,8 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st } diffResult := diffResults.Diffs[i] - if resource.HasAnnotationOption(obj, common.AnnotationSyncStatusOptions, "Ignore") { - // ignore annotated resources - } else if resState.Hook { + + if resState.Hook { // For resource hooks, don't store sync status, and do not affect overall sync status } else if diffResult.Modified || targetObjs[i] == nil || managedLiveObj[i] == nil { // Set resource state to OutOfSync since one of the following is true: @@ -324,7 +323,10 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st // * target resource not defined and live resource is extra // * target resource present but live resource is missing resState.Status = v1alpha1.SyncStatusCodeOutOfSync - syncCode = v1alpha1.SyncStatusCodeOutOfSync + // we don't apply to the application + if !resource.HasAnnotationOption(obj, common.AnnotationSyncStatusOptions, "Ignore") { + syncCode = v1alpha1.SyncStatusCodeOutOfSync + } } else { resState.Status = v1alpha1.SyncStatusCodeSynced } diff --git a/controller/state_test.go b/controller/state_test.go index 25202e3ddcec6..5ac898db29f7c 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -93,17 +93,34 @@ func TestCompareAppStateExtra(t *testing.T) { // TestCompareAppStateHook checks that hooks are detected during manifest generation, and not // considered as part of resources when assessing Synced status func TestCompareAppStateHook(t *testing.T) { - testCompareAppStateIgnoreAnnotation(t, common.AnnotationKeyHook, "PreSync") + pod := test.NewPod() + pod.SetAnnotations(map[string]string{common.AnnotationKeyHook: "PreSync"}) + podBytes, _ := json.Marshal(pod) + app := newFakeApp() + data := fakeData{ + apps: []runtime.Object{app}, + manifestResponse: &repository.ManifestResponse{ + Manifests: []string{string(podBytes)}, + Namespace: test.FakeDestNamespace, + Server: test.FakeClusterURL, + Revision: "abc123", + }, + managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), + } + ctrl := newFakeController(&data) + compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false) + assert.NoError(t, err) + assert.NotNil(t, compRes) + assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) + assert.Equal(t, 0, len(compRes.resources)) + assert.Equal(t, 0, len(compRes.managedResources)) + assert.Equal(t, 0, len(compRes.conditions)) } // checks that ignore resources are detected, but excluded from status -func TestCompareAppStateIgnore(t *testing.T) { - testCompareAppStateIgnoreAnnotation(t, common.AnnotationSyncStatusOptions, "Ignore") -} - -func testCompareAppStateIgnoreAnnotation(t *testing.T, key, val string) { +func TestCompareAppStateSyncOptionIgnore(t *testing.T) { pod := test.NewPod() - pod.SetAnnotations(map[string]string{key: val}) + pod.SetAnnotations(map[string]string{common.AnnotationSyncStatusOptions: "Ignore"}) podBytes, _ := json.Marshal(pod) app := newFakeApp() data := fakeData{ @@ -117,12 +134,14 @@ func testCompareAppStateIgnoreAnnotation(t *testing.T, key, val string) { managedLiveObjs: make(map[kube.ResourceKey]*unstructured.Unstructured), } ctrl := newFakeController(&data) + compRes, err := ctrl.appStateManager.CompareAppState(app, "", app.Spec.Source, false) + assert.NoError(t, err) assert.NotNil(t, compRes) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) - assert.Equal(t, 0, len(compRes.resources)) - assert.Equal(t, 0, len(compRes.managedResources)) + assert.Equal(t, []argoappv1.ResourceStatus{{Version: "v1", Kind: "Pod", Name: "my-pod", Namespace: test.FakeDestNamespace, Status: argoappv1.SyncStatusCodeOutOfSync, Health: &argoappv1.HealthStatus{Status: argoappv1.HealthStatusMissing}}}, compRes.resources) + assert.Equal(t, 1, len(compRes.managedResources)) assert.Equal(t, 0, len(compRes.conditions)) } diff --git a/docs/user-guide/kustomize.md b/docs/user-guide/kustomize.md index be6f485ec0877..0a5e3d207fb04 100644 --- a/docs/user-guide/kustomize.md +++ b/docs/user-guide/kustomize.md @@ -10,4 +10,7 @@ You have three configuration options for Kustomize: * `imageTags` is a list of Kustomize 1.0 image tag overrides * `images` is a list of Kustomize 2.0 image overrides -To use Kustomize with an overlay, point your path to the overlay. \ No newline at end of file +To use Kustomize with an overlay, point your path to the overlay. + +!!! tip + If you're generating resources, you should read up how to ignore those generated resources using [sync status options](sync_status_options.md). \ No newline at end of file diff --git a/docs/user-guide/sync_status_options.md b/docs/user-guide/sync_status_options.md new file mode 100644 index 0000000000000..513aa9f2a546d --- /dev/null +++ b/docs/user-guide/sync_status_options.md @@ -0,0 +1,27 @@ +# Sync Status Options + +## Ignoring Resources + +You may wish to exclude resources for the app's overall sync status under certain circumstances, for example if they are generated by the tool. This can be done by adding an annotation: + +```yaml +metadata: + annotations: + argocd.argoproj.io/sync-status-options: Ignore +``` + +!!! note + This only affect the sync status. If the resource's health is degraded, then the app will also be degraded. + +Kustomize has a feature that allows you to generate config maps. You can set `generatorOptions` to add this annotation so that your app remains in sync: + +```yaml +configMapGenerator: + - name: my-map + literals: + - foo=bar +generatorOptions: + annotations: + argocd.argoproj.io/sync-status-options: Ignore +kind: Kustomization +``` \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 650d12ae9b5e8..e71a6a764ca35 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -46,6 +46,7 @@ nav: - user-guide/private-repositories.md - user-guide/auto_sync.md - user-guide/diffing.md + - user-guide/sync_status_options.md - user-guide/parameters.md - user-guide/tracking_strategies.md - user-guide/resource_hooks.md diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index 58563f5c23c35..435abc9eb3fae 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -3,12 +3,15 @@ package e2e import ( "testing" + "github.com/stretchr/testify/assert" + . "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" . "github.com/argoproj/argo-cd/test/e2e/fixture/app" ) // when we have a config map generator, AND the ignore annotation, it is ignored in the app's sync status func TestSyncStatusOptionIgnore(t *testing.T) { + var mapName string Given(t). Path("kustomize-cm-gen"). When(). @@ -18,6 +21,13 @@ func TestSyncStatusOptionIgnore(t *testing.T) { Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(HealthStatusHealthy)). + And(func(app *Application) { + resourceStatus := app.Status.Resources[0] + assert.Contains(t, resourceStatus.Name, "my-map-") + assert.Equal(t, SyncStatusCodeSynced, resourceStatus.Status) + + mapName = resourceStatus.Name + }). When(). // we now force generation of a second CM Patch("kustomization.yaml", `[{"op": "replace", "path": "/configMapGenerator/0/literals/0", "value": "foo=baz"}]`). @@ -26,5 +36,12 @@ func TestSyncStatusOptionIgnore(t *testing.T) { Then(). Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). - Expect(HealthIs(HealthStatusHealthy)) + Expect(HealthIs(HealthStatusHealthy)). + And(func(app *Application) { + resourceStatus := app.Status.Resources[0] + assert.Contains(t, resourceStatus.Name, "my-map-") + // make sure we've a new map with changed name + assert.NotEqual(t, mapName, resourceStatus.Name) + assert.Equal(t, SyncStatusCodeSynced, resourceStatus.Status) + }) } diff --git a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml index a9a3213a90337..c49ef656f11c7 100644 --- a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml +++ b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml @@ -1,8 +1,8 @@ -kind: Kustomization -generatorOptions: - annotations: - argocd.argoproj.io/sync-status-options: Ignore configMapGenerator: - name: my-map literals: - foo=bar +generatorOptions: + annotations: + argocd.argoproj.io/sync-status-options: Ignore +kind: Kustomization From b59f516c673d7d6d18ab261627be1deaf5d6ab8b Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 13:43:57 -0700 Subject: [PATCH 07/30] "Kustomize has a feature that allows you to generat..." to sync_status_options.md --- docs/user-guide/sync_status_options.md | 7 +++++-- test/e2e/fixture/app/actions.go | 6 +++++- test/e2e/fixture/app/context.go | 8 +++++++- test/e2e/kustomize_test.go | 22 +++++++++++++++++----- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/docs/user-guide/sync_status_options.md b/docs/user-guide/sync_status_options.md index 513aa9f2a546d..373042c52580a 100644 --- a/docs/user-guide/sync_status_options.md +++ b/docs/user-guide/sync_status_options.md @@ -13,7 +13,7 @@ metadata: !!! note This only affect the sync status. If the resource's health is degraded, then the app will also be degraded. -Kustomize has a feature that allows you to generate config maps. You can set `generatorOptions` to add this annotation so that your app remains in sync: +Kustomize has a feature that allows you to generate config maps ([read more ⧉](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/configGeneration.md)). You can set `generatorOptions` to add this annotation so that your app remains in sync: ```yaml configMapGenerator: @@ -24,4 +24,7 @@ generatorOptions: annotations: argocd.argoproj.io/sync-status-options: Ignore kind: Kustomization -``` \ No newline at end of file +``` + +!!! note + `generatorOptions` adds annotations to both config maps and secrets ([read more ⧉](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/generatorOptions.md)). \ No newline at end of file diff --git a/test/e2e/fixture/app/actions.go b/test/e2e/fixture/app/actions.go index b33d262917341..4600d75ad4186 100644 --- a/test/e2e/fixture/app/actions.go +++ b/test/e2e/fixture/app/actions.go @@ -39,7 +39,11 @@ func (a *Actions) Create() *Actions { } func (a *Actions) Sync() *Actions { - a.runCli("app", "sync", a.context.name, "--timeout", "5", "--prune") + args := []string{"app", "sync", a.context.name, "--timeout", "5"} + if a.context.prune { + args = append(args, "--prune") + } + a.runCli(args...) return a } diff --git a/test/e2e/fixture/app/context.go b/test/e2e/fixture/app/context.go index cda0c823dcfce..f8949e81efef7 100644 --- a/test/e2e/fixture/app/context.go +++ b/test/e2e/fixture/app/context.go @@ -15,11 +15,12 @@ type Context struct { destServer string env string parameters []string + prune bool } func Given(t *testing.T) *Context { fixture.EnsureCleanState() - return &Context{t: t, destServer: KubernetesInternalAPIServerAddr, name: fixture.Name()} + return &Context{t: t, destServer: KubernetesInternalAPIServerAddr, name: fixture.Name(), prune:true} } func (c *Context) Repo(url string) *Context { @@ -47,6 +48,11 @@ func (c *Context) Parameter(parameter string) *Context { return c } +func (c *Context) Prune(prune bool) *Context{ + c.prune= prune + return c +} + func (c *Context) And(block func()) *Context { block() return c diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index 435abc9eb3fae..90eb36f5e075d 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -14,6 +14,8 @@ func TestSyncStatusOptionIgnore(t *testing.T) { var mapName string Given(t). Path("kustomize-cm-gen"). + // note we don't want to prune resources, check the config maps exist + Prune(false). When(). Create(). Sync(). @@ -38,10 +40,20 @@ func TestSyncStatusOptionIgnore(t *testing.T) { Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(HealthStatusHealthy)). And(func(app *Application) { - resourceStatus := app.Status.Resources[0] - assert.Contains(t, resourceStatus.Name, "my-map-") - // make sure we've a new map with changed name - assert.NotEqual(t, mapName, resourceStatus.Name) - assert.Equal(t, SyncStatusCodeSynced, resourceStatus.Status) + assert.Equal(t, 2, len(app.Status.Resources)) + // new map in-sync + { + resourceStatus := app.Status.Resources[0] + assert.Contains(t, resourceStatus.Name, "my-map-") + // make sure we've a new map with changed name + assert.NotEqual(t, mapName, resourceStatus.Name) + assert.Equal(t, SyncStatusCodeSynced, resourceStatus.Status) + } + // old map is out of sync + { + resourceStatus := app.Status.Resources[1] + assert.Equal(t, mapName, resourceStatus.Name) + assert.Equal(t, SyncStatusCodeOutOfSync, resourceStatus.Status) + } }) } From 46eb1939ff27fbe4afa061a0ee3e9e0dc3a06a7a Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 13:47:04 -0700 Subject: [PATCH 08/30] "return &Context{t: t, destServer: KubernetesIntern..." to context.go --- test/e2e/fixture/app/context.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/e2e/fixture/app/context.go b/test/e2e/fixture/app/context.go index f8949e81efef7..cd63e7dfde3fb 100644 --- a/test/e2e/fixture/app/context.go +++ b/test/e2e/fixture/app/context.go @@ -20,7 +20,7 @@ type Context struct { func Given(t *testing.T) *Context { fixture.EnsureCleanState() - return &Context{t: t, destServer: KubernetesInternalAPIServerAddr, name: fixture.Name(), prune:true} + return &Context{t: t, destServer: KubernetesInternalAPIServerAddr, name: fixture.Name(), prune: true} } func (c *Context) Repo(url string) *Context { @@ -48,8 +48,8 @@ func (c *Context) Parameter(parameter string) *Context { return c } -func (c *Context) Prune(prune bool) *Context{ - c.prune= prune +func (c *Context) Prune(prune bool) *Context { + c.prune = prune return c } From 3f037d20a390e3cd4d19cbbfaab78d08d04bbc2d Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 18:29:59 -0700 Subject: [PATCH 09/30] "func (c *Context) Prune(prune bool) *Context {..." to context.go --- test/e2e/fixture/app/context.go | 5 ----- test/e2e/kustomize_test.go | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/test/e2e/fixture/app/context.go b/test/e2e/fixture/app/context.go index 79832acfcb822..d7e39b72a2c96 100644 --- a/test/e2e/fixture/app/context.go +++ b/test/e2e/fixture/app/context.go @@ -74,8 +74,3 @@ func (c *Context) And(block func()) *Context { func (c *Context) When() *Actions { return &Actions{context: c} } - -func (c *Context) Prune(prune bool) *Context { - c.prune = prune - return c -} diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index 1288057c90413..d5303793c1536 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -76,7 +76,7 @@ func TestSyncStatusOptionIgnore(t *testing.T) { }). When(). // we now force generation of a second CM - Patch("kustomization.yaml", `[{"op": "replace", "path": "/configMapGenerator/0/literals/0", "value": "foo=baz"}]`). + PatchFile("kustomization.yaml", `[{"op": "replace", "path": "/configMapGenerator/0/literals/0", "value": "foo=baz"}]`). Refresh(RefreshTypeHard). Sync(). Then(). From 0381c016403f9fe65a61b9b723293fa34b720afd Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 19:18:24 -0700 Subject: [PATCH 10/30] Adds NoPrune option --- common/common.go | 6 ++++-- controller/sync.go | 10 +++++++--- controller/sync_test.go | 20 ++++++++++++++++++++ test/e2e/app_management_test.go | 21 +++++++++++++++++++++ 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/common/common.go b/common/common.go index 8d2fc20aca421..fde2ec3790a30 100644 --- a/common/common.go +++ b/common/common.go @@ -81,6 +81,10 @@ const ( // LabelValueSecretTypeCluster indicates a secret type of cluster LabelValueSecretTypeCluster = "cluster" + // AnnotationSyncStatusOptions is a comma-separated list of options + AnnotationSyncStatusOptions = "argocd.argoproj.io/sync-status-options" + // AnnotationSyncOptions contains sync options + AnnotationSyncOptions = "argocd.argoproj.io/sync-options" // AnnotationSyncWave indicates which wave of the sync the resource or hook should be in AnnotationSyncWave = "argocd.argoproj.io/sync-wave" // AnnotationKeyHook contains the hook type of a resource @@ -98,8 +102,6 @@ const ( AnnotationKeyHelmHook = "helm.sh/hook" // AnnotationValueHelmHookCRDInstall is a value of crd helm hook AnnotationValueHelmHookCRDInstall = "crd-install" - // AnnotationSyncStatusOptions is a comma-separated list of options - AnnotationSyncStatusOptions = "argocd.argoproj.io/sync-status-options" // ResourcesFinalizerName the finalizer value which we inject to finalize deletion of an application ResourcesFinalizerName = "resources-finalizer.argocd.argoproj.io" ) diff --git a/controller/sync.go b/controller/sync.go index f6e7ca065b655..4f4e5d646a06c 100644 --- a/controller/sync.go +++ b/controller/sync.go @@ -16,6 +16,7 @@ import ( "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" + "github.com/argoproj/argo-cd/common" "github.com/argoproj/argo-cd/controller/metrics" "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" listersv1alpha1 "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1" @@ -23,6 +24,7 @@ import ( "github.com/argoproj/argo-cd/util/health" "github.com/argoproj/argo-cd/util/hook" "github.com/argoproj/argo-cd/util/kube" + "github.com/argoproj/argo-cd/util/resource" ) type syncContext struct { @@ -473,7 +475,11 @@ func (sc *syncContext) applyObject(targetObj *unstructured.Unstructured, dryRun // pruneObject deletes the object if both prune is true and dryRun is false. Otherwise appropriate message func (sc *syncContext) pruneObject(liveObj *unstructured.Unstructured, prune, dryRun bool) (v1alpha1.ResultCode, string) { - if prune { + if !prune { + return v1alpha1.ResultCodePruneSkipped, "ignored (requires pruning)" + } else if resource.HasAnnotationOption(liveObj, common.AnnotationSyncOptions, "NoPrune") { + return v1alpha1.ResultCodePruneSkipped, "ignored (no prune)" + } else { if dryRun { return v1alpha1.ResultCodePruned, "pruned (dry run)" } else { @@ -487,8 +493,6 @@ func (sc *syncContext) pruneObject(liveObj *unstructured.Unstructured, prune, dr } return v1alpha1.ResultCodePruned, "pruned" } - } else { - return v1alpha1.ResultCodePruneSkipped, "ignored (requires pruning)" } } diff --git a/controller/sync_test.go b/controller/sync_test.go index 202130777055f..90ecef1e9f27b 100644 --- a/controller/sync_test.go +++ b/controller/sync_test.go @@ -305,6 +305,26 @@ func TestDontSyncOrPruneHooks(t *testing.T) { assert.Equal(t, v1alpha1.OperationSucceeded, syncCtx.opState.Phase) } +// make sure that we do not prune resources with NoPrune +func TestDontPruneNoPrune(t *testing.T) { + syncCtx := newTestSyncCtx() + pod := test.NewPod() + pod.SetAnnotations(map[string]string{common.AnnotationSyncOptions: "NoPrune"}) + pod.SetNamespace(test.FakeArgoCDNamespace) + syncCtx.compareResult = &comparisonResult{managedResources: []managedResource{{Live: pod}}} + + syncCtx.sync() + + assert.Equal(t, v1alpha1.OperationRunning, syncCtx.opState.Phase) + assert.Len(t, syncCtx.syncRes.Resources, 1) + assert.Equal(t, v1alpha1.ResultCodePruneSkipped, syncCtx.syncRes.Resources[0].Status) + assert.Equal(t, "ignored (no prune)", syncCtx.syncRes.Resources[0].Message) + + syncCtx.sync() + + assert.Equal(t, v1alpha1.OperationSucceeded, syncCtx.opState.Phase) +} + func TestSelectiveSyncOnly(t *testing.T) { syncCtx := newTestSyncCtx() pod1 := test.NewPod() diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 10f800843282e..4203dcedf10ac 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -504,3 +504,24 @@ func TestPermissions(t *testing.T) { assert.True(t, destinationErrorExist) assert.True(t, sourceErrorExist) } + +// make sure that if we deleted a resource from the app, it is not pruned if annotated with NoPrune +func TestSyncOptionNoPrune(t *testing.T) { + Given(t). + Path("two-nice-pods"). + When(). + PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "NoPrune"}}]`). + Create(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)). + When(). + DeleteFile("pod-1.yaml"). + Refresh(RefreshTypeHard). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeOutOfSync)). + Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeOutOfSync)) +} From f7b3b525b01456326cc52a943fa3f3fb7f52c2a0 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 19:49:33 -0700 Subject: [PATCH 11/30] "AnnotationSyncStatusOptions = "argocd.argoproj.io/..." to common.go --- common/common.go | 2 +- ...{sync_status_options.md => compare-options.md} | 6 +++--- docs/user-guide/sync-options.md | 15 +++++++++++++++ mkdocs.yml | 3 ++- .../testdata/kustomize-cm-gen/kustomization.yaml | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) rename docs/user-guide/{sync_status_options.md => compare-options.md} (88%) create mode 100644 docs/user-guide/sync-options.md diff --git a/common/common.go b/common/common.go index fde2ec3790a30..b595217f25b23 100644 --- a/common/common.go +++ b/common/common.go @@ -82,7 +82,7 @@ const ( LabelValueSecretTypeCluster = "cluster" // AnnotationSyncStatusOptions is a comma-separated list of options - AnnotationSyncStatusOptions = "argocd.argoproj.io/sync-status-options" + AnnotationSyncStatusOptions = "argocd.argoproj.io/compare-options" // AnnotationSyncOptions contains sync options AnnotationSyncOptions = "argocd.argoproj.io/sync-options" // AnnotationSyncWave indicates which wave of the sync the resource or hook should be in diff --git a/docs/user-guide/sync_status_options.md b/docs/user-guide/compare-options.md similarity index 88% rename from docs/user-guide/sync_status_options.md rename to docs/user-guide/compare-options.md index 373042c52580a..138cb64b4db0b 100644 --- a/docs/user-guide/sync_status_options.md +++ b/docs/user-guide/compare-options.md @@ -1,4 +1,4 @@ -# Sync Status Options +# Compare Options ## Ignoring Resources @@ -7,7 +7,7 @@ You may wish to exclude resources for the app's overall sync status under certai ```yaml metadata: annotations: - argocd.argoproj.io/sync-status-options: Ignore + argocd.argoproj.io/compare-options: Ignore ``` !!! note @@ -22,7 +22,7 @@ configMapGenerator: - foo=bar generatorOptions: annotations: - argocd.argoproj.io/sync-status-options: Ignore + argocd.argoproj.io/compare-options: Ignore kind: Kustomization ``` diff --git a/docs/user-guide/sync-options.md b/docs/user-guide/sync-options.md new file mode 100644 index 0000000000000..745aca513c35d --- /dev/null +++ b/docs/user-guide/sync-options.md @@ -0,0 +1,15 @@ +# Sync Options + +## No Prune Resources + +You may wish to prevent an object from being pruned: + +```yaml +metadata: + annotations: + argocd.argoproj.io/sync-options: NoPrune +``` + +!!! note + The app will be out of sync if Argo CD expects a resource to be pruned. You may wish to use this along with [compare options](compare-options.md). + diff --git a/mkdocs.yml b/mkdocs.yml index c62c1f28a1a26..8d2942b6b53ee 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -46,7 +46,8 @@ nav: - user-guide/private-repositories.md - user-guide/auto_sync.md - user-guide/diffing.md - - user-guide/sync_status_options.md + - user-guide/compare-options.md + - user-guide/sync-options.md - user-guide/parameters.md - user-guide/tracking_strategies.md - user-guide/resource_hooks.md diff --git a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml index c49ef656f11c7..7f88c4816c264 100644 --- a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml +++ b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml @@ -4,5 +4,5 @@ configMapGenerator: - foo=bar generatorOptions: annotations: - argocd.argoproj.io/sync-status-options: Ignore + argocd.argoproj.io/compare-options: Ignore kind: Kustomization From 6c9dc19f556a839394a3347be57db82eda4b3837 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 20:02:11 -0700 Subject: [PATCH 12/30] "// AnnotationSyncStatusOptions is a comma-separate..." to common.go --- common/common.go | 6 +++--- controller/state.go | 16 +++++++++------- controller/state_test.go | 4 ++-- docs/user-guide/compare-options.md | 6 +++--- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/common/common.go b/common/common.go index b595217f25b23..0b02807c29ba8 100644 --- a/common/common.go +++ b/common/common.go @@ -81,9 +81,9 @@ const ( // LabelValueSecretTypeCluster indicates a secret type of cluster LabelValueSecretTypeCluster = "cluster" - // AnnotationSyncStatusOptions is a comma-separated list of options - AnnotationSyncStatusOptions = "argocd.argoproj.io/compare-options" - // AnnotationSyncOptions contains sync options + // AnnotationCompareOptions is a comma-separated list of options for comparison + AnnotationCompareOptions = "argocd.argoproj.io/compare-options" + // AnnotationSyncOptions is a comma-separated list of options for syncing AnnotationSyncOptions = "argocd.argoproj.io/sync-options" // AnnotationSyncWave indicates which wave of the sync the resource or hook should be in AnnotationSyncWave = "argocd.argoproj.io/sync-wave" diff --git a/controller/state.go b/controller/state.go index cd851d055e77e..a3620f3cf2a48 100644 --- a/controller/state.go +++ b/controller/state.go @@ -297,10 +297,11 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st syncCode := v1alpha1.SyncStatusCodeSynced managedResources := make([]managedResource, len(targetObjs)) resourceSummaries := make([]v1alpha1.ResourceStatus, len(targetObjs)) - for i := 0; i < len(targetObjs); i++ { - obj := managedLiveObj[i] + for i, targetObj := range targetObjs { + liveObj := managedLiveObj[i] + obj := liveObj if obj == nil { - obj = targetObjs[i] + obj = targetObj } if obj == nil { continue @@ -319,14 +320,15 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st diffResult := diffResults.Diffs[i] if resState.Hook || resource.Ignore(obj) { // For resource hooks, don't store sync status, and do not affect overall sync status - } else if diffResult.Modified || targetObjs[i] == nil || managedLiveObj[i] == nil { + } else if diffResult.Modified || targetObj == nil || liveObj == nil { // Set resource state to OutOfSync since one of the following is true: // * target and live resource are different // * target resource not defined and live resource is extra // * target resource present but live resource is missing resState.Status = v1alpha1.SyncStatusCodeOutOfSync // we don't apply to the application - if !resource.HasAnnotationOption(obj, common.AnnotationSyncStatusOptions, "Ignore") { + needsPruning := targetObj == nil && liveObj != nil + if !(needsPruning && resource.HasAnnotationOption(obj, common.AnnotationCompareOptions, "IgnoreNeedsPruning")) { syncCode = v1alpha1.SyncStatusCodeOutOfSync } } else { @@ -338,8 +340,8 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st Group: resState.Group, Kind: resState.Kind, Version: resState.Version, - Live: managedLiveObj[i], - Target: targetObjs[i], + Live: liveObj, + Target: targetObj, Diff: diffResult, Hook: resState.Hook, } diff --git a/controller/state_test.go b/controller/state_test.go index 9718421335c8a..367e13fa037fa 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -118,9 +118,9 @@ func TestCompareAppStateHook(t *testing.T) { } // checks that ignore resources are detected, but excluded from status -func TestCompareAppStateSyncOptionIgnore(t *testing.T) { +func TestCompareAppStateSyncOptionIgnoreNeedsPruning(t *testing.T) { pod := test.NewPod() - pod.SetAnnotations(map[string]string{common.AnnotationSyncStatusOptions: "Ignore"}) + pod.SetAnnotations(map[string]string{common.AnnotationCompareOptions: "IgnoreNeedsPruning"}) podBytes, _ := json.Marshal(pod) app := newFakeApp() data := fakeData{ diff --git a/docs/user-guide/compare-options.md b/docs/user-guide/compare-options.md index 138cb64b4db0b..e8e92e47c9e21 100644 --- a/docs/user-guide/compare-options.md +++ b/docs/user-guide/compare-options.md @@ -1,13 +1,13 @@ # Compare Options -## Ignoring Resources +## Ignoring Resources That Needs Pruning You may wish to exclude resources for the app's overall sync status under certain circumstances, for example if they are generated by the tool. This can be done by adding an annotation: ```yaml metadata: annotations: - argocd.argoproj.io/compare-options: Ignore + argocd.argoproj.io/compare-options: IgnoreNeedsPruning ``` !!! note @@ -22,7 +22,7 @@ configMapGenerator: - foo=bar generatorOptions: annotations: - argocd.argoproj.io/compare-options: Ignore + argocd.argoproj.io/compare-options: IgnoreNeedsPruning kind: Kustomization ``` From d3ae6dba93a566955980890261c428aded2f9854 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 20:11:26 -0700 Subject: [PATCH 13/30] "func TestCompareAppStateSyncOptionIgnoreNeedsPruni..." to state_test.go --- controller/state_test.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/controller/state_test.go b/controller/state_test.go index 367e13fa037fa..cb827609d3eab 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -118,15 +118,14 @@ func TestCompareAppStateHook(t *testing.T) { } // checks that ignore resources are detected, but excluded from status -func TestCompareAppStateSyncOptionIgnoreNeedsPruning(t *testing.T) { +func TestCompareAppStateCompareOptionIgnoreNeedsPruning(t *testing.T) { pod := test.NewPod() pod.SetAnnotations(map[string]string{common.AnnotationCompareOptions: "IgnoreNeedsPruning"}) - podBytes, _ := json.Marshal(pod) app := newFakeApp() data := fakeData{ apps: []runtime.Object{app}, manifestResponse: &repository.ManifestResponse{ - Manifests: []string{string(podBytes)}, + Manifests: []string{}, Namespace: test.FakeDestNamespace, Server: test.FakeClusterURL, Revision: "abc123", @@ -140,9 +139,9 @@ func TestCompareAppStateSyncOptionIgnoreNeedsPruning(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, compRes) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) - assert.Equal(t, []argoappv1.ResourceStatus{{Version: "v1", Kind: "Pod", Name: "my-pod", Namespace: test.FakeDestNamespace, Status: argoappv1.SyncStatusCodeOutOfSync, Health: &argoappv1.HealthStatus{Status: argoappv1.HealthStatusMissing}}}, compRes.resources) - assert.Equal(t, 1, len(compRes.managedResources)) - assert.Equal(t, 0, len(compRes.conditions)) + assert.Len(t, compRes.resources, 0) + assert.Len(t, compRes.managedResources,0) + assert.Len(t, compRes.conditions, 0) } // TestCompareAppStateExtraHook tests when there is an extra _hook_ object in live but not defined in git From c1b03825178f6a391711e3a0e81cc0a223d71345 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 20:17:09 -0700 Subject: [PATCH 14/30] "assert.Len(t, compRes.managedResources,0)..." to state_test.go --- controller/state_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/state_test.go b/controller/state_test.go index cb827609d3eab..9d90939c40b17 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -140,7 +140,7 @@ func TestCompareAppStateCompareOptionIgnoreNeedsPruning(t *testing.T) { assert.NotNil(t, compRes) assert.Equal(t, argoappv1.SyncStatusCodeSynced, compRes.syncStatus.Status) assert.Len(t, compRes.resources, 0) - assert.Len(t, compRes.managedResources,0) + assert.Len(t, compRes.managedResources, 0) assert.Len(t, compRes.conditions, 0) } From 8b639cd68bb4d524dea32fefe163d07efd827525 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 20:40:18 -0700 Subject: [PATCH 15/30] "argocd.argoproj.io/compare-options: Ignore..." to kustomization.yaml --- test/e2e/testdata/kustomize-cm-gen/kustomization.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml index 7f88c4816c264..8b9daa0af9e3c 100644 --- a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml +++ b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml @@ -4,5 +4,5 @@ configMapGenerator: - foo=bar generatorOptions: annotations: - argocd.argoproj.io/compare-options: Ignore + argocd.argoproj.io/compare-options: IgnoreNeedsPrune kind: Kustomization From 4ed7b277d884229276c2ea5cf366ee55a3917d05 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Tue, 4 Jun 2019 21:26:15 -0700 Subject: [PATCH 16/30] "repoUrl = fmt.Sprintf("file:///%s", repoDirectory(..." to fixture.go --- test/e2e/fixture/fixture.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/fixture/fixture.go b/test/e2e/fixture/fixture.go index 2efd4b36fc239..8d51ef3750c2d 100644 --- a/test/e2e/fixture/fixture.go +++ b/test/e2e/fixture/fixture.go @@ -170,7 +170,7 @@ func EnsureCleanState() { // new random ID id = strings.ToLower(rand.RandString(5)) - repoUrl = fmt.Sprintf("file:///%s", repoDirectory()) + repoUrl = fmt.Sprintf("file://%s", repoDirectory()) // create tmp dir FailOnErr(Run("", "mkdir", "-p", tmpDir)) From 84e0260142d700cf5c5dd688e3d7bcd71318ff14 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 09:33:26 -0700 Subject: [PATCH 17/30] "func TestConfigMap(t *testing.T) {" to app_management_test.go --- test/e2e/app_management_test.go | 6 ++++++ test/e2e/testdata/config-map/config-map.yaml | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 test/e2e/testdata/config-map/config-map.yaml diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 4203dcedf10ac..e554c4ef8fc4d 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -317,6 +317,10 @@ func TestDuplicatedResources(t *testing.T) { testEdgeCasesApplicationResources(t, "duplicated-resources") } +func TestConfigMap(t *testing.T) { + testEdgeCasesApplicationResources(t, "config-map") +} + func TestFailedConversion(t *testing.T) { defer func() { @@ -333,7 +337,9 @@ func testEdgeCasesApplicationResources(t *testing.T, appPath string) { Create(). Sync(). Then(). + Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). + Expect(HealthIs(HealthStatusHealthy)). And(func(app *Application) { diffOutput, err := fixture.RunCli("app", "diff", app.Name, "--local", path.Join("testdata", appPath)) assert.Empty(t, diffOutput) diff --git a/test/e2e/testdata/config-map/config-map.yaml b/test/e2e/testdata/config-map/config-map.yaml new file mode 100644 index 0000000000000..b3c2f6a2decf8 --- /dev/null +++ b/test/e2e/testdata/config-map/config-map.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ConfigMap +data: + foo: bar \ No newline at end of file From a2a714b8294f0e14d098438499e3e8e305d81aa0 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 09:35:36 -0700 Subject: [PATCH 18/30] "Expect(OperationPhaseIs(OperationSucceeded))...." to app_management_test.go --- test/e2e/app_management_test.go | 4 ++-- test/e2e/testdata/config-map/config-map.yaml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index e554c4ef8fc4d..3b16041f8e1b2 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -337,9 +337,9 @@ func testEdgeCasesApplicationResources(t *testing.T, appPath string) { Create(). Sync(). Then(). - Expect(OperationPhaseIs(OperationSucceeded)). + // Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). - Expect(HealthIs(HealthStatusHealthy)). + // Expect(HealthIs(HealthStatusHealthy)). And(func(app *Application) { diffOutput, err := fixture.RunCli("app", "diff", app.Name, "--local", path.Join("testdata", appPath)) assert.Empty(t, diffOutput) diff --git a/test/e2e/testdata/config-map/config-map.yaml b/test/e2e/testdata/config-map/config-map.yaml index b3c2f6a2decf8..b781028c394f5 100644 --- a/test/e2e/testdata/config-map/config-map.yaml +++ b/test/e2e/testdata/config-map/config-map.yaml @@ -1,4 +1,6 @@ apiVersion: v1 kind: ConfigMap +metadata: + name: my-map data: foo: bar \ No newline at end of file From 3a69e0c75fda4b5e92df293c9ea3162764827fd8 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 09:49:44 -0700 Subject: [PATCH 19/30] "// Expect(OperationPhaseIs(OperationSucceeded))...." to app_management_test.go --- test/e2e/app_management_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 3b16041f8e1b2..e554c4ef8fc4d 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -337,9 +337,9 @@ func testEdgeCasesApplicationResources(t *testing.T, appPath string) { Create(). Sync(). Then(). - // Expect(OperationPhaseIs(OperationSucceeded)). + Expect(OperationPhaseIs(OperationSucceeded)). Expect(SyncStatusIs(SyncStatusCodeSynced)). - // Expect(HealthIs(HealthStatusHealthy)). + Expect(HealthIs(HealthStatusHealthy)). And(func(app *Application) { diffOutput, err := fixture.RunCli("app", "diff", app.Name, "--local", path.Join("testdata", appPath)) assert.Empty(t, diffOutput) From 4133fa40de9140e561bff26675cd12c95e8a11cd Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 09:51:54 -0700 Subject: [PATCH 20/30] "Then()." to kustomize_test.go --- test/e2e/kustomize_test.go | 4 ++++ test/e2e/testdata/kustomize-cm-gen/kustomization.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index d5303793c1536..15492d4bf154d 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -78,6 +78,10 @@ func TestSyncStatusOptionIgnore(t *testing.T) { // we now force generation of a second CM PatchFile("kustomization.yaml", `[{"op": "replace", "path": "/configMapGenerator/0/literals/0", "value": "foo=baz"}]`). Refresh(RefreshTypeHard). + Then(). + // this is standard logging from the command - tough one - true statement + Expect(Error("1 resources require pruning")). + When(). Sync(). Then(). Expect(OperationPhaseIs(OperationSucceeded)). diff --git a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml index 8b9daa0af9e3c..49a69783da0e5 100644 --- a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml +++ b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml @@ -4,5 +4,5 @@ configMapGenerator: - foo=bar generatorOptions: annotations: - argocd.argoproj.io/compare-options: IgnoreNeedsPrune + argocd.argoproj.io/compare-options: IgnoreNeedsPruning kind: Kustomization From 301058759fcb4b2aaaf885debc2e282df023b349 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 10:49:56 -0700 Subject: [PATCH 21/30] "testEdgeCasesApplicationResources(t, "deprecated-e..." to app_management_test.go --- test/e2e/app_management_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index e554c4ef8fc4d..625061a452d17 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -306,19 +306,19 @@ func TestResourceDiffing(t *testing.T) { } func TestDeprecatedExtensions(t *testing.T) { - testEdgeCasesApplicationResources(t, "deprecated-extensions") + testEdgeCasesApplicationResources(t, "deprecated-extensions", OperationRunning, HealthStatusProgressing) } func TestCRDs(t *testing.T) { - testEdgeCasesApplicationResources(t, "crd-creation") + testEdgeCasesApplicationResources(t, "crd-creation", OperationSucceeded, HealthStatusHealthy) } func TestDuplicatedResources(t *testing.T) { - testEdgeCasesApplicationResources(t, "duplicated-resources") + testEdgeCasesApplicationResources(t, "duplicated-resources", OperationSucceeded, HealthStatusHealthy) } func TestConfigMap(t *testing.T) { - testEdgeCasesApplicationResources(t, "config-map") + testEdgeCasesApplicationResources(t, "config-map", OperationSucceeded, HealthStatusHealthy) } func TestFailedConversion(t *testing.T) { @@ -327,19 +327,19 @@ func TestFailedConversion(t *testing.T) { errors.FailOnErr(fixture.Run("", "kubectl", "delete", "apiservice", "v1beta1.metrics.k8s.io")) }() - testEdgeCasesApplicationResources(t, "failed-conversion") + testEdgeCasesApplicationResources(t, "failed-conversion", OperationSucceeded, HealthStatusHealthy) } -func testEdgeCasesApplicationResources(t *testing.T, appPath string) { +func testEdgeCasesApplicationResources(t *testing.T, appPath string, phase OperationPhase, statusCode HealthStatusCode) { Given(t). Path(appPath). When(). Create(). Sync(). Then(). - Expect(OperationPhaseIs(OperationSucceeded)). + Expect(OperationPhaseIs(phase)). Expect(SyncStatusIs(SyncStatusCodeSynced)). - Expect(HealthIs(HealthStatusHealthy)). + Expect(HealthIs(statusCode)). And(func(app *Application) { diffOutput, err := fixture.RunCli("app", "diff", app.Name, "--local", path.Join("testdata", appPath)) assert.Empty(t, diffOutput) From d827645b1c7e9cd82e333842d78f649952d4f08d Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 11:15:26 -0700 Subject: [PATCH 22/30] "![compare option needs pruning](../assets/compare-..." to compare-options.md --- .../compare-option-ignore-needs-pruning.png | Bin 0 -> 39718 bytes .../sync-option-no-prune-sync-status.png | Bin 0 -> 31771 bytes docs/assets/sync-option-no-prune.png | Bin 0 -> 35327 bytes docs/user-guide/compare-options.md | 2 ++ docs/user-guide/sync-options.md | 9 +++++++ test/e2e/app_management_test.go | 22 ++++++++++++++++++ test/e2e/kustomize_test.go | 2 ++ 7 files changed, 35 insertions(+) create mode 100644 docs/assets/compare-option-ignore-needs-pruning.png create mode 100644 docs/assets/sync-option-no-prune-sync-status.png create mode 100644 docs/assets/sync-option-no-prune.png diff --git a/docs/assets/compare-option-ignore-needs-pruning.png b/docs/assets/compare-option-ignore-needs-pruning.png new file mode 100644 index 0000000000000000000000000000000000000000..78cfe688f9a07fafc7301b78b33b2d3ae28ddf3a GIT binary patch literal 39718 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 literal 0 HcmV?d00001 diff --git a/docs/assets/sync-option-no-prune-sync-status.png b/docs/assets/sync-option-no-prune-sync-status.png new file mode 100644 index 0000000000000000000000000000000000000000..ae117e31d375937dee7cc22f734aa4f10e43026f GIT binary patch literal 31771 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 literal 0 HcmV?d00001 diff --git a/docs/assets/sync-option-no-prune.png b/docs/assets/sync-option-no-prune.png new file mode 100644 index 0000000000000000000000000000000000000000..1da6a0e1b8c461385c1ef59c3a82767ac5b49c34 GIT binary patch literal 35327 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 Date: Wed, 5 Jun 2019 11:24:55 -0700 Subject: [PATCH 23/30] "})" to app_management_test.go --- test/e2e/app_management_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 218ca9d4bc4fd..41717982fd971 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -551,5 +551,5 @@ func TestCompareOptionIgnoreNeedsPruning(t *testing.T) { And(func(app *Application) { assert.Len(t, app.Status.Resources, 2) assert.Equal(t, SyncStatusCodeOutOfSync, app.Status.Resources[1].Status) - }) + }) } From 1039c7e1f5b927cab55c5517b449c64047cbab3c Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 11:37:45 -0700 Subject: [PATCH 24/30] "})" to app_management_test.go --- test/e2e/app_management_test.go | 7 ++++++- test/e2e/kustomize_test.go | 6 ++---- test/e2e/testdata/kustomize-cm-gen/kustomization.yaml | 1 + 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 41717982fd971..0448392c1687a 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -551,5 +551,10 @@ func TestCompareOptionIgnoreNeedsPruning(t *testing.T) { And(func(app *Application) { assert.Len(t, app.Status.Resources, 2) assert.Equal(t, SyncStatusCodeOutOfSync, app.Status.Resources[1].Status) - }) + }). + When(). + Sync(). + Then(). + Expect(OperationPhaseIs(OperationSucceeded)). + Expect(SyncStatusIs(SyncStatusCodeSynced)) } diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index 178dd87bf7c63..91bdaea283c16 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -58,8 +58,6 @@ func TestSyncStatusOptionIgnore(t *testing.T) { var mapName string Given(t). Path("kustomize-cm-gen"). - // note we don't want to prune resources, check the config maps exist - Prune(false). When(). Create(). Sync(). @@ -80,12 +78,12 @@ func TestSyncStatusOptionIgnore(t *testing.T) { Refresh(RefreshTypeHard). Then(). // this is standard logging from the command - tough one - true statement - Expect(Error("1 resources require pruning")). When(). Sync(). Then(). + Expect(Error("1 resources require pruning")). Expect(OperationPhaseIs(OperationSucceeded)). - // this is the key check - we expect the app to be healthy because, even though we have a resources that needs + // this is a key check - we expect the app to be healthy because, even though we have a resources that needs // pruning, because it is annotated with IgnoreNeedsPruning it should not contribute to the sync status Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(HealthStatusHealthy)). diff --git a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml index 49a69783da0e5..f59bd2cd84c18 100644 --- a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml +++ b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml @@ -5,4 +5,5 @@ configMapGenerator: generatorOptions: annotations: argocd.argoproj.io/compare-options: IgnoreNeedsPruning + argocd.argoproj.io/sync-options: NoPrune kind: Kustomization From 45bde7d51a45ba0e1525d89ad8422c962fd4e817 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 12:26:24 -0700 Subject: [PATCH 25/30] Update compare-options.md --- docs/user-guide/compare-options.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/user-guide/compare-options.md b/docs/user-guide/compare-options.md index 7716d962c83c0..63a9820d871fd 100644 --- a/docs/user-guide/compare-options.md +++ b/docs/user-guide/compare-options.md @@ -2,7 +2,7 @@ ## Ignoring Resources That Needs Pruning -You may wish to exclude resources for the app's overall sync status under certain circumstances, for example if they are generated by the tool. This can be done by adding an annotation: +You may wish to exclude resources from the app's overall sync status under certain circumstances. E.g. if they are generated by a tool. This can be done by adding this annotation: ```yaml metadata: @@ -13,9 +13,9 @@ metadata: ![compare option needs pruning](../assets/compare-option-ignore-needs-pruning.png) !!! note - This only affect the sync status. If the resource's health is degraded, then the app will also be degraded. + This only affects the sync status. If the resource's health is degraded, then the app will also be degraded. -Kustomize has a feature that allows you to generate config maps ([read more ⧉](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/configGeneration.md)). You can set `generatorOptions` to add this annotation so that your app remains in sync: +Kustomize has a feature that allows you to generate config maps ([read more ⧉](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/configGeneration.md)). You can set `generatorOptions` to add this annotation so that your app remains in sync: ```yaml configMapGenerator: @@ -29,4 +29,6 @@ kind: Kustomization ``` !!! note - `generatorOptions` adds annotations to both config maps and secrets ([read more ⧉](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/generatorOptions.md)). \ No newline at end of file + `generatorOptions` adds annotations to both config maps and secrets ([read more ⧉](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/generatorOptions.md)). + +You may wish to combine this with the [`NoPrune` compare option](compare-options.md). From 00d1243b88439c659abcd0aaa119bedd8067fb17 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 12:26:40 -0700 Subject: [PATCH 26/30] Update compare-options.md --- docs/user-guide/compare-options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/compare-options.md b/docs/user-guide/compare-options.md index 63a9820d871fd..18e7867ebfec7 100644 --- a/docs/user-guide/compare-options.md +++ b/docs/user-guide/compare-options.md @@ -31,4 +31,4 @@ kind: Kustomization !!! note `generatorOptions` adds annotations to both config maps and secrets ([read more ⧉](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/generatorOptions.md)). -You may wish to combine this with the [`NoPrune` compare option](compare-options.md). +You may wish to combine this with the [`NoPrune` sync option](sync-options.md). From 543ae6fe17863b3532bb4fd3bc023ca1d82df0d0 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 12:27:29 -0700 Subject: [PATCH 27/30] Update kustomize.md --- docs/user-guide/kustomize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/kustomize.md b/docs/user-guide/kustomize.md index 0a5e3d207fb04..13eb102881984 100644 --- a/docs/user-guide/kustomize.md +++ b/docs/user-guide/kustomize.md @@ -13,4 +13,4 @@ You have three configuration options for Kustomize: To use Kustomize with an overlay, point your path to the overlay. !!! tip - If you're generating resources, you should read up how to ignore those generated resources using [sync status options](sync_status_options.md). \ No newline at end of file + If you're generating resources, you should read up how to ignore those generated resources using the [`IgnoreNeedsPruning` compare option](compare-options.md). From e41bd84b1716b494fada30be6593917fcb02b146 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 12:29:27 -0700 Subject: [PATCH 28/30] "func (c *Context) Prune(prune bool) *Context {..." to context.go --- test/e2e/fixture/app/context.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/e2e/fixture/app/context.go b/test/e2e/fixture/app/context.go index d7e39b72a2c96..b9bfc8dcaa499 100644 --- a/test/e2e/fixture/app/context.go +++ b/test/e2e/fixture/app/context.go @@ -61,11 +61,6 @@ func (c *Context) NamePrefix(namePrefix string) *Context { return c } -func (c *Context) Prune(prune bool) *Context { - c.prune = prune - return c -} - func (c *Context) And(block func()) *Context { block() return c @@ -74,3 +69,8 @@ func (c *Context) And(block func()) *Context { func (c *Context) When() *Actions { return &Actions{context: c} } + +func (c *Context) Prune(prune bool) *Context { + c.prune = prune + return c +} From fe0ec63ce1006fe739c013fc93c2f6a78c95dffe Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 14:28:27 -0700 Subject: [PATCH 29/30] "if !(needsPruning && resource.HasAnnotationOption(..." to state.go --- controller/state.go | 2 +- controller/state_test.go | 4 ++-- controller/sync.go | 2 +- controller/sync_test.go | 6 +++--- docs/user-guide/compare-options.md | 8 ++++---- docs/user-guide/kustomize.md | 2 +- docs/user-guide/sync-options.md | 2 +- test/e2e/app_management_test.go | 10 +++++----- test/e2e/kustomize_test.go | 2 +- test/e2e/testdata/kustomize-cm-gen/kustomization.yaml | 4 ++-- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/controller/state.go b/controller/state.go index a3620f3cf2a48..721e97c23e994 100644 --- a/controller/state.go +++ b/controller/state.go @@ -328,7 +328,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st resState.Status = v1alpha1.SyncStatusCodeOutOfSync // we don't apply to the application needsPruning := targetObj == nil && liveObj != nil - if !(needsPruning && resource.HasAnnotationOption(obj, common.AnnotationCompareOptions, "IgnoreNeedsPruning")) { + if !(needsPruning && resource.HasAnnotationOption(obj, common.AnnotationCompareOptions, "IgnoreExtraneous")) { syncCode = v1alpha1.SyncStatusCodeOutOfSync } } else { diff --git a/controller/state_test.go b/controller/state_test.go index 9d90939c40b17..6ba9178f05aaa 100644 --- a/controller/state_test.go +++ b/controller/state_test.go @@ -118,9 +118,9 @@ func TestCompareAppStateHook(t *testing.T) { } // checks that ignore resources are detected, but excluded from status -func TestCompareAppStateCompareOptionIgnoreNeedsPruning(t *testing.T) { +func TestCompareAppStateCompareOptionIgnoreExtraneous(t *testing.T) { pod := test.NewPod() - pod.SetAnnotations(map[string]string{common.AnnotationCompareOptions: "IgnoreNeedsPruning"}) + pod.SetAnnotations(map[string]string{common.AnnotationCompareOptions: "IgnoreExtraneous"}) app := newFakeApp() data := fakeData{ apps: []runtime.Object{app}, diff --git a/controller/sync.go b/controller/sync.go index 4f4e5d646a06c..896a05fc29a24 100644 --- a/controller/sync.go +++ b/controller/sync.go @@ -477,7 +477,7 @@ func (sc *syncContext) applyObject(targetObj *unstructured.Unstructured, dryRun func (sc *syncContext) pruneObject(liveObj *unstructured.Unstructured, prune, dryRun bool) (v1alpha1.ResultCode, string) { if !prune { return v1alpha1.ResultCodePruneSkipped, "ignored (requires pruning)" - } else if resource.HasAnnotationOption(liveObj, common.AnnotationSyncOptions, "NoPrune") { + } else if resource.HasAnnotationOption(liveObj, common.AnnotationSyncOptions, "Prune=false") { return v1alpha1.ResultCodePruneSkipped, "ignored (no prune)" } else { if dryRun { diff --git a/controller/sync_test.go b/controller/sync_test.go index 90ecef1e9f27b..d6af8ce3f0970 100644 --- a/controller/sync_test.go +++ b/controller/sync_test.go @@ -305,11 +305,11 @@ func TestDontSyncOrPruneHooks(t *testing.T) { assert.Equal(t, v1alpha1.OperationSucceeded, syncCtx.opState.Phase) } -// make sure that we do not prune resources with NoPrune -func TestDontPruneNoPrune(t *testing.T) { +// make sure that we do not prune resources with Prune=false +func TestDontPrunePruneFalse(t *testing.T) { syncCtx := newTestSyncCtx() pod := test.NewPod() - pod.SetAnnotations(map[string]string{common.AnnotationSyncOptions: "NoPrune"}) + pod.SetAnnotations(map[string]string{common.AnnotationSyncOptions: "Prune=false"}) pod.SetNamespace(test.FakeArgoCDNamespace) syncCtx.compareResult = &comparisonResult{managedResources: []managedResource{{Live: pod}}} diff --git a/docs/user-guide/compare-options.md b/docs/user-guide/compare-options.md index 18e7867ebfec7..88d9350f7f97a 100644 --- a/docs/user-guide/compare-options.md +++ b/docs/user-guide/compare-options.md @@ -1,13 +1,13 @@ # Compare Options -## Ignoring Resources That Needs Pruning +## Ignoring Resources That Are Extraneous You may wish to exclude resources from the app's overall sync status under certain circumstances. E.g. if they are generated by a tool. This can be done by adding this annotation: ```yaml metadata: annotations: - argocd.argoproj.io/compare-options: IgnoreNeedsPruning + argocd.argoproj.io/compare-options: IgnoreExtraneous ``` ![compare option needs pruning](../assets/compare-option-ignore-needs-pruning.png) @@ -24,11 +24,11 @@ configMapGenerator: - foo=bar generatorOptions: annotations: - argocd.argoproj.io/compare-options: IgnoreNeedsPruning + argocd.argoproj.io/compare-options: IgnoreExtraneous kind: Kustomization ``` !!! note `generatorOptions` adds annotations to both config maps and secrets ([read more ⧉](https://github.com/kubernetes-sigs/kustomize/blob/master/examples/generatorOptions.md)). -You may wish to combine this with the [`NoPrune` sync option](sync-options.md). +You may wish to combine this with the [`Prune=false` sync option](sync-options.md). diff --git a/docs/user-guide/kustomize.md b/docs/user-guide/kustomize.md index 13eb102881984..fc9303070f242 100644 --- a/docs/user-guide/kustomize.md +++ b/docs/user-guide/kustomize.md @@ -13,4 +13,4 @@ You have three configuration options for Kustomize: To use Kustomize with an overlay, point your path to the overlay. !!! tip - If you're generating resources, you should read up how to ignore those generated resources using the [`IgnoreNeedsPruning` compare option](compare-options.md). + If you're generating resources, you should read up how to ignore those generated resources using the [`IgnoreExtraneous` compare option](compare-options.md). diff --git a/docs/user-guide/sync-options.md b/docs/user-guide/sync-options.md index fc65accd01d2d..4e8e5f6c7b41c 100644 --- a/docs/user-guide/sync-options.md +++ b/docs/user-guide/sync-options.md @@ -7,7 +7,7 @@ You may wish to prevent an object from being pruned: ```yaml metadata: annotations: - argocd.argoproj.io/sync-options: NoPrune + argocd.argoproj.io/sync-options: Prune=false ``` In the UI, the pod will simply appear as out-of-sync: diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go index 0448392c1687a..20299d5c536c3 100644 --- a/test/e2e/app_management_test.go +++ b/test/e2e/app_management_test.go @@ -511,12 +511,12 @@ func TestPermissions(t *testing.T) { assert.True(t, sourceErrorExist) } -// make sure that if we deleted a resource from the app, it is not pruned if annotated with NoPrune -func TestSyncOptionNoPrune(t *testing.T) { +// make sure that if we deleted a resource from the app, it is not pruned if annotated with Prune=false +func TestSyncOptionPruneFalse(t *testing.T) { Given(t). Path("two-nice-pods"). When(). - PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "NoPrune"}}]`). + PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`). Create(). Sync(). Then(). @@ -533,11 +533,11 @@ func TestSyncOptionNoPrune(t *testing.T) { } // make sure that, if we have a resource that needs pruning, but we're ignoring it, the app is in-sync -func TestCompareOptionIgnoreNeedsPruning(t *testing.T) { +func TestCompareOptionIgnoreExtraneous(t *testing.T) { Given(t). Path("two-nice-pods"). When(). - PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/compare-options": "IgnoreNeedsPruning"}}]`). + PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/compare-options": "IgnoreExtraneous"}}]`). Create(). Sync(). Then(). diff --git a/test/e2e/kustomize_test.go b/test/e2e/kustomize_test.go index 91bdaea283c16..75ccf131457fb 100644 --- a/test/e2e/kustomize_test.go +++ b/test/e2e/kustomize_test.go @@ -84,7 +84,7 @@ func TestSyncStatusOptionIgnore(t *testing.T) { Expect(Error("1 resources require pruning")). Expect(OperationPhaseIs(OperationSucceeded)). // this is a key check - we expect the app to be healthy because, even though we have a resources that needs - // pruning, because it is annotated with IgnoreNeedsPruning it should not contribute to the sync status + // pruning, because it is annotated with IgnoreExtraneous it should not contribute to the sync status Expect(SyncStatusIs(SyncStatusCodeSynced)). Expect(HealthIs(HealthStatusHealthy)). And(func(app *Application) { diff --git a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml index f59bd2cd84c18..5b47525454277 100644 --- a/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml +++ b/test/e2e/testdata/kustomize-cm-gen/kustomization.yaml @@ -4,6 +4,6 @@ configMapGenerator: - foo=bar generatorOptions: annotations: - argocd.argoproj.io/compare-options: IgnoreNeedsPruning - argocd.argoproj.io/sync-options: NoPrune + argocd.argoproj.io/compare-options: IgnoreExtraneous + argocd.argoproj.io/sync-options: Prune=false kind: Kustomization From 131fb575356404ee299cdb293cadf0c37f0e4cf2 Mon Sep 17 00:00:00 2001 From: Alex Collins Date: Wed, 5 Jun 2019 14:57:37 -0700 Subject: [PATCH 30/30] Update state.go --- controller/state.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/state.go b/controller/state.go index 721e97c23e994..e3d8ce9ac3575 100644 --- a/controller/state.go +++ b/controller/state.go @@ -326,7 +326,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st // * target resource not defined and live resource is extra // * target resource present but live resource is missing resState.Status = v1alpha1.SyncStatusCodeOutOfSync - // we don't apply to the application + // we ignore the status if the obj needs pruning AND we have the annotation needsPruning := targetObj == nil && liveObj != nil if !(needsPruning && resource.HasAnnotationOption(obj, common.AnnotationCompareOptions, "IgnoreExtraneous")) { syncCode = v1alpha1.SyncStatusCodeOutOfSync