1
16
17 package client_test
18
19 import (
20 "context"
21 "fmt"
22 "sync/atomic"
23
24 . "github.com/onsi/ginkgo/v2"
25 . "github.com/onsi/gomega"
26 appsv1 "k8s.io/api/apps/v1"
27 corev1 "k8s.io/api/core/v1"
28 apierrors "k8s.io/apimachinery/pkg/api/errors"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/types"
31 "k8s.io/utils/ptr"
32
33 "sigs.k8s.io/controller-runtime/pkg/client"
34 )
35
36 var _ = Describe("DryRunClient", func() {
37 var dep *appsv1.Deployment
38 var count uint64 = 0
39 var replicaCount int32 = 2
40 var ns = "default"
41 ctx := context.Background()
42
43 getClient := func() client.Client {
44 cl, err := client.New(cfg, client.Options{DryRun: ptr.To(true)})
45 Expect(err).NotTo(HaveOccurred())
46 Expect(cl).NotTo(BeNil())
47 return cl
48 }
49
50 BeforeEach(func() {
51 atomic.AddUint64(&count, 1)
52 dep = &appsv1.Deployment{
53 ObjectMeta: metav1.ObjectMeta{
54 Name: fmt.Sprintf("dry-run-deployment-%v", count),
55 Namespace: ns,
56 Labels: map[string]string{"name": fmt.Sprintf("dry-run-deployment-%v", count)},
57 },
58 Spec: appsv1.DeploymentSpec{
59 Replicas: &replicaCount,
60 Selector: &metav1.LabelSelector{
61 MatchLabels: map[string]string{"foo": "bar"},
62 },
63 Template: corev1.PodTemplateSpec{
64 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
65 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "nginx", Image: "nginx"}}},
66 },
67 },
68 }
69
70 var err error
71 dep, err = clientset.AppsV1().Deployments(ns).Create(ctx, dep, metav1.CreateOptions{})
72 Expect(err).NotTo(HaveOccurred())
73 })
74
75 AfterEach(func() {
76 deleteDeployment(ctx, dep, ns)
77 })
78
79 It("should successfully Get an object", func() {
80 name := types.NamespacedName{Namespace: ns, Name: dep.Name}
81 result := &appsv1.Deployment{}
82
83 Expect(getClient().Get(ctx, name, result)).NotTo(HaveOccurred())
84 Expect(result).To(BeEquivalentTo(dep))
85 })
86
87 It("should successfully List objects", func() {
88 result := &appsv1.DeploymentList{}
89 opts := client.MatchingLabels(dep.Labels)
90
91 Expect(getClient().List(ctx, result, opts)).NotTo(HaveOccurred())
92
93 Expect(len(result.Items)).To(BeEquivalentTo(1))
94 Expect(result.Items[0]).To(BeEquivalentTo(*dep))
95 })
96
97 It("should not create an object", func() {
98 newDep := dep.DeepCopy()
99 newDep.Name = "new-deployment"
100
101 Expect(getClient().Create(ctx, newDep)).ToNot(HaveOccurred())
102
103 _, err := clientset.AppsV1().Deployments(ns).Get(ctx, newDep.Name, metav1.GetOptions{})
104 Expect(apierrors.IsNotFound(err)).To(BeTrue())
105 })
106
107 It("should not create an object with opts", func() {
108 newDep := dep.DeepCopy()
109 newDep.Name = "new-deployment"
110 opts := &client.CreateOptions{DryRun: []string{"Bye", "Pippa"}}
111
112 Expect(getClient().Create(ctx, newDep, opts)).ToNot(HaveOccurred())
113
114 _, err := clientset.AppsV1().Deployments(ns).Get(ctx, newDep.Name, metav1.GetOptions{})
115 Expect(apierrors.IsNotFound(err)).To(BeTrue())
116 })
117
118 It("should refuse a create request for an invalid object", func() {
119 changedDep := dep.DeepCopy()
120 changedDep.Spec.Template.Spec.Containers = nil
121
122 err := getClient().Create(ctx, changedDep)
123 Expect(apierrors.IsInvalid(err)).To(BeTrue())
124 })
125
126 It("should not change objects via update", func() {
127 changedDep := dep.DeepCopy()
128 *changedDep.Spec.Replicas = 2
129
130 Expect(getClient().Update(ctx, changedDep)).ToNot(HaveOccurred())
131
132 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
133 Expect(err).NotTo(HaveOccurred())
134 Expect(actual).NotTo(BeNil())
135 Expect(actual).To(BeEquivalentTo(dep))
136 })
137
138 It("should not change objects via update with opts", func() {
139 changedDep := dep.DeepCopy()
140 *changedDep.Spec.Replicas = 2
141 opts := &client.UpdateOptions{DryRun: []string{"Bye", "Pippa"}}
142
143 Expect(getClient().Update(ctx, changedDep, opts)).ToNot(HaveOccurred())
144
145 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
146 Expect(err).NotTo(HaveOccurred())
147 Expect(actual).NotTo(BeNil())
148 Expect(actual).To(BeEquivalentTo(dep))
149 })
150
151 It("should refuse an update request for an invalid change", func() {
152 changedDep := dep.DeepCopy()
153 changedDep.Spec.Template.Spec.Containers = nil
154
155 err := getClient().Update(ctx, changedDep)
156 Expect(apierrors.IsInvalid(err)).To(BeTrue())
157 })
158
159 It("should not change objects via patch", func() {
160 changedDep := dep.DeepCopy()
161 *changedDep.Spec.Replicas = 2
162
163 Expect(getClient().Patch(ctx, changedDep, client.MergeFrom(dep))).ToNot(HaveOccurred())
164
165 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
166 Expect(err).NotTo(HaveOccurred())
167 Expect(actual).NotTo(BeNil())
168 Expect(actual).To(BeEquivalentTo(dep))
169 })
170
171 It("should not change objects via patch with opts", func() {
172 changedDep := dep.DeepCopy()
173 *changedDep.Spec.Replicas = 2
174 opts := &client.PatchOptions{DryRun: []string{"Bye", "Pippa"}}
175
176 Expect(getClient().Patch(ctx, changedDep, client.MergeFrom(dep), opts)).ToNot(HaveOccurred())
177
178 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
179 Expect(err).NotTo(HaveOccurred())
180 Expect(actual).NotTo(BeNil())
181 Expect(actual).To(BeEquivalentTo(dep))
182 })
183
184 It("should not delete objects", func() {
185 Expect(getClient().Delete(ctx, dep)).NotTo(HaveOccurred())
186
187 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
188 Expect(err).NotTo(HaveOccurred())
189 Expect(actual).NotTo(BeNil())
190 Expect(actual).To(BeEquivalentTo(dep))
191 })
192
193 It("should not delete objects with opts", func() {
194 opts := &client.DeleteOptions{DryRun: []string{"Bye", "Pippa"}}
195
196 Expect(getClient().Delete(ctx, dep, opts)).NotTo(HaveOccurred())
197
198 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
199 Expect(err).NotTo(HaveOccurred())
200 Expect(actual).NotTo(BeNil())
201 Expect(actual).To(BeEquivalentTo(dep))
202 })
203
204 It("should not delete objects via deleteAllOf", func() {
205 opts := []client.DeleteAllOfOption{client.InNamespace(ns), client.MatchingLabels(dep.Labels)}
206
207 Expect(getClient().DeleteAllOf(ctx, dep, opts...)).NotTo(HaveOccurred())
208
209 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
210 Expect(err).NotTo(HaveOccurred())
211 Expect(actual).NotTo(BeNil())
212 Expect(actual).To(BeEquivalentTo(dep))
213 })
214
215 It("should not change objects via update status", func() {
216 changedDep := dep.DeepCopy()
217 changedDep.Status.Replicas = 99
218
219 Expect(getClient().Status().Update(ctx, changedDep)).NotTo(HaveOccurred())
220
221 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
222 Expect(err).NotTo(HaveOccurred())
223 Expect(actual).NotTo(BeNil())
224 Expect(actual).To(BeEquivalentTo(dep))
225 })
226
227 It("should not change objects via update status with opts", func() {
228 changedDep := dep.DeepCopy()
229 changedDep.Status.Replicas = 99
230 opts := &client.SubResourceUpdateOptions{UpdateOptions: client.UpdateOptions{DryRun: []string{"Bye", "Pippa"}}}
231
232 Expect(getClient().Status().Update(ctx, changedDep, opts)).NotTo(HaveOccurred())
233
234 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
235 Expect(err).NotTo(HaveOccurred())
236 Expect(actual).NotTo(BeNil())
237 Expect(actual).To(BeEquivalentTo(dep))
238 })
239
240 It("should not change objects via status patch", func() {
241 changedDep := dep.DeepCopy()
242 changedDep.Status.Replicas = 99
243
244 Expect(getClient().Status().Patch(ctx, changedDep, client.MergeFrom(dep))).ToNot(HaveOccurred())
245
246 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
247 Expect(err).NotTo(HaveOccurred())
248 Expect(actual).NotTo(BeNil())
249 Expect(actual).To(BeEquivalentTo(dep))
250 })
251
252 It("should not change objects via status patch with opts", func() {
253 changedDep := dep.DeepCopy()
254 changedDep.Status.Replicas = 99
255
256 opts := &client.SubResourcePatchOptions{PatchOptions: client.PatchOptions{DryRun: []string{"Bye", "Pippa"}}}
257
258 Expect(getClient().Status().Patch(ctx, changedDep, client.MergeFrom(dep), opts)).ToNot(HaveOccurred())
259
260 actual, err := clientset.AppsV1().Deployments(ns).Get(ctx, dep.Name, metav1.GetOptions{})
261 Expect(err).NotTo(HaveOccurred())
262 Expect(actual).NotTo(BeNil())
263 Expect(actual).To(BeEquivalentTo(dep))
264 })
265 })
266
View as plain text