mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Merge pull request #3247 from monopole/secretsAndMaps
Secrets and ConfigMaps with string data in kyaml
This commit is contained in:
@@ -4,6 +4,7 @@ go 1.14
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/evanphx/json-patch v4.5.0+incompatible
|
github.com/evanphx/json-patch v4.5.0+incompatible
|
||||||
|
github.com/go-errors/errors v1.0.1
|
||||||
github.com/go-openapi/spec v0.19.5
|
github.com/go-openapi/spec v0.19.5
|
||||||
github.com/golangci/golangci-lint v1.21.0
|
github.com/golangci/golangci-lint v1.21.0
|
||||||
github.com/google/go-cmp v0.3.0
|
github.com/google/go-cmp v0.3.0
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ package wrappy
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/go-errors/errors"
|
||||||
"sigs.k8s.io/kustomize/api/ifc"
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
"sigs.k8s.io/kustomize/api/konfig"
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
@@ -76,11 +78,107 @@ func (k *WNodeFactory) Hasher() ifc.KunstructuredHasher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (k *WNodeFactory) MakeConfigMap(
|
func (k *WNodeFactory) MakeConfigMap(
|
||||||
kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
|
ldr ifc.KvLoader, args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
|
||||||
panic("TODO(#WNodeFactory): implement MakeConfigMap")
|
rn, err := k.makeConfigMap(ldr, args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return FromRNode(rn), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *WNodeFactory) makeConfigMap(
|
||||||
|
ldr ifc.KvLoader, args *types.ConfigMapArgs) (*yaml.RNode, error) {
|
||||||
|
rn, err := yaml.Parse(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = applyGeneratorArgs(rn, ldr, args.GeneratorArgs)
|
||||||
|
return rn, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *WNodeFactory) MakeSecret(
|
func (k *WNodeFactory) MakeSecret(
|
||||||
kvLdr ifc.KvLoader, args *types.SecretArgs) (ifc.Kunstructured, error) {
|
ldr ifc.KvLoader, args *types.SecretArgs) (ifc.Kunstructured, error) {
|
||||||
panic("TODO(#WNodeFactory): implement MakeSecret")
|
rn, err := k.makeSecret(ldr, args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return FromRNode(rn), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *WNodeFactory) makeSecret(
|
||||||
|
ldr ifc.KvLoader, args *types.SecretArgs) (*yaml.RNode, error) {
|
||||||
|
rn, err := yaml.Parse(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = applyGeneratorArgs(rn, ldr, args.GeneratorArgs)
|
||||||
|
if 1+1 == 2 {
|
||||||
|
err = fmt.Errorf("TODO(WNodeFactory): finish implementation of makeSecret")
|
||||||
|
}
|
||||||
|
return rn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyGeneratorArgs(
|
||||||
|
rn *yaml.RNode, ldr ifc.KvLoader, args types.GeneratorArgs) error {
|
||||||
|
if _, err := rn.Pipe(yaml.SetK8sName(args.Name)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if args.Namespace != "" {
|
||||||
|
if _, err := rn.Pipe(yaml.SetK8sNamespace(args.Namespace)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all, err := ldr.Load(args.KvPairSources)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefix(err, "loading KV pairs", 0)
|
||||||
|
}
|
||||||
|
for _, p := range all {
|
||||||
|
if err := ldr.Validator().ErrIfInvalidKey(p.Key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := rn.Pipe(yaml.SetK8sData(p.Key, p.Value)); err != nil {
|
||||||
|
return errors.WrapPrefix(err, "configMap generate error", 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copyLabelsAndAnnotations(rn, args.Options)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyLabelsAndAnnotations copies labels and annotations from
|
||||||
|
// GeneratorOptions into the given object.
|
||||||
|
func copyLabelsAndAnnotations(
|
||||||
|
rn *yaml.RNode, opts *types.GeneratorOptions) error {
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, k := range sortedKeys(opts.Labels) {
|
||||||
|
v := opts.Labels[k]
|
||||||
|
if _, err := rn.Pipe(yaml.SetLabel(k, v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, k := range sortedKeys(opts.Annotations) {
|
||||||
|
v := opts.Annotations[k]
|
||||||
|
if _, err := rn.Pipe(yaml.SetAnnotation(k, v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortedKeys(m map[string]string) []string {
|
||||||
|
keys := make([]string, len(m))
|
||||||
|
i := 0
|
||||||
|
for k := range m {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
return keys
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,395 @@ package wrappy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
"sigs.k8s.io/kustomize/api/kv"
|
||||||
|
"sigs.k8s.io/kustomize/api/loader"
|
||||||
|
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestMakeConfigMap(t *testing.T) {
|
||||||
|
factory := &WNodeFactory{}
|
||||||
|
type expected struct {
|
||||||
|
out string
|
||||||
|
errMsg string
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
args types.ConfigMapArgs
|
||||||
|
exp expected
|
||||||
|
}{
|
||||||
|
"construct config map from env": {
|
||||||
|
args: types.ConfigMapArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "envConfigMap",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
EnvSources: []string{
|
||||||
|
filepath.Join("configmap", "app.env"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: envConfigMap
|
||||||
|
data:
|
||||||
|
DB_USERNAME: admin
|
||||||
|
DB_PASSWORD: qwerty
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"construct config map from text file": {
|
||||||
|
args: types.ConfigMapArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "fileConfigMap1",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
FileSources: []string{
|
||||||
|
filepath.Join("configmap", "app-init.ini"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: fileConfigMap1
|
||||||
|
data:
|
||||||
|
app-init.ini: |
|
||||||
|
FOO=bar
|
||||||
|
BAR=baz
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"construct config map from text and binary file": {
|
||||||
|
args: types.ConfigMapArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "fileConfigMap2",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
FileSources: []string{
|
||||||
|
filepath.Join("configmap", "app-init.ini"),
|
||||||
|
filepath.Join("configmap", "app.bin"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
errMsg: "configMap generate error: key 'app.bin' appears " +
|
||||||
|
"to have non-utf8 data; binaryData field not yet supported",
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: fileConfigMap2
|
||||||
|
data:
|
||||||
|
app-init.ini: |
|
||||||
|
FOO=bar
|
||||||
|
BAR=baz
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"construct config map from literal": {
|
||||||
|
args: types.ConfigMapArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "literalConfigMap1",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||||
|
},
|
||||||
|
Options: &types.GeneratorOptions{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: literalConfigMap1
|
||||||
|
labels:
|
||||||
|
foo: 'bar'
|
||||||
|
data:
|
||||||
|
a: x
|
||||||
|
b: y
|
||||||
|
c: Hello World
|
||||||
|
d: "true"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"construct config map from literal with GeneratorOptions in ConfigMapArgs": {
|
||||||
|
args: types.ConfigMapArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "literalConfigMap2",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||||
|
},
|
||||||
|
Options: &types.GeneratorOptions{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"veggie": "celery",
|
||||||
|
"dog": "beagle",
|
||||||
|
"cat": "annoying",
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"river": "Missouri",
|
||||||
|
"city": "Iowa City",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: literalConfigMap2
|
||||||
|
labels:
|
||||||
|
cat: 'annoying'
|
||||||
|
dog: 'beagle'
|
||||||
|
veggie: 'celery'
|
||||||
|
annotations:
|
||||||
|
city: 'Iowa City'
|
||||||
|
river: 'Missouri'
|
||||||
|
data:
|
||||||
|
a: x
|
||||||
|
b: y
|
||||||
|
c: Hello World
|
||||||
|
d: "true"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
fSys.WriteFile(
|
||||||
|
filesys.RootedPath("configmap", "app.env"),
|
||||||
|
[]byte("DB_USERNAME=admin\nDB_PASSWORD=qwerty\n"))
|
||||||
|
fSys.WriteFile(
|
||||||
|
filesys.RootedPath("configmap", "app-init.ini"),
|
||||||
|
[]byte("FOO=bar\nBAR=baz\n"))
|
||||||
|
fSys.WriteFile(
|
||||||
|
filesys.RootedPath("configmap", "app.bin"),
|
||||||
|
[]byte{0xff, 0xfd})
|
||||||
|
kvLdr := kv.NewLoader(
|
||||||
|
loader.NewFileLoaderAtRoot(fSys),
|
||||||
|
valtest_test.MakeFakeValidator())
|
||||||
|
|
||||||
|
for n := range testCases {
|
||||||
|
tc := testCases[n]
|
||||||
|
t.Run(n, func(t *testing.T) {
|
||||||
|
rn, err := factory.makeConfigMap(kvLdr, &tc.args)
|
||||||
|
if err != nil {
|
||||||
|
if !assert.EqualError(t, err, tc.exp.errMsg) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tc.exp.errMsg != "" {
|
||||||
|
t.Fatalf("%s: should return error '%s'", n, tc.exp.errMsg)
|
||||||
|
}
|
||||||
|
output := rn.MustString()
|
||||||
|
if !assert.Equal(t, tc.exp.out, output) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMakeSecret(t *testing.T) {
|
||||||
|
factory := &WNodeFactory{}
|
||||||
|
type expected struct {
|
||||||
|
out string
|
||||||
|
errMsg string
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
args types.SecretArgs
|
||||||
|
exp expected
|
||||||
|
}{
|
||||||
|
"construct secret from env": {
|
||||||
|
args: types.SecretArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "envSecret",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
EnvSources: []string{
|
||||||
|
filepath.Join("secret", "app.env"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
errMsg: "TODO(WNodeFactory): finish implementation of makeSecret",
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: envSecret
|
||||||
|
data:
|
||||||
|
DB_USERNAME: admin
|
||||||
|
DB_PASSWORD: qwerty
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"construct secret from text file": {
|
||||||
|
args: types.SecretArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "fileSecret1",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
FileSources: []string{
|
||||||
|
filepath.Join("secret", "app-init.ini"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
errMsg: "TODO(WNodeFactory): finish implementation of makeSecret",
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: fileSecret1
|
||||||
|
data:
|
||||||
|
app-init.ini: |
|
||||||
|
FOO=bar
|
||||||
|
BAR=baz
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"construct secret from text and binary file": {
|
||||||
|
args: types.SecretArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "fileSecret2",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
FileSources: []string{
|
||||||
|
filepath.Join("secret", "app-init.ini"),
|
||||||
|
filepath.Join("secret", "app.bin"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
errMsg: "TODO(WNodeFactory): finish implementation of makeSecret",
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: fileSecret2
|
||||||
|
data:
|
||||||
|
app-init.ini: |
|
||||||
|
FOO=bar
|
||||||
|
BAR=baz
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"construct secret from literal": {
|
||||||
|
args: types.SecretArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "literalSecret1",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||||
|
},
|
||||||
|
Options: &types.GeneratorOptions{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
errMsg: "TODO(WNodeFactory): finish implementation of makeSecret",
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: literalSecret1
|
||||||
|
labels:
|
||||||
|
foo: 'bar'
|
||||||
|
data:
|
||||||
|
a: x
|
||||||
|
b: y
|
||||||
|
c: Hello World
|
||||||
|
d: "true"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"construct secret from literal with GeneratorOptions in SecretArgs": {
|
||||||
|
args: types.SecretArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "literalSecret2",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||||
|
},
|
||||||
|
Options: &types.GeneratorOptions{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"veggie": "celery",
|
||||||
|
"dog": "beagle",
|
||||||
|
"cat": "annoying",
|
||||||
|
},
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"river": "Missouri",
|
||||||
|
"city": "Iowa City",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: expected{
|
||||||
|
errMsg: "TODO(WNodeFactory): finish implementation of makeSecret",
|
||||||
|
out: `apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: literalSecret2
|
||||||
|
labels:
|
||||||
|
cat: 'annoying'
|
||||||
|
dog: 'beagle'
|
||||||
|
veggie: 'celery'
|
||||||
|
annotations:
|
||||||
|
city: 'Iowa City'
|
||||||
|
river: 'Missouri'
|
||||||
|
data:
|
||||||
|
a: x
|
||||||
|
b: y
|
||||||
|
c: Hello World
|
||||||
|
d: "true"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
fSys.WriteFile(
|
||||||
|
filesys.RootedPath("secret", "app.env"),
|
||||||
|
[]byte("DB_USERNAME=admin\nDB_PASSWORD=qwerty\n"))
|
||||||
|
fSys.WriteFile(
|
||||||
|
filesys.RootedPath("secret", "app-init.ini"),
|
||||||
|
[]byte("FOO=bar\nBAR=baz\n"))
|
||||||
|
fSys.WriteFile(
|
||||||
|
filesys.RootedPath("secret", "app.bin"),
|
||||||
|
[]byte{0xff, 0xfd})
|
||||||
|
kvLdr := kv.NewLoader(
|
||||||
|
loader.NewFileLoaderAtRoot(fSys),
|
||||||
|
valtest_test.MakeFakeValidator())
|
||||||
|
|
||||||
|
for n := range testCases {
|
||||||
|
tc := testCases[n]
|
||||||
|
t.Run(n, func(t *testing.T) {
|
||||||
|
rn, err := factory.makeSecret(kvLdr, &tc.args)
|
||||||
|
if err != nil {
|
||||||
|
if !assert.EqualError(t, err, tc.exp.errMsg) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tc.exp.errMsg != "" {
|
||||||
|
t.Fatalf("%s: should return error '%s'", n, tc.exp.errMsg)
|
||||||
|
}
|
||||||
|
output := rn.MustString()
|
||||||
|
if !assert.Equal(t, tc.exp.out, output) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSliceFromBytes(t *testing.T) {
|
func TestSliceFromBytes(t *testing.T) {
|
||||||
factory := &WNodeFactory{}
|
factory := &WNodeFactory{}
|
||||||
testConfigMap :=
|
testConfigMap :=
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
package yaml
|
package yaml
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
)
|
)
|
||||||
@@ -40,6 +44,70 @@ func ClearEmptyAnnotations(rn *RNode) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// k8sDataSetter place key value pairs in either a 'data' or 'binaryData' field.
|
||||||
|
// Useful for creating ConfigMaps and Secrets.
|
||||||
|
type k8sDataSetter struct {
|
||||||
|
Key string `yaml:"key,omitempty"`
|
||||||
|
Value string `yaml:"value,omitempty"`
|
||||||
|
ProtectExisting bool `yaml:"protectExisting,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s k8sDataSetter) Filter(rn *RNode) (*RNode, error) {
|
||||||
|
if !utf8.Valid([]byte(s.Value)) {
|
||||||
|
// Core k8s ConfigMaps store k,v pairs with 'v' passing the above utf8
|
||||||
|
// test in a mapping field called "data" as a string. Pairs with a 'v'
|
||||||
|
// failing this test go into a field called binaryData as a []byte.
|
||||||
|
// TODO: support this distinction in kyaml with NodeTagBytes?
|
||||||
|
return nil, errors.Errorf(
|
||||||
|
"key '%s' appears to have non-utf8 data; "+
|
||||||
|
"binaryData field not yet supported", s.Key)
|
||||||
|
}
|
||||||
|
keyNode, err := rn.Pipe(Lookup(DataField, s.Key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if keyNode != nil && s.ProtectExisting {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"protecting existing %s='%s' against attempt to add new value '%s'",
|
||||||
|
s.Key, strings.TrimSpace(keyNode.MustString()), s.Value)
|
||||||
|
}
|
||||||
|
v := NewScalarRNode(s.Value)
|
||||||
|
v.YNode().Tag = NodeTagString
|
||||||
|
// TODO: use schema to determine node style and tag.
|
||||||
|
// FormatNonStringStyle(v.YNode(), *k8sSch)
|
||||||
|
_, err = rn.Pipe(
|
||||||
|
LookupCreate(yaml.MappingNode, DataField), SetField(s.Key, v))
|
||||||
|
return rn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetK8sData(key, value string) k8sDataSetter {
|
||||||
|
return k8sDataSetter{Key: key, Value: value, ProtectExisting: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// k8sMetaSetter sets a name at metadata.{key}.
|
||||||
|
// Creates metadata if does not exist.
|
||||||
|
type k8sMetaSetter struct {
|
||||||
|
Key string `yaml:"key,omitempty"`
|
||||||
|
Value string `yaml:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s k8sMetaSetter) Filter(rn *RNode) (*RNode, error) {
|
||||||
|
v := NewScalarRNode(s.Value)
|
||||||
|
v.YNode().Tag = NodeTagString
|
||||||
|
_, err := rn.Pipe(
|
||||||
|
PathGetter{Path: []string{MetadataField}, Create: yaml.MappingNode},
|
||||||
|
FieldSetter{Name: s.Key, Value: v})
|
||||||
|
return rn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetK8sName(value string) k8sMetaSetter {
|
||||||
|
return k8sMetaSetter{Key: NameField, Value: value}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetK8sNamespace(value string) k8sMetaSetter {
|
||||||
|
return k8sMetaSetter{Key: NamespaceField, Value: value}
|
||||||
|
}
|
||||||
|
|
||||||
// AnnotationSetter sets an annotation at metadata.annotations.
|
// AnnotationSetter sets an annotation at metadata.annotations.
|
||||||
// Creates metadata.annotations if does not exist.
|
// Creates metadata.annotations if does not exist.
|
||||||
type AnnotationSetter struct {
|
type AnnotationSetter struct {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ package yaml
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var input = `apiVersion: v1
|
var input = `apiVersion: v1
|
||||||
@@ -16,6 +18,72 @@ data:
|
|||||||
enableRisky: "false"
|
enableRisky: "false"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
func TestSetK8sData(t *testing.T) {
|
||||||
|
rn := MustParse(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
data:
|
||||||
|
altGreeting: "Good Morning!"
|
||||||
|
`)
|
||||||
|
_, err := rn.Pipe(
|
||||||
|
SetK8sData("foo", "bar"),
|
||||||
|
SetK8sData("fruit", "apple"),
|
||||||
|
SetK8sData("veggie", "celery"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
output := rn.MustString()
|
||||||
|
|
||||||
|
expected := `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
data:
|
||||||
|
altGreeting: "Good Morning!"
|
||||||
|
foo: bar
|
||||||
|
fruit: apple
|
||||||
|
veggie: celery
|
||||||
|
`
|
||||||
|
if !assert.Equal(t, expected, output) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetK8sDataForbidOverwrite(t *testing.T) {
|
||||||
|
rn := MustParse(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
data:
|
||||||
|
altGreeting: "Good Morning!"
|
||||||
|
`)
|
||||||
|
_, err := rn.Pipe(
|
||||||
|
SetK8sData("foo", "bar"),
|
||||||
|
SetK8sData("altGreeting", "hey"),
|
||||||
|
SetK8sData("veggie", "celery"))
|
||||||
|
assert.EqualError(
|
||||||
|
t, err, "protecting existing altGreeting='\"Good Morning!\"' "+
|
||||||
|
"against attempt to add new value 'hey'")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetMeta(t *testing.T) {
|
||||||
|
rn := MustParse(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
data:
|
||||||
|
altGreeting: "Good Morning!"
|
||||||
|
`)
|
||||||
|
_, err := rn.Pipe(SetK8sName("foo"), SetK8sNamespace("bar"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
output := rn.MustString()
|
||||||
|
|
||||||
|
expected := `apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
data:
|
||||||
|
altGreeting: "Good Morning!"
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
namespace: bar
|
||||||
|
`
|
||||||
|
if !assert.Equal(t, expected, output) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSetLabel1(t *testing.T) {
|
func TestSetLabel1(t *testing.T) {
|
||||||
rn := MustParse(input)
|
rn := MustParse(input)
|
||||||
_, err := rn.Pipe(SetLabel("foo", "bar"))
|
_, err := rn.Pipe(SetLabel("foo", "bar"))
|
||||||
|
|||||||
Reference in New Issue
Block a user