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