Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated Create AppsV1Deployment resource lifecycle test - +6 endpoint coverage #93458

Merged
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
302 changes: 302 additions & 0 deletions test/e2e/apps/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,20 @@ import (
"k8s.io/apimachinery/pkg/fields"
"k8s.io/client-go/tools/cache"

"encoding/json"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
unstructuredv1 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic"
clientset "k8s.io/client-go/kubernetes"
appsclient "k8s.io/client-go/kubernetes/typed/apps/v1"
watchtools "k8s.io/client-go/tools/watch"
Expand All @@ -50,6 +55,7 @@ import (
e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
testutil "k8s.io/kubernetes/test/utils"
imageutils "k8s.io/kubernetes/test/utils/image"
utilpointer "k8s.io/utils/pointer"
)

Expand All @@ -67,6 +73,7 @@ var (
var _ = SIGDescribe("Deployment", func() {
var ns string
var c clientset.Interface
var dc dynamic.Interface

ginkgo.AfterEach(func() {
failureTrap(c, ns)
Expand All @@ -77,6 +84,7 @@ var _ = SIGDescribe("Deployment", func() {
ginkgo.BeforeEach(func() {
c = f.ClientSet
ns = f.Namespace.Name
dc = f.DynamicClient
})

ginkgo.It("deployment reaping should cascade to its replica sets and pods", func() {
Expand Down Expand Up @@ -134,6 +142,300 @@ var _ = SIGDescribe("Deployment", func() {
})
// TODO: add tests that cover deployment.Spec.MinReadySeconds once we solved clock-skew issues
// See https://github.com/kubernetes/kubernetes/issues/29229

ginkgo.It("should run the lifecycle of a Deployment", func() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we separate this into multiple tests?
Current test is one of big tests by comparing the other tests in this test package and that would make it difficult to investigate issues if they happen.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

every individual test has setup/tear down cost, and our e2e jobs already trend long. A single coherent test that walks an object through its paces seems better to me.

deploymentResource := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}
testNamespaceName := f.Namespace.Name
testDeploymentName := "test-deployment"
testDeploymentInitialImage := imageutils.GetE2EImage(imageutils.Agnhost)
testDeploymentPatchImage := imageutils.GetE2EImage(imageutils.Pause)
testDeploymentUpdateImage := imageutils.GetE2EImage(imageutils.Httpd)
testDeploymentDefaultReplicas := int32(2)
testDeploymentMinimumReplicas := int32(1)
testDeploymentNoReplicas := int32(0)
testDeploymentLabels := map[string]string{"test-deployment-static": "true"}
testDeploymentLabelsFlat := "test-deployment-static=true"
testDeploymentLabelSelectors := metav1.LabelSelector{
MatchLabels: testDeploymentLabels,
}
w := &cache.ListWatch{
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
options.LabelSelector = testDeploymentLabelsFlat
return f.ClientSet.AppsV1().Deployments(testNamespaceName).Watch(context.TODO(), options)
},
}
deploymentsList, err := f.ClientSet.AppsV1().Deployments("").List(context.TODO(), metav1.ListOptions{LabelSelector: testDeploymentLabelsFlat})
framework.ExpectNoError(err, "failed to list Deployments")

ginkgo.By("creating a Deployment")
testDeployment := appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: testDeploymentName,
Labels: map[string]string{"test-deployment-static": "true"},
},
Spec: appsv1.DeploymentSpec{
Replicas: &testDeploymentDefaultReplicas,
Selector: &testDeploymentLabelSelectors,
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: testDeploymentLabelSelectors.MatchLabels,
},
Spec: v1.PodSpec{
Containers: []v1.Container{{
Name: testDeploymentName,
Image: testDeploymentInitialImage,
}},
},
},
},
}
_, err = f.ClientSet.AppsV1().Deployments(testNamespaceName).Create(context.TODO(), &testDeployment, metav1.CreateOptions{})
framework.ExpectNoError(err, "failed to create Deployment %v in namespace %v", testDeploymentName, testNamespaceName)

ginkgo.By("waiting for Deployment to be created")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = watchtools.Until(ctx, deploymentsList.ResourceVersion, w, func(event watch.Event) (bool, error) {
switch event.Type {
case watch.Added:
if deployment, ok := event.Object.(*appsv1.Deployment); ok {
found := deployment.ObjectMeta.Name == testDeployment.Name &&
deployment.ObjectMeta.Labels["test-deployment-static"] == "true"
return found, nil
}
default:
framework.Logf("observed event type %v", event.Type)
}
return false, nil
})
framework.ExpectNoError(err, "failed to see %v event", watch.Added)

ginkgo.By("waiting for all Replicas to be Ready")
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = watchtools.Until(ctx, deploymentsList.ResourceVersion, w, func(event watch.Event) (bool, error) {
if deployment, ok := event.Object.(*appsv1.Deployment); ok {
found := deployment.ObjectMeta.Name == testDeployment.Name &&
deployment.ObjectMeta.Labels["test-deployment-static"] == "true" &&
deployment.Status.ReadyReplicas == testDeploymentDefaultReplicas
if !found {
framework.Logf("observed Deployment %v in namespace %v with ReadyReplicas %v and labels %v", deployment.ObjectMeta.Name, deployment.ObjectMeta.Namespace, deployment.Status.ReadyReplicas, deployment.ObjectMeta.Labels)
}
return found, nil
}
return false, nil
})
framework.ExpectNoError(err, "failed to see replicas of %v in namespace %v scale to requested amount of %v", testDeployment.Name, testNamespaceName, testDeploymentDefaultReplicas)

ginkgo.By("patching the Deployment")
deploymentPatch, err := json.Marshal(map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]string{"test-deployment": "patched"},
},
"spec": map[string]interface{}{
"replicas": testDeploymentMinimumReplicas,
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": [1]map[string]interface{}{{
"name": testDeploymentName,
"image": testDeploymentPatchImage,
"command": []string{"/bin/sleep", "100000"},
}},
},
},
},
})
framework.ExpectNoError(err, "failed to Marshal Deployment JSON patch")
_, err = f.ClientSet.AppsV1().Deployments(testNamespaceName).Patch(context.TODO(), testDeploymentName, types.StrategicMergePatchType, []byte(deploymentPatch), metav1.PatchOptions{})
framework.ExpectNoError(err, "failed to patch Deployment")
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = watchtools.Until(ctx, deploymentsList.ResourceVersion, w, func(event watch.Event) (bool, error) {
switch event.Type {
case watch.Modified:
if deployment, ok := event.Object.(*appsv1.Deployment); ok {
found := deployment.ObjectMeta.Name == testDeployment.Name &&
deployment.ObjectMeta.Labels["test-deployment-static"] == "true"
if !found {
framework.Logf("observed Deployment %v in namespace %v with ReadyReplicas %v", deployment.ObjectMeta.Name, deployment.ObjectMeta.Namespace, deployment.Status.ReadyReplicas)
}
return found, nil
}
default:
framework.Logf("observed event type %v", event.Type)
}
return false, nil
})
framework.ExpectNoError(err, "failed to see %v event", watch.Modified)

