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
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package aws.sdk.kotlin.runtime.config

import aws.sdk.kotlin.runtime.auth.credentials.CredentialsProvider
import aws.sdk.kotlin.runtime.auth.credentials.DefaultChainCredentialsProvider
import aws.sdk.kotlin.runtime.client.AwsClientConfig
import aws.sdk.kotlin.runtime.region.resolveRegion
import aws.smithy.kotlin.runtime.util.Platform
import aws.smithy.kotlin.runtime.util.PlatformProvider

/**
* Options that control how an [aws.sdk.kotlin.runtime.client.AwsClientConfig] is resolved
*/
public class AwsClientConfigLoadOptions {
// TODO - see Go SDK for an idea of possible knobs
// https://github.com/aws/aws-sdk-go-v2/blob/973e5f5e9477e0690bcb4be3145bb72e956651cc/config/load_options.go#L22
// Most of these will not be needed if you are using a corresponding environment variable or system property but there should be an explicit
// option to control behavior.

/**
* The region to make requests to. When set, region will not attempt to be resolved from other sources
*/
public var region: String? = null

/**
* The [CredentialsProvider] to use when sourcing [aws.sdk.kotlin.runtime.auth.credentials.Credentials].
*/
public var credentialsProvider: CredentialsProvider? = null

// FIXME - expose profile name override and thread through region/cred provider chains
}

/**
* Load the AWS client configuration from the environment.
*/
public suspend fun AwsClientConfig.Companion.fromEnvironment(
block: AwsClientConfigLoadOptions.() -> Unit = {}
): AwsClientConfig = loadAwsClientConfig(Platform, block)

internal suspend fun loadAwsClientConfig(
platformProvider: PlatformProvider,
block: AwsClientConfigLoadOptions.() -> Unit
): AwsClientConfig {
val opts = AwsClientConfigLoadOptions().apply(block)

val region = opts.region ?: resolveRegion(platformProvider)

val credentialsProvider = opts.credentialsProvider ?: DefaultChainCredentialsProvider()

return ResolvedAwsConfig(region, credentialsProvider)
}

