@@ -18,6 +18,7 @@ package apps
18
18
19
19
import (
20
20
"context"
21
+ "encoding/json"
21
22
"fmt"
22
23
"math/rand"
23
24
"time"
@@ -32,11 +33,15 @@ import (
32
33
v1 "k8s.io/api/core/v1"
33
34
apierrors "k8s.io/apimachinery/pkg/api/errors"
34
35
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36
+ unstructuredv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
35
37
"k8s.io/apimachinery/pkg/labels"
38
+ "k8s.io/apimachinery/pkg/runtime"
39
+ "k8s.io/apimachinery/pkg/runtime/schema"
36
40
"k8s.io/apimachinery/pkg/types"
37
41
"k8s.io/apimachinery/pkg/util/intstr"
38
42
"k8s.io/apimachinery/pkg/util/wait"
39
43
"k8s.io/apimachinery/pkg/watch"
44
+ "k8s.io/client-go/dynamic"
40
45
clientset "k8s.io/client-go/kubernetes"
41
46
appsclient "k8s.io/client-go/kubernetes/typed/apps/v1"
42
47
watchtools "k8s.io/client-go/tools/watch"
@@ -50,6 +55,7 @@ import (
50
55
e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
51
56
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
52
57
testutil "k8s.io/kubernetes/test/utils"
58
+ imageutils "k8s.io/kubernetes/test/utils/image"
53
59
utilpointer "k8s.io/utils/pointer"
54
60
)
55
61
67
73
var _ = SIGDescribe ("Deployment" , func () {
68
74
var ns string
69
75
var c clientset.Interface
76
+ var dc dynamic.Interface
70
77
71
78
ginkgo .AfterEach (func () {
72
79
failureTrap (c , ns )
@@ -77,6 +84,7 @@ var _ = SIGDescribe("Deployment", func() {
77
84
ginkgo .BeforeEach (func () {
78
85
c = f .ClientSet
79
86
ns = f .Namespace .Name
87
+ dc = f .DynamicClient
80
88
})
81
89
82
90
ginkgo .It ("deployment reaping should cascade to its replica sets and pods" , func () {
@@ -134,6 +142,283 @@ var _ = SIGDescribe("Deployment", func() {
134
142
})
135
143
// TODO: add tests that cover deployment.Spec.MinReadySeconds once we solved clock-skew issues
136
144
// See https://github.com/kubernetes/kubernetes/issues/29229
145
+
146
+ ginkgo .It ("should run the lifecycle of a Deployment" , func () {
147
+ deploymentResource := schema.GroupVersionResource {Group : "apps" , Version : "v1" , Resource : "deployments" }
148
+ testNamespaceName := f .Namespace .Name
149
+ testDeploymentName := "test-deployment"
150
+ testDeploymentInitialImage := imageutils .GetE2EImage (imageutils .Agnhost )
151
+ testDeploymentPatchImage := imageutils .GetE2EImage (imageutils .Pause )
152
+ testDeploymentUpdateImage := imageutils .GetE2EImage (imageutils .Httpd )
153
+ testDeploymentDefaultReplicas := int32 (3 )
154
+ testDeploymentMinimumReplicas := int32 (1 )
155
+ testDeploymentNoReplicas := int32 (0 )
156
+ testDeploymentLabels := map [string ]string {"test-deployment-static" : "true" }
157
+ testDeploymentLabelsFlat := "test-deployment-static=true"
158
+ testDeploymentLabelSelectors := metav1.LabelSelector {
159
+ MatchLabels : testDeploymentLabels ,
160
+ }
161
+ w := & cache.ListWatch {
162
+ WatchFunc : func (options metav1.ListOptions ) (watch.Interface , error ) {
163
+ options .LabelSelector = testDeploymentLabelsFlat
164
+ return f .ClientSet .AppsV1 ().Deployments (testNamespaceName ).Watch (context .TODO (), options )
165
+ },
166
+ }
167
+ deploymentsList , err := f .ClientSet .AppsV1 ().Deployments ("" ).List (context .TODO (), metav1.ListOptions {LabelSelector : testDeploymentLabelsFlat })
168
+ framework .ExpectNoError (err , "failed to list Deployments" )
169
+
170
+ ginkgo .By ("creating a Deployment" )
171
+ testDeployment := appsv1.Deployment {
172
+ ObjectMeta : metav1.ObjectMeta {
173
+ Name : testDeploymentName ,
174
+ Labels : map [string ]string {"test-deployment-static" : "true" },
175
+ },
176
+ Spec : appsv1.DeploymentSpec {
177
+ Replicas : & testDeploymentDefaultReplicas ,
178
+ Selector : & testDeploymentLabelSelectors ,
179
+ Template : v1.PodTemplateSpec {
180
+ ObjectMeta : metav1.ObjectMeta {
181
+ Labels : testDeploymentLabelSelectors .MatchLabels ,
182
+ },
183
+ Spec : v1.PodSpec {
184
+ Containers : []v1.Container {{
185
+ Name : testDeploymentName ,
186
+ Image : testDeploymentInitialImage ,
187
+ }},
188
+ },
189
+ },
190
+ },
191
+ }
192
+ _ , err = f .ClientSet .AppsV1 ().Deployments (testNamespaceName ).Create (context .TODO (), & testDeployment , metav1.CreateOptions {})
193
+ framework .ExpectNoError (err , "failed to create Deployment %v in namespace %v" , testDeploymentName , testNamespaceName )
194
+
195
+ ginkgo .By ("waiting for Deployment to be created" )
196
+ ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
197
+ defer cancel ()
198
+ _ , err = watchtools .Until (ctx , deploymentsList .ResourceVersion , w , func (event watch.Event ) (bool , error ) {
199
+ switch event .Type {
200
+ case watch .Added :
201
+ if deployment , ok := event .Object .(* appsv1.Deployment ); ok {
202
+ found := deployment .ObjectMeta .Name == testDeployment .Name &&
203
+ deployment .Labels ["test-deployment-static" ] == "true"
204
+ return found , nil
205
+ }
206
+ default :
207
+ framework .Logf ("observed event type %v" , event .Type )
208
+ }
209
+ return false , nil
210
+ })
211
+ framework .ExpectNoError (err , "failed to see %v event" , watch .Added )
212
+
213
+ ginkgo .By ("waiting for all Replicas to be Ready" )
214
+ ctx , cancel = context .WithTimeout (context .Background (), 30 * time .Second )
215
+ defer cancel ()
216
+ _ , err = watchtools .Until (ctx , deploymentsList .ResourceVersion , w , func (event watch.Event ) (bool , error ) {
217
+ if deployment , ok := event .Object .(* appsv1.Deployment ); ok {
218
+ found := deployment .ObjectMeta .Name == testDeployment .Name &&
219
+ deployment .Labels ["test-deployment-static" ] == "true" &&
220
+ deployment .Status .AvailableReplicas == testDeploymentDefaultReplicas &&
221
+ deployment .Status .ReadyReplicas == testDeploymentDefaultReplicas
222
+ return found , nil
223
+ }
224
+ return false , nil
225
+ })
226
+ framework .ExpectNoError (err , "failed to see replicas of %v in namespace %v scale to requested amount of %v" , testDeployment .Name , testNamespaceName , testDeploymentDefaultReplicas )
227
+
228
+ ginkgo .By ("patching the Deployment" )
229
+ deploymentPatch , err := json .Marshal (map [string ]interface {}{
230
+ "metadata" : map [string ]interface {}{
231
+ "labels" : map [string ]string {"test-deployment" : "patched" },
232
+ },
233
+ "spec" : map [string ]interface {}{
234
+ "replicas" : testDeploymentMinimumReplicas ,
235
+ "template" : map [string ]interface {}{
236
+ "spec" : map [string ]interface {}{
237
+ "containers" : [1 ]map [string ]interface {}{{
238
+ "name" : testDeploymentName ,
239
+ "image" : testDeploymentPatchImage ,
240
+ "command" : []string {"/bin/sleep" , "100000" },
241
+ }},
242
+ },
243
+ },
244
+ },
245
+ })
246
+ framework .ExpectNoError (err , "failed to Marshal Deployment JSON patch" )
247
+ _ , err = f .ClientSet .AppsV1 ().Deployments (testNamespaceName ).Patch (context .TODO (), testDeploymentName , types .StrategicMergePatchType , []byte (deploymentPatch ), metav1.PatchOptions {})
248
+ framework .ExpectNoError (err , "failed to patch Deployment" )
249
+ ctx , cancel = context .WithTimeout (context .Background (), 30 * time .Second )
250
+ defer cancel ()
251
+ _ , err = watchtools .Until (ctx , deploymentsList .ResourceVersion , w , func (event watch.Event ) (bool , error ) {
252
+ switch event .Type {
253
+ case watch .Modified :
254
+ if deployment , ok := event .Object .(* appsv1.Deployment ); ok {
255
+ found := deployment .ObjectMeta .Name == testDeployment .Name &&
256
+ deployment .Labels ["test-deployment-static" ] == "true"
257
+ return found , nil
258
+ }
259
+ default :
260
+ framework .Logf ("observed event type %v" , event .Type )
261
+ }
262
+ return false , nil
263
+ })
264
+ framework .ExpectNoError (err , "failed to see %v event" , watch .Modified )
265
+
266
+ ginkgo .By ("waiting for Replicas to scale" )
267
+ ctx , cancel = context .WithTimeout (context .Background (), 30 * time .Second )
268
+ defer cancel ()
269
+ _ , err = watchtools .Until (ctx , deploymentsList .ResourceVersion , w , func (event watch.Event ) (bool , error ) {
270
+ if deployment , ok := event .Object .(* appsv1.Deployment ); ok {
271
+ found := deployment .ObjectMeta .Name == testDeployment .Name &&
272
+ deployment .Labels ["test-deployment-static" ] == "true" &&
273
+ deployment .Status .AvailableReplicas == testDeploymentMinimumReplicas &&
274
+ deployment .Status .ReadyReplicas == testDeploymentMinimumReplicas &&
275
+ deployment .Spec .Template .Spec .Containers [0 ].Image == testDeploymentPatchImage
276
+ return found , nil
277
+ }
278
+ return false , nil
279
+ })
280
+ framework .ExpectNoError (err , "failed to see replicas of %v in namespace %v scale to requested amount of %v" , testDeployment .Name , testNamespaceName , testDeploymentMinimumReplicas )
281
+
282
+ ginkgo .By ("listing Deployments" )
283
+ deploymentsList , err = f .ClientSet .AppsV1 ().Deployments ("" ).List (context .TODO (), metav1.ListOptions {LabelSelector : testDeploymentLabelsFlat })
284
+ framework .ExpectNoError (err , "failed to list Deployments" )
285
+ foundDeployment := false
286
+ for _ , deploymentItem := range deploymentsList .Items {
287
+ if deploymentItem .ObjectMeta .Name == testDeploymentName &&
288
+ deploymentItem .ObjectMeta .Namespace == testNamespaceName &&
289
+ deploymentItem .ObjectMeta .Labels ["test-deployment-static" ] == "true" {
290
+ foundDeployment = true
291
+ break
292
+ }
293
+ }
294
+ framework .ExpectEqual (foundDeployment , true , "unable to find the Deployment in list" , deploymentsList )
295
+
296
+ ginkgo .By ("updating the Deployment" )
297
+ testDeploymentUpdate := testDeployment
298
+ testDeploymentUpdate .ObjectMeta .Labels ["test-deployment" ] = "updated"
299
+ testDeploymentUpdate .Spec .Template .Spec .Containers [0 ].Image = testDeploymentUpdateImage
300
+ testDeploymentDefaultReplicasPointer := & testDeploymentDefaultReplicas
301
+ testDeploymentUpdate .Spec .Replicas = testDeploymentDefaultReplicasPointer
302
+ testDeploymentUpdateUnstructuredMap , err := runtime .DefaultUnstructuredConverter .ToUnstructured (& testDeploymentUpdate )
303
+ framework .ExpectNoError (err , "failed to convert to unstructured" )
304
+ testDeploymentUpdateUnstructured := unstructuredv1.Unstructured {
305
+ Object : testDeploymentUpdateUnstructuredMap ,
306
+ }
307
+ // currently this hasn't been able to hit the endpoint replaceAppsV1NamespacedDeploymentStatus
308
+ _ , err = dc .Resource (deploymentResource ).Namespace (testNamespaceName ).Update (context .TODO (), & testDeploymentUpdateUnstructured , metav1.UpdateOptions {}) //, "status")
309
+ framework .ExpectNoError (err , "failed to update the DeploymentStatus" )
310
+ ctx , cancel = context .WithTimeout (context .Background (), 30 * time .Second )
311
+ defer cancel ()
312
+ _ , err = watchtools .Until (ctx , deploymentsList .ResourceVersion , w , func (event watch.Event ) (bool , error ) {
313
+ switch event .Type {
314
+ case watch .Modified :
315
+ if deployment , ok := event .Object .(* appsv1.Deployment ); ok {
316
+ found := deployment .ObjectMeta .Name == testDeployment .Name &&
317
+ deployment .Labels ["test-deployment-static" ] == "true"
318
+ return found , nil
319
+ }
320
+ default :
321
+ framework .Logf ("observed event type %v" , event .Type )
322
+ }
323
+ return false , nil
324
+ })
325
+ framework .ExpectNoError (err , "failed to see %v event" , watch .Modified )
326
+
327
+ ginkgo .By ("fetching the DeploymentStatus" )
328
+ deploymentGetUnstructured , err := dc .Resource (deploymentResource ).Namespace (testNamespaceName ).Get (context .TODO (), testDeploymentName , metav1.GetOptions {}, "status" )
329
+ framework .ExpectNoError (err , "failed to fetch the Deployment" )
330
+ deploymentGet := appsv1.Deployment {}
331
+ err = runtime .DefaultUnstructuredConverter .FromUnstructured (deploymentGetUnstructured .Object , & deploymentGet )
332
+ framework .ExpectNoError (err , "failed to convert the unstructured response to a Deployment" )
333
+ framework .ExpectEqual (deploymentGet .Spec .Template .Spec .Containers [0 ].Image , testDeploymentUpdateImage , "failed to update image" )
334
+ framework .ExpectEqual (deploymentGet .ObjectMeta .Labels ["test-deployment" ], "updated" , "failed to update labels" )
335
+
336
+ ctx , cancel = context .WithTimeout (context .Background (), 30 * time .Second )
337
+ defer cancel ()
338
+ _ , err = watchtools .Until (ctx , deploymentsList .ResourceVersion , w , func (event watch.Event ) (bool , error ) {
339
+ if deployment , ok := event .Object .(* appsv1.Deployment ); ok {
340
+ found := deployment .ObjectMeta .Name == testDeployment .Name &&
341
+ deployment .Labels ["test-deployment-static" ] == "true" &&
342
+ deployment .Status .AvailableReplicas == testDeploymentDefaultReplicas &&
343
+ deployment .Status .ReadyReplicas == testDeploymentDefaultReplicas
344
+ return found , nil
345
+ }
346
+ return false , nil
347
+ })
348
+ framework .ExpectNoError (err , "failed to see replicas of %v in namespace %v scale to requested amount of %v" , testDeployment .Name , testNamespaceName , testDeploymentDefaultReplicas )
349
+
350
+ ginkgo .By ("patching the DeploymentStatus" )
351
+ deploymentStatusPatch , err := json .Marshal (map [string ]interface {}{
352
+ "metadata" : map [string ]interface {}{
353
+ "labels" : map [string ]string {"test-deployment" : "patched-status" },
354
+ },
355
+ "status" : map [string ]interface {}{
356
+ "readyReplicas" : testDeploymentNoReplicas ,
357
+ },
358
+ })
359
+ framework .ExpectNoError (err , "failed to Marshal Deployment JSON patch" )
360
+ dc .Resource (deploymentResource ).Namespace (testNamespaceName ).Patch (context .TODO (), testDeploymentName , types .StrategicMergePatchType , []byte (deploymentStatusPatch ), metav1.PatchOptions {}, "status" )
361
+ ctx , cancel = context .WithTimeout (context .Background (), 30 * time .Second )
362
+ defer cancel ()
363
+ _ , err = watchtools .Until (ctx , deploymentsList .ResourceVersion , w , func (event watch.Event ) (bool , error ) {
364
+ switch event .Type {
365
+ case watch .Modified :
366
+ if deployment , ok := event .Object .(* appsv1.Deployment ); ok {
367
+ found := deployment .ObjectMeta .Name == testDeployment .Name &&
368
+ deployment .Labels ["test-deployment-static" ] == "true"
369
+ return found , nil
370
+ }
371
+ default :
372
+ framework .Logf ("observed event type %v" , event .Type )
373
+ }
374
+ return false , nil
375
+ })
376
+ framework .ExpectNoError (err , "failed to see %v event" , watch .Modified )
377
+
378
+ ginkgo .By ("fetching the DeploymentStatus" )
379
+ deploymentGetUnstructured , err = dc .Resource (deploymentResource ).Namespace (testNamespaceName ).Get (context .TODO (), testDeploymentName , metav1.GetOptions {}, "status" )
380
+ framework .ExpectNoError (err , "failed to fetch the DeploymentStatus" )
381
+ deploymentGet = appsv1.Deployment {}
382
+ err = runtime .DefaultUnstructuredConverter .FromUnstructured (deploymentGetUnstructured .Object , & deploymentGet )
383
+ framework .ExpectNoError (err , "failed to convert the unstructured response to a Deployment" )
384
+ framework .ExpectEqual (deploymentGet .Spec .Template .Spec .Containers [0 ].Image , testDeploymentUpdateImage , "failed to update image" )
385
+ framework .ExpectEqual (deploymentGet .ObjectMeta .Labels ["test-deployment" ], "updated" , "failed to update labels" )
386
+ ctx , cancel = context .WithTimeout (context .Background (), 30 * time .Second )
387
+ defer cancel ()
388
+ _ , err = watchtools .Until (ctx , deploymentsList .ResourceVersion , w , func (event watch.Event ) (bool , error ) {
389
+ if deployment , ok := event .Object .(* appsv1.Deployment ); ok {
390
+ found := deployment .ObjectMeta .Name == testDeployment .Name &&
391
+ deployment .Labels ["test-deployment-static" ] == "true" &&
392
+ deployment .Status .AvailableReplicas == testDeploymentDefaultReplicas &&
393
+ deployment .Status .ReadyReplicas == testDeploymentDefaultReplicas &&
394
+ deployment .Spec .Template .Spec .Containers [0 ].Image == testDeploymentUpdateImage
395
+ return found , nil
396
+ }
397
+ return false , nil
398
+ })
399
+ framework .ExpectNoError (err , "failed to see replicas of %v in namespace %v scale to requested amount of %v" , testDeployment .Name , testNamespaceName , testDeploymentDefaultReplicas )
400
+
401
+ ginkgo .By ("deleting the Deployment" )
402
+ err = f .ClientSet .AppsV1 ().Deployments (testNamespaceName ).DeleteCollection (context .TODO (), metav1.DeleteOptions {}, metav1.ListOptions {LabelSelector : testDeploymentLabelsFlat })
403
+ framework .ExpectNoError (err , "failed to delete Deployment via collection" )
404
+
405
+ ctx , cancel = context .WithTimeout (context .Background (), 30 * time .Second )
406
+ defer cancel ()
407
+ _ , err = watchtools .Until (ctx , deploymentsList .ResourceVersion , w , func (event watch.Event ) (bool , error ) {
408
+ switch event .Type {
409
+ case watch .Deleted :
410
+ if deployment , ok := event .Object .(* appsv1.Deployment ); ok {
411
+ found := deployment .ObjectMeta .Name == testDeployment .Name &&
412
+ deployment .Labels ["test-deployment-static" ] == "true"
413
+ return found , nil
414
+ }
415
+ default :
416
+ framework .Logf ("observed event type %v" , event .Type )
417
+ }
418
+ return false , nil
419
+ })
420
+ framework .ExpectNoError (err , "failed to see %v event" , watch .Deleted )
421
+ })
137
422
})
138
423
139
424
func failureTrap (c clientset.Interface , ns string ) {
0 commit comments