From c2be457792bd2ee461f40f035f7858ad2b395b2f Mon Sep 17 00:00:00 2001 From: edibble21 <85638465+edibble21@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:50:19 -0700 Subject: [PATCH] chore: Instance Profile V2 migration (#7097) Co-authored-by: Amanuel Engeda <74629455+engedaam@users.noreply.github.com> --- go.mod | 18 +++ go.sum | 30 +++++ pkg/aws/sdk.go | 32 +++++ .../nodeclass/status/instanceprofile_test.go | 32 ++--- .../nodeclass/termination/suite_test.go | 32 ++--- pkg/errors/errors.go | 30 +++++ pkg/fake/iamapi.go | 113 ++++++++++++------ pkg/operator/operator.go | 19 ++- .../instanceprofile/instanceprofile.go | 49 ++++---- 9 files changed, 257 insertions(+), 98 deletions(-) create mode 100644 pkg/aws/sdk.go diff --git a/go.mod b/go.mod index 2d2a4fc11e0d..3f1261e50056 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,13 @@ require ( github.com/PuerkitoBio/goquery v1.10.0 github.com/avast/retry-go v3.0.0+incompatible github.com/aws/aws-sdk-go v1.55.5 + github.com/aws/aws-sdk-go-v2 v1.31.0 + github.com/aws/aws-sdk-go-v2/config v1.26.6 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.177.3 + github.com/aws/aws-sdk-go-v2/service/iam v1.36.2 github.com/aws/karpenter-provider-aws/tools/kompat v0.0.0-20240410220356-6b868db24881 + github.com/aws/smithy-go v1.21.0 github.com/awslabs/amazon-eks-ami/nodeadm v0.0.0-20240229193347-cfab22a10647 github.com/awslabs/operatorpkg v0.0.0-20240920182301-771460b3160b github.com/go-logr/zapr v1.3.0 @@ -35,6 +41,18 @@ require ( sigs.k8s.io/yaml v1.4.0 ) +require ( + github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect +) + require ( github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect diff --git a/go.sum b/go.sum index daae6e9b312f..b462ef829585 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,38 @@ github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHS github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= +github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= +github.com/aws/aws-sdk-go-v2/config v1.26.6 h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o= +github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.177.3 h1:dqdCh1M8h+j8OGNUpxTs7eBPFr6lOdLpdlE6IPLLSq4= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.177.3/go.mod h1:TFSALWR7Xs7+KyMM87ZAYxncKFBvzEt2rpK/BJCH2ps= +github.com/aws/aws-sdk-go-v2/service/iam v1.36.2 h1:2/kSYD8hfRU/q1HbgSzZ4PGiDmzDwtPSYgJq4yxF6bs= +github.com/aws/aws-sdk-go-v2/service/iam v1.36.2/go.mod h1:HSvujsK8xeEHMIB18oMXjSfqaN9cVqpo/MtHJIksQRk= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 h1:rfprUlsdzgl7ZL2KlXiUAoJnI/VxfHCvDFr2QDFj6u4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19/go.mod h1:SCWkEdRq8/7EK60NcvvQ6NXKuTcchAD4ROAsC37VEZE= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= github.com/aws/karpenter-provider-aws/tools/kompat v0.0.0-20240410220356-6b868db24881 h1:m9rhsGhdepdQV96tZgfy68oU75AWAjOH8u65OefTjwA= github.com/aws/karpenter-provider-aws/tools/kompat v0.0.0-20240410220356-6b868db24881/go.mod h1:+Mk5k0b6HpKobxNq+B56DOhZ+I/NiPhd5MIBhQMSTSs= +github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA= +github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/awslabs/amazon-eks-ami/nodeadm v0.0.0-20240229193347-cfab22a10647 h1:8yRBVsjGmI7qQsPWtIrbWP+XfwHO9Wq7gdLVzjqiZFs= github.com/awslabs/amazon-eks-ami/nodeadm v0.0.0-20240229193347-cfab22a10647/go.mod h1:9NafTAUHL0FlMeL6Cu5PXnMZ1q/LnC9X2emLXHsVbM8= github.com/awslabs/operatorpkg v0.0.0-20240920182301-771460b3160b h1:aG1+YRmKIf5nLTZJNhw1NmuxvjUprWYyluqJ2jmVqiU= diff --git a/pkg/aws/sdk.go b/pkg/aws/sdk.go new file mode 100644 index 000000000000..24d15fd253cf --- /dev/null +++ b/pkg/aws/sdk.go @@ -0,0 +1,32 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sdk + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/service/iam" +) + +type IAMAPI interface { + // IAM Methods + GetInstanceProfile(context.Context, *iam.GetInstanceProfileInput, ...func(*iam.Options)) (*iam.GetInstanceProfileOutput, error) + CreateInstanceProfile(context.Context, *iam.CreateInstanceProfileInput, ...func(*iam.Options)) (*iam.CreateInstanceProfileOutput, error) + DeleteInstanceProfile(context.Context, *iam.DeleteInstanceProfileInput, ...func(*iam.Options)) (*iam.DeleteInstanceProfileOutput, error) + AddRoleToInstanceProfile(context.Context, *iam.AddRoleToInstanceProfileInput, ...func(*iam.Options)) (*iam.AddRoleToInstanceProfileOutput, error) + TagInstanceProfile(context.Context, *iam.TagInstanceProfileInput, ...func(*iam.Options)) (*iam.TagInstanceProfileOutput, error) + RemoveRoleFromInstanceProfile(context.Context, *iam.RemoveRoleFromInstanceProfileInput, ...func(*iam.Options)) (*iam.RemoveRoleFromInstanceProfileOutput, error) + UntagInstanceProfile(context.Context, *iam.UntagInstanceProfileInput, ...func(*iam.Options)) (*iam.UntagInstanceProfileOutput, error) +} diff --git a/pkg/controllers/nodeclass/status/instanceprofile_test.go b/pkg/controllers/nodeclass/status/instanceprofile_test.go index 75f3fa7df1ad..2f0f342eb863 100644 --- a/pkg/controllers/nodeclass/status/instanceprofile_test.go +++ b/pkg/controllers/nodeclass/status/instanceprofile_test.go @@ -17,8 +17,8 @@ package status_test import ( "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/iam" + "github.com/aws/aws-sdk-go-v2/aws" + iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" "github.com/samber/lo" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" @@ -45,9 +45,9 @@ var _ = Describe("NodeClass InstanceProfile Status Controller", func() { Expect(awsEnv.IAMAPI.InstanceProfiles[profileName].Roles).To(HaveLen(1)) Expect(*awsEnv.IAMAPI.InstanceProfiles[profileName].Roles[0].RoleName).To(Equal("test-role")) Expect(awsEnv.IAMAPI.InstanceProfiles[profileName].Tags).To(ContainElements( - &iam.Tag{Key: lo.ToPtr(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: lo.ToPtr("owned")}, - &iam.Tag{Key: lo.ToPtr(v1.LabelNodeClass), Value: lo.ToPtr(nodeClass.Name)}, - &iam.Tag{Key: lo.ToPtr(v1.EKSClusterNameTagKey), Value: lo.ToPtr(options.FromContext(ctx).ClusterName)}, + iamtypes.Tag{Key: lo.ToPtr(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: lo.ToPtr("owned")}, + iamtypes.Tag{Key: lo.ToPtr(v1.LabelNodeClass), Value: lo.ToPtr(nodeClass.Name)}, + iamtypes.Tag{Key: lo.ToPtr(v1.EKSClusterNameTagKey), Value: lo.ToPtr(options.FromContext(ctx).ClusterName)}, )) nodeClass = ExpectExists(ctx, env.Client, nodeClass) @@ -55,7 +55,7 @@ var _ = Describe("NodeClass InstanceProfile Status Controller", func() { Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeInstanceProfileReady)).To(BeTrue()) }) It("should add the role to the instance profile when it exists without a role", func() { - awsEnv.IAMAPI.InstanceProfiles = map[string]*iam.InstanceProfile{ + awsEnv.IAMAPI.InstanceProfiles = map[string]*iamtypes.InstanceProfile{ profileName: { InstanceProfileId: aws.String(fake.InstanceProfileID()), InstanceProfileName: aws.String(profileName), @@ -75,11 +75,11 @@ var _ = Describe("NodeClass InstanceProfile Status Controller", func() { Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeInstanceProfileReady)).To(BeTrue()) }) It("should update the role for the instance profile when the wrong role exists", func() { - awsEnv.IAMAPI.InstanceProfiles = map[string]*iam.InstanceProfile{ + awsEnv.IAMAPI.InstanceProfiles = map[string]*iamtypes.InstanceProfile{ profileName: { InstanceProfileId: aws.String(fake.InstanceProfileID()), InstanceProfileName: aws.String(profileName), - Roles: []*iam.Role{ + Roles: []iamtypes.Role{ { RoleName: aws.String("other-role"), }, @@ -100,16 +100,16 @@ var _ = Describe("NodeClass InstanceProfile Status Controller", func() { Expect(nodeClass.StatusConditions().IsTrue(v1.ConditionTypeInstanceProfileReady)).To(BeTrue()) }) It("should add the eks:eks-cluster-name tag when the tag doesn't exist", func() { - awsEnv.IAMAPI.InstanceProfiles = map[string]*iam.InstanceProfile{ + awsEnv.IAMAPI.InstanceProfiles = map[string]*iamtypes.InstanceProfile{ profileName: { InstanceProfileId: aws.String(fake.InstanceProfileID()), InstanceProfileName: aws.String(profileName), - Roles: []*iam.Role{ + Roles: []iamtypes.Role{ { RoleName: aws.String("other-role"), }, }, - Tags: []*iam.Tag{ + Tags: []iamtypes.Tag{ { Key: lo.ToPtr(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: lo.ToPtr("owned"), @@ -127,17 +127,17 @@ var _ = Describe("NodeClass InstanceProfile Status Controller", func() { Expect(awsEnv.IAMAPI.InstanceProfiles).To(HaveLen(1)) Expect(awsEnv.IAMAPI.InstanceProfiles[profileName].Tags).To(ContainElements( - &iam.Tag{Key: lo.ToPtr(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: lo.ToPtr("owned")}, - &iam.Tag{Key: lo.ToPtr(v1.LabelNodeClass), Value: lo.ToPtr(nodeClass.Name)}, - &iam.Tag{Key: lo.ToPtr(v1.EKSClusterNameTagKey), Value: lo.ToPtr(options.FromContext(ctx).ClusterName)}, + iamtypes.Tag{Key: lo.ToPtr(fmt.Sprintf("kubernetes.io/cluster/%s", options.FromContext(ctx).ClusterName)), Value: lo.ToPtr("owned")}, + iamtypes.Tag{Key: lo.ToPtr(v1.LabelNodeClass), Value: lo.ToPtr(nodeClass.Name)}, + iamtypes.Tag{Key: lo.ToPtr(v1.EKSClusterNameTagKey), Value: lo.ToPtr(options.FromContext(ctx).ClusterName)}, )) }) It("should not call CreateInstanceProfile or AddRoleToInstanceProfile when instance profile exists with correct role", func() { - awsEnv.IAMAPI.InstanceProfiles = map[string]*iam.InstanceProfile{ + awsEnv.IAMAPI.InstanceProfiles = map[string]*iamtypes.InstanceProfile{ profileName: { InstanceProfileId: aws.String(fake.InstanceProfileID()), InstanceProfileName: aws.String(profileName), - Roles: []*iam.Role{ + Roles: []iamtypes.Role{ { RoleName: aws.String("test-role"), }, diff --git a/pkg/controllers/nodeclass/termination/suite_test.go b/pkg/controllers/nodeclass/termination/suite_test.go index c24f42121d6f..adbde65f02ca 100644 --- a/pkg/controllers/nodeclass/termination/suite_test.go +++ b/pkg/controllers/nodeclass/termination/suite_test.go @@ -22,9 +22,13 @@ import ( "sigs.k8s.io/karpenter/pkg/test/v1alpha1" - "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go-v2/aws" + ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" + iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" + + //used for launch template tests until they are migrated "github.com/aws/aws-sdk-go/service/ec2" - "github.com/aws/aws-sdk-go/service/iam" + "github.com/awslabs/operatorpkg/object" "github.com/samber/lo" "k8s.io/client-go/tools/record" @@ -109,7 +113,7 @@ var _ = Describe("NodeClass Termination", func() { }) It("should not delete the NodeClass if launch template deletion fails", func() { launchTemplateName := aws.String(fake.LaunchTemplateName()) - awsEnv.EC2API.LaunchTemplates.Store(launchTemplateName, &ec2.LaunchTemplate{LaunchTemplateName: launchTemplateName, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{&ec2.Tag{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}}}) + awsEnv.EC2API.LaunchTemplates.Store(launchTemplateName, &ec2types.LaunchTemplate{LaunchTemplateName: launchTemplateName, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []ec2types.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}}}) _, ok := awsEnv.EC2API.LaunchTemplates.Load(launchTemplateName) Expect(ok).To(BeTrue()) controllerutil.AddFinalizer(nodeClass, v1.TerminationFinalizer) @@ -123,7 +127,7 @@ var _ = Describe("NodeClass Termination", func() { }) It("should not delete the launch template not associated with the nodeClass", func() { launchTemplateName := aws.String(fake.LaunchTemplateName()) - awsEnv.EC2API.LaunchTemplates.Store(launchTemplateName, &ec2.LaunchTemplate{LaunchTemplateName: launchTemplateName, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{&ec2.Tag{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}}}) + awsEnv.EC2API.LaunchTemplates.Store(launchTemplateName, &ec2.LaunchTemplate{LaunchTemplateName: launchTemplateName, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}}}) _, ok := awsEnv.EC2API.LaunchTemplates.Load(launchTemplateName) Expect(ok).To(BeTrue()) controllerutil.AddFinalizer(nodeClass, v1.TerminationFinalizer) @@ -138,9 +142,9 @@ var _ = Describe("NodeClass Termination", func() { }) It("should succeed to delete the launch template", func() { ltName1 := aws.String(fake.LaunchTemplateName()) - awsEnv.EC2API.LaunchTemplates.Store(ltName1, &ec2.LaunchTemplate{LaunchTemplateName: ltName1, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{&ec2.Tag{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}, {Key: aws.String("karpenter.k8s.aws/ec2nodeclass"), Value: aws.String(nodeClass.Name)}}}) + awsEnv.EC2API.LaunchTemplates.Store(ltName1, &ec2.LaunchTemplate{LaunchTemplateName: ltName1, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}, {Key: aws.String("karpenter.k8s.aws/ec2nodeclass"), Value: aws.String(nodeClass.Name)}}}) ltName2 := aws.String(fake.LaunchTemplateName()) - awsEnv.EC2API.LaunchTemplates.Store(ltName2, &ec2.LaunchTemplate{LaunchTemplateName: ltName2, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{&ec2.Tag{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}, {Key: aws.String("karpenter.k8s.aws/ec2nodeclass"), Value: aws.String(nodeClass.Name)}}}) + awsEnv.EC2API.LaunchTemplates.Store(ltName2, &ec2.LaunchTemplate{LaunchTemplateName: ltName2, LaunchTemplateId: aws.String(fake.LaunchTemplateID()), Tags: []*ec2.Tag{{Key: aws.String("karpenter.k8s.aws/cluster"), Value: aws.String("test-cluster")}, {Key: aws.String("karpenter.k8s.aws/ec2nodeclass"), Value: aws.String(nodeClass.Name)}}}) _, ok := awsEnv.EC2API.LaunchTemplates.Load(ltName1) Expect(ok).To(BeTrue()) _, ok = awsEnv.EC2API.LaunchTemplates.Load(ltName2) @@ -148,7 +152,6 @@ var _ = Describe("NodeClass Termination", func() { controllerutil.AddFinalizer(nodeClass, v1.TerminationFinalizer) ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, terminationController, nodeClass) - Expect(env.Client.Delete(ctx, nodeClass)).To(Succeed()) ExpectObjectReconciled(ctx, env.Client, terminationController, nodeClass) _, ok = awsEnv.EC2API.LaunchTemplates.Load(ltName1) @@ -158,10 +161,10 @@ var _ = Describe("NodeClass Termination", func() { ExpectNotFound(ctx, env.Client, nodeClass) }) It("should succeed to delete the instance profile with no NodeClaims", func() { - awsEnv.IAMAPI.InstanceProfiles = map[string]*iam.InstanceProfile{ + awsEnv.IAMAPI.InstanceProfiles = map[string]*iamtypes.InstanceProfile{ profileName: { InstanceProfileName: aws.String(profileName), - Roles: []*iam.Role{ + Roles: []iamtypes.Role{ { RoleId: aws.String(fake.RoleID()), RoleName: aws.String(nodeClass.Spec.Role), @@ -180,7 +183,7 @@ var _ = Describe("NodeClass Termination", func() { ExpectNotFound(ctx, env.Client, nodeClass) }) It("should succeed to delete the instance profile when no roles exist with no NodeClaims", func() { - awsEnv.IAMAPI.InstanceProfiles = map[string]*iam.InstanceProfile{ + awsEnv.IAMAPI.InstanceProfiles = map[string]*iamtypes.InstanceProfile{ profileName: { InstanceProfileName: aws.String(profileName), }, @@ -189,7 +192,6 @@ var _ = Describe("NodeClass Termination", func() { ExpectApplied(ctx, env.Client, nodeClass) ExpectObjectReconciled(ctx, env.Client, terminationController, nodeClass) Expect(awsEnv.IAMAPI.InstanceProfiles).To(HaveLen(1)) - Expect(env.Client.Delete(ctx, nodeClass)).To(Succeed()) ExpectObjectReconciled(ctx, env.Client, terminationController, nodeClass) Expect(awsEnv.IAMAPI.InstanceProfiles).To(HaveLen(0)) @@ -220,10 +222,10 @@ var _ = Describe("NodeClass Termination", func() { ExpectApplied(ctx, env.Client, nc) nodeClaims = append(nodeClaims, nc) } - awsEnv.IAMAPI.InstanceProfiles = map[string]*iam.InstanceProfile{ + awsEnv.IAMAPI.InstanceProfiles = map[string]*iamtypes.InstanceProfile{ profileName: { InstanceProfileName: aws.String(profileName), - Roles: []*iam.Role{ + Roles: []iamtypes.Role{ { RoleId: aws.String(fake.RoleID()), RoleName: aws.String(nodeClass.Spec.Role), @@ -258,10 +260,10 @@ var _ = Describe("NodeClass Termination", func() { ExpectNotFound(ctx, env.Client, nodeClass) }) It("should not call the IAM API when deleting a NodeClass with an instanceProfile specified", func() { - awsEnv.IAMAPI.InstanceProfiles = map[string]*iam.InstanceProfile{ + awsEnv.IAMAPI.InstanceProfiles = map[string]*iamtypes.InstanceProfile{ profileName: { InstanceProfileName: aws.String("test-instance-profile"), - Roles: []*iam.Role{ + Roles: []iamtypes.Role{ { RoleId: aws.String(fake.RoleID()), RoleName: aws.String("fake-role"), diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go index 5267aad4672d..8e759cfd6d20 100644 --- a/pkg/errors/errors.go +++ b/pkg/errors/errors.go @@ -17,6 +17,10 @@ package errors import ( "errors" + //v2 imports + "github.com/aws/smithy-go" + + //V1 imports "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" @@ -36,6 +40,9 @@ var ( "InvalidLaunchTemplateId.NotFound", sqs.ErrCodeQueueDoesNotExist, iam.ErrCodeNoSuchEntityException, + + //v2 error codes + "NoSuchEntityException", ) alreadyExistsErrorCodes = sets.New[string]( iam.ErrCodeEntityAlreadyExistsException, @@ -107,3 +114,26 @@ func IsLaunchTemplateNotFound(err error) bool { } return false } + +//V2 will become new full file when every provider is migrated + +// IsNotFound returns true if the err is an AWS error (even if it's +// wrapped) and is a known to mean "not found" (as opposed to a more +// serious or unexpected error) +func IsNotFoundV2(err error) bool { + if err == nil { + return false + } + var apiErr smithy.APIError + if errors.As(err, &apiErr) { + return notFoundErrorCodes.Has(apiErr.ErrorCode()) + } + return false +} + +func IgnoreNotFoundV2(err error) error { + if IsNotFoundV2(err) { + return nil + } + return err +} diff --git a/pkg/fake/iamapi.go b/pkg/fake/iamapi.go index 54e377b74532..dd5588232683 100644 --- a/pkg/fake/iamapi.go +++ b/pkg/fake/iamapi.go @@ -20,12 +20,13 @@ import ( "sync" "time" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/service/iam" - "github.com/aws/aws-sdk-go/service/iam/iamiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/iam" + iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" + "github.com/aws/smithy-go" "github.com/samber/lo" + + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" ) const () @@ -44,121 +45,155 @@ type IAMAPIBehavior struct { type IAMAPI struct { sync.Mutex - iamiface.IAMAPI + sdk.IAMAPI IAMAPIBehavior - InstanceProfiles map[string]*iam.InstanceProfile + InstanceProfiles map[string]*iamtypes.InstanceProfile } func NewIAMAPI() *IAMAPI { - return &IAMAPI{InstanceProfiles: map[string]*iam.InstanceProfile{}} + return &IAMAPI{InstanceProfiles: map[string]*iamtypes.InstanceProfile{}} } -// Reset must be called between tests otherwise tests will pollute -// each other. func (s *IAMAPI) Reset() { s.GetInstanceProfileBehavior.Reset() s.CreateInstanceProfileBehavior.Reset() s.DeleteInstanceProfileBehavior.Reset() s.AddRoleToInstanceProfileBehavior.Reset() s.RemoveRoleFromInstanceProfileBehavior.Reset() - s.InstanceProfiles = map[string]*iam.InstanceProfile{} + s.InstanceProfiles = map[string]*iamtypes.InstanceProfile{} } -func (s *IAMAPI) GetInstanceProfileWithContext(_ context.Context, input *iam.GetInstanceProfileInput, _ ...request.Option) (*iam.GetInstanceProfileOutput, error) { +func (s *IAMAPI) GetInstanceProfile(_ context.Context, input *iam.GetInstanceProfileInput, _ ...func(*iam.Options)) (*iam.GetInstanceProfileOutput, error) { return s.GetInstanceProfileBehavior.Invoke(input, func(*iam.GetInstanceProfileInput) (*iam.GetInstanceProfileOutput, error) { s.Lock() defer s.Unlock() - if i, ok := s.InstanceProfiles[aws.StringValue(input.InstanceProfileName)]; ok { + if i, ok := s.InstanceProfiles[aws.ToString(input.InstanceProfileName)]; ok { return &iam.GetInstanceProfileOutput{InstanceProfile: i}, nil } - return nil, awserr.New(iam.ErrCodeNoSuchEntityException, fmt.Sprintf("Instance Profile %s cannot be found", aws.StringValue(input.InstanceProfileName)), nil) + return nil, &smithy.GenericAPIError{ + Code: "NoSuchEntityException", + Message: fmt.Sprintf("Instance Profile %s cannot be found", + aws.ToString(input.InstanceProfileName)), + } }) } -func (s *IAMAPI) CreateInstanceProfileWithContext(_ context.Context, input *iam.CreateInstanceProfileInput, _ ...request.Option) (*iam.CreateInstanceProfileOutput, error) { +func (s *IAMAPI) CreateInstanceProfile(_ context.Context, input *iam.CreateInstanceProfileInput, _ ...func(*iam.Options)) (*iam.CreateInstanceProfileOutput, error) { return s.CreateInstanceProfileBehavior.Invoke(input, func(output *iam.CreateInstanceProfileInput) (*iam.CreateInstanceProfileOutput, error) { s.Lock() defer s.Unlock() - if _, ok := s.InstanceProfiles[aws.StringValue(input.InstanceProfileName)]; ok { - return nil, awserr.New(iam.ErrCodeEntityAlreadyExistsException, fmt.Sprintf("Instance Profile %s already exists", aws.StringValue(input.InstanceProfileName)), nil) + if _, ok := s.InstanceProfiles[aws.ToString(input.InstanceProfileName)]; ok { + return nil, &smithy.GenericAPIError{ + Code: "EntityAlreadyExistsException", + Message: fmt.Sprintf("Instance Profile %s already exists", + aws.ToString(input.InstanceProfileName)), + } } - instanceProfile := &iam.InstanceProfile{ + instanceProfile := &iamtypes.InstanceProfile{ CreateDate: aws.Time(time.Now()), InstanceProfileId: aws.String(InstanceProfileID()), InstanceProfileName: input.InstanceProfileName, Path: input.Path, Tags: input.Tags, } - s.InstanceProfiles[aws.StringValue(input.InstanceProfileName)] = instanceProfile + s.InstanceProfiles[aws.ToString(input.InstanceProfileName)] = instanceProfile return &iam.CreateInstanceProfileOutput{InstanceProfile: instanceProfile}, nil }) } -func (s *IAMAPI) DeleteInstanceProfileWithContext(_ context.Context, input *iam.DeleteInstanceProfileInput, _ ...request.Option) (*iam.DeleteInstanceProfileOutput, error) { +func (s *IAMAPI) DeleteInstanceProfile(_ context.Context, input *iam.DeleteInstanceProfileInput, _ ...func(*iam.Options)) (*iam.DeleteInstanceProfileOutput, error) { return s.DeleteInstanceProfileBehavior.Invoke(input, func(output *iam.DeleteInstanceProfileInput) (*iam.DeleteInstanceProfileOutput, error) { s.Lock() defer s.Unlock() - if i, ok := s.InstanceProfiles[aws.StringValue(input.InstanceProfileName)]; ok { + if i, ok := s.InstanceProfiles[aws.ToString(input.InstanceProfileName)]; ok { if len(i.Roles) > 0 { - return nil, awserr.New(iam.ErrCodeDeleteConflictException, "Cannot delete entity, must remove roles from instance profile first.", nil) + return nil, &smithy.GenericAPIError{ + Code: "DeleteConflictException", + Message: fmt.Sprintf("Instance Profile %s has roles and cannot be deleted", + aws.ToString(input.InstanceProfileName)), + } } - delete(s.InstanceProfiles, aws.StringValue(input.InstanceProfileName)) + delete(s.InstanceProfiles, aws.ToString(input.InstanceProfileName)) return &iam.DeleteInstanceProfileOutput{}, nil } - return nil, awserr.New(iam.ErrCodeNoSuchEntityException, fmt.Sprintf("Instance Profile %s cannot be found", aws.StringValue(input.InstanceProfileName)), nil) + return nil, &smithy.GenericAPIError{ + Code: "NoSuchEntityException", + Message: fmt.Sprintf("Instance Profile %s cannot be found", + aws.ToString(input.InstanceProfileName)), + } }) } -func (s *IAMAPI) TagInstanceProfileWithContext(_ context.Context, input *iam.TagInstanceProfileInput, _ ...request.Option) (*iam.TagInstanceProfileOutput, error) { +func (s *IAMAPI) TagInstanceProfile(_ context.Context, input *iam.TagInstanceProfileInput, _ ...func(*iam.Options)) (*iam.TagInstanceProfileOutput, error) { return s.TagInstanceProfileBehavior.Invoke(input, func(output *iam.TagInstanceProfileInput) (*iam.TagInstanceProfileOutput, error) { s.Lock() defer s.Unlock() - if profile, ok := s.InstanceProfiles[aws.StringValue(input.InstanceProfileName)]; ok { - profile.Tags = lo.UniqBy(append(input.Tags, profile.Tags...), func(t *iam.Tag) string { + if profile, ok := s.InstanceProfiles[aws.ToString(input.InstanceProfileName)]; ok { + profile.Tags = lo.UniqBy(append(input.Tags, profile.Tags...), func(t iamtypes.Tag) string { return lo.FromPtr(t.Key) }) return nil, nil } - return nil, awserr.New(iam.ErrCodeNoSuchEntityException, fmt.Sprintf("Instance Profile %s cannot be found", aws.StringValue(input.InstanceProfileName)), nil) + return nil, &smithy.GenericAPIError{ + Code: "NoSuchEntityException", + Message: fmt.Sprintf("Instance Profile %s cannot be found", + aws.ToString(input.InstanceProfileName)), + } }) } -func (s *IAMAPI) AddRoleToInstanceProfileWithContext(_ context.Context, input *iam.AddRoleToInstanceProfileInput, _ ...request.Option) (*iam.AddRoleToInstanceProfileOutput, error) { +func (s *IAMAPI) AddRoleToInstanceProfile(_ context.Context, input *iam.AddRoleToInstanceProfileInput, _ ...func(*iam.Options)) (*iam.AddRoleToInstanceProfileOutput, error) { return s.AddRoleToInstanceProfileBehavior.Invoke(input, func(output *iam.AddRoleToInstanceProfileInput) (*iam.AddRoleToInstanceProfileOutput, error) { s.Lock() defer s.Unlock() - if i, ok := s.InstanceProfiles[aws.StringValue(input.InstanceProfileName)]; ok { + if i, ok := s.InstanceProfiles[aws.ToString(input.InstanceProfileName)]; ok { if len(i.Roles) > 0 { - return nil, awserr.New(iam.ErrCodeLimitExceededException, "Cannot exceed quota for InstanceSessionsPerInstanceProfile: 1", nil) + return nil, &smithy.GenericAPIError{ + Code: "LimitExceededException", + Message: fmt.Sprintf("Instance Profile %s already has a role", + aws.ToString(input.InstanceProfileName)), + } } - i.Roles = append(i.Roles, &iam.Role{RoleId: aws.String(RoleID()), RoleName: input.RoleName}) + i.Roles = append(i.Roles, iamtypes.Role{RoleId: aws.String(RoleID()), RoleName: input.RoleName}) return nil, nil } - return nil, awserr.New(iam.ErrCodeNoSuchEntityException, fmt.Sprintf("Instance Profile %s cannot be found", aws.StringValue(input.InstanceProfileName)), nil) + return nil, &smithy.GenericAPIError{ + Code: "NoSuchEntityException", + Message: fmt.Sprintf("Instance Profile %s cannot be found", + aws.ToString(input.InstanceProfileName)), + } }) } -func (s *IAMAPI) RemoveRoleFromInstanceProfileWithContext(_ context.Context, input *iam.RemoveRoleFromInstanceProfileInput, _ ...request.Option) (*iam.RemoveRoleFromInstanceProfileOutput, error) { +func (s *IAMAPI) RemoveRoleFromInstanceProfile(_ context.Context, input *iam.RemoveRoleFromInstanceProfileInput, _ ...func(*iam.Options)) (*iam.RemoveRoleFromInstanceProfileOutput, error) { return s.RemoveRoleFromInstanceProfileBehavior.Invoke(input, func(output *iam.RemoveRoleFromInstanceProfileInput) (*iam.RemoveRoleFromInstanceProfileOutput, error) { s.Lock() defer s.Unlock() - if i, ok := s.InstanceProfiles[aws.StringValue(input.InstanceProfileName)]; ok { - newRoles := lo.Reject(i.Roles, func(r *iam.Role, _ int) bool { - return aws.StringValue(r.RoleName) == aws.StringValue(input.RoleName) + if i, ok := s.InstanceProfiles[aws.ToString(input.InstanceProfileName)]; ok { + newRoles := lo.Reject(i.Roles, func(r iamtypes.Role, _ int) bool { + return aws.ToString(r.RoleName) == aws.ToString(input.RoleName) }) if len(i.Roles) == len(newRoles) { - return nil, awserr.New(iam.ErrCodeNoSuchEntityException, fmt.Sprintf("The role with name %s cannot be found", aws.StringValue(input.RoleName)), nil) + return nil, &smithy.GenericAPIError{ + Code: "NoSuchEntityException", + Message: fmt.Sprintf("Instance Profile %s does not have role %s", + aws.ToString(input.InstanceProfileName), aws.ToString(input.RoleName)), + } } i.Roles = newRoles return nil, nil } - return nil, awserr.New(iam.ErrCodeNoSuchEntityException, fmt.Sprintf("Instance Profile %s cannot be found", aws.StringValue(input.InstanceProfileName)), nil) + return nil, &smithy.GenericAPIError{ + Code: "NoSuchEntityException", + Message: fmt.Sprintf("Instance Profile %s cannot be found", + aws.ToString(input.InstanceProfileName)), + } }) } diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index d0c9c752b091..65c7e31e10a8 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -22,6 +22,10 @@ import ( "net" "os" + configV2 "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" + iamV2 "github.com/aws/aws-sdk-go-v2/service/iam" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" awsclient "github.com/aws/aws-sdk-go/aws/client" @@ -33,7 +37,6 @@ import ( "github.com/aws/aws-sdk-go/service/ec2/ec2iface" "github.com/aws/aws-sdk-go/service/eks" "github.com/aws/aws-sdk-go/service/eks/eksiface" - "github.com/aws/aws-sdk-go/service/iam" "github.com/aws/aws-sdk-go/service/ssm" prometheusv1 "github.com/jonathan-innis/aws-sdk-go-prometheus/v1" "github.com/patrickmn/go-cache" @@ -73,7 +76,6 @@ type Operator struct { Session *session.Session UnavailableOfferingsCache *awscache.UnavailableOfferings - EC2API ec2iface.EC2API SubnetProvider subnet.Provider SecurityGroupProvider securitygroup.Provider InstanceProfileProvider instanceprofile.Provider @@ -88,6 +90,7 @@ type Operator struct { } func NewOperator(ctx context.Context, operator *operator.Operator) (context.Context, *Operator) { + //v1 config := &aws.Config{ STSRegionalEndpoint: endpoints.RegionalSTSEndpoint, } @@ -130,10 +133,19 @@ func NewOperator(ctx context.Context, operator *operator.Operator) (context.Cont log.FromContext(ctx).WithValues("kube-dns-ip", kubeDNSIP).V(1).Info("discovered kube dns") } + //v2 + //Once everything is migrated we will need to update prometheus metrics to v2 + cfg := lo.Must(configV2.LoadDefaultConfig(ctx, configV2.WithRetryMaxAttempts(3))) + if cfg.Region == "" { + log.FromContext(ctx).V(1).Info("retrieving region from IMDS") + metaDataClient := imds.NewFromConfig(cfg) + cfg.Region = lo.Must(metaDataClient.GetRegion(ctx, nil)).Region + } + unavailableOfferingsCache := awscache.NewUnavailableOfferings() subnetProvider := subnet.NewDefaultProvider(ec2api, cache.New(awscache.DefaultTTL, awscache.DefaultCleanupInterval), cache.New(awscache.AvailableIPAddressTTL, awscache.DefaultCleanupInterval), cache.New(awscache.AssociatePublicIPAddressTTL, awscache.DefaultCleanupInterval)) securityGroupProvider := securitygroup.NewDefaultProvider(ec2api, cache.New(awscache.DefaultTTL, awscache.DefaultCleanupInterval)) - instanceProfileProvider := instanceprofile.NewDefaultProvider(*sess.Config.Region, iam.New(sess), cache.New(awscache.InstanceProfileTTL, awscache.DefaultCleanupInterval)) + instanceProfileProvider := instanceprofile.NewDefaultProvider(cfg.Region, iamV2.NewFromConfig(cfg), cache.New(awscache.InstanceProfileTTL, awscache.DefaultCleanupInterval)) pricingProvider := pricing.NewDefaultProvider( ctx, pricing.NewAPI(sess, *sess.Config.Region), @@ -176,7 +188,6 @@ func NewOperator(ctx context.Context, operator *operator.Operator) (context.Cont Operator: operator, Session: sess, UnavailableOfferingsCache: unavailableOfferingsCache, - EC2API: ec2api, SubnetProvider: subnetProvider, SecurityGroupProvider: securityGroupProvider, InstanceProfileProvider: instanceProfileProvider, diff --git a/pkg/providers/instanceprofile/instanceprofile.go b/pkg/providers/instanceprofile/instanceprofile.go index 8dd66d95b7aa..0b5df39461a9 100644 --- a/pkg/providers/instanceprofile/instanceprofile.go +++ b/pkg/providers/instanceprofile/instanceprofile.go @@ -18,15 +18,16 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/iam" - "github.com/aws/aws-sdk-go/service/iam/iamiface" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/iam" + iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" "github.com/patrickmn/go-cache" "github.com/samber/lo" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" v1 "github.com/aws/karpenter-provider-aws/pkg/apis/v1" + sdk "github.com/aws/karpenter-provider-aws/pkg/aws" awserrors "github.com/aws/karpenter-provider-aws/pkg/errors" "github.com/aws/karpenter-provider-aws/pkg/operator/options" ) @@ -46,11 +47,11 @@ type Provider interface { type DefaultProvider struct { region string - iamapi iamiface.IAMAPI + iamapi sdk.IAMAPI cache *cache.Cache } -func NewDefaultProvider(region string, iamapi iamiface.IAMAPI, cache *cache.Cache) *DefaultProvider { +func NewDefaultProvider(region string, iamapi sdk.IAMAPI, cache *cache.Cache) *DefaultProvider { return &DefaultProvider{ region: region, iamapi: iamapi, @@ -67,27 +68,27 @@ func (p *DefaultProvider) Create(ctx context.Context, m ResourceOwner) (string, return profileName, nil } // Validate if the instance profile exists and has the correct role assigned to it - var instanceProfile *iam.InstanceProfile - out, err := p.iamapi.GetInstanceProfileWithContext(ctx, &iam.GetInstanceProfileInput{InstanceProfileName: aws.String(profileName)}) + var instanceProfile *iamtypes.InstanceProfile + out, err := p.iamapi.GetInstanceProfile(ctx, &iam.GetInstanceProfileInput{InstanceProfileName: aws.String(profileName)}) if err != nil { - if !awserrors.IsNotFound(err) { + if !awserrors.IsNotFoundV2(err) { return "", fmt.Errorf("getting instance profile %q, %w", profileName, err) } - o, err := p.iamapi.CreateInstanceProfileWithContext(ctx, &iam.CreateInstanceProfileInput{ + o, err := p.iamapi.CreateInstanceProfile(ctx, &iam.CreateInstanceProfileInput{ InstanceProfileName: aws.String(profileName), - Tags: lo.MapToSlice(tags, func(k, v string) *iam.Tag { return &iam.Tag{Key: aws.String(k), Value: aws.String(v)} }), + Tags: lo.MapToSlice(tags, func(k, v string) iamtypes.Tag { return iamtypes.Tag{Key: aws.String(k), Value: aws.String(v)} }), }) if err != nil { return "", fmt.Errorf("creating instance profile %q, %w", profileName, err) } instanceProfile = o.InstanceProfile } else { - if !lo.ContainsBy(out.InstanceProfile.Tags, func(t *iam.Tag) bool { + if !lo.ContainsBy(out.InstanceProfile.Tags, func(t iamtypes.Tag) bool { return lo.FromPtr(t.Key) == v1.EKSClusterNameTagKey }) { - if _, err = p.iamapi.TagInstanceProfileWithContext(ctx, &iam.TagInstanceProfileInput{ + if _, err = p.iamapi.TagInstanceProfile(ctx, &iam.TagInstanceProfileInput{ InstanceProfileName: aws.String(profileName), - Tags: lo.MapToSlice(tags, func(k, v string) *iam.Tag { return &iam.Tag{Key: aws.String(k), Value: aws.String(v)} }), + Tags: lo.MapToSlice(tags, func(k, v string) iamtypes.Tag { return iamtypes.Tag{Key: aws.String(k), Value: aws.String(v)} }), }); err != nil { return "", fmt.Errorf("tagging instance profile %q, %w", profileName, err) } @@ -97,48 +98,48 @@ func (p *DefaultProvider) Create(ctx context.Context, m ResourceOwner) (string, // Instance profiles can only have a single role assigned to them so this profile either has 1 or 0 roles // https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html if len(instanceProfile.Roles) == 1 { - if aws.StringValue(instanceProfile.Roles[0].RoleName) == m.InstanceProfileRole() { + if aws.ToString(instanceProfile.Roles[0].RoleName) == m.InstanceProfileRole() { return profileName, nil } - if _, err = p.iamapi.RemoveRoleFromInstanceProfileWithContext(ctx, &iam.RemoveRoleFromInstanceProfileInput{ + if _, err = p.iamapi.RemoveRoleFromInstanceProfile(ctx, &iam.RemoveRoleFromInstanceProfileInput{ InstanceProfileName: aws.String(profileName), RoleName: instanceProfile.Roles[0].RoleName, }); err != nil { - return "", fmt.Errorf("removing role %q for instance profile %q, %w", aws.StringValue(instanceProfile.Roles[0].RoleName), profileName, err) + return "", fmt.Errorf("removing role %q for instance profile %q, %w", aws.ToString(instanceProfile.Roles[0].RoleName), profileName, err) } } - if _, err = p.iamapi.AddRoleToInstanceProfileWithContext(ctx, &iam.AddRoleToInstanceProfileInput{ + if _, err = p.iamapi.AddRoleToInstanceProfile(ctx, &iam.AddRoleToInstanceProfileInput{ InstanceProfileName: aws.String(profileName), RoleName: aws.String(m.InstanceProfileRole()), }); err != nil { return "", fmt.Errorf("adding role %q to instance profile %q, %w", m.InstanceProfileRole(), profileName, err) } p.cache.SetDefault(string(m.GetUID()), nil) - return aws.StringValue(instanceProfile.InstanceProfileName), nil + return aws.ToString(instanceProfile.InstanceProfileName), nil } func (p *DefaultProvider) Delete(ctx context.Context, m ResourceOwner) error { profileName := m.InstanceProfileName(options.FromContext(ctx).ClusterName, p.region) - out, err := p.iamapi.GetInstanceProfileWithContext(ctx, &iam.GetInstanceProfileInput{ + out, err := p.iamapi.GetInstanceProfile(ctx, &iam.GetInstanceProfileInput{ InstanceProfileName: aws.String(profileName), }) if err != nil { - return awserrors.IgnoreNotFound(fmt.Errorf("getting instance profile %q, %w", profileName, err)) + return awserrors.IgnoreNotFoundV2(fmt.Errorf("getting instance profile %q, %w", profileName, err)) } // Instance profiles can only have a single role assigned to them so this profile either has 1 or 0 roles // https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html if len(out.InstanceProfile.Roles) == 1 { - if _, err = p.iamapi.RemoveRoleFromInstanceProfileWithContext(ctx, &iam.RemoveRoleFromInstanceProfileInput{ + if _, err = p.iamapi.RemoveRoleFromInstanceProfile(ctx, &iam.RemoveRoleFromInstanceProfileInput{ InstanceProfileName: aws.String(profileName), RoleName: out.InstanceProfile.Roles[0].RoleName, }); err != nil { - return fmt.Errorf("removing role %q from instance profile %q, %w", aws.StringValue(out.InstanceProfile.Roles[0].RoleName), profileName, err) + return fmt.Errorf("removing role %q from instance profile %q, %w", aws.ToString(out.InstanceProfile.Roles[0].RoleName), profileName, err) } } - if _, err = p.iamapi.DeleteInstanceProfileWithContext(ctx, &iam.DeleteInstanceProfileInput{ + if _, err = p.iamapi.DeleteInstanceProfile(ctx, &iam.DeleteInstanceProfileInput{ InstanceProfileName: aws.String(profileName), }); err != nil { - return awserrors.IgnoreNotFound(fmt.Errorf("deleting instance profile %q, %w", profileName, err)) + return awserrors.IgnoreNotFoundV2(fmt.Errorf("deleting instance profile %q, %w", profileName, err)) } return nil }