1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

Add a parse_yaml function (#2999)

Signed-off-by: Sambhav Kothari <sambhavs.email@gmail.com>

Co-authored-by: shuting <shutting06@gmail.com>
This commit is contained in:
Sambhav Kothari 2022-01-17 13:41:08 +00:00 committed by GitHub
parent de6c6f2199
commit f5e00ee034
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 186 additions and 0 deletions

View file

@ -16,6 +16,7 @@ import (
"github.com/blang/semver/v4"
gojmespath "github.com/jmespath/go-jmespath"
"github.com/minio/pkg/wildcard"
"sigs.k8s.io/yaml"
)
var (
@ -59,6 +60,7 @@ var (
truncate = "truncate"
semverCompare = "semver_compare"
parseJson = "parse_json"
parseYAML = "parse_yaml"
)
const errorPrefix = "JMESPath function '%s': "
@ -271,6 +273,13 @@ func getFunctions() []*gojmespath.FunctionEntry {
},
Handler: jpParseJson,
},
{
Name: parseYAML,
Arguments: []ArgSpec{
{Types: []JpType{JpString}},
},
Handler: jpParseYAML,
},
}
}
@ -684,6 +693,20 @@ func jpParseJson(arguments []interface{}) (interface{}, error) {
return output, err
}
func jpParseYAML(arguments []interface{}) (interface{}, error) {
input, err := validateArg(parseYAML, arguments, 0, reflect.String)
if err != nil {
return nil, err
}
jsonData, err := yaml.YAMLToJSON([]byte(input.String()))
if err != nil {
return nil, err
}
var output interface{}
err = json.Unmarshal(jsonData, &output)
return output, err
}
// InterfaceToString casts an interface to a string type
func ifaceToString(iface interface{}) (string, error) {
switch i := iface.(type) {

View file

@ -101,6 +101,82 @@ func Test_ParseJsonComplex(t *testing.T) {
}
}
func Test_ParseYAML(t *testing.T) {
testCases := []struct {
input string
output interface{}
}{
{
input: `a: b`,
output: map[string]interface{}{
"a": "b",
},
},
{
input: `
- 1
- 2
- 3
- a: b`,
output: []interface{}{
1.0,
2.0,
3.0,
map[string]interface{}{
"a": "b",
},
},
},
{
input: `
spec:
test: 1
test2:
- 2
- 3
`,
output: map[string]interface{}{
"spec": map[string]interface{}{
"test": 1.0,
"test2": []interface{}{2.0, 3.0},
},
},
},
{
input: `
bar: >
this is not a normal string it
spans more than
one line
see?`,
output: map[string]interface{}{
"bar": "this is not a normal string it spans more than one line see?",
},
},
{
input: `
---
foo: ~
bar: null
`,
output: map[string]interface{}{
"bar": nil,
"foo": nil,
},
},
}
for _, tc := range testCases {
t.Run(tc.input, func(t *testing.T) {
jp, err := New(fmt.Sprintf(`parse_yaml('%s')`, tc.input))
assert.NilError(t, err)
result, err := jp.Search("")
assert.NilError(t, err)
assert.DeepEqual(t, result, tc.output)
})
}
}
func Test_EqualFold(t *testing.T) {
testCases := []struct {
jmesPath string

View file

@ -39,3 +39,23 @@ results:
resource: invalid-test
kind: ConfigMap
result: fail
- policy: test-parse-yaml
rule: test-yaml-parsing-jmespath
resource: valid-yaml-test
kind: ConfigMap
result: pass
- policy: test-parse-yaml
rule: test-yaml-parsing-jmespath
resource: invalid-yaml-test
kind: ConfigMap
result: fail
- policy: test-parse-yaml-array
rule: test-yaml-parsing-jmespath
resource: valid-yaml-test
kind: ConfigMap
result: pass
- policy: test-parse-yaml-array
rule: test-yaml-parsing-jmespath
resource: invalid-yaml-test
kind: ConfigMap
result: fail

View file

@ -95,3 +95,45 @@ spec:
- key: "{{request.object.metadata.annotations.test | parse_json(@).a }}"
operator: NotEquals
value: b
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: test-parse-yaml
spec:
validationFailureAction: enforce
background: false
rules:
- name: test-yaml-parsing-jmespath
match:
resources:
kinds:
- ConfigMap
validate:
message: "Test JMESPath"
deny:
conditions:
- key: "{{request.object.metadata.annotations.test | parse_yaml(@).value }}"
operator: NotEquals
value: "a"
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: test-parse-yaml-array
spec:
validationFailureAction: enforce
background: false
rules:
- name: test-yaml-parsing-jmespath
match:
resources:
kinds:
- ConfigMap
validate:
message: "Test JMESPath"
deny:
conditions:
- key: a
operator: NotIn
value: "{{request.object.metadata.annotations.test | parse_yaml(@).array }}"

View file

@ -67,3 +67,28 @@ metadata:
annotations:
test: |
{"a": "not-b"}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: valid-yaml-test
annotations:
test: |
value: a
array:
- a
- b
- c
---
apiVersion: v1
kind: ConfigMap
metadata:
name: invalid-yaml-test
annotations:
test: |
value: b
array:
- d
- e
- f