sigs.k8s.io/cluster-api-provider-azure@v1.14.3/api/v1beta1/azuremanagedcontrolplanetemplate_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  	. "github.com/onsi/gomega"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/utils/ptr"
    26  )
    27  
    28  func TestControlPlaneTemplateDefaultingWebhook(t *testing.T) {
    29  	g := NewWithT(t)
    30  
    31  	t.Logf("Testing amcp defaulting webhook with no baseline")
    32  	amcpt := getAzureManagedControlPlaneTemplate()
    33  	mcptw := &azureManagedControlPlaneTemplateWebhook{}
    34  	err := mcptw.Default(context.Background(), amcpt)
    35  	g.Expect(err).NotTo(HaveOccurred())
    36  	g.Expect(*amcpt.Spec.Template.Spec.NetworkPlugin).To(Equal("azure"))
    37  	g.Expect(*amcpt.Spec.Template.Spec.LoadBalancerSKU).To(Equal("Standard"))
    38  	g.Expect(amcpt.Spec.Template.Spec.Version).To(Equal("v1.17.5"))
    39  	g.Expect(amcpt.Spec.Template.Spec.VirtualNetwork.Name).To(Equal("fooName"))
    40  	g.Expect(amcpt.Spec.Template.Spec.VirtualNetwork.CIDRBlock).To(Equal(defaultAKSVnetCIDR))
    41  	g.Expect(amcpt.Spec.Template.Spec.VirtualNetwork.Subnet.Name).To(Equal("fooName"))
    42  	g.Expect(amcpt.Spec.Template.Spec.VirtualNetwork.Subnet.CIDRBlock).To(Equal(defaultAKSNodeSubnetCIDR))
    43  	g.Expect(amcpt.Spec.Template.Spec.SKU.Tier).To(Equal(FreeManagedControlPlaneTier))
    44  	g.Expect(*amcpt.Spec.Template.Spec.EnablePreviewFeatures).To(BeFalse())
    45  
    46  	t.Logf("Testing amcp defaulting webhook with baseline")
    47  	netPlug := "kubenet"
    48  	lbSKU := "Basic"
    49  	netPol := "azure"
    50  	amcpt.Spec.Template.Spec.NetworkPlugin = &netPlug
    51  	amcpt.Spec.Template.Spec.LoadBalancerSKU = &lbSKU
    52  	amcpt.Spec.Template.Spec.NetworkPolicy = &netPol
    53  	amcpt.Spec.Template.Spec.Version = "9.99.99"
    54  	amcpt.Spec.Template.Spec.VirtualNetwork.Name = "fooVnetName"
    55  	amcpt.Spec.Template.Spec.VirtualNetwork.Subnet.Name = "fooSubnetName"
    56  	amcpt.Spec.Template.Spec.SKU.Tier = PaidManagedControlPlaneTier
    57  	amcpt.Spec.Template.Spec.EnablePreviewFeatures = ptr.To(true)
    58  
    59  	err = mcptw.Default(context.Background(), amcpt)
    60  	g.Expect(err).NotTo(HaveOccurred())
    61  	g.Expect(*amcpt.Spec.Template.Spec.NetworkPlugin).To(Equal(netPlug))
    62  	g.Expect(*amcpt.Spec.Template.Spec.LoadBalancerSKU).To(Equal(lbSKU))
    63  	g.Expect(*amcpt.Spec.Template.Spec.NetworkPolicy).To(Equal(netPol))
    64  	g.Expect(amcpt.Spec.Template.Spec.Version).To(Equal("v9.99.99"))
    65  	g.Expect(amcpt.Spec.Template.Spec.VirtualNetwork.Name).To(Equal("fooVnetName"))
    66  	g.Expect(amcpt.Spec.Template.Spec.VirtualNetwork.Subnet.Name).To(Equal("fooSubnetName"))
    67  	g.Expect(amcpt.Spec.Template.Spec.SKU.Tier).To(Equal(StandardManagedControlPlaneTier))
    68  	g.Expect(*amcpt.Spec.Template.Spec.EnablePreviewFeatures).To(BeTrue())
    69  }
    70  
    71  func TestControlPlaneTemplateUpdateWebhook(t *testing.T) {
    72  	tests := []struct {
    73  		name                    string
    74  		oldControlPlaneTemplate *AzureManagedControlPlaneTemplate
    75  		controlPlaneTemplate    *AzureManagedControlPlaneTemplate
    76  		wantErr                 bool
    77  	}{
    78  		{
    79  			name:                    "azuremanagedcontrolplanetemplate no changes - valid spec",
    80  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(),
    81  			controlPlaneTemplate:    getAzureManagedControlPlaneTemplate(),
    82  			wantErr:                 false,
    83  		},
    84  		{
    85  			name: "azuremanagedcontrolplanetemplate subscriptionID is immutable",
    86  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
    87  				cpt.Spec.Template.Spec.SubscriptionID = "foo"
    88  			}),
    89  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
    90  				cpt.Spec.Template.Spec.SubscriptionID = "bar"
    91  			}),
    92  			wantErr: true,
    93  		},
    94  		{
    95  			name: "azuremanagedcontrolplanetemplate location is immutable",
    96  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
    97  				cpt.Spec.Template.Spec.Location = "foo"
    98  			}),
    99  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   100  				cpt.Spec.Template.Spec.Location = "bar"
   101  			}),
   102  			wantErr: true,
   103  		},
   104  		{
   105  			name: "azuremanagedcontrolplanetemplate DNSServiceIP is immutable",
   106  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   107  				cpt.Spec.Template.Spec.DNSServiceIP = ptr.To("foo")
   108  			}),
   109  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   110  				cpt.Spec.Template.Spec.DNSServiceIP = ptr.To("bar")
   111  			}),
   112  			wantErr: true,
   113  		},
   114  		{
   115  			name: "azuremanagedcontrolplanetemplate NetworkPlugin is immutable",
   116  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   117  				cpt.Spec.Template.Spec.NetworkPlugin = ptr.To("foo")
   118  			}),
   119  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   120  				cpt.Spec.Template.Spec.NetworkPlugin = ptr.To("bar")
   121  			}),
   122  			wantErr: true,
   123  		},
   124  		{
   125  			name: "azuremanagedcontrolplanetemplate NetworkPolicy is immutable",
   126  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   127  				cpt.Spec.Template.Spec.NetworkPolicy = ptr.To("foo")
   128  			}),
   129  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   130  				cpt.Spec.Template.Spec.NetworkPolicy = ptr.To("bar")
   131  			}),
   132  			wantErr: true,
   133  		},
   134  		{
   135  			name: "azuremanagedcontrolplanetemplate LoadBalancerSKU is immutable",
   136  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   137  				cpt.Spec.Template.Spec.LoadBalancerSKU = ptr.To("foo")
   138  			}),
   139  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   140  				cpt.Spec.Template.Spec.LoadBalancerSKU = ptr.To("bar")
   141  			}),
   142  			wantErr: true,
   143  		},
   144  		{
   145  			name: "cannot disable AADProfile",
   146  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   147  				cpt.Spec.Template.Spec.AADProfile = &AADProfile{
   148  					Managed: true,
   149  				}
   150  			}),
   151  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(),
   152  			wantErr:              true,
   153  		},
   154  		{
   155  			name: "cannot set AADProfile.Managed to false",
   156  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   157  				cpt.Spec.Template.Spec.AADProfile = &AADProfile{
   158  					Managed: true,
   159  				}
   160  			}),
   161  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   162  				cpt.Spec.Template.Spec.AADProfile = &AADProfile{
   163  					Managed: false,
   164  				}
   165  			}),
   166  			wantErr: true,
   167  		},
   168  		{
   169  			name: "length of AADProfile.AdminGroupObjectIDs cannot be zero",
   170  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   171  				cpt.Spec.Template.Spec.AADProfile = &AADProfile{
   172  					AdminGroupObjectIDs: []string{"foo"},
   173  				}
   174  			}),
   175  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   176  				cpt.Spec.Template.Spec.AADProfile = &AADProfile{
   177  					AdminGroupObjectIDs: []string{},
   178  				}
   179  			}),
   180  			wantErr: true,
   181  		},
   182  		{
   183  			name: "azuremanagedcontrolplanetemplate OutboundType is immutable",
   184  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   185  				cpt.Spec.Template.Spec.OutboundType = (*ManagedControlPlaneOutboundType)(ptr.To(string(ManagedControlPlaneOutboundTypeLoadBalancer)))
   186  			}),
   187  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   188  				cpt.Spec.Template.Spec.OutboundType = (*ManagedControlPlaneOutboundType)(ptr.To(string(ManagedControlPlaneOutboundTypeManagedNATGateway)))
   189  			}),
   190  			wantErr: true,
   191  		},
   192  		{
   193  			name: "azuremanagedcontrolplanetemplate AKSExtension type and plan are immutable",
   194  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   195  				cpt.Spec.Template.Spec.Extensions = []AKSExtension{
   196  					{
   197  						Name:          "foo",
   198  						ExtensionType: ptr.To("foo-type"),
   199  						Plan: &ExtensionPlan{
   200  							Name:      "foo-name",
   201  							Product:   "foo-product",
   202  							Publisher: "foo-publisher",
   203  						},
   204  					},
   205  				}
   206  			}),
   207  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   208  				cpt.Spec.Template.Spec.Extensions = []AKSExtension{
   209  					{
   210  						Name:          "foo",
   211  						ExtensionType: ptr.To("bar"),
   212  						Plan: &ExtensionPlan{
   213  							Name:      "bar-name",
   214  							Product:   "bar-product",
   215  							Publisher: "bar-publisher",
   216  						},
   217  					},
   218  				}
   219  			}),
   220  			wantErr: true,
   221  		},
   222  		{
   223  			name: "azuremanagedcontrolplanetemplate AKSExtension autoUpgradeMinorVersion is mutable",
   224  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   225  				cpt.Spec.Template.Spec.Extensions = []AKSExtension{
   226  					{
   227  						Name:                    "foo",
   228  						ExtensionType:           ptr.To("foo"),
   229  						AutoUpgradeMinorVersion: ptr.To(true),
   230  						Plan: &ExtensionPlan{
   231  							Name:      "bar-name",
   232  							Product:   "bar-product",
   233  							Publisher: "bar-publisher",
   234  						},
   235  					},
   236  				}
   237  			}),
   238  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   239  				cpt.Spec.Template.Spec.Extensions = []AKSExtension{
   240  					{
   241  						Name:                    "foo",
   242  						ExtensionType:           ptr.To("foo"),
   243  						AutoUpgradeMinorVersion: ptr.To(false),
   244  						Plan: &ExtensionPlan{
   245  							Name:      "bar-name",
   246  							Product:   "bar-product",
   247  							Publisher: "bar-publisher",
   248  						},
   249  					},
   250  				}
   251  			}),
   252  			wantErr: false,
   253  		},
   254  		{
   255  			name: "azuremanagedcontrolplanetemplate networkDataplane is immutable",
   256  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   257  				cpt.Spec.Template.Spec.NetworkDataplane = ptr.To(NetworkDataplaneTypeAzure)
   258  			}),
   259  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   260  				cpt.Spec.Template.Spec.NetworkDataplane = ptr.To(NetworkDataplaneTypeCilium)
   261  			}),
   262  			wantErr: true,
   263  		},
   264  		{
   265  			name: "AzureManagedControlPlane invalid version downgrade change",
   266  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   267  				cpt.Spec.Template.Spec.Version = "v1.18.0"
   268  			}),
   269  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   270  				cpt.Spec.Template.Spec.Version = "v1.17.0"
   271  			}),
   272  			wantErr: true,
   273  		},
   274  	}
   275  	for _, tc := range tests {
   276  		t.Run(tc.name, func(t *testing.T) {
   277  			g := NewWithT(t)
   278  			cpw := &azureManagedControlPlaneTemplateWebhook{}
   279  			_, err := cpw.ValidateUpdate(context.Background(), tc.oldControlPlaneTemplate, tc.controlPlaneTemplate)
   280  			if tc.wantErr {
   281  				g.Expect(err).To(HaveOccurred())
   282  			} else {
   283  				g.Expect(err).NotTo(HaveOccurred())
   284  			}
   285  		})
   286  	}
   287  }
   288  
   289  func TestValidateVirtualNetworkTemplateUpdate(t *testing.T) {
   290  	tests := []struct {
   291  		name                    string
   292  		oldControlPlaneTemplate *AzureManagedControlPlaneTemplate
   293  		controlPlaneTemplate    *AzureManagedControlPlaneTemplate
   294  		wantErr                 bool
   295  	}{
   296  		{
   297  			name:                    "azuremanagedcontrolplanetemplate no changes - valid spec",
   298  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(),
   299  			controlPlaneTemplate:    getAzureManagedControlPlaneTemplate(),
   300  			wantErr:                 false,
   301  		},
   302  		{
   303  			name: "azuremanagedcontrolplanetemplate name is immutable",
   304  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   305  				cpt.Spec.Template.Spec.VirtualNetwork = ManagedControlPlaneVirtualNetwork{
   306  					ManagedControlPlaneVirtualNetworkClassSpec: ManagedControlPlaneVirtualNetworkClassSpec{
   307  						Name: "fooName",
   308  					},
   309  				}
   310  			}),
   311  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   312  				cpt.Spec.Template.Spec.VirtualNetwork = ManagedControlPlaneVirtualNetwork{
   313  					ManagedControlPlaneVirtualNetworkClassSpec: ManagedControlPlaneVirtualNetworkClassSpec{
   314  						Name: "barName",
   315  					},
   316  				}
   317  			}),
   318  			wantErr: true,
   319  		},
   320  		{
   321  			name: "azuremanagedcontrolplanetemplate networkCIDRBlock is immutable",
   322  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   323  				cpt.Spec.Template.Spec.VirtualNetwork = ManagedControlPlaneVirtualNetwork{
   324  					ManagedControlPlaneVirtualNetworkClassSpec: ManagedControlPlaneVirtualNetworkClassSpec{
   325  						CIDRBlock: "fooCIDRBlock",
   326  					},
   327  				}
   328  			}),
   329  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   330  				cpt.Spec.Template.Spec.VirtualNetwork = ManagedControlPlaneVirtualNetwork{
   331  					ManagedControlPlaneVirtualNetworkClassSpec: ManagedControlPlaneVirtualNetworkClassSpec{
   332  						CIDRBlock: "barCIDRBlock",
   333  					},
   334  				}
   335  			}),
   336  			wantErr: true,
   337  		},
   338  	}
   339  	for _, tc := range tests {
   340  		t.Run(tc.name, func(t *testing.T) {
   341  			g := NewWithT(t)
   342  			allErrs := tc.controlPlaneTemplate.validateVirtualNetworkTemplateUpdate(tc.oldControlPlaneTemplate)
   343  			if tc.wantErr {
   344  				g.Expect(allErrs).NotTo(BeEmpty())
   345  			} else {
   346  				g.Expect(allErrs).To(BeEmpty())
   347  			}
   348  		})
   349  	}
   350  }
   351  
   352  func TestValidateAPIServerAccessProfileUpdate(t *testing.T) {
   353  	tests := []struct {
   354  		name                    string
   355  		oldControlPlaneTemplate *AzureManagedControlPlaneTemplate
   356  		controlPlaneTemplate    *AzureManagedControlPlaneTemplate
   357  		wantErr                 bool
   358  	}{
   359  		{
   360  			name:                    "azuremanagedcontrolplanetemplate no changes - valid spec",
   361  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(),
   362  			controlPlaneTemplate:    getAzureManagedControlPlaneTemplate(),
   363  			wantErr:                 false,
   364  		},
   365  		{
   366  			name: "azuremanagedcontrolplanetemplate enablePrivateCluster is immutable",
   367  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   368  				cpt.Spec.Template.Spec.APIServerAccessProfile = &APIServerAccessProfile{
   369  					APIServerAccessProfileClassSpec: APIServerAccessProfileClassSpec{
   370  						EnablePrivateCluster: ptr.To(true),
   371  					},
   372  				}
   373  			}),
   374  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   375  				cpt.Spec.Template.Spec.APIServerAccessProfile = &APIServerAccessProfile{
   376  					APIServerAccessProfileClassSpec: APIServerAccessProfileClassSpec{
   377  						EnablePrivateCluster: ptr.To(false),
   378  					},
   379  				}
   380  			}),
   381  			wantErr: true,
   382  		},
   383  		{
   384  			name: "azuremanagedcontrolplanetemplate privateDNSZone is immutable",
   385  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   386  				cpt.Spec.Template.Spec.APIServerAccessProfile = &APIServerAccessProfile{
   387  					APIServerAccessProfileClassSpec: APIServerAccessProfileClassSpec{
   388  						PrivateDNSZone: ptr.To("foo"),
   389  					},
   390  				}
   391  			}),
   392  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   393  				cpt.Spec.Template.Spec.APIServerAccessProfile = &APIServerAccessProfile{
   394  					APIServerAccessProfileClassSpec: APIServerAccessProfileClassSpec{
   395  						PrivateDNSZone: ptr.To("bar"),
   396  					},
   397  				}
   398  			}),
   399  			wantErr: true,
   400  		},
   401  		{
   402  			name: "azuremanagedcontrolplanetemplate enablePrivateClusterPublicFQDN is immutable",
   403  			oldControlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   404  				cpt.Spec.Template.Spec.APIServerAccessProfile = &APIServerAccessProfile{
   405  					APIServerAccessProfileClassSpec: APIServerAccessProfileClassSpec{
   406  						EnablePrivateClusterPublicFQDN: ptr.To(true),
   407  					},
   408  				}
   409  			}),
   410  			controlPlaneTemplate: getAzureManagedControlPlaneTemplate(func(cpt *AzureManagedControlPlaneTemplate) {
   411  				cpt.Spec.Template.Spec.APIServerAccessProfile = &APIServerAccessProfile{
   412  					APIServerAccessProfileClassSpec: APIServerAccessProfileClassSpec{
   413  						EnablePrivateClusterPublicFQDN: ptr.To(false),
   414  					},
   415  				}
   416  			}),
   417  			wantErr: true,
   418  		},
   419  	}
   420  	for _, tc := range tests {
   421  		t.Run(tc.name, func(t *testing.T) {
   422  			g := NewWithT(t)
   423  			allErrs := tc.controlPlaneTemplate.validateAPIServerAccessProfileTemplateUpdate(tc.oldControlPlaneTemplate)
   424  			if tc.wantErr {
   425  				g.Expect(allErrs).NotTo(BeEmpty())
   426  			} else {
   427  				g.Expect(allErrs).To(BeEmpty())
   428  			}
   429  		})
   430  	}
   431  }
   432  
   433  func getAzureManagedControlPlaneTemplate(changes ...func(*AzureManagedControlPlaneTemplate)) *AzureManagedControlPlaneTemplate {
   434  	input := &AzureManagedControlPlaneTemplate{
   435  		ObjectMeta: metav1.ObjectMeta{
   436  			Name: "fooName",
   437  		},
   438  		Spec: AzureManagedControlPlaneTemplateSpec{
   439  			Template: AzureManagedControlPlaneTemplateResource{
   440  				Spec: AzureManagedControlPlaneTemplateResourceSpec{
   441  					AzureManagedControlPlaneClassSpec: AzureManagedControlPlaneClassSpec{
   442  						Location: "fooLocation",
   443  						Version:  "v1.17.5",
   444  					},
   445  				},
   446  			},
   447  		},
   448  	}
   449  
   450  	for _, change := range changes {
   451  		change(input)
   452  	}
   453  
   454  	return input
   455  }