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  }