diff --git a/pkg/actions/podidentityassociation/migrator.go b/pkg/actions/podidentityassociation/migrator.go index 02148d2fc8..9d4c1c7833 100644 --- a/pkg/actions/podidentityassociation/migrator.go +++ b/pkg/actions/podidentityassociation/migrator.go @@ -215,6 +215,11 @@ func (m *Migrator) MigrateToPodIdentity(ctx context.Context, options PodIdentity } func IsPodIdentityAgentInstalled(ctx context.Context, eksAPI awsapi.EKS, clusterName string) (bool, error) { + + if autoMode, _ := IsAutoModeEnabled(ctx, eksAPI, clusterName); autoMode { + return true, nil + } + if _, err := eksAPI.DescribeAddon(ctx, &awseks.DescribeAddonInput{ AddonName: aws.String(api.PodIdentityAgentAddon), ClusterName: &clusterName, @@ -228,6 +233,22 @@ func IsPodIdentityAgentInstalled(ctx context.Context, eksAPI awsapi.EKS, cluster return true, nil } +func IsAutoModeEnabled(ctx context.Context, eksAPI awsapi.EKS, clusterName string) (bool, error) { + clstrDescribeResponse, err := eksAPI.DescribeCluster(ctx, &awseks.DescribeClusterInput{ + Name: aws.String(clusterName), + }) + + if err != nil { + return false, fmt.Errorf("calling EKS::DescribeCluster: %w", err) + } + + if *clstrDescribeResponse.Cluster.ComputeConfig.Enabled { + return true, nil + } + + return false, nil +} + type IRSAv1StackNameResolver map[string]IRSAv1StackSummary type IRSAv1StackSummary struct { diff --git a/pkg/actions/podidentityassociation/migrator_test.go b/pkg/actions/podidentityassociation/migrator_test.go index b1adf80e7a..a35391c5f5 100644 --- a/pkg/actions/podidentityassociation/migrator_test.go +++ b/pkg/actions/podidentityassociation/migrator_test.go @@ -66,11 +66,24 @@ var _ = Describe("Create", func() { genericErr = fmt.Errorf("ERR") ) - mockDescribeAddon := func(provider *mockprovider.MockProvider, err error) { + mockDescribeAddon := func(provider *mockprovider.MockProvider, err error, autoMode bool) { mockProvider.MockEKS(). - On("DescribeAddon", mock.Anything, mock.Anything). - Return(nil, err). + On("DescribeCluster", mock.Anything, mock.Anything). + Return(&awseks.DescribeClusterOutput{ + Cluster: &ekstypes.Cluster{ + ComputeConfig: &ekstypes.ComputeConfigResponse{ + Enabled: aws.Bool(autoMode), + }, + }, + }, nil). Once() + if !autoMode { + mockProvider.MockEKS(). + On("DescribeAddon", mock.Anything, mock.Anything). + Return(nil, err). + Once() + + } } createFakeServiceAccount := func(clientSet *fake.Clientset, namespace, serviceAccountName, roleARN string) { @@ -139,14 +152,14 @@ var _ = Describe("Create", func() { }, Entry("[API errors] describing pod identity agent addon fails", migrateToPodIdentityAssociationEntry{ mockEKS: func(provider *mockprovider.MockProvider) { - mockDescribeAddon(provider, genericErr) + mockDescribeAddon(provider, genericErr, false) }, expectedErr: fmt.Sprintf("calling %q", fmt.Sprintf("EKS::DescribeAddon::%s", api.PodIdentityAgentAddon)), }), Entry("[API errors] fetching iamserviceaccounts fails", migrateToPodIdentityAssociationEntry{ mockEKS: func(provider *mockprovider.MockProvider) { - mockDescribeAddon(provider, nil) + mockDescribeAddon(provider, nil, false) }, mockCFN: func(stackUpdater *fakes.FakeStackUpdater) { stackUpdater.GetIAMServiceAccountsReturns(nil, genericErr) @@ -158,7 +171,7 @@ var _ = Describe("Create", func() { mockEKS: func(provider *mockprovider.MockProvider) { mockDescribeAddon(provider, &ekstypes.ResourceNotFoundException{ Message: aws.String(genericErr.Error()), - }) + }, false) }, mockCFN: func(stackUpdater *fakes.FakeStackUpdater) { stackUpdater.GetIAMServiceAccountsReturns([]*api.ClusterIAMServiceAccount{}, nil) @@ -173,7 +186,7 @@ var _ = Describe("Create", func() { Entry("[taskTree] contains tasks to remove IRSAv1 EKS Role annotation if remove trust option is specified", migrateToPodIdentityAssociationEntry{ mockEKS: func(provider *mockprovider.MockProvider) { - mockDescribeAddon(provider, nil) + mockDescribeAddon(provider, nil, false) }, mockCFN: func(stackUpdater *fakes.FakeStackUpdater) { stackUpdater.GetIAMServiceAccountsReturns([]*api.ClusterIAMServiceAccount{}, nil) @@ -191,7 +204,7 @@ var _ = Describe("Create", func() { Entry("[taskTree] contains all other expected tasks", migrateToPodIdentityAssociationEntry{ mockEKS: func(provider *mockprovider.MockProvider) { - mockDescribeAddon(provider, nil) + mockDescribeAddon(provider, nil, false) }, mockCFN: func(stackUpdater *fakes.FakeStackUpdater) { stackUpdater.GetIAMServiceAccountsReturns([]*api.ClusterIAMServiceAccount{ @@ -220,7 +233,88 @@ var _ = Describe("Create", func() { Entry("completes all tasks successfully", migrateToPodIdentityAssociationEntry{ mockEKS: func(provider *mockprovider.MockProvider) { - mockDescribeAddon(provider, nil) + mockDescribeAddon(provider, nil, false) + + mockProvider.MockEKS(). + On("CreatePodIdentityAssociation", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + Expect(args).To(HaveLen(2)) + Expect(args[1]).To(BeAssignableToTypeOf(&awseks.CreatePodIdentityAssociationInput{})) + }). + Return(nil, nil). + Twice() + + mockProvider.MockIAM(). + On("GetRole", mock.Anything, mock.Anything). + Return(&awsiam.GetRoleOutput{ + Role: &iamtypes.Role{ + AssumeRolePolicyDocument: policyDocument, + }, + }, nil). + Twice() + + mockProvider.MockIAM(). + On("UpdateAssumeRolePolicy", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + Expect(args).To(HaveLen(2)) + Expect(args[1]).To(BeAssignableToTypeOf(&awsiam.UpdateAssumeRolePolicyInput{})) + input := args[1].(*awsiam.UpdateAssumeRolePolicyInput) + + var trustPolicy api.IAMPolicyDocument + Expect(json.Unmarshal([]byte(*input.PolicyDocument), &trustPolicy)).NotTo(HaveOccurred()) + Expect(trustPolicy.Statements).To(HaveLen(1)) + value, exists := trustPolicy.Statements[0].Principal["Service"] + Expect(exists).To(BeTrue()) + Expect(value).To(ConsistOf([]string{api.EKSServicePrincipal})) + }). + Return(nil, nil). + Once() + }, + mockCFN: func(stackUpdater *fakes.FakeStackUpdater) { + stackUpdater.GetIAMServiceAccountsReturns([]*api.ClusterIAMServiceAccount{ + { + Status: &api.ClusterIAMServiceAccountStatus{ + RoleARN: aws.String(roleARN1), + StackName: aws.String(makeIRSAv1StackName(podidentityassociation.Identifier{ + Namespace: nsDefault, + ServiceAccountName: sa1, + })), + Capabilities: []string{"CAPABILITY_IAM"}, + }, + }, + }, nil) + + stackUpdater.GetStackTemplateReturnsOnCall(0, iamRoleStackTemplate(nsDefault, sa1), nil) + stackUpdater.GetStackTemplateReturnsOnCall(1, iamRoleStackTemplate(nsDefault, sa2), nil) + + stackUpdater.MustUpdateStackStub = func(ctx context.Context, options manager.UpdateStackOptions) error { + Expect(options.Stack).NotTo(BeNil()) + Expect(options.Stack.Tags).To(ConsistOf([]cfntypes.Tag{ + { + Key: aws.String(api.PodIdentityAssociationNameTag), + Value: aws.String(nsDefault + "/" + sa1), + }, + })) + Expect(options.Stack.Capabilities).To(ConsistOf([]cfntypes.Capability{"CAPABILITY_IAM"})) + template := string(options.TemplateData.(manager.TemplateBody)) + Expect(template).To(ContainSubstring(api.EKSServicePrincipal)) + Expect(template).NotTo(ContainSubstring("oidc")) + return nil + } + }, + mockK8s: func(clientSet *fake.Clientset) { + createFakeServiceAccount(clientSet, nsDefault, sa1, roleARN1) + createFakeServiceAccount(clientSet, nsDefault, sa2, roleARN2) + }, + options: podidentityassociation.PodIdentityMigrationOptions{ + RemoveOIDCProviderTrustRelationship: true, + Approve: true, + }, + }), + + Entry("completes all tasks successfully for auto-mode", migrateToPodIdentityAssociationEntry{ + mockEKS: func(provider *mockprovider.MockProvider) { + mockDescribeAddon(provider, nil, true) mockProvider.MockEKS(). On("CreatePodIdentityAssociation", mock.Anything, mock.Anything). diff --git a/pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go b/pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go index a7f4cf7a23..72f8c7df1c 100644 --- a/pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go +++ b/pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go @@ -264,6 +264,7 @@ func (in *AutoModeConfig) DeepCopyInto(out *AutoModeConfig) { **out = **in } out.NodeRoleARN = in.NodeRoleARN + out.PermissionsBoundaryARN = in.PermissionsBoundaryARN if in.NodePools != nil { in, out := &in.NodePools, &out.NodePools *out = new([]string) @@ -788,6 +789,11 @@ func (in *ClusterIAMServiceAccountStatus) DeepCopy() *ClusterIAMServiceAccountSt // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterMeta) DeepCopyInto(out *ClusterMeta) { *out = *in + if in.ForceUpdateVersion != nil { + in, out := &in.ForceUpdateVersion, &out.ForceUpdateVersion + *out = new(bool) + **out = **in + } if in.Tags != nil { in, out := &in.Tags, &out.Tags *out = make(map[string]string, len(*in)) @@ -1544,6 +1550,11 @@ func (in *NodeGroup) DeepCopyInto(out *NodeGroup) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.EnclaveEnabled != nil { + in, out := &in.EnclaveEnabled, &out.EnclaveEnabled + *out = new(bool) + **out = **in + } return } diff --git a/pkg/goformation/cloudformation/types/types.go b/pkg/goformation/cloudformation/types/types.go index e8738f2467..af8bbf2dc3 100644 --- a/pkg/goformation/cloudformation/types/types.go +++ b/pkg/goformation/cloudformation/types/types.go @@ -32,7 +32,7 @@ func NewValueFromPrimitive(raw interface{}) (*Value, error) { case json.Number: i, err := p.Int64() if err == nil { - if i <= int64(^uint(0) >> 1) { + if i <= int64(^uint(0)>>1) { return NewInteger(int(i)), nil } return NewLong(i), nil diff --git a/userdocs/src/usage/pod-identity-associations.md b/userdocs/src/usage/pod-identity-associations.md index 014019af62..f4645e5fe7 100644 --- a/userdocs/src/usage/pod-identity-associations.md +++ b/userdocs/src/usage/pod-identity-associations.md @@ -10,6 +10,9 @@ As a result, IAM roles no longer need to reference an [OIDC provider](/usage/iam Behind the scenes, the implementation of pod identity associations is running an agent as a daemonset on the worker nodes. To run the pre-requisite agent on the cluster, EKS provides a new add-on called EKS Pod Identity Agent. Therefore, creating pod identity associations (in general, and with `eksctl`) requires the `eks-pod-identity-agent` addon pre-installed on the cluster. This addon can be [created using `eksctl`](/usage/addons/#creating-addons) in the same fashion any other supported addon is, e.g. +???+ note + If you are using [EKS Auto Mode](https://eksctl.io/usage/auto-mode/) cluster the `eks-pod-identity-agent` comes pre-installed and you can skip creating the addon. + ``` eksctl create addon --cluster my-cluster --name eks-pod-identity-agent ```