sigs.k8s.io/cluster-api-provider-azure@v1.14.3/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 // NOTE: AzureManagedMachinePool is behind AKS feature gate flag; the webhook 658 // must prevent creating new objects in case the feature flag is disabled. 659 defer utilfeature.SetFeatureGateDuringTest(t, feature.Gates, capifeature.MachinePool, true)() 660 tests := []struct { 661 name string 662 ammp *AzureManagedMachinePool 663 wantErr bool 664 errorLen int 665 }{ 666 { 667 name: "valid", 668 ammp: getKnownValidAzureManagedMachinePool(), 669 wantErr: false, 670 }, 671 { 672 name: "another valid permutation", 673 ammp: &AzureManagedMachinePool{ 674 Spec: AzureManagedMachinePoolSpec{ 675 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 676 MaxPods: ptr.To(249), 677 OsDiskType: ptr.To(string(asocontainerservicev1.OSDiskType_Managed)), 678 }, 679 }, 680 }, 681 wantErr: false, 682 }, 683 { 684 name: "valid - optional configuration not present", 685 ammp: &AzureManagedMachinePool{ 686 Spec: AzureManagedMachinePoolSpec{}, 687 }, 688 wantErr: false, 689 }, 690 { 691 name: "too many MaxPods", 692 ammp: &AzureManagedMachinePool{ 693 Spec: AzureManagedMachinePoolSpec{ 694 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 695 MaxPods: ptr.To(251), 696 }, 697 }, 698 }, 699 wantErr: true, 700 errorLen: 1, 701 }, 702 { 703 name: "invalid subnetname", 704 ammp: &AzureManagedMachinePool{ 705 Spec: AzureManagedMachinePoolSpec{ 706 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 707 SubnetName: ptr.To("1+subnet"), 708 }, 709 }, 710 }, 711 wantErr: true, 712 errorLen: 1, 713 }, 714 { 715 name: "invalid subnetname", 716 ammp: &AzureManagedMachinePool{ 717 Spec: AzureManagedMachinePoolSpec{ 718 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 719 SubnetName: ptr.To("1"), 720 }, 721 }, 722 }, 723 wantErr: true, 724 errorLen: 1, 725 }, 726 { 727 name: "invalid subnetname", 728 ammp: &AzureManagedMachinePool{ 729 Spec: AzureManagedMachinePoolSpec{ 730 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 731 SubnetName: ptr.To("-a_b-c"), 732 }, 733 }, 734 }, 735 wantErr: true, 736 errorLen: 1, 737 }, 738 { 739 name: "invalid subnetname with versioning", 740 ammp: &AzureManagedMachinePool{ 741 Spec: AzureManagedMachinePoolSpec{ 742 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 743 SubnetName: ptr.To("workload-ampt-v0.1.0."), 744 }, 745 }, 746 }, 747 wantErr: true, 748 errorLen: 1, 749 }, 750 { 751 name: "invalid subnetname", 752 ammp: &AzureManagedMachinePool{ 753 Spec: AzureManagedMachinePoolSpec{ 754 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 755 SubnetName: ptr.To("-_-_"), 756 }, 757 }, 758 }, 759 wantErr: true, 760 errorLen: 1, 761 }, 762 { 763 name: "invalid subnetname", 764 ammp: &AzureManagedMachinePool{ 765 Spec: AzureManagedMachinePoolSpec{ 766 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 767 SubnetName: ptr.To("abc@#$"), 768 }, 769 }, 770 }, 771 wantErr: true, 772 errorLen: 1, 773 }, 774 { 775 name: "invalid subnetname with character length 81", 776 ammp: &AzureManagedMachinePool{ 777 Spec: AzureManagedMachinePoolSpec{ 778 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 779 SubnetName: ptr.To("3DgIb8EZMkLs0KlyPaTcNxoJU9ufmW6jvXrweqz1hVp5nS4RtH2QY7AFOiC5nS4RtH2QY7AFOiC3DgIb8"), 780 }, 781 }, 782 }, 783 wantErr: true, 784 errorLen: 1, 785 }, 786 { 787 name: "valid subnetname with character length 80", 788 ammp: &AzureManagedMachinePool{ 789 Spec: AzureManagedMachinePoolSpec{ 790 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 791 SubnetName: ptr.To("3DgIb8EZMkLs0KlyPaTcNxoJU9ufmW6jvXrweqz1hVp5nS4RtH2QY7AFOiC5nS4RtH2QY7AFOiC3DgIb"), 792 }, 793 }, 794 }, 795 wantErr: false, 796 }, 797 { 798 name: "valid subnetname with versioning", 799 ammp: &AzureManagedMachinePool{ 800 Spec: AzureManagedMachinePoolSpec{ 801 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 802 SubnetName: ptr.To("workload-ampt-v0.1.0"), 803 }, 804 }, 805 }, 806 wantErr: false, 807 }, 808 { 809 name: "valid subnetname", 810 ammp: &AzureManagedMachinePool{ 811 Spec: AzureManagedMachinePoolSpec{ 812 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 813 SubnetName: ptr.To("1abc"), 814 }, 815 }, 816 }, 817 wantErr: false, 818 }, 819 { 820 name: "valid subnetname", 821 ammp: &AzureManagedMachinePool{ 822 Spec: AzureManagedMachinePoolSpec{ 823 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 824 SubnetName: ptr.To("1-a-b-c"), 825 }, 826 }, 827 }, 828 wantErr: false, 829 }, 830 { 831 name: "valid subnetname", 832 ammp: &AzureManagedMachinePool{ 833 Spec: AzureManagedMachinePoolSpec{ 834 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 835 SubnetName: ptr.To("my-subnet"), 836 }, 837 }, 838 }, 839 wantErr: false, 840 }, 841 { 842 name: "too few MaxPods", 843 ammp: &AzureManagedMachinePool{ 844 Spec: AzureManagedMachinePoolSpec{ 845 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 846 MaxPods: ptr.To(9), 847 }, 848 }, 849 }, 850 wantErr: true, 851 errorLen: 1, 852 }, 853 { 854 name: "ostype Windows with System mode not allowed", 855 ammp: &AzureManagedMachinePool{ 856 Spec: AzureManagedMachinePoolSpec{ 857 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 858 Mode: "System", 859 OSType: ptr.To(WindowsOS), 860 }, 861 }, 862 }, 863 wantErr: true, 864 errorLen: 1, 865 }, 866 { 867 name: "ostype windows with User mode", 868 ammp: &AzureManagedMachinePool{ 869 Spec: AzureManagedMachinePoolSpec{ 870 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 871 Mode: "User", 872 OSType: ptr.To(WindowsOS), 873 }, 874 }, 875 }, 876 wantErr: false, 877 }, 878 { 879 name: "Windows clusters with 6char or less name", 880 ammp: &AzureManagedMachinePool{ 881 ObjectMeta: metav1.ObjectMeta{ 882 Name: "pool0", 883 }, 884 Spec: AzureManagedMachinePoolSpec{ 885 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 886 Mode: "User", 887 OSType: ptr.To(WindowsOS), 888 }, 889 }, 890 }, 891 wantErr: false, 892 }, 893 { 894 name: "Windows clusters with more than 6char names are not allowed", 895 ammp: &AzureManagedMachinePool{ 896 Spec: AzureManagedMachinePoolSpec{ 897 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 898 Name: ptr.To("pool0-name-too-long"), 899 Mode: "User", 900 OSType: ptr.To(WindowsOS), 901 }, 902 }, 903 }, 904 wantErr: true, 905 errorLen: 1, 906 }, 907 { 908 name: "valid label", 909 ammp: &AzureManagedMachinePool{ 910 Spec: AzureManagedMachinePoolSpec{ 911 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 912 Mode: "User", 913 OSType: ptr.To(LinuxOS), 914 NodeLabels: map[string]string{ 915 "foo": "bar", 916 }, 917 }, 918 }, 919 }, 920 wantErr: false, 921 }, 922 { 923 name: "kubernetes.azure.com label", 924 ammp: &AzureManagedMachinePool{ 925 Spec: AzureManagedMachinePoolSpec{ 926 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 927 Mode: "User", 928 OSType: ptr.To(LinuxOS), 929 NodeLabels: map[string]string{ 930 "kubernetes.azure.com/scalesetpriority": "spot", 931 }, 932 }, 933 }, 934 }, 935 wantErr: true, 936 errorLen: 1, 937 }, 938 { 939 name: "pool with invalid public ip prefix", 940 ammp: &AzureManagedMachinePool{ 941 Spec: AzureManagedMachinePoolSpec{ 942 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 943 EnableNodePublicIP: ptr.To(true), 944 NodePublicIPPrefixID: ptr.To("not a valid resource ID"), 945 }, 946 }, 947 }, 948 wantErr: true, 949 errorLen: 1, 950 }, 951 { 952 name: "pool with public ip prefix cannot omit node public IP", 953 ammp: &AzureManagedMachinePool{ 954 Spec: AzureManagedMachinePoolSpec{ 955 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 956 EnableNodePublicIP: nil, 957 NodePublicIPPrefixID: ptr.To("subscriptions/11111111-2222-aaaa-bbbb-cccccccccccc/resourceGroups/public-ip-test/providers/Microsoft.Network/publicipprefixes/public-ip-prefix"), 958 }, 959 }, 960 }, 961 wantErr: true, 962 errorLen: 1, 963 }, 964 { 965 name: "pool with public ip prefix cannot disable node public IP", 966 ammp: &AzureManagedMachinePool{ 967 Spec: AzureManagedMachinePoolSpec{ 968 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 969 EnableNodePublicIP: ptr.To(false), 970 NodePublicIPPrefixID: ptr.To("subscriptions/11111111-2222-aaaa-bbbb-cccccccccccc/resourceGroups/public-ip-test/providers/Microsoft.Network/publicipprefixes/public-ip-prefix"), 971 }, 972 }, 973 }, 974 wantErr: true, 975 errorLen: 1, 976 }, 977 { 978 name: "pool with public ip prefix with node public IP enabled ok", 979 ammp: &AzureManagedMachinePool{ 980 Spec: AzureManagedMachinePoolSpec{ 981 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 982 EnableNodePublicIP: ptr.To(true), 983 NodePublicIPPrefixID: ptr.To("subscriptions/11111111-2222-aaaa-bbbb-cccccccccccc/resourceGroups/public-ip-test/providers/Microsoft.Network/publicipprefixes/public-ip-prefix"), 984 }, 985 }, 986 }, 987 wantErr: false, 988 }, 989 { 990 name: "pool with public ip prefix with leading slash with node public IP enabled ok", 991 ammp: &AzureManagedMachinePool{ 992 Spec: AzureManagedMachinePoolSpec{ 993 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 994 EnableNodePublicIP: ptr.To(true), 995 NodePublicIPPrefixID: ptr.To("/subscriptions/11111111-2222-aaaa-bbbb-cccccccccccc/resourceGroups/public-ip-test/providers/Microsoft.Network/publicipprefixes/public-ip-prefix"), 996 }, 997 }, 998 }, 999 wantErr: false, 1000 }, 1001 { 1002 name: "pool without public ip prefix with node public IP unset ok", 1003 ammp: &AzureManagedMachinePool{ 1004 Spec: AzureManagedMachinePoolSpec{ 1005 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1006 EnableNodePublicIP: nil, 1007 }, 1008 }, 1009 }, 1010 wantErr: false, 1011 }, 1012 { 1013 name: "pool without public ip prefix with node public IP enabled ok", 1014 ammp: &AzureManagedMachinePool{ 1015 Spec: AzureManagedMachinePoolSpec{ 1016 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1017 EnableNodePublicIP: ptr.To(true), 1018 }, 1019 }, 1020 }, 1021 wantErr: false, 1022 }, 1023 { 1024 name: "pool without public ip prefix with node public IP disabled ok", 1025 ammp: &AzureManagedMachinePool{ 1026 Spec: AzureManagedMachinePoolSpec{ 1027 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1028 EnableNodePublicIP: ptr.To(false), 1029 }, 1030 }, 1031 }, 1032 wantErr: false, 1033 }, 1034 { 1035 name: "KubeletConfig CPUCfsQuotaPeriod needs 'ms' suffix", 1036 ammp: &AzureManagedMachinePool{ 1037 Spec: AzureManagedMachinePoolSpec{ 1038 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1039 KubeletConfig: &KubeletConfig{ 1040 CPUCfsQuotaPeriod: ptr.To("100"), 1041 }, 1042 }, 1043 }, 1044 }, 1045 wantErr: true, 1046 errorLen: 1, 1047 }, 1048 { 1049 name: "KubeletConfig CPUCfsQuotaPeriod has valid 'ms' suffix", 1050 ammp: &AzureManagedMachinePool{ 1051 Spec: AzureManagedMachinePoolSpec{ 1052 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1053 KubeletConfig: &KubeletConfig{ 1054 CPUCfsQuotaPeriod: ptr.To("100ms"), 1055 }, 1056 }, 1057 }, 1058 }, 1059 wantErr: false, 1060 }, 1061 { 1062 name: "KubeletConfig ImageGcLowThreshold can't be more than ImageGcHighThreshold", 1063 ammp: &AzureManagedMachinePool{ 1064 Spec: AzureManagedMachinePoolSpec{ 1065 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1066 KubeletConfig: &KubeletConfig{ 1067 ImageGcLowThreshold: ptr.To(100), 1068 ImageGcHighThreshold: ptr.To(99), 1069 }, 1070 }, 1071 }, 1072 }, 1073 wantErr: true, 1074 errorLen: 1, 1075 }, 1076 { 1077 name: "KubeletConfig ImageGcLowThreshold is lower than ImageGcHighThreshold", 1078 ammp: &AzureManagedMachinePool{ 1079 Spec: AzureManagedMachinePoolSpec{ 1080 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1081 KubeletConfig: &KubeletConfig{ 1082 ImageGcLowThreshold: ptr.To(99), 1083 ImageGcHighThreshold: ptr.To(100), 1084 }, 1085 }, 1086 }, 1087 }, 1088 wantErr: false, 1089 }, 1090 { 1091 name: "valid KubeletConfig AllowedUnsafeSysctls values", 1092 ammp: &AzureManagedMachinePool{ 1093 Spec: AzureManagedMachinePoolSpec{ 1094 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1095 KubeletConfig: &KubeletConfig{ 1096 AllowedUnsafeSysctls: []string{ 1097 "kernel.shm*", 1098 "kernel.msg*", 1099 "kernel.sem", 1100 "fs.mqueue.*", 1101 "net.*", 1102 }, 1103 }, 1104 }, 1105 }, 1106 }, 1107 wantErr: false, 1108 }, 1109 { 1110 name: "more valid KubeletConfig AllowedUnsafeSysctls values", 1111 ammp: &AzureManagedMachinePool{ 1112 Spec: AzureManagedMachinePoolSpec{ 1113 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1114 KubeletConfig: &KubeletConfig{ 1115 AllowedUnsafeSysctls: []string{ 1116 "kernel.shm.something", 1117 "kernel.msg.foo.bar", 1118 "kernel.sem", 1119 "fs.mqueue.baz", 1120 "net.my.configuration.path", 1121 }, 1122 }, 1123 }, 1124 }, 1125 }, 1126 wantErr: false, 1127 }, 1128 { 1129 name: "an invalid KubeletConfig AllowedUnsafeSysctls value in a set", 1130 ammp: &AzureManagedMachinePool{ 1131 Spec: AzureManagedMachinePoolSpec{ 1132 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1133 KubeletConfig: &KubeletConfig{ 1134 AllowedUnsafeSysctls: []string{ 1135 "kernel.shm.something", 1136 "kernel.msg.foo.bar", 1137 "kernel.sem", 1138 "fs.mqueue.baz", 1139 "net.my.configuration.path", 1140 "kernel.not.allowed", 1141 }, 1142 }, 1143 }, 1144 }, 1145 }, 1146 wantErr: true, 1147 errorLen: 1, 1148 }, 1149 { 1150 name: "validLinuxOSConfig Sysctls NetIpv4IpLocalPortRange.First is less than NetIpv4IpLocalPortRange.Last", 1151 ammp: &AzureManagedMachinePool{ 1152 Spec: AzureManagedMachinePoolSpec{ 1153 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1154 LinuxOSConfig: &LinuxOSConfig{ 1155 Sysctls: &SysctlConfig{ 1156 NetIpv4IPLocalPortRange: ptr.To("2000 33000"), 1157 }, 1158 }, 1159 }, 1160 }, 1161 }, 1162 wantErr: false, 1163 }, 1164 { 1165 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.First string is ill-formed", 1166 ammp: &AzureManagedMachinePool{ 1167 Spec: AzureManagedMachinePoolSpec{ 1168 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1169 LinuxOSConfig: &LinuxOSConfig{ 1170 Sysctls: &SysctlConfig{ 1171 NetIpv4IPLocalPortRange: ptr.To("wrong 33000"), 1172 }, 1173 }, 1174 }, 1175 }, 1176 }, 1177 wantErr: true, 1178 errorLen: 1, 1179 }, 1180 { 1181 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.Last string is ill-formed", 1182 ammp: &AzureManagedMachinePool{ 1183 Spec: AzureManagedMachinePoolSpec{ 1184 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1185 LinuxOSConfig: &LinuxOSConfig{ 1186 Sysctls: &SysctlConfig{ 1187 NetIpv4IPLocalPortRange: ptr.To("2000 wrong"), 1188 }, 1189 }, 1190 }, 1191 }, 1192 }, 1193 wantErr: true, 1194 errorLen: 1, 1195 }, 1196 { 1197 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.First less than allowed value", 1198 ammp: &AzureManagedMachinePool{ 1199 Spec: AzureManagedMachinePoolSpec{ 1200 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1201 LinuxOSConfig: &LinuxOSConfig{ 1202 Sysctls: &SysctlConfig{ 1203 NetIpv4IPLocalPortRange: ptr.To("1020 32999"), 1204 }, 1205 }, 1206 }, 1207 }, 1208 }, 1209 wantErr: true, 1210 errorLen: 1, 1211 }, 1212 { 1213 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.Last less than allowed value", 1214 ammp: &AzureManagedMachinePool{ 1215 Spec: AzureManagedMachinePoolSpec{ 1216 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1217 LinuxOSConfig: &LinuxOSConfig{ 1218 Sysctls: &SysctlConfig{ 1219 NetIpv4IPLocalPortRange: ptr.To("1024 32000"), 1220 }, 1221 }, 1222 }, 1223 }, 1224 }, 1225 wantErr: true, 1226 errorLen: 1, 1227 }, 1228 { 1229 name: "an invalid LinuxOSConfig Sysctls NetIpv4IpLocalPortRange.First is greater than NetIpv4IpLocalPortRange.Last", 1230 ammp: &AzureManagedMachinePool{ 1231 Spec: AzureManagedMachinePoolSpec{ 1232 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1233 LinuxOSConfig: &LinuxOSConfig{ 1234 Sysctls: &SysctlConfig{ 1235 NetIpv4IPLocalPortRange: ptr.To("33000 32999"), 1236 }, 1237 }, 1238 }, 1239 }, 1240 }, 1241 wantErr: true, 1242 errorLen: 1, 1243 }, 1244 { 1245 name: "valid LinuxOSConfig Sysctls is set by disabling FailSwapOn", 1246 ammp: &AzureManagedMachinePool{ 1247 Spec: AzureManagedMachinePoolSpec{ 1248 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1249 KubeletConfig: &KubeletConfig{ 1250 FailSwapOn: ptr.To(false), 1251 }, 1252 LinuxOSConfig: &LinuxOSConfig{ 1253 SwapFileSizeMB: ptr.To(1500), 1254 }, 1255 }, 1256 }, 1257 }, 1258 wantErr: false, 1259 }, 1260 { 1261 name: "an invalid LinuxOSConfig Sysctls is set with FailSwapOn set to true", 1262 ammp: &AzureManagedMachinePool{ 1263 Spec: AzureManagedMachinePoolSpec{ 1264 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1265 KubeletConfig: &KubeletConfig{ 1266 FailSwapOn: ptr.To(true), 1267 }, 1268 LinuxOSConfig: &LinuxOSConfig{ 1269 SwapFileSizeMB: ptr.To(1500), 1270 }, 1271 }, 1272 }, 1273 }, 1274 wantErr: true, 1275 errorLen: 1, 1276 }, 1277 { 1278 name: "an invalid LinuxOSConfig Sysctls is set without disabling FailSwapOn", 1279 ammp: &AzureManagedMachinePool{ 1280 Spec: AzureManagedMachinePoolSpec{ 1281 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1282 LinuxOSConfig: &LinuxOSConfig{ 1283 SwapFileSizeMB: ptr.To(1500), 1284 }, 1285 }, 1286 }, 1287 }, 1288 wantErr: true, 1289 errorLen: 1, 1290 }, 1291 } 1292 1293 var client client.Client 1294 for _, tc := range tests { 1295 t.Run(tc.name, func(t *testing.T) { 1296 g := NewWithT(t) 1297 mw := &azureManagedMachinePoolWebhook{ 1298 Client: client, 1299 } 1300 _, err := mw.ValidateCreate(context.Background(), tc.ammp) 1301 if tc.wantErr { 1302 g.Expect(err).To(HaveOccurred()) 1303 g.Expect(err).To(HaveLen(tc.errorLen)) 1304 } else { 1305 g.Expect(err).NotTo(HaveOccurred()) 1306 } 1307 }) 1308 } 1309 } 1310 1311 func TestAzureManagedMachinePool_ValidateCreateFailure(t *testing.T) { 1312 tests := []struct { 1313 name string 1314 ammp *AzureManagedMachinePool 1315 deferFunc func() 1316 }{ 1317 { 1318 name: "feature gate explicitly disabled", 1319 ammp: getKnownValidAzureManagedMachinePool(), 1320 deferFunc: utilfeature.SetFeatureGateDuringTest(t, feature.Gates, capifeature.MachinePool, false), 1321 }, 1322 { 1323 name: "feature gate implicitly disabled", 1324 ammp: getKnownValidAzureManagedMachinePool(), 1325 deferFunc: func() {}, 1326 }, 1327 } 1328 for _, tc := range tests { 1329 t.Run(tc.name, func(t *testing.T) { 1330 defer tc.deferFunc() 1331 g := NewWithT(t) 1332 mw := &azureManagedMachinePoolWebhook{} 1333 _, err := mw.ValidateCreate(context.Background(), tc.ammp) 1334 g.Expect(err).To(HaveOccurred()) 1335 }) 1336 } 1337 } 1338 1339 func TestAzureManagedMachinePool_validateLastSystemNodePool(t *testing.T) { 1340 deletionTime := metav1.Now() 1341 finalizers := []string{"test"} 1342 systemMachinePool := getManagedMachinePoolWithSystemMode() 1343 systemMachinePoolWithDeletionAnnotation := getAzureManagedMachinePoolWithChanges( 1344 // Add the DeleteForMoveAnnotation annotation to the AMMP 1345 func(azureManagedMachinePool *AzureManagedMachinePool) { 1346 azureManagedMachinePool.Annotations = map[string]string{ 1347 clusterctlv1alpha3.DeleteForMoveAnnotation: "true", 1348 } 1349 }, 1350 ) 1351 tests := []struct { 1352 name string 1353 ammp *AzureManagedMachinePool 1354 cluster *clusterv1.Cluster 1355 wantErr bool 1356 }{ 1357 { 1358 // AzureManagedMachinePool will be deleted since AMMP has DeleteForMoveAnnotation annotation 1359 // Note that Owner Cluster's deletion timestamp is nil and Owner cluster being paused does not matter anymore. 1360 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", 1361 ammp: systemMachinePoolWithDeletionAnnotation, 1362 cluster: &clusterv1.Cluster{ 1363 ObjectMeta: metav1.ObjectMeta{ 1364 Name: systemMachinePool.GetLabels()[clusterv1.ClusterNameLabel], 1365 Namespace: systemMachinePool.Namespace, 1366 Finalizers: finalizers, 1367 }, 1368 }, 1369 wantErr: false, 1370 }, 1371 { 1372 // AzureManagedMachinePool will be deleted since Owner Cluster has been marked for deletion 1373 name: "AzureManagedMachinePool should be deleted since the Cluster is paused with a deletion timestamp", 1374 ammp: systemMachinePool, 1375 cluster: &clusterv1.Cluster{ 1376 ObjectMeta: metav1.ObjectMeta{ 1377 Name: systemMachinePool.GetLabels()[clusterv1.ClusterNameLabel], 1378 Namespace: systemMachinePool.Namespace, 1379 DeletionTimestamp: &deletionTime, 1380 Finalizers: finalizers, 1381 }, 1382 }, 1383 wantErr: false, 1384 }, 1385 { 1386 name: "AzureManagedMachinePool should not be deleted without a deletion timestamp on Owner Cluster and having one system pool node(invalid delete)", 1387 ammp: systemMachinePool, 1388 cluster: &clusterv1.Cluster{ 1389 ObjectMeta: metav1.ObjectMeta{ 1390 Name: systemMachinePool.GetLabels()[clusterv1.ClusterNameLabel], 1391 Namespace: systemMachinePool.Namespace, 1392 }, 1393 }, 1394 wantErr: true, 1395 }, 1396 { 1397 name: "AzureManagedMachinePool should be deleted when Cluster is set with a deletion timestamp having one system pool node(valid delete)", 1398 ammp: systemMachinePool, 1399 cluster: &clusterv1.Cluster{ 1400 ObjectMeta: metav1.ObjectMeta{ 1401 Name: systemMachinePool.GetLabels()[clusterv1.ClusterNameLabel], 1402 Namespace: systemMachinePool.Namespace, 1403 DeletionTimestamp: &deletionTime, 1404 Finalizers: finalizers, 1405 }, 1406 }, 1407 wantErr: false, 1408 }, 1409 } 1410 1411 for _, tc := range tests { 1412 t.Run(tc.name, func(t *testing.T) { 1413 g := NewWithT(t) 1414 scheme := runtime.NewScheme() 1415 _ = AddToScheme(scheme) 1416 _ = clusterv1.AddToScheme(scheme) 1417 fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(tc.cluster, tc.ammp).Build() 1418 err := validateLastSystemNodePool(fakeClient, tc.ammp.Spec.NodeLabels, tc.ammp.Namespace, tc.ammp.Annotations) 1419 if tc.wantErr { 1420 g.Expect(err).To(HaveOccurred()) 1421 } else { 1422 g.Expect(err).NotTo(HaveOccurred()) 1423 } 1424 }) 1425 } 1426 } 1427 1428 func getKnownValidAzureManagedMachinePool() *AzureManagedMachinePool { 1429 return &AzureManagedMachinePool{ 1430 Spec: AzureManagedMachinePoolSpec{ 1431 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1432 MaxPods: ptr.To(30), 1433 OsDiskType: ptr.To(string(asocontainerservicev1.OSDiskType_Ephemeral)), 1434 }, 1435 }, 1436 } 1437 } 1438 1439 func getManagedMachinePoolWithSystemMode() *AzureManagedMachinePool { 1440 return &AzureManagedMachinePool{ 1441 ObjectMeta: metav1.ObjectMeta{ 1442 Namespace: metav1.NamespaceDefault, 1443 Labels: map[string]string{ 1444 clusterv1.ClusterNameLabel: "test-cluster", 1445 LabelAgentPoolMode: string(NodePoolModeSystem), 1446 }, 1447 }, 1448 Spec: AzureManagedMachinePoolSpec{ 1449 AzureManagedMachinePoolClassSpec: AzureManagedMachinePoolClassSpec{ 1450 NodeLabels: map[string]string{ 1451 clusterv1.ClusterNameLabel: "test-cluster", 1452 }, 1453 }, 1454 }, 1455 } 1456 } 1457 1458 func getAzureManagedMachinePoolWithChanges(changes ...func(*AzureManagedMachinePool)) *AzureManagedMachinePool { 1459 ammp := getManagedMachinePoolWithSystemMode().DeepCopy() 1460 for _, change := range changes { 1461 change(ammp) 1462 } 1463 return ammp 1464 }