1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00
kyverno/pkg/engine/context/deferred_test.go
Vishal Choudhary 93eac3f7a4
fix: deferred loader panic when mutate and generate policies are applied (#9935)
* fix: deferred loader panic when mutate and generate policies are applied

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: tests

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: update policies

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* remove clusterrolebinding

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: copy only json context

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

* fix: polctx

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>

---------

Signed-off-by: Vishal Choudhary <vishal.choudhary@nirmata.com>
Co-authored-by: shuting <shuting@nirmata.com>
2024-03-29 14:37:15 +01:00

414 lines
10 KiB
Go

package context
import (
"fmt"
"testing"
"github.com/pkg/errors"
"gotest.tools/assert"
)
func TestDeferredLoaderMatch(t *testing.T) {
ctx := newContext()
mockLoader, _ := AddMockDeferredLoader(ctx, "one", "1")
assert.Equal(t, 0, mockLoader.invocations)
val, err := ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "1", val)
assert.Equal(t, 1, mockLoader.invocations)
_, _ = ctx.Query("one")
assert.Equal(t, 1, mockLoader.invocations)
ctx = newContext()
ml, _ := AddMockDeferredLoader(ctx, "one", "1")
testCheckMatch(t, ctx, "one<two", "one", "1", ml)
ml, _ = AddMockDeferredLoader(ctx, "one", "1")
testCheckMatch(t, ctx, "(one)", "one", "1", ml)
ml, _ = AddMockDeferredLoader(ctx, "one", "1")
testCheckMatch(t, ctx, "one.two.three", "one", "1", ml)
ml, _ = AddMockDeferredLoader(ctx, "one", "1")
testCheckMatch(t, ctx, "one-two", "one", "1", ml)
ml, _ = AddMockDeferredLoader(ctx, "one", "1")
testCheckMatch(t, ctx, "one; two; three", "one", "1", ml)
ml, _ = AddMockDeferredLoader(ctx, "one", "1")
testCheckMatch(t, ctx, "one>two", "one", "1", ml)
ml, _ = AddMockDeferredLoader(ctx, "one", "1")
testCheckMatch(t, ctx, "one, two, three", "one", "1", ml)
ml, _ = AddMockDeferredLoader(ctx, "one1", "11")
testCheckMatch(t, ctx, "one1", "one1", "11", ml)
}
func testCheckMatch(t *testing.T, ctx *context, query, name, value string, ml *mockLoader) {
var events []string
hdlr := func(name string) {
events = append(events, name)
}
ml.setEventHandler(hdlr)
err := ctx.deferred.LoadMatching(query, len(ctx.jsonRawCheckpoints))
assert.NilError(t, err)
assert.Equal(t, 1, len(events), "deferred loader %s not executed for query %s", name, query)
expected := fmt.Sprintf("%s=%s", name, value)
assert.Equal(t, expected, events[0], "deferred loader %s name mismatch for query %s; received %s", name, query, events[0])
}
func TestDeferredLoaderMismatch(t *testing.T) {
ctx := newContext()
AddMockDeferredLoader(ctx, "one", "1")
_, err := ctx.Query("oneTwoThree")
assert.ErrorContains(t, err, `Unknown key "oneTwoThree" in path`)
_, err = ctx.Query("one1")
assert.ErrorContains(t, err, `Unknown key "one1" in path`)
_, err = ctx.Query("one_two")
assert.ErrorContains(t, err, `Unknown key "one_two" in path`)
_, err = ctx.Query("\"one-two\"")
assert.ErrorContains(t, err, `Unknown key "one-two" in path`)
ctx.AddVariable("two.one", "0")
val, err := ctx.Query("two.one")
assert.NilError(t, err)
assert.Equal(t, "0", val)
val, err = ctx.Query("one.two.three")
assert.NilError(t, err)
assert.Equal(t, nil, val)
val, err = ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "1", val)
}
func newContext() *context {
return &context{
jp: jp,
jsonRaw: make(map[string]interface{}),
jsonRawCheckpoints: make([]map[string]interface{}, 0),
deferred: NewDeferredLoaders(),
}
}
func TestDeferredReset(t *testing.T) {
ctx := newContext()
AddMockDeferredLoader(ctx, "value", "0")
ctx.Checkpoint()
val, err := ctx.Query("value")
assert.NilError(t, err)
assert.Equal(t, "0", val)
ctx.Reset()
val, err = ctx.Query("value")
assert.NilError(t, err)
assert.Equal(t, "0", val)
}
func TestDeferredCheckpointRestore(t *testing.T) {
ctx := newContext()
ctx.Checkpoint()
unused, _ := AddMockDeferredLoader(ctx, "unused", "unused")
mock, _ := AddMockDeferredLoader(ctx, "one", "1")
ctx.Restore()
assert.Equal(t, 0, mock.invocations)
assert.Equal(t, 0, unused.invocations)
err := ctx.deferred.LoadMatching("unused", len(ctx.jsonRawCheckpoints))
assert.NilError(t, err)
_, err = ctx.Query("unused")
assert.ErrorContains(t, err, "Unknown key \"unused\" in path")
err = ctx.deferred.LoadMatching("one", len(ctx.jsonRawCheckpoints))
assert.NilError(t, err)
_, err = ctx.Query("one")
assert.ErrorContains(t, err, "Unknown key \"one\" in path")
_, _ = AddMockDeferredLoader(ctx, "one", "1")
ctx.Checkpoint()
one, err := ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "1", one)
ctx.Restore()
_, err = ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "1", one)
ctx.Restore()
_, err = ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "1", one)
mock, _ = AddMockDeferredLoader(ctx, "one", "1")
ctx.Checkpoint()
val, err := ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "1", val)
assert.Equal(t, 1, mock.invocations)
mock2, _ := AddMockDeferredLoader(ctx, "two", "2")
val, err = ctx.Query("two")
assert.NilError(t, err)
assert.Equal(t, "2", val)
assert.Equal(t, 1, mock2.invocations)
ctx.Restore()
val, err = ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "1", val)
assert.Equal(t, 2, mock.invocations)
_, _ = ctx.Query("one")
assert.Equal(t, 2, mock.invocations)
_, err = ctx.Query("two")
assert.ErrorContains(t, err, `Unknown key "two" in path`)
ctx.Checkpoint()
val, err = ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "1", val)
assert.Equal(t, 2, mock.invocations)
_, err = ctx.Query("two")
assert.ErrorContains(t, err, `Unknown key "two" in path`)
mock3, _ := AddMockDeferredLoader(ctx, "three", "3")
val, err = ctx.Query("three")
assert.NilError(t, err)
assert.Equal(t, "3", val)
assert.Equal(t, 1, mock3.invocations)
ctx.Reset()
val, err = ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "1", val)
assert.Equal(t, 2, mock.invocations)
_, err = ctx.Query("two")
assert.ErrorContains(t, err, `Unknown key "two" in path`)
_, err = ctx.Query("three")
assert.ErrorContains(t, err, `Unknown key "three" in path`)
}
func TestDeferredForloop(t *testing.T) {
ctx := newContext()
AddMockDeferredLoader(ctx, "value", float64(-1))
ctx.Checkpoint()
for i := 0; i < 5; i++ {
val, err := ctx.Query("value")
assert.NilError(t, err)
assert.Equal(t, float64(i-1), val)
ctx.Reset()
mock, _ := AddMockDeferredLoader(ctx, "value", float64(i))
val, err = ctx.Query("value")
assert.NilError(t, err)
assert.Equal(t, float64(i), val)
assert.Equal(t, 1, mock.invocations)
}
ctx.Restore()
val, err := ctx.Query("value")
assert.NilError(t, err)
assert.Equal(t, float64(-1), val)
}
func TestDeferredInvalidReset(t *testing.T) {
ctx := newContext()
AddMockDeferredLoader(ctx, "value", "0")
ctx.Reset() // no checkpoint
val, err := ctx.Query("value")
assert.NilError(t, err)
assert.Equal(t, "0", val)
AddMockDeferredLoader(ctx, "value", "0")
ctx.Restore() // no checkpoint
val, err = ctx.Query("value")
assert.NilError(t, err)
assert.Equal(t, "0", val)
}
func TestDeferredValidResetRestore(t *testing.T) {
ctx := newContext()
AddMockDeferredLoader(ctx, "value", "0")
ctx.Checkpoint()
AddMockDeferredLoader(ctx, "leak", "leak")
ctx.Reset()
_, err := ctx.Query("leak")
assert.ErrorContains(t, err, `Unknown key "leak" in path`)
AddMockDeferredLoader(ctx, "value", "0")
ctx.Checkpoint()
AddMockDeferredLoader(ctx, "leak", "leak")
ctx.Restore()
_, err = ctx.Query("leak")
assert.ErrorContains(t, err, `Unknown key "leak" in path`)
}
func TestDeferredSameName(t *testing.T) {
ctx := newContext()
var sequence []string
hdlr := func(name string) {
sequence = append(sequence, name)
}
mock1, _ := AddMockDeferredLoader(ctx, "value", "0")
mock1.setEventHandler(hdlr)
mock2, _ := AddMockDeferredLoader(ctx, "value", "1")
mock2.setEventHandler(hdlr)
val, err := ctx.Query("value")
assert.NilError(t, err)
assert.Equal(t, "1", val)
assert.Equal(t, 1, mock1.invocations)
assert.Equal(t, 1, mock2.invocations)
assert.Equal(t, 2, len(sequence))
assert.Equal(t, sequence[0], "value=0")
assert.Equal(t, sequence[1], "value=1")
}
func TestDeferredRecursive(t *testing.T) {
ctx := newContext()
addDeferredWithQuery(ctx, "value", "0", "value")
ctx.Checkpoint()
val, err := ctx.Query("value")
assert.NilError(t, err)
assert.Equal(t, "0", val)
}
func TestJMESPathDependency(t *testing.T) {
ctx := newContext()
AddMockDeferredLoader(ctx, "foo", "foo")
addDeferredWithQuery(ctx, "one", "1", "foo")
val, err := ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "foo", val)
}
func TestDeferredHiddenEval(t *testing.T) {
ctx := newContext()
AddMockDeferredLoader(ctx, "foo", "foo")
ctx.Checkpoint()
AddMockDeferredLoader(ctx, "foo", "bar")
val, err := ctx.Query("foo")
assert.NilError(t, err)
assert.Equal(t, "bar", val)
}
func TestDeferredNotHidden(t *testing.T) {
ctx := newContext()
AddMockDeferredLoader(ctx, "foo", "foo")
addDeferredWithQuery(ctx, "one", "1", "foo")
ctx.Checkpoint()
AddMockDeferredLoader(ctx, "foo", "bar")
val, err := ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "foo", val)
}
func TestDeferredNotHiddenOrdered(t *testing.T) {
ctx := newContext()
AddMockDeferredLoader(ctx, "foo", "foo")
addDeferredWithQuery(ctx, "one", "1", "foo")
AddMockDeferredLoader(ctx, "foo", "baz")
ctx.Checkpoint()
AddMockDeferredLoader(ctx, "foo", "bar")
val, err := ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "foo", val)
val, err = ctx.Query("foo")
assert.NilError(t, err)
assert.Equal(t, "bar", val)
ctx.Restore()
val, err = ctx.Query("one")
assert.NilError(t, err)
assert.Equal(t, "foo", val)
val, err = ctx.Query("foo")
assert.NilError(t, err)
assert.Equal(t, "baz", val)
}
type invalidLoader struct {
name string
level int
value interface{}
query string
hasLoaded bool
ctx *context
}
func (il *invalidLoader) Name() string {
return il.name
}
func (il *invalidLoader) SetLevel(lvl int) {
il.level = lvl
}
func (il *invalidLoader) GetLevel() int {
return il.level
}
func (il *invalidLoader) HasLoaded() bool {
return il.hasLoaded
}
func (il *invalidLoader) LoadData() error {
return errors.New("failed to load data")
}
func addInvalidDeferredLoader(ctx *context, name string, value interface{}, query string) (*invalidLoader, error) {
loader := &invalidLoader{
name: name,
value: value,
ctx: ctx,
query: query,
}
d, err := NewDeferredLoader(name, loader, logger)
if err != nil {
return loader, err
}
ctx.AddDeferredLoader(d)
return loader, nil
}
func TestDeferredLoadMatchingError(t *testing.T) {
ctx := newContext()
addInvalidDeferredLoader(ctx, "value", "0", "value")
err := ctx.deferred.LoadMatching("value", len(ctx.jsonRawCheckpoints))
assert.ErrorContains(t, err, `failed to load data`)
}