Skip to content

Commit 96fdd7f

Browse files
authored
fix: enable DefaultRegionProviderChainTest to pass in environments where an IMDS client is already present (#369)
1 parent 2569768 commit 96fdd7f

File tree

7 files changed

+116
-9
lines changed

7 files changed

+116
-9
lines changed

aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/auth/credentials/ImdsCredentialsProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package aws.sdk.kotlin.runtime.auth.credentials
88
import aws.sdk.kotlin.runtime.config.AwsSdkSetting
99
import aws.sdk.kotlin.runtime.config.imds.EC2MetadataError
1010
import aws.sdk.kotlin.runtime.config.imds.ImdsClient
11+
import aws.sdk.kotlin.runtime.config.imds.InstanceMetadataProvider
1112
import aws.sdk.kotlin.runtime.config.resolve
1213
import aws.smithy.kotlin.runtime.http.HttpStatusCode
1314
import aws.smithy.kotlin.runtime.io.Closeable
@@ -36,7 +37,7 @@ private const val CODE_ASSUME_ROLE_UNAUTHORIZED_ACCESS: String = "AssumeRoleUnau
3637
*/
3738
public class ImdsCredentialsProvider(
3839
private val profileOverride: String? = null,
39-
private val client: Lazy<ImdsClient> = lazy { ImdsClient() },
40+
private val client: Lazy<InstanceMetadataProvider> = lazy { ImdsClient() },
4041
private val platformProvider: PlatformEnvironProvider = Platform
4142
) : CredentialsProvider, Closeable {
4243
private val logger = Logger.getLogger<ImdsCredentialsProvider>()

aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/config/imds/ImdsClient.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ internal const val DEFAULT_MAX_RETRIES: UInt = 3u
3838

3939
private const val SERVICE = "imds"
4040

41+
/**
42+
* Represents a generic client that can fetch instance metadata.
43+
*/
44+
public interface InstanceMetadataProvider : Closeable {
45+
/**
46+
* Gets the specified instance metadata value by the given path.
47+
*/
48+
public suspend fun get(path: String): String
49+
}
50+
4151
/**
4252
* IMDSv2 Client
4353
*
@@ -48,7 +58,7 @@ private const val SERVICE = "imds"
4858
* for more information.
4959
*/
5060
@OptIn(ExperimentalTime::class)
51-
public class ImdsClient private constructor(builder: Builder) : Closeable {
61+
public class ImdsClient private constructor(builder: Builder) : InstanceMetadataProvider {
5262
public constructor() : this(Builder())
5363

5464
private val logger = Logger.getLogger<ImdsClient>()
@@ -115,7 +125,7 @@ public class ImdsClient private constructor(builder: Builder) : Closeable {
115125
* val amiId = client.get("/latest/meta-data/ami-id")
116126
* ```
117127
*/
118-
public suspend fun get(path: String): String {
128+
public override suspend fun get(path: String): String {
119129
val op = SdkHttpOperation.build<Unit, String> {
120130
serializer = UnitSerializer
121131
deserializer = object : HttpDeserialize<String> {

aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/region/DefaultRegionProviderChain.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package aws.sdk.kotlin.runtime.region
77

8+
import aws.sdk.kotlin.runtime.config.imds.ImdsClient
9+
import aws.sdk.kotlin.runtime.config.imds.InstanceMetadataProvider
810
import aws.smithy.kotlin.runtime.io.Closeable
911
import aws.smithy.kotlin.runtime.util.Platform
1012
import aws.smithy.kotlin.runtime.util.PlatformProvider
@@ -17,5 +19,6 @@ import aws.smithy.kotlin.runtime.util.PlatformProvider
1719
* 4. If running on EC2, check the EC2 metadata service for region
1820
*/
1921
internal expect class DefaultRegionProviderChain constructor(
20-
platformProvider: PlatformProvider = Platform
22+
platformProvider: PlatformProvider = Platform,
23+
imdsClient: Lazy<InstanceMetadataProvider> = lazy { ImdsClient() },
2124
) : RegionProvider, Closeable

aws-runtime/aws-config/common/src/aws/sdk/kotlin/runtime/region/ImdsRegionProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package aws.sdk.kotlin.runtime.region
77

88
import aws.sdk.kotlin.runtime.config.AwsSdkSetting
99
import aws.sdk.kotlin.runtime.config.imds.ImdsClient
10+
import aws.sdk.kotlin.runtime.config.imds.InstanceMetadataProvider
1011
import aws.sdk.kotlin.runtime.config.resolve
1112
import aws.smithy.kotlin.runtime.io.Closeable
1213
import aws.smithy.kotlin.runtime.util.Platform
@@ -22,7 +23,7 @@ private const val REGION_PATH: String = "/latest/meta-data/placement/region"
2223
* @param platformProvider the [PlatformEnvironProvider] instance
2324
*/
2425
internal class ImdsRegionProvider(
25-
private val client: Lazy<ImdsClient> = lazy { ImdsClient() },
26+
private val client: Lazy<InstanceMetadataProvider> = lazy { ImdsClient() },
2627
private val platformProvider: PlatformEnvironProvider = Platform,
2728
) : RegionProvider, Closeable {
2829
private val resolvedRegion = asyncLazy(::loadRegion)

aws-runtime/aws-config/common/test/aws/sdk/kotlin/runtime/region/DefaultRegionProviderChainTest.kt

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package aws.sdk.kotlin.runtime.region
77

88
import aws.sdk.kotlin.runtime.testing.TestPlatformProvider
99
import aws.sdk.kotlin.runtime.testing.runSuspendTest
10+
import aws.sdk.kotlin.runtime.util.TestInstanceMetadataProvider
1011
import kotlinx.serialization.json.*
1112
import kotlin.test.Test
1213
import kotlin.test.assertEquals
@@ -15,6 +16,7 @@ class DefaultRegionProviderChainTest {
1516
private data class RegionProviderChainTest(
1617
val name: String,
1718
val platformProvider: TestPlatformProvider,
19+
val instanceMetadataProvider: TestInstanceMetadataProvider,
1820
val region: String?,
1921
val targets: List<String> = emptyList()
2022
)
@@ -26,12 +28,16 @@ class DefaultRegionProviderChainTest {
2628
.map {
2729
val name = it["name"]!!.jsonPrimitive.content
2830
val platform = TestPlatformProvider.fromJsonNode(it["platform"]!!.jsonObject)
31+
val instanceMetadata = TestInstanceMetadataProvider.fromJsonNode(it["imds"]!!.jsonObject)
2932
val region = it["region"]!!.jsonPrimitive.contentOrNull
30-
RegionProviderChainTest(name, platform, region)
33+
RegionProviderChainTest(name, platform, instanceMetadata, region)
3134
}
3235

3336
tests.forEach { test ->
34-
val provider = DefaultRegionProviderChain(test.platformProvider)
37+
val provider = DefaultRegionProviderChain(
38+
platformProvider = test.platformProvider,
39+
imdsClient = lazy { test.instanceMetadataProvider },
40+
)
3541
val actual = provider.getRegion()
3642
assertEquals(test.region, actual, test.name)
3743
}
@@ -62,6 +68,14 @@ fun TestPlatformProvider.Companion.fromJsonNode(obj: JsonObject): TestPlatformPr
6268
return TestPlatformProvider(env, props, fs)
6369
}
6470

71+
/**
72+
* Construct a [TestInstanceMetadataProvider] from a JSON object containing metadata as key-value pairs.
73+
*/
74+
fun TestInstanceMetadataProvider.Companion.fromJsonNode(obj: JsonObject): TestInstanceMetadataProvider {
75+
val metadata = obj.jsonObject.mapValues { it.value.jsonPrimitive.content }
76+
return TestInstanceMetadataProvider(metadata)
77+
}
78+
6579
// language=JSON
6680
private const val regionProviderChainTestSuite = """
6781
[
@@ -72,6 +86,7 @@ private const val regionProviderChainTestSuite = """
7286
"props": {},
7387
"fs": {}
7488
},
89+
"imds": {},
7590
"region": null
7691
},
7792
{
@@ -83,6 +98,7 @@ private const val regionProviderChainTestSuite = """
8398
"props": {},
8499
"fs": {}
85100
},
101+
"imds": {},
86102
"region": "us-east-2"
87103
},
88104
{
@@ -96,6 +112,7 @@ private const val regionProviderChainTestSuite = """
96112
},
97113
"fs": {}
98114
},
115+
"imds": {},
99116
"region": "us-west-1"
100117
},
101118
{
@@ -109,6 +126,7 @@ private const val regionProviderChainTestSuite = """
109126
"config": "[default]\nregion = us-east-2"
110127
}
111128
},
129+
"imds": {},
112130
"region": "us-east-2"
113131
},
114132
{
@@ -123,7 +141,64 @@ private const val regionProviderChainTestSuite = """
123141
"config": "[default]\nregion = us-east-2\n[profile test-profile]\nregion = us-west-1"
124142
}
125143
},
144+
"imds": {},
126145
"region": "us-west-1"
146+
},
147+
{
148+
"name": "imds configured",
149+
"platform": {
150+
"env": {},
151+
"props": {},
152+
"fs": {}
153+
},
154+
"imds": {
155+
"/latest/meta-data/placement/region": "us-east-1"
156+
},
157+
"region": "us-east-1"
158+
},
159+
{
160+
"name": "jvm system properties are favored over imds",
161+
"platform": {
162+
"env": {
163+
"AWS_REGION": "us-east-2"
164+
},
165+
"props": {},
166+
"fs": {}
167+
},
168+
"imds": {
169+
"/latest/meta-data/placement/region": "us-east-1"
170+
},
171+
"region": "us-east-2"
172+
},
173+
{
174+
"name": "environment variables are favored over imds",
175+
"platform": {
176+
"env": {},
177+
"props": {
178+
"aws.region": "us-west-1"
179+
},
180+
"fs": {}
181+
},
182+
"imds": {
183+
"/latest/meta-data/placement/region": "us-east-1"
184+
},
185+
"region": "us-west-1"
186+
},
187+
{
188+
"name": "profile is favored over imds",
189+
"platform": {
190+
"env": {
191+
"AWS_CONFIG_FILE": "config"
192+
},
193+
"props": {},
194+
"fs": {
195+
"config": "[default]\nregion = us-west-2"
196+
}
197+
},
198+
"imds": {
199+
"/latest/meta-data/placement/region": "us-east-1"
200+
},
201+
"region": "us-west-2"
127202
}
128203
]
129204
"""
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
package aws.sdk.kotlin.runtime.util
7+
8+
import aws.sdk.kotlin.runtime.config.imds.InstanceMetadataProvider
9+
10+
public class TestInstanceMetadataProvider(private val metadata: Map<String, String>) : InstanceMetadataProvider {
11+
public companion object { }
12+
13+
override fun close(): Unit = Unit
14+
override suspend fun get(path: String): String = metadata[path] ?: throw IllegalArgumentException("$path missing")
15+
}

aws-runtime/aws-config/jvm/src/aws/sdk/kotlin/runtime/region/DefaultRegionProviderChainJVM.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@
55

66
package aws.sdk.kotlin.runtime.region
77

8+
import aws.sdk.kotlin.runtime.config.imds.InstanceMetadataProvider
89
import aws.smithy.kotlin.runtime.io.Closeable
910
import aws.smithy.kotlin.runtime.util.PlatformProvider
1011

1112
internal actual class DefaultRegionProviderChain actual constructor(
12-
platformProvider: PlatformProvider
13+
platformProvider: PlatformProvider,
14+
imdsClient: Lazy<InstanceMetadataProvider>,
1315
) : RegionProvider,
1416
Closeable,
1517
RegionProviderChain(
1618
JvmSystemPropRegionProvider(platformProvider),
1719
EnvironmentRegionProvider(platformProvider),
1820
ProfileRegionProvider(platformProvider),
19-
ImdsRegionProvider(platformProvider = platformProvider)
21+
ImdsRegionProvider(client = imdsClient, platformProvider = platformProvider)
2022
) {
2123

2224
override fun close() {

0 commit comments

Comments
 (0)