diff --git a/pkg/apis/v1/ec2nodeclass.go b/pkg/apis/v1/ec2nodeclass.go index ceaa7b49af64..c1fac7712e88 100644 --- a/pkg/apis/v1/ec2nodeclass.go +++ b/pkg/apis/v1/ec2nodeclass.go @@ -25,6 +25,8 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + awssdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) // EC2NodeClassSpec is the top level specification for the AWS Karpenter Provider. @@ -487,9 +489,26 @@ type EC2NodeClass struct { // 3. A field is removed from the hash calculations const EC2NodeClassHashVersion = "v4" -func (in *EC2NodeClass) Hash() string { +// HashForRegion returns a hash of the EC2NodeClass with region-specific filtering applied. +// This ensures that unsupported features in certain regions don't cause drift. +func (in *EC2NodeClass) HashForRegion(region string) string { + spec := in.Spec.DeepCopy() + + // Filter out HTTPProtocolIPv6 for regions that don't support it + // This matches the filtering logic in pkg/providers/amifamily/resolver.go + if awssdk.HTTPProtocolUnsupportedRegions.Has(region) && spec.MetadataOptions != nil { + // Create a copy of MetadataOptions without HTTPProtocolIPv6 + filteredMetadataOptions := &MetadataOptions{ + HTTPEndpoint: spec.MetadataOptions.HTTPEndpoint, + HTTPPutResponseHopLimit: spec.MetadataOptions.HTTPPutResponseHopLimit, + HTTPTokens: spec.MetadataOptions.HTTPTokens, + // HTTPProtocolIPv6 is intentionally omitted for unsupported regions + } + spec.MetadataOptions = filteredMetadataOptions + } + return fmt.Sprint(lo.Must(hashstructure.Hash([]interface{}{ - in.Spec, + spec, // AMIFamily should be hashed using the dynamically resolved value rather than the literal value of the field. // This ensures that scenarios such as changing the field from nil to AL2023 with the alias "al2023@latest" // doesn't trigger drift. diff --git a/pkg/apis/v1/ec2nodeclass_hash_test.go b/pkg/apis/v1/ec2nodeclass_hash_test.go index 87ad4de0b5af..15c5630c9d4d 100644 --- a/pkg/apis/v1/ec2nodeclass_hash_test.go +++ b/pkg/apis/v1/ec2nodeclass_hash_test.go @@ -15,6 +15,8 @@ limitations under the License. package v1_test import ( + "fmt" + "github.com/imdario/mergo" "github.com/samber/lo" "k8s.io/apimachinery/pkg/api/resource" @@ -24,6 +26,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" + awssdk "github.com/aws/karpenter-provider-aws/pkg/aws" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -77,7 +80,7 @@ var _ = Describe("Hash", func() { "should match static hash on field value change", func(hash string, changes v1.EC2NodeClass) { Expect(mergo.Merge(nodeClass, changes, mergo.WithOverride, mergo.WithSliceDeepCopy)).To(Succeed()) - Expect(nodeClass.Hash()).To(Equal(hash)) + Expect(nodeClass.HashForRegion("us-west-2")).To(Equal(hash)) }, Entry("Base EC2NodeClass", staticHash, v1.EC2NodeClass{}), // Static fields, expect changed hash from base @@ -111,25 +114,25 @@ var _ = Describe("Hash", func() { // doesn't work well with unexported fields, like the ones that are present in resource.Quantity It("should match static hash when updating blockDeviceMapping volumeSize", func() { nodeClass.Spec.BlockDeviceMappings[0].EBS.VolumeSize = resource.NewScaledQuantity(10, resource.Giga) - Expect(nodeClass.Hash()).To(Equal("5906178522470964189")) + Expect(nodeClass.HashForRegion("us-west-2")).To(Equal("5906178522470964189")) }) It("should match static hash for instanceProfile", func() { nodeClass.Spec.Role = "" nodeClass.Spec.InstanceProfile = lo.ToPtr("test-instance-profile") - Expect(nodeClass.Hash()).To(Equal("5855570904022890593")) + Expect(nodeClass.HashForRegion("us-west-2")).To(Equal("5855570904022890593")) }) It("should match static hash when reordering tags", func() { nodeClass.Spec.Tags = map[string]string{"keyTag-2": "valueTag-2", "keyTag-1": "valueTag-1"} - Expect(nodeClass.Hash()).To(Equal(staticHash)) + Expect(nodeClass.HashForRegion("us-west-2")).To(Equal(staticHash)) }) It("should match static hash when reordering blockDeviceMappings", func() { nodeClass.Spec.BlockDeviceMappings[0], nodeClass.Spec.BlockDeviceMappings[1] = nodeClass.Spec.BlockDeviceMappings[1], nodeClass.Spec.BlockDeviceMappings[0] - Expect(nodeClass.Hash()).To(Equal(staticHash)) + Expect(nodeClass.HashForRegion("us-west-2")).To(Equal(staticHash)) }) DescribeTable("should change hash when static fields are updated", func(changes v1.EC2NodeClass) { - hash := nodeClass.Hash() + hash := nodeClass.HashForRegion("us-west-2") Expect(mergo.Merge(nodeClass, changes, mergo.WithOverride, mergo.WithSliceDeepCopy)).To(Succeed()) - updatedHash := nodeClass.Hash() + updatedHash := nodeClass.HashForRegion("us-west-2") Expect(hash).ToNot(Equal(updatedHash)) }, Entry("UserData", v1.EC2NodeClass{Spec: v1.EC2NodeClassSpec{UserData: aws.String("userdata-test-2")}}), @@ -155,33 +158,33 @@ var _ = Describe("Hash", func() { // We create a separate test for updating blockDeviceMapping volumeSize, since resource.Quantity is a struct, and mergo.WithSliceDeepCopy // doesn't work well with unexported fields, like the ones that are present in resource.Quantity It("should change hash blockDeviceMapping volumeSize is updated", func() { - hash := nodeClass.Hash() + hash := nodeClass.HashForRegion("us-west-2") nodeClass.Spec.BlockDeviceMappings[0].EBS.VolumeSize = resource.NewScaledQuantity(10, resource.Giga) - updatedHash := nodeClass.Hash() + updatedHash := nodeClass.HashForRegion("us-west-2") Expect(hash).ToNot(Equal(updatedHash)) }) It("should change hash when instanceProfile is updated", func() { nodeClass.Spec.Role = "" nodeClass.Spec.InstanceProfile = lo.ToPtr("test-instance-profile") - hash := nodeClass.Hash() + hash := nodeClass.HashForRegion("us-west-2") nodeClass.Spec.InstanceProfile = lo.ToPtr("other-instance-profile") - updatedHash := nodeClass.Hash() + updatedHash := nodeClass.HashForRegion("us-west-2") Expect(hash).ToNot(Equal(updatedHash)) }) It("should not change hash when tags are re-ordered", func() { - hash := nodeClass.Hash() + hash := nodeClass.HashForRegion("us-west-2") nodeClass.Spec.Tags = map[string]string{"keyTag-2": "valueTag-2", "keyTag-1": "valueTag-1"} - updatedHash := nodeClass.Hash() + updatedHash := nodeClass.HashForRegion("us-west-2") Expect(hash).To(Equal(updatedHash)) }) It("should not change hash when blockDeviceMappings are re-ordered", func() { - hash := nodeClass.Hash() + hash := nodeClass.HashForRegion("us-west-2") nodeClass.Spec.BlockDeviceMappings[0], nodeClass.Spec.BlockDeviceMappings[1] = nodeClass.Spec.BlockDeviceMappings[1], nodeClass.Spec.BlockDeviceMappings[0] - updatedHash := nodeClass.Hash() + updatedHash := nodeClass.HashForRegion("us-west-2") Expect(hash).To(Equal(updatedHash)) }) It("should not change hash when behavior/dynamic fields are updated", func() { - hash := nodeClass.Hash() + hash := nodeClass.HashForRegion("us-west-2") // Update a behavior/dynamic field nodeClass.Spec.SubnetSelectorTerms = []v1.SubnetSelectorTerm{{ @@ -196,13 +199,118 @@ var _ = Describe("Hash", func() { nodeClass.Spec.CapacityReservationSelectorTerms = []v1.CapacityReservationSelectorTerm{{ Tags: map[string]string{"cr-test-key": "cr-test-value"}, }} - updatedHash := nodeClass.Hash() + updatedHash := nodeClass.HashForRegion("us-west-2") Expect(hash).To(Equal(updatedHash)) }) It("should expect two EC2NodeClasses with the same spec to have the same hash", func() { otherNodeClass := &v1.EC2NodeClass{ Spec: nodeClass.Spec, } - Expect(nodeClass.Hash()).To(Equal(otherNodeClass.Hash())) + Expect(nodeClass.HashForRegion("us-west-2")).To(Equal(otherNodeClass.HashForRegion("us-west-2"))) + }) + + Context("Region-specific filtering", func() { + It("should filter HTTPProtocolIPv6 for unsupported regions", func() { + nodeClass.Spec.MetadataOptions = &v1.MetadataOptions{ + HTTPEndpoint: lo.ToPtr("enabled"), + HTTPProtocolIPv6: lo.ToPtr("enabled"), + HTTPPutResponseHopLimit: lo.ToPtr(int64(1)), + HTTPTokens: lo.ToPtr("required"), + } + + // Hash for supported region should include HTTPProtocolIPv6 + supportedRegionHash := nodeClass.HashForRegion("us-west-2") + + // Hash for unsupported region should exclude HTTPProtocolIPv6 + unsupportedRegionHash := nodeClass.HashForRegion("us-iso-east-1") + + // The hashes should be different because HTTPProtocolIPv6 is filtered out + Expect(supportedRegionHash).ToNot(Equal(unsupportedRegionHash)) + }) + + It("should have same hash for unsupported regions regardless of HTTPProtocolIPv6 value", func() { + nodeClass1 := nodeClass.DeepCopy() + nodeClass1.Spec.MetadataOptions = &v1.MetadataOptions{ + HTTPEndpoint: lo.ToPtr("enabled"), + HTTPProtocolIPv6: lo.ToPtr("enabled"), + HTTPPutResponseHopLimit: lo.ToPtr(int64(1)), + HTTPTokens: lo.ToPtr("required"), + } + + nodeClass2 := nodeClass.DeepCopy() + nodeClass2.Spec.MetadataOptions = &v1.MetadataOptions{ + HTTPEndpoint: lo.ToPtr("enabled"), + HTTPProtocolIPv6: lo.ToPtr("disabled"), + HTTPPutResponseHopLimit: lo.ToPtr(int64(1)), + HTTPTokens: lo.ToPtr("required"), + } + + // Both should have the same hash for unsupported regions since HTTPProtocolIPv6 is filtered out + hash1 := nodeClass1.HashForRegion("us-iso-east-1") + hash2 := nodeClass2.HashForRegion("us-iso-east-1") + Expect(hash1).To(Equal(hash2)) + }) + + It("should not filter HTTPProtocolIPv6 when MetadataOptions is nil", func() { + nodeClass.Spec.MetadataOptions = nil + + supportedRegionHash := nodeClass.HashForRegion("us-west-2") + unsupportedRegionHash := nodeClass.HashForRegion("us-iso-east-1") + + // Should be the same since there's nothing to filter + Expect(supportedRegionHash).To(Equal(unsupportedRegionHash)) + }) + + It("should match all unsupported regions", func() { + nodeClass.Spec.MetadataOptions = &v1.MetadataOptions{ + HTTPEndpoint: lo.ToPtr("enabled"), + HTTPProtocolIPv6: lo.ToPtr("enabled"), + HTTPPutResponseHopLimit: lo.ToPtr(int64(1)), + HTTPTokens: lo.ToPtr("required"), + } + + supportedRegionHash := nodeClass.HashForRegion("us-west-2") + + for region := range awssdk.HTTPProtocolUnsupportedRegions { + unsupportedRegionHash := nodeClass.HashForRegion(region) + Expect(unsupportedRegionHash).ToNot(Equal(supportedRegionHash), fmt.Sprintf("Region %s should filter HTTPProtocolIPv6", region)) + } + }) + }) + + It("should produce same hash for unsupported regions regardless of HTTPProtocolIPv6 value", func() { + nodeClass.Spec.MetadataOptions = &v1.MetadataOptions{ + HTTPEndpoint: lo.ToPtr("enabled"), + HTTPProtocolIPv6: lo.ToPtr("enabled"), + HTTPPutResponseHopLimit: lo.ToPtr(int64(1)), + HTTPTokens: lo.ToPtr("required"), + } + + hashWithEnabled := nodeClass.HashForRegion("us-iso-east-1") + + nodeClass.Spec.MetadataOptions.HTTPProtocolIPv6 = lo.ToPtr("disabled") + hashWithDisabled := nodeClass.HashForRegion("us-iso-east-1") + + nodeClass.Spec.MetadataOptions.HTTPProtocolIPv6 = nil + hashWithNil := nodeClass.HashForRegion("us-iso-east-1") + + Expect(hashWithEnabled).To(Equal(hashWithDisabled)) + Expect(hashWithEnabled).To(Equal(hashWithNil)) + }) + + It("should produce different hashes for supported regions when HTTPProtocolIPv6 changes", func() { + nodeClass.Spec.MetadataOptions = &v1.MetadataOptions{ + HTTPEndpoint: lo.ToPtr("enabled"), + HTTPProtocolIPv6: lo.ToPtr("enabled"), + HTTPPutResponseHopLimit: lo.ToPtr(int64(1)), + HTTPTokens: lo.ToPtr("required"), + } + + hashWithEnabled := nodeClass.HashForRegion("us-west-2") + + nodeClass.Spec.MetadataOptions.HTTPProtocolIPv6 = lo.ToPtr("disabled") + hashWithDisabled := nodeClass.HashForRegion("us-west-2") + + Expect(hashWithEnabled).ToNot(Equal(hashWithDisabled)) }) }) diff --git a/pkg/aws/sdk.go b/pkg/aws/sdk.go index a594c9e53646..ae21e4c2fb94 100644 --- a/pkg/aws/sdk.go +++ b/pkg/aws/sdk.go @@ -24,6 +24,17 @@ import ( "github.com/aws/aws-sdk-go-v2/service/sqs" "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/aws/aws-sdk-go-v2/service/timestreamwrite" + "k8s.io/apimachinery/pkg/util/sets" +) + +// HTTPProtocolUnsupportedRegions contains regions that don't support HTTPProtocolIPv6 +var HTTPProtocolUnsupportedRegions = sets.New[string]( + "us-iso-east-1", + "us-iso-west-1", + "us-isob-east-1", + "us-isob-west-1", + "us-isof-south-1", + "us-isof-east-1", ) type EC2API interface { diff --git a/pkg/cloudprovider/cloudprovider.go b/pkg/cloudprovider/cloudprovider.go index 95b9e8144c29..089294146457 100644 --- a/pkg/cloudprovider/cloudprovider.go +++ b/pkg/cloudprovider/cloudprovider.go @@ -147,7 +147,7 @@ func (c *CloudProvider) Create(ctx context.Context, nodeClaim *karpv1.NodeClaim) }) nc := c.instanceToNodeClaim(instance, instanceType, nodeClass) nc.Annotations = lo.Assign(nc.Annotations, map[string]string{ - v1.AnnotationEC2NodeClassHash: nodeClass.Hash(), + v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion(c.instanceProvider.Region()), v1.AnnotationEC2NodeClassHashVersion: v1.EC2NodeClassHashVersion, v1.AnnotationInstanceProfile: nodeClass.Status.InstanceProfile, }) diff --git a/pkg/cloudprovider/suite_test.go b/pkg/cloudprovider/suite_test.go index bcf6337d7e25..1328980ca951 100644 --- a/pkg/cloudprovider/suite_test.go +++ b/pkg/cloudprovider/suite_test.go @@ -812,13 +812,13 @@ var _ = Describe("CloudProvider", func() { Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{instance}}}, }) nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{ - v1.AnnotationEC2NodeClassHash: nodeClass.Hash(), + v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion("us-west-2"), v1.AnnotationEC2NodeClassHashVersion: v1.EC2NodeClassHashVersion, }) nodeClaim.Status.ProviderID = fake.ProviderID(lo.FromPtr(instance.InstanceId)) nodeClaim.Status.ImageID = amdAMIID nodeClaim.Annotations = lo.Assign(nodeClaim.Annotations, map[string]string{ - v1.AnnotationEC2NodeClassHash: nodeClass.Hash(), + v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion("us-west-2"), v1.AnnotationEC2NodeClassHashVersion: v1.EC2NodeClassHashVersion, }) nodeClaim.Labels = lo.Assign(nodeClaim.Labels, map[string]string{corev1.LabelInstanceTypeStable: selectedInstanceType.Name}) @@ -1076,8 +1076,8 @@ var _ = Describe("CloudProvider", func() { }, }, } - nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.Hash()}) - nodeClaim.Annotations = lo.Assign(nodeClaim.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.Hash()}) + nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion("us-west-2")}) + nodeClaim.Annotations = lo.Assign(nodeClaim.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion("us-west-2")}) }) DescribeTable("should return drifted if a statically drifted EC2NodeClass.Spec field is updated", func(changes v1.EC2NodeClass) { @@ -1087,7 +1087,7 @@ var _ = Describe("CloudProvider", func() { Expect(isDrifted).To(BeEmpty()) Expect(mergo.Merge(nodeClass, changes, mergo.WithOverride, mergo.WithSliceDeepCopy)).To(Succeed()) - nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.Hash()}) + nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion("us-west-2")}) ExpectApplied(ctx, env.Client, nodeClass) isDrifted, err = cloudProvider.IsDrifted(ctx, nodeClaim) @@ -1118,7 +1118,7 @@ var _ = Describe("CloudProvider", func() { // doesn't work well with unexported fields, like the ones that are present in resource.Quantity It("should return drifted when updating blockDeviceMapping volumeSize", func() { nodeClass.Spec.BlockDeviceMappings[0].EBS.VolumeSize = resource.NewScaledQuantity(10, resource.Giga) - nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.Hash()}) + nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion("us-west-2")}) ExpectApplied(ctx, env.Client, nodeClass) isDrifted, err := cloudProvider.IsDrifted(ctx, nodeClaim) @@ -1133,7 +1133,7 @@ var _ = Describe("CloudProvider", func() { Expect(isDrifted).To(BeEmpty()) Expect(mergo.Merge(nodeClass, changes, mergo.WithOverride)) - nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.Hash()}) + nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion("us-west-2")}) ExpectApplied(ctx, env.Client, nodeClass) isDrifted, err = cloudProvider.IsDrifted(ctx, nodeClaim) diff --git a/pkg/controllers/controllers.go b/pkg/controllers/controllers.go index 16c1eeee5db1..02c9c241fa7a 100644 --- a/pkg/controllers/controllers.go +++ b/pkg/controllers/controllers.go @@ -89,7 +89,7 @@ func NewControllers( amiResolver amifamily.Resolver, ) []controller.Controller { controllers := []controller.Controller{ - nodeclasshash.NewController(kubeClient), + nodeclasshash.NewController(kubeClient, cfg.Region), nodeclass.NewController(clk, kubeClient, cloudProvider, recorder, cfg.Region, subnetProvider, securityGroupProvider, amiProvider, instanceProfileProvider, instanceTypeProvider, launchTemplateProvider, capacityReservationProvider, ec2api, validationCache, recreationCache, amiResolver, options.FromContext(ctx).DisableDryRun), nodeclaimgarbagecollection.NewController(kubeClient, cloudProvider), nodeclaimtagging.NewController(kubeClient, cloudProvider, instanceProvider), diff --git a/pkg/controllers/nodeclass/hash/controller.go b/pkg/controllers/nodeclass/hash/controller.go index 38f4ca695b6c..3fdb7abf4b90 100644 --- a/pkg/controllers/nodeclass/hash/controller.go +++ b/pkg/controllers/nodeclass/hash/controller.go @@ -36,11 +36,13 @@ import ( type Controller struct { kubeClient client.Client + region string } -func NewController(kubeClient client.Client) *Controller { +func NewController(kubeClient client.Client, region string) *Controller { return &Controller{ kubeClient: kubeClient, + region: region, } } @@ -55,7 +57,7 @@ func (c *Controller) Reconcile(ctx context.Context, nodeClass *v1.EC2NodeClass) } } nodeClass.Annotations = lo.Assign(nodeClass.Annotations, map[string]string{ - v1.AnnotationEC2NodeClassHash: nodeClass.Hash(), + v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion(c.region), v1.AnnotationEC2NodeClassHashVersion: v1.EC2NodeClassHashVersion, }) @@ -103,7 +105,7 @@ func (c *Controller) updateNodeClaimHash(ctx context.Context, nodeClass *v1.EC2N // Since the hashing mechanism has changed we will not be able to determine if the drifted status of the NodeClaim has changed if nc.StatusConditions().Get(karpv1.ConditionTypeDrifted) == nil { nc.Annotations = lo.Assign(nc.Annotations, map[string]string{ - v1.AnnotationEC2NodeClassHash: nodeClass.Hash(), + v1.AnnotationEC2NodeClassHash: nodeClass.HashForRegion(c.region), }) } diff --git a/pkg/controllers/nodeclass/hash/suite_test.go b/pkg/controllers/nodeclass/hash/suite_test.go index eebd2cecabd9..5f6d837ec5d9 100644 --- a/pkg/controllers/nodeclass/hash/suite_test.go +++ b/pkg/controllers/nodeclass/hash/suite_test.go @@ -60,7 +60,7 @@ var _ = BeforeSuite(func() { ctx = options.ToContext(ctx, test.Options()) awsEnv = test.NewEnvironment(ctx, env) - hashController = hash.NewController(env.Client) + hashController = hash.NewController(env.Client, "us-west-2") }) var _ = AfterSuite(func() { @@ -119,7 +119,7 @@ var _ = Describe("NodeClass Hash Controller", func() { ExpectObjectReconciled(ctx, env.Client, hashController, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) - expectedHash := nodeClass.Hash() + expectedHash := nodeClass.HashForRegion("us-west-2") Expect(nodeClass.ObjectMeta.Annotations[v1.AnnotationEC2NodeClassHash]).To(Equal(expectedHash)) Expect(mergo.Merge(nodeClass, changes, mergo.WithOverride)).To(Succeed()) @@ -128,7 +128,7 @@ var _ = Describe("NodeClass Hash Controller", func() { ExpectObjectReconciled(ctx, env.Client, hashController, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) - expectedHashTwo := nodeClass.Hash() + expectedHashTwo := nodeClass.HashForRegion("us-west-2") Expect(nodeClass.Annotations[v1.AnnotationEC2NodeClassHash]).To(Equal(expectedHashTwo)) Expect(expectedHash).ToNot(Equal(expectedHashTwo)) @@ -145,7 +145,7 @@ var _ = Describe("NodeClass Hash Controller", func() { ExpectObjectReconciled(ctx, env.Client, hashController, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) - expectedHash := nodeClass.Hash() + expectedHash := nodeClass.HashForRegion("us-west-2") Expect(nodeClass.Annotations[v1.AnnotationEC2NodeClassHash]).To(Equal(expectedHash)) nodeClass.Spec.SubnetSelectorTerms = []v1.SubnetSelectorTerm{ @@ -179,7 +179,7 @@ var _ = Describe("NodeClass Hash Controller", func() { ExpectObjectReconciled(ctx, env.Client, hashController, nodeClass) nodeClass = ExpectExists(ctx, env.Client, nodeClass) - expectedHash := nodeClass.Hash() + expectedHash := nodeClass.HashForRegion("us-west-2") // Expect ec2nodeclass-hash on the NodeClass to be updated Expect(nodeClass.Annotations).To(HaveKeyWithValue(v1.AnnotationEC2NodeClassHash, expectedHash)) Expect(nodeClass.Annotations).To(HaveKeyWithValue(v1.AnnotationEC2NodeClassHashVersion, v1.EC2NodeClassHashVersion)) @@ -229,7 +229,7 @@ var _ = Describe("NodeClass Hash Controller", func() { nodeClaimOne = ExpectExists(ctx, env.Client, nodeClaimOne) nodeClaimTwo = ExpectExists(ctx, env.Client, nodeClaimTwo) - expectedHash := nodeClass.Hash() + expectedHash := nodeClass.HashForRegion("us-west-2") // Expect ec2nodeclass-hash on the NodeClaims to be updated Expect(nodeClaimOne.Annotations).To(HaveKeyWithValue(v1.AnnotationEC2NodeClassHash, expectedHash)) Expect(nodeClaimOne.Annotations).To(HaveKeyWithValue(v1.AnnotationEC2NodeClassHashVersion, v1.EC2NodeClassHashVersion)) @@ -263,7 +263,7 @@ var _ = Describe("NodeClass Hash Controller", func() { nodeClass = ExpectExists(ctx, env.Client, nodeClass) nodeClaim = ExpectExists(ctx, env.Client, nodeClaim) - expectedHash := nodeClass.Hash() + expectedHash := nodeClass.HashForRegion("us-west-2") // Expect ec2nodeclass-hash on the NodeClass to be updated Expect(nodeClass.Annotations).To(HaveKeyWithValue(v1.AnnotationEC2NodeClassHash, expectedHash)) diff --git a/pkg/providers/amifamily/resolver.go b/pkg/providers/amifamily/resolver.go index 98957ce05899..f6f9affa54c6 100644 --- a/pkg/providers/amifamily/resolver.go +++ b/pkg/providers/amifamily/resolver.go @@ -25,7 +25,6 @@ import ( "github.com/samber/lo" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/apimachinery/pkg/util/sets" karpv1 "sigs.k8s.io/karpenter/pkg/apis/v1" @@ -33,6 +32,7 @@ import ( "sigs.k8s.io/karpenter/pkg/scheduling" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" + awssdk "github.com/aws/karpenter-provider-aws/pkg/aws" "github.com/aws/karpenter-provider-aws/pkg/providers/amifamily/bootstrap" "github.com/aws/karpenter-provider-aws/pkg/providers/ssm" ) @@ -278,14 +278,7 @@ func (r DefaultResolver) resolveLaunchTemplates( if len(capacityReservationIDs) == 0 { capacityReservationIDs = append(capacityReservationIDs, "") } - httpProtocolUnsupportedRegions := sets.New[string]( - "us-iso-east-1", - "us-iso-west-1", - "us-isob-east-1", - "us-isob-west-1", - "us-isof-south-1", - "us-isof-east-1", - ) + httpProtocolUnsupportedRegions := awssdk.HTTPProtocolUnsupportedRegions return lo.Map(capacityReservationIDs, func(id string, _ int) *LaunchTemplate { resolved := &LaunchTemplate{ Options: options, diff --git a/pkg/providers/instance/instance.go b/pkg/providers/instance/instance.go index d93275484fbf..55364d295bb2 100644 --- a/pkg/providers/instance/instance.go +++ b/pkg/providers/instance/instance.go @@ -82,6 +82,7 @@ type Provider interface { List(context.Context) ([]*Instance, error) Delete(context.Context, string) error CreateTags(context.Context, string, map[string]string) error + Region() string } type options struct { @@ -582,6 +583,10 @@ func instancesFromOutput(ctx context.Context, out *ec2.DescribeInstancesOutput) return lo.Map(instances, func(i ec2types.Instance, _ int) *Instance { return NewInstance(ctx, i) }), nil } +func (p *DefaultProvider) Region() string { + return p.region +} + func combineFleetErrors(fleetErrs []ec2types.CreateFleetError) (errs error) { unique := sets.NewString() for _, err := range fleetErrs { diff --git a/test/suites/drift/suite_test.go b/test/suites/drift/suite_test.go index 47e3ec2860c1..e72e37182c98 100644 --- a/test/suites/drift/suite_test.go +++ b/test/suites/drift/suite_test.go @@ -467,7 +467,7 @@ var _ = Describe("Drift", Ordered, func() { env.EventuallyExpectHealthyPodCount(selector, numPods) nodeClaim := env.EventuallyExpectCreatedNodeClaimCount("==", 1)[0] nodeClass = env.ExpectExists(nodeClass).(*v1.EC2NodeClass) - expectedHash := nodeClass.Hash() + expectedHash := nodeClass.HashForRegion("us-west-2") By(fmt.Sprintf("expect nodeclass %s and nodeclaim %s to contain %s and %s annotations", nodeClass.Name, nodeClaim.Name, v1.AnnotationEC2NodeClassHash, v1.AnnotationEC2NodeClassHashVersion)) Eventually(func(g Gomega) { @@ -497,7 +497,7 @@ var _ = Describe("Drift", Ordered, func() { // The nodeclaim will need to be updated first, as the hash controller will only be triggered on changes to the nodeclass env.ExpectUpdated(nodeClaim, nodeClass) - expectedHash = nodeClass.Hash() + expectedHash = nodeClass.HashForRegion("us-west-2") // Expect all nodeclaims not to be drifted and contain an updated `nodepool-hash` and `nodepool-hash-version` annotation Eventually(func(g Gomega) { diff --git a/test/suites/integration/hash_test.go b/test/suites/integration/hash_test.go index 6f48ac4eee85..41bf81c4985b 100644 --- a/test/suites/integration/hash_test.go +++ b/test/suites/integration/hash_test.go @@ -34,7 +34,7 @@ var _ = Describe("CRD Hash", func() { hash, found := nc.Annotations[v1.AnnotationEC2NodeClassHash] g.Expect(found).To(BeTrue()) - g.Expect(hash).To(Equal(nc.Hash())) + g.Expect(hash).To(Equal(nc.HashForRegion("us-west-2"))) }) }) })