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

Migrate eks discovery to aws sdk v2 #50603

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open

Migrate eks discovery to aws sdk v2 #50603

wants to merge 20 commits into from

Conversation

creack
Copy link
Member

@creack creack commented Dec 28, 2024

Closes #49129

Removes all usage of sdk v1 for EKS.

@creack creack changed the title Migrate eks discovery to aws sdk v2 [wip] Migrate eks discovery to aws sdk v2 Dec 28, 2024
@creack creack force-pushed the creack/eks-sdk-v2 branch from 7174944 to d968a0e Compare January 5, 2025 18:35
@creack creack marked this pull request as ready for review January 5, 2025 18:41
@github-actions github-actions bot added database-access Database access related issues and PRs discovery kubernetes-access labels Jan 5, 2025
@github-actions github-actions bot requested a review from greedy52 January 5, 2025 18:41
@github-actions github-actions bot requested a review from tigrato January 5, 2025 18:41
@creack creack added the no-changelog Indicates that a PR does not require a changelog entry label Jan 5, 2025
@creack creack changed the title [wip] Migrate eks discovery to aws sdk v2 Migrate eks discovery to aws sdk v2 Jan 5, 2025
Comment on lines 361 to 362
// ClientGetter is an interface for getting an EKS client and an STS client.
type ClientGetter interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this intended to only ever be for retrieving AWS clients? If so should this be renamed to reflect that.

Copy link
Contributor

Choose a reason for hiding this comment

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

I vote for the same

Comment on lines 377 to 378
// TODO(@GavinFrazar): Re-enable this when session cache v2 gets merged (#50561).
// awsconfig.WithoutSessionCache(),
Copy link
Contributor

Choose a reason for hiding this comment

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

#50561 was merged earlier today

Copy link
Contributor

@GavinFrazar GavinFrazar Jan 7, 2025

Choose a reason for hiding this comment

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

might be worth mentioning #50809 as well, though I'm not sure it applies here - you can just call awsconfig.GetConfig, which is not cached, instead of using awsconfig.Cache.

Comment on lines 722 to 724
for k, v := range cfg.eksCluster.Tags {
eksTags[k] = aws.String(v)
eksTags[k] = v
}
Copy link
Contributor

Choose a reason for hiding this comment

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

can you use maps.Clone/Copy now?

Comment on lines 361 to 362
// ClientGetter is an interface for getting an EKS client and an STS client.
type ClientGetter interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

I vote for the same

Comment on lines +352 to +359
type EKSClient interface {
eks.DescribeClusterAPIClient
}

// STSClient is the subset of the STS Client interface we use.
type STSClient interface {
stscreds.AssumeRoleAPIClient
}
Copy link
Contributor

Choose a reason for hiding this comment

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

If our goal is to expose on those two interfaces, why not referring them directly?
This is mostly internal packages and I don't see them being expanded in the future

Copy link
Member Author

Choose a reason for hiding this comment

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

it is easier to have them split as one is implemented by sts.Client the other by eks.Client, used in different places.

