1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

moved variable substitution to higher level to avoid unhandled cases ()

Signed-off-by: Max Goncharenko <kacejot@fex.net>
This commit is contained in:
Max Goncharenko 2021-04-13 21:44:43 +03:00 committed by GitHub
parent 67973c2776
commit 8050c4e77b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 139 additions and 62 deletions

View file

@ -6,7 +6,6 @@ import (
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/engine/variables"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
@ -56,21 +55,7 @@ func newpatchStrategicMergeHandler(ruleName string, mutate *kyverno.Mutation, pa
}
func (h patchStrategicMergeHandler) Handle() (response.RuleResponse, unstructured.Unstructured) {
var ruleResponse response.RuleResponse
PatchStrategicMerge := h.mutation.PatchStrategicMerge
log := h.logger
// substitute the variables
var err error
if PatchStrategicMerge, err = variables.SubstituteAll(log, h.evalCtx, PatchStrategicMerge); err != nil {
// variable subsitution failed
ruleResponse.Name = h.ruleName
ruleResponse.Success = false
ruleResponse.Message = err.Error()
return ruleResponse, h.patchedResource
}
return ProcessStrategicMergePatch(h.ruleName, PatchStrategicMerge, h.patchedResource, log)
return ProcessStrategicMergePatch(h.ruleName, h.mutation.PatchStrategicMerge, h.patchedResource, h.logger)
}
// overlayHandler
@ -126,19 +111,7 @@ func (h patchesJSON6902Handler) Handle() (resp response.RuleResponse, patchedRes
}
func (h overlayHandler) Handle() (response.RuleResponse, unstructured.Unstructured) {
var ruleResponse response.RuleResponse
overlay := h.mutation.Overlay
// substitute the variables
var err error
if overlay, err = variables.SubstituteAll(h.logger, h.evalCtx, overlay); err != nil {
// variable substitution failed
ruleResponse.Success = false
ruleResponse.Message = err.Error()
return ruleResponse, h.patchedResource
}
return ProcessOverlay(h.logger, h.ruleName, overlay, h.patchedResource)
return ProcessOverlay(h.logger, h.ruleName, h.mutation.Overlay, h.patchedResource)
}
// patchesHandler

View file

@ -1,12 +1,14 @@
package engine
import (
"fmt"
"time"
"github.com/go-logr/logr"
kyverno "github.com/kyverno/kyverno/pkg/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/engine/mutate"
"github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/engine/variables"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/log"
@ -88,6 +90,19 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
continue
}
if rule, err = variables.SubstituteAllInRule(logger, policyContext.JSONContext, rule); err != nil {
ruleResp := response.RuleResponse{
Name: rule.Name,
Type: utils.Validation.String(),
Message: fmt.Sprintf("variable substitution failed for rule %s: %s", rule.Name, err.Error()),
Success: false,
}
incrementAppliedCount(resp)
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResp)
continue
}
mutation := rule.Mutation.DeepCopy()
mutateHandler := mutate.CreateMutateHandler(rule.Name, mutation, patchedResource, ctx, logger)
ruleResponse, patchedResource = mutateHandler.Handle()

View file

@ -160,7 +160,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) {
JSONContext: ctx,
NewResource: *resourceUnstructured}
er := Mutate(policyContext)
expectedErrorStr := "variable request.object.metadata.name1 not resolved at path /spec/name"
expectedErrorStr := "variable substitution failed for rule test-path-not-exist: variable request.object.metadata.name1 not resolved at path /mutate/overlay/spec/name"
t.Log(er.PolicyResponse.Rules[0].Message)
assert.Equal(t, er.PolicyResponse.Rules[0].Message, expectedErrorStr)
}

View file

