sigs.k8s.io/cluster-api-provider-azure@v1.17.0/api/v1beta1/azuremanagedmachinepool_webhook_test.go (about) 1 /* 2 Copyright 2023 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 v1beta1 18 19 import ( 20 "context" 21 "testing" 22 23 asocontainerservicev1 "github.com/Azure/azure-service-operator/v2/api/containerservice/v1api20231001" 24 . "github.com/onsi/gomega" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 utilfeature "k8s.io/component-base/featuregate/testing" 28 "k8s.io/utils/ptr" 29 "sigs.k8s.io/cluster-api-provider-azure/feature" 30 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 31 clusterctlv1alpha3 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" 32 capifeature "sigs.k8s.io/cluster-api/feature" 33 "sigs.k8s.io/controller-runtime/pkg/client" 34 "sigs.k8s.io/controller-runtime/pkg/client/fake" 35 ) 36 37 func TestAzureManagedMachinePoolDefaultingWebhook(t *testing.T) { 38 g := NewWithT(t) 39 40 t.Logf("Testing ammp defaulting webhook with mode system") 41 ammp := &AzureManagedMachinePool{ 42 ObjectMeta: metav1.ObjectMeta{ 43 Name: "fooname", 44 }, 45 Spec: AzureManagedMachinePoolSpec{ 46 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 47 Mode: "System", 48 SKU: "StandardD2S_V3", 49 OSDiskSizeGB: ptr.To(512), 50 }, 51 }, 52 } 53 var client client.Client 54 mw := &azureManagedMachinePoolWebhook{ 55 Client: client, 56 } 57 err := mw.Default(context.Background(), ammp) 58 g.Expect(err).NotTo(HaveOccurred()) 59 g.Expect(ammp.Labels).NotTo(BeNil()) 60 val, ok := ammp.Labels[LabelAgentPoolMode] 61 g.Expect(ok).To(BeTrue()) 62 g.Expect(val).To(Equal("System")) 63 g.Expect(*ammp.Spec.Name).To(Equal("fooname")) 64 g.Expect(*ammp.Spec.OSType).To(Equal(LinuxOS)) 65 66 t.Logf("Testing ammp defaulting webhook with empty string name specified in Spec") 67 emptyName := "" 68 ammp.Spec.Name = &emptyName 69 err = mw.Default(context.Background(), ammp) 70 g.Expect(err).NotTo(HaveOccurred()) 71 g.Expect(*ammp.Spec.Name).To(Equal("fooname")) 72 73 t.Logf("Testing ammp defaulting webhook with normal name specified in Spec") 74 normalName := "barname" 75 ammp.Spec.Name = &normalName 76 err = mw.Default(context.Background(), ammp) 77 g.Expect(err).NotTo(HaveOccurred()) 78 g.Expect(*ammp.Spec.Name).To(Equal("barname")) 79 80 t.Logf("Testing ammp defaulting webhook with normal OsDiskType specified in Spec") 81 normalOsDiskType := "Ephemeral" 82 ammp.Spec.OsDiskType = &normalOsDiskType 83 err = mw.Default(context.Background(), ammp) 84 g.Expect(err).NotTo(HaveOccurred()) 85 g.Expect(*ammp.Spec.OsDiskType).To(Equal("Ephemeral")) 86 } 87 88 func TestAzureManagedMachinePoolUpdatingWebhook(t *testing.T) { 89 t.Logf("Testing ammp updating webhook with mode system") 90 91 tests := []struct { 92 name string 93 new *AzureManagedMachinePool 94 old *AzureManagedMachinePool 95 wantErr bool 96 }{ 97 { 98 name: "Cannot change Name of the agentpool", 99 new: &AzureManagedMachinePool{ 100 Spec: AzureManagedMachinePoolSpec{ 101 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 102 Name: ptr.To("pool-new"), 103 }, 104 }, 105 }, 106 old: &AzureManagedMachinePool{ 107 Spec: AzureManagedMachinePoolSpec{ 108 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 109 Name: ptr.To("pool-old"), 110 }, 111 }, 112 }, 113 wantErr: true, 114 }, 115 { 116 name: "Cannot change SKU of the agentpool", 117 new: &AzureManagedMachinePool{ 118 Spec: AzureManagedMachinePoolSpec{ 119 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 120 Mode: "System", 121 SKU: "StandardD2S_V3", 122 OSDiskSizeGB: ptr.To(512), 123 }, 124 }, 125 }, 126 old: &AzureManagedMachinePool{ 127 Spec: AzureManagedMachinePoolSpec{ 128 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 129 Mode: "System", 130 SKU: "StandardD2S_V4", 131 OSDiskSizeGB: ptr.To(512), 132 }, 133 }, 134 }, 135 wantErr: true, 136 }, 137 { 138 name: "Cannot change OSType of the agentpool", 139 new: &AzureManagedMachinePool{ 140 Spec: AzureManagedMachinePoolSpec{ 141 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 142 OSType: ptr.To(LinuxOS), 143 Mode: "System", 144 SKU: "StandardD2S_V3", 145 OSDiskSizeGB: ptr.To(512), 146 }, 147 }, 148 }, 149 old: &AzureManagedMachinePool{ 150 Spec: AzureManagedMachinePoolSpec{ 151 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 152 OSType: ptr.To(WindowsOS), 153 Mode: "System", 154 SKU: "StandardD2S_V4", 155 OSDiskSizeGB: ptr.To(512), 156 }, 157 }, 158 }, 159 wantErr: true, 160 }, 161 { 162 name: "Cannot change OSDiskSizeGB of the agentpool", 163 new: &AzureManagedMachinePool{ 164 Spec: AzureManagedMachinePoolSpec{ 165 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 166 Mode: "System", 167 SKU: "StandardD2S_V3", 168 OSDiskSizeGB: ptr.To(512), 169 }, 170 }, 171 }, 172 old: &AzureManagedMachinePool{ 173 Spec: AzureManagedMachinePoolSpec{ 174 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 175 Mode: "System", 176 SKU: "StandardD2S_V3", 177 OSDiskSizeGB: ptr.To(1024), 178 }, 179 }, 180 }, 181 wantErr: true, 182 }, 183 { 184 name: "Cannot add AvailabilityZones after creating agentpool", 185 new: &AzureManagedMachinePool{ 186 Spec: AzureManagedMachinePoolSpec{ 187 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 188 Mode: "System", 189 SKU: "StandardD2S_V3", 190 OSDiskSizeGB: ptr.To(512), 191 AvailabilityZones: []string{"1", "2", "3"}, 192 }, 193 }, 194 }, 195 old: &AzureManagedMachinePool{ 196 Spec: AzureManagedMachinePoolSpec{ 197 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 198 Mode: "System", 199 SKU: "StandardD2S_V3", 200 OSDiskSizeGB: ptr.To(512), 201 }, 202 }, 203 }, 204 wantErr: true, 205 }, 206 { 207 name: "Cannot remove AvailabilityZones after creating agentpool", 208 new: &AzureManagedMachinePool{ 209 Spec: AzureManagedMachinePoolSpec{ 210 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 211 Mode: "System", 212 SKU: "StandardD2S_V3", 213 OSDiskSizeGB: ptr.To(512), 214 }, 215 }, 216 }, 217 old: &AzureManagedMachinePool{ 218 Spec: AzureManagedMachinePoolSpec{ 219 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 220 Mode: "System", 221 SKU: "StandardD2S_V3", 222 OSDiskSizeGB: ptr.To(512), 223 AvailabilityZones: []string{"1", "2", "3"}, 224 }, 225 }, 226 }, 227 wantErr: true, 228 }, 229 { 230 name: "Cannot change AvailabilityZones of the agentpool", 231 new: &AzureManagedMachinePool{ 232 Spec: AzureManagedMachinePoolSpec{ 233 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 234 Mode: "System", 235 SKU: "StandardD2S_V3", 236 OSDiskSizeGB: ptr.To(512), 237 AvailabilityZones: []string{"1", "2"}, 238 }, 239 }, 240 }, 241 old: &AzureManagedMachinePool{ 242 Spec: AzureManagedMachinePoolSpec{ 243 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 244 Mode: "System", 245 SKU: "StandardD2S_V3", 246 OSDiskSizeGB: ptr.To(512), 247 AvailabilityZones: []string{"1", "2", "3"}, 248 }, 249 }, 250 }, 251 wantErr: true, 252 }, 253 { 254 name: "AvailabilityZones order can be different", 255 new: &AzureManagedMachinePool{ 256 Spec: AzureManagedMachinePoolSpec{ 257 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 258 Mode: "System", 259 SKU: "StandardD2S_V3", 260 OSDiskSizeGB: ptr.To(512), 261 AvailabilityZones: []string{"1", "3", "2"}, 262 }, 263 }, 264 }, 265 old: &AzureManagedMachinePool{ 266 Spec: AzureManagedMachinePoolSpec{ 267 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 268 Mode: "System", 269 SKU: "StandardD2S_V3", 270 OSDiskSizeGB: ptr.To(512), 271 AvailabilityZones: []string{"1", "2", "3"}, 272 }, 273 }, 274 }, 275 wantErr: false, 276 }, 277 { 278 name: "Cannot change MaxPods of the agentpool", 279 new: &AzureManagedMachinePool{ 280 Spec: AzureManagedMachinePoolSpec{ 281 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 282 Mode: "System", 283 SKU: "StandardD2S_V3", 284 OSDiskSizeGB: ptr.To(512), 285 MaxPods: ptr.To(24), 286 }, 287 }, 288 }, 289 old: &AzureManagedMachinePool{ 290 Spec: AzureManagedMachinePoolSpec{ 291 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 292 Mode: "System", 293 SKU: "StandardD2S_V3", 294 OSDiskSizeGB: ptr.To(512), 295 MaxPods: ptr.To(25), 296 }, 297 }, 298 }, 299 wantErr: true, 300 }, 301 { 302 name: "Unchanged MaxPods in an agentpool should not result in an error", 303 new: &AzureManagedMachinePool{ 304 Spec: AzureManagedMachinePoolSpec{ 305 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 306 Mode: "System", 307 SKU: "StandardD2S_V3", 308 OSDiskSizeGB: ptr.To(512), 309 MaxPods: ptr.To(30), 310 }, 311 }, 312 }, 313 old: &AzureManagedMachinePool{ 314 Spec: AzureManagedMachinePoolSpec{ 315 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 316 Mode: "System", 317 SKU: "StandardD2S_V3", 318 OSDiskSizeGB: ptr.To(512), 319 MaxPods: ptr.To(30), 320 }, 321 }, 322 }, 323 wantErr: false, 324 }, 325 { 326 name: "Cannot change OSDiskType of the agentpool", 327 new: &AzureManagedMachinePool{ 328 Spec: AzureManagedMachinePoolSpec{ 329 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 330 Mode: "System", 331 SKU: "StandardD2S_V3", 332 OSDiskSizeGB: ptr.To(512), 333 MaxPods: ptr.To(24), 334 OsDiskType: ptr.To(string(asocontainerservicev1.OSDiskType_Ephemeral)), 335 }, 336 }, 337 }, 338 old: &AzureManagedMachinePool{ 339 Spec: AzureManagedMachinePoolSpec{ 340 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 341 Mode: "System", 342 SKU: "StandardD2S_V3", 343 OSDiskSizeGB: ptr.To(512), 344 MaxPods: ptr.To(24), 345 OsDiskType: ptr.To(string(asocontainerservicev1.OSDiskType_Managed)), 346 }, 347 }, 348 }, 349 wantErr: true, 350 }, 351 { 352 name: "Unchanged OSDiskType in an agentpool should not result in an error", 353 new: &AzureManagedMachinePool{ 354 Spec: AzureManagedMachinePoolSpec{ 355 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 356 Mode: "System", 357 SKU: "StandardD2S_V3", 358 OSDiskSizeGB: ptr.To(512), 359 MaxPods: ptr.To(30), 360 OsDiskType: ptr.To(string(asocontainerservicev1.OSDiskType_Managed)), 361 }, 362 }, 363 }, 364 old: &AzureManagedMachinePool{ 365 Spec: AzureManagedMachinePoolSpec{ 366 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 367 Mode: "System", 368 SKU: "StandardD2S_V3", 369 OSDiskSizeGB: ptr.To(512), 370 MaxPods: ptr.To(30), 371 OsDiskType: ptr.To(string(asocontainerservicev1.OSDiskType_Managed)), 372 }, 373 }, 374 }, 375 wantErr: false, 376 }, 377 { 378 name: "Unexpected error, value EnableUltraSSD is unchanged", 379 new: &AzureManagedMachinePool{ 380 Spec: AzureManagedMachinePoolSpec{ 381 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 382 EnableUltraSSD: ptr.To(true), 383 }, 384 }, 385 }, 386 old: &AzureManagedMachinePool{ 387 Spec: AzureManagedMachinePoolSpec{ 388 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 389 EnableUltraSSD: ptr.To(true), 390 }, 391 }, 392 }, 393 wantErr: false, 394 }, 395 { 396 name: "EnableUltraSSD feature is immutable and currently enabled on this agentpool", 397 new: &AzureManagedMachinePool{ 398 Spec: AzureManagedMachinePoolSpec{ 399 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 400 EnableUltraSSD: ptr.To(false), 401 }, 402 }, 403 }, 404 old: &AzureManagedMachinePool{ 405 Spec: AzureManagedMachinePoolSpec{ 406 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 407 EnableUltraSSD: ptr.To(true), 408 }, 409 }, 410 }, 411 wantErr: true, 412 }, 413 { 414 name: "Unexpected error, value EnableNodePublicIP is unchanged", 415 new: &AzureManagedMachinePool{ 416 Spec: AzureManagedMachinePoolSpec{ 417 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 418 EnableNodePublicIP: ptr.To(true), 419 }, 420 }, 421 }, 422 old: &AzureManagedMachinePool{ 423 Spec: AzureManagedMachinePoolSpec{ 424 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 425 EnableNodePublicIP: ptr.To(true), 426 }, 427 }, 428 }, 429 wantErr: false, 430 }, 431 { 432 name: "EnableNodePublicIP feature is immutable and currently enabled on this agentpool", 433 new: &AzureManagedMachinePool{ 434 Spec: AzureManagedMachinePoolSpec{ 435 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 436 EnableNodePublicIP: ptr.To(false), 437 }, 438 }, 439 }, 440 old: &AzureManagedMachinePool{ 441 Spec: AzureManagedMachinePoolSpec{ 442 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 443 EnableNodePublicIP: ptr.To(true), 444 }, 445 }, 446 }, 447 wantErr: true, 448 }, 449 { 450 name: "NodeTaints are mutable", 451 new: &AzureManagedMachinePool{ 452 Spec: AzureManagedMachinePoolSpec{ 453 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 454 Taints: []Taint{ 455 { 456 Effect: TaintEffect("NoSchedule"), 457 Key: "foo", 458 Value: "baz", 459 }, 460 }, 461 }, 462 }, 463 }, 464 old: &AzureManagedMachinePool{ 465 Spec: AzureManagedMachinePoolSpec{ 466 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 467 Taints: []Taint{ 468 { 469 Effect: TaintEffect("NoSchedule"), 470 Key: "foo", 471 Value: "bar", 472 }, 473 }, 474 }, 475 }, 476 }, 477 wantErr: false, 478 }, 479 { 480 name: "Can't add a node label that begins with kubernetes.azure.com", 481 new: &AzureManagedMachinePool{ 482 Spec: AzureManagedMachinePoolSpec{ 483 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 484 NodeLabels: map[string]string{ 485 "foo": "bar", 486 "kubernetes.azure.com/scalesetpriority": "spot", 487 }, 488 }, 489 }, 490 }, 491 old: &AzureManagedMachinePool{ 492 Spec: AzureManagedMachinePoolSpec{ 493 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 494 NodeLabels: map[string]string{ 495 "foo": "bar", 496 }, 497 }, 498 }, 499 }, 500 wantErr: true, 501 }, 502 { 503 name: "Can't update kubeletconfig", 504 new: &AzureManagedMachinePool{ 505 Spec: AzureManagedMachinePoolSpec{ 506 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 507 KubeletConfig: &KubeletConfig{ 508 CPUCfsQuota: ptr.To(true), 509 }, 510 }, 511 }, 512 }, 513 old: &AzureManagedMachinePool{ 514 Spec: AzureManagedMachinePoolSpec{ 515 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 516 KubeletConfig: &KubeletConfig{ 517 CPUCfsQuota: ptr.To(false), 518 }, 519 }, 520 }, 521 }, 522 wantErr: true, 523 }, 524 { 525 name: "Can't update LinuxOSConfig", 526 new: &AzureManagedMachinePool{ 527 Spec: AzureManagedMachinePoolSpec{ 528 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 529 LinuxOSConfig: &LinuxOSConfig{ 530 SwapFileSizeMB: ptr.To(10), 531 }, 532 }, 533 }, 534 }, 535 old: &AzureManagedMachinePool{ 536 Spec: AzureManagedMachinePoolSpec{ 537 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 538 LinuxOSConfig: &LinuxOSConfig{ 539 SwapFileSizeMB: ptr.To(5), 540 }, 541 }, 542 }, 543 }, 544 wantErr: true, 545 }, 546 { 547 name: "Can't update SubnetName with error", 548 new: &AzureManagedMachinePool{ 549 Spec: AzureManagedMachinePoolSpec{ 550 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 551 SubnetName: ptr.To("my-subnet"), 552 }, 553 }, 554 }, 555 old: &AzureManagedMachinePool{ 556 Spec: AzureManagedMachinePoolSpec{ 557 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 558 SubnetName: ptr.To("my-subnet-1"), 559 }, 560 }, 561 }, 562 wantErr: true, 563 }, 564 { 565 name: "Can update SubnetName if subnetName is empty", 566 new: &AzureManagedMachinePool{ 567 Spec: AzureManagedMachinePoolSpec{ 568 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 569 SubnetName: ptr.To("my-subnet"), 570 }, 571 }, 572 }, 573 old: &AzureManagedMachinePool{ 574 Spec: AzureManagedMachinePoolSpec{ 575 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 576 SubnetName: nil, 577 }, 578 }, 579 }, 580 wantErr: false, 581 }, 582 { 583 name: "Can't update SubnetName without error", 584 new: &AzureManagedMachinePool{ 585 Spec: AzureManagedMachinePoolSpec{ 586 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 587 SubnetName: ptr.To("my-subnet"), 588 }, 589 }, 590 }, 591 old: &AzureManagedMachinePool{ 592 Spec: AzureManagedMachinePoolSpec{ 593 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 594 SubnetName: ptr.To("my-subnet"), 595 }, 596 }, 597 }, 598 wantErr: false, 599 }, 600 { 601 name: "Cannot update enableFIPS", 602 new: &AzureManagedMachinePool{ 603 Spec: AzureManagedMachinePoolSpec{ 604 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 605 EnableFIPS: ptr.To(true), 606 }, 607 }, 608 }, 609 old: &AzureManagedMachinePool{ 610 Spec: AzureManagedMachinePoolSpec{ 611 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 612 EnableFIPS: ptr.To(false), 613 }, 614 }, 615 }, 616 wantErr: true, 617 }, 618 { 619 name: "Cannot update enableEncryptionAtHost", 620 new: &AzureManagedMachinePool{ 621 Spec: AzureManagedMachinePoolSpec{ 622 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 623 EnableEncryptionAtHost: ptr.To(true), 624 }, 625 }, 626 }, 627 old: &AzureManagedMachinePool{ 628 Spec: AzureManagedMachinePoolSpec{ 629 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 630 EnableEncryptionAtHost: ptr.To(false), 631 }, 632 }, 633 }, 634 wantErr: true, 635 }, 636 } 637 var client client.Client 638 for _, tc := range tests { 639 tc := tc 640 t.Run(tc.name, func(t *testing.T) { 641 t.Parallel() 642 g := NewWithT(t) 643 mw := &azureManagedMachinePoolWebhook{ 644 Client: client, 645 } 646 _, err := mw.ValidateUpdate(context.Background(), tc.old, tc.new) 647 if tc.wantErr { 648 g.Expect(err).To(HaveOccurred()) 649 } else { 650 g.Expect(err).NotTo(HaveOccurred()) 651 } 652 }) 653 } 654 } 655 656 func TestAzureManagedMachinePool_ValidateCreate(t *testing.T) { 657 tests := []struct { 658 name string 659 ammp *AzureManagedMachinePool 660 wantErr bool 661 errorLen int 662 }{ 663 { 664 name: "valid", 665 ammp: getKnownValidAzureManagedMachinePool(), 666 wantErr: false, 667 }, 668 { 669 name: "another valid permutation", 670 ammp: &AzureManagedMachinePool{ 671 Spec: AzureManagedMachinePoolSpec{ 672 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 673 MaxPods: ptr.To(249), 674 OsDiskType: ptr.To(string(asocontainerservicev1.OSDiskType_Managed)), 675 }, 676 }, 677 }, 678 wantErr: false, 679 }, 680 { 681 name: "valid - optional configuration not present", 682 ammp: &AzureManagedMachinePool{ 683 Spec: AzureManagedMachinePoolSpec{}, 684 }, 685 wantErr: false, 686 }, 687 { 688 name: "too many MaxPods", 689 ammp: &AzureManagedMachinePool{ 690 Spec: AzureManagedMachinePoolSpec{ 691 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 692 MaxPods: ptr.To(251), 693 }, 694 }, 695 }, 696 wantErr: true, 697 errorLen: 1, 698 }, 699 { 700 name: "invalid subnetname", 701 ammp: &AzureManagedMachinePool{ 702 Spec: AzureManagedMachinePoolSpec{ 703 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 704 SubnetName: ptr.To("1+subnet"), 705 }, 706 }, 707 }, 708 wantErr: true, 709 errorLen: 1, 710 }, 711 { 712 name: "invalid subnetname", 713 ammp: &AzureManagedMachinePool{ 714 Spec: AzureManagedMachinePoolSpec{ 715 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 716 SubnetName: ptr.To("1"), 717 }, 718 }, 719 }, 720 wantErr: true, 721 errorLen: 1, 722 }, 723 { 724 name: "invalid subnetname", 725 ammp: &AzureManagedMachinePool{ 726 Spec: AzureManagedMachinePoolSpec{ 727 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 728 SubnetName: ptr.To("-a_b-c"), 729 }, 730 }, 731 }, 732 wantErr: true, 733 errorLen: 1, 734 }, 735 { 736 name: "invalid subnetname with versioning", 737 ammp: &AzureManagedMachinePool{ 738 Spec: AzureManagedMachinePoolSpec{ 739 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 740 SubnetName: ptr.To("workload-ampt-v0.1.0."), 741 }, 742 }, 743 }, 744 wantErr: true, 745 errorLen: 1, 746 }, 747 { 748 name: "invalid subnetname", 749 ammp: &AzureManagedMachinePool{ 750 Spec: AzureManagedMachinePoolSpec{ 751 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 752 SubnetName: ptr.To("-_-_"), 753 }, 754 }, 755 }, 756 wantErr: true, 757 errorLen: 1, 758 }, 759 { 760 name: "invalid subnetname", 761 ammp: &AzureManagedMachinePool{ 762 Spec: AzureManagedMachinePoolSpec{ 763 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 764 SubnetName: ptr.To("abc@#$"), 765 }, 766 }, 767 }, 768 wantErr: true, 769 errorLen: 1, 770 }, 771 { 772 name: "invalid subnetname with character length 81", 773 ammp: &AzureManagedMachinePool{ 774 Spec: AzureManagedMachinePoolSpec{ 775 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 776 SubnetName: ptr.To("3DgIb8EZMkLs0KlyPaTcNxoJU9ufmW6jvXrweqz1hVp5nS4RtH2QY7AFOiC5nS4RtH2QY7AFOiC3DgIb8"), 777 }, 778 }, 779 }, 780 wantErr: true, 781 errorLen: 1, 782 }, 783 { 784 name: "valid subnetname with character length 80", 785 ammp: &AzureManagedMachinePool{ 786 Spec: AzureManagedMachinePoolSpec{ 787 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 788 SubnetName: ptr.To("3DgIb8EZMkLs0KlyPaTcNxoJU9ufmW6jvXrweqz1hVp5nS4RtH2QY7AFOiC5nS4RtH2QY7AFOiC3DgIb"), 789 }, 790 }, 791 }, 792 wantErr: false, 793 }, 794 { 795 name: "valid subnetname with versioning", 796 ammp: &AzureManagedMachinePool{ 797 Spec: AzureManagedMachinePoolSpec{ 798 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 799 SubnetName: ptr.To("workload-ampt-v0.1.0"), 800 }, 801 }, 802 }, 803 wantErr: false, 804 }, 805 { 806 name: "valid subnetname", 807 ammp: &AzureManagedMachinePool{ 808 Spec: AzureManagedMachinePoolSpec{ 809 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 810 SubnetName: ptr.To("1abc"), 811 }, 812 }, 813 }, 814 wantErr: false, 815 }, 816 { 817 name: "valid subnetname", 818 ammp: &AzureManagedMachinePool{ 819 Spec: AzureManagedMachinePoolSpec{ 820 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 821 SubnetName: ptr.To("1-a-b-c"), 822 }, 823 }, 824 }, 825 wantErr: false, 826 }, 827 { 828 name: "valid subnetname", 829 ammp: &AzureManagedMachinePool{ 830 Spec: AzureManagedMachinePoolSpec{ 831 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 832 SubnetName: ptr.To("my-subnet"), 833 }, 834 }, 835 }, 836 wantErr: false, 837 }, 838 { 839 name: "too few MaxPods", 840 ammp: &AzureManagedMachinePool{ 841 Spec: AzureManagedMachinePoolSpec{ 842 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 843 MaxPods: ptr.To(9), 844 }, 845 }, 846 }, 847 wantErr: true, 848 errorLen: 1, 849 }, 850 { 851 name: "ostype Windows with System mode not allowed", 852 ammp: &AzureManagedMachinePool{ 853 Spec: AzureManagedMachinePoolSpec{ 854 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 855 Mode: "System", 856 OSType: ptr.To(WindowsOS), 857 }, 858 }, 859 }, 860 wantErr: true, 861 errorLen: 1, 862 }, 863 { 864 name: "ostype windows with User mode", 865 ammp: &AzureManagedMachinePool{ 866 Spec: AzureManagedMachinePoolSpec{ 867 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 868 Mode: "User", 869 OSType: ptr.To(WindowsOS), 870 }, 871 }, 872 }, 873 wantErr: false, 874 }, 875 { 876 name: "Windows clusters with 6char or less name", 877 ammp: &AzureManagedMachinePool{ 878 ObjectMeta: metav1.ObjectMeta{ 879 Name: "pool0", 880 }, 881 Spec: AzureManagedMachinePoolSpec{ 882 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 883 Mode: "User", 884 OSType: ptr.To(WindowsOS), 885 }, 886 }, 887 }, 888 wantErr: false, 889 }, 890 { 891 name: "Windows clusters with more than 6char names are not allowed", 892 ammp: &AzureManagedMachinePool{ 893 Spec: AzureManagedMachinePoolSpec{ 894 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 895 Name: ptr.To("pool0-name-too-long"), 896 Mode: "User", 897 OSType: ptr.To(WindowsOS), 898 }, 899 }, 900 }, 901 wantErr: true, 902 errorLen: 1, 903 }, 904 { 905 name: "valid label", 906 ammp: &AzureManagedMachinePool{ 907 Spec: AzureManagedMachinePoolSpec{ 908 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 909 Mode: "User", 910 OSType: ptr.To(LinuxOS), 911 NodeLabels: map[string]string{ 912 "foo": "bar", 913 }, 914 }, 915 }, 916 }, 917 wantErr: false, 918 }, 919 { 920 name: "kubernetes.azure.com label", 921 ammp: &AzureManagedMachinePool{ 922 Spec: AzureManagedMachinePoolSpec{ 923 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 924 Mode: "User", 925 OSType: ptr.To(LinuxOS), 926 NodeLabels: map[string]string{ 927 "kubernetes.azure.com/scalesetpriority": "spot", 928 }, 929 }, 930 }, 931 }, 932 wantErr: true, 933 errorLen: 1, 934 }, 935 { 936 name: "pool with invalid public ip prefix", 937 ammp: &AzureManagedMachinePool{ 938 Spec: AzureManagedMachinePoolSpec{ 939 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 940 EnableNodePublicIP: ptr.To(true), 941 NodePublicIPPrefixID: ptr.To("not a valid resource ID"), 942 }, 943 }, 944 }, 945 wantErr: true, 946 errorLen: 1, 947 }, 948 { 949 name: "pool with public ip prefix cannot omit node public IP", 950 ammp: &AzureManagedMachinePool{ 951 Spec: AzureManagedMachinePoolSpec{ 952 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 953 EnableNodePublicIP: nil, 954 NodePublicIPPrefixID: ptr.To("subscriptions/11111111-2222-aaaa-bbbb-cccccccccccc/resourceGroups/public-ip-test/providers/Microsoft.Network/publicipprefixes/public-ip-prefix"), 955 }, 956 }, 957 }, 958 wantErr: true, 959 errorLen: 1, 960 }, 961 { 962 name: "pool with public ip prefix cannot disable node public IP", 963 ammp: &AzureManagedMachinePool{ 964 Spec: AzureManagedMachinePoolSpec{ 965 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 966 EnableNodePublicIP: ptr.To(false), 967 NodePublicIPPrefixID: ptr.To("subscriptions/11111111-2222-aaaa-bbbb-cccccccccccc/resourceGroups/public-ip-test/providers/Microsoft.Network/publicipprefixes/public-ip-prefix"), 968 }, 969 }, 970 }, 971 wantErr: true, 972 errorLen: 1, 973 }, 974 { 975 name: "pool with public ip prefix with node public IP enabled ok", 976 ammp: &AzureManagedMachinePool{ 977 Spec: AzureManagedMachinePoolSpec{ 978 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 979 EnableNodePublicIP: ptr.To(true), 980 NodePublicIPPrefixID: ptr.To("subscriptions/11111111-2222-aaaa-bbbb-cccccccccccc/resourceGroups/public-ip-test/providers/Microsoft.Network/publicipprefixes/public-ip-prefix"), 981 }, 982 }, 983 }, 984 wantErr: false, 985 }, 986 { 987 name: "pool with public ip prefix with leading slash with node public IP enabled ok", 988 ammp: &AzureManagedMachinePool{ 989 Spec: AzureManagedMachinePoolSpec{ 990 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 991 EnableNodePublicIP: ptr.To(true), 992 NodePublicIPPrefixID: ptr.To("/subscriptions/11111111-2222-aaaa-bbbb-cccccccccccc/resourceGroups/public-ip-test/providers/Microsoft.Network/publicipprefixes/public-ip-prefix"), 993 }, 994 }, 995 }, 996 wantErr: false, 997 }, 998 { 999 name: "pool without public ip prefix with node public IP unset ok", 1000 ammp: &AzureManagedMachinePool{ 1001 Spec: AzureManagedMachinePoolSpec{ 1002 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1003 EnableNodePublicIP: nil, 1004 }, 1005 }, 1006 }, 1007 wantErr: false, 1008 }, 1009 { 1010 name: "pool without public ip prefix with node public IP enabled ok", 1011 ammp: &AzureManagedMachinePool{ 1012 Spec: AzureManagedMachinePoolSpec{ 1013 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1014 EnableNodePublicIP: ptr.To(true), 1015 }, 1016 }, 1017 }, 1018 wantErr: false, 1019 }, 1020 { 1021 name: "pool without public ip prefix with node public IP disabled ok", 1022 ammp: &AzureManagedMachinePool{ 1023 Spec: AzureManagedMachinePoolSpec{ 1024 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1025 EnableNodePublicIP: ptr.To(false), 1026 }, 1027 }, 1028 }, 1029 wantErr: false, 1030 }, 1031 { 1032 name: "KubeletConfig CPUCfsQuotaPeriod needs 'ms' suffix", 1033 ammp: &AzureManagedMachinePool{ 1034 Spec: AzureManagedMachinePoolSpec{ 1035 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1036 KubeletConfig: &KubeletConfig{ 1037 CPUCfsQuotaPeriod: ptr.To("100"), 1038 }, 1039 }, 1040 }, 1041 }, 1042 wantErr: true, 1043 errorLen: 1, 1044 }, 1045 { 1046 name: "KubeletConfig CPUCfsQuotaPeriod has valid 'ms' suffix", 1047 ammp: &AzureManagedMachinePool{ 1048 Spec: AzureManagedMachinePoolSpec{ 1049 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1050 KubeletConfig: &KubeletConfig{ 1051 CPUCfsQuotaPeriod: ptr.To("100ms"), 1052 }, 1053 }, 1054 }, 1055 }, 1056 wantErr: false, 1057 }, 1058 { 1059 name: "KubeletConfig ImageGcLowThreshold can't be more than ImageGcHighThreshold", 1060 ammp: &AzureManagedMachinePool{ 1061 Spec: AzureManagedMachinePoolSpec{ 1062 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1063 KubeletConfig: &KubeletConfig{ 1064 ImageGcLowThreshold: ptr.To(100), 1065 ImageGcHighThreshold: ptr.To(99), 1066 }, 1067 }, 1068 }, 1069 }, 1070 wantErr: true, 1071 errorLen: 1, 1072 }, 1073 { 1074 name: "KubeletConfig ImageGcLowThreshold is lower than ImageGcHighThreshold", 1075 ammp: &AzureManagedMachinePool{ 1076 Spec: AzureManagedMachinePoolSpec{ 1077 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1078 KubeletConfig: &KubeletConfig{ 1079 ImageGcLowThreshold: ptr.To(99), 1080 ImageGcHighThreshold: ptr.To(100), 1081 }, 1082 }, 1083 }, 1084 }, 1085 wantErr: false, 1086 }, 1087 { 1088 name: "valid KubeletConfig AllowedUnsafeSysctls values", 1089 ammp: &AzureManagedMachinePool{ 1090 Spec: AzureManagedMachinePoolSpec{ 1091 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1092 KubeletConfig: &KubeletConfig{ 1093 AllowedUnsafeSysctls: []string{ 1094 "kernel.shm*", 1095 "kernel.msg*", 1096 "kernel.sem", 1097 "fs.mqueue.*", 1098 "net.*", 1099 }, 1100 }, 1101 }, 1102 }, 1103 }, 1104 wantErr: false, 1105 }, 1106 { 1107 name: "more valid KubeletConfig AllowedUnsafeSysctls values", 1108 ammp: &AzureManagedMachinePool{ 1109 Spec: AzureManagedMachinePoolSpec{ 1110 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1111 KubeletConfig: &KubeletConfig{ 1112 AllowedUnsafeSysctls: []string{ 1113 "kernel.shm.something", 1114 "kernel.msg.foo.bar", 1115 "kernel.sem", 1116 "fs.mqueue.baz", 1117 "net.my.configuration.path", 1118 }, 1119 }, 1120 }, 1121 }, 1122 }, 1123 wantErr: false, 1124 }, 1125 { 1126 name: "an invalid KubeletConfig AllowedUnsafeSysctls value in a set", 1127 ammp: &AzureManagedMachinePool{ 1128 Spec: AzureManagedMachinePoolSpec{ 1129 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1130 KubeletConfig: &KubeletConfig{ 1131 AllowedUnsafeSysctls: []string{ 1132 "kernel.shm.something", 1133 "kernel.msg.foo.bar", 1134 "kernel.sem", 1135 "fs.mqueue.baz", 1136 "net.my.configuration.path", 1137 "kernel.not.allowed", 1138 }, 1139 }, 1140 }, 1141 }, 1142 }, 1143 wantErr: true, 1144 errorLen: 1, 1145 }, 1146 { 1147 name: "validLinuxOSConfig Sysctls NetIpv4IpLocalPortRange.First is less than NetIpv4IpLocalPortRange.Last", 1148 ammp: &AzureManagedMachinePool{ 1149 Spec: AzureManagedMachinePoolSpec{ 1150 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1151 LinuxOSConfig: &LinuxOSConfig{ 1152 Sysctls: &SysctlConfig{ 1153 NetIpv4IPLocalPortRange: ptr.To("2000 33000"), 1154 }, 1155 }, 1156 }, 1157 }, 1158 }, 1159 wantErr: false, 1160 }, 1161 { 1162 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.First string is ill-formed", 1163 ammp: &AzureManagedMachinePool{ 1164 Spec: AzureManagedMachinePoolSpec{ 1165 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1166 LinuxOSConfig: &LinuxOSConfig{ 1167 Sysctls: &SysctlConfig{ 1168 NetIpv4IPLocalPortRange: ptr.To("wrong 33000"), 1169 }, 1170 }, 1171 }, 1172 }, 1173 }, 1174 wantErr: true, 1175 errorLen: 1, 1176 }, 1177 { 1178 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.Last string is ill-formed", 1179 ammp: &AzureManagedMachinePool{ 1180 Spec: AzureManagedMachinePoolSpec{ 1181 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1182 LinuxOSConfig: &LinuxOSConfig{ 1183 Sysctls: &SysctlConfig{ 1184 NetIpv4IPLocalPortRange: ptr.To("2000 wrong"), 1185 }, 1186 }, 1187 }, 1188 }, 1189 }, 1190 wantErr: true, 1191 errorLen: 1, 1192 }, 1193 { 1194 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.First less than allowed value", 1195 ammp: &AzureManagedMachinePool{ 1196 Spec: AzureManagedMachinePoolSpec{ 1197 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1198 LinuxOSConfig: &LinuxOSConfig{ 1199 Sysctls: &SysctlConfig{ 1200 NetIpv4IPLocalPortRange: ptr.To("1020 32999"), 1201 }, 1202 }, 1203 }, 1204 }, 1205 }, 1206 wantErr: true, 1207 errorLen: 1, 1208 }, 1209 { 1210 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.Last less than allowed value", 1211 ammp: &AzureManagedMachinePool{ 1212 Spec: AzureManagedMachinePoolSpec{ 1213 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1214 LinuxOSConfig: &LinuxOSConfig{ 1215 Sysctls: &SysctlConfig{ 1216 NetIpv4IPLocalPortRange: ptr.To("1024 32000"), 1217 }, 1218 }, 1219 }, 1220 }, 1221 }, 1222 wantErr: true, 1223 errorLen: 1, 1224 }, 1225 { 1226 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.First is greater than NetIpv4IpLocalPortRange.Last", 1227 ammp: &AzureManagedMachinePool{ 1228 Spec: AzureManagedMachinePoolSpec{ 1229 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1230 LinuxOSConfig: &LinuxOSConfig{ 1231 Sysctls: &SysctlConfig{ 1232 NetIpv4IPLocalPortRange: ptr.To("33000 32999"), 1233 }, 1234 }, 1235 }, 1236 }, 1237 }, 1238 wantErr: true, 1239 errorLen: 1, 1240 }, 1241 { 1242 name: "valid LinuxOSConfig Sysctls is set by disabling FailSwapOn", 1243 ammp: &AzureManagedMachinePool{ 1244 Spec: AzureManagedMachinePoolSpec{ 1245 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1246 KubeletConfig: &KubeletConfig{ 1247 FailSwapOn: ptr.To(false), 1248 }, 1249 LinuxOSConfig: &LinuxOSConfig{ 1250 SwapFileSizeMB: ptr.To(1500), 1251 }, 1252 }, 1253 }, 1254 }, 1255 wantErr: false, 1256 }, 1257 { 1258 name: "an invalid LinuxOSConfig Sysctls is set with FailSwapOn set to true", 1259 ammp: &AzureManagedMachinePool{ 1260 Spec: AzureManagedMachinePoolSpec{ 1261 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1262 KubeletConfig: &KubeletConfig{ 1263 FailSwapOn: ptr.To(true), 1264 }, 1265 LinuxOSConfig: &LinuxOSConfig{ 1266 SwapFileSizeMB: ptr.To(1500), 1267 }, 1268 }, 1269 }, 1270 }, 1271 wantErr: true, 1272 errorLen: 1, 1273 }, 1274 { 1275 name: "an invalid LinuxOSConfig Sysctls is set without disabling FailSwapOn", 1276 ammp: &AzureManagedMachinePool{ 1277 Spec: AzureManagedMachinePoolSpec{ 1278 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1279 LinuxOSConfig: &LinuxOSConfig{ 1280 SwapFileSizeMB: ptr.To(1500), 1281 }, 1282 }, 1283 }, 1284 }, 1285 wantErr: true, 1286 errorLen: 1, 1287 }, 1288 } 1289 1290 var client client.Client 1291 for _, tc := range tests { 1292 t.Run(tc.name, func(t *testing.T) { 1293 g := NewWithT(t) 1294 mw := &azureManagedMachinePoolWebhook{ 1295 Client: client, 1296 } 1297 _, err := mw.ValidateCreate(context.Background(), tc.ammp) 1298 if tc.wantErr { 1299 g.Expect(err).To(HaveOccurred()) 1300 g.Expect(err).To(HaveLen(tc.errorLen)) 1301 } else { 1302 g.Expect(err).NotTo(HaveOccurred()) 1303 } 1304 }) 1305 } 1306 } 1307 1308 func TestAzureManagedMachinePool_ValidateCreateFailure(t *testing.T) { 1309 tests := []struct { 1310 name string 1311 ammp *AzureManagedMachinePool 1312 featureGateEnabled *bool 1313 expectError bool 1314 }{ 1315 { 1316 name: "feature gate explicitly disabled", 1317 ammp: getKnownValidAzureManagedMachinePool(), 1318 featureGateEnabled: ptr.To(false), 1319 expectError: true, 1320 }, 1321 { 1322 name: "feature gate implicitly enabled", 1323 ammp: getKnownValidAzureManagedMachinePool(), 1324 featureGateEnabled: nil, 1325 expectError: false, 1326 }, 1327 } 1328 for _, tc := range tests { 1329 t.Run(tc.name, func(t *testing.T) { 1330 if tc.featureGateEnabled != nil { 1331 defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, capifeature.MachinePool, *tc.featureGateEnabled)() 1332 } 1333 g := NewWithT(t) 1334 mw := &azureManagedMachinePoolWebhook{} 1335 _, err := mw.ValidateCreate(context.Background(), tc.ammp) 1336 if tc.expectError { 1337 g.Expect(err).To(HaveOccurred()) 1338 } else { 1339 g.Expect(err).NotTo(HaveOccurred()) 1340 } 1341 }) 1342 } 1343 } 1344 1345 func TestAzureManagedMachinePool_validateLastSystemNodePool(t *testing.T) { 1346 deletionTime := metav1.Now() 1347 finalizers := []string{"test"} 1348 systemMachinePool := getManagedMachinePoolWithSystemMode() 1349 systemMachinePoolWithDeletionAnnotation := getAzureManagedMachinePoolWithChanges( 1350 // Add the DeleteForMoveAnnotation annotation to the AMMP 1351 func(azureManagedMachinePool *AzureManagedMachinePool) { 1352 azureManagedMachinePool.Annotations = map[string]string{ 1353 clusterctlv1alpha3.DeleteForMoveAnnotation: "true", 1354 } 1355 }, 1356 ) 1357 tests := []struct { 1358 name string 1359 ammp *AzureManagedMachinePool 1360 cluster *clusterv1.Cluster 1361 wantErr bool 1362 }{ 1363 { 1364 // AzureManagedMachinePool will be deleted since AMMP has DeleteForMoveAnnotation annotation 1365 // Note that Owner Cluster's deletion timestamp is nil and Owner cluster being paused does not matter anymore. 1366 name: "AzureManagedMachinePool (AMMP) should be deleted if this AMMP has the annotation 'cluster.x-k8s.io/move-to-delete' with the owner cluster being paused and 'No' deletion timestamp", 1367 ammp: systemMachinePoolWithDeletionAnnotation, 1368 cluster: &clusterv1.Cluster{ 1369 ObjectMeta: metav1.ObjectMeta{ 1370 Name: systemMachinePool.GetLabels()[clusterv1.ClusterNameLabel], 1371 Namespace: systemMachinePool.Namespace, 1372 Finalizers: finalizers, 1373 }, 1374 }, 1375 wantErr: false, 1376 }, 1377 { 1378 // AzureManagedMachinePool will be deleted since Owner Cluster has been marked for deletion 1379 name: "AzureManagedMachinePool should be deleted since the Cluster is paused with a deletion timestamp", 1380 ammp: systemMachinePool, 1381 cluster: &clusterv1.Cluster{ 1382 ObjectMeta: metav1.ObjectMeta{ 1383 Name: systemMachinePool.GetLabels()[clusterv1.ClusterNameLabel], 1384 Namespace: systemMachinePool.Namespace, 1385 DeletionTimestamp: &deletionTime, 1386 Finalizers: finalizers, 1387 }, 1388 }, 1389 wantErr: false, 1390 }, 1391 { 1392 name: "AzureManagedMachinePool should not be deleted without a deletion timestamp on Owner Cluster and having one system pool node(invalid delete)", 1393 ammp: systemMachinePool, 1394 cluster: &clusterv1.Cluster{ 1395 ObjectMeta: metav1.ObjectMeta{ 1396 Name: systemMachinePool.GetLabels()[clusterv1.ClusterNameLabel], 1397 Namespace: systemMachinePool.Namespace, 1398 }, 1399 }, 1400 wantErr: true, 1401 }, 1402 { 1403 name: "AzureManagedMachinePool should be deleted when Cluster is set with a deletion timestamp having one system pool node(valid delete)", 1404 ammp: systemMachinePool, 1405 cluster: &clusterv1.Cluster{ 1406 ObjectMeta: metav1.ObjectMeta{ 1407 Name: systemMachinePool.GetLabels()[clusterv1.ClusterNameLabel], 1408 Namespace: systemMachinePool.Namespace, 1409 DeletionTimestamp: &deletionTime, 1410 Finalizers: finalizers, 1411 }, 1412 }, 1413 wantErr: false, 1414 }, 1415 } 1416 1417 for _, tc := range tests { 1418 t.Run(tc.name, func(t *testing.T) { 1419 g := NewWithT(t) 1420 scheme := runtime.NewScheme() 1421 _ = AddToScheme(scheme) 1422 _ = clusterv1.AddToScheme(scheme) 1423 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tc.cluster, tc.ammp).Build() 1424 err := validateLastSystemNodePool(fakeClient, tc.ammp.Spec.NodeLabels, tc.ammp.Namespace, tc.ammp.Annotations) 1425 if tc.wantErr { 1426 g.Expect(err).To(HaveOccurred()) 1427 } else { 1428 g.Expect(err).NotTo(HaveOccurred()) 1429 } 1430 }) 1431 } 1432 } 1433 1434 func getKnownValidAzureManagedMachinePool() *AzureManagedMachinePool { 1435 return &AzureManagedMachinePool{ 1436 Spec: AzureManagedMachinePoolSpec{ 1437 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1438 MaxPods: ptr.To(30), 1439 OsDiskType: ptr.To(string(asocontainerservicev1.OSDiskType_Ephemeral)), 1440 }, 1441 }, 1442 } 1443 } 1444 1445 func getManagedMachinePoolWithSystemMode() *AzureManagedMachinePool { 1446 return &AzureManagedMachinePool{ 1447 ObjectMeta: metav1.ObjectMeta{ 1448 Namespace: metav1.NamespaceDefault, 1449 Labels: map[string]string{ 1450 clusterv1.ClusterNameLabel: "test-cluster", 1451 LabelAgentPoolMode: string(NodePoolModeSystem), 1452 }, 1453 }, 1454 Spec: AzureManagedMachinePoolSpec{ 1455 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1456 NodeLabels: map[string]string{ 1457 clusterv1.ClusterNameLabel: "test-cluster", 1458 }, 1459 }, 1460 }, 1461 } 1462 } 1463 1464 func getAzureManagedMachinePoolWithChanges(changes ...func(*AzureManagedMachinePool)) *AzureManagedMachinePool { 1465 ammp := getManagedMachinePoolWithSystemMode().DeepCopy() 1466 for _, change := range changes { 1467 change(ammp) 1468 } 1469 return ammp 1470 }