opts := []awsconfig.OptionsFn{
awsconfig.WithAmbientCredentials(),
// TODO(@GavinFrazar): Re-enable this when session cache v2 gets merged (#50561).
// awsconfig.WithoutSessionCache(),
Copy link
Contributor

Choose a reason for hiding this comment

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

we need this merged otherwise we will introduce the same bug #50074 again

Copy link
Contributor

@GavinFrazar GavinFrazar Jan 8, 2025

Choose a reason for hiding this comment

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

it's not going through cache in this case, since the ClientGetter is calling the uncached awsconfig.GetConfig free function:

func (*awsClientsGetter) getAWSConfig(ctx context.Context, region string, opts ...awsconfig.OptionsFn) (aws.Config, error) {
	cfg, err := awsconfig.GetConfig(ctx, region, opts...)
	return cfg, trace.Wrap(err)
}

I think the expiry time returned from GenAWSEKSToken should be taken from the underlying aws.Credentials expiry instead of the 14 minute const, but just skipping the cache works too

@@ -74,6 +78,7 @@ type TLSServerConfig struct {
OnReconcile func(types.KubeClusters)
// CloudClients is a set of cloud clients that Teleport supports.
CloudClients cloud.Clients
AWSClients *awsClientsGetter
Copy link
Contributor

Choose a reason for hiding this comment

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

if this is a private structure w/out any way of building it, does it make sense for the field to public?

// GenAWSEKSToken creates an AWS token to access EKS clusters.
// Logic from https://github.com/aws/aws-cli/blob/6c0d168f0b44136fc6175c57c090d4b115437ad1/awscli/customizations/eks/get_token.py#L211-L229
func GenAWSEKSToken(stsClient stsiface.STSAPI, clusterID string, clock clockwork.Clock) (string, time.Time, error) {
func GenAWSEKSToken(ctx context.Context, stsClient STSPresignClient, clusterID string, clock clockwork.Clock) (string, time.Time, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

now that this package was converted to sdkv2, we can make unify

func presignCallerIdentityURL(ctx context.Context, stsClient *sts.Client, clusterName string) (string, error) {
presignClient := sts.NewPresignClient(stsClient)
// This function adds required headers for accessing an EKS cluster to the presigned URL.
// Header "x-k8s-aws-id" specifies EKS cluster name and header "X-Amz-Expires" is just required for compatibility reasons.
addEKSHeaders := func(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (
out middleware.BuildOutput, metadata middleware.Metadata, err error,
) {
req, ok := in.Request.(*smithyhttp.Request)
if !ok {
return out, metadata, fmt.Errorf("unknown transport type %T", req)
}
req.Header.Add(awsHeaderClusterName, clusterName)
// 60 is put for compatibility reasons, in reality it is ignored and real expiration time is 15 minutes.
req.Header.Add(awsHeaderExpires, "60")
return next.HandleBuild(ctx, in)
}
presigned, err := presignClient.PresignGetCallerIdentity(ctx, &sts.GetCallerIdentityInput{}, func(options *sts.PresignOptions) {
options.ClientOptions = append(options.ClientOptions,
sts.WithAPIOptions(func(stack *middleware.Stack) error {
return stack.Build.Add(middleware.BuildMiddlewareFunc("AddEKSHeaders", addEKSHeaders), 0)
}))
})
if err != nil {
return "", trace.Wrap(err, "failed to presign caller identity")
}
return presigned.URL, nil
}
to use this function

Copy link
Contributor

Choose a reason for hiding this comment

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

They are essentially doing the same thing

Copy link
Member Author

@creack creack Jan 8, 2025

Choose a reason for hiding this comment

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

maybe better in a separate PR as the integration one requires a full blown sts Client, which would be difficult to test. Added a TODO.

@@ -115,6 +117,13 @@ type gcpInstaller interface {
type Config struct {
// CloudClients is an interface for retrieving cloud clients.
CloudClients cloud.Clients

// FetchersClients gets the AWS clients for the given region for the fetchers.
FetchersClients fetchers.ClientGetter
Copy link
Contributor

Choose a reason for hiding this comment

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

IF we reserve FetchersClients for AWS, it should be more clear about it.
We should probably also split the cloud clients as well

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the idea indeed, EC2 and SSM have been split out from cloud clients already, this extracts out completely EKS and partially STS. I'll rename to be more explicit about AWS.

c *Config
}

func (f *fetchersClientsGetter) GetAWSEKSClient(ctx context.Context, region string, opts ...awsconfig.OptionsFn) (fetchers.EKSClient, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I recommend just making these into simple functions that take an aws.Config and return the relevant interface, and using the awsconfig.Provider along side them instead.

Then in tests you just set Config.AWSConfigProvider to &mocks.AWSConfigProvider{} to mock out the config loading

It's a little more tedious/ceremony because you end up doing this:

awsCfg, err := foo.getAWSConfig(...options...)
if err != nil {
 ...
}
barClt := foo.GetBarClient(awsCfg)

But the benefit is that you always test the options passed to getAWSConfig, and each client can be mocked separately as a trivial function rather than trying to fake config loading in each mock client provider.

I nitpick on this because the sdk v1 cloud clients code was structured like you've done so here and I've found that tests end up faking more than they should, e.g not checking the credentials source at all.

Copy link
Member Author

Choose a reason for hiding this comment

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

I followed #48950 model 😅 we probably should align everything. Should it be done upfront here though, or in a separate PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
database-access Database access related issues and PRs discovery kubernetes-access no-changelog Indicates that a PR does not require a changelog entry size/md
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Convert EKS discovery code to use aws-sdk-go-v2
4 participants