Skip to content
This repository was archived by the owner on Jul 26, 2022. It is now read-only.

Commit 9d6c2f9

Browse files
furikakeFlydiverny
authored andcommitted
feat(secrets-manager): Added support for secrets versioning in Secrets Manager using version stage labels (#181)
* Added support for secrets versioning in Secrets Manager using version stage labels
1 parent 0af60a2 commit 9d6c2f9

File tree

5 files changed

+74
-9
lines changed

5 files changed

+74
-9
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,11 @@ spec:
233233
- key: hello-service/credentials
234234
name: username
235235
property: username
236+
- key: hello-service/credentials
237+
name: password
238+
# Version Stage in Secrets Manager
239+
versionStage: AWSPREVIOUS
240+
property: password_previous
236241
```
237242

238243
alternatively you can use `dataFrom` and get all the values from hello-service/credentials:

lib/backends/kv-backend.test.js

+29
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,35 @@ describe('kv-backend', () => {
130130
})
131131
expect(secretPropertyValues).deep.equals([{ fakePropertyName1: 'fakePropertyValue1' }, { fakePropertyName2: 'fakePropertyValue2' }])
132132
})
133+
134+
it('fetches secret property with version', async () => {
135+
kvBackend._get.onFirstCall().resolves('fakePropertyValue1')
136+
kvBackend._get.onSecondCall().resolves('fakePropertyValue2')
137+
138+
const secretPropertyValues = await kvBackend._fetchDataValues({
139+
data: [{
140+
key: 'fakePropertyKey1',
141+
name: 'fakePropertyName1'
142+
}, {
143+
key: 'fakePropertyKey2',
144+
versionStage: 'AWSPREVIOUS',
145+
name: 'fakePropertyName2'
146+
}],
147+
specOptions: {}
148+
})
149+
150+
expect(kvBackend._get.getCall(0).args[0]).to.deep.equal({
151+
key: 'fakePropertyKey1',
152+
keyOptions: {},
153+
specOptions: {}
154+
})
155+
expect(kvBackend._get.getCall(1).args[0]).to.deep.equal({
156+
key: 'fakePropertyKey2',
157+
keyOptions: { versionStage: 'AWSPREVIOUS' },
158+
specOptions: {}
159+
})
160+
expect(secretPropertyValues).deep.equals([{ fakePropertyName1: 'fakePropertyValue1' }, { fakePropertyName2: 'fakePropertyValue2' }])
161+
})
133162
})
134163

135164
describe('_fetchDataFromValues', () => {

lib/backends/secrets-manager-backend.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ class SecretsManagerBackend extends KVBackend {
2020
* Get secret property value from Secrets Manager.
2121
* @param {string} key - Key used to store secret property value in Secrets Manager.
2222
* @param {object} keyOptions - Options for this specific key, eg version etc.
23+
* @param {string} keyOptions.versionStage - Version stage
2324
* @param {object} specOptions - Options for this external secret, eg role
2425
* @param {string} specOptions.roleArn - IAM role arn to assume
2526
* @returns {Promise} Promise object representing secret property value.
2627
*/
27-
async _get ({ key, specOptions: { roleArn } }) {
28+
async _get ({ key, specOptions: { roleArn }, keyOptions: { versionStage = 'AWSCURRENT' } }) {
2829
this._logger.info(`fetching secret property ${key} with role: ${roleArn || 'pods role'}`)
2930

3031
let client = this._client
@@ -41,7 +42,7 @@ class SecretsManagerBackend extends KVBackend {
4142
}
4243

4344
const data = await client
44-
.getSecretValue({ SecretId: key })
45+
.getSecretValue({ SecretId: key, VersionStage: versionStage })
4546
.promise()
4647

4748
if ('SecretBinary' in data) {

lib/backends/secrets-manager-backend.test.js

+35-6
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ describe('SecretsManagerBackend', () => {
1313
let assumeRoleMock
1414
let secretsManagerBackend
1515
const specOptions = {}
16+
const keyOptions = {}
1617

1718
const assumeRoleCredentials = {
1819
Credentials: {
@@ -51,11 +52,13 @@ describe('SecretsManagerBackend', () => {
5152

5253
const secretPropertyValue = await secretsManagerBackend._get({
5354
key: 'fakeSecretKey',
54-
specOptions
55+
specOptions,
56+
keyOptions
5557
})
5658

5759
expect(clientMock.getSecretValue.calledWith({
58-
SecretId: 'fakeSecretKey'
60+
SecretId: 'fakeSecretKey',
61+
VersionStage: 'AWSCURRENT'
5962
})).to.equal(true)
6063
expect(clientFactoryMock.getCall(0).args).deep.equals([])
6164
expect(assumeRoleMock.callCount).equals(0)
@@ -69,11 +72,13 @@ describe('SecretsManagerBackend', () => {
6972

7073
const secretPropertyValue = await secretsManagerBackend._get({
7174
key: 'fakeSecretKey',
72-
specOptions
75+
specOptions,
76+
keyOptions
7377
})
7478

7579
expect(clientMock.getSecretValue.calledWith({
76-
SecretId: 'fakeSecretKey'
80+
SecretId: 'fakeSecretKey',
81+
VersionStage: 'AWSCURRENT'
7782
})).to.equal(true)
7883
expect(clientFactoryMock.getCall(0).args).deep.equals([])
7984
expect(assumeRoleMock.callCount).equals(0)
@@ -87,7 +92,8 @@ describe('SecretsManagerBackend', () => {
8792

8893
const secretPropertyValue = await secretsManagerBackend._get({
8994
key: 'fakeSecretKey',
90-
specOptions: { roleArn: 'my-role' }
95+
specOptions: { roleArn: 'my-role' },
96+
keyOptions
9197
})
9298

9399
expect(clientFactoryMock.lastArg).deep.equals({
@@ -96,7 +102,8 @@ describe('SecretsManagerBackend', () => {
96102
sessionToken: assumeRoleCredentials.Credentials.SessionToken
97103
})
98104
expect(clientMock.getSecretValue.calledWith({
99-
SecretId: 'fakeSecretKey'
105+
SecretId: 'fakeSecretKey',
106+
VersionStage: 'AWSCURRENT'
100107
})).to.equal(true)
101108
expect(clientFactoryMock.getCall(0).args).deep.equals([])
102109
expect(clientFactoryMock.getCall(1).args).deep.equals([{
@@ -107,5 +114,27 @@ describe('SecretsManagerBackend', () => {
107114
expect(assumeRoleMock.callCount).equals(1)
108115
expect(secretPropertyValue).equals('fakeAssumeRoleSecretValue')
109116
})
117+
118+
it('returns secret property value with versionStage', async () => {
119+
getSecretValuePromise.promise.resolves({
120+
SecretString: 'fakeSecretPropertyValuePreviousVersion'
121+
})
122+
123+
const secretPropertyValue = await secretsManagerBackend._get({
124+
key: 'fakeSecretKey',
125+
specOptions,
126+
keyOptions: {
127+
versionStage: 'AWSPREVIOUS'
128+
}
129+
})
130+
131+
expect(clientMock.getSecretValue.calledWith({
132+
SecretId: 'fakeSecretKey',
133+
VersionStage: 'AWSPREVIOUS'
134+
})).to.equal(true)
135+
expect(clientFactoryMock.getCall(0).args).deep.equals([])
136+
expect(assumeRoleMock.callCount).equals(0)
137+
expect(secretPropertyValue).equals('fakeSecretPropertyValuePreviousVersion')
138+
})
110139
})
111140
})

lib/poller.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const merge = require('lodash.merge')
1313
* @param {string} properties[].name - Kubernetes Secret property name.
1414
* @param {string} properties[].property - If the backend secret is an
1515
* object, this is the property name of the value to use.
16+
* @param {string} properties[].versionStage - If the backend supports versioning, eg secretsManager backend
1617
*/
1718

1819
/** Poller class. */
@@ -132,7 +133,7 @@ class Poller {
132133
}
133134

134135
/**
135-
* Create or update Kubernets secret in the cluster.
136+
* Create or update Kubernetes secret in the cluster.
136137
* @returns {Promise} Promise object representing operation result.
137138
*/
138139
async _upsertKubernetesSecret () {

0 commit comments

Comments
 (0)