Add WNodeFactory.MakeConfigMap,MakeSecret

This commit is contained in:
jregan
2020-11-17 17:04:31 -08:00
parent f7613631d1
commit 486be07e22
5 changed files with 495 additions and 11 deletions

View File

@@ -4,6 +4,7 @@ go 1.14
require (
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/golangci/golangci-lint v1.21.0
github.com/google/go-cmp v0.3.0

View File

@@ -6,7 +6,9 @@ package wrappy
import (
"bytes"
"fmt"
"sort"
"github.com/go-errors/errors"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
@@ -76,11 +78,107 @@ func (k *WNodeFactory) Hasher() ifc.KunstructuredHasher {
}
func (k *WNodeFactory) MakeConfigMap(
kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
panic("TODO(#WNodeFactory): implement MakeConfigMap")
ldr ifc.KvLoader, args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
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(
kvLdr ifc.KvLoader, args *types.SecretArgs) (ifc.Kunstructured, error) {
panic("TODO(#WNodeFactory): implement MakeSecret")
ldr ifc.KvLoader, args *types.SecretArgs) (ifc.Kunstructured, error) {
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
}

View File

@@ -5,10 +5,395 @@ package wrappy
import (
"fmt"
"path/filepath"
"reflect"
"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) {
factory := &WNodeFactory{}
testConfigMap :=

View File

@@ -58,7 +58,7 @@ func (s k8sDataSetter) Filter(rn *RNode) (*RNode, error) {
// 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, fmt.Errorf(
return nil, errors.Errorf(
"key '%s' appears to have non-utf8 data; "+
"binaryData field not yet supported", s.Key)
}
@@ -73,8 +73,8 @@ func (s k8sDataSetter) Filter(rn *RNode) (*RNode, error) {
}
v := NewScalarRNode(s.Value)
v.YNode().Tag = NodeTagString
// Add quotes?
// v.YNode().Style = yaml.SingleQuotedStyle
// 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

View File

@@ -39,8 +39,8 @@ data:
fruit: apple
veggie: celery
`
if output != expected {
t.Fatalf("expected \n%s\nbut got \n%s\n", expected, output)
if !assert.Equal(t, expected, output) {
t.FailNow()
}
}
@@ -79,8 +79,8 @@ metadata:
name: foo
namespace: bar
`
if output != expected {
t.Fatalf("expected \n%s\nbut got \n%s\n", expected, output)
if !assert.Equal(t, expected, output) {
t.FailNow()
}
}