private data class ResolvedAwsConfig(
override val region: String,
override val credentialsProvider: CredentialsProvider
) : AwsClientConfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ import aws.smithy.kotlin.runtime.util.PlatformProvider
* 3. Check the AWS config files/profile for region information
* 4. If running on EC2, check the EC2 metadata service for region
*/
public expect class DefaultRegionProviderChain public constructor(
internal expect class DefaultRegionProviderChain constructor(
platformProvider: PlatformProvider = Platform
) : RegionProvider, Closeable
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ private const val REGION_PATH: String = "/latest/meta-data/placement/region"
* @param client the IMDS client to use to resolve region information with
* @param platformProvider the [PlatformEnvironProvider] instance
*/
public class ImdsRegionProvider(
internal class ImdsRegionProvider(
private val client: Lazy<ImdsClient> = lazy { ImdsClient() },
private val platformProvider: PlatformEnvironProvider = Platform,
) : RegionProvider, Closeable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,14 @@

package aws.sdk.kotlin.runtime.region

import aws.sdk.kotlin.runtime.ClientException
import aws.sdk.kotlin.runtime.InternalSdkApi
import aws.sdk.kotlin.runtime.client.AwsAdvancedClientOption
import aws.sdk.kotlin.runtime.client.AwsClientOption
import aws.smithy.kotlin.runtime.client.ExecutionContext
import aws.sdk.kotlin.runtime.ConfigurationException
import aws.smithy.kotlin.runtime.io.use
import aws.smithy.kotlin.runtime.util.PlatformProvider

/**
* Attempt to resolve the region to make requests to.
*
* Regions are resolved in the following order:
* 1. From the existing [ctx]
* 2. From the region [config]
* 3. Using default region detection (only if-enabled)
*/
@InternalSdkApi
public suspend fun resolveRegionForOperation(ctx: ExecutionContext, config: RegionConfig): String {
// favor the context if it's already set
val regionFromCtx = ctx.getOrNull(AwsClientOption.Region)
if (regionFromCtx != null) return regionFromCtx

// use the default from the service config if configured
if (config.region != null) return config.region!!

// attempt to detect
val allowDefaultRegionDetect = ctx.getOrNull(AwsAdvancedClientOption.EnableDefaultRegionDetection) ?: true
if (!allowDefaultRegionDetect) {
throw ClientException("No region was configured and region detection has been disabled")
internal suspend fun resolveRegion(platformProvider: PlatformProvider): String =
DefaultRegionProviderChain(platformProvider).use { providerChain ->
providerChain.getRegion() ?: throw ConfigurationException("unable to auto detect AWS region, tried: $providerChain")
}

// TODO - propagate any relevant ctx/config to the default chain
val providerChain = DefaultRegionProviderChain()
return providerChain.getRegion() ?: throw ClientException("unable to auto detect AWS region, tried: $providerChain")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package aws.sdk.kotlin.runtime.config

import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider
import aws.sdk.kotlin.runtime.testing.TestPlatformProvider
import aws.sdk.kotlin.runtime.testing.runSuspendTest
import kotlin.test.Test
import kotlin.test.assertEquals

class AwsClientConfigLoaderTest {
@Test
fun testExplicit(): Unit = runSuspendTest {
val provider = TestPlatformProvider()
val actual = loadAwsClientConfig(provider) {
region = "us-east-2"
credentialsProvider = StaticCredentialsProvider {
accessKeyId = "AKID"
secretAccessKey = "secret"
}
}
assertEquals("us-east-2", actual.region)
assertEquals("AKID", actual.credentialsProvider.getCredentials().accessKeyId)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ package aws.sdk.kotlin.runtime.region
import aws.smithy.kotlin.runtime.io.Closeable
import aws.smithy.kotlin.runtime.util.PlatformProvider

public actual class DefaultRegionProviderChain public actual constructor(
internal actual class DefaultRegionProviderChain actual constructor(
platformProvider: PlatformProvider
) : RegionProvider,
Closeable,
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package aws.sdk.kotlin.runtime.client

import aws.sdk.kotlin.runtime.auth.credentials.CredentialsProvider

/**
* Shared AWS service client configuration that all AWS service clients implement as part of their configuration state.
*/
public interface AwsClientConfig {
/**
* The AWS region to make requests to
*/
public val region: String

/**
* The [CredentialsProvider] that will be called to resolve credentials before making AWS service calls
*/
public val credentialsProvider: CredentialsProvider

public companion object {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Question: Why does AwsClientConfig need a public empty companion object?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's extended in aws-config

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ object AwsRuntimeTypes {
object Types {
val CredentialsProvider = runtimeSymbol("CredentialsProvider", AwsKotlinDependency.AWS_TYPES, "auth.credentials")
val Credentials = runtimeSymbol("Credentials", AwsKotlinDependency.AWS_TYPES, "auth.credentials")
val AwsClientConfig = runtimeSymbol("AwsClientConfig", AwsKotlinDependency.AWS_TYPES, "client")
}

object Config {
Expand All @@ -51,6 +52,9 @@ object AwsRuntimeTypes {
val DefaultChainCredentialsProvider = runtimeSymbol("DefaultChainCredentialsProvider", AwsKotlinDependency.AWS_CONFIG, "auth.credentials")
val StaticCredentialsProvider = runtimeSymbol("StaticCredentialsProvider", AwsKotlinDependency.AWS_CONFIG, "auth.credentials")
}

val AwsClientConfigLoadOptions = runtimeSymbol("AwsClientConfigLoadOptions", AwsKotlinDependency.AWS_CONFIG, "config")
val fromEnvironment = runtimeSymbol("fromEnvironment", AwsKotlinDependency.AWS_CONFIG, "config")
}

object Signing {
Expand Down
Loading