Skip to content

Commit 3c277bd

Browse files
authored
Merge pull request #3227 from jonathan-innis/add-unsafe-disable-get-options
✨ Add UnsafeDisableDeepCopy to GetOptions
2 parents 71f7db5 + 121afaa commit 3c277bd

File tree

4 files changed

+82
-28
lines changed

4 files changed

+82
-28
lines changed

pkg/cache/cache_test.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2462,27 +2462,43 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca
24622462
})
24632463
})
24642464
})
2465-
Describe("use UnsafeDisableDeepCopy list options", func() {
2466-
It("should be able to change object in informer cache", func() {
2467-
By("listing pods")
2468-
out := corev1.PodList{}
2469-
Expect(informerCache.List(context.Background(), &out, client.UnsafeDisableDeepCopy)).To(Succeed())
2470-
for _, item := range out.Items {
2471-
if strings.Compare(item.Name, "test-pod-3") == 0 { // test-pod-3 has labels
2472-
item.Labels["UnsafeDisableDeepCopy"] = "true"
2473-
break
2465+
Context("using UnsafeDisableDeepCopy", func() {
2466+
Describe("with ListOptions", func() {
2467+
It("should be able to change object in informer cache", func() {
2468+
By("listing pods")
2469+
out := corev1.PodList{}
2470+
Expect(informerCache.List(context.Background(), &out, client.UnsafeDisableDeepCopy)).To(Succeed())
2471+
for _, item := range out.Items {
2472+
if strings.Compare(item.Name, "test-pod-3") == 0 { // test-pod-3 has labels
2473+
item.Labels["UnsafeDisableDeepCopy"] = "true"
2474+
break
2475+
}
24742476
}
2475-
}
24762477

2477-
By("verifying that the returned pods were changed")
2478-
out2 := corev1.PodList{}
2479-
Expect(informerCache.List(context.Background(), &out, client.UnsafeDisableDeepCopy)).To(Succeed())
2480-
for _, item := range out2.Items {
2481-
if strings.Compare(item.Name, "test-pod-3") == 0 {
2482-
Expect(item.Labels["UnsafeDisableDeepCopy"]).To(Equal("true"))
2483-
break
2478+
By("verifying that the returned pods were changed")
2479+
out2 := corev1.PodList{}
2480+
Expect(informerCache.List(context.Background(), &out, client.UnsafeDisableDeepCopy)).To(Succeed())
2481+
for _, item := range out2.Items {
2482+
if strings.Compare(item.Name, "test-pod-3") == 0 {
2483+
Expect(item.Labels["UnsafeDisableDeepCopy"]).To(Equal("true"))
2484+
break
2485+
}
24842486
}
2485-
}
2487+
})
2488+
})
2489+
Describe("with GetOptions", func() {
2490+
It("should be able to change object in informer cache", func() {
2491+
out := corev1.Pod{}
2492+
podKey := client.ObjectKey{Name: "test-pod-2", Namespace: testNamespaceTwo}
2493+
Expect(informerCache.Get(context.Background(), podKey, &out, client.UnsafeDisableDeepCopy)).To(Succeed())
2494+
2495+
out.Labels["UnsafeDisableDeepCopy"] = "true"
2496+
2497+
By("verifying that the returned pod was changed")
2498+
out2 := corev1.Pod{}
2499+
Expect(informerCache.Get(context.Background(), podKey, &out2, client.UnsafeDisableDeepCopy)).To(Succeed())
2500+
Expect(out2.Labels["UnsafeDisableDeepCopy"]).To(Equal("true"))
2501+
})
24862502
})
24872503
})
24882504
})

pkg/cache/internal/cache_reader.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ type CacheReader struct {
5454
}
5555

5656
// Get checks the indexer for the object and writes a copy of it if found.
57-
func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Object, _ ...client.GetOption) error {
57+
func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Object, opts ...client.GetOption) error {
58+
getOpts := client.GetOptions{}
59+
getOpts.ApplyOptions(opts)
60+
5861
if c.scopeName == apimeta.RESTScopeNameRoot {
5962
key.Namespace = ""
6063
}
@@ -81,7 +84,7 @@ func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Ob
8184
return fmt.Errorf("cache contained %T, which is not an Object", obj)
8285
}
8386

84-
if c.disableDeepCopy {
87+
if c.disableDeepCopy || (getOpts.UnsafeDisableDeepCopy != nil && *getOpts.UnsafeDisableDeepCopy) {
8588
// skip deep copy which might be unsafe
8689
// you must DeepCopy any object before mutating it outside
8790
} else {
@@ -97,7 +100,7 @@ func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out client.Ob
97100
return fmt.Errorf("cache had type %s, but %s was asked for", objVal.Type(), outVal.Type())
98101
}
99102
reflect.Indirect(outVal).Set(reflect.Indirect(objVal))
100-
if !c.disableDeepCopy {
103+
if !c.disableDeepCopy && (getOpts.UnsafeDisableDeepCopy == nil || !*getOpts.UnsafeDisableDeepCopy) {
101104
out.GetObjectKind().SetGroupVersionKind(c.groupVersionKind)
102105
}
103106

pkg/client/options.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"k8s.io/apimachinery/pkg/fields"
2222
"k8s.io/apimachinery/pkg/labels"
2323
"k8s.io/apimachinery/pkg/selection"
24+
"k8s.io/utils/ptr"
2425
)
2526

2627
// {{{ "Functional" Option Interfaces
@@ -431,6 +432,12 @@ type GetOptions struct {
431432
// Raw represents raw GetOptions, as passed to the API server. Note
432433
// that these may not be respected by all implementations of interface.
433434
Raw *metav1.GetOptions
435+
436+
// UnsafeDisableDeepCopy indicates not to deep copy objects during get object.
437+
// Be very careful with this, when enabled you must DeepCopy any object before mutating it,
438+
// otherwise you will mutate the object in the cache.
439+
// +optional
440+
UnsafeDisableDeepCopy *bool
434441
}
435442

436443
var _ GetOption = &GetOptions{}
@@ -440,6 +447,9 @@ func (o *GetOptions) ApplyToGet(lo *GetOptions) {
440447
if o.Raw != nil {
441448
lo.Raw = o.Raw
442449
}
450+
if o.UnsafeDisableDeepCopy != nil {
451+
lo.UnsafeDisableDeepCopy = o.UnsafeDisableDeepCopy
452+
}
443453
}
444454

445455
// AsGetOptions returns these options as a flattened metav1.GetOptions.
@@ -692,15 +702,14 @@ func (l Limit) ApplyToList(opts *ListOptions) {
692702
// otherwise you will mutate the object in the cache.
693703
type UnsafeDisableDeepCopyOption bool
694704

705+
// ApplyToGet applies this configuration to the given an Get options.
706+
func (d UnsafeDisableDeepCopyOption) ApplyToGet(opts *GetOptions) {
707+
opts.UnsafeDisableDeepCopy = ptr.To(bool(d))
708+
}
709+
695710
// ApplyToList applies this configuration to the given an List options.
696711
func (d UnsafeDisableDeepCopyOption) ApplyToList(opts *ListOptions) {
697-
definitelyTrue := true
698-
definitelyFalse := false
699-
if d {
700-
opts.UnsafeDisableDeepCopy = &definitelyTrue
701-
} else {
702-
opts.UnsafeDisableDeepCopy = &definitelyFalse
703-
}
712+
opts.UnsafeDisableDeepCopy = ptr.To(bool(d))
704713
}
705714

706715
// UnsafeDisableDeepCopy indicates not to deep copy objects during list objects.

pkg/client/options_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ var _ = Describe("ListOptions", func() {
6666
o.ApplyToList(newListOpts)
6767
Expect(newListOpts).To(Equal(o))
6868
})
69+
It("Should set UnsafeDisableDeepCopy", func() {
70+
definitelyTrue := true
71+
o := &client.ListOptions{UnsafeDisableDeepCopy: &definitelyTrue}
72+
newListOpts := &client.ListOptions{}
73+
o.ApplyToList(newListOpts)
74+
Expect(newListOpts).To(Equal(o))
75+
})
76+
It("Should set UnsafeDisableDeepCopy through option", func() {
77+
listOpts := &client.ListOptions{}
78+
client.UnsafeDisableDeepCopy.ApplyToList(listOpts)
79+
Expect(listOpts.UnsafeDisableDeepCopy).ToNot(BeNil())
80+
Expect(*listOpts.UnsafeDisableDeepCopy).To(BeTrue())
81+
})
6982
It("Should not set anything", func() {
7083
o := &client.ListOptions{}
7184
newListOpts := &client.ListOptions{}
@@ -81,6 +94,19 @@ var _ = Describe("GetOptions", func() {
8194
o.ApplyToGet(newGetOpts)
8295
Expect(newGetOpts).To(Equal(o))
8396
})
97+
It("Should set UnsafeDisableDeepCopy", func() {
98+
definitelyTrue := true
99+
o := &client.GetOptions{UnsafeDisableDeepCopy: &definitelyTrue}
100+
newGetOpts := &client.GetOptions{}
101+
o.ApplyToGet(newGetOpts)
102+
Expect(newGetOpts).To(Equal(o))
103+
})
104+
It("Should set UnsafeDisableDeepCopy through option", func() {
105+
getOpts := &client.GetOptions{}
106+
client.UnsafeDisableDeepCopy.ApplyToGet(getOpts)
107+
Expect(getOpts.UnsafeDisableDeepCopy).ToNot(BeNil())
108+
Expect(*getOpts.UnsafeDisableDeepCopy).To(BeTrue())
109+
})
84110
})
85111

86112
var _ = Describe("CreateOptions", func() {

0 commit comments

Comments
 (0)