ginkgo.By("waiting for Replicas to scale")
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = watchtools.Until(ctx, deploymentsList.ResourceVersion, w, func(event watch.Event) (bool, error) {
if deployment, ok := event.Object.(*appsv1.Deployment); ok {
found := deployment.ObjectMeta.Name == testDeployment.Name &&
deployment.ObjectMeta.Labels["test-deployment-static"] == "true" &&
deployment.Status.ReadyReplicas == testDeploymentMinimumReplicas &&
deployment.Spec.Template.Spec.Containers[0].Image == testDeploymentPatchImage
if !found {
framework.Logf("observed Deployment %v in namespace %v with ReadyReplicas %v", deployment.ObjectMeta.Name, deployment.ObjectMeta.Namespace, deployment.Status.ReadyReplicas)
}
return found, nil
}
return false, nil
})
framework.ExpectNoError(err, "failed to see replicas of %v in namespace %v scale to requested amount of %v", testDeployment.Name, testNamespaceName, testDeploymentMinimumReplicas)

ginkgo.By("listing Deployments")
deploymentsList, err = f.ClientSet.AppsV1().Deployments("").List(context.TODO(), metav1.ListOptions{LabelSelector: testDeploymentLabelsFlat})
framework.ExpectNoError(err, "failed to list Deployments")
foundDeployment := false
for _, deploymentItem := range deploymentsList.Items {
if deploymentItem.ObjectMeta.Name == testDeploymentName &&
deploymentItem.ObjectMeta.Namespace == testNamespaceName &&
deploymentItem.ObjectMeta.Labels["test-deployment-static"] == "true" {
foundDeployment = true
break
}
}
framework.ExpectEqual(foundDeployment, true, "unable to find the Deployment in list", deploymentsList)

