Skip to content

Commit 666fa19

Browse files
checkettssksamuel
andauthored
Add ability to us ane entire JSON formatted secret from AWS Secrets manager as a source (#482)
Co-authored-by: Sam <[email protected]>
1 parent e080c02 commit 666fa19

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.sksamuel.hoplite.aws2
2+
3+
import kotlinx.serialization.decodeFromString
4+
import kotlinx.serialization.json.Json
5+
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient
6+
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest
7+
8+
/**
9+
* Fetches a secret from AWS Secrets manager, which can be added as a
10+
* map source for configuration
11+
*/
12+
class AwsSecretsManagerSource(
13+
private val json: Json = Json.Default,
14+
private val createClient: () -> SecretsManagerClient = { SecretsManagerClient.create() },
15+
) {
16+
17+
private val client by lazy { createClient() }
18+
19+
fun fetchSecretAsMap(key: String): Map<String, String> {
20+
val secretRequest = GetSecretValueRequest.builder().secretId(key).build()
21+
val secret = client.getSecretValue(secretRequest)
22+
23+
return json.decodeFromString<Map<String, String>>(secret.secretString())
24+
}
25+
26+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.sksamuel.hoplite.aws2
2+
3+
import com.sksamuel.hoplite.ConfigLoaderBuilder
4+
import com.sksamuel.hoplite.addMapSource
5+
import com.sksamuel.hoplite.parsers.PropsPropertySource
6+
import io.kotest.core.extensions.install
7+
import io.kotest.core.spec.style.FunSpec
8+
import io.kotest.extensions.testcontainers.ContainerExtension
9+
import io.kotest.matchers.shouldBe
10+
import kotlinx.serialization.json.Json
11+
import org.testcontainers.containers.localstack.LocalStackContainer
12+
import org.testcontainers.utility.DockerImageName
13+
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
14+
import software.amazon.awssdk.regions.Region
15+
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient
16+
import java.util.Properties
17+
18+
class AwsSecretsManagerSourceTest : FunSpec() {
19+
private val localstack = LocalStackContainer(DockerImageName.parse("localstack/localstack:3.6.0"))
20+
.withServices(LocalStackContainer.Service.SECRETSMANAGER)
21+
.withEnv("SKIP_SSL_CERT_DOWNLOAD", "true")
22+
23+
init {
24+
25+
install(ContainerExtension(localstack))
26+
27+
val client = SecretsManagerClient.builder()
28+
.region(Region.US_EAST_1)
29+
.endpointOverride(localstack.getEndpointOverride(LocalStackContainer.Service.SECRETSMANAGER))
30+
.credentialsProvider { AwsBasicCredentials.create(localstack.accessKey, localstack.secretKey) }
31+
.build()
32+
33+
test("should support secret with unquoted number when isLenient is set") {
34+
client.createSecret {
35+
it.name("unquoted")
36+
it.secretString("""{"key1": 222, "key2": "override2"}""")
37+
}
38+
val props = Properties()
39+
props["key2"] = "original2"
40+
41+
val json = Json { isLenient = true }
42+
ConfigLoaderBuilder.default()
43+
.addMapSource(AwsSecretsManagerSource(json = json) { client }.fetchSecretAsMap("unquoted"))
44+
.addPropertySource(PropsPropertySource(props))
45+
.build()
46+
.loadConfigOrThrow<PortHolder>().apply {
47+
key1.shouldBe(222)
48+
key2.shouldBe("override2")
49+
}
50+
51+
}
52+
53+
test("should support secret with quoted number for default") {
54+
client.createSecret {
55+
it.name("quoted")
56+
it.secretString("""{"key1": "222", "key2": "override2"}""")
57+
}
58+
val props = Properties()
59+
props["key2"] = "original2"
60+
ConfigLoaderBuilder.default()
61+
.addMapSource(AwsSecretsManagerSource { client }.fetchSecretAsMap("quoted"))
62+
.addPropertySource(PropsPropertySource(props))
63+
.build()
64+
.loadConfigOrThrow<PortHolder>().apply {
65+
key1.shouldBe(222)
66+
key2.shouldBe("override2")
67+
}
68+
}
69+
}
70+
71+
data class PortHolder(val key1: Int, val key2: String)
72+
}

0 commit comments

Comments
 (0)