Alexey Odinokov e2c56108ee Nextgen secrets implementation with separation per cluster
1. Extending templater with kyaml functions and creating combined catalogue
to be able to request/update the existing resources.
This is based on 'everything is transformer' concept introduced in kustomize 4.x
That includes gathering all secrets into 1 variable catalogue and
special mechanism to regenerate/merge with manual secrets.

2. Implementing 'catalogue per cluster' approach for secrets.

3. Rearranging secrets so it's possible to use:
pgp (each person may have his own key), age, Hachicorp Vault and etc
and the list of people who can decrypt documents is set in a special file.
Since in some cases there should be a separate list of people who can decrypt
data - this list is set for each cluster (ephemeral and target) separatelly.

Closes: #586
Change-Id: I038f84dd138d5ad4a35f4862c61ff2124c2fd530
2021-09-03 20:46:15 +00:00

435 lines
6.9 KiB
Go

/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package extlib
import (
"testing"
"bytes"
"strings"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
kfilters "sigs.k8s.io/kustomize/kyaml/kio/filters"
)
func TestKFilter(t *testing.T) {
testCases := []struct {
in string
expectedOut kio.Filter
}{
{
in: `
kind: GrepFilter
path:
- metadata
- annotations
- test-annotation
value: ^x$
invertMatch: true
`,
expectedOut: kfilters.GrepFilter{
Path: []string{
"metadata",
"annotations",
"test-annotation",
},
Value: "^x$",
InvertMatch: true,
},
},
{
in: `
kind: NonExistentFilter
path:
- metadata
`,
expectedOut: nil,
},
{
in: `
kind: GrepFilter
path: "incorrectdata"
`,
expectedOut: nil,
},
{
in: `
kind: Modifier
pipeline: "incorrectdata"
`,
expectedOut: nil,
},
}
for _, tc := range testCases {
r := kFilter(tc.in)
// GrepFilter is a special case
grepFilter, ok := r.(kfilters.GrepFilter)
if ok {
require.NotNil(t, grepFilter.Compare)
grepFilter.Compare = nil
r = grepFilter
}
assert.Equal(t, tc.expectedOut, r)
}
}
func TestKPipe(t *testing.T) {
testCases := []struct {
in string
filters string
expectedOut string
}{
{
in: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cf2
---
apiVersion: v1
kind: Deployment
metadata:
name: cf1
`,
filters: `
kind: GrepFilter
path:
- metadata
- name
value: cf1
---
kind: GrepFilter
path:
- kind
value: ConfigMap
`,
expectedOut: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
`,
},
{
in: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
`,
filters: `
kind: InvalidFilter
`,
expectedOut: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
`,
},
}
for _, tc := range testCases {
// convert in to []*yaml.RNode
b := kio.PackageBuffer{}
p := kio.Pipeline{
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(tc.in)}},
Outputs: []kio.Writer{&b},
}
err := p.Execute()
require.NoError(t, err)
// get list of filters
kfilters := []interface{}{}
for _, flt := range strings.Split(tc.filters, "\n---\n") {
kfilters = append(kfilters, kFilter(flt))
}
nodes := kPipe(b.Nodes, kfilters)
// convert to string and compare with expected
out := &bytes.Buffer{}
err = kio.ByteWriter{Writer: out}.Write(nodes)
require.NoError(t, err)
assert.Equal(t, tc.expectedOut[1:], out.String())
}
}
func TestYFilter(t *testing.T) {
testCases := []struct {
in string
expectedOut yaml.Filter
}{
{
in: `
kind: PathGetter
path: ["data", "fld1"]
`,
expectedOut: &yaml.PathGetter{
Kind: "PathGetter",
Path: []string{
"data",
"fld1",
},
},
},
{in: `
kind: PathGetter
path: "data"
`,
expectedOut: nil,
},
{in: `
kind: nonExistingFilter
path: "data"
`,
expectedOut: nil,
},
}
for _, tc := range testCases {
out := yFilter(tc.in)
assert.Equal(t, tc.expectedOut, out)
}
}
func TestYPipe(t *testing.T) {
testCases := []struct {
in string
filters string
expectedIn string
expectedOut string
}{
{
in: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
`,
filters: `
kind: PathGetter
path: ["metadata"]
---
kind: FieldSetter
name: "name"
stringValue: "cf2"
`,
expectedIn: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf2
`,
expectedOut: `
cf2
`,
},
{
in: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
`,
filters: `
kind: InvalidPathGetter
path: ["metadata"]
`,
},
{
in: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
`,
filters: `
kind: PathGetter
path: ["xmetadata"]
---
kind: FieldSetter
name: "namex"
stringValue: "cf2"
`,
expectedIn: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
`,
},
}
for _, tc := range testCases {
inRNode, err := yaml.Parse(tc.in)
require.NoError(t, err)
// get list of filters
yfilters := []interface{}{}
for _, flt := range strings.Split(tc.filters, "\n---\n") {
yfilters = append(yfilters, yFilter(flt))
}
outRNode := yPipe(inRNode, yfilters)
if tc.expectedOut != "" {
require.NotNil(t, outRNode)
out, err := outRNode.String()
require.NoError(t, err)
assert.Equal(t, tc.expectedOut[1:], out)
}
if tc.expectedIn != "" {
in, err := inRNode.String()
require.NoError(t, err)
assert.Equal(t, tc.expectedIn[1:], in)
}
}
}
func TestYValue(t *testing.T) {
testCases := []struct {
in string
expectedOut interface{}
}{
{
in: `
x
`,
expectedOut: "x",
},
{
in: `
kind: x
value: b
list:
- a
- b
`,
expectedOut: map[string]interface{}{
"kind": "x",
"list": []interface{}{
"a",
"b",
},
"value": "b",
},
},
}
for _, tc := range testCases {
inRNode, err := yaml.Parse(tc.in)
require.NoError(t, err)
out := yValue(inRNode)
assert.Equal(t, tc.expectedOut, out)
}
}
func TestKYFilter(t *testing.T) {
testCases := []struct {
in string
filters string
expectedOut string
}{
{
in: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
labels: {}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cf2
labels: {}
---
apiVersion: v1
kind: Deployment
metadata:
name: cf1
labels: {}
`,
filters: `
kind: PathGetter
path: ["metadata", "labels"]
---
kind: FieldSetter
name: "newlabel"
stringValue: "newvalue"
`,
expectedOut: `
apiVersion: v1
kind: ConfigMap
metadata:
name: cf1
labels: {newlabel: newvalue}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cf2
labels: {newlabel: newvalue}
---
apiVersion: v1
kind: Deployment
metadata:
name: cf1
labels: {newlabel: newvalue}
`,
},
}
for _, tc := range testCases {
// convert in to []*yaml.RNode
b := kio.PackageBuffer{}
p := kio.Pipeline{
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(tc.in)}},
Outputs: []kio.Writer{&b},
}
err := p.Execute()
require.NoError(t, err)
// get list of filters
yfilters := []interface{}{}
for _, flt := range strings.Split(tc.filters, "\n---\n") {
yfilters = append(yfilters, yFilter(flt))
}
kfilters := []interface{}{newKYFilter(yfilters)}
nodes := kPipe(b.Nodes, kfilters)
// convert to string and compare with expected
out := &bytes.Buffer{}
err = kio.ByteWriter{Writer: out}.Write(nodes)
require.NoError(t, err)
assert.Equal(t, tc.expectedOut[1:], out.String())
}
}