Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement updating and deleting pod identity associations #7315

Merged
merged 10 commits into from
Nov 27, 2023

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/actions/addon/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func CreateAddonTasks(ctx context.Context, cfg *api.ClusterConfig, clusterProvid
var preAddons []*api.Addon
var postAddons []*api.Addon
for _, addon := range cfg.Addons {
if strings.ToLower(addon.Name) == "vpc-cni" {
if strings.EqualFold(addon.Name, api.VPCCNIAddon) {
preAddons = append(preAddons, addon)
} else {
postAddons = append(postAddons, addon)
Expand Down
9 changes: 8 additions & 1 deletion pkg/actions/cluster/owned.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import (

"github.com/weaveworks/eksctl/pkg/actions/addon"
"github.com/weaveworks/eksctl/pkg/actions/nodegroup"
"github.com/weaveworks/eksctl/pkg/actions/podidentityassociation"
api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
"github.com/weaveworks/eksctl/pkg/cfn/manager"
"github.com/weaveworks/eksctl/pkg/ctl/cmdutils"
"github.com/weaveworks/eksctl/pkg/eks"
iamoidc "github.com/weaveworks/eksctl/pkg/iam/oidc"
"github.com/weaveworks/eksctl/pkg/kubernetes"
"github.com/weaveworks/eksctl/pkg/utils/tasks"
"github.com/weaveworks/eksctl/pkg/vpc"
)

Expand Down Expand Up @@ -121,7 +123,12 @@ func (c *OwnedCluster) Delete(ctx context.Context, _, podEvictionWaitPeriod time
return c.ctl.NewOpenIDConnectManager(ctx, c.cfg)
}
newTasksToDeleteAddonIAM := addon.NewRemover(c.stackManager).DeleteAddonIAMTasks
tasks, err := c.stackManager.NewTasksToDeleteClusterWithNodeGroups(ctx, c.clusterStack, allStacks, clusterOperable, newOIDCManager, newTasksToDeleteAddonIAM, c.ctl.Status.ClusterInfo.Cluster, kubernetes.NewCachedClientSet(clientSet), wait, force, func(errs chan error, _ string) error {
newTasksToDeletePodIdentityRoles := func() (*tasks.TaskTree, error) {
return podidentityassociation.NewDeleter(c.cfg.Metadata.Name, c.stackManager, c.ctl.AWSProvider.EKS()).
DeleteTasks(ctx, []podidentityassociation.Identifier{})
}

tasks, err := c.stackManager.NewTasksToDeleteClusterWithNodeGroups(ctx, c.clusterStack, allStacks, clusterOperable, newOIDCManager, newTasksToDeleteAddonIAM, newTasksToDeletePodIdentityRoles, c.ctl.Status.ClusterInfo.Cluster, kubernetes.NewCachedClientSet(clientSet), wait, force, func(errs chan error, _ string) error {
logger.Info("trying to cleanup dangling network interfaces")
stack, err := c.stackManager.DescribeClusterStack(ctx)
if err != nil {
Expand Down
17 changes: 1 addition & 16 deletions pkg/actions/podidentityassociation/creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package podidentityassociation
import (
"context"
"fmt"
"strings"

"github.com/kris-nova/logger"

api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
"github.com/weaveworks/eksctl/pkg/awsapi"
Expand All @@ -28,19 +25,7 @@ func NewCreator(clusterName string, stackManager StackManager, eksAPI awsapi.EKS
}

func (c *Creator) CreatePodIdentityAssociations(ctx context.Context, podIdentityAssociations []api.PodIdentityAssociation) error {
taskTree := c.CreateTasks(ctx, podIdentityAssociations)
logger.Info(taskTree.Describe())

if errs := taskTree.DoAllSync(); len(errs) > 0 {
var allErrs []string
for _, err := range errs {
allErrs = append(allErrs, err.Error())
}
return fmt.Errorf(strings.Join(allErrs, "\n"))
}

logger.Info("successfully created all pod identity associations")
return nil
return runAllTasks(c.CreateTasks(ctx, podIdentityAssociations))
}

func (c *Creator) CreateTasks(ctx context.Context, podIdentityAssociations []api.PodIdentityAssociation) *tasks.TaskTree {
Expand Down
186 changes: 186 additions & 0 deletions pkg/actions/podidentityassociation/deleter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package podidentityassociation

import (
"context"
"fmt"
"strings"

cfntypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types"

"golang.org/x/exp/slices"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/eks"

"github.com/kris-nova/logger"

api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
"github.com/weaveworks/eksctl/pkg/cfn/manager"
"github.com/weaveworks/eksctl/pkg/utils/tasks"
)

// A StackLister lists and describes CloudFormation stacks.
type StackLister interface {
ListStackNames(ctx context.Context, regExp string) ([]string, error)
DescribeStack(ctx context.Context, stack *manager.Stack) (*manager.Stack, error)
}

// A StackDeleter lists and deletes CloudFormation stacks.
type StackDeleter interface {
StackLister
DeleteStackBySpecSync(ctx context.Context, stack *cfntypes.Stack, errCh chan error) error
}

// APILister lists pod identity associations using the EKS API.
type APILister interface {
ListPodIdentityAssociations(ctx context.Context, params *eks.ListPodIdentityAssociationsInput, optFns ...func(*eks.Options)) (*eks.ListPodIdentityAssociationsOutput, error)
}

// APIDeleter lists and deletes pod identity associations using the EKS API.
type APIDeleter interface {
APILister
DeletePodIdentityAssociation(ctx context.Context, params *eks.DeletePodIdentityAssociationInput, optFns ...func(*eks.Options)) (*eks.DeletePodIdentityAssociationOutput, error)
}

// A Deleter deletes pod identity associations.
type Deleter struct {
// ClusterName is the cluster name.
ClusterName string
// StackDeleter is used to delete stacks.
StackDeleter StackDeleter
// APIDeleter deletes pod identity associations using the EKS API.
APIDeleter APIDeleter
}

// Identifier represents a pod identity association.
type Identifier struct {
// Namespace is the namespace the service account belongs to.
Namespace string
// ServiceAccountName is the name of the Kubernetes ServiceAccount.
ServiceAccountName string
}

func NewDeleter(clusterName string, stackDeleter StackDeleter, apiDeleter APIDeleter) *Deleter {
return &Deleter{
ClusterName: clusterName,
StackDeleter: stackDeleter,
APIDeleter: apiDeleter,
}
}

// Delete deletes the specified podIdentityAssociations.
func (d *Deleter) Delete(ctx context.Context, podIDs []Identifier) error {
tasks, err := d.DeleteTasks(ctx, podIDs)
if err != nil {
return err
}
return runAllTasks(tasks)
}

func (d *Deleter) DeleteTasks(ctx context.Context, podIDs []Identifier) (*tasks.TaskTree, error) {
roleStackNames, err := d.StackDeleter.ListStackNames(ctx, fmt.Sprintf("^%s*", makeStackNamePrefix(d.ClusterName)))
if err != nil {
return nil, fmt.Errorf("error listing stack names for pod identity associations: %w", err)
}
taskTree := &tasks.TaskTree{Parallel: true}

// this is true during cluster deletion, when no association identifier is given as user input,
// instead we will delete all pod-identity-role stacks for the cluster
if len(podIDs) == 0 {
for _, stackName := range roleStackNames {
name := strings.Clone(stackName)
taskTree.Append(&tasks.GenericTask{
Description: fmt.Sprintf("deleting IAM resources stack %q", stackName),
Doer: func() error {
return d.deleteRoleStack(ctx, name)
},
})
}
return taskTree, nil
}

for _, p := range podIDs {
taskTree.Append(d.makeDeleteTask(ctx, p, roleStackNames))
}
return taskTree, nil
}

func (d *Deleter) makeDeleteTask(ctx context.Context, p Identifier, roleStackNames []string) tasks.Task {
podIdentityAssociationID := makeID(p.Namespace, p.ServiceAccountName)
return &tasks.GenericTask{
Description: fmt.Sprintf("delete pod identity association %q", podIdentityAssociationID),
Doer: func() error {
if err := d.deletePodIdentityAssociation(ctx, p, roleStackNames, podIdentityAssociationID); err != nil {
return fmt.Errorf("error deleting pod identity association %q: %w", podIdentityAssociationID, err)
}
return nil
},
}
}

func (d *Deleter) deletePodIdentityAssociation(ctx context.Context, p Identifier, roleStackNames []string, podIdentityAssociationID string) error {
output, err := d.APIDeleter.ListPodIdentityAssociations(ctx, &eks.ListPodIdentityAssociationsInput{
ClusterName: aws.String(d.ClusterName),
Namespace: aws.String(p.Namespace),
ServiceAccount: aws.String(p.ServiceAccountName),
})
if err != nil {
return fmt.Errorf("listing pod identity associations: %w", err)
}
switch len(output.Associations) {
case 0:
logger.Warning("pod identity association %q not found", podIdentityAssociationID)
default:
return fmt.Errorf("expected to find only 1 pod identity association for %q; got %d", podIdentityAssociationID, len(output.Associations))
case 1:
if _, err := d.APIDeleter.DeletePodIdentityAssociation(ctx, &eks.DeletePodIdentityAssociationInput{
ClusterName: aws.String(d.ClusterName),
AssociationId: output.Associations[0].AssociationId,
}); err != nil {
return fmt.Errorf("deleting pod identity association: %w", err)
}
}

stackName := MakeStackName(d.ClusterName, p.Namespace, p.ServiceAccountName)
if !slices.Contains(roleStackNames, stackName) {
return nil
}
logger.Info("deleting IAM resources stack %q for pod identity association %q", stackName, podIdentityAssociationID)
return d.deleteRoleStack(ctx, stackName)
}

func (d *Deleter) deleteRoleStack(ctx context.Context, stackName string) error {
stack, err := d.StackDeleter.DescribeStack(ctx, &manager.Stack{
StackName: aws.String(stackName),
})
if err != nil {
return fmt.Errorf("describing stack %q: %w", stackName, err)
}

deleteStackCh := make(chan error)
if err := d.StackDeleter.DeleteStackBySpecSync(ctx, stack, deleteStackCh); err != nil {
return fmt.Errorf("deleting stack %q for IAM role: %w", stackName, err)
}
select {
case err := <-deleteStackCh:
return err
case <-ctx.Done():
return fmt.Errorf("timed out waiting for deletion of pod identity association: %w", ctx.Err())
}
}

// ToIdentifiers maps a list of PodIdentityAssociations to a list of Identifiers.
func ToIdentifiers(podIdentityAssociations []api.PodIdentityAssociation) []Identifier {
identifiers := make([]Identifier, len(podIdentityAssociations))
for i, p := range podIdentityAssociations {
identifiers[i] = Identifier{
Namespace: p.Namespace,
ServiceAccountName: p.ServiceAccountName,
}
}
return identifiers
}

func makeID(namespace, serviceAccountName string) string {
return fmt.Sprintf("%s/%s", namespace, serviceAccountName)
}
Loading
Loading