2017-05-12 19:44:31 +00:00
/ *
Copyright 2017 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package deployment
import (
2017-10-17 23:08:53 +00:00
"fmt"
2017-08-15 23:31:59 +00:00
"reflect"
"strings"
2017-05-12 19:44:31 +00:00
"testing"
2018-03-20 17:20:43 +00:00
apps "k8s.io/api/apps/v1"
2017-06-22 17:25:57 +00:00
"k8s.io/api/core/v1"
2017-08-15 23:31:59 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-10-30 21:43:27 +00:00
"k8s.io/apimachinery/pkg/util/intstr"
2018-02-02 00:21:07 +00:00
"k8s.io/apimachinery/pkg/util/uuid"
2017-09-22 07:57:27 +00:00
"k8s.io/apimachinery/pkg/util/wait"
2018-02-02 00:21:07 +00:00
"k8s.io/client-go/util/retry"
2017-05-12 19:44:31 +00:00
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
"k8s.io/kubernetes/test/integration/framework"
2018-07-27 11:47:02 +00:00
"k8s.io/utils/pointer"
2017-05-12 19:44:31 +00:00
)
func TestNewDeployment ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-new-deployment"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
replicas := int32 ( 20 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( name , ns . Name , replicas ) }
tester . deployment . Spec . MinReadySeconds = 4
tester . deployment . Annotations = map [ string ] string { "test" : "should-copy-to-replica-set" , v1 . LastAppliedConfigAnnotation : "should-not-copy-to-replica-set" }
2017-09-06 20:42:26 +00:00
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2017-05-12 19:44:31 +00:00
if err != nil {
2017-09-06 20:42:26 +00:00
t . Fatalf ( "failed to create deployment %s: %v" , tester . deployment . Name , err )
2017-05-12 19:44:31 +00:00
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Wait for the Deployment to be updated to revision 1
2017-09-07 20:09:05 +00:00
if err := tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
2017-05-12 19:44:31 +00:00
2017-10-13 21:09:46 +00:00
// Make sure the Deployment completes while manually marking Deployment pods as ready at the same time.
// Use soft check because this deployment was just created and rolling update strategy might be violated.
if err := tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
2017-09-07 20:09:05 +00:00
t . Fatal ( err )
}
2017-05-12 19:44:31 +00:00
// Check new RS annotations
2017-09-07 20:09:05 +00:00
newRS , err := tester . expectNewReplicaSet ( )
if err != nil {
t . Fatal ( err )
}
2017-05-12 19:44:31 +00:00
if newRS . Annotations [ "test" ] != "should-copy-to-replica-set" {
2017-09-06 20:42:26 +00:00
t . Errorf ( "expected new ReplicaSet annotations copied from Deployment %s, got: %v" , tester . deployment . Name , newRS . Annotations )
2017-05-12 19:44:31 +00:00
}
if newRS . Annotations [ v1 . LastAppliedConfigAnnotation ] != "" {
2017-09-06 20:42:26 +00:00
t . Errorf ( "expected new ReplicaSet last-applied annotation not copied from Deployment %s" , tester . deployment . Name )
2017-05-12 19:44:31 +00:00
}
2018-03-26 17:23:31 +00:00
// New RS should contain pod-template-hash in its selector, label, and template label
rsHash , err := checkRSHashLabels ( newRS )
if err != nil {
t . Error ( err )
}
// All pods targeted by the deployment should contain pod-template-hash in their labels
selector , err := metav1 . LabelSelectorAsSelector ( tester . deployment . Spec . Selector )
if err != nil {
t . Fatalf ( "failed to parse deployment %s selector: %v" , name , err )
}
pods , err := c . CoreV1 ( ) . Pods ( ns . Name ) . List ( metav1 . ListOptions { LabelSelector : selector . String ( ) } )
if err != nil {
t . Fatalf ( "failed to list pods of deployment %s: %v" , name , err )
}
if len ( pods . Items ) != int ( replicas ) {
t . Errorf ( "expected %d pods, got %d pods" , replicas , len ( pods . Items ) )
}
podHash , err := checkPodsHashLabel ( pods )
if err != nil {
t . Error ( err )
}
if rsHash != podHash {
t . Errorf ( "found mismatching pod-template-hash value: rs hash = %s whereas pod hash = %s" , rsHash , podHash )
}
2017-05-12 19:44:31 +00:00
}
2017-08-15 23:31:59 +00:00
2018-12-19 16:18:53 +00:00
// Deployments should support roll out, roll back, and roll over.
// TODO: drop the rollback portions of this test when extensions/v1beta1 is no longer served
// and rollback endpoint is no longer supported.
2017-10-30 21:43:27 +00:00
func TestDeploymentRollingUpdate ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-rolling-update-deployment"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
replicas := int32 ( 20 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( name , ns . Name , replicas ) }
tester . deployment . Spec . MinReadySeconds = 4
quarter := intstr . FromString ( "25%" )
2018-03-20 17:20:43 +00:00
tester . deployment . Spec . Strategy . RollingUpdate = & apps . RollingUpdateDeployment {
2017-10-30 21:43:27 +00:00
MaxUnavailable : & quarter ,
MaxSurge : & quarter ,
}
// Create a deployment.
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2017-10-30 21:43:27 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %s: %v" , tester . deployment . Name , err )
}
oriImage := tester . deployment . Spec . Template . Spec . Containers [ 0 ] . Image
if err := tester . waitForDeploymentRevisionAndImage ( "1" , oriImage ) ; err != nil {
t . Fatal ( err )
}
if err := tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatal ( err )
}
// 1. Roll out a new image.
image := "new-image"
if oriImage == image {
t . Fatalf ( "bad test setup, deployment %s roll out with the same image" , tester . deployment . Name )
}
2018-03-20 17:20:43 +00:00
imageFn := func ( update * apps . Deployment ) {
2017-10-30 21:43:27 +00:00
update . Spec . Template . Spec . Containers [ 0 ] . Image = image
}
tester . deployment , err = tester . updateDeployment ( imageFn )
if err != nil {
t . Fatalf ( "failed to update deployment %s: %v" , tester . deployment . Name , err )
}
if err := tester . waitForDeploymentRevisionAndImage ( "2" , image ) ; err != nil {
t . Fatal ( err )
}
if err := tester . waitForDeploymentCompleteAndCheckRollingAndMarkPodsReady ( ) ; err != nil {
t . Fatal ( err )
}
// 2. Roll back to the last revision.
revision := int64 ( 0 )
rollback := newDeploymentRollback ( tester . deployment . Name , nil , revision )
if err = c . ExtensionsV1beta1 ( ) . Deployments ( ns . Name ) . Rollback ( rollback ) ; err != nil {
t . Fatalf ( "failed to roll back deployment %s to last revision: %v" , tester . deployment . Name , err )
}
// Wait for the deployment to start rolling back
if err = tester . waitForDeploymentRollbackCleared ( ) ; err != nil {
t . Fatalf ( "failed to roll back deployment %s to last revision: %v" , tester . deployment . Name , err )
}
// Wait for the deployment to be rolled back to the template stored in revision 1 and rolled forward to revision 3.
if err := tester . waitForDeploymentRevisionAndImage ( "3" , oriImage ) ; err != nil {
t . Fatal ( err )
}
if err := tester . waitForDeploymentCompleteAndCheckRollingAndMarkPodsReady ( ) ; err != nil {
t . Fatal ( err )
}
// 3. Roll over a deployment before the previous rolling update finishes.
image = "dont-finish"
2018-03-20 17:20:43 +00:00
imageFn = func ( update * apps . Deployment ) {
2017-10-30 21:43:27 +00:00
update . Spec . Template . Spec . Containers [ 0 ] . Image = image
}
tester . deployment , err = tester . updateDeployment ( imageFn )
if err != nil {
t . Fatalf ( "failed to update deployment %s: %v" , tester . deployment . Name , err )
}
if err := tester . waitForDeploymentRevisionAndImage ( "4" , image ) ; err != nil {
t . Fatal ( err )
}
// We don't mark pods as ready so that rollout won't finish.
// Before the rollout finishes, trigger another rollout.
image = "rollover"
2018-03-20 17:20:43 +00:00
imageFn = func ( update * apps . Deployment ) {
2017-10-30 21:43:27 +00:00
update . Spec . Template . Spec . Containers [ 0 ] . Image = image
}
tester . deployment , err = tester . updateDeployment ( imageFn )
if err != nil {
t . Fatalf ( "failed to update deployment %s: %v" , tester . deployment . Name , err )
}
if err := tester . waitForDeploymentRevisionAndImage ( "5" , image ) ; err != nil {
t . Fatal ( err )
}
if err := tester . waitForDeploymentCompleteAndCheckRollingAndMarkPodsReady ( ) ; err != nil {
t . Fatal ( err )
}
2018-03-20 17:20:43 +00:00
_ , allOldRSs , err := deploymentutil . GetOldReplicaSets ( tester . deployment , c . AppsV1 ( ) )
2017-10-30 21:43:27 +00:00
if err != nil {
t . Fatalf ( "failed retrieving old replicasets of deployment %s: %v" , tester . deployment . Name , err )
}
for _ , oldRS := range allOldRSs {
if * oldRS . Spec . Replicas != 0 {
t . Errorf ( "expected old replicaset %s of deployment %s to have 0 replica, got %d" , oldRS . Name , tester . deployment . Name , * oldRS . Spec . Replicas )
}
}
}
2017-08-15 23:31:59 +00:00
// selectors are IMMUTABLE for all API versions except apps/v1beta1 and extensions/v1beta1
func TestDeploymentSelectorImmutability ( t * testing . T ) {
s , closeFn , c := dcSimpleSetup ( t )
defer closeFn ( )
name := "test-deployment-selector-immutability"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( name , ns . Name , int32 ( 20 ) ) }
2018-03-20 17:20:43 +00:00
var err error
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2017-08-15 23:31:59 +00:00
if err != nil {
2018-03-20 17:20:43 +00:00
t . Fatalf ( "failed to create apps/v1 deployment %s: %v" , tester . deployment . Name , err )
2017-08-15 23:31:59 +00:00
}
// test to ensure extensions/v1beta1 selector is mutable
2018-12-19 16:18:53 +00:00
// TODO: drop the extensions/v1beta1 portion of this test when extensions/v1beta1 is no longer served
2017-08-15 23:31:59 +00:00
newSelectorLabels := map [ string ] string { "name_extensions_v1beta1" : "test_extensions_v1beta1" }
2018-03-20 17:20:43 +00:00
deploymentExtensionsV1beta1 , err := c . ExtensionsV1beta1 ( ) . Deployments ( ns . Name ) . Get ( name , metav1 . GetOptions { } )
if err != nil {
t . Fatalf ( "failed to get extensions/v1beta deployment %s: %v" , name , err )
}
2017-08-15 23:31:59 +00:00
deploymentExtensionsV1beta1 . Spec . Selector . MatchLabels = newSelectorLabels
deploymentExtensionsV1beta1 . Spec . Template . Labels = newSelectorLabels
updatedDeploymentExtensionsV1beta1 , err := c . ExtensionsV1beta1 ( ) . Deployments ( ns . Name ) . Update ( deploymentExtensionsV1beta1 )
if err != nil {
t . Fatalf ( "failed to update extensions/v1beta1 deployment %s: %v" , deploymentExtensionsV1beta1 . Name , err )
}
if ! reflect . DeepEqual ( updatedDeploymentExtensionsV1beta1 . Spec . Selector . MatchLabels , newSelectorLabels ) {
t . Errorf ( "selector should be changed for extensions/v1beta1, expected: %v, got: %v" , newSelectorLabels , updatedDeploymentExtensionsV1beta1 . Spec . Selector . MatchLabels )
}
// test to ensure apps/v1beta1 selector is mutable
deploymentAppsV1beta1 , err := c . AppsV1beta1 ( ) . Deployments ( ns . Name ) . Get ( updatedDeploymentExtensionsV1beta1 . Name , metav1 . GetOptions { } )
if err != nil {
t . Fatalf ( "failed to get apps/v1beta1 deployment %s: %v" , updatedDeploymentExtensionsV1beta1 . Name , err )
}
newSelectorLabels = map [ string ] string { "name_apps_v1beta1" : "test_apps_v1beta1" }
deploymentAppsV1beta1 . Spec . Selector . MatchLabels = newSelectorLabels
deploymentAppsV1beta1 . Spec . Template . Labels = newSelectorLabels
updatedDeploymentAppsV1beta1 , err := c . AppsV1beta1 ( ) . Deployments ( ns . Name ) . Update ( deploymentAppsV1beta1 )
if err != nil {
t . Fatalf ( "failed to update apps/v1beta1 deployment %s: %v" , deploymentAppsV1beta1 . Name , err )
}
if ! reflect . DeepEqual ( updatedDeploymentAppsV1beta1 . Spec . Selector . MatchLabels , newSelectorLabels ) {
t . Errorf ( "selector should be changed for apps/v1beta1, expected: %v, got: %v" , newSelectorLabels , updatedDeploymentAppsV1beta1 . Spec . Selector . MatchLabels )
}
// test to ensure apps/v1beta2 selector is immutable
deploymentAppsV1beta2 , err := c . AppsV1beta2 ( ) . Deployments ( ns . Name ) . Get ( updatedDeploymentAppsV1beta1 . Name , metav1 . GetOptions { } )
if err != nil {
t . Fatalf ( "failed to get apps/v1beta2 deployment %s: %v" , updatedDeploymentAppsV1beta1 . Name , err )
}
newSelectorLabels = map [ string ] string { "name_apps_v1beta2" : "test_apps_v1beta2" }
deploymentAppsV1beta2 . Spec . Selector . MatchLabels = newSelectorLabels
deploymentAppsV1beta2 . Spec . Template . Labels = newSelectorLabels
_ , err = c . AppsV1beta2 ( ) . Deployments ( ns . Name ) . Update ( deploymentAppsV1beta2 )
if err == nil {
t . Fatalf ( "failed to provide validation error when changing immutable selector when updating apps/v1beta2 deployment %s" , deploymentAppsV1beta2 . Name )
}
expectedErrType := "Invalid value"
expectedErrDetail := "field is immutable"
if ! strings . Contains ( err . Error ( ) , expectedErrType ) || ! strings . Contains ( err . Error ( ) , expectedErrDetail ) {
t . Errorf ( "error message does not match, expected type: %s, expected detail: %s, got: %s" , expectedErrType , expectedErrDetail , err . Error ( ) )
}
2018-03-20 17:20:43 +00:00
// test to ensure apps/v1 selector is immutable
deploymentAppsV1 , err := c . AppsV1 ( ) . Deployments ( ns . Name ) . Get ( updatedDeploymentAppsV1beta1 . Name , metav1 . GetOptions { } )
if err != nil {
t . Fatalf ( "failed to get apps/v1 deployment %s: %v" , updatedDeploymentAppsV1beta1 . Name , err )
}
newSelectorLabels = map [ string ] string { "name_apps_v1" : "test_apps_v1" }
deploymentAppsV1 . Spec . Selector . MatchLabels = newSelectorLabels
deploymentAppsV1 . Spec . Template . Labels = newSelectorLabels
_ , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Update ( deploymentAppsV1 )
if err == nil {
t . Fatalf ( "failed to provide validation error when changing immutable selector when updating apps/v1 deployment %s" , deploymentAppsV1 . Name )
}
if ! strings . Contains ( err . Error ( ) , expectedErrType ) || ! strings . Contains ( err . Error ( ) , expectedErrDetail ) {
t . Errorf ( "error message does not match, expected type: %s, expected detail: %s, got: %s" , expectedErrType , expectedErrDetail , err . Error ( ) )
}
2017-08-15 23:31:59 +00:00
}
2017-09-06 20:42:26 +00:00
// Paused deployment should not start new rollout
func TestPausedDeployment ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-paused-deployment"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
replicas := int32 ( 1 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( name , ns . Name , replicas ) }
tester . deployment . Spec . Paused = true
tgps := int64 ( 1 )
tester . deployment . Spec . Template . Spec . TerminationGracePeriodSeconds = & tgps
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2017-09-06 20:42:26 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %s: %v" , tester . deployment . Name , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Verify that the paused deployment won't create new replica set.
2017-09-07 20:09:05 +00:00
if err := tester . expectNoNewReplicaSet ( ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// Resume the deployment
2017-09-22 08:06:59 +00:00
tester . deployment , err = tester . updateDeployment ( resumeFn )
2017-09-06 20:42:26 +00:00
if err != nil {
2017-09-07 20:09:05 +00:00
t . Fatalf ( "failed to resume deployment %s: %v" , tester . deployment . Name , err )
2017-09-06 20:42:26 +00:00
}
// Wait for the controller to notice the resume.
2017-09-07 20:09:05 +00:00
if err := tester . waitForObservedDeployment ( tester . deployment . Generation ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// Wait for the Deployment to be updated to revision 1
2017-09-07 20:09:05 +00:00
if err := tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
2017-10-13 21:09:46 +00:00
// Make sure the Deployment completes while manually marking Deployment pods as ready at the same time.
// Use soft check because this deployment was just created and rolling update strategy might be violated.
if err := tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
2017-09-07 20:09:05 +00:00
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// A new replicaset should be created.
2017-09-07 20:09:05 +00:00
if _ , err := tester . expectNewReplicaSet ( ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// Pause the deployment.
// The paused deployment shouldn't trigger a new rollout.
2017-09-22 08:06:59 +00:00
tester . deployment , err = tester . updateDeployment ( pauseFn )
2017-09-06 20:42:26 +00:00
if err != nil {
2017-09-07 20:09:05 +00:00
t . Fatalf ( "failed to pause deployment %s: %v" , tester . deployment . Name , err )
2017-09-06 20:42:26 +00:00
}
// Wait for the controller to notice the pause.
2017-09-07 20:09:05 +00:00
if err := tester . waitForObservedDeployment ( tester . deployment . Generation ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// Update the deployment template
newTGPS := int64 ( 0 )
2018-03-20 17:20:43 +00:00
tester . deployment , err = tester . updateDeployment ( func ( update * apps . Deployment ) {
2017-09-06 20:42:26 +00:00
update . Spec . Template . Spec . TerminationGracePeriodSeconds = & newTGPS
} )
if err != nil {
2017-09-07 20:09:05 +00:00
t . Fatalf ( "failed updating template of deployment %s: %v" , tester . deployment . Name , err )
2017-09-06 20:42:26 +00:00
}
// Wait for the controller to notice the rollout.
2017-09-07 20:09:05 +00:00
if err := tester . waitForObservedDeployment ( tester . deployment . Generation ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// Verify that the paused deployment won't create new replica set.
2017-09-07 20:09:05 +00:00
if err := tester . expectNoNewReplicaSet ( ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
2018-03-20 17:20:43 +00:00
_ , allOldRs , err := deploymentutil . GetOldReplicaSets ( tester . deployment , c . AppsV1 ( ) )
2017-09-06 20:42:26 +00:00
if err != nil {
2017-09-07 20:09:05 +00:00
t . Fatalf ( "failed retrieving old replicasets of deployment %s: %v" , tester . deployment . Name , err )
2017-09-06 20:42:26 +00:00
}
if len ( allOldRs ) != 1 {
t . Errorf ( "expected an old replica set, got %v" , allOldRs )
}
if * allOldRs [ 0 ] . Spec . Template . Spec . TerminationGracePeriodSeconds == newTGPS {
t . Errorf ( "TerminationGracePeriodSeconds on the replica set should be %d, got %d" , tgps , newTGPS )
}
}
// Paused deployment can be scaled
func TestScalePausedDeployment ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-scale-paused-deployment"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
replicas := int32 ( 1 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( name , ns . Name , replicas ) }
tgps := int64 ( 1 )
tester . deployment . Spec . Template . Spec . TerminationGracePeriodSeconds = & tgps
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2017-09-06 20:42:26 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %s: %v" , tester . deployment . Name , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Wait for the Deployment to be updated to revision 1
2017-09-07 20:09:05 +00:00
if err := tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
2017-10-13 21:09:46 +00:00
// Make sure the Deployment completes while manually marking Deployment pods as ready at the same time.
// Use soft check because this deployment was just created and rolling update strategy might be violated.
if err := tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
2017-09-07 20:09:05 +00:00
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// A new replicaset should be created.
2017-09-07 20:09:05 +00:00
if _ , err := tester . expectNewReplicaSet ( ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// Pause the deployment.
2017-09-22 08:06:59 +00:00
tester . deployment , err = tester . updateDeployment ( pauseFn )
2017-09-06 20:42:26 +00:00
if err != nil {
2017-09-07 20:09:05 +00:00
t . Fatalf ( "failed to pause deployment %s: %v" , tester . deployment . Name , err )
2017-09-06 20:42:26 +00:00
}
// Wait for the controller to notice the scale.
2017-09-07 20:09:05 +00:00
if err := tester . waitForObservedDeployment ( tester . deployment . Generation ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// Scale the paused deployment.
newReplicas := int32 ( 10 )
2018-03-20 17:20:43 +00:00
tester . deployment , err = tester . updateDeployment ( func ( update * apps . Deployment ) {
2017-09-06 20:42:26 +00:00
update . Spec . Replicas = & newReplicas
} )
if err != nil {
2017-09-07 20:09:05 +00:00
t . Fatalf ( "failed updating deployment %s: %v" , tester . deployment . Name , err )
2017-09-06 20:42:26 +00:00
}
// Wait for the controller to notice the scale.
2017-09-07 20:09:05 +00:00
if err := tester . waitForObservedDeployment ( tester . deployment . Generation ) ; err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
// Verify that the new replicaset is scaled.
2017-09-07 20:09:05 +00:00
rs , err := tester . expectNewReplicaSet ( )
if err != nil {
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
if * rs . Spec . Replicas != newReplicas {
t . Errorf ( "expected new replicaset replicas = %d, got %d" , newReplicas , * rs . Spec . Replicas )
}
2017-10-13 21:09:46 +00:00
// Make sure the Deployment completes while manually marking Deployment pods as ready at the same time.
// Use soft check because this deployment was just scaled and rolling update strategy might be violated.
if err := tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
2017-09-07 20:09:05 +00:00
t . Fatal ( err )
}
2017-09-06 20:42:26 +00:00
}
2017-09-22 07:57:27 +00:00
// Deployment rollout shouldn't be blocked on hash collisions
func TestDeploymentHashCollision ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-hash-collision-deployment"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
replicas := int32 ( 1 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( name , ns . Name , replicas ) }
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2017-09-22 07:57:27 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %s: %v" , tester . deployment . Name , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Wait for the Deployment to be updated to revision 1
if err := tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
// Mock a hash collision
2018-03-20 17:20:43 +00:00
newRS , err := deploymentutil . GetNewReplicaSet ( tester . deployment , c . AppsV1 ( ) )
2017-09-22 07:57:27 +00:00
if err != nil {
t . Fatalf ( "failed getting new replicaset of deployment %s: %v" , tester . deployment . Name , err )
}
if newRS == nil {
t . Fatalf ( "unable to find new replicaset of deployment %s" , tester . deployment . Name )
}
2018-03-20 17:20:43 +00:00
_ , err = tester . updateReplicaSet ( newRS . Name , func ( update * apps . ReplicaSet ) {
2017-09-22 07:57:27 +00:00
* update . Spec . Template . Spec . TerminationGracePeriodSeconds = int64 ( 5 )
} )
if err != nil {
t . Fatalf ( "failed updating replicaset %s template: %v" , newRS . Name , err )
}
// Expect deployment collision counter to increment
2017-09-25 21:17:43 +00:00
if err := wait . PollImmediate ( pollInterval , pollTimeout , func ( ) ( bool , error ) {
2018-03-20 17:20:43 +00:00
d , err := c . AppsV1 ( ) . Deployments ( ns . Name ) . Get ( tester . deployment . Name , metav1 . GetOptions { } )
2017-09-22 07:57:27 +00:00
if err != nil {
return false , nil
}
return d . Status . CollisionCount != nil && * d . Status . CollisionCount == int32 ( 1 ) , nil
} ) ; err != nil {
t . Fatalf ( "Failed to increment collision counter for deployment %q: %v" , tester . deployment . Name , err )
}
// Expect a new ReplicaSet to be created
if err := tester . waitForDeploymentRevisionAndImage ( "2" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
}
2017-10-13 21:09:46 +00:00
// Deployment supports rollback even when there's old replica set without revision.
2018-12-19 16:18:53 +00:00
// TODO: drop this test when extensions/v1beta1 is no longer served
2017-10-13 21:09:46 +00:00
func TestRollbackDeploymentRSNoRevision ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-rollback-no-revision-deployment"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
// Create an old RS without revision
rsName := "test-rollback-no-revision-controller"
rsReplicas := int32 ( 1 )
rs := newReplicaSet ( rsName , ns . Name , rsReplicas )
rs . Annotations = make ( map [ string ] string )
rs . Annotations [ "make" ] = "difference"
rs . Spec . Template . Spec . Containers [ 0 ] . Image = "different-image"
2018-03-20 17:20:43 +00:00
_ , err := c . AppsV1 ( ) . ReplicaSets ( ns . Name ) . Create ( rs )
2017-10-13 21:09:46 +00:00
if err != nil {
t . Fatalf ( "failed to create replicaset %s: %v" , rsName , err )
}
replicas := int32 ( 1 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( name , ns . Name , replicas ) }
oriImage := tester . deployment . Spec . Template . Spec . Containers [ 0 ] . Image
2018-03-20 17:20:43 +00:00
// Set absolute rollout limits (defaults changed to percentages)
max := intstr . FromInt ( 1 )
tester . deployment . Spec . Strategy . RollingUpdate . MaxUnavailable = & max
tester . deployment . Spec . Strategy . RollingUpdate . MaxSurge = & max
2017-10-13 21:09:46 +00:00
// Create a deployment which have different template than the replica set created above.
2018-03-20 17:20:43 +00:00
if tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment ) ; err != nil {
2017-10-13 21:09:46 +00:00
t . Fatalf ( "failed to create deployment %s: %v" , tester . deployment . Name , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Wait for the Deployment to be updated to revision 1
if err = tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
// 1. Rollback to the last revision
// Since there's only 1 revision in history, it should still be revision 1
revision := int64 ( 0 )
rollback := newDeploymentRollback ( tester . deployment . Name , nil , revision )
if err = c . ExtensionsV1beta1 ( ) . Deployments ( ns . Name ) . Rollback ( rollback ) ; err != nil {
t . Fatalf ( "failed to roll back deployment %s to last revision: %v" , tester . deployment . Name , err )
}
// Wait for the deployment to start rolling back
if err = tester . waitForDeploymentRollbackCleared ( ) ; err != nil {
t . Fatalf ( "failed to roll back deployment %s to last revision: %v" , tester . deployment . Name , err )
}
// TODO: report RollbackRevisionNotFound in deployment status and check it here
// The pod template shouldn't change since there's no last revision
// Check if the deployment is still revision 1 and still has the old pod template
err = tester . checkDeploymentRevisionAndImage ( "1" , oriImage )
if err != nil {
t . Fatal ( err )
}
// 2. Update the deployment to revision 2.
updatedImage := "update"
2018-03-20 17:20:43 +00:00
tester . deployment , err = tester . updateDeployment ( func ( update * apps . Deployment ) {
2017-10-13 21:09:46 +00:00
update . Spec . Template . Spec . Containers [ 0 ] . Name = updatedImage
update . Spec . Template . Spec . Containers [ 0 ] . Image = updatedImage
} )
if err != nil {
t . Fatalf ( "failed updating deployment %s: %v" , tester . deployment . Name , err )
}
// Use observedGeneration to determine if the controller noticed the pod template update.
// Wait for the controller to notice the resume.
if err = tester . waitForObservedDeployment ( tester . deployment . Generation ) ; err != nil {
t . Fatal ( err )
}
// Wait for it to be updated to revision 2
if err = tester . waitForDeploymentRevisionAndImage ( "2" , updatedImage ) ; err != nil {
t . Fatal ( err )
}
// Wait for the Deployment to complete while manually marking Deployment pods as ready at the same time
if err = tester . waitForDeploymentCompleteAndCheckRollingAndMarkPodsReady ( ) ; err != nil {
t . Fatal ( err )
}
// 3. Update the deploymentRollback to rollback to revision 1
revision = int64 ( 1 )
rollback = newDeploymentRollback ( tester . deployment . Name , nil , revision )
if err = c . ExtensionsV1beta1 ( ) . Deployments ( ns . Name ) . Rollback ( rollback ) ; err != nil {
t . Fatalf ( "failed to roll back deployment %s to revision %d: %v" , tester . deployment . Name , revision , err )
}
// Wait for the deployment to start rolling back
if err = tester . waitForDeploymentRollbackCleared ( ) ; err != nil {
t . Fatalf ( "failed to roll back deployment %s to revision %d: %v" , tester . deployment . Name , revision , err )
}
// TODO: report RollbackDone in deployment status and check it here
// The pod template should be updated to the one in revision 1
// Wait for it to be updated to revision 3
if err = tester . waitForDeploymentRevisionAndImage ( "3" , oriImage ) ; err != nil {
t . Fatal ( err )
}
// Wait for the Deployment to complete while manually marking Deployment pods as ready at the same time
if err = tester . waitForDeploymentCompleteAndCheckRollingAndMarkPodsReady ( ) ; err != nil {
t . Fatal ( err )
}
}
2017-10-17 23:08:53 +00:00
2018-03-20 17:20:43 +00:00
func checkRSHashLabels ( rs * apps . ReplicaSet ) ( string , error ) {
hash := rs . Labels [ apps . DefaultDeploymentUniqueLabelKey ]
selectorHash := rs . Spec . Selector . MatchLabels [ apps . DefaultDeploymentUniqueLabelKey ]
templateLabelHash := rs . Spec . Template . Labels [ apps . DefaultDeploymentUniqueLabelKey ]
2017-10-17 23:08:53 +00:00
if hash != selectorHash || selectorHash != templateLabelHash {
return "" , fmt . Errorf ( "mismatching hash value found in replicaset %s: %#v" , rs . Name , rs )
}
if len ( hash ) == 0 {
return "" , fmt . Errorf ( "unexpected replicaset %s missing required pod-template-hash labels" , rs . Name )
}
2018-07-12 00:01:38 +00:00
if ! strings . HasSuffix ( rs . Name , hash ) {
return "" , fmt . Errorf ( "unexpected replicaset %s name suffix doesn't match hash %s" , rs . Name , hash )
}
2017-10-17 23:08:53 +00:00
return hash , nil
}
func checkPodsHashLabel ( pods * v1 . PodList ) ( string , error ) {
if len ( pods . Items ) == 0 {
return "" , fmt . Errorf ( "no pods given" )
}
var hash string
for _ , pod := range pods . Items {
2018-03-20 17:20:43 +00:00
podHash := pod . Labels [ apps . DefaultDeploymentUniqueLabelKey ]
2017-10-17 23:08:53 +00:00
if len ( podHash ) == 0 {
return "" , fmt . Errorf ( "found pod %s missing pod-template-hash label: %#v" , pod . Name , pods )
}
// Save the first valid hash
if len ( hash ) == 0 {
hash = podHash
}
if podHash != hash {
return "" , fmt . Errorf ( "found pod %s with mismatching pod-template-hash value %s: %#v" , pod . Name , podHash , pods )
}
}
return hash , nil
}
2017-10-27 20:19:50 +00:00
// Deployment should have a timeout condition when it fails to progress after given deadline.
func TestFailedDeployment ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-failed-deployment"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
deploymentName := "progress-check"
replicas := int32 ( 1 )
three := int32 ( 3 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( deploymentName , ns . Name , replicas ) }
tester . deployment . Spec . ProgressDeadlineSeconds = & three
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2017-10-27 20:19:50 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %q: %v" , deploymentName , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
2018-02-02 00:21:07 +00:00
if err = tester . waitForDeploymentUpdatedReplicasGTE ( replicas ) ; err != nil {
2017-10-27 20:19:50 +00:00
t . Fatal ( err )
}
// Pods are not marked as Ready, therefore the deployment progress will eventually timeout after progressDeadlineSeconds has passed.
// Wait for the deployment to have a progress timeout condition.
2018-03-20 17:20:43 +00:00
if err = tester . waitForDeploymentWithCondition ( deploymentutil . TimedOutReason , apps . DeploymentProgressing ) ; err != nil {
2017-10-27 20:19:50 +00:00
t . Fatal ( err )
}
// Manually mark pods as Ready and wait for deployment to complete.
if err := tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatalf ( "deployment %q fails to have its status becoming valid: %v" , deploymentName , err )
}
// Wait for the deployment to have a progress complete condition.
2018-03-20 17:20:43 +00:00
if err = tester . waitForDeploymentWithCondition ( deploymentutil . NewRSAvailableReason , apps . DeploymentProgressing ) ; err != nil {
2017-10-27 20:19:50 +00:00
t . Fatal ( err )
}
}
2017-10-31 18:39:27 +00:00
func TestOverlappingDeployments ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-overlapping-deployments"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
replicas := int32 ( 1 )
firstDeploymentName := "first-deployment"
secondDeploymentName := "second-deployment"
testers := [ ] * deploymentTester {
{ t : t , c : c , deployment : newDeployment ( firstDeploymentName , ns . Name , replicas ) } ,
{ t : t , c : c , deployment : newDeployment ( secondDeploymentName , ns . Name , replicas ) } ,
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Create 2 deployments with overlapping selectors
var err error
2018-03-20 17:20:43 +00:00
var rss [ ] * apps . ReplicaSet
2017-10-31 18:39:27 +00:00
for _ , tester := range testers {
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2017-10-31 18:39:27 +00:00
dname := tester . deployment . Name
if err != nil {
t . Fatalf ( "failed to create deployment %q: %v" , dname , err )
}
// Wait for the deployment to be updated to revision 1
if err = tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatalf ( "failed to update deployment %q to revision 1: %v" , dname , err )
}
// Make sure the deployment completes while manually marking its pods as ready at the same time
if err = tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatalf ( "deployment %q failed to complete: %v" , dname , err )
}
// Get replicaset of the deployment
newRS , err := tester . getNewReplicaSet ( )
if err != nil {
t . Fatalf ( "failed to get new replicaset of deployment %q: %v" , dname , err )
}
if newRS == nil {
t . Fatalf ( "unable to find new replicaset of deployment %q" , dname )
}
// Store the replicaset for future usage
rss = append ( rss , newRS )
}
// Both deployments should proceed independently, so their respective replicaset should not be the same replicaset
if rss [ 0 ] . UID == rss [ 1 ] . UID {
t . Fatalf ( "overlapping deployments should not share the same replicaset" )
}
// Scale only the first deployment by 1
newReplicas := replicas + 1
2018-03-20 17:20:43 +00:00
testers [ 0 ] . deployment , err = testers [ 0 ] . updateDeployment ( func ( update * apps . Deployment ) {
2017-10-31 18:39:27 +00:00
update . Spec . Replicas = & newReplicas
} )
if err != nil {
t . Fatalf ( "failed updating deployment %q: %v" , firstDeploymentName , err )
}
// Make sure the deployment completes after scaling
if err := testers [ 0 ] . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatalf ( "deployment %q failed to complete after scaling: %v" , firstDeploymentName , err )
}
// Verify replicaset of both deployments has updated number of replicas
for i , tester := range testers {
2018-03-20 17:20:43 +00:00
rs , err := c . AppsV1 ( ) . ReplicaSets ( ns . Name ) . Get ( rss [ i ] . Name , metav1 . GetOptions { } )
2017-10-31 18:39:27 +00:00
if err != nil {
t . Fatalf ( "failed to get replicaset %q: %v" , rss [ i ] . Name , err )
}
if * rs . Spec . Replicas != * tester . deployment . Spec . Replicas {
t . Errorf ( "expected replicaset %q of deployment %q has %d replicas, but found %d replicas" , rs . Name , firstDeploymentName , * tester . deployment . Spec . Replicas , * rs . Spec . Replicas )
}
}
}
2017-11-20 23:36:27 +00:00
// Deployment should not block rollout when updating spec replica number and template at the same time.
func TestScaledRolloutDeployment ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-scaled-rollout-deployment"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Create a deployment with rolling update strategy, max surge = 3, and max unavailable = 2
var err error
replicas := int32 ( 10 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( name , ns . Name , replicas ) }
tester . deployment . Spec . Strategy . RollingUpdate . MaxSurge = intOrStrP ( 3 )
tester . deployment . Spec . Strategy . RollingUpdate . MaxUnavailable = intOrStrP ( 2 )
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2017-11-20 23:36:27 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %q: %v" , name , err )
}
if err = tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
if err = tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatalf ( "deployment %q failed to complete: %v" , name , err )
}
// Record current replicaset before starting new rollout
firstRS , err := tester . expectNewReplicaSet ( )
if err != nil {
t . Fatal ( err )
}
// Update the deployment with another new image but do not mark the pods as ready to block new replicaset
fakeImage2 := "fakeimage2"
2018-03-20 17:20:43 +00:00
tester . deployment , err = tester . updateDeployment ( func ( update * apps . Deployment ) {
2017-11-20 23:36:27 +00:00
update . Spec . Template . Spec . Containers [ 0 ] . Image = fakeImage2
} )
if err != nil {
t . Fatalf ( "failed updating deployment %q: %v" , name , err )
}
if err = tester . waitForDeploymentRevisionAndImage ( "2" , fakeImage2 ) ; err != nil {
t . Fatal ( err )
}
// Verify the deployment has minimum available replicas after 2nd rollout
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Get ( name , metav1 . GetOptions { } )
2017-11-20 23:36:27 +00:00
if err != nil {
t . Fatalf ( "failed to get deployment %q: %v" , name , err )
}
minAvailableReplicas := deploymentutil . MinAvailable ( tester . deployment )
if tester . deployment . Status . AvailableReplicas < minAvailableReplicas {
t . Fatalf ( "deployment %q does not have minimum number of available replicas after 2nd rollout" , name )
}
// Wait for old replicaset of 1st rollout to have desired replicas
2018-03-20 17:20:43 +00:00
firstRS , err = c . AppsV1 ( ) . ReplicaSets ( ns . Name ) . Get ( firstRS . Name , metav1 . GetOptions { } )
2017-11-20 23:36:27 +00:00
if err != nil {
t . Fatalf ( "failed to get replicaset %q: %v" , firstRS . Name , err )
}
if err = tester . waitRSStable ( firstRS ) ; err != nil {
t . Fatal ( err )
}
// Wait for new replicaset of 2nd rollout to have desired replicas
secondRS , err := tester . expectNewReplicaSet ( )
if err != nil {
t . Fatal ( err )
}
if err = tester . waitRSStable ( secondRS ) ; err != nil {
t . Fatal ( err )
}
// Scale up the deployment and update its image to another new image simultaneously (this time marks all pods as ready)
newReplicas := int32 ( 20 )
fakeImage3 := "fakeimage3"
2018-03-20 17:20:43 +00:00
tester . deployment , err = tester . updateDeployment ( func ( update * apps . Deployment ) {
2017-11-20 23:36:27 +00:00
update . Spec . Replicas = & newReplicas
update . Spec . Template . Spec . Containers [ 0 ] . Image = fakeImage3
} )
if err != nil {
t . Fatalf ( "failed updating deployment %q: %v" , name , err )
}
if err = tester . waitForDeploymentRevisionAndImage ( "3" , fakeImage3 ) ; err != nil {
t . Fatal ( err )
}
if err = tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatalf ( "deployment %q failed to complete: %v" , name , err )
}
// Verify every replicaset has correct desiredReplicas annotation after 3rd rollout
2018-03-20 17:20:43 +00:00
thirdRS , err := deploymentutil . GetNewReplicaSet ( tester . deployment , c . AppsV1 ( ) )
2017-11-20 23:36:27 +00:00
if err != nil {
t . Fatalf ( "failed getting new revision 3 replicaset for deployment %q: %v" , name , err )
}
2018-03-20 17:20:43 +00:00
rss := [ ] * apps . ReplicaSet { firstRS , secondRS , thirdRS }
2017-11-20 23:36:27 +00:00
for _ , curRS := range rss {
2018-03-20 17:20:43 +00:00
curRS , err = c . AppsV1 ( ) . ReplicaSets ( ns . Name ) . Get ( curRS . Name , metav1 . GetOptions { } )
2017-11-20 23:36:27 +00:00
if err != nil {
t . Fatalf ( "failed to get replicaset when checking desired replicas annotation: %v" , err )
}
desired , ok := deploymentutil . GetDesiredReplicasAnnotation ( curRS )
if ! ok {
t . Fatalf ( "failed to retrieve desiredReplicas annotation for replicaset %q" , curRS . Name )
}
if desired != * ( tester . deployment . Spec . Replicas ) {
t . Fatalf ( "unexpected desiredReplicas annotation for replicaset %q: expected %d, got %d" , curRS . Name , * ( tester . deployment . Spec . Replicas ) , desired )
}
}
// Update the deployment with another new image but do not mark the pods as ready to block new replicaset
fakeImage4 := "fakeimage4"
2018-03-20 17:20:43 +00:00
tester . deployment , err = tester . updateDeployment ( func ( update * apps . Deployment ) {
2017-11-20 23:36:27 +00:00
update . Spec . Template . Spec . Containers [ 0 ] . Image = fakeImage4
} )
if err != nil {
t . Fatalf ( "failed updating deployment %q: %v" , name , err )
}
if err = tester . waitForDeploymentRevisionAndImage ( "4" , fakeImage4 ) ; err != nil {
t . Fatal ( err )
}
// Verify the deployment has minimum available replicas after 4th rollout
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Get ( name , metav1 . GetOptions { } )
2017-11-20 23:36:27 +00:00
if err != nil {
t . Fatalf ( "failed to get deployment %q: %v" , name , err )
}
minAvailableReplicas = deploymentutil . MinAvailable ( tester . deployment )
if tester . deployment . Status . AvailableReplicas < minAvailableReplicas {
t . Fatalf ( "deployment %q does not have minimum number of available replicas after 4th rollout" , name )
}
// Wait for old replicaset of 3rd rollout to have desired replicas
2018-03-20 17:20:43 +00:00
thirdRS , err = c . AppsV1 ( ) . ReplicaSets ( ns . Name ) . Get ( thirdRS . Name , metav1 . GetOptions { } )
2017-11-20 23:36:27 +00:00
if err != nil {
t . Fatalf ( "failed to get replicaset %q: %v" , thirdRS . Name , err )
}
if err = tester . waitRSStable ( thirdRS ) ; err != nil {
t . Fatal ( err )
}
// Wait for new replicaset of 4th rollout to have desired replicas
fourthRS , err := tester . expectNewReplicaSet ( )
if err != nil {
t . Fatal ( err )
}
if err = tester . waitRSStable ( fourthRS ) ; err != nil {
t . Fatal ( err )
}
// Scale down the deployment and update its image to another new image simultaneously (this time marks all pods as ready)
newReplicas = int32 ( 5 )
fakeImage5 := "fakeimage5"
2018-03-20 17:20:43 +00:00
tester . deployment , err = tester . updateDeployment ( func ( update * apps . Deployment ) {
2017-11-20 23:36:27 +00:00
update . Spec . Replicas = & newReplicas
update . Spec . Template . Spec . Containers [ 0 ] . Image = fakeImage5
} )
if err != nil {
t . Fatalf ( "failed updating deployment %q: %v" , name , err )
}
if err = tester . waitForDeploymentRevisionAndImage ( "5" , fakeImage5 ) ; err != nil {
t . Fatal ( err )
}
if err = tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatalf ( "deployment %q failed to complete: %v" , name , err )
}
// Verify every replicaset has correct desiredReplicas annotation after 5th rollout
2018-03-20 17:20:43 +00:00
fifthRS , err := deploymentutil . GetNewReplicaSet ( tester . deployment , c . AppsV1 ( ) )
2017-11-20 23:36:27 +00:00
if err != nil {
t . Fatalf ( "failed getting new revision 5 replicaset for deployment %q: %v" , name , err )
}
2018-03-20 17:20:43 +00:00
rss = [ ] * apps . ReplicaSet { thirdRS , fourthRS , fifthRS }
2017-11-20 23:36:27 +00:00
for _ , curRS := range rss {
2018-03-20 17:20:43 +00:00
curRS , err = c . AppsV1 ( ) . ReplicaSets ( ns . Name ) . Get ( curRS . Name , metav1 . GetOptions { } )
2017-11-20 23:36:27 +00:00
if err != nil {
t . Fatalf ( "failed to get replicaset when checking desired replicas annotation: %v" , err )
}
desired , ok := deploymentutil . GetDesiredReplicasAnnotation ( curRS )
if ! ok {
t . Fatalf ( "failed to retrieve desiredReplicas annotation for replicaset %q" , curRS . Name )
}
if desired != * ( tester . deployment . Spec . Replicas ) {
t . Fatalf ( "unexpected desiredReplicas annotation for replicaset %q: expected %d, got %d" , curRS . Name , * ( tester . deployment . Spec . Replicas ) , desired )
}
}
}
2018-02-02 00:21:07 +00:00
func TestSpecReplicasChange ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-spec-replicas-change"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
deploymentName := "deployment"
replicas := int32 ( 1 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( deploymentName , ns . Name , replicas ) }
2018-03-20 17:20:43 +00:00
tester . deployment . Spec . Strategy . Type = apps . RecreateDeploymentStrategyType
2018-02-02 00:21:07 +00:00
tester . deployment . Spec . Strategy . RollingUpdate = nil
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2018-02-02 00:21:07 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %q: %v" , deploymentName , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Scale up/down deployment and verify its replicaset has matching .spec.replicas
if err = tester . scaleDeployment ( 2 ) ; err != nil {
t . Fatal ( err )
}
if err = tester . scaleDeployment ( 0 ) ; err != nil {
t . Fatal ( err )
}
if err = tester . scaleDeployment ( 1 ) ; err != nil {
t . Fatal ( err )
}
// Add a template annotation change to test deployment's status does update
// without .spec.replicas change
var oldGeneration int64
2018-03-20 17:20:43 +00:00
tester . deployment , err = tester . updateDeployment ( func ( update * apps . Deployment ) {
2018-02-02 00:21:07 +00:00
oldGeneration = update . Generation
update . Spec . RevisionHistoryLimit = pointer . Int32Ptr ( 4 )
} )
if err != nil {
t . Fatalf ( "failed updating deployment %q: %v" , tester . deployment . Name , err )
}
savedGeneration := tester . deployment . Generation
if savedGeneration == oldGeneration {
t . Fatalf ( "Failed to verify .Generation has incremented for deployment %q" , deploymentName )
}
if err = tester . waitForObservedDeployment ( savedGeneration ) ; err != nil {
t . Fatal ( err )
}
}
func TestDeploymentAvailableCondition ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-deployment-available-condition"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
deploymentName := "deployment"
replicas := int32 ( 10 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( deploymentName , ns . Name , replicas ) }
// Assign a high value to the deployment's minReadySeconds
tester . deployment . Spec . MinReadySeconds = 3600
2018-03-20 17:20:43 +00:00
// progressDeadlineSeconds must be greater than minReadySeconds
tester . deployment . Spec . ProgressDeadlineSeconds = pointer . Int32Ptr ( 7200 )
2018-02-02 00:21:07 +00:00
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2018-02-02 00:21:07 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %q: %v" , deploymentName , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Wait for the deployment to be observed by the controller and has at least specified number of updated replicas
if err = tester . waitForDeploymentUpdatedReplicasGTE ( replicas ) ; err != nil {
t . Fatal ( err )
}
// Wait for the deployment to have MinimumReplicasUnavailable reason because the pods are not marked as ready
2018-03-20 17:20:43 +00:00
if err = tester . waitForDeploymentWithCondition ( deploymentutil . MinimumReplicasUnavailable , apps . DeploymentAvailable ) ; err != nil {
2018-02-02 00:21:07 +00:00
t . Fatal ( err )
}
// Verify all replicas fields of DeploymentStatus have desired counts
if err = tester . checkDeploymentStatusReplicasFields ( 10 , 10 , 0 , 0 , 10 ) ; err != nil {
t . Fatal ( err )
}
// Mark the pods as ready without waiting for the deployment to complete
if err = tester . markUpdatedPodsReadyWithoutComplete ( ) ; err != nil {
t . Fatal ( err )
}
// Wait for number of ready replicas to equal number of replicas.
if err = tester . waitForReadyReplicas ( ) ; err != nil {
t . Fatal ( err )
}
// Wait for the deployment to still have MinimumReplicasUnavailable reason within minReadySeconds period
2018-03-20 17:20:43 +00:00
if err = tester . waitForDeploymentWithCondition ( deploymentutil . MinimumReplicasUnavailable , apps . DeploymentAvailable ) ; err != nil {
2018-02-02 00:21:07 +00:00
t . Fatal ( err )
}
// Verify all replicas fields of DeploymentStatus have desired counts
if err = tester . checkDeploymentStatusReplicasFields ( 10 , 10 , 10 , 0 , 10 ) ; err != nil {
t . Fatal ( err )
}
// Update the deployment's minReadySeconds to a small value
2018-03-20 17:20:43 +00:00
tester . deployment , err = tester . updateDeployment ( func ( update * apps . Deployment ) {
2018-02-02 00:21:07 +00:00
update . Spec . MinReadySeconds = 1
} )
if err != nil {
t . Fatalf ( "failed updating deployment %q: %v" , deploymentName , err )
}
// Wait for the deployment to notice minReadySeconds has changed
if err := tester . waitForObservedDeployment ( tester . deployment . Generation ) ; err != nil {
t . Fatal ( err )
}
// Wait for the deployment to have MinimumReplicasAvailable reason after minReadySeconds period
2018-03-20 17:20:43 +00:00
if err = tester . waitForDeploymentWithCondition ( deploymentutil . MinimumReplicasAvailable , apps . DeploymentAvailable ) ; err != nil {
2018-02-02 00:21:07 +00:00
t . Fatal ( err )
}
// Verify all replicas fields of DeploymentStatus have desired counts
if err = tester . checkDeploymentStatusReplicasFields ( 10 , 10 , 10 , 10 , 0 ) ; err != nil {
t . Fatal ( err )
}
}
// Wait for deployment to automatically patch incorrect ControllerRef of RS
2018-03-20 17:20:43 +00:00
func testRSControllerRefPatch ( t * testing . T , tester * deploymentTester , rs * apps . ReplicaSet , ownerReference * metav1 . OwnerReference , expectedOwnerReferenceNum int ) {
2018-02-02 00:21:07 +00:00
ns := rs . Namespace
2018-03-20 17:20:43 +00:00
rsClient := tester . c . AppsV1 ( ) . ReplicaSets ( ns )
rs , err := tester . updateReplicaSet ( rs . Name , func ( update * apps . ReplicaSet ) {
2018-02-02 00:21:07 +00:00
update . OwnerReferences = [ ] metav1 . OwnerReference { * ownerReference }
} )
if err != nil {
t . Fatalf ( "failed to update replicaset %q: %v" , rs . Name , err )
}
if err := wait . PollImmediate ( pollInterval , pollTimeout , func ( ) ( bool , error ) {
newRS , err := rsClient . Get ( rs . Name , metav1 . GetOptions { } )
if err != nil {
return false , err
}
return metav1 . GetControllerOf ( newRS ) != nil , nil
} ) ; err != nil {
t . Fatalf ( "failed to wait for controllerRef of the replicaset %q to become nil: %v" , rs . Name , err )
}
newRS , err := rsClient . Get ( rs . Name , metav1 . GetOptions { } )
if err != nil {
t . Fatalf ( "failed to obtain replicaset %q: %v" , rs . Name , err )
}
controllerRef := metav1 . GetControllerOf ( newRS )
if controllerRef . UID != tester . deployment . UID {
t . Fatalf ( "controllerRef of replicaset %q has a different UID: Expected %v, got %v" , newRS . Name , tester . deployment . UID , controllerRef . UID )
}
ownerReferenceNum := len ( newRS . GetOwnerReferences ( ) )
if ownerReferenceNum != expectedOwnerReferenceNum {
t . Fatalf ( "unexpected number of owner references for replicaset %q: Expected %d, got %d" , newRS . Name , expectedOwnerReferenceNum , ownerReferenceNum )
}
}
func TestGeneralReplicaSetAdoption ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-general-replicaset-adoption"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
deploymentName := "deployment"
replicas := int32 ( 1 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( deploymentName , ns . Name , replicas ) }
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2018-02-02 00:21:07 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %q: %v" , deploymentName , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Wait for the Deployment to be updated to revision 1
if err := tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
// Ensure the deployment completes while marking its pods as ready simultaneously
if err := tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatal ( err )
}
// Get replicaset of the deployment
2018-03-20 17:20:43 +00:00
rs , err := deploymentutil . GetNewReplicaSet ( tester . deployment , c . AppsV1 ( ) )
2018-02-02 00:21:07 +00:00
if err != nil {
t . Fatalf ( "failed to get replicaset of deployment %q: %v" , deploymentName , err )
}
if rs == nil {
t . Fatalf ( "unable to find replicaset of deployment %q" , deploymentName )
}
// When the only OwnerReference of the RS points to another type of API object such as statefulset
// with Controller=false, the deployment should add a second OwnerReference (ControllerRef) pointing to itself
// with Controller=true
var falseVar = false
2018-08-16 02:06:39 +00:00
ownerReference := metav1 . OwnerReference { UID : uuid . NewUUID ( ) , APIVersion : "apps/v1" , Kind : "StatefulSet" , Name : deploymentName , Controller : & falseVar }
2018-02-02 00:21:07 +00:00
testRSControllerRefPatch ( t , tester , rs , & ownerReference , 2 )
// When the only OwnerReference of the RS points to the deployment with Controller=false,
// the deployment should set Controller=true for the only OwnerReference
2018-08-16 02:06:39 +00:00
ownerReference = metav1 . OwnerReference { UID : tester . deployment . UID , APIVersion : "apps/v1" , Kind : "Deployment" , Name : deploymentName , Controller : & falseVar }
2018-02-02 00:21:07 +00:00
testRSControllerRefPatch ( t , tester , rs , & ownerReference , 1 )
}
func testScalingUsingScaleSubresource ( t * testing . T , tester * deploymentTester , replicas int32 ) {
ns := tester . deployment . Namespace
deploymentName := tester . deployment . Name
2018-03-20 17:20:43 +00:00
deploymentClient := tester . c . AppsV1 ( ) . Deployments ( ns )
2018-02-02 00:21:07 +00:00
deployment , err := deploymentClient . Get ( deploymentName , metav1 . GetOptions { } )
if err != nil {
t . Fatalf ( "Failed to obtain deployment %q: %v" , deploymentName , err )
}
2018-10-30 15:37:41 +00:00
scale , err := tester . c . AppsV1 ( ) . Deployments ( ns ) . GetScale ( deploymentName , metav1 . GetOptions { } )
2018-02-02 00:21:07 +00:00
if err != nil {
t . Fatalf ( "Failed to obtain scale subresource for deployment %q: %v" , deploymentName , err )
}
if scale . Spec . Replicas != * deployment . Spec . Replicas {
t . Fatalf ( "Scale subresource for deployment %q does not match .Spec.Replicas: expected %d, got %d" , deploymentName , * deployment . Spec . Replicas , scale . Spec . Replicas )
}
if err := retry . RetryOnConflict ( retry . DefaultBackoff , func ( ) error {
2018-10-30 15:37:41 +00:00
scale , err := tester . c . AppsV1 ( ) . Deployments ( ns ) . GetScale ( deploymentName , metav1 . GetOptions { } )
2018-02-02 00:21:07 +00:00
if err != nil {
return err
}
scale . Spec . Replicas = replicas
2018-10-30 15:37:41 +00:00
_ , err = tester . c . AppsV1 ( ) . Deployments ( ns ) . UpdateScale ( deploymentName , scale )
2018-02-02 00:21:07 +00:00
return err
} ) ; err != nil {
t . Fatalf ( "Failed to set .Spec.Replicas of scale subresource for deployment %q: %v" , deploymentName , err )
}
deployment , err = deploymentClient . Get ( deploymentName , metav1 . GetOptions { } )
if err != nil {
t . Fatalf ( "Failed to obtain deployment %q: %v" , deploymentName , err )
}
if * deployment . Spec . Replicas != replicas {
t . Fatalf ( ".Spec.Replicas of deployment %q does not match its scale subresource: expected %d, got %d" , deploymentName , replicas , * deployment . Spec . Replicas )
}
}
func TestDeploymentScaleSubresource ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-deployment-scale-subresource"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
deploymentName := "deployment"
replicas := int32 ( 2 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( deploymentName , ns . Name , replicas ) }
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2018-02-02 00:21:07 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %q: %v" , deploymentName , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Wait for the Deployment to be updated to revision 1
if err := tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
// Ensure the deployment completes while marking its pods as ready simultaneously
if err := tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatal ( err )
}
// Use scale subresource to scale the deployment up to 3
testScalingUsingScaleSubresource ( t , tester , 3 )
// Use the scale subresource to scale the deployment down to 0
testScalingUsingScaleSubresource ( t , tester , 0 )
}
2018-02-07 19:28:43 +00:00
// This test verifies that the Deployment does orphan a ReplicaSet when the ReplicaSet's
// .Labels field is changed to no longer match the Deployment's selector. It also partially
// verifies that collision avoidance mechanism is triggered when a Deployment's new ReplicaSet
// is orphaned, even without PodTemplateSpec change. Refer comment below for more info:
// https://github.com/kubernetes/kubernetes/pull/59212#discussion_r166465113
func TestReplicaSetOrphaningAndAdoptionWhenLabelsChange ( t * testing . T ) {
s , closeFn , rm , dc , informers , c := dcSetup ( t )
defer closeFn ( )
name := "test-replicaset-orphaning-and-adoption-when-labels-change"
ns := framework . CreateTestingNamespace ( name , s , t )
defer framework . DeleteTestingNamespace ( ns , s , t )
deploymentName := "deployment"
replicas := int32 ( 1 )
tester := & deploymentTester { t : t , c : c , deployment : newDeployment ( deploymentName , ns . Name , replicas ) }
var err error
2018-03-20 17:20:43 +00:00
tester . deployment , err = c . AppsV1 ( ) . Deployments ( ns . Name ) . Create ( tester . deployment )
2018-02-07 19:28:43 +00:00
if err != nil {
t . Fatalf ( "failed to create deployment %q: %v" , deploymentName , err )
}
// Start informer and controllers
stopCh := make ( chan struct { } )
defer close ( stopCh )
informers . Start ( stopCh )
go rm . Run ( 5 , stopCh )
go dc . Run ( 5 , stopCh )
// Wait for the Deployment to be updated to revision 1
if err := tester . waitForDeploymentRevisionAndImage ( "1" , fakeImage ) ; err != nil {
t . Fatal ( err )
}
// Ensure the deployment completes while marking its pods as ready simultaneously
if err := tester . waitForDeploymentCompleteAndMarkPodsReady ( ) ; err != nil {
t . Fatal ( err )
}
// Orphaning: deployment should remove OwnerReference from a RS when the RS's labels change to not match its labels
// Get replicaset of the deployment
2018-03-20 17:20:43 +00:00
rs , err := deploymentutil . GetNewReplicaSet ( tester . deployment , c . AppsV1 ( ) )
2018-02-07 19:28:43 +00:00
if err != nil {
t . Fatalf ( "failed to get replicaset of deployment %q: %v" , deploymentName , err )
}
if rs == nil {
t . Fatalf ( "unable to find replicaset of deployment %q" , deploymentName )
}
// Verify controllerRef of the replicaset is not nil and pointing to the deployment
controllerRef := metav1 . GetControllerOf ( rs )
if controllerRef == nil {
t . Fatalf ( "controllerRef of replicaset %q is nil" , rs . Name )
}
if controllerRef . UID != tester . deployment . UID {
t . Fatalf ( "controllerRef of replicaset %q has a different UID: Expected %v, got %v" , rs . Name , tester . deployment . UID , controllerRef . UID )
}
// Change the replicaset's labels to not match the deployment's labels
labelMap := map [ string ] string { "new-name" : "new-test" }
2018-03-20 17:20:43 +00:00
rs , err = tester . updateReplicaSet ( rs . Name , func ( update * apps . ReplicaSet ) {
2018-02-07 19:28:43 +00:00
update . Labels = labelMap
} )
if err != nil {
t . Fatalf ( "failed to update replicaset %q: %v" , rs . Name , err )
}
// Wait for the controllerRef of the replicaset to become nil
2018-03-20 17:20:43 +00:00
rsClient := tester . c . AppsV1 ( ) . ReplicaSets ( ns . Name )
2018-02-07 19:28:43 +00:00
if err = wait . PollImmediate ( pollInterval , pollTimeout , func ( ) ( bool , error ) {
rs , err = rsClient . Get ( rs . Name , metav1 . GetOptions { } )
if err != nil {
return false , err
}
return metav1 . GetControllerOf ( rs ) == nil , nil
} ) ; err != nil {
t . Fatalf ( "failed to wait for controllerRef of replicaset %q to become nil: %v" , rs . Name , err )
}
// Wait for the deployment to create a new replicaset
// This will trigger collision avoidance due to deterministic nature of replicaset name
// i.e., the new replicaset will have a name with different hash to preserve name uniqueness
2018-03-20 17:20:43 +00:00
var newRS * apps . ReplicaSet
2018-02-07 19:28:43 +00:00
if err = wait . PollImmediate ( pollInterval , pollTimeout , func ( ) ( bool , error ) {
2018-03-20 17:20:43 +00:00
newRS , err = deploymentutil . GetNewReplicaSet ( tester . deployment , c . AppsV1 ( ) )
2018-02-07 19:28:43 +00:00
if err != nil {
return false , fmt . Errorf ( "failed to get new replicaset of deployment %q after orphaning: %v" , deploymentName , err )
}
return newRS != nil , nil
} ) ; err != nil {
t . Fatalf ( "failed to wait for deployment %q to create a new replicaset after orphaning: %v" , deploymentName , err )
}
if newRS . UID == rs . UID {
t . Fatalf ( "expect deployment %q to create a new replicaset different from the orphaned one, but it isn't" , deploymentName )
}
// Adoption: deployment should add controllerRef to a RS when the RS's labels change to match its labels
// Change the old replicaset's labels to match the deployment's labels
2018-03-20 17:20:43 +00:00
rs , err = tester . updateReplicaSet ( rs . Name , func ( update * apps . ReplicaSet ) {
2018-02-07 19:28:43 +00:00
update . Labels = testLabels ( )
} )
if err != nil {
t . Fatalf ( "failed to update replicaset %q: %v" , rs . Name , err )
}
// Wait for the deployment to adopt the old replicaset
if err = wait . PollImmediate ( pollInterval , pollTimeout , func ( ) ( bool , error ) {
rs , err := rsClient . Get ( rs . Name , metav1 . GetOptions { } )
if err != nil {
return false , err
}
controllerRef = metav1 . GetControllerOf ( rs )
return controllerRef != nil && controllerRef . UID == tester . deployment . UID , nil
} ) ; err != nil {
t . Fatalf ( "failed waiting for replicaset adoption by deployment %q to complete: %v" , deploymentName , err )
}
}