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

Commit d2ebaeb

Browse files
authored
feat: add validation to CRD (#208)
* feat: add validation to CRD * docs: add deprecation notice * docs: bump prereq kubernetes version
1 parent 7abc3ac commit d2ebaeb

15 files changed

+163
-66
lines changed

README.md

+14-3
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,17 @@ data:
174174
password: MTIzNA==
175175
```
176176

177+
## Deprecations
178+
179+
A few properties has changed name overtime, we still maintain backwards compatbility with these but they will eventually be removed, and they are not validated using the CRD validation.
180+
181+
| Old | New |
182+
| ----------------------------- | ------------------------------ |
183+
| `secretDescriptor` | `spec` |
184+
| `spec.type` | `spec.template.type` |
185+
| `spec.properties` | `spec.data` |
186+
| `backendType: secretManager` | `backendType: secretsManager` |
187+
177188
## Backends
178189

179190
kubernetes-external-secrets supports AWS Secrets Manager, AWS System Manager, and Hashicorp Vault.
@@ -197,7 +208,7 @@ aws secretsmanager create-secret --region us-west-2 --name hello-service/credent
197208
We can declare which properties we want from hello-service/credentials:
198209

199210
```yml
200-
apiVersion: 'kubernetes-client.io/v1'
211+
apiVersion: kubernetes-client.io/v1
201212
kind: ExternalSecret
202213
metadata:
203214
name: hello-service
@@ -217,7 +228,7 @@ spec:
217228
alternatively you can use `dataFrom` and get all the values from hello-service/credentials:
218229

219230
```yml
220-
apiVersion: 'kubernetes-client.io/v1'
231+
apiVersion: kubernetes-client.io/v1
221232
kind: ExternalSecret
222233
metadata:
223234
name: hello-service
@@ -232,7 +243,7 @@ spec:
232243
`data` and `dataFrom` can of course be combined, any naming conflicts will use the last defined, with `data` overriding `dataFrom`
233244

234245
```yml
235-
apiVersion: 'kubernetes-client.io/v1'
246+
apiVersion: kubernetes-client.io/v1
236247
kind: ExternalSecret
237248
metadata:
238249
name: hello-service

charts/kubernetes-external-secrets/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ $ helm install external-secrets/kubernetes-external-secrets
1111

1212
## Prerequisites
1313

14-
* Kubernetes 1.7+
14+
* Kubernetes 1.12+
1515

1616
## Installing the Chart
1717

config/index.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,21 @@ const vault = require('node-vault')
44
const kube = require('kubernetes-client')
55
const KubeRequest = require('kubernetes-client/backends/request')
66
const pino = require('pino')
7+
const yaml = require('js-yaml')
8+
const fs = require('fs')
9+
const path = require('path')
710

811
const awsConfig = require('./aws-config')
912
const envConfig = require('./environment')
1013
const CustomResourceManager = require('../lib/custom-resource-manager')
11-
const customResourceManifest = require('../custom-resource-manifest.json')
1214
const SecretsManagerBackend = require('../lib/backends/secrets-manager-backend')
1315
const SystemManagerBackend = require('../lib/backends/system-manager-backend')
1416
const VaultBackend = require('../lib/backends/vault-backend')
1517

18+
// Get document, or throw exception on error
19+
// eslint-disable-next-line security/detect-non-literal-fs-filename
20+
const customResourceManifest = yaml.safeLoad(fs.readFileSync(path.resolve(__dirname, '../crd.yaml'), 'utf8'))
21+
1622
const kubeconfig = new kube.KubeConfig()
1723
kubeconfig.loadFromDefault()
1824
const kubeBackend = new KubeRequest({ kubeconfig })

crd.yaml

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
apiVersion: apiextensions.k8s.io/v1beta1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
name: externalsecrets.kubernetes-client.io
5+
spec:
6+
group: kubernetes-client.io
7+
version: v1
8+
scope: Namespaced
9+
10+
names:
11+
shortNames:
12+
- es
13+
kind: ExternalSecret
14+
plural: externalsecrets
15+
singular: externalsecret
16+
17+
additionalPrinterColumns:
18+
- JSONPath: .status.lastSync
19+
name: Last Sync
20+
type: date
21+
- JSONPath: .status.status
22+
name: status
23+
type: string
24+
- JSONPath: .metadata.creationTimestamp
25+
name: Age
26+
type: date
27+
28+
validation:
29+
openAPIV3Schema:
30+
properties:
31+
spec:
32+
type: object
33+
properties:
34+
template:
35+
description: Template which will be deep merged without mutating
36+
any existing fields. into generated secret, can be used to
37+
set for example annotations or type on the generated secret
38+
type: object
39+
backendType:
40+
type: string
41+
enum:
42+
- secretsManager
43+
- systemManager
44+
- vault
45+
dataFrom:
46+
type: array
47+
items:
48+
type: string
49+
data:
50+
type: array
51+
items:
52+
type: object
53+
properties:
54+
key:
55+
description: Secret key in backend
56+
type: string
57+
name:
58+
description: Name set for this key in the generated secret
59+
type: string
60+
property:
61+
description: Property to extract if secret in backend is a JSON object
62+
required:
63+
- name
64+
- key
65+
roleArn:
66+
type: string
67+
required:
68+
- backendType
69+
anyOf:
70+
- required:
71+
- data
72+
- required:
73+
- dataFrom
74+
75+
subresources:
76+
status: {}

custom-resource-manifest.json

-40
This file was deleted.

examples/data-from-example.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: kubernetes-client.io/v1
2+
kind: ExternalSecret
3+
metadata:
4+
name: data-from-example
5+
spec:
6+
backendType: systemManager
7+
dataFrom:
8+
- /foo/name1

examples/dockerconfig-example.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
apiVersion: kubernetes-client.io/v1
22
kind: ExternalSecret
33
metadata:
4-
name: dockerhub-secret
4+
name: dockerconfig-example
55
spec:
66
backendType: secretsManager
7-
type: kubernetes.io/dockerconfigjson
7+
template:
8+
type: kubernetes.io/dockerconfigjson
89
data:
910
- key: /development/dockerhub
1011
name: .dockerconfigjson
+7-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
apiVersion: 'kubernetes-client.io/v1'
1+
apiVersion: kubernetes-client.io/v1
22
kind: ExternalSecret
33
metadata:
44
name: hello-service
55
spec:
6+
template:
7+
metadata:
8+
annotations:
9+
external-secret: 'Yes please!'
610
backendType: secretsManager
711
data:
812
- key: hello-service/password
913
name: password
14+
dataFrom:
15+
- hello-service/secret-envs

examples/secretsmanager-example.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
apiVersion: 'kubernetes-client.io/v1'
1+
apiVersion: kubernetes-client.io/v1
22
kind: ExternalSecret
33
metadata:
4-
name: demo-service
4+
name: secretsmanager-example
55
spec:
66
backendType: secretsManager
77
# optional: specify role to assume when retrieving the data

examples/ssm-example.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
apiVersion: 'kubernetes-client.io/v1'
1+
apiVersion: kubernetes-client.io/v1
22
kind: ExternalSecret
33
metadata:
4-
name: ssm-secret-key
4+
name: ssm-example
55
spec:
66
backendType: systemManager
77
# optional: specify role to assume when retrieving the data

examples/tls-example.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
apiVersion: kubernetes-client.io/v1
22
kind: ExternalSecret
33
metadata:
4-
name: dockerhub-secret
4+
name: tls-secret
55
spec:
66
backendType: secretsManager
7-
type: kubernetes.io/tls
7+
template:
8+
type: kubernetes.io/tls
89
data:
910
- key: /development/certificate
1011
property: crt

lib/poller.js

+9-8
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,15 @@ class Poller {
7575
*/
7676
async _createSecretManifest () {
7777
const spec = this._spec
78-
const template = spec.template
78+
const template = spec.template || {}
79+
80+
// spec.type for backwards compat
81+
const type = template.type || spec.type || 'Opaque'
82+
7983
const data = await this._backends[spec.backendType]
8084
.getSecretManifestData({ spec })
81-
let secretManifest = {
85+
86+
const secretManifest = {
8287
apiVersion: 'v1',
8388
kind: 'Secret',
8489
metadata: {
@@ -87,15 +92,11 @@ class Poller {
8792
this._ownerReference
8893
]
8994
},
90-
type: spec.type || 'Opaque',
95+
type,
9196
data
9297
}
9398

94-
if (template) {
95-
secretManifest = merge(clonedeep(template), secretManifest)
96-
}
97-
98-
return secretManifest
99+
return merge(clonedeep(template), secretManifest)
99100
}
100101

101102
/**

lib/poller.test.js

+29-3
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,12 @@ describe('Poller', () => {
183183
})
184184
})
185185

186-
it('creates secret manifest - with type', async () => {
186+
it('creates secret manifest - with type (backwards compat)', async () => {
187187
const poller = pollerFactory({
188188
type: 'dummy-test-type',
189189
backendType: 'fakeBackendType',
190190
name: 'fakeSecretName',
191-
properties: [
191+
data: [
192192
'fakePropertyName1',
193193
'fakePropertyName2'
194194
]
@@ -206,7 +206,7 @@ describe('Poller', () => {
206206
type: 'dummy-test-type',
207207
backendType: 'fakeBackendType',
208208
name: 'fakeSecretName',
209-
properties: [
209+
data: [
210210
'fakePropertyName1',
211211
'fakePropertyName2'
212212
]
@@ -228,6 +228,32 @@ describe('Poller', () => {
228228
})
229229
})
230230

231+
it('creates secret manifest - with template type (should work with backwards compat type)', async () => {
232+
const poller = pollerFactory({
233+
template: {
234+
type: 'dummy-test-type'
235+
},
236+
backendType: 'fakeBackendType',
237+
name: 'fakeSecretName',
238+
data: []
239+
})
240+
241+
backendMock.getSecretManifestData.resolves({})
242+
243+
const secretManifest = await poller._createSecretManifest()
244+
245+
expect(secretManifest).deep.equals({
246+
apiVersion: 'v1',
247+
kind: 'Secret',
248+
metadata: {
249+
name: 'fakeSecretName',
250+
ownerReferences: [getOwnerReference()]
251+
},
252+
type: 'dummy-test-type',
253+
data: {}
254+
})
255+
})
256+
231257
it('creates secret manifest - with template', async () => {
232258
const poller = pollerFactory({
233259
type: 'dummy-test-type',

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"dependencies": {
3131
"aws-sdk": "^2.566.0",
3232
"express": "^4.17.1",
33+
"js-yaml": "^3.13.1",
3334
"json-stream": "^1.0.0",
3435
"kubernetes-client": "^8.3.0",
3536
"lodash.clonedeep": "^4.5.0",

0 commit comments

Comments
 (0)