2020-02-13 13:57:48 -08:00
package variables
import (
"encoding/json"
2021-03-19 23:43:26 +02:00
"fmt"
2021-03-25 18:11:32 -07:00
"strings"
2020-02-13 13:57:48 -08:00
"testing"
2020-10-07 11:12:31 -07:00
"github.com/kyverno/kyverno/pkg/engine/context"
2021-03-11 22:06:04 +02:00
ju "github.com/kyverno/kyverno/pkg/engine/json-utils"
2020-02-26 16:41:48 -08:00
"gotest.tools/assert"
2020-03-17 16:25:34 -07:00
"sigs.k8s.io/controller-runtime/pkg/log"
2020-02-13 13:57:48 -08:00
)
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 ( )
err = ctx . AddResource ( resourceRaw )
if err != nil {
t . Error ( err )
}
2021-03-11 22:06:04 +02:00
if _ , err := SubstituteAll ( log . Log , ctx , pattern ) ; err != nil {
2020-02-13 13:57:48 -08:00
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 ( )
err = ctx . AddResource ( resourceRaw )
if err != nil {
t . Error ( err )
}
2021-03-11 22:06:04 +02:00
if _ , err := SubstituteAll ( log . Log , ctx , pattern ) ; err == nil {
2020-02-13 13:57:48 -08:00
t . Error ( "error is expected" )
}
}
2020-02-26 16:41:48 -08:00
2021-03-19 23:43:26 +02:00
func Test_ReplacingPathWhenDeleting ( t * testing . T ) {
patternRaw := [ ] byte ( ` " {{ request .object .metadata .annotations .target }} " ` )
var 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 )
if err != nil {
t . Error ( err )
}
ctx := context . NewContext ( )
err = ctx . AddJSON ( resourceRaw )
assert . NilError ( t , err )
2021-03-25 18:11:32 -07:00
pattern , err = SubstituteAll ( log . Log , ctx , pattern )
2021-03-19 23:43:26 +02:00
assert . NilError ( t , err )
assert . Equal ( t , fmt . Sprintf ( "%v" , pattern ) , "bar" )
}
func Test_ReplacingNestedVariableWhenDeleting ( t * testing . T ) {
patternRaw := [ ] byte ( ` " {{ request .object .metadata .annotations . { { request .object .metadata .annotations .targetnew }} }}" ` )
var 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 )
}
ctx := context . NewContext ( )
err = ctx . AddJSON ( resourceRaw )
assert . NilError ( t , err )
2021-03-25 18:11:32 -07:00
pattern , err = SubstituteAll ( log . Log , ctx , pattern )
2021-03-19 23:43:26 +02:00
assert . NilError ( t , err )
assert . Equal ( t , fmt . Sprintf ( "%v" , pattern ) , "nested_target" )
}
2021-01-14 10:46:51 -08:00
var resourceRaw = [ ] byte ( `
2020-02-26 16:41:48 -08:00
{
"metadata" : {
"name" : "temp" ,
"namespace" : "n1" ,
2021-01-14 10:46:51 -08:00
"annotations" : {
"test" : "name"
}
2020-02-26 16:41:48 -08:00
} ,
"spec" : {
"namespace" : "n1" ,
"name" : "temp1"
}
}
2021-01-14 10:46:51 -08:00
` )
func Test_SubstituteSuccess ( t * testing . T ) {
ctx := context . NewContext ( )
assert . Assert ( t , ctx . AddResource ( resourceRaw ) )
var pattern interface { }
patternRaw := [ ] byte ( ` " {{ request .object .metadata .annotations .test }} " ` )
assert . Assert ( t , json . Unmarshal ( patternRaw , & pattern ) )
2021-03-11 22:06:04 +02:00
action := substituteVariablesIfAny ( log . Log , ctx )
results , err := action ( & ju . ActionData {
Document : nil ,
Element : string ( patternRaw ) ,
Path : "/" } )
2021-01-14 10:46:51 -08:00
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 ( )
assert . Assert ( t , ctx . AddResource ( resourceRaw ) )
2020-02-26 16:41:48 -08:00
2021-01-14 10:46:51 -08:00
var pattern interface { }
patternRaw := [ ] byte ( ` " {{ request .object .metadata . { { request .object .metadata .annotations .test2 }} }}" ` )
assert . Assert ( t , json . Unmarshal ( patternRaw , & pattern ) )
2021-03-11 22:06:04 +02:00
action := substituteVariablesIfAny ( log . Log , ctx )
results , err := action ( & ju . ActionData {
Document : nil ,
Element : string ( patternRaw ) ,
Path : "/" } )
2021-01-14 10:46:51 -08:00
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 ) )
2021-03-11 22:06:04 +02:00
action = substituteVariablesIfAny ( log . Log , ctx )
results , err = action ( & ju . ActionData {
Document : nil ,
Element : string ( patternRaw ) ,
Path : "/" } )
2021-01-14 10:46:51 -08:00
if err == nil {
t . Errorf ( "expected error but received: %v" , results )
}
}
func Test_SubstituteRecursive ( t * testing . T ) {
2020-02-26 16:41:48 -08:00
ctx := context . NewContext ( )
assert . Assert ( t , ctx . AddResource ( resourceRaw ) )
2021-01-14 10:46:51 -08:00
var pattern interface { }
patternRaw := [ ] byte ( ` " {{ request .object .metadata . { { request .object .metadata .annotations .test }} }}" ` )
assert . Assert ( t , json . Unmarshal ( patternRaw , & pattern ) )
2021-03-11 22:06:04 +02:00
action := substituteVariablesIfAny ( log . Log , ctx )
results , err := action ( & ju . ActionData {
Document : nil ,
Element : string ( patternRaw ) ,
Path : "/" } )
2021-01-14 10:46:51 -08:00
if err != nil {
t . Errorf ( "substitution failed: %v" , err . Error ( ) )
return
}
if results . ( string ) != ` "temp" ` {
t . Errorf ( "expected %s received %v" , "temp" , results )
}
2020-02-26 16:41:48 -08:00
}
2021-02-22 12:08:26 -08:00
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 . NewContext ( "request.object" )
2021-03-11 22:06:04 +02:00
_ , err = SubstituteAll ( log . Log , ctx , contextMap )
2021-02-22 12:08:26 -08:00
assert . Assert ( t , err != nil , err )
}
2021-03-11 22:06:04 +02:00
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 ( )
err = ctx . AddResource ( jsonRaw )
assert . NilError ( t , err )
actualDocument , err := SubstituteAll ( log . Log , 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_RelativePathWithBackToTopInTheBegining ( 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 ( log . Log , 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 ( log . Log , 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 ( log . Log , message , "spec/jobTemplate" , "anyPattern" )
assert . Equal ( t , expectedMessage , actualMessage )
}