1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00
kyverno/pkg/engine/variables/vars_test.go
Jim Bugwadia 2289720ba0
add support for shallow substitution (#11058)
* add support for shallow substitution

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* linter issue

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* exclude EphemeralReport and ClusterEphemeralReport

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

* update codegen

Signed-off-by: Jim Bugwadia <jim@nirmata.com>

---------

Signed-off-by: Jim Bugwadia <jim@nirmata.com>
Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
2024-10-08 19:43:04 +00:00

1260 lines
29 KiB
Go

package variables
import (
"fmt"
"strings"
"testing"
"github.com/go-logr/logr"
v1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/context"
ju "github.com/kyverno/kyverno/pkg/engine/jsonutils"
"gotest.tools/assert"
)
func Test_subVars_success(t *testing.T) {
patternMap := []byte(`
{
"kind": "{{request.object.metadata.name}}",
"name": "ns-owner-{{request.object.metadata.name}}",
"data": {
"rules": [
{
"apiGroups": [
"{{request.object.metadata.name}}"
],
"resources": [
"namespaces"
],
"verbs": [
"*"
],
"resourceNames": [
"{{request.object.metadata.name}}"
]
}
]
}
}
`)
resourceRaw := []byte(`
{
"metadata": {
"name": "temp",
"namespace": "n1"
},
"spec": {
"namespace": "n1",
"name": "temp1"
}
}
`)
var pattern, resource interface{}
var err error
err = json.Unmarshal(patternMap, &pattern)
if err != nil {
t.Error(err)
}
err = json.Unmarshal(resourceRaw, &resource)
if err != nil {
t.Error(err)
}
// context
ctx := context.NewContext(jp)
err = context.AddResource(ctx, resourceRaw)
if err != nil {
t.Error(err)
}
if _, err := SubstituteAll(logr.Discard(), ctx, pattern); err != nil {
t.Error(err)
}
}
func Test_subVars_failed(t *testing.T) {
patternMap := []byte(`
{
"kind": "{{request.object.metadata.name1}}",
"name": "ns-owner-{{request.object.metadata.name}}",
"data": {
"rules": [
{
"apiGroups": [
"{{request.object.metadata.name}}"
],
"resources": [
"namespaces"
],
"verbs": [
"*"
],
"resourceNames": [
"{{request.object.metadata.name1}}"
]
}
]
}
}
`)
resourceRaw := []byte(`
{
"metadata": {
"name": "temp",
"namespace": "n1"
},
"spec": {
"namespace": "n1",
"name": "temp1"
}
}
`)
var pattern, resource interface{}
var err error
err = json.Unmarshal(patternMap, &pattern)
if err != nil {
t.Error(err)
}
err = json.Unmarshal(resourceRaw, &resource)
if err != nil {
t.Error(err)
}
// context
ctx := context.NewContext(jp)
err = context.AddResource(ctx, resourceRaw)
if err != nil {
t.Error(err)
}
if _, err := SubstituteAll(logr.Discard(), ctx, pattern); err == nil {
t.Error("error is expected")
}
}
func Test_subVars_with_JMESPath_At(t *testing.T) {
patternMap := []byte(`{
"mutate": {
"overlay": {
"spec": {
"kind": "{{@}}",
"data": {
"rules": [
{
"apiGroups": [
"{{request.object.metadata.name}}"
],
"resources": [
"namespaces"
],
"verbs": [
"*"
],
"resourceNames": [
"{{request.object.metadata.name}}"
]
}
]
}
}
}
}
}`)
resourceRaw := []byte(`
{
"metadata": {
"name": "temp",
"namespace": "n1"
},
"spec": {
"kind": "foo",
"namespace": "n1",
"name": "temp1"
}
}
`)
expectedRaw := []byte(`{
"mutate":{
"overlay":{
"spec":{
"data":{
"rules":[
{
"apiGroups":[
"temp"
],
"resourceNames":[
"temp"
],
"resources":[
"namespaces"
],
"verbs":[
"*"
]
}
]
},
"kind":"foo"
}
}
}
}`)
var err error
var pattern, resource interface{}
err = json.Unmarshal(patternMap, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(resourceRaw, &resource)
assert.NilError(t, err)
// context
ctx := context.NewContext(jp)
err = context.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
output, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
out, err := json.Marshal(output)
assert.NilError(t, err)
assert.Equal(t, string(out), compact(t, expectedRaw))
}
func Test_subVars_withRegexMatch(t *testing.T) {
patternMap := []byte(`{
"mutate": {
"overlay": {
"spec": {
"port": "{{ regex_match('(443)', '{{@}}') }}",
"name": "ns-owner-{{request.object.metadata.name}}"
}
}
}
}`)
resourceRaw := []byte(`
{
"metadata": {
"name": "temp",
"namespace": "n1"
},
"spec": {
"port": "443",
"namespace": "n1",
"name": "temp1"
}
}`)
expectedRaw := []byte(`{
"mutate":{
"overlay":{
"spec":{
"name":"ns-owner-temp",
"port":true
}
}
}
}`)
var err error
var pattern, resource interface{}
err = json.Unmarshal(patternMap, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(resourceRaw, &resource)
assert.NilError(t, err)
// context
ctx := context.NewContext(jp)
err = context.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
output, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
out, err := json.Marshal(output)
assert.NilError(t, err)
assert.Equal(t, string(out), compact(t, expectedRaw))
}
func Test_subVars_withMerge(t *testing.T) {
patternMap := []byte(`{"map": "{{ merge(` + "`{\\\"a\\\": 1}`, `{\\\"b\\\": 1}`" + `)}}"}`)
resourceRaw := []byte(`{}`)
expectedRaw := []byte(`{"map": {"a":1,"b":1}}`)
var err error
var pattern, resource interface{}
err = json.Unmarshal(patternMap, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(resourceRaw, &resource)
assert.NilError(t, err)
// context
ctx := context.NewContext(jp)
err = context.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
output, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
out, err := json.Marshal(output)
assert.NilError(t, err)
assert.Equal(t, string(out), compact(t, expectedRaw))
}
func compact(t *testing.T, in []byte) string {
var tmp map[string]interface{}
err := json.Unmarshal(in, &tmp)
assert.NilError(t, err)
out, err := json.Marshal(tmp)
assert.NilError(t, err)
return string(out)
}
func Test_subVars_withRegexReplaceAll(t *testing.T) {
patternMap := []byte(`{
"mutate": {
"overlay": {
"spec": {
"port": "{{ regex_replace_all_literal('.*', '{{@}}', '1313') }}",
"name": "ns-owner-{{request.object.metadata.name}}"
}
}
}
}`)
resourceRaw := []byte(`{
"metadata": {
"name": "temp",
"namespace": "n1"
},
"spec": {
"port": "43123",
"namespace": "n1",
"name": "temp1"
}
}`)
expected := []byte(`{"mutate":{"overlay":{"spec":{"name":"ns-owner-temp","port":"1313"}}}}`)
var pattern, resource interface{}
var err error
err = json.Unmarshal(patternMap, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(resourceRaw, &resource)
assert.NilError(t, err)
// context
ctx := context.NewContext(jp)
err = context.AddResource(ctx, resourceRaw)
assert.NilError(t, err)
output, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
out, err := json.Marshal(output)
assert.NilError(t, err)
assert.Equal(t, string(out), string(expected))
}
func Test_ReplacingPathWhenDeleting(t *testing.T) {
patternRaw := []byte(`"{{request.object.metadata.annotations.target}}"`)
resourceRaw := []byte(`
{
"request": {
"operation": "DELETE",
"object": {
"metadata": {
"name": "curr",
"namespace": "ns",
"annotations": {
"target": "foo"
}
}
},
"oldObject": {
"metadata": {
"name": "old",
"annotations": {
"target": "bar"
}
}
}
}
}
`)
var pattern interface{}
var err error
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
ctxMap, err := unmarshalToMap(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContextFromRaw(jp, ctxMap)
assert.NilError(t, err)
pattern, err = SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
assert.Equal(t, fmt.Sprintf("%v", pattern), "bar")
}
func unmarshalToMap(jsonBytes []byte) (map[string]interface{}, error) {
var data map[string]interface{}
if err := json.Unmarshal(jsonBytes, &data); err != nil {
return nil, err
}
return data, nil
}
func Test_ReplacingNestedVariableWhenDeleting(t *testing.T) {
patternRaw := []byte(`"{{request.object.metadata.annotations.{{request.object.metadata.annotations.targetnew}}}}"`)
resourceRaw := []byte(`
{
"request":{
"operation":"DELETE",
"oldObject":{
"metadata":{
"name":"current",
"namespace":"ns",
"annotations":{
"target":"nested_target",
"targetnew":"target"
}
}
}
}
}`)
var pattern interface{}
var err error
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
ctxMap, err := unmarshalToMap(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContextFromRaw(jp, ctxMap)
pattern, err = SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
assert.Equal(t, fmt.Sprintf("%v", pattern), "nested_target")
}
var resourceRaw = []byte(`
{
"metadata": {
"name": "temp",
"namespace": "n1",
"annotations": {
"test": "name"
}
},
"spec": {
"namespace": "n1",
"name": "temp1"
}
}
`)
func Test_SubstituteSuccess(t *testing.T) {
ctx := context.NewContext(jp)
assert.Assert(t, context.AddResource(ctx, resourceRaw))
var pattern interface{}
patternRaw := []byte(`"{{request.object.metadata.annotations.test}}"`)
assert.Assert(t, json.Unmarshal(patternRaw, &pattern))
action := substituteVariablesIfAny(logr.Discard(), ctx, DefaultVariableResolver)
results, err := action(&ju.ActionData{
Document: nil,
Element: string(patternRaw),
Path: "/",
})
if err != nil {
t.Errorf("substitution failed: %v", err.Error())
return
}
if results.(string) != `"name"` {
t.Errorf("expected %s received %v", "name", results)
}
}
func Test_SubstituteRecursiveErrors(t *testing.T) {
ctx := context.NewContext(jp)
assert.Assert(t, context.AddResource(ctx, resourceRaw))
var pattern interface{}
patternRaw := []byte(`"{{request.object.metadata.{{request.object.metadata.annotations.test2}}}}"`)
assert.Assert(t, json.Unmarshal(patternRaw, &pattern))
action := substituteVariablesIfAny(logr.Discard(), ctx, DefaultVariableResolver)
results, err := action(&ju.ActionData{
Document: nil,
Element: string(patternRaw),
Path: "/",
})
if err == nil {
t.Errorf("expected error but received: %v", results)
}
patternRaw = []byte(`"{{request.object.metadata2.{{request.object.metadata.annotations.test}}}}"`)
assert.Assert(t, json.Unmarshal(patternRaw, &pattern))
action = substituteVariablesIfAny(logr.Discard(), ctx, DefaultVariableResolver)
results, err = action(&ju.ActionData{
Document: nil,
Element: string(patternRaw),
Path: "/",
})
if err == nil {
t.Errorf("expected error but received: %v", results)
}
}
func Test_SubstituteRecursive(t *testing.T) {
ctx := context.NewContext(jp)
assert.Assert(t, context.AddResource(ctx, resourceRaw))
var pattern interface{}
patternRaw := []byte(`"{{request.object.metadata.{{request.object.metadata.annotations.test}}}}"`)
assert.Assert(t, json.Unmarshal(patternRaw, &pattern))
action := substituteVariablesIfAny(logr.Discard(), ctx, DefaultVariableResolver)
results, err := action(&ju.ActionData{
Document: nil,
Element: string(patternRaw),
Path: "/",
})
if err != nil {
t.Errorf("substitution failed: %v", err.Error())
return
}
if results.(string) != `"temp"` {
t.Errorf("expected %s received %v", "temp", results)
}
}
func Test_SubstituteShallow(t *testing.T) {
ctx := context.NewContext(jp)
data := map[string]interface{}{
"variableWithVariables": "{{ DO_NOT_SUBSTITUTE_ME {{OR_ME}} }}",
"foo": "bar",
"foo2": "bar2",
"variablesNested": "{{foo2}}",
}
assert.NilError(t, context.AddJSONObject(ctx, data))
patternRaw := []byte(`"{{- variableWithVariables }} {{foo}} {{variablesNested}}"`)
action := substituteVariablesIfAny(logr.Discard(), ctx, DefaultVariableResolver)
results, err := action(&ju.ActionData{
Document: nil,
Element: string(patternRaw),
Path: "/",
})
assert.NilError(t, err)
assert.Equal(t, results.(string), "\"{{ DO_NOT_SUBSTITUTE_ME {{OR_ME}} }} bar bar2\"")
patternRaw = []byte(`"{{foo}} {{- variableWithVariables }} {{variablesNested}}"`)
action = substituteVariablesIfAny(logr.Discard(), ctx, DefaultVariableResolver)
results, err = action(&ju.ActionData{
Document: nil,
Element: string(patternRaw),
Path: "/",
})
assert.NilError(t, err)
assert.Equal(t, results.(string), "\"bar {{ DO_NOT_SUBSTITUTE_ME {{OR_ME}} }} bar2\"")
patternRaw = []byte(`"{{- variableWithVariables {{foo}} {{variablesNested}} }}"`)
action = substituteVariablesIfAny(logr.Discard(), ctx, DefaultVariableResolver)
_, err = action(&ju.ActionData{
Document: nil,
Element: string(patternRaw),
Path: "/",
})
assert.ErrorContains(t, err, "failed to resolve variableWithVariables bar bar2")
}
func Test_policyContextValidation(t *testing.T) {
policyContext := []byte(`
{
"context": [
{
"name": "myconfigmap",
"apiCall": {
"urlPath": "/api/v1/namespaces/{{ request.namespace }}/configmaps/generate-pod"
}
}
]
}
`)
var contextMap interface{}
err := json.Unmarshal(policyContext, &contextMap)
assert.NilError(t, err)
ctx := context.NewMockContext(nil, "request.object")
_, err = SubstituteAll(logr.Discard(), ctx, contextMap)
assert.Assert(t, err != nil, err)
}
func Test_variableSubstitution_array(t *testing.T) {
configmapRaw := []byte(`
{
"animals": {
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "animals",
"namespace": "default"
},
"data": {
"animals": "snake\nbear\ncat\ndog"
}
}
}`)
ruleRaw := []byte(`
{
"name": "validate-role-annotation",
"context": [
{
"name": "animals",
"configMap": {
"name": "animals",
"namespace": "default"
}
}
],
"match": {
"resources": {
"kinds": [
"Deployment"
]
}
},
"validate": {
"message": "The animal {{ request.object.metadata.labels.animal }} is not in the allowed list of animals: {{ animals.data.animals }}.",
"deny": {
"conditions": [
{
"key": "{{ request.object.metadata.labels.animal }}",
"operator": "NotIn",
"value": "{{ animals.data.animals }}"
}
]
}
}
}`)
resourceRaw := []byte(`
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "busybox",
"labels": {
"app": "busybox",
"color": "red",
"animal": "cow",
"food": "pizza",
"car": "jeep",
"env": "qa"
}
}
}
`)
var rule v1.Rule
err := json.Unmarshal(ruleRaw, &rule)
assert.NilError(t, err)
ctxMap, err := unmarshalToMap(configmapRaw)
assert.NilError(t, err)
ctx := context.NewContextFromRaw(jp, ctxMap)
context.AddResource(ctx, resourceRaw)
vars, err := SubstituteAllInRule(logr.Discard(), ctx, rule)
assert.NilError(t, err)
assert.DeepEqual(t, vars.Validation.Message, "The animal cow is not in the allowed list of animals: snake\nbear\ncat\ndog.")
}
var variableObject = []byte(`
{
"complex_object_array": [
"value1",
"value2",
"value3"
],
"complex_object_map": {
"key1": "value1",
"key2": "value2",
"key3": "value3"
},
"simple_object_bool": false,
"simple_object_int": 5,
"simple_object_float": -5.5,
"simple_object_string": "example",
"simple_object_null": null
}
`)
func Test_SubstituteNull(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "{{ request.object.simple_object_null }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
var expected interface{}
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteNullInString(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "content = {{ request.object.simple_object_null }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := "content = null"
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteArray(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "{{ request.object.complex_object_array }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := resource["complex_object_array"]
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteArrayInString(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "content is {{ request.object.complex_object_map }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := `content is {"key1":"value1","key2":"value2","key3":"value3"}`
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteInt(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "{{ request.object.simple_object_int }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := resource["simple_object_int"]
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteIntInString(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "content = {{ request.object.simple_object_int }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := "content = 5"
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteBool(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "{{ request.object.simple_object_bool }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := false
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteBoolInString(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "content = {{ request.object.simple_object_bool }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := "content = false"
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteString(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "{{ request.object.simple_object_string }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := "example"
assert.DeepEqual(t, expected, content)
}
func Test_SubstituteStringInString(t *testing.T) {
patternRaw := []byte(`
{
"spec": {
"content": "content = {{ request.object.simple_object_string }}"
}
}
`)
var err error
var pattern, resource map[string]interface{}
err = json.Unmarshal(patternRaw, &pattern)
assert.NilError(t, err)
err = json.Unmarshal(variableObject, &resource)
assert.NilError(t, err)
ctx := context.NewContext(jp)
context.AddResource(ctx, variableObject)
resolved, err := SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
content := resolved.(map[string]interface{})["spec"].(map[string]interface{})["content"]
expected := "content = example"
assert.DeepEqual(t, expected, content)
}
func Test_ReferenceSubstitution(t *testing.T) {
jsonRaw := []byte(`
{
"metadata": {
"name": "temp",
"namespace": "n1",
"annotations": {
"test": "$(../../../../spec/namespace)"
}
},
"(spec)": {
"namespace": "n1",
"name": "temp1"
}
}`)
expectedJSON := []byte(`
{
"metadata": {
"name": "temp",
"namespace": "n1",
"annotations": {
"test": "n1"
}
},
"(spec)": {
"namespace": "n1",
"name": "temp1"
}
}`)
var document interface{}
err := json.Unmarshal(jsonRaw, &document)
assert.NilError(t, err)
var expectedDocument interface{}
err = json.Unmarshal(expectedJSON, &expectedDocument)
assert.NilError(t, err)
ctx := context.NewContext(jp)
err = context.AddResource(ctx, jsonRaw)
assert.NilError(t, err)
actualDocument, err := SubstituteAll(logr.Discard(), ctx, document)
assert.NilError(t, err)
assert.DeepEqual(t, expectedDocument, actualDocument)
}
func TestFormAbsolutePath_RelativePathExists(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := "./../../limits/memory"
expectedString := "/spec/containers/0/resources/limits/memory"
result := formAbsolutePath(referencePath, absolutePath)
assert.Assert(t, result == expectedString)
}
func TestFormAbsolutePath_RelativePathWithBackToTopInTheBeginning(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := "../../limits/memory"
expectedString := "/spec/containers/0/resources/limits/memory"
result := formAbsolutePath(referencePath, absolutePath)
assert.Assert(t, result == expectedString)
}
func TestFormAbsolutePath_AbsolutePathExists(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := "/spec/containers/0/resources/limits/memory"
result := formAbsolutePath(referencePath, absolutePath)
assert.Assert(t, result == referencePath)
}
func TestFormAbsolutePath_EmptyPath(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := ""
result := formAbsolutePath(referencePath, absolutePath)
assert.Assert(t, result == absolutePath)
}
func TestActualizePattern_GivenRelativePathThatExists(t *testing.T) {
absolutePath := "/spec/containers/0/resources/requests/memory"
referencePath := "$(<=./../../limits/memory)"
rawPattern := []byte(`{
"spec":{
"containers":[
{
"name":"*",
"resources":{
"requests":{
"memory":"$(<=./../../limits/memory)"
},
"limits":{
"memory":"2048Mi"
}
}
}
]
}
}`)
resolvedReference := "<=2048Mi"
var pattern interface{}
assert.NilError(t, json.Unmarshal(rawPattern, &pattern))
// pattern, err := actualizePattern(log.Log, pattern, referencePath, absolutePath)
pattern, err := resolveReference(pattern, referencePath, absolutePath)
assert.NilError(t, err)
assert.DeepEqual(t, resolvedReference, pattern)
}
func TestFindAndShiftReferences_PositiveCase(t *testing.T) {
message := "Message with $(./../../pattern/spec/containers/0/image) reference inside. Or maybe even two $(./../../pattern/spec/containers/0/image), but they are same."
expectedMessage := strings.Replace(message, "$(./../../pattern/spec/containers/0/image)", "$(./../../pattern/spec/jobTemplate/spec/containers/0/image)", -1)
actualMessage := FindAndShiftReferences(logr.Discard(), message, "spec/jobTemplate", "pattern")
assert.Equal(t, expectedMessage, actualMessage)
}
func TestFindAndShiftReferences_AnyPatternPositiveCase(t *testing.T) {
message := "Message with $(./../../anyPattern/0/spec/containers/0/image)."
expectedMessage := strings.Replace(message, "$(./../../anyPattern/0/spec/containers/0/image)", "$(./../../anyPattern/0/spec/jobTemplate/spec/containers/0/image)", -1)
actualMessage := FindAndShiftReferences(logr.Discard(), message, "spec/jobTemplate", "anyPattern")
assert.Equal(t, expectedMessage, actualMessage)
}
func Test_EscpReferenceSubstitution(t *testing.T) {
jsonRaw := []byte(`
{
"metadata": {
"name": "temp",
"namespace": "n1",
"annotations": {
"test1": "$(../../../../spec/namespace)",
"test2": "\\$(ENV_VAR)",
"test3": "\\${ENV_VAR}",
"test4": "\\\\\\${ENV_VAR}",
"test5": "\\$(NODE_NAME)/postgres/\\$(POD_NAME)"
}
},
"(spec)": {
"namespace": "n1",
"name": "temp1"
}
}`)
expectedJSON := []byte(`
{
"metadata": {
"name": "temp",
"namespace": "n1",
"annotations": {
"test1": "n1",
"test2": "$(ENV_VAR)",
"test3": "\\${ENV_VAR}",
"test4": "\\\\\\${ENV_VAR}",
"test5": "$(NODE_NAME)/postgres/$(POD_NAME)"
}
},
"(spec)": {
"namespace": "n1",
"name": "temp1"
}
}`)
var document interface{}
err := json.Unmarshal(jsonRaw, &document)
assert.NilError(t, err)
var expectedDocument interface{}
err = json.Unmarshal(expectedJSON, &expectedDocument)
assert.NilError(t, err)
ctx := context.NewContext(jp)
err = context.AddResource(ctx, jsonRaw)
assert.NilError(t, err)
actualDocument, err := SubstituteAll(logr.Discard(), ctx, document)
assert.NilError(t, err)
assert.DeepEqual(t, expectedDocument, actualDocument)
}
func Test_ReplacingEscpNestedVariableWhenDeleting(t *testing.T) {
patternRaw := []byte(`"\\{{request.object.metadata.annotations.{{request.object.metadata.annotations.targetnew}}}}"`)
resourceRaw := []byte(`
{
"request":{
"operation":"DELETE",
"oldObject":{
"metadata":{
"name":"current",
"namespace":"ns",
"annotations":{
"target":"nested_target",
"targetnew":"target"
}
}
}
}
}`)
var pattern interface{}
var err error
err = json.Unmarshal(patternRaw, &pattern)
if err != nil {
t.Error(err)
}
ctxMap, err := unmarshalToMap(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContextFromRaw(jp, ctxMap)
assert.NilError(t, err)
pattern, err = SubstituteAll(logr.Discard(), ctx, pattern)
assert.NilError(t, err)
assert.Equal(t, fmt.Sprintf("%v", pattern), "{{request.object.metadata.annotations.target}}")
}
func Test_ReplaceAllVars(t *testing.T) {
result := ReplaceAllVars("{{ foo }}", func(s string) string { return "test" })
assert.Equal(t, result, "test")
result = ReplaceAllVars("\"{{ foo }}\"", func(s string) string { return "test" })
assert.Equal(t, result, "\"test\"")
result = ReplaceAllVars("/s/{{elementIndex}}/r", func(s string) string { return "test" })
assert.Equal(t, result, "/s/test/r")
result = ReplaceAllVars("{{ foo }} {{foo}} {{foo}}", func(s string) string { return "test" })
assert.Equal(t, result, "test test test")
result = ReplaceAllVars("{{ foo }} \\{{foo}} {{foo}}", func(s string) string { return "test" })
assert.Equal(t, result, "test \\{{foo}} test")
result = ReplaceAllVars("{{ foo {{foo}} }}", func(s string) string { return "test" })
assert.Equal(t, result, "{{ foo test }}")
}