Skip to content

Commit c5c2a0b

Browse files
authored
objects with resource policy "keep" should use the annotation-based watch (#83)
1 parent 73b36a6 commit c5c2a0b

File tree

7 files changed

+182
-46
lines changed

7 files changed

+182
-46
lines changed

pkg/client/actionclient.go

+2-14
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"encoding/json"
2222
"errors"
2323
"fmt"
24-
"strings"
2524

2625
sdkhandler "github.com/operator-framework/operator-lib/handler"
2726
"gomodules.xyz/jsonpatch/v2"
@@ -46,6 +45,7 @@ import (
4645
"sigs.k8s.io/yaml"
4746

4847
"github.com/joelanford/helm-operator/pkg/internal/sdk/controllerutil"
48+
"github.com/joelanford/helm-operator/pkg/manifestutil"
4949
)
5050

5151
type ActionClientGetter interface {
@@ -326,7 +326,7 @@ func (pr *ownerPostRenderer) Run(in *bytes.Buffer) (*bytes.Buffer, error) {
326326
if err != nil {
327327
return err
328328
}
329-
if useOwnerRef && !containsResourcePolicyKeep(u.GetAnnotations()) {
329+
if useOwnerRef && !manifestutil.HasResourcePolicyKeep(u.GetAnnotations()) {
330330
ownerRef := metav1.NewControllerRef(pr.owner, pr.owner.GetObjectKind().GroupVersionKind())
331331
ownerRefs := append(u.GetOwnerReferences(), *ownerRef)
332332
u.SetOwnerReferences(ownerRefs)
@@ -349,15 +349,3 @@ func (pr *ownerPostRenderer) Run(in *bytes.Buffer) (*bytes.Buffer, error) {
349349
}
350350
return &out, nil
351351
}
352-
353-
func containsResourcePolicyKeep(annotations map[string]string) bool {
354-
if annotations == nil {
355-
return false
356-
}
357-
resourcePolicyType, ok := annotations[kube.ResourcePolicyAnno]
358-
if !ok {
359-
return false
360-
}
361-
resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType))
362-
return resourcePolicyType == kube.KeepPolicy
363-
}

pkg/client/actionclient_test.go

-28
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"context"
2222
"errors"
2323
"strconv"
24-
"strings"
2524

2625
. "github.com/onsi/ginkgo"
2726
. "github.com/onsi/gomega"
@@ -544,33 +543,6 @@ var _ = Describe("ActionClient", func() {
544543
Expect(err).NotTo(BeNil())
545544
})
546545
})
547-
548-
var _ = Describe("containsResourcePolicyKeep", func() {
549-
It("returns true on base case", func() {
550-
annotations := map[string]string{kube.ResourcePolicyAnno: kube.KeepPolicy}
551-
Expect(containsResourcePolicyKeep(annotations)).To(BeTrue())
552-
})
553-
It("returns false when annotation key is not found", func() {
554-
annotations := map[string]string{"not-" + kube.ResourcePolicyAnno: kube.KeepPolicy}
555-
Expect(containsResourcePolicyKeep(annotations)).To(BeFalse())
556-
})
557-
It("returns false when annotation value is not 'keep'", func() {
558-
annotations := map[string]string{"not-" + kube.ResourcePolicyAnno: "not-" + kube.KeepPolicy}
559-
Expect(containsResourcePolicyKeep(annotations)).To(BeFalse())
560-
})
561-
It("returns true when annotation is uppercase", func() {
562-
annotations := map[string]string{kube.ResourcePolicyAnno: strings.ToUpper(kube.KeepPolicy)}
563-
Expect(containsResourcePolicyKeep(annotations)).To(BeTrue())
564-
})
565-
It("returns true when annotation is has whitespace prefix and/or suffix", func() {
566-
annotations := map[string]string{kube.ResourcePolicyAnno: " " + kube.KeepPolicy + " "}
567-
Expect(containsResourcePolicyKeep(annotations)).To(BeTrue())
568-
})
569-
It("returns true when annotation is uppercase and has whitespace prefix and/or suffix", func() {
570-
annotations := map[string]string{kube.ResourcePolicyAnno: " " + strings.ToUpper(kube.KeepPolicy) + " "}
571-
Expect(containsResourcePolicyKeep(annotations)).To(BeTrue())
572-
})
573-
})
574546
})
575547

