Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions pkg/actions/podidentityassociation/migrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Comment on lines +237 to +245
Copy link
Member

@naclonts naclonts Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
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 {
clusterDescribeResponse, err := eksAPI.DescribeCluster(ctx, &awseks.DescribeClusterInput{
Name: aws.String(clusterName),
})
if err != nil {
return false, fmt.Errorf("calling EKS::DescribeCluster: %w", err)
}
if *clusterDescribeResponse.Cluster.ComputeConfig.Enabled {

return true, nil
}

return false, nil
}

type IRSAv1StackNameResolver map[string]IRSAv1StackSummary

type IRSAv1StackSummary struct {
Expand Down
112 changes: 103 additions & 9 deletions pkg/actions/podidentityassociation/migrator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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().
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for updating the test here! Do you think it makes sense to add a test case to ensure IsPodIdentityAgentInstalled returns true when auto-mode is enabled?

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) {
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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{
Expand Down Expand Up @@ -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).
Expand Down
11 changes: 11 additions & 0 deletions pkg/apis/eksctl.io/v1alpha5/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/goformation/cloudformation/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions userdocs/src/usage/pod-identity-associations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Comment on lines +13 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this may not work with Github's markdown format (screenshot of preview in Github PR).

Also, should we put the note below the command on line 17? It's a little hard to see the connection between the paragraph and the CLI command with the note in between.

image

```
eksctl create addon --cluster my-cluster --name eks-pod-identity-agent
```
Expand Down
Loading