...

Source file src/k8s.io/kubernetes/pkg/registry/apps/deployment/strategy_test.go

Documentation: k8s.io/kubernetes/pkg/registry/apps/deployment

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package deployment
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"k8s.io/apimachinery/pkg/api/resource"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/apimachinery/pkg/util/intstr"
    27  	"k8s.io/apimachinery/pkg/util/validation/field"
    28  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    29  	"k8s.io/kubernetes/pkg/apis/apps"
    30  	api "k8s.io/kubernetes/pkg/apis/core"
    31  )
    32  
    33  const (
    34  	fakeImageName  = "fake-name"
    35  	fakeImage      = "fakeimage"
    36  	deploymentName = "test-deployment"
    37  	namespace      = "test-namespace"
    38  )
    39  
    40  func TestStatusUpdates(t *testing.T) {
    41  	tests := []struct {
    42  		old      runtime.Object
    43  		obj      runtime.Object
    44  		expected runtime.Object
    45  	}{
    46  		{
    47  			old:      newDeployment(map[string]string{"test": "label"}, map[string]string{"test": "annotation"}),
    48  			obj:      newDeployment(map[string]string{"test": "label", "sneaky": "label"}, map[string]string{"test": "annotation"}),
    49  			expected: newDeployment(map[string]string{"test": "label"}, map[string]string{"test": "annotation"}),
    50  		},
    51  		{
    52  			old:      newDeployment(map[string]string{"test": "label"}, map[string]string{"test": "annotation"}),
    53  			obj:      newDeployment(map[string]string{"test": "label"}, map[string]string{"test": "annotation", "sneaky": "annotation"}),
    54  			expected: newDeployment(map[string]string{"test": "label"}, map[string]string{"test": "annotation", "sneaky": "annotation"}),
    55  		},
    56  	}
    57  
    58  	for _, test := range tests {
    59  		deploymentStatusStrategy{}.PrepareForUpdate(genericapirequest.NewContext(), test.obj, test.old)
    60  		if !reflect.DeepEqual(test.expected, test.obj) {
    61  			t.Errorf("Unexpected object mismatch! Expected:\n%#v\ngot:\n%#v", test.expected, test.obj)
    62  		}
    63  	}
    64  }
    65  
    66  func newDeployment(labels, annotations map[string]string) *apps.Deployment {
    67  	return &apps.Deployment{
    68  		ObjectMeta: metav1.ObjectMeta{
    69  			Name:        "test",
    70  			Labels:      labels,
    71  			Annotations: annotations,
    72  		},
    73  		Spec: apps.DeploymentSpec{
    74  			Replicas: 1,
    75  			Strategy: apps.DeploymentStrategy{
    76  				Type: apps.RecreateDeploymentStrategyType,
    77  			},
    78  			Template: api.PodTemplateSpec{
    79  				Spec: api.PodSpec{
    80  					Containers: []api.Container{
    81  						{
    82  							Name:  "test",
    83  							Image: "test",
    84  						},
    85  					},
    86  				},
    87  			},
    88  		},
    89  	}
    90  }
    91  
    92  func TestSelectorImmutability(t *testing.T) {
    93  	tests := []struct {
    94  		requestInfo       genericapirequest.RequestInfo
    95  		oldSelectorLabels map[string]string
    96  		newSelectorLabels map[string]string
    97  		expectedErrorList field.ErrorList
    98  	}{
    99  		{
   100  			genericapirequest.RequestInfo{
   101  				APIGroup:   "apps",
   102  				APIVersion: "v1beta2",
   103  				Resource:   "deployments",
   104  			},
   105  			map[string]string{"a": "b"},
   106  			map[string]string{"c": "d"},
   107  			field.ErrorList{
   108  				&field.Error{
   109  					Type:  field.ErrorTypeInvalid,
   110  					Field: field.NewPath("spec").Child("selector").String(),
   111  					BadValue: &metav1.LabelSelector{
   112  						MatchLabels:      map[string]string{"c": "d"},
   113  						MatchExpressions: []metav1.LabelSelectorRequirement{},
   114  					},
   115  					Detail: "field is immutable",
   116  				},
   117  			},
   118  		},
   119  		{
   120  			genericapirequest.RequestInfo{
   121  				APIGroup:   "apps",
   122  				APIVersion: "v1beta1",
   123  				Resource:   "deployments",
   124  			},
   125  			map[string]string{"a": "b"},
   126  			map[string]string{"c": "d"},
   127  			field.ErrorList{},
   128  		},
   129  		{
   130  			genericapirequest.RequestInfo{
   131  				APIGroup:   "extensions",
   132  				APIVersion: "v1beta1",
   133  			},
   134  			map[string]string{"a": "b"},
   135  			map[string]string{"c": "d"},
   136  			field.ErrorList{},
   137  		},
   138  	}
   139  
   140  	for _, test := range tests {
   141  		oldDeployment := newDeploymentWithSelectorLabels(test.oldSelectorLabels)
   142  		newDeployment := newDeploymentWithSelectorLabels(test.newSelectorLabels)
   143  		context := genericapirequest.NewContext()
   144  		context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
   145  		errorList := deploymentStrategy{}.ValidateUpdate(context, newDeployment, oldDeployment)
   146  		if len(test.expectedErrorList) == 0 && len(errorList) == 0 {
   147  			continue
   148  		}
   149  		if !reflect.DeepEqual(test.expectedErrorList, errorList) {
   150  			t.Errorf("Unexpected error list, expected: %v, actual: %v", test.expectedErrorList, errorList)
   151  		}
   152  	}
   153  }
   154  
   155  func newDeploymentWithSelectorLabels(selectorLabels map[string]string) *apps.Deployment {
   156  	return &apps.Deployment{
   157  		ObjectMeta: metav1.ObjectMeta{
   158  			Name:            deploymentName,
   159  			Namespace:       namespace,
   160  			ResourceVersion: "1",
   161  		},
   162  		Spec: apps.DeploymentSpec{
   163  			Selector: &metav1.LabelSelector{
   164  				MatchLabels:      selectorLabels,
   165  				MatchExpressions: []metav1.LabelSelectorRequirement{},
   166  			},
   167  			Strategy: apps.DeploymentStrategy{
   168  				Type: apps.RollingUpdateDeploymentStrategyType,
   169  				RollingUpdate: &apps.RollingUpdateDeployment{
   170  					MaxSurge:       intstr.FromInt32(1),
   171  					MaxUnavailable: intstr.FromInt32(1),
   172  				},
   173  			},
   174  			Template: api.PodTemplateSpec{
   175  				ObjectMeta: metav1.ObjectMeta{
   176  					Labels: selectorLabels,
   177  				},
   178  				Spec: api.PodSpec{
   179  					RestartPolicy: api.RestartPolicyAlways,
   180  					DNSPolicy:     api.DNSDefault,
   181  					Containers:    []api.Container{{Name: fakeImageName, Image: fakeImage, ImagePullPolicy: api.PullNever, TerminationMessagePolicy: api.TerminationMessageReadFile}},
   182  				},
   183  			},
   184  		},
   185  	}
   186  }
   187  
   188  func newDeploymentWithHugePageValue(resourceName api.ResourceName, value resource.Quantity) *apps.Deployment {
   189  	return &apps.Deployment{
   190  		ObjectMeta: metav1.ObjectMeta{
   191  			Name:            deploymentName,
   192  			Namespace:       namespace,
   193  			ResourceVersion: "1",
   194  		},
   195  		Spec: apps.DeploymentSpec{
   196  			Selector: &metav1.LabelSelector{
   197  				MatchLabels:      map[string]string{"foo": "bar"},
   198  				MatchExpressions: []metav1.LabelSelectorRequirement{},
   199  			},
   200  			Strategy: apps.DeploymentStrategy{
   201  				Type: apps.RollingUpdateDeploymentStrategyType,
   202  				RollingUpdate: &apps.RollingUpdateDeployment{
   203  					MaxSurge:       intstr.FromInt32(1),
   204  					MaxUnavailable: intstr.FromInt32(1),
   205  				},
   206  			},
   207  			Template: api.PodTemplateSpec{
   208  				ObjectMeta: metav1.ObjectMeta{
   209  					Namespace: "default",
   210  					Name:      "foo",
   211  					Labels:    map[string]string{"foo": "bar"},
   212  				},
   213  				Spec: api.PodSpec{
   214  					RestartPolicy: api.RestartPolicyAlways,
   215  					DNSPolicy:     api.DNSDefault,
   216  					Containers: []api.Container{{
   217  						Name:                     fakeImageName,
   218  						Image:                    fakeImage,
   219  						ImagePullPolicy:          api.PullNever,
   220  						TerminationMessagePolicy: api.TerminationMessageReadFile,
   221  						Resources: api.ResourceRequirements{
   222  							Requests: api.ResourceList{
   223  								api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
   224  								api.ResourceName(resourceName):    value,
   225  							},
   226  							Limits: api.ResourceList{
   227  								api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
   228  								api.ResourceName(resourceName):    value,
   229  							},
   230  						}},
   231  					}},
   232  			},
   233  		},
   234  	}
   235  }
   236  
   237  func TestDeploymentStrategyValidate(t *testing.T) {
   238  	tests := []struct {
   239  		name       string
   240  		deployment *apps.Deployment
   241  	}{
   242  		{
   243  			name:       "validation on a new deployment with indivisible hugepages values",
   244  			deployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"2Mi", resource.MustParse("2.1Mi")),
   245  		},
   246  	}
   247  
   248  	for _, tc := range tests {
   249  		t.Run(tc.name, func(t *testing.T) {
   250  			if errs := Strategy.Validate(genericapirequest.NewContext(), tc.deployment); len(errs) == 0 {
   251  				t.Error("expected failure")
   252  			}
   253  		})
   254  	}
   255  }
   256  
   257  func TestDeploymentStrategyValidateUpdate(t *testing.T) {
   258  	tests := []struct {
   259  		name          string
   260  		newDeployment *apps.Deployment
   261  		oldDeployment *apps.Deployment
   262  	}{
   263  		{
   264  			name:          "validation on an existing deployment with indivisible hugepages values to a new deployment with indivisible hugepages values",
   265  			newDeployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"2Mi", resource.MustParse("2.1Mi")),
   266  			oldDeployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"1Gi", resource.MustParse("1.1Gi")),
   267  		},
   268  	}
   269  
   270  	for _, tc := range tests {
   271  		t.Run(tc.name, func(t *testing.T) {
   272  			if errs := Strategy.ValidateUpdate(genericapirequest.NewContext(), tc.newDeployment, tc.oldDeployment); len(errs) != 0 {
   273  				t.Errorf("unexpected error:%v", errs)
   274  			}
   275  		})
   276  	}
   277  
   278  	errTests := []struct {
   279  		name          string
   280  		newDeployment *apps.Deployment
   281  		oldDeployment *apps.Deployment
   282  	}{
   283  		{
   284  			name:          "validation on an existing deployment with divisible hugepages values to a new deployment with indivisible hugepages values",
   285  			newDeployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"2Mi", resource.MustParse("2.1Mi")),
   286  			oldDeployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"1Gi", resource.MustParse("2Gi")),
   287  		},
   288  	}
   289  
   290  	for _, tc := range errTests {
   291  		t.Run(tc.name, func(t *testing.T) {
   292  			if errs := Strategy.ValidateUpdate(genericapirequest.NewContext(), tc.newDeployment, tc.oldDeployment); len(errs) == 0 {
   293  				t.Error("expected failure")
   294  			}
   295  		})
   296  	}
   297  }
   298  

View as plain text