576548
func manifestToObjects(manifest string) []client.Object {
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright 2021 The Operator-SDK Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package manifestutil_test
18+
19+
import (
20+
"testing"
21+
22+
. "github.com/onsi/ginkgo"
23+
. "github.com/onsi/gomega"
24+
)
25+
26+
func TestManifest(t *testing.T) {
27+
RegisterFailHandler(Fail)
28+
RunSpecs(t, "Manifest Suite")
29+
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
Copyright 2021 The Operator-SDK Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package manifestutil
18+
19+
import (
20+
"strings"
21+
22+
"helm.sh/helm/v3/pkg/kube"
23+
)
24+
25+
func HasResourcePolicyKeep(annotations map[string]string) bool {
26+
if annotations == nil {
27+
return false
28+
}
29+
resourcePolicyType, ok := annotations[kube.ResourcePolicyAnno]
30+
if !ok {
31+
return false
32+
}
33+
resourcePolicyType = strings.ToLower(strings.TrimSpace(resourcePolicyType))
34+
return resourcePolicyType == kube.KeepPolicy
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
Copyright 2021 The Operator-SDK Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package manifestutil_test
18+
19+
import (
20+
"strings"
21+
22+
. "github.com/onsi/ginkgo"
23+
. "github.com/onsi/gomega"
24+
"helm.sh/helm/v3/pkg/kube"
25+
26+
"github.com/joelanford/helm-operator/pkg/manifestutil"
27+
)
28+
29+
var _ = Describe("HasResourcePolicyKeep", func() {
30+
It("returns false for nil annotations", func() {
31+
Expect(manifestutil.HasResourcePolicyKeep(nil)).To(BeFalse())
32+
})
33+
It("returns true on base case", func() {
34+
annotations := map[string]string{kube.ResourcePolicyAnno: kube.KeepPolicy}
35+
Expect(manifestutil.HasResourcePolicyKeep(annotations)).To(BeTrue())
36+
})
37+
It("returns false when annotation key is not found", func() {
38+
annotations := map[string]string{"not-" + kube.ResourcePolicyAnno: kube.KeepPolicy}
39+
Expect(manifestutil.HasResourcePolicyKeep(annotations)).To(BeFalse())
40+
})
41+
It("returns false when annotation value is not 'keep'", func() {
42+
annotations := map[string]string{"not-" + kube.ResourcePolicyAnno: "not-" + kube.KeepPolicy}
43+
Expect(manifestutil.HasResourcePolicyKeep(annotations)).To(BeFalse())
44+
})
45+
It("returns true when annotation is uppercase", func() {
46+
annotations := map[string]string{kube.ResourcePolicyAnno: strings.ToUpper(kube.KeepPolicy)}
47+
Expect(manifestutil.HasResourcePolicyKeep(annotations)).To(BeTrue())
48+
})
49+
It("returns true when annotation is has whitespace prefix and/or suffix", func() {
50+
annotations := map[string]string{kube.ResourcePolicyAnno: " " + kube.KeepPolicy + " "}
51+
Expect(manifestutil.HasResourcePolicyKeep(annotations)).To(BeTrue())
52+
})
53+
It("returns true when annotation is uppercase and has whitespace prefix and/or suffix", func() {
54+
annotations := map[string]string{kube.ResourcePolicyAnno: " " + strings.ToUpper(kube.KeepPolicy) + " "}
55+
Expect(manifestutil.HasResourcePolicyKeep(annotations)).To(BeTrue())
56+
})
57+
})

pkg/reconciler/internal/hook/hook.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/joelanford/helm-operator/pkg/hook"
3535
"github.com/joelanford/helm-operator/pkg/internal/sdk/controllerutil"
3636
"github.com/joelanford/helm-operator/pkg/internal/sdk/predicate"
37+
"github.com/joelanford/helm-operator/pkg/manifestutil"
3738
)
3839

3940
func NewDependentResourceWatcher(c controller.Controller, rm meta.RESTMapper) hook.PostHook {
@@ -77,7 +78,7 @@ func (d *dependentResourceWatcher) Exec(owner *unstructured.Unstructured, rel re
7778
return err
7879
}
7980

80-
if useOwnerRef {
81+
if useOwnerRef && !manifestutil.HasResourcePolicyKeep(obj.GetAnnotations()) {
8182
if err := d.controller.Watch(&source.Kind{Type: &obj}, &handler.EnqueueRequestForOwner{
8283
OwnerType: owner,
8384
IsController: true,

pkg/reconciler/internal/hook/hook_test.go

+57-3
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,18 @@ var _ = Describe("Hook", func() {
150150
Expect(c.WatchCalls[0].Handler).To(BeAssignableToTypeOf(&handler.EnqueueRequestForOwner{}))
151151
Expect(c.WatchCalls[1].Handler).To(BeAssignableToTypeOf(&handler.EnqueueRequestForOwner{}))
152152
})
153+
It("should watch resource policy keep resources with annotation handler", func() {
154+
rel = &release.Release{
155+
Manifest: strings.Join([]string{rsOwnerNamespaceWithKeep, ssOtherNamespaceWithKeep, clusterRoleWithKeep, clusterRoleBindingWithKeep}, "---\n"),
156+
}
157+
drw = internalhook.NewDependentResourceWatcher(c, rm)
158+
Expect(drw.Exec(owner, *rel, log)).To(Succeed())
159+
Expect(c.WatchCalls).To(HaveLen(4))
160+
Expect(c.WatchCalls[0].Handler).To(BeAssignableToTypeOf(&sdkhandler.EnqueueRequestForAnnotation{}))
161+
Expect(c.WatchCalls[1].Handler).To(BeAssignableToTypeOf(&sdkhandler.EnqueueRequestForAnnotation{}))
162+
Expect(c.WatchCalls[2].Handler).To(BeAssignableToTypeOf(&sdkhandler.EnqueueRequestForAnnotation{}))
163+
Expect(c.WatchCalls[3].Handler).To(BeAssignableToTypeOf(&sdkhandler.EnqueueRequestForAnnotation{}))
164+
})
153165
})
154166

155167
Context("when the owner is namespace-scoped", func() {
@@ -165,7 +177,6 @@ var _ = Describe("Hook", func() {
165177
},
166178
}
167179
})
168-
169180
It("should watch namespace-scoped dependent resources in the same namespace with ownerRef handler", func() {
170181
rel = &release.Release{
171182
Manifest: strings.Join([]string{rsOwnerNamespace}, "---\n"),
@@ -175,7 +186,6 @@ var _ = Describe("Hook", func() {
175186
Expect(c.WatchCalls).To(HaveLen(1))
176187
Expect(c.WatchCalls[0].Handler).To(BeAssignableToTypeOf(&handler.EnqueueRequestForOwner{}))
177188
})
178-
179189
It("should watch cluster-scoped resources with annotation handler", func() {
180190
rel = &release.Release{
181191
Manifest: strings.Join([]string{clusterRole}, "---\n"),
@@ -185,7 +195,6 @@ var _ = Describe("Hook", func() {
185195
Expect(c.WatchCalls).To(HaveLen(1))
186196
Expect(c.WatchCalls[0].Handler).To(BeAssignableToTypeOf(&sdkhandler.EnqueueRequestForAnnotation{}))
187197
})
188-
189198
It("should watch namespace-scoped resources in a different namespace with annotation handler", func() {
190199
rel = &release.Release{
191200
Manifest: strings.Join([]string{ssOtherNamespace}, "---\n"),
@@ -195,6 +204,17 @@ var _ = Describe("Hook", func() {
195204
Expect(c.WatchCalls).To(HaveLen(1))
196205
Expect(c.WatchCalls[0].Handler).To(BeAssignableToTypeOf(&sdkhandler.EnqueueRequestForAnnotation{}))
197206
})
207+
It("should watch resource policy keep resources with annotation handler", func() {
208+
rel = &release.Release{
209+
Manifest: strings.Join([]string{rsOwnerNamespaceWithKeep, ssOtherNamespaceWithKeep, clusterRoleWithKeep}, "---\n"),
210+
}
211+
drw = internalhook.NewDependentResourceWatcher(c, rm)
212+
Expect(drw.Exec(owner, *rel, log)).To(Succeed())
213+
Expect(c.WatchCalls).To(HaveLen(3))
214+
Expect(c.WatchCalls[0].Handler).To(BeAssignableToTypeOf(&sdkhandler.EnqueueRequestForAnnotation{}))
215+
Expect(c.WatchCalls[1].Handler).To(BeAssignableToTypeOf(&sdkhandler.EnqueueRequestForAnnotation{}))
216+
Expect(c.WatchCalls[2].Handler).To(BeAssignableToTypeOf(&sdkhandler.EnqueueRequestForAnnotation{}))
217+
})
198218
})
199219
})
200220
})
@@ -207,24 +227,58 @@ kind: ReplicaSet
207227
metadata:
208228
name: testReplicaSet
209229
namespace: ownerNamespace
230+
`
231+
rsOwnerNamespaceWithKeep = `
232+
apiVersion: apps/v1
233+
kind: ReplicaSet
234+
metadata:
235+
name: testReplicaSet
236+
namespace: ownerNamespace
237+
annotations:
238+
helm.sh/resource-policy: keep
210239
`
211240
ssOtherNamespace = `
212241
apiVersion: apps/v1
213242
kind: StatefulSet
214243
metadata:
215244
name: otherTestStatefulSet
216245
namespace: otherNamespace
246+
`
247+
ssOtherNamespaceWithKeep = `
248+
apiVersion: apps/v1
249+
kind: StatefulSet
250+
metadata:
251+
name: otherTestStatefulSet
252+
namespace: otherNamespace
253+
annotations:
254+
helm.sh/resource-policy: keep
217255
`
218256
clusterRole = `
219257
apiVersion: rbac.authorization.k8s.io/v1
220258
kind: ClusterRole
221259
metadata:
222260
name: testClusterRole
261+
`
262+
clusterRoleWithKeep = `
263+
apiVersion: rbac.authorization.k8s.io/v1
264+
kind: ClusterRole
265+
metadata:
266+
name: testClusterRole
267+
annotations:
268+
helm.sh/resource-policy: keep
223269
`
224270
clusterRoleBinding = `
225271
apiVersion: rbac.authorization.k8s.io/v1
226272
kind: ClusterRoleBinding
227273
metadata:
228274
name: testClusterRoleBinding
275+
`
276+
clusterRoleBindingWithKeep = `
277+
apiVersion: rbac.authorization.k8s.io/v1
278+
kind: ClusterRoleBinding
279+
metadata:
280+
name: testClusterRoleBinding
281+
annotations:
282+
helm.sh/resource-policy: keep
229283
`
230284
)

0 commit comments

Comments
 (0)