Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions api/internal/generators/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,36 @@
package generators

import (
"fmt"

"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
)

// MakeSecret makes a kubernetes Secret.
//
// Secret: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#secret-v1-core
// Secret: https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/secret-v1/
//
// ConfigMaps and Secrets are similar.
//
// Like a ConfigMap, a Secret has a `data` field, but unlike a ConfigMap it has
// no `binaryData` field.
// no `binaryData` field. Secret also provides a `stringData` field.
//
// All of a Secret's data is assumed to be opaque in nature, and assumed to be
// A Secret's `data` is assumed to be opaque in nature, and assumed to be
// base64 encoded from its original representation, regardless of whether the
// original data was UTF-8 text or binary.
//
// This encoding provides no secrecy. It's just a neutral, common means to
// represent opaque text and binary data. Beneath the base64 encoding
// is presumably further encoding under control of the Secret's consumer.
//
// A Secret's `stringData` field is similar to ConfigMap's `data` field.
// `stringData` allows specifying non-binary, UTF-8 secret data in string form.
// It is provided as a write-only input field for convenience.
// All keys and values are merged into the data field on write, overwriting any
// existing values. The stringData field is never output when reading from the API.
Comment on lines +31 to +35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Thank you for this.

//
// A Secret has string field `type` which holds an identifier, used by the
// client, to choose the algorithm to interpret the `data` field. Kubernetes
// cannot make use of this data; it's up to a controller or some pod's service
Expand All @@ -50,8 +58,14 @@ func MakeSecret(
if err != nil {
return nil, err
}
if err = rn.LoadMapIntoSecretData(m); err != nil {
return nil, err
if args.EmitStringData {
if err = rn.LoadMapIntoSecretStringData(m); err != nil {
return nil, fmt.Errorf("Failed to load map into Secret stringData: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether kustomize eschews the general advice for Go programs to not capitalize the first word in an error message, assuming that this message might wind up serving as a suffix for more prefixes to be added onto it higher up in the call stack. If this capitalization isn't us playing along with the rest of the code base, consider this alternative:

Suggested change
return nil, fmt.Errorf("Failed to load map into Secret stringData: %w", err)
return nil, fmt.Errorf(`loading map into Secret "stringData" field: %w`, err)

The message adopts the advice from Preslav Rachev's essay Go's Error Handling Is a Form of Storytelling.

}
} else {
if err = rn.LoadMapIntoSecretData(m); err != nil {
return nil, fmt.Errorf("Failed to load map into Secret data: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise with the capitalization.

}
}
copyLabelsAndAnnotations(rn, args.Options)
setImmutable(rn, args.Options)
Expand Down
76 changes: 76 additions & 0 deletions api/internal/generators/secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,82 @@ data:
c: SGVsbG8gV29ybGQ=
d: dHJ1ZQ==
immutable: true
`,
},
},
"construct secret from text file as stringData": {
args: types.SecretArgs{
EmitStringData: true,
GeneratorArgs: types.GeneratorArgs{
Name: "fileSecret1",
KvPairSources: types.KvPairSources{
FileSources: []string{
filepath.Join("secret", "app-init.ini"),
},
},
},
},
exp: expected{
out: `apiVersion: v1
kind: Secret
metadata:
name: fileSecret1
type: Opaque
stringData:
app-init.ini: |
FOO=bar
BAR=baz
`,
},
},
"construct secret from text and binary file with stringData and data": {
args: types.SecretArgs{
EmitStringData: true,
GeneratorArgs: types.GeneratorArgs{
Name: "fileSecret2",
KvPairSources: types.KvPairSources{
FileSources: []string{
filepath.Join("secret", "app-init.ini"),
filepath.Join("secret", "app.bin"),
},
},
},
},
exp: expected{
out: `apiVersion: v1
kind: Secret
metadata:
name: fileSecret2
type: Opaque
stringData:
app-init.ini: |
FOO=bar
BAR=baz
data:
app.bin: //0=
`,
},
},
"construct secret from a binary file and fallback to data from stringData": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"construct secret from a binary file and fallback to data from stringData": {
"construct secret from a binary file and fall back to data from stringData": {

args: types.SecretArgs{
EmitStringData: true,
GeneratorArgs: types.GeneratorArgs{
Name: "fileSecret2",
KvPairSources: types.KvPairSources{
FileSources: []string{
filepath.Join("secret", "app.bin"),
},
},
},
},
exp: expected{
out: `apiVersion: v1
kind: Secret
metadata:
name: fileSecret2
type: Opaque
data:
app.bin: //0=
`,
},
},
Expand Down
106 changes: 102 additions & 4 deletions api/krusty/configmaps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ func TestGeneratorFromProperties(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
configMapGenerator:
- name: test-configmap
behavior: create
envs:
- properties
- name: test-configmap
behavior: create
envs:
- properties
`)
th.WriteF("base/properties", `
VAR1=100
Expand Down Expand Up @@ -118,6 +118,104 @@ metadata:
`)
}

func TestGeneratorOverrideDataWithBinaryDataInvalidAtKubeAPI(t *testing.T) {
// the resulting ConfigMap will fail Kubernetes API validation:
// The ConfigMap "test-configmap-b6h9d5bfmt" is invalid: data[CHANGING]: Invalid value: "CHANGING": duplicate of key present in binaryData
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
configMapGenerator:
- name: test-configmap
behavior: create
envs:
- properties
`)
th.WriteF("base/properties", `
CHANGING=data-before
BASE=red
`)
th.WriteK("overlay", `
resources:
- ../base
configMapGenerator:
- name: test-configmap
behavior: "merge"
envs:
- properties
files:
- CHANGING
`)
th.WriteF("overlay/CHANGING", string(manyHellos(30)))
th.WriteF("overlay/properties", `
OVERLAY=blue
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
binaryData:
CHANGING: |
/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbG
xv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hl
bGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2
hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv
data:
BASE: red
CHANGING: data-before
OVERLAY: blue
kind: ConfigMap
metadata:
name: test-configmap-b6h9d5bfmt
`)
}

func TestGeneratorOverrideBinaryDataWithDataInvalidAtKubeAPI(t *testing.T) {
// the resulting ConfigMap will fail Kubernetes API validation:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// the resulting ConfigMap will fail Kubernetes API validation:
// The resulting ConfigMap will fail Kubernetes API validation with the following complaint:

// The ConfigMap "test-configmap-kt6d6mk694" is invalid: data[CHANGING]: Invalid value: "CHANGING": duplicate of key present in binaryData
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
configMapGenerator:
- name: test-configmap
behavior: create
envs:
- properties
files:
- CHANGING
`)
th.WriteF("base/CHANGING", string(manyHellos(30)))
th.WriteF("base/properties", `
BASE=red
`)
th.WriteK("overlay", `
resources:
- ../base
configMapGenerator:
- name: test-configmap
behavior: "merge"
envs:
- properties
`)
th.WriteF("overlay/properties", `
CHANGING=data-after
OVERLAY=blue
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
binaryData:
CHANGING: |
/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbG
xv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hl
bGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv/2
hlbGxv/2hlbGxv/2hlbGxv/2hlbGxv
data:
BASE: red
CHANGING: data-after
OVERLAY: blue
kind: ConfigMap
metadata:
name: test-configmap-kt6d6mk694
`)
}

// Generate a Secret and a ConfigMap from the same data
// to compare the result.
func TestGeneratorBasics(t *testing.T) {
Expand Down
Loading
Loading