sigs.k8s.io/cluster-api-provider-aws@v1.5.5/controlplane/eks/api/v1beta1/awsmanagedcontrolplane_webhook_test.go (about)

     1  /*
     2  Copyright 2021 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  	"strings"
    22  	"testing"
    23  
    24  	"github.com/aws/aws-sdk-go/aws"
    25  	. "github.com/onsi/gomega"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/utils/pointer"
    28  
    29  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    30  	utildefaulting "sigs.k8s.io/cluster-api/util/defaulting"
    31  )
    32  
    33  var (
    34  	vV1_17_1 = "v1.17.1"
    35  	vV1_17   = "v1.17"
    36  	vV1_16   = "v1.16"
    37  )
    38  
    39  func TestDefaultingWebhook(t *testing.T) {
    40  	defaultTestBastion := infrav1.Bastion{
    41  		AllowedCIDRBlocks: []string{"0.0.0.0/0"},
    42  	}
    43  	AZUsageLimit := 3
    44  	defaultVPCSpec := infrav1.VPCSpec{
    45  		AvailabilityZoneUsageLimit: &AZUsageLimit,
    46  		AvailabilityZoneSelection:  &infrav1.AZSelectionSchemeOrdered,
    47  	}
    48  	defaultIdentityRef := &infrav1.AWSIdentityReference{
    49  		Kind: infrav1.ControllerIdentityKind,
    50  		Name: infrav1.AWSClusterControllerIdentityName,
    51  	}
    52  	defaultNetworkSpec := infrav1.NetworkSpec{
    53  		VPC: defaultVPCSpec,
    54  		CNI: &infrav1.CNISpec{
    55  			CNIIngressRules: []infrav1.CNIIngressRule{
    56  				{
    57  					Description: "bgp (calico)",
    58  					Protocol:    "tcp",
    59  					FromPort:    179,
    60  					ToPort:      179,
    61  				},
    62  				{
    63  					Description: "IP-in-IP (calico)",
    64  					Protocol:    "4",
    65  					FromPort:    -1,
    66  					ToPort:      65535,
    67  				},
    68  			},
    69  		},
    70  	}
    71  
    72  	tests := []struct {
    73  		name         string
    74  		resourceName string
    75  		resourceNS   string
    76  		expectHash   bool
    77  		expect       string
    78  		spec         AWSManagedControlPlaneSpec
    79  		expectSpec   AWSManagedControlPlaneSpec
    80  	}{
    81  		{
    82  			name:         "less than 100 chars",
    83  			resourceName: "cluster1",
    84  			resourceNS:   "default",
    85  			expectHash:   false,
    86  			expectSpec:   AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator},
    87  		},
    88  		{
    89  			name:         "less than 100 chars, dot in name",
    90  			resourceName: "team1.cluster1",
    91  			resourceNS:   "default",
    92  			expectHash:   false,
    93  			expectSpec:   AWSManagedControlPlaneSpec{EKSClusterName: "default_team1_cluster1", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator},
    94  		},
    95  		{
    96  			name:         "more than 100 chars",
    97  			resourceName: "abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde",
    98  			resourceNS:   "default",
    99  			expectHash:   true,
   100  			expectSpec:   AWSManagedControlPlaneSpec{EKSClusterName: "capi_", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator},
   101  		},
   102  		{
   103  			name:         "with patch",
   104  			resourceName: "cluster1",
   105  			resourceNS:   "default",
   106  			expectHash:   false,
   107  			spec:         AWSManagedControlPlaneSpec{Version: &vV1_17_1},
   108  			expectSpec:   AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", Version: &vV1_17, IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator},
   109  		},
   110  		{
   111  			name:         "with allowed ip on bastion",
   112  			resourceName: "cluster1",
   113  			resourceNS:   "default",
   114  			expectHash:   false,
   115  			spec:         AWSManagedControlPlaneSpec{Bastion: infrav1.Bastion{AllowedCIDRBlocks: []string{"100.100.100.100/0"}}},
   116  			expectSpec:   AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", IdentityRef: defaultIdentityRef, Bastion: infrav1.Bastion{AllowedCIDRBlocks: []string{"100.100.100.100/0"}}, NetworkSpec: defaultNetworkSpec, TokenMethod: &EKSTokenMethodIAMAuthenticator},
   117  		},
   118  		{
   119  			name:         "with CNI on network",
   120  			resourceName: "cluster1",
   121  			resourceNS:   "default",
   122  			expectHash:   false,
   123  			spec:         AWSManagedControlPlaneSpec{NetworkSpec: infrav1.NetworkSpec{CNI: &infrav1.CNISpec{}}},
   124  			expectSpec:   AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: infrav1.NetworkSpec{CNI: &infrav1.CNISpec{}, VPC: defaultVPCSpec}, TokenMethod: &EKSTokenMethodIAMAuthenticator},
   125  		},
   126  		{
   127  			name:         "secondary CIDR",
   128  			resourceName: "cluster1",
   129  			resourceNS:   "default",
   130  			expectHash:   false,
   131  			expectSpec:   AWSManagedControlPlaneSpec{EKSClusterName: "default_cluster1", IdentityRef: defaultIdentityRef, Bastion: defaultTestBastion, NetworkSpec: defaultNetworkSpec, SecondaryCidrBlock: nil, TokenMethod: &EKSTokenMethodIAMAuthenticator},
   132  		},
   133  	}
   134  
   135  	for _, tc := range tests {
   136  		t.Run(tc.name, func(t *testing.T) {
   137  			ctx := context.TODO()
   138  			g := NewWithT(t)
   139  
   140  			mcp := &AWSManagedControlPlane{
   141  				ObjectMeta: metav1.ObjectMeta{
   142  					Name:      tc.resourceName,
   143  					Namespace: tc.resourceNS,
   144  				},
   145  			}
   146  			t.Run("for AWSManagedMachinePool", utildefaulting.DefaultValidateTest(mcp))
   147  			mcp.Spec = tc.spec
   148  
   149  			g.Expect(testEnv.Create(ctx, mcp)).To(Succeed())
   150  
   151  			defer func() {
   152  				testEnv.Delete(ctx, mcp)
   153  			}()
   154  
   155  			g.Expect(mcp.Spec.EKSClusterName).ToNot(BeEmpty())
   156  
   157  			if tc.expectHash {
   158  				g.Expect(strings.HasPrefix(mcp.Spec.EKSClusterName, "capa_")).To(BeTrue())
   159  				// We don't care about the exact name
   160  				tc.expectSpec.EKSClusterName = mcp.Spec.EKSClusterName
   161  			}
   162  			g.Expect(mcp.Spec).To(Equal(tc.expectSpec))
   163  		})
   164  	}
   165  }
   166  
   167  func TestWebhookCreate(t *testing.T) {
   168  	tests := []struct { //nolint:maligned
   169  		name           string
   170  		eksClusterName string
   171  		expectError    bool
   172  		eksVersion     string
   173  		hasAddons      bool
   174  		disableVPCCNI  bool
   175  		additionalTags infrav1.Tags
   176  		secondaryCidr  *string
   177  		kubeProxy      KubeProxy
   178  	}{
   179  		{
   180  			name:           "ekscluster specified",
   181  			eksClusterName: "default_cluster1",
   182  			expectError:    false,
   183  			hasAddons:      false,
   184  			disableVPCCNI:  false,
   185  			additionalTags: infrav1.Tags{
   186  				"a":     "b",
   187  				"key-2": "value-2",
   188  			},
   189  		},
   190  		{
   191  			name:           "ekscluster NOT specified",
   192  			eksClusterName: "",
   193  			expectError:    false,
   194  			hasAddons:      false,
   195  			disableVPCCNI:  false,
   196  		},
   197  		{
   198  			name:           "invalid version",
   199  			eksClusterName: "default_cluster1",
   200  			eksVersion:     "v1.x17",
   201  			expectError:    true,
   202  			hasAddons:      false,
   203  			disableVPCCNI:  false,
   204  		},
   205  		{
   206  			name:           "addons with allowed k8s version",
   207  			eksClusterName: "default_cluster1",
   208  			eksVersion:     "v1.18",
   209  			expectError:    false,
   210  			hasAddons:      true,
   211  			disableVPCCNI:  false,
   212  		},
   213  		{
   214  			name:           "addons with not allowed k8s version",
   215  			eksClusterName: "default_cluster1",
   216  			eksVersion:     "v1.17",
   217  			expectError:    true,
   218  			hasAddons:      true,
   219  			disableVPCCNI:  false,
   220  		},
   221  		{
   222  			name:           "disable vpc cni allowed with no addons or secondary cidr",
   223  			eksClusterName: "default_cluster1",
   224  			eksVersion:     "v1.19",
   225  			expectError:    false,
   226  			hasAddons:      false,
   227  			disableVPCCNI:  true,
   228  		},
   229  		{
   230  			name:           "disable vpc cni not allowed with vpc cni addon",
   231  			eksClusterName: "default_cluster1",
   232  			eksVersion:     "v1.19",
   233  			expectError:    true,
   234  			hasAddons:      true,
   235  			disableVPCCNI:  true,
   236  		},
   237  		{
   238  			name:           "disable vpc cni allowed with valid secondary",
   239  			eksClusterName: "default_cluster1",
   240  			eksVersion:     "v1.19",
   241  			expectError:    false,
   242  			hasAddons:      false,
   243  			disableVPCCNI:  true,
   244  			secondaryCidr:  aws.String("100.64.0.0/16"),
   245  		},
   246  		{
   247  			name:           "disable vpc cni allowed with invalid secondary",
   248  			eksClusterName: "default_cluster1",
   249  			eksVersion:     "v1.19",
   250  			expectError:    true,
   251  			hasAddons:      false,
   252  			disableVPCCNI:  true,
   253  			secondaryCidr:  aws.String("100.64.0.0/10"),
   254  		},
   255  		{
   256  			name:           "invalid tags not allowed",
   257  			eksClusterName: "default_cluster1",
   258  			expectError:    true,
   259  			hasAddons:      false,
   260  			disableVPCCNI:  false,
   261  			additionalTags: infrav1.Tags{
   262  				"key-1":                    "value-1",
   263  				"":                         "value-2",
   264  				strings.Repeat("CAPI", 33): "value-3",
   265  				"key-4":                    strings.Repeat("CAPI", 65),
   266  			},
   267  		},
   268  		{
   269  			name:           "disable kube-proxy allowed with no addons",
   270  			eksClusterName: "default_cluster1",
   271  			eksVersion:     "v1.19",
   272  			expectError:    false,
   273  			hasAddons:      false,
   274  			disableVPCCNI:  false,
   275  			kubeProxy: KubeProxy{
   276  				Disable: true,
   277  			},
   278  		},
   279  		{
   280  			name:           "disable kube-proxy not allowed with kube-proxy addon",
   281  			eksClusterName: "default_cluster1",
   282  			eksVersion:     "v1.19",
   283  			expectError:    true,
   284  			hasAddons:      true,
   285  			disableVPCCNI:  false,
   286  			kubeProxy: KubeProxy{
   287  				Disable: true,
   288  			},
   289  		},
   290  		{
   291  			name:           "disable vpc cni and disable kube-proxy allowed with no addons",
   292  			eksClusterName: "default_cluster1",
   293  			eksVersion:     "v1.19",
   294  			expectError:    false,
   295  			hasAddons:      false,
   296  			disableVPCCNI:  true,
   297  			kubeProxy: KubeProxy{
   298  				Disable: true,
   299  			},
   300  		},
   301  		{
   302  			name:           "disable vpc cni and disable kube-proxy not allowed with vpc cni and kube-proxy addons",
   303  			eksClusterName: "default_cluster1",
   304  			eksVersion:     "v1.19",
   305  			expectError:    true,
   306  			hasAddons:      true,
   307  			disableVPCCNI:  true,
   308  			kubeProxy: KubeProxy{
   309  				Disable: true,
   310  			},
   311  		},
   312  	}
   313  
   314  	for _, tc := range tests {
   315  		t.Run(tc.name, func(t *testing.T) {
   316  			ctx := context.TODO()
   317  			g := NewWithT(t)
   318  
   319  			mcp := &AWSManagedControlPlane{
   320  				ObjectMeta: metav1.ObjectMeta{
   321  					GenerateName: "mcp-",
   322  					Namespace:    "default",
   323  				},
   324  				Spec: AWSManagedControlPlaneSpec{
   325  					EKSClusterName: tc.eksClusterName,
   326  					DisableVPCCNI:  tc.disableVPCCNI,
   327  					KubeProxy:      tc.kubeProxy,
   328  					AdditionalTags: tc.additionalTags,
   329  				},
   330  			}
   331  			if tc.eksVersion != "" {
   332  				mcp.Spec.Version = &tc.eksVersion
   333  			}
   334  			if tc.hasAddons {
   335  				testAddons := []Addon{
   336  					{
   337  						Name:    vpcCniAddon,
   338  						Version: "v1.0.0",
   339  					},
   340  					{
   341  						Name:    kubeProxyAddon,
   342  						Version: "v1.0.0",
   343  					},
   344  				}
   345  				mcp.Spec.Addons = &testAddons
   346  			}
   347  			if tc.secondaryCidr != nil {
   348  				mcp.Spec.SecondaryCidrBlock = tc.secondaryCidr
   349  			}
   350  
   351  			err := testEnv.Create(ctx, mcp)
   352  
   353  			if tc.expectError {
   354  				g.Expect(err).ToNot(BeNil())
   355  			} else {
   356  				g.Expect(err).To(BeNil())
   357  			}
   358  		})
   359  	}
   360  }
   361  
   362  func TestWebhookUpdate(t *testing.T) {
   363  	tests := []struct {
   364  		name           string
   365  		oldClusterSpec AWSManagedControlPlaneSpec
   366  		newClusterSpec AWSManagedControlPlaneSpec
   367  		oldClusterName string
   368  		newClusterName string
   369  		oldEksVersion  string
   370  		newEksVersion  string
   371  		expectError    bool
   372  	}{
   373  		{
   374  			name: "ekscluster specified, same cluster names",
   375  			oldClusterSpec: AWSManagedControlPlaneSpec{
   376  				EKSClusterName: "default_cluster1",
   377  			},
   378  			newClusterSpec: AWSManagedControlPlaneSpec{
   379  				EKSClusterName: "default_cluster1",
   380  			},
   381  			expectError: false,
   382  		},
   383  		{
   384  			name: "ekscluster specified, different cluster names",
   385  			oldClusterSpec: AWSManagedControlPlaneSpec{
   386  				EKSClusterName: "default_cluster1",
   387  			},
   388  			newClusterSpec: AWSManagedControlPlaneSpec{
   389  				EKSClusterName: "default_cluster2",
   390  			},
   391  			expectError: true,
   392  		},
   393  		{
   394  			name: "old ekscluster specified, no new cluster name",
   395  			oldClusterSpec: AWSManagedControlPlaneSpec{
   396  				EKSClusterName: "default_cluster1",
   397  			},
   398  			newClusterSpec: AWSManagedControlPlaneSpec{
   399  				EKSClusterName: "",
   400  			},
   401  			expectError: true,
   402  		},
   403  		{
   404  			name: "older version",
   405  			oldClusterSpec: AWSManagedControlPlaneSpec{
   406  				EKSClusterName: "default_cluster1",
   407  				Version:        &vV1_17,
   408  			},
   409  			newClusterSpec: AWSManagedControlPlaneSpec{
   410  				EKSClusterName: "default_cluster1",
   411  				Version:        &vV1_16,
   412  			},
   413  			expectError: true,
   414  		},
   415  		{
   416  			name: "same version",
   417  			oldClusterSpec: AWSManagedControlPlaneSpec{
   418  				EKSClusterName: "default_cluster1",
   419  				Version:        &vV1_17,
   420  			},
   421  			newClusterSpec: AWSManagedControlPlaneSpec{
   422  				EKSClusterName: "default_cluster1",
   423  				Version:        &vV1_17,
   424  			},
   425  			expectError: false,
   426  		},
   427  		{
   428  			name: "newer version",
   429  			oldClusterSpec: AWSManagedControlPlaneSpec{
   430  				EKSClusterName: "default_cluster1",
   431  				Version:        &vV1_16,
   432  			},
   433  			newClusterSpec: AWSManagedControlPlaneSpec{
   434  				EKSClusterName: "default_cluster1",
   435  				Version:        &vV1_17,
   436  			},
   437  			expectError: false,
   438  		},
   439  		{
   440  			name: "change in encryption config to nil",
   441  			oldClusterSpec: AWSManagedControlPlaneSpec{
   442  				EKSClusterName: "default_cluster1",
   443  				EncryptionConfig: &EncryptionConfig{
   444  					Provider:  pointer.String("provider"),
   445  					Resources: []*string{pointer.String("foo"), pointer.String("bar")},
   446  				},
   447  			},
   448  			newClusterSpec: AWSManagedControlPlaneSpec{
   449  				EKSClusterName: "default_cluster1",
   450  			},
   451  			expectError: true,
   452  		},
   453  		{
   454  			name: "change in encryption config from nil to valid encryption-config",
   455  			oldClusterSpec: AWSManagedControlPlaneSpec{
   456  				EKSClusterName: "default_cluster1",
   457  			},
   458  			newClusterSpec: AWSManagedControlPlaneSpec{
   459  				EKSClusterName: "default_cluster1",
   460  				EncryptionConfig: &EncryptionConfig{
   461  					Provider:  pointer.String("provider"),
   462  					Resources: []*string{pointer.String("foo"), pointer.String("bar")},
   463  				},
   464  			},
   465  			expectError: false,
   466  		},
   467  		{
   468  			name: "change in provider of encryption config",
   469  			oldClusterSpec: AWSManagedControlPlaneSpec{
   470  				EKSClusterName: "default_cluster1",
   471  				EncryptionConfig: &EncryptionConfig{
   472  					Provider:  pointer.String("provider"),
   473  					Resources: []*string{pointer.String("foo"), pointer.String("bar")},
   474  				},
   475  			},
   476  			newClusterSpec: AWSManagedControlPlaneSpec{
   477  				EKSClusterName: "default_cluster1",
   478  				EncryptionConfig: &EncryptionConfig{
   479  					Provider:  pointer.String("new-provider"),
   480  					Resources: []*string{pointer.String("foo"), pointer.String("bar")},
   481  				},
   482  			},
   483  			expectError: true,
   484  		},
   485  		{
   486  			name: "no change in provider of encryption config",
   487  			oldClusterSpec: AWSManagedControlPlaneSpec{
   488  				EKSClusterName: "default_cluster1",
   489  				EncryptionConfig: &EncryptionConfig{
   490  					Provider: pointer.String("provider"),
   491  				},
   492  			},
   493  			newClusterSpec: AWSManagedControlPlaneSpec{
   494  				EKSClusterName: "default_cluster1",
   495  				EncryptionConfig: &EncryptionConfig{
   496  					Provider: pointer.String("provider"),
   497  				},
   498  			},
   499  			expectError: false,
   500  		},
   501  		{
   502  			name: "ekscluster specified, same name, invalid tags",
   503  			oldClusterSpec: AWSManagedControlPlaneSpec{
   504  				EKSClusterName: "default_cluster1",
   505  			},
   506  			newClusterSpec: AWSManagedControlPlaneSpec{
   507  				EKSClusterName: "default_cluster1",
   508  				AdditionalTags: infrav1.Tags{
   509  					"key-1":                    "value-1",
   510  					"":                         "value-2",
   511  					strings.Repeat("CAPI", 33): "value-3",
   512  					"key-4":                    strings.Repeat("CAPI", 65),
   513  				},
   514  			},
   515  			expectError: true,
   516  		},
   517  	}
   518  
   519  	for _, tc := range tests {
   520  		t.Run(tc.name, func(t *testing.T) {
   521  			g := NewWithT(t)
   522  			ctx := context.TODO()
   523  			mcp := &AWSManagedControlPlane{
   524  				ObjectMeta: metav1.ObjectMeta{
   525  					GenerateName: "mcp-",
   526  					Namespace:    "default",
   527  				},
   528  				Spec: tc.oldClusterSpec,
   529  			}
   530  			g.Expect(testEnv.Create(ctx, mcp)).To(Succeed())
   531  			mcp.Spec = tc.newClusterSpec
   532  			err := testEnv.Update(ctx, mcp)
   533  
   534  			if tc.expectError {
   535  				g.Expect(err).ToNot(BeNil())
   536  			} else {
   537  				g.Expect(err).To(BeNil())
   538  			}
   539  		})
   540  	}
   541  }
   542  
   543  func TestValidatingWebhookCreate_SecondaryCidr(t *testing.T) {
   544  	tests := []struct {
   545  		name        string
   546  		expectError bool
   547  		cidrRange   string
   548  	}{
   549  		{
   550  			name:        "complete range 1",
   551  			cidrRange:   "100.64.0.0/10",
   552  			expectError: true,
   553  		},
   554  		{
   555  			name:        "complete range 2",
   556  			cidrRange:   "198.19.0.0/16",
   557  			expectError: false,
   558  		},
   559  		{
   560  			name:        "subrange",
   561  			cidrRange:   "100.67.0.0/16",
   562  			expectError: false,
   563  		},
   564  		{
   565  			name:        "invalid value",
   566  			cidrRange:   "not a cidr range",
   567  			expectError: true,
   568  		},
   569  		{
   570  			name:        "unsupported range",
   571  			cidrRange:   "10.0.0.1/20",
   572  			expectError: true,
   573  		},
   574  		{
   575  			name:        "too large",
   576  			cidrRange:   "100.64.0.0/15",
   577  			expectError: true,
   578  		},
   579  		{
   580  			name:        "too small",
   581  			cidrRange:   "100.64.0.0/29",
   582  			expectError: true,
   583  		},
   584  	}
   585  
   586  	for _, tc := range tests {
   587  		t.Run(tc.name, func(t *testing.T) {
   588  			g := NewWithT(t)
   589  
   590  			mcp := &AWSManagedControlPlane{
   591  				Spec: AWSManagedControlPlaneSpec{
   592  					EKSClusterName: "default_cluster1",
   593  				},
   594  			}
   595  			if tc.cidrRange != "" {
   596  				mcp.Spec.SecondaryCidrBlock = &tc.cidrRange
   597  			}
   598  			err := mcp.ValidateCreate()
   599  
   600  			if tc.expectError {
   601  				g.Expect(err).ToNot(BeNil())
   602  			} else {
   603  				g.Expect(err).To(BeNil())
   604  			}
   605  		})
   606  	}
   607  }
   608  
   609  func TestValidatingWebhookUpdate_SecondaryCidr(t *testing.T) {
   610  	tests := []struct {
   611  		name        string
   612  		cidrRange   string
   613  		expectError bool
   614  	}{
   615  		{
   616  			name:        "complete range 1",
   617  			cidrRange:   "100.64.0.0/10",
   618  			expectError: true,
   619  		},
   620  		{
   621  			name:        "complete range 2",
   622  			cidrRange:   "198.19.0.0/16",
   623  			expectError: false,
   624  		},
   625  		{
   626  			name:        "subrange",
   627  			cidrRange:   "100.67.0.0/16",
   628  			expectError: false,
   629  		},
   630  		{
   631  			name:        "invalid value",
   632  			cidrRange:   "not a cidr range",
   633  			expectError: true,
   634  		},
   635  		{
   636  			name:        "unsupported range",
   637  			cidrRange:   "10.0.0.1/20",
   638  			expectError: true,
   639  		},
   640  		{
   641  			name:        "too large",
   642  			cidrRange:   "100.64.0.0/15",
   643  			expectError: true,
   644  		},
   645  		{
   646  			name:        "too small",
   647  			cidrRange:   "100.64.0.0/29",
   648  			expectError: true,
   649  		},
   650  	}
   651  
   652  	for _, tc := range tests {
   653  		t.Run(tc.name, func(t *testing.T) {
   654  			g := NewWithT(t)
   655  
   656  			newMCP := &AWSManagedControlPlane{
   657  				Spec: AWSManagedControlPlaneSpec{
   658  					EKSClusterName:     "default_cluster1",
   659  					SecondaryCidrBlock: &tc.cidrRange,
   660  				},
   661  			}
   662  			oldMCP := &AWSManagedControlPlane{
   663  				Spec: AWSManagedControlPlaneSpec{
   664  					EKSClusterName:     "default_cluster1",
   665  					SecondaryCidrBlock: nil,
   666  				},
   667  			}
   668  
   669  			err := newMCP.ValidateUpdate(oldMCP)
   670  
   671  			if tc.expectError {
   672  				g.Expect(err).ToNot(BeNil())
   673  			} else {
   674  				g.Expect(err).To(BeNil())
   675  			}
   676  		})
   677  	}
   678  }