Skip to content

Commit 8a3fc2d

Browse files
authored
Merge pull request kubernetes#92589 from ii/create-deployment-resource-lifecycle-test
Create AppsV1Deployment resource lifecycle test - +6 endpoint coverage
2 parents e884b40 + ee29022 commit 8a3fc2d

File tree

1 file changed

+285
-0
lines changed

1 file changed

+285
-0
lines changed

test/e2e/apps/deployment.go

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package apps
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"fmt"
2223
"math/rand"
2324
"time"
@@ -32,11 +33,15 @@ import (
3233
v1 "k8s.io/api/core/v1"
3334
apierrors "k8s.io/apimachinery/pkg/api/errors"
3435
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36+
unstructuredv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3537
"k8s.io/apimachinery/pkg/labels"
38+
"k8s.io/apimachinery/pkg/runtime"
39+
"k8s.io/apimachinery/pkg/runtime/schema"
3640
"k8s.io/apimachinery/pkg/types"
3741
"k8s.io/apimachinery/pkg/util/intstr"
3842
"k8s.io/apimachinery/pkg/util/wait"
3943
"k8s.io/apimachinery/pkg/watch"
44+
"k8s.io/client-go/dynamic"
4045
clientset "k8s.io/client-go/kubernetes"
4146
appsclient "k8s.io/client-go/kubernetes/typed/apps/v1"
4247
watchtools "k8s.io/client-go/tools/watch"
@@ -50,6 +55,7 @@ import (
5055
e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
5156
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
5257
testutil "k8s.io/kubernetes/test/utils"
58+
imageutils "k8s.io/kubernetes/test/utils/image"
5359
utilpointer "k8s.io/utils/pointer"
5460
)
5561

@@ -67,6 +73,7 @@ var (
6773
var _ = SIGDescribe("Deployment", func() {
6874
var ns string
6975
var c clientset.Interface
76+
var dc dynamic.Interface
7077

7178
ginkgo.AfterEach(func() {
7279
failureTrap(c, ns)
@@ -77,6 +84,7 @@ var _ = SIGDescribe("Deployment", func() {
7784
ginkgo.BeforeEach(func() {
7885
c = f.ClientSet
7986
ns = f.Namespace.Name
87+
dc = f.DynamicClient
8088
})
8189

8290
ginkgo.It("deployment reaping should cascade to its replica sets and pods", func() {
@@ -134,6 +142,283 @@ var _ = SIGDescribe("Deployment", func() {
134142
})
135143
// TODO: add tests that cover deployment.Spec.MinReadySeconds once we solved clock-skew issues
136144
// 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+
})
137422
})
138423

139424
func failureTrap(c clientset.Interface, ns string) {

0 commit comments

Comments
 (0)