ginkgo.By("updating the Deployment")
testDeploymentUpdate := testDeployment
testDeploymentUpdate.ObjectMeta.Labels["test-deployment"] = "updated"
testDeploymentUpdate.Spec.Template.Spec.Containers[0].Image = testDeploymentUpdateImage
testDeploymentDefaultReplicasPointer := &testDeploymentDefaultReplicas
testDeploymentUpdate.Spec.Replicas = testDeploymentDefaultReplicasPointer
testDeploymentUpdateUnstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&testDeploymentUpdate)
framework.ExpectNoError(err, "failed to convert to unstructured")
testDeploymentUpdateUnstructured := unstructuredv1.Unstructured{
Object: testDeploymentUpdateUnstructuredMap,
}
// currently this hasn't been able to hit the endpoint replaceAppsV1NamespacedDeploymentStatus
_, err = dc.Resource(deploymentResource).Namespace(testNamespaceName).Update(context.TODO(), &testDeploymentUpdateUnstructured, metav1.UpdateOptions{}) //, "status")
framework.ExpectNoError(err, "failed to update the DeploymentStatus")
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = watchtools.Until(ctx, deploymentsList.ResourceVersion, w, func(event watch.Event) (bool, error) {
switch event.Type {
case watch.Modified:
if deployment, ok := event.Object.(*appsv1.Deployment); ok {
found := deployment.ObjectMeta.Name == testDeployment.Name &&
deployment.ObjectMeta.Labels["test-deployment-static"] == "true"
if !found {
framework.Logf("observed Deployment %v in namespace %v with ReadyReplicas %v", deployment.ObjectMeta.Name, deployment.ObjectMeta.Namespace, deployment.Status.ReadyReplicas)
}
return found, nil
}
default:
framework.Logf("observed event type %v", event.Type)
}
return false, nil
})
framework.ExpectNoError(err, "failed to see %v event", watch.Modified)

ginkgo.By("fetching the DeploymentStatus")
deploymentGetUnstructured, err := dc.Resource(deploymentResource).Namespace(testNamespaceName).Get(context.TODO(), testDeploymentName, metav1.GetOptions{}, "status")
framework.ExpectNoError(err, "failed to fetch the Deployment")
deploymentGet := appsv1.Deployment{}
err = runtime.DefaultUnstructuredConverter.FromUnstructured(deploymentGetUnstructured.Object, &deploymentGet)
framework.ExpectNoError(err, "failed to convert the unstructured response to a Deployment")
framework.ExpectEqual(deploymentGet.Spec.Template.Spec.Containers[0].Image, testDeploymentUpdateImage, "failed to update image")
framework.ExpectEqual(deploymentGet.ObjectMeta.Labels["test-deployment"], "updated", "failed to update labels")

ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = watchtools.Until(ctx, deploymentsList.ResourceVersion, w, func(event watch.Event) (bool, error) {
if deployment, ok := event.Object.(*appsv1.Deployment); ok {
found := deployment.ObjectMeta.Name == testDeployment.Name &&
deployment.ObjectMeta.Labels["test-deployment-static"] == "true" &&
deployment.Status.ReadyReplicas == testDeploymentDefaultReplicas
if !found {
framework.Logf("observed Deployment %v in namespace %v with ReadyReplicas %v and labels %v", deployment.ObjectMeta.Name, deployment.ObjectMeta.Namespace, deployment.Status.ReadyReplicas, deployment.ObjectMeta.Labels)
}
return found, nil
}
return false, nil
})
framework.ExpectNoError(err, "failed to see replicas of %v in namespace %v scale to requested amount of %v", testDeployment.Name, testNamespaceName, testDeploymentDefaultReplicas)

