Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Prune=false and IgnoreExtraneous options #1680

Merged
merged 34 commits into from
Jun 7, 2019
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6c6f0d4
Adds the option to ignore resources when calculating sync status
alexec Jun 3, 2019
8aa745d
lint
alexec Jun 3, 2019
7ba87a9
""github.com/argoproj/argo-cd/util/resources"..." to state.go
alexec Jun 4, 2019
f4c7908
"// AnnotationSyncStatusOptions is a comma-separate..." to common.go
alexec Jun 4, 2019
eed7261
"testCompareAppStateIgnoreAnnotation(t, common.Anno..." to state_test.go
alexec Jun 4, 2019
c72752a
"if resource.HasAnnotationOption(obj, common.Annota..." to state.go
alexec Jun 4, 2019
b59f516
"Kustomize has a feature that allows you to generat..." to sync_statu…
alexec Jun 4, 2019
46eb193
"return &Context{t: t, destServer: KubernetesIntern..." to context.go
alexec Jun 4, 2019
57dccdd
Merge branch 'master' into sync-option-ignore
alexec Jun 5, 2019
2b30c71
Merge branch 'master' into sync-option-ignore
alexec Jun 5, 2019
3f037d2
"func (c *Context) Prune(prune bool) *Context {..." to context.go
alexec Jun 5, 2019
0381c01
Adds NoPrune option
alexec Jun 5, 2019
f7b3b52
"AnnotationSyncStatusOptions = "argocd.argoproj.io/..." to common.go
alexec Jun 5, 2019
6c9dc19
"// AnnotationSyncStatusOptions is a comma-separate..." to common.go
alexec Jun 5, 2019
d3ae6db
"func TestCompareAppStateSyncOptionIgnoreNeedsPruni..." to state_test.go
alexec Jun 5, 2019
c1b0382
"assert.Len(t, compRes.managedResources,0)..." to state_test.go
alexec Jun 5, 2019
8b639cd
"argocd.argoproj.io/compare-options: Ignore..." to kustomization.yaml
alexec Jun 5, 2019
4ed7b27
"repoUrl = fmt.Sprintf("file:///%s", repoDirectory(..." to fixture.go
alexec Jun 5, 2019
84e0260
"func TestConfigMap(t *testing.T) {" to app_management_test.go
alexec Jun 5, 2019
a2a714b
"Expect(OperationPhaseIs(OperationSucceeded))...." to app_management_…
alexec Jun 5, 2019
3a69e0c
"// Expect(OperationPhaseIs(OperationSucceeded))...." to app_manageme…
alexec Jun 5, 2019
4133fa4
"Then()." to kustomize_test.go
alexec Jun 5, 2019
3010587
"testEdgeCasesApplicationResources(t, "deprecated-e..." to app_manage…
alexec Jun 5, 2019
d827645
"![compare option needs pruning](../assets/compare-..." to compare-op…
alexec Jun 5, 2019
28e38a7
"})" to app_management_test.go
alexec Jun 5, 2019
1039c7e
"})" to app_management_test.go
alexec Jun 5, 2019
45bde7d
Update compare-options.md
alexec Jun 5, 2019
00d1243
Update compare-options.md
alexec Jun 5, 2019
543ae6f
Update kustomize.md
alexec Jun 5, 2019
e41bd84
"func (c *Context) Prune(prune bool) *Context {..." to context.go
alexec Jun 5, 2019
fe0ec63
"if !(needsPruning && resource.HasAnnotationOption(..." to state.go
alexec Jun 5, 2019
131fb57
Update state.go
alexec Jun 5, 2019
1744612
Merge branch 'master' into sync-option-ignore
alexec Jun 6, 2019
9927e86
Merge branch 'master' into sync-option-ignore
alexec Jun 6, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
alexec marked this conversation as resolved.
Show resolved Hide resolved
// ResourcesFinalizerName the finalizer value which we inject to finalize deletion of an application
ResourcesFinalizerName = "resources-finalizer.argocd.argoproj.io"
)
Expand Down
7 changes: 6 additions & 1 deletion controller/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/resource"
"github.com/argoproj/argo-cd/util/settings"
)

Expand Down Expand Up @@ -313,6 +314,7 @@ func (m *appStateManager) CompareAppState(app *v1alpha1.Application, revision st
}

diffResult := diffResults.Diffs[i]

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 {
Expand All @@ -321,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
alexec marked this conversation as resolved.
Show resolved Hide resolved
if !resource.HasAnnotationOption(obj, common.AnnotationSyncStatusOptions, "Ignore") {
alexec marked this conversation as resolved.
Show resolved Hide resolved
syncCode = v1alpha1.SyncStatusCodeOutOfSync
}
} else {
resState.Status = v1alpha1.SyncStatusCodeSynced
}
Expand Down
28 changes: 28 additions & 0 deletions controller/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,34 @@ func TestCompareAppStateHook(t *testing.T) {
assert.Equal(t, 0, len(compRes.conditions))
}

// checks that ignore resources are detected, but excluded from status
func TestCompareAppStateSyncOptionIgnore(t *testing.T) {
pod := test.NewPod()
pod.SetAnnotations(map[string]string{common.AnnotationSyncStatusOptions: "Ignore"})
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)
alexec marked this conversation as resolved.
Show resolved Hide resolved
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))
}

// TestCompareAppStateExtraHook tests when there is an extra _hook_ object in live but not defined in git
func TestCompareAppStateExtraHook(t *testing.T) {
pod := test.NewPod()
Expand Down
5 changes: 4 additions & 1 deletion docs/user-guide/kustomize.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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).
27 changes: 27 additions & 0 deletions docs/user-guide/sync_status_options.md
Original file line number Diff line number Diff line change
@@ -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
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 47 additions & 0 deletions test/e2e/kustomize_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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().
Create().
Sync().
alexec marked this conversation as resolved.
Show resolved Hide resolved
Then().
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"}]`).
Refresh(RefreshTypeHard).
Sync().
Then().
Expect(OperationPhaseIs(OperationSucceeded)).
Expect(SyncStatusIs(SyncStatusCodeSynced)).
Expect(HealthIs(HealthStatusHealthy)).
And(func(app *Application) {
resourceStatus := app.Status.Resources[0]
alexec marked this conversation as resolved.
Show resolved Hide resolved
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)
})
}
8 changes: 8 additions & 0 deletions test/e2e/testdata/kustomize-cm-gen/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
configMapGenerator:
- name: my-map
literals:
- foo=bar
generatorOptions:
annotations:
argocd.argoproj.io/sync-status-options: Ignore
kind: Kustomization
16 changes: 16 additions & 0 deletions util/resource/annotations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package resource

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
}
41 changes: 41 additions & 0 deletions util/resource/annotations_test.go
Original file line number Diff line number Diff line change
@@ -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
}