From 86968762690117d7eb9793b2fc8fe4f6cd298922 Mon Sep 17 00:00:00 2001 From: Philip Laine Date: Wed, 12 Feb 2020 23:00:25 +0100 Subject: [PATCH 1/5] Add DisableGarbageCollect annotation --- pkg/cluster/kubernetes/sync.go | 5 +++++ pkg/cluster/kubernetes/sync_test.go | 33 +++++++++++++++++++++++++++++ pkg/policy/policy.go | 15 +++++++------ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/pkg/cluster/kubernetes/sync.go b/pkg/cluster/kubernetes/sync.go index 5883e2289..93afa5c10 100644 --- a/pkg/cluster/kubernetes/sync.go +++ b/pkg/cluster/kubernetes/sync.go @@ -145,6 +145,11 @@ func (c *Cluster) collectGarbage( switch { case !ok: // was not recorded as having been staged for application + if res.Policies().Has(policy.DisableGarbageCollect) { + c.logger.Log("info", "not deleting resource; garbage colelction annotation in file", "resource", res.ResourceID()) + continue + } + c.logger.Log("info", "cluster resource not in resources to be synced; deleting", "dry-run", dryRun, "resource", resourceID) if !dryRun { orphanedResources.stage("delete", res.ResourceID(), "", res.IdentifyingBytes()) diff --git a/pkg/cluster/kubernetes/sync_test.go b/pkg/cluster/kubernetes/sync_test.go index e276cfe39..4443d2968 100644 --- a/pkg/cluster/kubernetes/sync_test.go +++ b/pkg/cluster/kubernetes/sync_test.go @@ -329,6 +329,8 @@ kind: Deployment metadata: name: dep1 namespace: foobar + annotations: + fluxcd.io/disable_gc: "false" ` const defs2 = `--- @@ -353,6 +355,25 @@ metadata: name: dep3 namespace: other ` + + const ns4 = `--- +apiVersion: v1 +kind: Namespace +metadata: + name: baz + annotations: + fluxcd.io/disable_gc: "true" +` + + const defs4 = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep4 + namespace: baz + annotations: + fluxcd.io/disable_gc: "true" +` // checkSame is a check that a result returned from the cluster is // the same as an expected. labels and annotations may be altered // by the sync process; we'll look at the "spec" field as an @@ -464,6 +485,18 @@ metadata: test(t, kube, "", ns1+defs1+defs2+ns3+defs3, false) }) + t.Run("sync adds and GCs skips resources", func(t *testing.T) { + kube, _, cancel := setup(t) + defer cancel() + + // With GC switched on, resources disabling GC with an annoation wont be removed + kube.GC = true + test(t, kube, "", "", false) + test(t, kube, ns1+defs1+ns4+defs4, ns1+defs1+ns4+defs4, false) + test(t, kube, ns1+defs1, ns1+defs1+ns4+defs4, false) + test(t, kube, "", ns4+defs4, false) + }) + t.Run("sync won't incorrectly delete non-namespaced resources", func(t *testing.T) { kube, _, cancel := setup(t) defer cancel() diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index c9ec95f34..570e3463f 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -6,12 +6,13 @@ import ( ) const ( - Ignore = Policy("ignore") - Locked = Policy("locked") - LockedUser = Policy("locked_user") - LockedMsg = Policy("locked_msg") - Automated = Policy("automated") - TagAll = Policy("tag_all") + DisableGarbageCollect = Policy("disable_gc") + Ignore = Policy("ignore") + Locked = Policy("locked") + LockedUser = Policy("locked_user") + LockedMsg = Policy("locked_msg") + Automated = Policy("automated") + TagAll = Policy("tag_all") ) // Policy is an string, denoting the current deployment policy of a service, @@ -20,7 +21,7 @@ type Policy string func Boolean(policy Policy) bool { switch policy { - case Locked, Automated, Ignore: + case Locked, Automated, Ignore, DisableGarbageCollect: return true } return false From 1f5649aca07b1fd0b8da053370fc177fb973881a Mon Sep 17 00:00:00 2001 From: Philip Laine Date: Sat, 29 Feb 2020 17:32:22 +0100 Subject: [PATCH 2/5] Add support for sync_only value on ignore policy Change GC behavior to skip GC of resources with ignore policy --- pkg/cluster/kubernetes/sync.go | 10 ++- pkg/cluster/kubernetes/sync_test.go | 105 ++++++++++++++++++---------- pkg/policy/policy.go | 15 ++-- 3 files changed, 85 insertions(+), 45 deletions(-) diff --git a/pkg/cluster/kubernetes/sync.go b/pkg/cluster/kubernetes/sync.go index 93afa5c10..e82392fe5 100644 --- a/pkg/cluster/kubernetes/sync.go +++ b/pkg/cluster/kubernetes/sync.go @@ -145,8 +145,14 @@ func (c *Cluster) collectGarbage( switch { case !ok: // was not recorded as having been staged for application - if res.Policies().Has(policy.DisableGarbageCollect) { - c.logger.Log("info", "not deleting resource; garbage colelction annotation in file", "resource", res.ResourceID()) + if res.Policies().Has(policy.Ignore) { + c.logger.Log("info", "skipping GC of cluster resource; resource has ignore policy true", "dry-run", dryRun, "resource", resourceID) + continue + } + + v, ok := res.Policies().Get(policy.Ignore) + if ok && v == "sync_only" { + c.logger.Log("info", "skipping GC of cluster resource; resource has ignore policy sync_only ", "dry-run", dryRun, "resource", resourceID) continue } diff --git a/pkg/cluster/kubernetes/sync_test.go b/pkg/cluster/kubernetes/sync_test.go index 4443d2968..7ea44532e 100644 --- a/pkg/cluster/kubernetes/sync_test.go +++ b/pkg/cluster/kubernetes/sync_test.go @@ -329,8 +329,6 @@ kind: Deployment metadata: name: dep1 namespace: foobar - annotations: - fluxcd.io/disable_gc: "false" ` const defs2 = `--- @@ -356,24 +354,6 @@ metadata: namespace: other ` - const ns4 = `--- -apiVersion: v1 -kind: Namespace -metadata: - name: baz - annotations: - fluxcd.io/disable_gc: "true" -` - - const defs4 = `--- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: dep4 - namespace: baz - annotations: - fluxcd.io/disable_gc: "true" -` // checkSame is a check that a result returned from the cluster is // the same as an expected. labels and annotations may be altered // by the sync process; we'll look at the "spec" field as an @@ -485,18 +465,6 @@ metadata: test(t, kube, "", ns1+defs1+defs2+ns3+defs3, false) }) - t.Run("sync adds and GCs skips resources", func(t *testing.T) { - kube, _, cancel := setup(t) - defer cancel() - - // With GC switched on, resources disabling GC with an annoation wont be removed - kube.GC = true - test(t, kube, "", "", false) - test(t, kube, ns1+defs1+ns4+defs4, ns1+defs1+ns4+defs4, false) - test(t, kube, ns1+defs1, ns1+defs1+ns4+defs4, false) - test(t, kube, "", ns4+defs4, false) - }) - t.Run("sync won't incorrectly delete non-namespaced resources", func(t *testing.T) { kube, _, cancel := setup(t) defer cancel() @@ -640,7 +608,7 @@ metadata: test(t, kube, ns1+defs1invalid, ns1+defs1invalid, true) }) - t.Run("sync doesn't apply or delete manifests marked with ignore", func(t *testing.T) { + t.Run("sync doesn't apply or GC manifests marked with ignore: 'true'", func(t *testing.T) { kube, _, cancel := setup(t) defer cancel() kube.GC = true @@ -684,7 +652,7 @@ spec: test(t, kube, ns1+dep1ignored+dep2, ns1+dep1, false) }) - t.Run("sync doesn't update a cluster resource marked with ignore", func(t *testing.T) { + t.Run("sync doesn't update a cluster resource marked with ignore: 'true'", func(t *testing.T) { const dep1 = `--- apiVersion: apps/v1 kind: Deployment @@ -736,7 +704,74 @@ spec: test(t, kube, ns1+mod1, ns1+dep1, false) }) - t.Run("sync doesn't update or delete a pre-existing resource marked with ignore", func(t *testing.T) { + t.Run("sync doesn't GC resources annotated with ignore: 'sync_only'", func(t *testing.T) { + kube, _, cancel := setup(t) + defer cancel() + kube.GC = true + + const dep1 = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep1 + namespace: foobar + annotations: {flux.weave.works/ignore: "sync_only"} +` + + // sync namespace and deployment + test(t, kube, ns1+dep1, ns1+dep1, false) + + // sync namespace only but expect deployment to not be GC + test(t, kube, ns1, ns1+dep1, false) + }) + + t.Run("sync doesn't GC resources annotated with ignore: 'true'", func(t *testing.T) { + kube, _, cancel := setup(t) + defer cancel() + kube.GC = true + + const dep1 = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep1 + namespace: foobar +` + const dep2 = `--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep2 + namespace: foobar + annotations: {flux.weave.works/ignore: "false"} +` + + // dep1 is created + test(t, kube, ns1+dep1+dep2, ns1+dep1+dep2, false) + + // add ignore: 'true' annotation outside of sync loop + dc := kube.client.dynamicClient + rc := dc.Resource(schema.GroupVersionResource{ + Group: "apps", + Version: "v1", + Resource: "deployments", + }) + res, err := rc.Namespace("foobar").Get("dep1", metav1.GetOptions{}) + if err != nil { + t.Fatal(err) + } + annots := res.GetAnnotations() + annots["flux.weave.works/ignore"] = "true" + res.SetAnnotations(annots) + if _, err = rc.Namespace("foobar").Update(res, metav1.UpdateOptions{}); err != nil { + t.Fatal(err) + } + + // only sync ns1 but expect nothing to be GC + test(t, kube, ns1, ns1+dep1, false) + }) + + t.Run("sync doesn't update or delete a pre-existing resource marked with ignore: 'true'", func(t *testing.T) { kube, _, cancel := setup(t) defer cancel() diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index 570e3463f..c9ec95f34 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -6,13 +6,12 @@ import ( ) const ( - DisableGarbageCollect = Policy("disable_gc") - Ignore = Policy("ignore") - Locked = Policy("locked") - LockedUser = Policy("locked_user") - LockedMsg = Policy("locked_msg") - Automated = Policy("automated") - TagAll = Policy("tag_all") + Ignore = Policy("ignore") + Locked = Policy("locked") + LockedUser = Policy("locked_user") + LockedMsg = Policy("locked_msg") + Automated = Policy("automated") + TagAll = Policy("tag_all") ) // Policy is an string, denoting the current deployment policy of a service, @@ -21,7 +20,7 @@ type Policy string func Boolean(policy Policy) bool { switch policy { - case Locked, Automated, Ignore, DisableGarbageCollect: + case Locked, Automated, Ignore: return true } return false From e3839499db7947c9be3c3594af954e3a32cdf0be Mon Sep 17 00:00:00 2001 From: Philip Laine Date: Sat, 29 Feb 2020 17:48:07 +0100 Subject: [PATCH 3/5] Update docs with new annotation value --- docs/faq.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 7affd90d4..6fb817c1d 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -234,7 +234,7 @@ Now restart `fluxd` to re-read the k8s secret (if it is running): `kubectl delete $(kubectl get pod -o name -l name=flux)` -If you have installed flux through Helm, make sure to pass +If you have installed flux through Helm, make sure to pass `--set git.secretName=flux-git-deploy` when installing/upgrading the chart. ### How do I use a private git host (or one that's not github.com, gitlab.com, bitbucket.org, dev.azure.com, or vs-ssh.visualstudio.com)? @@ -341,6 +341,18 @@ how/where to undo the change (cf [flux#1211](https://github.com/fluxcd/flux/issu annotation exists in either the cluster or in git, it will be respected, so you may need to remove it from both places. +Additionally when garbage collection is enabled. Flux will not garbage collect resources in the cluster +with the ignore annotation if the resource is removed from git. + +### Can I disable garbage collection for a specific resource? + +Yes. By adding the annotation below to a resource Flux will sync updates from git, but it will not +garbage collect when the resource is removed from git. + +```yaml + fluxcd.io/ignore: sync_only +``` + ### How can I prevent Flux overriding the replicas when using HPA? When using a horizontal pod autoscaler you have to remove the `spec.replicas` from your deployment definition. From 7b2d98ad03060cb9c41387a6f2190ff66fde4e18 Mon Sep 17 00:00:00 2001 From: Philip Laine Date: Mon, 2 Mar 2020 22:41:28 +0100 Subject: [PATCH 4/5] Make sync only value constant --- pkg/cluster/kubernetes/sync.go | 2 +- pkg/policy/policy.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/cluster/kubernetes/sync.go b/pkg/cluster/kubernetes/sync.go index e82392fe5..2bd718a8a 100644 --- a/pkg/cluster/kubernetes/sync.go +++ b/pkg/cluster/kubernetes/sync.go @@ -151,7 +151,7 @@ func (c *Cluster) collectGarbage( } v, ok := res.Policies().Get(policy.Ignore) - if ok && v == "sync_only" { + if ok && v == policy.IgnoreSyncOnly { c.logger.Log("info", "skipping GC of cluster resource; resource has ignore policy sync_only ", "dry-run", dryRun, "resource", resourceID) continue } diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index c9ec95f34..93aea64f8 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -14,6 +14,8 @@ const ( TagAll = Policy("tag_all") ) +const IgnoreSyncOnly = "sync_only" + // Policy is an string, denoting the current deployment policy of a service, // e.g. automated, or locked. type Policy string From c0f7160207c24306bb5c6faf2a25cbc437aac52e Mon Sep 17 00:00:00 2001 From: Philip Laine Date: Mon, 2 Mar 2020 22:41:46 +0100 Subject: [PATCH 5/5] Fix docs grammar --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 6fb817c1d..8aaee294d 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -341,7 +341,7 @@ how/where to undo the change (cf [flux#1211](https://github.com/fluxcd/flux/issu annotation exists in either the cluster or in git, it will be respected, so you may need to remove it from both places. -Additionally when garbage collection is enabled. Flux will not garbage collect resources in the cluster +Additionally, when garbage collection is enabled, Flux will not garbage collect resources in the cluster with the ignore annotation if the resource is removed from git. ### Can I disable garbage collection for a specific resource?