sigs.k8s.io/controller-runtime@v0.18.2/pkg/client/dryrun_test.go (about) 1 /* 2 Copyright 2020 The Kubernetes 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 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 })