-
Notifications
You must be signed in to change notification settings - Fork 74
/
AwsSecretsManagerPreprocessorTest.kt
129 lines (113 loc) · 4.87 KB
/
AwsSecretsManagerPreprocessorTest.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package com.sksamuel.hoplite.aws
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder
import com.amazonaws.services.secretsmanager.model.CreateSecretRequest
import com.sksamuel.hoplite.CommonMetadata
import com.sksamuel.hoplite.ConfigException
import com.sksamuel.hoplite.ConfigFailure
import com.sksamuel.hoplite.ConfigLoaderBuilder
import com.sksamuel.hoplite.Pos
import com.sksamuel.hoplite.StringNode
import com.sksamuel.hoplite.decoder.DotPath
import com.sksamuel.hoplite.fp.Validated
import com.sksamuel.hoplite.parsers.PropsPropertySource
import com.sksamuel.hoplite.traverse
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.extensions.install
import io.kotest.core.spec.style.FunSpec
import io.kotest.extensions.testcontainers.TestContainerExtension
import io.kotest.matchers.nulls.shouldNotBeNull
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldNotContain
import io.kotest.matchers.types.shouldBeInstanceOf
import org.testcontainers.containers.localstack.LocalStackContainer
import org.testcontainers.utility.DockerImageName
import java.util.Properties
class AwsSecretsManagerPreprocessorTest : FunSpec() {
private val localstackImage = DockerImageName.parse("localstack/localstack:0.14.2")
private val localstack = LocalStackContainer(localstackImage).withServices(LocalStackContainer.Service.SECRETSMANAGER)
init {
install(TestContainerExtension(localstack))
val client = AWSSecretsManagerClientBuilder.standard()
.withEndpointConfiguration(localstack.getEndpointConfiguration(LocalStackContainer.Service.SECRETSMANAGER))
.withCredentials(localstack.defaultCredentialsProvider)
.build()
client.createSecret(CreateSecretRequest().withName("foo").withSecretString("secret!"))
client.createSecret(CreateSecretRequest().withName("bubble").withSecretString("""{"f": "1", "g": "2"}"""))
test("placeholder should be detected and used") {
ConfigLoaderBuilder.default()
.addPreprocessor(AwsSecretsManagerPreprocessor { client })
.build()
.loadConfigOrThrow<ConfigHolder>("/secrets.props")
.a.shouldBe("secret!")
}
test("node should be annotated with IsDecodedSecret attribute") {
ConfigLoaderBuilder.default()
.addPreprocessor(AwsSecretsManagerPreprocessor { client })
.build()
.loadNodeOrThrow("/secrets.props")
.traverse()
.find { it.path == DotPath("a") }
.shouldNotBeNull().meta[CommonMetadata.Secret] shouldBe true
}
test("node should be annotated with UnprocessedValue attribute") {
ConfigLoaderBuilder.default()
.addPreprocessor(AwsSecretsManagerPreprocessor { client })
.build()
.loadNodeOrThrow("/secrets.props")
.traverse()
.find { it.path == DotPath("a") }
.shouldNotBeNull().meta[CommonMetadata.UnprocessedValue] shouldBe "awssm://foo"
}
test("unknown secret should return error and include key") {
AwsSecretsManagerPreprocessor { client }.process(
StringNode(
"secretsmanager://unkunk",
Pos.NoPos,
DotPath.root,
emptyMap()
)
).shouldBeInstanceOf<Validated.Invalid<ConfigFailure>>().error.description().shouldContain("unkunk")
}
test("empty secret should return error and include key") {
client.createSecret(CreateSecretRequest().withName("bibblebobble").withSecretString(""))
AwsSecretsManagerPreprocessor { client }.process(
StringNode(
"secretsmanager://bibblebobble",
Pos.NoPos,
DotPath.root,
emptyMap(),
)
).shouldBeInstanceOf<Validated.Invalid<ConfigFailure>>().error.description().shouldContain("Empty secret")
}
test("unknown secret should return error and not include prefix") {
AwsSecretsManagerPreprocessor { client }.process(
StringNode(
"secretsmanager://unkunk", Pos.NoPos, DotPath.root,
emptyMap()
)
)
.shouldBeInstanceOf<Validated.Invalid<ConfigFailure>>().error.description()
.shouldNotContain("secretsmanager://")
}
test("multiple errors should be returned at once") {
shouldThrow<ConfigException> {
ConfigLoaderBuilder.default()
.addPreprocessor(AwsSecretsManagerPreprocessor { client })
.build()
.loadConfigOrThrow<ConfigHolder>("/multiple_secrets.props")
}.message.shouldContain("foo.bar").shouldContain("bar.baz")
}
test("should support index keys") {
val props = Properties()
props["a"] = "awssm://bubble[f]"
ConfigLoaderBuilder.default()
.addPreprocessor(AwsSecretsManagerPreprocessor { client })
.addPropertySource(PropsPropertySource(props))
.build()
.loadConfigOrThrow<ConfigHolder>()
.a shouldBe "1"
}
}
data class ConfigHolder(val a: String)
}