Skip to content

Commit

Permalink
Merge pull request #1636 from realshuting/1621_fix_configmap_variables
Browse files Browse the repository at this point in the history
Substitute variables in context.configMap
  • Loading branch information
JimBugwadia committed Feb 26, 2021
2 parents b0ac8c5 + edc89c7 commit 0d1f0b5
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 49 deletions.
24 changes: 24 additions & 0 deletions pkg/engine/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ type Interface interface {
// AddServiceAccount merges ServiceAccount types
AddServiceAccount(userName string) error

// AddNamespace merges resource json under request.namespace
AddNamespace(namespace string) error

EvalInterface
}

Expand Down Expand Up @@ -190,6 +193,27 @@ func (ctx *Context) AddServiceAccount(userName string) error {
return nil
}

// AddNamespace merges resource json under request.namespace
func (ctx *Context) AddNamespace(namespace string) error {
modifiedResource := struct {
Request interface{} `json:"request"`
}{
Request: struct {
Namespace string `json:"namespace"`
}{
Namespace: namespace,
},
}

objRaw, err := json.Marshal(modifiedResource)
if err != nil {
ctx.log.Error(err, "failed to marshal the resource")
return err
}

return ctx.AddJSON(objRaw)
}

// Checkpoint creates a copy of the internal state.
// Prior checkpoints will be overridden.
func (ctx *Context) Checkpoint() {
Expand Down
27 changes: 18 additions & 9 deletions pkg/engine/jsonContext.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func LoadContext(logger logr.Logger, contextEntries []kyverno.ContextEntry, resC

for _, entry := range contextEntries {
if entry.ConfigMap != nil {
if err := loadConfigMap(entry, lister, ctx.JSONContext); err != nil {
if err := loadConfigMap(logger, entry, lister, ctx.JSONContext); err != nil {
return err
}
} else if entry.APICall != nil {
Expand Down Expand Up @@ -90,7 +90,7 @@ func applyJMESPath(jmesPath string, jsonData []byte) (interface{}, error) {
var data interface{}
err = json.Unmarshal(jsonData, &data)
if err != nil {
return nil, fmt.Errorf("failed to unmarshall JSON: %s, error: %v", string(jsonData), err)
return nil, fmt.Errorf("failed to unmarshal JSON: %s, error: %v", string(jsonData), err)
}

return jp.Search(data)
Expand Down Expand Up @@ -151,24 +151,33 @@ func loadResource(ctx *PolicyContext, p *APIPath) ([]byte, error) {
return r.MarshalJSON()
}

func loadConfigMap(entry kyverno.ContextEntry, lister dynamiclister.Lister, ctx *context.Context) error {
data, err := fetchConfigMap(entry, lister)
func loadConfigMap(logger logr.Logger, entry kyverno.ContextEntry, lister dynamiclister.Lister, ctx *context.Context) error {
data, err := fetchConfigMap(logger, entry, lister, ctx)
if err != nil {
return fmt.Errorf("failed to retrieve config map for context entry %v: %v", entry, err)
return fmt.Errorf("failed to retrieve config map for context entry %s: %v", entry.Name, err)
}

err = ctx.AddJSON(data)
if err != nil {
return fmt.Errorf("failed to add config map for context entry %v: %v", entry, err)
return fmt.Errorf("failed to add config map for context entry %s: %v", entry.Name, err)
}

return nil
}

func fetchConfigMap(entry kyverno.ContextEntry, lister dynamiclister.Lister) ([]byte, error) {
func fetchConfigMap(logger logr.Logger, entry kyverno.ContextEntry, lister dynamiclister.Lister, jsonContext *context.Context) ([]byte, error) {
contextData := make(map[string]interface{})
name := entry.ConfigMap.Name
namespace := entry.ConfigMap.Namespace

name, err := variables.SubstituteVars(logger, jsonContext, entry.ConfigMap.Name)
if err != nil {
return nil, fmt.Errorf("failed to substitute variables in context %s configMap.name %s: %v", entry.Name, entry.ConfigMap.Name, err)
}

namespace, err := variables.SubstituteVars(logger, jsonContext, entry.ConfigMap.Namespace)
if err != nil {
return nil, fmt.Errorf("failed to substitute variables in context %s configMap.namespace %s: %v", entry.Name, entry.ConfigMap.Namespace, err)
}

if namespace == "" {
namespace = "default"
}
Expand Down
7 changes: 6 additions & 1 deletion pkg/policy/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
ctx := context.NewContext()
err = ctx.AddResource(transformResource(resource))
if err != nil {
logger.Error(err, "enable to add transform resource to ctx")
logger.Error(err, "failed to add transform resource to ctx")
}

err = ctx.AddNamespace(resource.GetNamespace())
if err != nil {
logger.Error(err, "failed to add namespace to ctx")
}

engineResponseMutation, err = mutation(policy, resource, logger, resCache, ctx, namespaceLabels)
Expand Down
22 changes: 11 additions & 11 deletions pkg/policy/background.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func ContainsVariablesOtherThanObject(policy kyverno.ClusterPolicy) error {
return fmt.Errorf("invalid variable used at path: spec/rules[%d]/exclude/%s", idx, path)
}

filterVars := []string{"request.object"}
filterVars := []string{"request.object", "request.namespace"}
ctx := context.NewContext(filterVars...)

for contextIdx, contextEntry := range rule.Context {
Expand All @@ -41,56 +41,56 @@ func ContainsVariablesOtherThanObject(policy kyverno.ClusterPolicy) error {
ctx.AddBuiltInVars(contextEntry.Name)

if _, err = variables.SubstituteVars(log.Log, ctx, contextEntry.ConfigMap.Name); !checkNotFoundErr(err) {
return fmt.Errorf("invalid variable used at spec/rules[%d]/context[%d]/configMap/name", idx, contextIdx)
return fmt.Errorf("invalid variable used at spec/rules[%d]/context[%d]/configMap/name: %s", idx, contextIdx, err.Error())
}

if _, err = variables.SubstituteVars(log.Log, ctx, contextEntry.ConfigMap.Namespace); !checkNotFoundErr(err) {
return fmt.Errorf("invalid variable used at spec/rules[%d]/context[%d]/configMap/namespace", idx, contextIdx)
return fmt.Errorf("invalid variable used at spec/rules[%d]/context[%d]/configMap/namespace: %s", idx, contextIdx, err.Error())
}
}
}

for condIdx, condition := range rule.Conditions {
if condition.Key, err = variables.SubstituteVars(log.Log, ctx, condition.Key); !checkNotFoundErr(err) {
return fmt.Errorf("invalid variable %v used at spec/rules[%d]/condition[%d]/key", condition.Key, idx, condIdx)
return fmt.Errorf("invalid variable %v used at spec/rules[%d]/condition[%d]/key: %s", condition.Key, idx, condIdx, err.Error())
}

if condition.Value, err = variables.SubstituteVars(log.Log, ctx, condition.Value); !checkNotFoundErr(err) {
return fmt.Errorf("invalid %v variable used at spec/rules[%d]/condition[%d]/value: %v", condition.Value, idx, condIdx, err)
return fmt.Errorf("invalid %v variable used at spec/rules[%d]/condition[%d]/value: %s", condition.Value, idx, condIdx, err.Error())
}
}

if rule.Mutation.Overlay != nil {
if rule.Mutation.Overlay, err = variables.SubstituteVars(log.Log, ctx, rule.Mutation.Overlay); !checkNotFoundErr(err) {
return fmt.Errorf("invalid variable used at spec/rules[%d]/mutate/overlay", idx)
return fmt.Errorf("invalid variable used at spec/rules[%d]/mutate/overlay: %s", idx, err.Error())
}
}

if rule.Mutation.PatchStrategicMerge != nil {
if rule.Mutation.Overlay, err = variables.SubstituteVars(log.Log, ctx, rule.Mutation.PatchStrategicMerge); !checkNotFoundErr(err) {
return fmt.Errorf("invalid variable used at spec/rules[%d]/mutate/patchStrategicMerge", idx)
return fmt.Errorf("invalid variable used at spec/rules[%d]/mutate/patchStrategicMerge: %s", idx, err.Error())
}
}

if rule.Validation.Pattern != nil {
if rule.Validation.Pattern, err = variables.SubstituteVars(log.Log, ctx, rule.Validation.Pattern); !checkNotFoundErr(err) {
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/pattern: %v", idx, err)
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/pattern: %s", idx, err.Error())
}
}

anyPattern, err := rule.Validation.DeserializeAnyPattern()
if err != nil {
return fmt.Errorf("failed to deserialize anyPattern, expect array: %v", err)
return fmt.Errorf("failed to deserialize anyPattern, expect array: %s", err.Error())
}

for idx2, pattern := range anyPattern {
if anyPattern[idx2], err = variables.SubstituteVars(log.Log, ctx, pattern); !checkNotFoundErr(err) {
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/anyPattern[%d]", idx, idx2)
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/anyPattern[%d]: %s", idx, idx2, err.Error())
}
}

if _, err = variables.SubstituteVars(log.Log, ctx, rule.Validation.Message); !checkNotFoundErr(err) {
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/message", idx)
return fmt.Errorf("invalid variable used at spec/rules[%d]/validate/message: %s", idx, err.Error())
}

if rule.Validation.Deny != nil {
Expand Down
33 changes: 6 additions & 27 deletions pkg/policy/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import (
"encoding/json"
"testing"

"github.com/kyverno/kyverno/pkg/openapi"

kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/openapi"
"gotest.tools/assert"
)

Expand Down Expand Up @@ -913,11 +912,7 @@ func Test_BackGroundUserInfo_mutate_overlay1(t *testing.T) {
assert.NilError(t, err)

err = ContainsVariablesOtherThanObject(*policy)

if err.Error() != "invalid variable used at spec/rules[0]/mutate/overlay" {
t.Log(err)
t.Error("Incorrect Path")
}
assert.Assert(t, err != nil)
}

func Test_BackGroundUserInfo_mutate_overlay2(t *testing.T) {
Expand Down Expand Up @@ -948,11 +943,7 @@ func Test_BackGroundUserInfo_mutate_overlay2(t *testing.T) {
assert.NilError(t, err)

err = ContainsVariablesOtherThanObject(*policy)

if err.Error() != "invalid variable used at spec/rules[0]/mutate/overlay" {
t.Log(err)
t.Error("Incorrect Path")
}
assert.Assert(t, err != nil)
}

func Test_BackGroundUserInfo_validate_pattern(t *testing.T) {
Expand Down Expand Up @@ -1018,11 +1009,7 @@ func Test_BackGroundUserInfo_validate_anyPattern(t *testing.T) {
assert.NilError(t, err)

err = ContainsVariablesOtherThanObject(*policy)

if err.Error() != "invalid variable used at spec/rules[0]/validate/anyPattern[1]" {
t.Log(err)
t.Error("Incorrect Path")
}
assert.Assert(t, err != nil)
}

func Test_BackGroundUserInfo_validate_anyPattern_multiple_var(t *testing.T) {
Expand Down Expand Up @@ -1057,11 +1044,7 @@ func Test_BackGroundUserInfo_validate_anyPattern_multiple_var(t *testing.T) {
assert.NilError(t, err)

err = ContainsVariablesOtherThanObject(*policy)

if err.Error() != "invalid variable used at spec/rules[0]/validate/anyPattern[1]" {
t.Log(err)
t.Error("Incorrect Path")
}
assert.Assert(t, err != nil)
}

func Test_BackGroundUserInfo_validate_anyPattern_serviceAccount(t *testing.T) {
Expand Down Expand Up @@ -1096,11 +1079,7 @@ func Test_BackGroundUserInfo_validate_anyPattern_serviceAccount(t *testing.T) {
assert.NilError(t, err)

err = ContainsVariablesOtherThanObject(*policy)

if err.Error() != "invalid variable used at spec/rules[0]/validate/anyPattern[1]" {
t.Log(err)
t.Error("Incorrect Path")
}
assert.Assert(t, err != nil)
}

func Test_ruleOnlyDealsWithResourceMetaData(t *testing.T) {
Expand Down
8 changes: 7 additions & 1 deletion pkg/utils/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package utils
import (
"fmt"
"regexp"
"sigs.k8s.io/controller-runtime/pkg/log"
"strconv"

"sigs.k8s.io/controller-runtime/pkg/log"

"github.com/go-logr/logr"
client "github.com/kyverno/kyverno/pkg/dclient"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
Expand Down Expand Up @@ -122,6 +123,11 @@ func ConvertResource(raw []byte, group, version, kind, namespace string) (unstru
}

obj.SetGroupVersionKind(schema.GroupVersionKind{Group: group, Version: version, Kind: kind})

if namespace != "" {
obj.SetNamespace(namespace)
}

return *obj, nil
}

Expand Down

0 comments on commit 0d1f0b5

Please sign in to comment.