1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

Merge pull request from yashvardhan-kukreja/issue-1242/multiline-yaml-string-support

feat: added functionality for delimiting multi-line block by newline characters
This commit is contained in:
Jim Bugwadia 2021-03-10 09:58:38 -08:00 committed by GitHub
commit bb361df696
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 97 additions and 0 deletions

View file

@ -13,6 +13,7 @@ import (
"github.com/kyverno/kyverno/pkg/resourcecache"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/dynamic/dynamiclister"
"strings"
)
// LoadContext - Fetches and adds external data to the Context.
@ -193,6 +194,9 @@ func fetchConfigMap(logger logr.Logger, entry kyverno.ContextEntry, lister dynam
return nil, fmt.Errorf("failed to convert configmap %s/%s: %v", namespace, name, err)
}
// update the unstructuredObj["data"] to delimit and split the string value (containing "\n") with "\n"
unstructuredObj["data"] = parseMultilineBlockBody(unstructuredObj["data"].(map[string]interface{}))
// extract configmap data
contextData["data"] = unstructuredObj["data"]
contextData["metadata"] = unstructuredObj["metadata"]
@ -205,3 +209,25 @@ func fetchConfigMap(logger logr.Logger, entry kyverno.ContextEntry, lister dynam
return data, nil
}
// parseMultilineBlockBody recursively iterates through a map and updates its values in the following way
// whenever it encounters a string value containing "\n",
// it converts it into a []string by splitting it by "\n"
func parseMultilineBlockBody(m map[string]interface{}) map[string]interface{} {
for k, v := range m {
switch typedValue := v.(type) {
case string:
trimmedTypedValue := strings.Trim(typedValue, "\n")
if strings.Contains(trimmedTypedValue, "\n") {
m[k] = strings.Split(trimmedTypedValue, "\n")
} else {
m[k] = trimmedTypedValue // trimming a str if it has trailing newline characters
}
case map[string]interface{}:
m[k] = parseMultilineBlockBody(typedValue)
default:
continue
}
}
return m
}

View file

@ -0,0 +1,71 @@
package engine
import (
"bytes"
"encoding/json"
"gotest.tools/assert"
"testing"
)
func Test_parseMultilineBlockBody(t *testing.T) {
tcs := []struct {
multilineBlockRaw []byte
expectedMultilineBlockRaw []byte
expectedErr bool
}{
{
multilineBlockRaw: []byte(`{
"key1": "value",
"key2": "value2",
"key3": "word1\nword2\nword3",
"key4": "word4\n"
}`),
expectedMultilineBlockRaw: []byte(`{"key1":"value","key2":"value2","key3":["word1","word2","word3"],"key4":"word4"}`),
expectedErr: false,
},
{
multilineBlockRaw: []byte(`{
"key1": "value",
"key2": "value2",
"key3": "word1\nword2\nword3",
"key4": "word4"
}`),
expectedMultilineBlockRaw: []byte(`{"key1":"value","key2":"value2","key3":["word1","word2","word3"],"key4":"word4"}`),
expectedErr: false,
},
{
multilineBlockRaw: []byte(`{
"key1": "value1",
"key2": "value2\n",
"key3": "word1",
"key4": "word2"
}`),
expectedMultilineBlockRaw: []byte(`{"key1":"value1","key2":["value2",""]}`),
expectedErr: true,
},
{
multilineBlockRaw: []byte(`{
"key1": "value1",
"key2": "[\"cluster-admin\", \"cluster-operator\", \"tenant-admin\"]"
}`),
expectedMultilineBlockRaw: []byte(`{"key1":"value1","key2":"[\"cluster-admin\", \"cluster-operator\", \"tenant-admin\"]"}`),
expectedErr: false,
},
}
for _, tc := range tcs {
var multilineBlock map[string]interface{}
err := json.Unmarshal(tc.multilineBlockRaw, &multilineBlock)
assert.NilError(t, err)
parsedMultilineBlock := parseMultilineBlockBody(multilineBlock)
parsedMultilineBlockRaw, err := json.Marshal(parsedMultilineBlock)
assert.NilError(t, err)
if tc.expectedErr {
assert.Assert(t, bytes.Compare(parsedMultilineBlockRaw, tc.expectedMultilineBlockRaw) != 0)
} else {
assert.Assert(t, bytes.Compare(parsedMultilineBlockRaw, tc.expectedMultilineBlockRaw) == 0)
}
}
}