Skip to content

Conversation

emilia-grant
Copy link

@emilia-grant emilia-grant commented Oct 14, 2025

This PR updates awsutil to use aws-sdk-go-v2 because v1 has gone EOL. This is also part of addressing ICU-17267.

It required an API breaking change, so I bumped the mod version to v3. Not sure if this is the correct way to do it here (does this remove v2?)

I also refactored the tests into a suite pattern as a demo. This is a particularly good spot to use the suite pattern because the code only gets touched once in a blue moon, so encoding as much understanding about how the code should function directly into tests will help the next person two years from now.

PCI review checklist

  • I have documented a clear reason for, and description of, the change I am making.

  • If applicable, I've documented a plan to revert these changes if they require more than reverting the pull request.

  • If applicable, I've documented the impact of any changes to security controls.

    Examples of changes to security controls include using new access control methods, adding or removing logging pipelines, etc.

Copy link

hashicorp-cla-app bot commented Oct 14, 2025

CLA assistant check
All committers have signed the CLA.

Copy link

CLA assistant check

Thank you for your submission! We require that all contributors sign our Contributor License Agreement ("CLA") before we can accept the contribution. Read and sign the agreement

Learn more about why HashiCorp requires a CLA and what the CLA includes

Have you signed the CLA already but the status is still pending? Recheck it.

@psekar psekar requested review from ddebko, johanbrandhorst, kheina, louisruch and sgmiller and removed request for sgmiller October 14, 2025 23:44
Copy link
Collaborator

@johanbrandhorst johanbrandhorst left a comment

Choose a reason for hiding this comment

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

Thank you so much for updating the tests - they were in dire need of some love! I'm not sure I see the utility in using gomock or a test suite in this specific case though. Do you think you could try refactoring the tests so that they use the basic Go test setup (e.g. TestFoo(t *testing.T) and t.Run for sub tests), so we can compare? I'd also appreciate if you could separate the changes into separate commits, so I can view:

  1. Just the dependency upgrade (with any changes to existing tests required). This can also include the module version bump
  2. Just the test changes

I love the new tests but I don't know if we need the suite or gomock integratoin. What do you think?

Encrypt(ctx context.Context, input *kms.EncryptInput, opts ...func(*kms.Options)) (*kms.EncryptOutput, error)
Decrypt(ctx context.Context, input *kms.DecryptInput, opts ...func(*kms.Options)) (*kms.DecryptOutput, error)
DescribeKey(ctx context.Context, inpput *kms.DescribeKeyInput, opts ...func(*kms.Options)) (*kms.DescribeKeyOutput, error)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Was the old interface removed? Please add a doc comment since this is exported.

const (
EnvAwsKmsWrapperKeyId = "AWSKMS_WRAPPER_KEY_ID"
EnvVaultAwsKmsSealKeyId = "VAULT_AWSKMS_SEAL_KEY_ID"
EnvAwsKmsEndpoint = "AWS_KMS_ENDPOINT"
Copy link
Collaborator

Choose a reason for hiding this comment

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

I see below that we need to keep this environment variable, but could we perhaps also look for AWSKMS_ENDPOINT and deprecate AWS_KMS_ENDPOINT? It's unfortunate that the formatting differs to AWSKMS_WRAPPER_KEY_ID IMO.

return nil, errors.New("no key information returned")
}
k.currentKeyId.Store(aws.StringValue(keyInfo.KeyMetadata.KeyId))
k.currentKeyId.Store(*keyInfo.KeyMetadata.KeyId)
Copy link
Collaborator

Choose a reason for hiding this comment

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

what is the type of KeyId? Are we storing a pointer?

Copy link
Author

Choose a reason for hiding this comment

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

k.currentKeyId stores a string but keyinfo.KeyMetadata stores a *string. The reason for this was that aws v1 used pointers for basically all fields & parameters and so provided aws.String() to work around the awkwardness of taking the address of a module level const string.

They changed philosophies in sdk v2 and use fewer pointers, so I figured I'd use the plain pointer syntax to make clearer that there's nothing special going on with the types here.

Comment on lines +292 to +304
credsConfig := &awsutil.CredentialsConfig{
AccessKey: k.accessKey,
SecretKey: k.secretKey,
SessionToken: k.sessionToken,
Filename: k.sharedCredsFilename,
Profile: k.sharedCredsProfile,
RoleARN: k.roleArn,
RoleSessionName: k.roleSessionName,
WebIdentityTokenFile: k.webIdentityTokenFile,
Region: k.region,
Logger: k.logger,
HTTPClient: cleanhttp.DefaultClient(),
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice!

Comment on lines +326 to +328
kmsOpts := []func(*kms.Options){}

client := kms.NewFromConfig(cfg, kmsOpts...)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we intend to add any options here? I think we can just omit this variable if not.

@@ -1,33 +1,48 @@
module github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2
module github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v3
Copy link
Collaborator

Choose a reason for hiding this comment

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

This seems reasonable to me since it's a major dependency change, thanks.

Copy link
Author

Choose a reason for hiding this comment

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

Is this the correct way to do it so that we don't break client code on v2? i.e. if I merge this, will other modules still be able to pull v2? I was debating this vs have a separate v2 and v3 folder

module github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v3

go 1.20
go 1.24.0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Good call keeping this N-1

@emilia-grant
Copy link
Author

Thank you so much for updating the tests - they were in dire need of some love! I'm not sure I see the utility in using gomock or a test suite in this specific case though. Do you think you could try refactoring the tests so that they use the basic Go test setup (e.g. TestFoo(t *testing.T) and t.Run for sub tests), so we can compare? I'd also appreciate if you could separate the changes into separate commits, so I can view:

1. Just the dependency upgrade (with any changes to existing tests required). This can also include the module version bump

2. Just the test changes

I love the new tests but I don't know if we need the suite or gomock integratoin. What do you think?

Yeah I'll split them out into a couple commits to see the difference. I don't think gomock is necessary here since we already have mocks created and we don't use thorough enough DI to get the benefits.

I do think the suite pattern here is my hill to die on, though. Once I split out the commits, it'll be crystal clear how much clarity it's providing here, but I have another example in the meantime from another module I'm working on: https://github.com/hashicorp/go-secure-stdlib/blob/main/awsutil/generate_credentials_test.go#L232. Take a look at the suite I wrote out in this PR and then compare it against the TestGenerateAwsConfigOptions tests (with no offense towards the person that wrote the tests. It's the pattern not the person)

  • We're already writing adhoc test suites, we're just doing it with worse tooling (inline test case types and loops with t.Run). This means every non-trivial test function we write gets its own bespoke test harness logic that's just different enough to trip you up.
  • This particular adhoc suite is actually three separate logical suites kludged together. You can tell by the three giant if statements on lines 409, 422, and 431. Each one tests a completely different subset of functionality.
    • Now why did this happen? Because splitting it into three functions is a giant PITA. It requires three new loops, three sets of boilerplate, and three sets of assertions. Instead, all of the logic got mashed together and each new test case added now has to fulfill additional unrelated assertions, even if thing being tested doesn't interact with those assertions. (I know because I'm in the middle of divining exactly which fields need to be set to make my test pass.)
  • It might seem innocuous, but the secret, sinister part is that any of those test cases could have implicit, hidden dependencies on state set up by any of the tests run before it. How well do you know the side effects of the functions under test? (0% for me!) How much do you trust the developer that wrote the test to handle cleanup? (No idea!)
  • The kicker for me is imagining being the developer who gets to step into this codebase after no one's touched it for the next two years. Would you rather see a test suite or a kludge of test functions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants