github.com/banzaicloud/operator-tools@v0.28.10/pkg/merge/merge_test.go (about) 1 // Copyright © 2020 Banzai Cloud 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package merge 16 17 import ( 18 "testing" 19 20 "github.com/banzaicloud/operator-tools/pkg/typeoverride" 21 "github.com/banzaicloud/operator-tools/pkg/utils" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 v1 "k8s.io/api/apps/v1" 25 corev1 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/api/resource" 27 v12 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/util/intstr" 29 ) 30 31 func TestMerge(t *testing.T) { 32 base := &v1.DaemonSet{ 33 Spec: v1.DaemonSetSpec{ 34 Template: corev1.PodTemplateSpec{ 35 Spec: corev1.PodSpec{ 36 Containers: []corev1.Container{ 37 { 38 Name: "container-a", 39 Image: "image-a", 40 }, 41 { 42 Name: "container-b", 43 Image: "image-b", 44 Resources: corev1.ResourceRequirements{ 45 Limits: corev1.ResourceList{ 46 corev1.ResourceCPU: resource.MustParse("100m"), 47 corev1.ResourceMemory: resource.MustParse("100M"), 48 }, 49 }, 50 }, 51 { 52 Name: "container-c", 53 Image: "image-c", 54 // make sure we keep extra fields on the original slice item 55 Command: []string{"fake"}, 56 }, 57 }, 58 }, 59 }, 60 }, 61 } 62 overrides := &v1.DaemonSet{ 63 Spec: v1.DaemonSetSpec{ 64 Template: corev1.PodTemplateSpec{ 65 Spec: corev1.PodSpec{ 66 Containers: []corev1.Container{ 67 { 68 Name: "container-a", 69 Image: "image-a-2", 70 }, 71 { 72 Name: "container-b", 73 Resources: corev1.ResourceRequirements{ 74 Limits: corev1.ResourceList{ 75 corev1.ResourceCPU: resource.MustParse("123m"), 76 }, 77 }, 78 }, 79 { 80 Name: "container-d", 81 Image: "image-d", 82 }, 83 }, 84 }, 85 }, 86 }, 87 } 88 89 err := Merge(base, overrides) 90 require.NoError(t, err) 91 92 assert.Len(t, base.Spec.Template.Spec.Containers, 4) 93 94 // container a has a modified image 95 assert.Equal(t, "container-a", base.Spec.Template.Spec.Containers[0].Name) 96 assert.Equal(t, "image-a-2", base.Spec.Template.Spec.Containers[0].Image) 97 assert.Equal(t, corev1.ResourceRequirements{}, base.Spec.Template.Spec.Containers[0].Resources) 98 99 // container b has the same image but updated resource requirements 100 assert.Equal(t, "container-b", base.Spec.Template.Spec.Containers[1].Name) 101 assert.Equal(t, "image-b", base.Spec.Template.Spec.Containers[1].Image) 102 assert.Equal(t, corev1.ResourceRequirements{ 103 Limits: corev1.ResourceList{ 104 corev1.ResourceCPU: resource.MustParse("123m"), 105 corev1.ResourceMemory: resource.MustParse("100M"), 106 }, 107 }, base.Spec.Template.Spec.Containers[1].Resources) 108 109 // container d is added as a new item (note that it will come before container-c) 110 assert.Equal(t, corev1.Container{ 111 Name: "container-d", 112 Image: "image-d", 113 }, base.Spec.Template.Spec.Containers[2]) 114 115 // container c is not modified (note that it's index will change) 116 assert.Equal(t, corev1.Container{ 117 Name: "container-c", 118 Image: "image-c", 119 Command: []string{"fake"}, 120 }, base.Spec.Template.Spec.Containers[3]) 121 } 122 123 func TestVolume(t *testing.T) { 124 base := &v1.DaemonSet{ 125 Spec: v1.DaemonSetSpec{ 126 Template: corev1.PodTemplateSpec{ 127 Spec: corev1.PodSpec{ 128 Containers: []corev1.Container{ 129 { 130 Name: "container", 131 Image: "image", 132 VolumeMounts: []corev1.VolumeMount{ 133 { 134 Name: "asd", 135 MountPath: "/asd-path", 136 ReadOnly: true, 137 }, 138 }, 139 }, 140 }, 141 }, 142 }, 143 }, 144 } 145 overrides := &v1.DaemonSet{ 146 Spec: v1.DaemonSetSpec{ 147 Template: corev1.PodTemplateSpec{ 148 Spec: corev1.PodSpec{ 149 Containers: []corev1.Container{ 150 { 151 Name: "container", 152 Image: "image", 153 VolumeMounts: []corev1.VolumeMount{ 154 { 155 Name: "different", 156 MountPath: "/different-path", 157 }, 158 }, 159 }, 160 }, 161 }, 162 }, 163 }, 164 } 165 166 err := Merge(base, overrides) 167 require.NoError(t, err) 168 169 require.Equal(t, &v1.DaemonSet{ 170 Spec: v1.DaemonSetSpec{ 171 Template: corev1.PodTemplateSpec{ 172 Spec: corev1.PodSpec{ 173 Containers: []corev1.Container{ 174 { 175 Name: "container", 176 Image: "image", 177 VolumeMounts: []corev1.VolumeMount{ 178 { 179 Name: "different", 180 MountPath: "/different-path", 181 }, 182 { 183 Name: "asd", 184 MountPath: "/asd-path", 185 ReadOnly: true, 186 }, 187 }, 188 }, 189 }, 190 }, 191 }, 192 }, 193 }, base) 194 } 195 196 func TestMergeWithEmbeddedType(t *testing.T) { 197 base := &v1.DaemonSet{ 198 Spec: v1.DaemonSetSpec{ 199 Template: corev1.PodTemplateSpec{ 200 Spec: corev1.PodSpec{ 201 Containers: []corev1.Container{ 202 { 203 Name: "container-a", 204 Image: "image-a", 205 }, 206 { 207 Name: "container-b", 208 Image: "image-b", 209 Resources: corev1.ResourceRequirements{ 210 Limits: corev1.ResourceList{ 211 corev1.ResourceCPU: resource.MustParse("100m"), 212 corev1.ResourceMemory: resource.MustParse("100M"), 213 }, 214 }, 215 }, 216 { 217 Name: "container-c", 218 Image: "image-c", 219 }, 220 }, 221 }, 222 }, 223 }, 224 } 225 overrides := &typeoverride.DaemonSet{ 226 Spec: typeoverride.DaemonSetSpec{ 227 Template: typeoverride.PodTemplateSpec{ 228 Spec: typeoverride.PodSpec{ 229 Containers: []corev1.Container{ 230 { 231 Name: "container-a", 232 Image: "image-a-2", 233 }, 234 { 235 Name: "container-b", 236 Resources: corev1.ResourceRequirements{ 237 Limits: corev1.ResourceList{ 238 corev1.ResourceCPU: resource.MustParse("123m"), 239 }, 240 }, 241 }, 242 { 243 Name: "container-d", 244 Image: "image-d", 245 }, 246 }, 247 }, 248 }, 249 }, 250 } 251 252 err := Merge(base, overrides) 253 require.NoError(t, err) 254 255 assert.Len(t, base.Spec.Template.Spec.Containers, 4) 256 257 // container a has a modified image 258 assert.Equal(t, "container-a", base.Spec.Template.Spec.Containers[0].Name) 259 assert.Equal(t, "image-a-2", base.Spec.Template.Spec.Containers[0].Image) 260 assert.Equal(t, corev1.ResourceRequirements{}, base.Spec.Template.Spec.Containers[0].Resources) 261 262 // container b has the same image but updated resource requirements 263 assert.Equal(t, "container-b", base.Spec.Template.Spec.Containers[1].Name) 264 assert.Equal(t, "image-b", base.Spec.Template.Spec.Containers[1].Image) 265 assert.Equal(t, corev1.ResourceRequirements{ 266 Limits: corev1.ResourceList{ 267 corev1.ResourceCPU: resource.MustParse("123m"), 268 corev1.ResourceMemory: resource.MustParse("100M"), 269 }, 270 }, base.Spec.Template.Spec.Containers[1].Resources) 271 272 // container d is added as a new item (note that it will come before container-c) 273 assert.Equal(t, corev1.Container{ 274 Name: "container-d", 275 Image: "image-d", 276 }, base.Spec.Template.Spec.Containers[2]) 277 278 // container c is not modified (note that it's index will change) 279 assert.Equal(t, corev1.Container{ 280 Name: "container-c", 281 Image: "image-c", 282 }, base.Spec.Template.Spec.Containers[3]) 283 } 284 285 func TestMergeStatefulSetReplicas(t *testing.T) { 286 base := &v1.StatefulSet{ 287 Spec: v1.StatefulSetSpec{ 288 Replicas: utils.IntPointer(1), 289 }, 290 } 291 overrides := v1.StatefulSet{ 292 Spec: v1.StatefulSetSpec{ 293 Replicas: utils.IntPointer(0), 294 }, 295 } 296 err := Merge(base, overrides) 297 require.NoError(t, err) 298 299 assert.Equal(t, *base.Spec.Replicas, int32(0)) 300 } 301 302 func TestMergeArrayOverride(t *testing.T) { 303 base := &corev1.Service{ 304 Spec: corev1.ServiceSpec{ 305 ExternalIPs: []string{ 306 "a", "b", 307 }, 308 }, 309 } 310 overrides := &typeoverride.Service{ 311 Spec: corev1.ServiceSpec{ 312 ExternalIPs: []string{ 313 "c", "d", 314 }, 315 }, 316 } 317 318 err := Merge(base, overrides) 319 require.NoError(t, err) 320 321 require.Equal(t, []string{"c", "d"}, base.Spec.ExternalIPs) 322 } 323 324 func TestMergeMap(t *testing.T) { 325 base := &corev1.Service{ 326 ObjectMeta: v12.ObjectMeta{ 327 Labels: map[string]string{ 328 "a": "1", 329 "b": "2", 330 }, 331 }, 332 } 333 overrides := &corev1.Service{ 334 ObjectMeta: v12.ObjectMeta{ 335 Labels: map[string]string{ 336 "b": "3", 337 "c": "4", 338 }, 339 }, 340 } 341 342 err := Merge(base, overrides) 343 require.NoError(t, err) 344 345 require.Equal(t, map[string]string{ 346 "a": "1", 347 "b": "3", 348 "c": "4", 349 }, base.ObjectMeta.Labels) 350 } 351 352 func TestMergeMapWithEmbeddedType(t *testing.T) { 353 base := &corev1.Service{ 354 ObjectMeta: v12.ObjectMeta{ 355 Labels: map[string]string{ 356 "a": "1", 357 "b": "2", 358 }, 359 }, 360 } 361 overrides := &typeoverride.Service{ 362 ObjectMeta: typeoverride.ObjectMeta{ 363 Labels: map[string]string{ 364 "b": "3", 365 "c": "4", 366 }, 367 }, 368 } 369 370 err := Merge(base, overrides) 371 require.NoError(t, err) 372 373 require.Equal(t, map[string]string{ 374 "a": "1", 375 "b": "3", 376 "c": "4", 377 }, base.ObjectMeta.Labels) 378 } 379 380 func TestMergeService(t *testing.T) { 381 base := &corev1.Service{ 382 ObjectMeta: v12.ObjectMeta{ 383 Labels: map[string]string{ 384 "a": "1", 385 "b": "2", 386 }, 387 }, 388 Spec: corev1.ServiceSpec{ 389 Ports: []corev1.ServicePort{ 390 { 391 Name: "http", 392 Port: 80, 393 TargetPort: intstr.FromInt(8080), 394 }, 395 }, 396 Selector: map[string]string{ 397 "a": "1", 398 "b": "2", 399 }, 400 LoadBalancerIP: "1.2.3.4", 401 }, 402 } 403 overrides := &typeoverride.Service{ 404 ObjectMeta: typeoverride.ObjectMeta{ 405 Labels: map[string]string{ 406 "b": "3", 407 "c": "4", 408 }, 409 }, 410 Spec: corev1.ServiceSpec{ 411 Ports: []corev1.ServicePort{ 412 { 413 Name: "http1", 414 Port: 80, 415 TargetPort: intstr.FromInt(8081), 416 }, 417 { 418 Name: "http2", 419 Port: 82, 420 TargetPort: intstr.FromInt(8082), 421 }, 422 }, 423 Selector:map[string]string{ 424 "b": "3", 425 "c": "4", 426 }, 427 }, 428 } 429 430 err := Merge(base, overrides) 431 require.NoError(t, err) 432 433 require.Equal(t, map[string]string{ 434 "a": "1", 435 "b": "3", 436 "c": "4", 437 }, base.ObjectMeta.Labels) 438 439 require.Equal(t, base.Spec, corev1.ServiceSpec{ 440 Ports: []corev1.ServicePort{ 441 { 442 Name: "http1", 443 Port: 80, 444 TargetPort: intstr.FromInt(8081), 445 }, 446 { 447 Name: "http2", 448 Port: 82, 449 TargetPort: intstr.FromInt(8082), 450 }, 451 }, 452 Selector: map[string]string{ 453 "a": "1", 454 "b": "3", 455 "c": "4", 456 }, 457 LoadBalancerIP: "1.2.3.4", 458 }) 459 }