Add document filtering in KRM toolbox by GVK
It allows to pass needed documents to KRM toolbox. Relates-To: #517 Change-Id: I9a8e78796b5fffab5ee44ad8cf73a88563589929
This commit is contained in:
parent
05b36dd40f
commit
debd301319
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -5,3 +5,4 @@ resources:
|
||||
- wait_deploy
|
||||
- get_node
|
||||
- wait_pods
|
||||
- pause_bmh
|
||||
|
@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
set -xe
|
||||
|
||||
echo "Annotating BMH objects with a pause label in cluster with kubectl context ${KCTL_CONTEXT}" 1>&2
|
||||
kubectl annotate \
|
||||
--context $KCTL_CONTEXT \
|
||||
--namespace $CLUSTER_NAMESPACE \
|
||||
--overwrite \
|
||||
-f $RENDERED_BUNDLE_PATH baremetalhost.metal3.io/paused=true 1>&2
|
@ -0,0 +1,6 @@
|
||||
configMapGenerator:
|
||||
- name: kubectl-pause-bmh
|
||||
options:
|
||||
disableNameSuffixHash: true
|
||||
files:
|
||||
- script=kubectl_pause_bmh.sh
|
@ -391,3 +391,22 @@ configRef:
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: KubevalOptions
|
||||
name: kubeval-options
|
||||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: GenericContainer
|
||||
metadata:
|
||||
name: kubectl-pause-bmh
|
||||
labels:
|
||||
airshipit.org/deploy-k8s: "false"
|
||||
spec:
|
||||
image: quay.io/airshipit/toolbox:latest
|
||||
hostNetwork: true
|
||||
envVars:
|
||||
- CLUSTER_NAMESPACE=default
|
||||
- RESOURCE_GROUP_FILTER=metal3.io
|
||||
- RESOURCE_VERSION_FILTER=v1alpha1
|
||||
- RESOURCE_KIND_FILTER=BareMetalHost
|
||||
configRef:
|
||||
kind: ConfigMap
|
||||
name: kubectl-pause-bmh
|
||||
apiVersion: v1
|
||||
|
@ -366,3 +366,15 @@ config:
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: GenericContainer
|
||||
name: kubectl-wait-pods
|
||||
---
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: Phase
|
||||
metadata:
|
||||
name: kubectl-pause-bmh
|
||||
clusterName: ephemeral-cluster
|
||||
config:
|
||||
executorRef:
|
||||
apiVersion: airshipit.org/v1alpha1
|
||||
kind: GenericContainer
|
||||
name: kubectl-pause-bmh
|
||||
documentEntryPoint: ephemeral/controlplane
|
||||
|
@ -17,16 +17,14 @@ set -xe
|
||||
#Default wait timeout is 3600 seconds
|
||||
export TIMEOUT=${TIMEOUT:-3600}
|
||||
export KUBECONFIG=${KUBECONFIG:-"$HOME/.airship/kubeconfig"}
|
||||
export KUBECONFIG_EPHEMERAL_CONTEXT=${KUBECONFIG_EPHEMERAL_CONTEXT:-"ephemeral-cluster"}
|
||||
export KUBECONFIG_TARGET_CONTEXT=${KUBECONFIG_TARGET_CONTEXT:-"target-cluster"}
|
||||
export TARGET_NODE=${TARGET_NODE:-"node01"}
|
||||
export CLUSTER_NAMESPACE=${CLUSTER_NAMESPACE:-"default"}
|
||||
|
||||
echo "Check Cluster Status"
|
||||
kubectl --kubeconfig $KUBECONFIG --context $KUBECONFIG_EPHEMERAL_CONTEXT -n $CLUSTER_NAMESPACE get cluster target-cluster -o json | jq '.status.controlPlaneReady'
|
||||
|
||||
echo "Annotate BMH for target node"
|
||||
kubectl --kubeconfig $KUBECONFIG --context $KUBECONFIG_EPHEMERAL_CONTEXT -n $CLUSTER_NAMESPACE annotate bmh $TARGET_NODE baremetalhost.metal3.io/paused=true
|
||||
# Annotating BMH objects with a pause label
|
||||
# Scripts for this phase placed in manifests/function/phase-helpers/pause_bmh/
|
||||
# To get ConfigMap for this phase, execute `airshipctl phase render --source config -k ConfigMap`
|
||||
# and find ConfigMap with name kubectl-get-pods
|
||||
airshipctl phase run kubectl-pause-bmh --debug
|
||||
|
||||
echo "Move Cluster Object to Target Cluster"
|
||||
airshipctl phase run clusterctl-move
|
||||
|
Loading…
Reference in New Issue
Block a user