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

fix deny check and fmt

Signed-off-by: Jim Bugwadia <jim@nirmata.com>
This commit is contained in:
Jim Bugwadia 2021-09-27 23:40:05 -07:00
parent a905a61581
commit 3957a1400e
18 changed files with 128 additions and 112 deletions

View file

@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View file

@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View file

@ -37,4 +37,4 @@ func ToMap(data interface{}) (map[string]interface{}, error) {
}
return mapData, nil
}
}

View file

@ -53,21 +53,21 @@ type EvalInterface interface {
//Context stores the data resources as JSON
type Context struct {
mutex sync.RWMutex
jsonRaw []byte
mutex sync.RWMutex
jsonRaw []byte
jsonRawCheckpoints [][]byte
builtInVars []string
images *Images
log logr.Logger
builtInVars []string
images *Images
log logr.Logger
}
//NewContext returns a new context
// builtInVars is the list of known variables (e.g. serviceAccountName)
func NewContext(builtInVars ...string) *Context {
ctx := Context{
jsonRaw: []byte(`{}`), // empty json struct
builtInVars: builtInVars,
log: log.Log.WithName("context"),
jsonRaw: []byte(`{}`), // empty json struct
builtInVars: builtInVars,
log: log.Log.WithName("context"),
jsonRawCheckpoints: make([][]byte, 0),
}
@ -334,7 +334,7 @@ func (ctx *Context) reset(remove bool) {
ctx.mutex.Lock()
defer ctx.mutex.Unlock()
if len(ctx.jsonRawCheckpoints) == 0 {
if len(ctx.jsonRawCheckpoints) == 0 {
return
}

View file

@ -80,8 +80,8 @@ func filterRule(rule kyverno.Rule, policyContext *PolicyContext) *response.RuleR
// if the oldResource matched, return "false" to delete GR for it
if err = MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels); err == nil {
return &response.RuleResponse{
Name: rule.Name,
Type: "Generation",
Name: rule.Name,
Type: "Generation",
Status: response.RuleStatusFail,
RuleStats: response.RuleStats{
ProcessingTime: time.Since(startTime),
@ -123,8 +123,8 @@ func filterRule(rule kyverno.Rule, policyContext *PolicyContext) *response.RuleR
// build rule Response
return &response.RuleResponse{
Name: ruleCopy.Name,
Type: "Generation",
Name: ruleCopy.Name,
Type: "Generation",
Status: response.RuleStatusPass,
RuleStats: response.RuleStats{
ProcessingTime: time.Since(startTime),

View file

@ -325,7 +325,7 @@ func convertRNodeToInterface(document *yaml.RNode) (interface{}, error) {
}
func checkCondition(logger logr.Logger, pattern *yaml.RNode, resource *yaml.RNode) error {
patternInterface, err := convertRNodeToInterface(pattern);
patternInterface, err := convertRNodeToInterface(pattern)
if err != nil {
return err
}
@ -336,7 +336,7 @@ func checkCondition(logger logr.Logger, pattern *yaml.RNode, resource *yaml.RNod
}
err, _ = validate.MatchPattern(logger, resourceInterface, patternInterface)
if err != nil{
if err != nil {
return err
}

View file

@ -118,7 +118,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
Name: ruleCopy.Name,
Type: utils.Validation.String(),
Message: fmt.Sprintf("variable substitution failed: %s", err.Error()),
Status: response.RuleStatusPass,
Status: response.RuleStatusPass,
}
incrementAppliedCount(resp)

View file

@ -43,15 +43,15 @@ type PolicyContext struct {
func (pc *PolicyContext) Copy() *PolicyContext {
return &PolicyContext{
Policy: pc.Policy,
NewResource: pc.NewResource,
OldResource: pc.OldResource,
AdmissionInfo: pc.AdmissionInfo,
Client: pc.Client,
ExcludeGroupRole: pc.ExcludeGroupRole,
Policy: pc.Policy,
NewResource: pc.NewResource,
OldResource: pc.OldResource,
AdmissionInfo: pc.AdmissionInfo,
Client: pc.Client,
ExcludeGroupRole: pc.ExcludeGroupRole,
ExcludeResourceFunc: pc.ExcludeResourceFunc,
ResourceCache: pc.ResourceCache,
JSONContext: pc.JSONContext,
NamespaceLabels: pc.NamespaceLabels,
ResourceCache: pc.ResourceCache,
JSONContext: pc.JSONContext,
NamespaceLabels: pc.NamespaceLabels,
}
}
}

View file

@ -87,7 +87,7 @@ type RuleResponse struct {
Status RuleStatus `json:"status"`
// statistics
RuleStats `json:",inline"`
RuleStats `json:",inline"`
}
//ToString ...
@ -161,7 +161,7 @@ func (er EngineResponse) GetResourceSpec() ResourceSpec {
func (er EngineResponse) getRules(status RuleStatus) []string {
var rules []string
for _, r := range er.PolicyResponse.Rules {
if r.Status == status {
if r.Status == status {
rules = append(rules, r.Name)
}
}

View file

@ -27,4 +27,4 @@ func Test_parse_yaml(t *testing.T) {
}
assert.Equal(t, 1, len(pr.Rules))
assert.Equal(t, RuleStatusFail, pr.Rules[0].Status)
}
}

View file

@ -9,16 +9,21 @@ import (
// RuleStatus represents the status of rule execution
type RuleStatus int
// RuleStatusPass is used to report the result of processing a rule.
const (
// RuleStatusPass indicates that the policy rule requirements are met
// RuleStatusPass indicates that the resources meets the policy rule requirements
RuleStatusPass RuleStatus = iota
// Fail indicates that the policy rule requirements are not met
// Fail indicates that the resource does not meet the policy rule requirements
RuleStatusFail
// Warn indicates that the policy rule requirements are not met, and the policy is not scored
// Warn indicates that the the resource does not meet the policy rule requirements, but the policy is not scored
RuleStatusWarn
// Error indicates that the policy rule could not be evaluated due to a processing error
// Error indicates that the policy rule could not be evaluated due to a processing error, for
// example when a variable cannot be resolved in the policy rule definition. Note that variables
// that cannot be resolved in preconditions are replaced with empty values to allow existence
// checks.
RuleStatusError
// Skip indicates that the policy rule was not selected based on user inputs or applicability
// Skip indicates that the policy rule was not selected based on user inputs or applicability, for example
// when preconditions are not met, or when conditional or global anchors are not satistied.
RuleStatusSkip
)
@ -28,18 +33,18 @@ func (s *RuleStatus) String() string {
var toString = map[RuleStatus]string{
RuleStatusPass: "Pass",
RuleStatusFail: "Fail",
RuleStatusWarn: "Warning",
RuleStatusFail: "Fail",
RuleStatusWarn: "Warning",
RuleStatusError: "Error",
RuleStatusSkip: "Skip",
RuleStatusSkip: "Skip",
}
var toID = map[string]RuleStatus{
"Pass": RuleStatusPass,
"Fail": RuleStatusFail,
"Pass": RuleStatusPass,
"Fail": RuleStatusFail,
"Warning": RuleStatusWarn,
"Error": RuleStatusError,
"Skip": RuleStatusSkip,
"Error": RuleStatusError,
"Skip": RuleStatusSkip,
}
// MarshalJSON marshals the enum as a quoted json string
@ -66,7 +71,7 @@ func (s *RuleStatus) UnmarshalJSON(b []byte) error {
return nil
}
func getRuleStatus(s string) (*RuleStatus, error){
func getRuleStatus(s string) (*RuleStatus, error) {
for k, v := range toID {
if s == k {
return &v, nil

View file

@ -40,4 +40,3 @@ func getRawKeyIfWrappedWithAttributes(str string) string {
return str
}
}

View file

@ -49,7 +49,7 @@ func validateResourceElement(log logr.Logger, resourceElement, patternElement, o
log.V(4).Info("Pattern and resource have different structures.", "path", path, "expected", fmt.Sprintf("%T", patternElement), "current", fmt.Sprintf("%T", resourceElement))
return path, fmt.Errorf("Pattern and resource have different structures. Path: %s. Expected %T, found %T", path, patternElement, resourceElement)
}
// CheckAnchorInResource - check anchor anchor key exists in resource and update the AnchorKey fields.
// CheckAnchorInResource - check anchor key exists in resource and update the AnchorKey fields.
ac.CheckAnchorInResource(typedPatternElement, typedResourceElement)
return validateMap(log, typedResourceElement, typedPatternElement, originPattern, path, ac)
// array

View file

@ -1516,7 +1516,7 @@ func Test_global_anchor(t *testing.T) {
pattern []byte
resource []byte
nilErr bool
} {
}{
{
name: "check global anchor_skip",
pattern: []byte(`{"spec": {"containers": [{"name": "*","<(image)": "*:latest","imagePullPolicy": "!Always"}]}}`),
@ -1535,7 +1535,12 @@ func Test_global_anchor(t *testing.T) {
testMatchPattern(t, testCases[1])
}
func testMatchPattern(t *testing.T, testCase struct {name string;pattern []byte;resource []byte;nilErr bool}) {
func testMatchPattern(t *testing.T, testCase struct {
name string
pattern []byte
resource []byte
nilErr bool
}) {
var pattern, resource interface{}
err := json.Unmarshal(testCase.pattern, &pattern)
assert.NilError(t, err)
@ -1550,4 +1555,4 @@ func testMatchPattern(t *testing.T, testCase struct {name string;pattern []byte;
err != nil,
fmt.Sprintf("\ntest: %s\npattern: %s\nresource: %s\nmsg: %v", testCase.name, pattern, resource, err))
}
}
}

View file

@ -116,12 +116,11 @@ func validateResource(log logr.Logger, ctx *PolicyContext) *response.EngineRespo
}
func processValidationRule(log logr.Logger, ctx *PolicyContext, rule *kyverno.Rule) *response.RuleResponse {
v := newValidator(log, ctx, rule)
if rule.Validation.ForEachValidation != nil {
v := newValidator(log, ctx, rule)
return v.validateForEach()
}
v := newValidator(log, ctx, rule)
return v.validate()
}
@ -140,41 +139,48 @@ func addRuleResponse(log logr.Logger, resp *response.EngineResponse, ruleResp *r
}
type validator struct {
log logr.Logger
ctx *PolicyContext
rule *kyverno.Rule
contextEntries []kyverno.ContextEntry
log logr.Logger
ctx *PolicyContext
rule *kyverno.Rule
contextEntries []kyverno.ContextEntry
anyAllConditions apiextensions.JSON
pattern apiextensions.JSON
anyPattern apiextensions.JSON
deny *kyverno.Deny
pattern apiextensions.JSON
anyPattern apiextensions.JSON
deny *kyverno.Deny
}
func newValidator(log logr.Logger, ctx *PolicyContext, rule *kyverno.Rule) *validator {
ruleCopy := rule.DeepCopy()
return &validator{
log: log,
rule: ruleCopy,
ctx: ctx,
contextEntries: ruleCopy.Context,
log: log,
rule: ruleCopy,
ctx: ctx,
contextEntries: ruleCopy.Context,
anyAllConditions: ruleCopy.AnyAllConditions,
pattern: ruleCopy.Validation.Pattern,
anyPattern: ruleCopy.Validation.AnyPattern,
deny: ruleCopy.Validation.Deny,
pattern: ruleCopy.Validation.Pattern,
anyPattern: ruleCopy.Validation.AnyPattern,
deny: ruleCopy.Validation.Deny,
}
}
func newForeachValidator(log logr.Logger, ctx *PolicyContext, rule *kyverno.Rule) *validator {
ruleCopy := rule.DeepCopy()
// Variable substitution expects JSON data, so we convert to a map
anyAllConditions, err := common.ToMap(ruleCopy.Validation.ForEachValidation.AnyAllConditions)
if err != nil {
log.Error(err, "failed to convert ruleCopy.Validation.ForEachValidation.AnyAllConditions")
}
return &validator{
log: log,
ctx: ctx,
rule: ruleCopy,
contextEntries: ruleCopy.Validation.ForEachValidation.Context,
anyAllConditions: ruleCopy.Validation.ForEachValidation.AnyAllConditions,
pattern: ruleCopy.Validation.ForEachValidation.Pattern,
anyPattern: ruleCopy.Validation.ForEachValidation.AnyPattern,
deny: ruleCopy.Validation.ForEachValidation.Deny,
log: log,
ctx: ctx,
rule: ruleCopy,
contextEntries: ruleCopy.Validation.ForEachValidation.Context,
anyAllConditions: anyAllConditions,
pattern: ruleCopy.Validation.ForEachValidation.Pattern,
anyPattern: ruleCopy.Validation.ForEachValidation.AnyPattern,
deny: ruleCopy.Validation.ForEachValidation.Deny,
}
}
@ -233,6 +239,7 @@ func (v *validator) validateForEach() *response.RuleResponse {
v.ctx.JSONContext.Checkpoint()
defer v.ctx.JSONContext.Restore()
applyCount := 0
for _, e := range elements {
v.ctx.JSONContext.Reset()
@ -246,14 +253,20 @@ func (v *validator) validateForEach() *response.RuleResponse {
r := foreachValidator.validate()
if r == nil {
v.log.Info("skipping rule due to empty result")
continue
} else if r.Status == response.RuleStatusSkip {
v.log.Info("skipping rule as preconditions were not met")
continue
} else if r.Status != response.RuleStatusPass {
msg := fmt.Sprintf("validation failed in foreach rule for %v", e)
msg := fmt.Sprintf("validation failed in foreach rule for %v", r.Message)
return ruleResponse(v.rule, msg, r.Status)
}
applyCount++
}
if applyCount == 0 {
return ruleResponse(v.rule, "", response.RuleStatusSkip)
}
return ruleResponse(v.rule, "", response.RuleStatusPass)
@ -290,7 +303,6 @@ func (v *validator) evaluateList(jmesPath string) ([]interface{}, error) {
return l, nil
}
func (v *validator) loadContext() error {
if err := LoadContext(v.log, v.contextEntries, v.ctx.ResourceCache, v.ctx, v.rule.Name); err != nil {
if _, ok := err.(gojmespath.NotFoundError); ok {
@ -481,7 +493,7 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *respon
return ruleResponse(v.rule, v.rule.Validation.Message, response.RuleStatusPass)
}
func deserializeAnyPattern(anyPattern apiextensions.JSON ) ([]interface{}, error) {
func deserializeAnyPattern(anyPattern apiextensions.JSON) ([]interface{}, error) {
if anyPattern == nil {
return nil, nil
}
@ -538,7 +550,7 @@ func buildAnyPatternErrorMessage(rule *kyverno.Rule, errors []string) string {
return fmt.Sprintf("validation error: %s. %s", rule.Validation.Message, errStr)
}
func (v *validator) substitutePatterns() error {
func (v *validator) substitutePatterns() error {
if v.pattern != nil {
i, err := variables.SubstituteAll(v.log, v.ctx.JSONContext, v.pattern)
if err != nil {
@ -562,7 +574,7 @@ func (v *validator) substitutePatterns() error {
return nil
}
func (v *validator) substituteDeny() error {
func (v *validator) substituteDeny() error {
if v.deny == nil {
return nil
}
@ -586,6 +598,6 @@ func ruleResponse(rule *kyverno.Rule, msg string, status response.RuleStatus) *r
Name: rule.Name,
Type: utils.Validation.String(),
Message: msg,
Status: status,
Status: status,
}
}
}

View file

@ -1921,9 +1921,9 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing.
func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
tests := []struct {
name string
policyRaw []byte
resourceRaw []byte
name string
policyRaw []byte
resourceRaw []byte
expectedResults []response.RuleStatus
expectedMessages []string
}{
@ -1931,7 +1931,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
name: "path-not-present",
policyRaw: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"flux-multi-tenancy"},"spec":{"validationFailureAction":"enforce","rules":[{"name":"serviceAccountName","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":".spec.serviceAccountName is required","pattern":{"spec":{"serviceAccountName":"?*"}}}},{"name":"sourceRefNamespace","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":"spec.sourceRef.namespace must be the same as metadata.namespace","deny":{"conditions":[{"key":"{{request.object.spec.sourceRef.namespace}}","operator":"NotEquals","value":"{{request.object.metadata.namespace}}"}]}}}]}}`),
// referred variable path not present
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team"},"prune":true,"validation":"client"}}`),
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team"},"prune":true,"validation":"client"}}`),
expectedResults: []response.RuleStatus{response.RuleStatusPass, response.RuleStatusError},
expectedMessages: []string{"validation rule 'serviceAccountName' passed.", "failed to substitute variables in deny conditions: Unknown key \"namespace\" in path"},
},
@ -1939,7 +1939,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
name: "resource-with-violation",
policyRaw: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"flux-multi-tenancy"},"spec":{"validationFailureAction":"enforce","rules":[{"name":"serviceAccountName","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":".spec.serviceAccountName is required","pattern":{"spec":{"serviceAccountName":"?*"}}}},{"name":"sourceRefNamespace","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":"spec.sourceRef.namespace {{request.object.spec.sourceRef.namespace}} must be the same as metadata.namespace {{request.object.metadata.namespace}}","deny":{"conditions":[{"key":"{{request.object.spec.sourceRef.namespace}}","operator":"NotEquals","value":"{{request.object.metadata.namespace}}"}]}}}]}}`),
// referred variable path present with different value
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team","namespace":"default"},"prune":true,"validation":"client"}}`),
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team","namespace":"default"},"prune":true,"validation":"client"}}`),
expectedResults: []response.RuleStatus{response.RuleStatusPass, response.RuleStatusFail},
expectedMessages: []string{"validation rule 'serviceAccountName' passed.", "spec.sourceRef.namespace default must be the same as metadata.namespace apps"},
},
@ -1947,7 +1947,7 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) {
name: "resource-comply",
policyRaw: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"flux-multi-tenancy"},"spec":{"validationFailureAction":"enforce","rules":[{"name":"serviceAccountName","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":".spec.serviceAccountName is required","pattern":{"spec":{"serviceAccountName":"?*"}}}},{"name":"sourceRefNamespace","exclude":{"resources":{"namespaces":["flux-system"]}},"match":{"resources":{"kinds":["Kustomization","HelmRelease"]}},"validate":{"message":"spec.sourceRef.namespace must be the same as metadata.namespace","deny":{"conditions":[{"key":"{{request.object.spec.sourceRef.namespace}}","operator":"NotEquals","value":"{{request.object.metadata.namespace}}"}]}}}]}}`),
// referred variable path present with same value - validate passes
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team","namespace":"apps"},"prune":true,"validation":"client"}}`),
resourceRaw: []byte(`{"apiVersion":"kustomize.toolkit.fluxcd.io/v1beta1","kind":"Kustomization","metadata":{"name":"dev-team","namespace":"apps"},"spec":{"serviceAccountName":"dev-team","interval":"5m","sourceRef":{"kind":"GitRepository","name":"dev-team","namespace":"apps"},"prune":true,"validation":"client"}}`),
expectedResults: []response.RuleStatus{response.RuleStatusPass, response.RuleStatusPass},
expectedMessages: []string{"validation rule 'serviceAccountName' passed.", "validation rule 'sourceRefNamespace' passed."},
},
@ -2603,10 +2603,8 @@ func Test_foreach_context_preconditions(t *testing.T) {
"metadata": {"name": "test"},
"spec": { "template": { "spec": {
"containers": [
{"name": "pod1-valid", "image": "nginx/nginx:v1"},
{"name": "pod2-valid", "image": "nginx/nginx:v2"},
{"name": "pod3-valid", "image": "nginx/nginx:v3"},
{"name": "pod4-valid", "image": "nginx/nginx:v4"}
{"name": "podvalid", "image": "nginx/nginx:v1"},
{"name": "podinvalid", "image": "nginx/nginx:v2"}
]
}}}}`)
@ -2622,17 +2620,17 @@ func Test_foreach_context_preconditions(t *testing.T) {
"validate": {
"foreach": {
"list": "request.object.spec.template.spec.containers",
"context": [{"name": "tags", "configMap": {"name": "mycmap", "namespace": "default"}}],
"context": [{"name": "img", "configMap": {"name": "mycmap", "namespace": "default"}}],
"preconditions": { "all": [
{
"key": "{{request.object.name}}",
"operator": "Equals",
"value": "pod1-valid | pod2-valid | pod3-valid"
"operator": "In",
"value": ["podvalid"]
}
]},
"deny": {
"conditions": [
{"key": "images.{{ request.object.name }}.tag", "operator": "NotEquals", "value": "{{ tags.data.{{ request.object.name }} }}"}
{"key": "{{ request.object.image }}", "operator": "NotEquals", "value": "{{ img.data.{{ request.object.name }} }}"}
]
}
}
@ -2646,9 +2644,8 @@ func Test_foreach_context_preconditions(t *testing.T) {
{
Name: "test",
Values: map[string]string{
"tags.data.pod1-valid": "v1",
"tags.data.pod2-valid": "v2",
"tags.data.pod3-valid": "v3",
"img.data.podvalid": "nginx/nginx:v1",
"img.data.podinvalid": "nginx/nginx:v2",
},
},
},
@ -2670,10 +2667,8 @@ func Test_foreach_context_preconditions_fail(t *testing.T) {
"metadata": {"name": "test"},
"spec": { "template": { "spec": {
"containers": [
{"name": "pod1-valid", "image": "nginx/nginx:v1"},
{"name": "pod2-valid", "image": "nginx/nginx:v2"},
{"name": "pod3-valid", "image": "nginx/nginx:v3"},
{"name": "pod4-valid", "image": "nginx/nginx:v4"}
{"name": "podvalid", "image": "nginx/nginx:v1"},
{"name": "podinvalid", "image": "nginx/nginx:v2"}
]
}}}}`)
@ -2689,17 +2684,17 @@ func Test_foreach_context_preconditions_fail(t *testing.T) {
"validate": {
"foreach": {
"list": "request.object.spec.template.spec.containers",
"context": [{"name": "tags", "configMap": {"name": "mycmap", "namespace": "default"}}],
"context": [{"name": "img", "configMap": {"name": "mycmap", "namespace": "default"}}],
"preconditions": { "all": [
{
"key": "{{request.object.name}}",
"operator": "Equals",
"value": "pod1-valid | pod2-valid | pod3-valid"
"operator": "In",
"value": ["podvalid", "podinvalid"]
}
]},
"deny": {
"conditions": [
{"key": "images.{{ request.object.name }}.tag", "operator": "NotEquals", "value": "{{ tags.data.{{ request.object.name }} }}"}
{"key": "{{ request.object.image }}", "operator": "NotEquals", "value": "{{ img.data.{{ request.object.name }} }}"}
]
}
}
@ -2713,9 +2708,8 @@ func Test_foreach_context_preconditions_fail(t *testing.T) {
{
Name: "test",
Values: map[string]string{
"tags.data.pod1-valid": "v1",
"tags.data.pod2-valid": "v22",
"tags.data.pod3-valid": "v3",
"img.data.podvalid": "nginx/nginx:v1",
"img.data.podinvalid": "nginx/nginx:v1",
},
},
},

View file

@ -52,6 +52,8 @@ func newPreconditionsVariableResolver(log logr.Logger) VariableResolver {
}
}
// SubstituteAll substitutes variables and references in the document. The document must be JSON data
// i.e. string, []interface{}, map[string]interface{}
func SubstituteAll(log logr.Logger, ctx context.EvalInterface, document interface{}) (_ interface{}, err error) {
return substituteAll(log, ctx, document, DefaultVariableResolver)
}
@ -139,8 +141,6 @@ func SubstituteAllForceMutate(log logr.Logger, ctx context.EvalInterface, typedR
return UntypedToRule(rule)
}
//SubstituteVars replaces the variables with the values defined in the context
// - if any variable is invalid or has nil value, it is considered as a failed variable substitution
func substituteVars(log logr.Logger, ctx context.EvalInterface, rule interface{}, vr VariableResolver) (interface{}, error) {
return jsonUtils.NewTraversal(rule, substituteVariablesIfAny(log, ctx, vr)).TraverseJSON()
}

View file

@ -51,7 +51,6 @@ func Test_parse_file(t *testing.T) {
assert.Equal(t, response.RuleStatusFail, s.TestCases[0].Expected.Validation.PolicyResponse.Rules[0].Status, "invalid status")
}
func Test_parse_file2(t *testing.T) {
path := getRelativePath("test/scenarios/samples/best_practices/disallow_bind_mounts_fail.yaml")
data, err := ioutil.ReadFile(path)