@ -60,16 +60,6 @@ func buildResponse(logger logr.Logger, ctx *PolicyContext, resp *response.Engine
resp.PatchedResource = resource
}
for i := range resp.PolicyResponse.Rules {
messageInterface, err := variables.SubstituteAll(logger, ctx.JSONContext, resp.PolicyResponse.Rules[i].Message)
if err != nil {
logger.V(4).Info("failed to substitute variables", "error", err.Error())
continue
}
resp.PolicyResponse.Rules[i].Message, _ = messageInterface.(string)
}
resp.PolicyResponse.Policy = ctx.Policy.Name
resp.PolicyResponse.Resource.Name = resp.PatchedResource.GetName()
resp.PolicyResponse.Resource.Namespace = resp.PatchedResource.GetNamespace()
@ -94,6 +84,8 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
defer ctx.JSONContext.Restore()
for _, rule := range ctx.Policy.Spec.Rules {
var err error
if !rule.HasValidate() {
continue
}
@ -124,6 +116,20 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
continue
}
if rule, err = variables.SubstituteAllInRule(log, ctx.JSONContext, rule); err != nil {
ruleResp := response.RuleResponse{
Name: rule.Name,
Type: utils.Validation.String(),
Message: fmt.Sprintf("variable substitution failed for rule %s: %s", rule.Name, err.Error()),
Success: false,
}
incrementAppliedCount(resp)
resp.PolicyResponse.Rules = append(resp.PolicyResponse.Rules, ruleResp)
continue
}
if rule.Validation.Pattern != nil || rule.Validation.AnyPattern != nil {
ruleResponse := validateResourceWithRule(log, ctx, rule)
if ruleResponse != nil {
@ -225,13 +231,6 @@ func validatePatterns(log logr.Logger, ctx context.EvalInterface, resource unstr
logger.V(4).Info("finished processing rule", "processingTime", resp.RuleStats.ProcessingTime.String())
}()
var err error
if rule, err = variables.SubstituteAllInRule(logger, ctx, rule); err != nil {
resp.Success = false
resp.Message = fmt.Sprintf("variable substitution failed for rule %s: %s", rule.Name, err.Error())
return resp
}
validationRule := rule.Validation.DeepCopy()
if validationRule.Pattern != nil {
pattern := validationRule.Pattern

View file

@ -1599,6 +1599,104 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter
"validation error: Rule test-path-not-exist[0] failed at path /spec/template/spec/containers/0/name/. Rule test-path-not-exist[1] failed at path /spec/template/spec/containers/0/name/.")
}
func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing.T) {
resourceRaw := []byte(`{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "busybox",
"labels": {
"app": "busybox",
"color": "red",
"animal": "cow",
"food": "pizza",
"car": "jeep",
"env": "qa"
}
},
"spec": {
"replicas": 1,
"selector": {
"matchLabels": {
"app": "busybox"
}
},
"template": {
"metadata": {
"labels": {
"app": "busybox"
}
},
"spec": {
"containers": [
{
"image": "busybox:1.28",
"name": "busybox",
"command": [
"sleep",
"9999"
]
}
]
}
}
}
}`)
policyraw := []byte(`{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "cm-array-example"
},
"spec": {
"validationFailureAction": "enforce",
"background": false,
"rules": [
{
"name": "validate-role-annotation",
"match": {
"resources": {
"kinds": [
"Deployment"
]
}
},
"validate": {
"message": "The animal {{ request.object.metadata.labels.animal }} is not in the allowed list of animals.",
"deny": {
"conditions": [
{
"key": "{{ request.object.metadata.labels.animal }}",
"operator": "NotIn",
"value": "abcde"
}
]
}
}
}
]
}
}`)
var policy kyverno.ClusterPolicy
assert.NilError(t, json.Unmarshal(policyraw, &policy))
resourceUnstructured, err := utils.ConvertToUnstructured(resourceRaw)
assert.NilError(t, err)
ctx := context.NewContext()
err = ctx.AddResource(resourceRaw)
assert.NilError(t, err)
policyContext := &PolicyContext{
Policy: policy,
JSONContext: ctx,
NewResource: *resourceUnstructured}
er := Validate(policyContext)
assert.Assert(t, er.PolicyResponse.Rules[0].Success)
assert.Equal(t, er.PolicyResponse.Rules[0].Message, "The animal cow is not in the allowed list of animals.")
}
type testCase struct {
description string
policy []byte

View file

@ -199,6 +199,7 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext *engine.
ruleNameToProcessingTime := make(map[string]time.Duration)
for _, rule := range policy.Spec.Rules {
var err error
if !rule.HasGenerate() {
continue
}
@ -221,7 +222,12 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext *engine.
// add configmap json data to context
if err := engine.LoadContext(log, rule.Context, resCache, policyContext); err != nil {
log.Info("cannot add configmaps to context", "reason", err.Error())
log.Error(err, "cannot add configmaps to context")
return nil, err
}
if rule, err = variables.SubstituteAllInRule(log, policyContext.JSONContext, rule); err != nil {
log.Error(err, "variable substitution failed for rule %s", rule.Name)
return nil, err
}
@ -316,16 +322,6 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
return noGenResource, err
}
// Variable substitutions
// format : {{<variable_name}}
// - if there is variables that are not defined the context -> results in error and rule is not applied
// - valid variables are replaced with the values
object, err := variables.SubstituteAll(log, ctx, genUnst.Object)
if err != nil {
return noGenResource, err
}
genUnst.Object, _ = object.(map[string]interface{})
genKind, genName, genNamespace, genAPIVersion, err := getResourceInfo(genUnst.Object)
if err != nil {
return noGenResource, err

View file

@ -44,10 +44,6 @@ func ContainsVariablesOtherThanObject(policy kyverno.ClusterPolicy) error {
return fmt.Errorf("variable substitution failed for rule %s: %s", rule.Name, err.Error())
}
if rule, err = variables.SubstituteAllInRule(log.Log, ctx, rule); !checkNotFoundErr(err) {
return fmt.Errorf("variable substitution failed for rule %s: %s", rule.Name, err.Error())
}
if rule.AnyAllConditions != nil {
if err = validatePreConditions(idx, ctx, rule.AnyAllConditions); !checkNotFoundErr(err) {
return err