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

Add integration test for deployment #45742

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions hack/.linted_packages
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ test/integration/apiserver
test/integration/client
test/integration/configmap
test/integration/defaulttolerationseconds
test/integration/deployment
test/integration/etcd
test/integration/examples
test/integration/federation
Expand Down
34 changes: 0 additions & 34 deletions test/e2e/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ var _ = framework.KubeDescribe("Deployment", func() {
// TODO: Add failure traps once we have JustAfterEach
// See https://github.com/onsi/ginkgo/issues/303

It("deployment should create new pods", func() {
testNewDeployment(f)
})
It("deployment reaping should cascade to its replica sets and pods", func() {
testDeleteDeployment(f)
})
Expand Down Expand Up @@ -192,37 +189,6 @@ func stopDeployment(c clientset.Interface, internalClient internalclientset.Inte
}
}

func testNewDeployment(f *framework.Framework) {
ns := f.Namespace.Name
c := f.ClientSet

deploymentName := "test-new-deployment"
podLabels := map[string]string{"name": nginxImageName}
replicas := int32(1)
framework.Logf("Creating simple deployment %s", deploymentName)
d := framework.NewDeployment(deploymentName, replicas, podLabels, nginxImageName, nginxImage, extensions.RollingUpdateDeploymentStrategyType)
d.Annotations = map[string]string{"test": "should-copy-to-replica-set", v1.LastAppliedConfigAnnotation: "should-not-copy-to-replica-set"}
deploy, err := c.Extensions().Deployments(ns).Create(d)
Expect(err).NotTo(HaveOccurred())

// Wait for it to be updated to revision 1
err = framework.WaitForDeploymentRevisionAndImage(c, ns, deploymentName, "1", nginxImage)
Expect(err).NotTo(HaveOccurred())

err = framework.WaitForDeploymentStatusValid(c, deploy)
Expect(err).NotTo(HaveOccurred())

deployment, err := c.Extensions().Deployments(ns).Get(deploymentName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred())
newRS, err := deploymentutil.GetNewReplicaSet(deployment, c)
Expect(err).NotTo(HaveOccurred())
// Check new RS annotations
Expect(newRS.Annotations["test"]).Should(Equal("should-copy-to-replica-set"))
Expect(newRS.Annotations[v1.LastAppliedConfigAnnotation]).Should(Equal(""))
Expect(deployment.Annotations["test"]).Should(Equal("should-copy-to-replica-set"))
Expect(deployment.Annotations[v1.LastAppliedConfigAnnotation]).Should(Equal("should-not-copy-to-replica-set"))
}

func testDeleteDeployment(f *framework.Framework) {
ns := f.Namespace.Name
c := f.ClientSet
Expand Down
165 changes: 9 additions & 156 deletions test/e2e/framework/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import (
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
testutil "k8s.io/kubernetes/test/utils"

"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
Expand Down Expand Up @@ -122,6 +123,9 @@ const (
// How often to Poll pods, nodes and claims.
Poll = 2 * time.Second

pollShortTimeout = 1 * time.Minute
pollLongTimeout = 5 * time.Minute

// service accounts are provisioned after namespace creation
// a service account is required to support pod creation in a namespace as part of admission control
ServiceAccountProvisionTimeout = 2 * time.Minute
Expand Down Expand Up @@ -3174,7 +3178,7 @@ func NewDeployment(deploymentName string, replicas int32, podLabels map[string]s
Name: deploymentName,
},
Spec: extensions.DeploymentSpec{
Replicas: func(i int32) *int32 { return &i }(replicas),
Replicas: &replicas,
Selector: &metav1.LabelSelector{MatchLabels: podLabels},
Strategy: extensions.DeploymentStrategy{
Type: strategyType,
Expand All @@ -3201,72 +3205,7 @@ func NewDeployment(deploymentName string, replicas int32, podLabels map[string]s
// Note that the status should stay valid at all times unless shortly after a scaling event or the deployment is just created.
// To verify that the deployment status is valid and wait for the rollout to finish, use WaitForDeploymentStatus instead.
func WaitForDeploymentStatusValid(c clientset.Interface, d *extensions.Deployment) error {
var (
oldRSs, allOldRSs, allRSs []*extensions.ReplicaSet
newRS *extensions.ReplicaSet
deployment *extensions.Deployment
reason string
)

err := wait.Poll(Poll, 5*time.Minute, func() (bool, error) {
var err error
deployment, err = c.Extensions().Deployments(d.Namespace).Get(d.Name, metav1.GetOptions{})
if err != nil {
return false, err
}
oldRSs, allOldRSs, newRS, err = deploymentutil.GetAllReplicaSets(deployment, c)
if err != nil {
return false, err
}
if newRS == nil {
// New RC hasn't been created yet.
reason = "new replica set hasn't been created yet"
Logf(reason)
return false, nil
}
allRSs = append(oldRSs, newRS)
// The old/new ReplicaSets need to contain the pod-template-hash label
for i := range allRSs {
if !labelsutil.SelectorHasLabel(allRSs[i].Spec.Selector, extensions.DefaultDeploymentUniqueLabelKey) {
reason = "all replica sets need to contain the pod-template-hash label"
Logf(reason)
return false, nil
}
}
totalCreated := deploymentutil.GetReplicaCountForReplicaSets(allRSs)
maxCreated := *(deployment.Spec.Replicas) + deploymentutil.MaxSurge(*deployment)
if totalCreated > maxCreated {
reason = fmt.Sprintf("total pods created: %d, more than the max allowed: %d", totalCreated, maxCreated)
Logf(reason)
return false, nil
}
minAvailable := deploymentutil.MinAvailable(deployment)
if deployment.Status.AvailableReplicas < minAvailable {
reason = fmt.Sprintf("total pods available: %d, less than the min required: %d", deployment.Status.AvailableReplicas, minAvailable)
Logf(reason)
return false, nil
}

// When the deployment status and its underlying resources reach the desired state, we're done
if deploymentutil.DeploymentComplete(deployment, &deployment.Status) {
return true, nil
}

reason = fmt.Sprintf("deployment status: %#v", deployment.Status)
Logf(reason)

return false, nil
})

if err == wait.ErrWaitTimeout {
logReplicaSetsOfDeployment(deployment, allOldRSs, newRS)
logPodsOfDeployment(c, deployment, allRSs)
err = fmt.Errorf("%s", reason)
}
if err != nil {
return fmt.Errorf("error waiting for deployment %q status to match expectation: %v", d.Name, err)
}
return nil
return testutil.WaitForDeploymentStatusValid(c, d, Logf, Poll, pollLongTimeout)
}

// Waits for the deployment to reach desired state.
Expand Down Expand Up @@ -3409,66 +3348,7 @@ func WatchRecreateDeployment(c clientset.Interface, d *extensions.Deployment) er
// WaitForDeploymentRevisionAndImage waits for the deployment's and its new RS's revision and container image to match the given revision and image.
// Note that deployment revision and its new RS revision should be updated shortly, so we only wait for 1 minute here to fail early.
func WaitForDeploymentRevisionAndImage(c clientset.Interface, ns, deploymentName string, revision, image string) error {
var deployment *extensions.Deployment
var newRS *extensions.ReplicaSet
var reason string
err := wait.Poll(Poll, 1*time.Minute, func() (bool, error) {
var err error
deployment, err = c.Extensions().Deployments(ns).Get(deploymentName, metav1.GetOptions{})
if err != nil {
return false, err
}
// The new ReplicaSet needs to be non-nil and contain the pod-template-hash label

newRS, err = deploymentutil.GetNewReplicaSet(deployment, c)

if err != nil {
return false, err
}
if newRS == nil {
reason = fmt.Sprintf("New replica set for deployment %q is yet to be created", deployment.Name)
Logf(reason)
return false, nil
}
if !labelsutil.SelectorHasLabel(newRS.Spec.Selector, extensions.DefaultDeploymentUniqueLabelKey) {
reason = fmt.Sprintf("New replica set %q doesn't have DefaultDeploymentUniqueLabelKey", newRS.Name)
Logf(reason)
return false, nil
}
// Check revision of this deployment, and of the new replica set of this deployment
if deployment.Annotations == nil || deployment.Annotations[deploymentutil.RevisionAnnotation] != revision {
reason = fmt.Sprintf("Deployment %q doesn't have the required revision set", deployment.Name)
Logf(reason)
return false, nil
}
if deployment.Spec.Template.Spec.Containers[0].Image != image {
reason = fmt.Sprintf("Deployment %q doesn't have the required image set", deployment.Name)
Logf(reason)
return false, nil
}
if newRS.Annotations == nil || newRS.Annotations[deploymentutil.RevisionAnnotation] != revision {
reason = fmt.Sprintf("New replica set %q doesn't have the required revision set", newRS.Name)
Logf(reason)
return false, nil
}
if newRS.Spec.Template.Spec.Containers[0].Image != image {
reason = fmt.Sprintf("New replica set %q doesn't have the required image set", newRS.Name)
Logf(reason)
return false, nil
}
return true, nil
})
if err == wait.ErrWaitTimeout {
logReplicaSetsOfDeployment(deployment, nil, newRS)
err = fmt.Errorf(reason)
}
if newRS == nil {
return fmt.Errorf("deployment %q failed to create new replica set", deploymentName)
}
if err != nil {
return fmt.Errorf("error waiting for deployment %q (got %s / %s) and new replica set %q (got %s / %s) revision and image to match expectation (expected %s / %s): %v", deploymentName, deployment.Annotations[deploymentutil.RevisionAnnotation], deployment.Spec.Template.Spec.Containers[0].Image, newRS.Name, newRS.Annotations[deploymentutil.RevisionAnnotation], newRS.Spec.Template.Spec.Containers[0].Image, revision, image, err)
}
return nil
return testutil.WaitForDeploymentRevisionAndImage(c, ns, deploymentName, revision, image, Logf, Poll, pollShortTimeout)
}

// CheckNewRSAnnotations check if the new RS's annotation is as expected
Expand Down Expand Up @@ -3533,17 +3413,7 @@ func WaitForDeploymentOldRSsNum(c clientset.Interface, ns, deploymentName string
}

func logReplicaSetsOfDeployment(deployment *extensions.Deployment, allOldRSs []*extensions.ReplicaSet, newRS *extensions.ReplicaSet) {
if newRS != nil {
Logf("New ReplicaSet of Deployment %s:\n%+v", deployment.Name, *newRS)
} else {
Logf("New ReplicaSet of Deployment %s is nil.", deployment.Name)
}
if len(allOldRSs) > 0 {
Logf("All old ReplicaSets of Deployment %s:", deployment.Name)
}
for i := range allOldRSs {
Logf("%+v", *allOldRSs[i])
}
testutil.LogReplicaSetsOfDeployment(deployment, allOldRSs, newRS, Logf)
}

func WaitForObservedDeployment(c clientset.Interface, ns, deploymentName string, desiredGeneration int64) error {
Expand Down Expand Up @@ -3575,24 +3445,7 @@ func WaitForDeploymentWithCondition(c clientset.Interface, ns, deploymentName, r
}

func logPodsOfDeployment(c clientset.Interface, deployment *extensions.Deployment, rsList []*extensions.ReplicaSet) {
minReadySeconds := deployment.Spec.MinReadySeconds
podListFunc := func(namespace string, options metav1.ListOptions) (*v1.PodList, error) {
return c.Core().Pods(namespace).List(options)
}

podList, err := deploymentutil.ListPods(deployment, rsList, podListFunc)

if err != nil {
Logf("Failed to list Pods of Deployment %s: %v", deployment.Name, err)
return
}
for _, pod := range podList.Items {
availability := "not available"
if podutil.IsPodAvailable(&pod, minReadySeconds, metav1.Now()) {
availability = "available"
}
Logf("Pod %s is %s:\n%+v", pod.Name, availability, pod)
}
testutil.LogPodsOfDeployment(c, deployment, rsList, Logf)
}

// Waits for the number of events on the given object to reach a desired count.
Expand Down
1 change: 1 addition & 0 deletions test/integration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ filegroup(
"//test/integration/client:all-srcs",
"//test/integration/configmap:all-srcs",
"//test/integration/defaulttolerationseconds:all-srcs",
"//test/integration/deployment:all-srcs",
"//test/integration/etcd:all-srcs",
"//test/integration/evictions:all-srcs",
"//test/integration/examples:all-srcs",
Expand Down
54 changes: 54 additions & 0 deletions test/integration/deployment/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package(default_visibility = ["//visibility:public"])

licenses(["notice"])

load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)

go_test(
name = "go_default_test",
srcs = ["deployment_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api/v1:go_default_library",
"//pkg/controller/deployment/util:go_default_library",
"//test/integration/framework:go_default_library",
],
)

go_library(
name = "go_default_library",
srcs = ["util.go"],
tags = ["automanaged"],
deps = [
"//pkg/api/v1:go_default_library",
"//pkg/api/v1/pod:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/informers/informers_generated/externalversions:go_default_library",
"//pkg/controller/deployment:go_default_library",
"//pkg/controller/replicaset:go_default_library",
"//test/integration/framework:go_default_library",
"//test/utils:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],
)

filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)

filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
6 changes: 6 additions & 0 deletions test/integration/deployment/OWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
reviewers:
- janetkuo
- kargakis
approvers:
- janetkuo
- kargakis