Merge "Add document filtering in KRM toolbox by GVK"

This commit is contained in:
Zuul
2021-04-30 01:15:32 +00:00
committed by Gerrit Code Review
9 changed files with 227 additions and 12 deletions

View File

@@ -66,6 +66,8 @@ The toolbox image has pre-installed `sh` shell,`kubectl` and `calicoctl`.
## Input bundle usage
The KRM function writes to filesystem input bundle specified in `documentEntryPoint` in phase declaration and imports the path to this bundle in `RENDERED_BUNDLE_PATH` environment variable. For example it can be used with `calicoctl` as `calicoctl apply -f $RENDERED_BUNDLE_PATH`
Documents can be filtered by group, version and kind. You need to set `RESOURCE_GROUP_FILTER`, `RESOURCE_VERSION_FILTER` and/or`RESOURCE_KIND_FILTER` in executor definition to enable filtering.
## Important notes
The script must write to STDOUT valid yaml or redirect output to STDERR otherwise phase will fail with `mapping values are not allowed in this context`
1. The script must write to STDOUT valid yaml or redirect output to STDERR otherwise phase will fail with `mapping values are not allowed in this context`
2. All shell scripts must begin with `set -xe`. This allows errors to be passed from the container to the airshipctl itself. Without this flags the container will never fail.

View File

@@ -28,16 +28,23 @@ import (
"sigs.k8s.io/kustomize/kyaml/kio"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"opendev.org/airship/airshipctl/pkg/document/plugin/kyamlutils"
"opendev.org/airship/airshipctl/pkg/log"
)
const (
// EnvRenderedBundlePath will be passed to the script, it will contain path to the rendered bundle
EnvRenderedBundlePath = "RENDERED_BUNDLE_PATH"
scriptPath = "script.sh"
scriptKey = "script"
bundleFile = "bundle.yaml"
workdir = "/tmp"
// ResourceGroupFilter used for filtering input bundle by document group
ResourceGroupFilter = "RESOURCE_GROUP_FILTER"
// ResourceVersionFilter used for filtering input bundle by document version
ResourceVersionFilter = "RESOURCE_VERSION_FILTER"
// ResourceKindFilter used for filtering input bundle by document kind
ResourceKindFilter = "RESOURCE_KIND_FILTER"
scriptPath = "script.sh"
scriptKey = "script"
bundleFile = "bundle.yaml"
workdir = "/tmp"
)
func main() {
@@ -137,6 +144,11 @@ func (c *ScriptRunner) writeBundle(path string, items []*kyaml.RNode) error {
}
defer f.Close()
items, err = c.FilterBundle(items)
if err != nil {
return err
}
pipeline := kio.Pipeline{
Outputs: []kio.Writer{
kio.ByteWriter{
@@ -150,3 +162,15 @@ func (c *ScriptRunner) writeBundle(path string, items []*kyaml.RNode) error {
return pipeline.Execute()
}
// FilterBundle uses for filtering input documents
func (c *ScriptRunner) FilterBundle(items []*kyaml.RNode) ([]*kyaml.RNode, error) {
group := os.Getenv(ResourceGroupFilter)
version := os.Getenv(ResourceVersionFilter)
kind := os.Getenv(ResourceKindFilter)
log.Printf("Filtering input bundle by Group: %s, Version: %s, Kind: %s",
group, version, kind)
return kyamlutils.DocumentSelector{}.
ByGVK(group, version, kind).
Filter(items)
}

View File

@@ -17,6 +17,7 @@ package main
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
@@ -24,6 +25,7 @@ import (
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -166,3 +168,132 @@ func TestCmdRunCleanup(t *testing.T) {
}()
assert.NoError(t, err)
}
func TestFilterBundle(t *testing.T) {
rawDocs := `---
apiVersion: test/v1
kind: Pod
metadata:
name: t1
---
apiVersion: test/v1
kind: Secret
metadata:
name: t2
---
apiVersion: test/v1beta1
kind: Deployment
metadata:
name: t3
`
docs, err := (&kio.ByteReader{Reader: bytes.NewBufferString(rawDocs)}).Read()
require.NoError(t, err)
testCases := []struct {
name string
inputDocs []*yaml.RNode
groupFilter string
versionFilter string
kindFilter string
errContains string
expectedDocs string
}{
{
name: "Correct documents",
inputDocs: docs,
groupFilter: "test",
versionFilter: "v1",
kindFilter: "Pod",
errContains: "",
expectedDocs: `apiVersion: test/v1
kind: Pod
metadata:
name: t1
`,
},
{
name: "Empty kind",
inputDocs: docs,
groupFilter: "test",
versionFilter: "v1beta1",
kindFilter: "",
errContains: "",
expectedDocs: `apiVersion: test/v1beta1
kind: Deployment
metadata:
name: t3
`,
},
{
name: "Empty all filters",
inputDocs: docs,
groupFilter: "",
versionFilter: "",
kindFilter: "",
errContains: "",
expectedDocs: `apiVersion: test/v1
kind: Pod
metadata:
name: t1
---
apiVersion: test/v1
kind: Secret
metadata:
name: t2
---
apiVersion: test/v1beta1
kind: Deployment
metadata:
name: t3
`,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cMap := &v1.ConfigMap{
Data: map[string]string{
dataKey: script,
},
}
input, err := yaml.Parse(inputString)
require.NoError(t, err)
stderr := bytes.NewBuffer([]byte{})
stdout := bytes.NewBuffer([]byte{})
os.Setenv(ResourceGroupFilter, tc.groupFilter)
defer os.Unsetenv(ResourceGroupFilter)
os.Setenv(ResourceVersionFilter, tc.versionFilter)
defer os.Unsetenv(ResourceVersionFilter)
os.Setenv(ResourceKindFilter, tc.kindFilter)
defer os.Unsetenv(ResourceKindFilter)
cmd := &ScriptRunner{
ScriptFile: targetFile,
WorkDir: dir,
DataKey: dataKey,
ErrStream: stderr,
OutStream: stdout,
ResourceList: &framework.ResourceList{Items: []*yaml.RNode{input}},
ConfigMap: cMap,
RenderedBundleFile: bundlePath,
}
filteredDocs, err := cmd.FilterBundle(tc.inputDocs)
if tc.errContains != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tc.errContains)
} else {
require.NoError(t, err)
buf := &bytes.Buffer{}
err = kio.ByteWriter{Writer: buf}.Write(filteredDocs)
assert.Equal(t, tc.expectedDocs, buf.String())
require.NoError(t, err)
}
})
}
}