k8s.io/kubernetes@v1.29.3/pkg/registry/apps/replicaset/strategy_test.go (about) 1 /* 2 Copyright 2016 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 replicaset 18 19 import ( 20 "reflect" 21 "testing" 22 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/util/validation/field" 25 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 26 "k8s.io/kubernetes/pkg/apis/apps" 27 api "k8s.io/kubernetes/pkg/apis/core" 28 ) 29 30 const ( 31 fakeImageName = "fake-name" 32 fakeImage = "fakeimage" 33 replicasetName = "test-replicaset" 34 namespace = "test-namespace" 35 ) 36 37 func TestReplicaSetStrategy(t *testing.T) { 38 ctx := genericapirequest.NewDefaultContext() 39 if !Strategy.NamespaceScoped() { 40 t.Errorf("ReplicaSet must be namespace scoped") 41 } 42 if Strategy.AllowCreateOnUpdate() { 43 t.Errorf("ReplicaSet should not allow create on update") 44 } 45 46 validSelector := map[string]string{"a": "b"} 47 validPodTemplate := api.PodTemplate{ 48 Template: api.PodTemplateSpec{ 49 ObjectMeta: metav1.ObjectMeta{ 50 Labels: validSelector, 51 }, 52 Spec: api.PodSpec{ 53 RestartPolicy: api.RestartPolicyAlways, 54 DNSPolicy: api.DNSClusterFirst, 55 Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, 56 }, 57 }, 58 } 59 rs := &apps.ReplicaSet{ 60 ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, 61 Spec: apps.ReplicaSetSpec{ 62 Selector: &metav1.LabelSelector{MatchLabels: validSelector}, 63 Template: validPodTemplate.Template, 64 }, 65 Status: apps.ReplicaSetStatus{ 66 Replicas: 1, 67 ObservedGeneration: int64(10), 68 }, 69 } 70 71 Strategy.PrepareForCreate(ctx, rs) 72 if rs.Status.Replicas != 0 { 73 t.Error("ReplicaSet should not allow setting status.replicas on create") 74 } 75 if rs.Status.ObservedGeneration != int64(0) { 76 t.Error("ReplicaSet should not allow setting status.observedGeneration on create") 77 } 78 errs := Strategy.Validate(ctx, rs) 79 if len(errs) != 0 { 80 t.Errorf("Unexpected error validating %v", errs) 81 } 82 83 invalidRc := &apps.ReplicaSet{ 84 ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "4"}, 85 } 86 Strategy.PrepareForUpdate(ctx, invalidRc, rs) 87 errs = Strategy.ValidateUpdate(ctx, invalidRc, rs) 88 if len(errs) == 0 { 89 t.Errorf("Expected a validation error") 90 } 91 if invalidRc.ResourceVersion != "4" { 92 t.Errorf("Incoming resource version on update should not be mutated") 93 } 94 } 95 96 func TestReplicaSetStatusStrategy(t *testing.T) { 97 ctx := genericapirequest.NewDefaultContext() 98 if !StatusStrategy.NamespaceScoped() { 99 t.Errorf("ReplicaSet must be namespace scoped") 100 } 101 if StatusStrategy.AllowCreateOnUpdate() { 102 t.Errorf("ReplicaSet should not allow create on update") 103 } 104 validSelector := map[string]string{"a": "b"} 105 validPodTemplate := api.PodTemplate{ 106 Template: api.PodTemplateSpec{ 107 ObjectMeta: metav1.ObjectMeta{ 108 Labels: validSelector, 109 }, 110 Spec: api.PodSpec{ 111 RestartPolicy: api.RestartPolicyAlways, 112 DNSPolicy: api.DNSClusterFirst, 113 Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}}, 114 }, 115 }, 116 } 117 oldRS := &apps.ReplicaSet{ 118 ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "10"}, 119 Spec: apps.ReplicaSetSpec{ 120 Replicas: 3, 121 Selector: &metav1.LabelSelector{MatchLabels: validSelector}, 122 Template: validPodTemplate.Template, 123 }, 124 Status: apps.ReplicaSetStatus{ 125 Replicas: 1, 126 ObservedGeneration: int64(10), 127 }, 128 } 129 newRS := &apps.ReplicaSet{ 130 ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "9"}, 131 Spec: apps.ReplicaSetSpec{ 132 Replicas: 1, 133 Selector: &metav1.LabelSelector{MatchLabels: validSelector}, 134 Template: validPodTemplate.Template, 135 }, 136 Status: apps.ReplicaSetStatus{ 137 Replicas: 3, 138 ObservedGeneration: int64(11), 139 }, 140 } 141 StatusStrategy.PrepareForUpdate(ctx, newRS, oldRS) 142 if newRS.Status.Replicas != 3 { 143 t.Errorf("ReplicaSet status updates should allow change of replicas: %v", newRS.Status.Replicas) 144 } 145 if newRS.Spec.Replicas != 3 { 146 t.Errorf("PrepareForUpdate should have preferred spec") 147 } 148 errs := StatusStrategy.ValidateUpdate(ctx, newRS, oldRS) 149 if len(errs) != 0 { 150 t.Errorf("Unexpected error %v", errs) 151 } 152 } 153 154 func TestSelectorImmutability(t *testing.T) { 155 tests := []struct { 156 requestInfo genericapirequest.RequestInfo 157 oldSelectorLabels map[string]string 158 newSelectorLabels map[string]string 159 expectedErrorList field.ErrorList 160 }{ 161 { 162 genericapirequest.RequestInfo{ 163 APIGroup: "apps", 164 APIVersion: "v1beta2", 165 Resource: "replicasets", 166 }, 167 map[string]string{"a": "b"}, 168 map[string]string{"c": "d"}, 169 field.ErrorList{ 170 &field.Error{ 171 Type: field.ErrorTypeInvalid, 172 Field: field.NewPath("spec").Child("selector").String(), 173 BadValue: &metav1.LabelSelector{ 174 MatchLabels: map[string]string{"c": "d"}, 175 MatchExpressions: []metav1.LabelSelectorRequirement{}, 176 }, 177 Detail: "field is immutable", 178 }, 179 }, 180 }, 181 { 182 genericapirequest.RequestInfo{ 183 APIGroup: "extensions", 184 APIVersion: "v1beta1", 185 Resource: "replicasets", 186 }, 187 map[string]string{"a": "b"}, 188 map[string]string{"c": "d"}, 189 nil, 190 }, 191 } 192 193 for _, test := range tests { 194 oldReplicaSet := newReplicaSetWithSelectorLabels(test.oldSelectorLabels) 195 newReplicaSet := newReplicaSetWithSelectorLabels(test.newSelectorLabels) 196 context := genericapirequest.NewContext() 197 context = genericapirequest.WithRequestInfo(context, &test.requestInfo) 198 errorList := rsStrategy{}.ValidateUpdate(context, newReplicaSet, oldReplicaSet) 199 if !reflect.DeepEqual(test.expectedErrorList, errorList) { 200 t.Errorf("Unexpected error list, expected: %v, actual: %v", test.expectedErrorList, errorList) 201 } 202 } 203 } 204 205 func newReplicaSetWithSelectorLabels(selectorLabels map[string]string) *apps.ReplicaSet { 206 return &apps.ReplicaSet{ 207 ObjectMeta: metav1.ObjectMeta{ 208 Name: replicasetName, 209 Namespace: namespace, 210 ResourceVersion: "1", 211 }, 212 Spec: apps.ReplicaSetSpec{ 213 Selector: &metav1.LabelSelector{ 214 MatchLabels: selectorLabels, 215 MatchExpressions: []metav1.LabelSelectorRequirement{}, 216 }, 217 Template: api.PodTemplateSpec{ 218 ObjectMeta: metav1.ObjectMeta{ 219 Labels: selectorLabels, 220 }, 221 Spec: api.PodSpec{ 222 RestartPolicy: api.RestartPolicyAlways, 223 DNSPolicy: api.DNSClusterFirst, 224 Containers: []api.Container{{Name: fakeImageName, Image: fakeImage, ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, 225 }, 226 }, 227 }, 228 } 229 }