Skip to content

feat: Decompose CRT default chain into individual wrappers #1926

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

Merged
merged 3 commits into from
Apr 30, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//

@_spi(FileBasedConfig) import AWSSDKCommon
@_spi(DefaultAWSCredentialIdentityResolverChain) import AWSSDKIdentity
import AWSSDKIdentity
import SmithyIdentity
import SmithyIdentityAPI
import struct ClientRuntime.DefaultSDKRuntimeConfiguration
Expand All @@ -28,13 +28,10 @@ public class AWSClientConfigDefaultsProvider: ClientConfigDefaultsProvider {
_ awsCredentialIdentityResolver: (any AWSCredentialIdentityResolver)? = nil
) throws -> any AWSCredentialIdentityResolver {
let resolvedAWSCredentialIdentityResolver: any AWSCredentialIdentityResolver
let fileBasedConfig = try CRTFileBasedConfiguration.make()
if let awsCredentialIdentityResolver {
resolvedAWSCredentialIdentityResolver = awsCredentialIdentityResolver
} else {
resolvedAWSCredentialIdentityResolver = try DefaultAWSCredentialIdentityResolverChain(
fileBasedConfig: fileBasedConfig
)
resolvedAWSCredentialIdentityResolver = DefaultAWSCredentialIdentityResolverChain()
}
return resolvedAWSCredentialIdentityResolver
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import class AwsCommonRuntimeKit.CredentialsProvider
import ClientRuntime
import protocol SmithyIdentity.AWSCredentialIdentityResolvedByCRT
@_spi(FileBasedConfig) import AWSSDKCommon
import protocol SmithyIdentity.AWSCredentialIdentityResolver
import struct Smithy.Attributes

// swiftlint:disable type_name
// ^ Required to mute swiftlint warning about type name being too long.
Expand All @@ -24,21 +26,33 @@ import protocol SmithyIdentity.AWSCredentialIdentityResolvedByCRT
/// 5. EC2 Instance Metadata (IMDSv2)
///
/// The credentials retrieved from the chain are cached for 15 minutes.
public struct DefaultAWSCredentialIdentityResolverChain: AWSCredentialIdentityResolvedByCRT {
public let crtAWSCredentialIdentityResolver: AwsCommonRuntimeKit.CredentialsProvider

public struct DefaultAWSCredentialIdentityResolverChain: AWSCredentialIdentityResolver {
/// Creates a credential identity resolver that uses the default AWS credential identity resolver chain used by most AWS SDKs.
public init() throws {
let fileBasedConfig = try CRTFileBasedConfiguration()
try self.init(fileBasedConfig: fileBasedConfig)
}
public init() {}

public func getIdentity(identityProperties: Attributes?) async throws -> AWSCredentialIdentity {
typealias ResolverFactory = () throws -> any AWSCredentialIdentityResolver

let resolverFactories: [ResolverFactory] = [
{ try EnvironmentAWSCredentialIdentityResolver() },
{ try ProfileAWSCredentialIdentityResolver() },
{ try STSWebIdentityAWSCredentialIdentityResolver() },
{ try ECSAWSCredentialIdentityResolver() },
{ try IMDSAWSCredentialIdentityResolver() }
]

let lastIndex = resolverFactories.count - 1
for index in 0..<lastIndex {
do {
let resolver = try resolverFactories[index]()
return try await resolver.getIdentity(identityProperties: identityProperties)
} catch {
// Continue to the next resolver factory.
}
}

@_spi(DefaultAWSCredentialIdentityResolverChain)
public init(fileBasedConfig: CRTFileBasedConfiguration) throws {
self.crtAWSCredentialIdentityResolver = try AwsCommonRuntimeKit.CredentialsProvider(source: .defaultChain(
bootstrap: SDKDefaultIO.shared.clientBootstrap,
fileBasedConfiguration: fileBasedConfig
))
// The error thrown from the last resolver is not caught and instead gets thrown to caller.
return try await resolverFactories[lastIndex]().getIdentity(identityProperties: identityProperties)
Comment on lines +54 to +55
Copy link
Contributor

Choose a reason for hiding this comment

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

will this always throw the IMDS error if all resolver factories fail? If so this was a huge pain from the user perspective when we used CRT so we should take this opportunity to throw a more descriptive error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a great point, should we include that as part of the final breaking change PR tho? To maintain error behavior the same for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

@jbelkins what do you think? Change this now or change this later

Copy link
Contributor

Choose a reason for hiding this comment

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

I tend to agree with changing this now. The error should give some info about the specific providers in the chain that failed. Returning an IMDS error just because that’s the last in the chain does not make it clear what has really happened

Copy link
Contributor

Choose a reason for hiding this comment

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

After discussion, I recommend changing this error along with other breaking changes, i.e. adding to or reordering the chain.

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class DefaultAWSCredentialIdentityResolverChainTests: XCTestCase {
unsetenv("AWS_SECRET_ACCESS_KEY")
}

let subject = try DefaultAWSCredentialIdentityResolverChain()
let subject = DefaultAWSCredentialIdentityResolverChain()
let credentials = try await subject.getIdentity()

XCTAssertEqual(credentials.accessKey, "some_access_key_b")
Expand Down
Loading