ginkgo.By("patching the DeploymentStatus")
deploymentStatusPatch, err := json.Marshal(map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]string{"test-deployment": "patched-status"},
},
"status": map[string]interface{}{
"readyReplicas": testDeploymentNoReplicas,
},
})
framework.ExpectNoError(err, "failed to Marshal Deployment JSON patch")
dc.Resource(deploymentResource).Namespace(testNamespaceName).Patch(context.TODO(), testDeploymentName, types.StrategicMergePatchType, []byte(deploymentStatusPatch), metav1.PatchOptions{}, "status")
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = watchtools.Until(ctx, deploymentsList.ResourceVersion, w, func(event watch.Event) (bool, error) {
switch event.Type {
case watch.Modified:
if deployment, ok := event.Object.(*appsv1.Deployment); ok {
found := deployment.ObjectMeta.Name == testDeployment.Name &&
deployment.ObjectMeta.Labels["test-deployment-static"] == "true"
return found, nil
}
default:
framework.Logf("observed event type %v", event.Type)
}
return false, nil
})
framework.ExpectNoError(err, "failed to see %v event", watch.Modified)

ginkgo.By("fetching the DeploymentStatus")
deploymentGetUnstructured, err = dc.Resource(deploymentResource).Namespace(testNamespaceName).Get(context.TODO(), testDeploymentName, metav1.GetOptions{}, "status")
framework.ExpectNoError(err, "failed to fetch the DeploymentStatus")
deploymentGet = appsv1.Deployment{}
err = runtime.DefaultUnstructuredConverter.FromUnstructured(deploymentGetUnstructured.Object, &deploymentGet)
framework.ExpectNoError(err, "failed to convert the unstructured response to a Deployment")
framework.ExpectEqual(deploymentGet.Spec.Template.Spec.Containers[0].Image, testDeploymentUpdateImage, "failed to update image")
framework.ExpectEqual(deploymentGet.ObjectMeta.Labels["test-deployment"], "updated", "failed to update labels")
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = watchtools.Until(ctx, deploymentsList.ResourceVersion, w, func(event watch.Event) (bool, error) {
if deployment, ok := event.Object.(*appsv1.Deployment); ok {
found := deployment.ObjectMeta.Name == testDeployment.Name &&
deployment.ObjectMeta.Labels["test-deployment-static"] == "true" &&
deployment.Status.ReadyReplicas == testDeploymentDefaultReplicas &&
deployment.Spec.Template.Spec.Containers[0].Image == testDeploymentUpdateImage
if !found {
framework.Logf("observed Deployment %v in namespace %v with ReadyReplicas %v", deployment.ObjectMeta.Name, deployment.ObjectMeta.Namespace, deployment.Status.ReadyReplicas)
}
return found, nil
}
return false, nil
})
framework.ExpectNoError(err, "failed to see replicas of %v in namespace %v scale to requested amount of %v", testDeployment.Name, testNamespaceName, testDeploymentDefaultReplicas)

ginkgo.By("deleting the Deployment")
err = f.ClientSet.AppsV1().Deployments(testNamespaceName).DeleteCollection(context.TODO(), metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: testDeploymentLabelsFlat})
framework.ExpectNoError(err, "failed to delete Deployment via collection")

ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err = watchtools.Until(ctx, deploymentsList.ResourceVersion, w, func(event watch.Event) (bool, error) {
switch event.Type {
case watch.Deleted:
if deployment, ok := event.Object.(*appsv1.Deployment); ok {
found := deployment.ObjectMeta.Name == testDeployment.Name &&
deployment.ObjectMeta.Labels["test-deployment-static"] == "true"
if !found {
framework.Logf("observed Deployment %v in namespace %v with ReadyReplicas %v", deployment.ObjectMeta.Name, deployment.ObjectMeta.Namespace, deployment.Status.ReadyReplicas)
}
return found, nil
}
default:
framework.Logf("observed event type %v", event.Type)
}
return false, nil
})
framework.ExpectNoError(err, "failed to see %v event", watch.Deleted)
})
})

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