From 784ca0741942677264d8a7b352fba0d43f5b99fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Edouard=20Br=C3=A9t=C3=A9ch=C3=A9?= <charles.edouard@nirmata.com> Date: Wed, 5 Apr 2023 12:35:38 +0200 Subject: [PATCH] refactor: engine rule response creation (#6784) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: engine rule response creation Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * private fields Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * more private Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fix unit tests Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --- cmd/cli/kubectl-kyverno/apply/report_test.go | 31 +- cmd/cli/kubectl-kyverno/test/test_command.go | 24 +- .../kubectl-kyverno/utils/common/common.go | 39 +-- pkg/background/generate/generate.go | 4 +- pkg/background/mutate/mutate.go | 25 +- pkg/controllers/report/utils/events.go | 5 +- pkg/engine/api/engineresponse.go | 8 +- pkg/engine/api/engineresponse_test.go | 322 ++++++++---------- pkg/engine/api/policyresponse.go | 11 +- pkg/engine/api/ruleresponse.go | 166 +++++++-- pkg/engine/api/ruleresponse_test.go | 71 ++-- pkg/engine/background.go | 26 +- pkg/engine/engine.go | 12 +- pkg/engine/exceptions.go | 28 +- pkg/engine/forceMutate.go | 4 +- pkg/engine/handlers/handler.go | 18 +- pkg/engine/handlers/mutation/common.go | 22 +- .../handlers/mutation/mutate_existing.go | 8 +- pkg/engine/handlers/mutation/mutate_image.go | 6 +- .../handlers/mutation/mutate_resource.go | 2 +- .../handlers/validation/validate_image.go | 5 +- .../handlers/validation/validate_manifest.go | 6 +- .../handlers/validation/validate_pss.go | 19 +- .../handlers/validation/validate_resource.go | 66 ++-- pkg/engine/image_verify.go | 5 +- pkg/engine/image_verify_test.go | 42 +-- pkg/engine/internal/imageverifier.go | 30 +- pkg/engine/internal/response.go | 56 --- pkg/engine/metrics.go | 6 +- pkg/engine/mutate/mutation.go | 16 +- pkg/engine/mutate/mutation_test.go | 54 ++- pkg/engine/mutate/patch/patchJSON6902.go | 40 +-- pkg/engine/mutate/patch/patchJSON6902_test.go | 8 +- pkg/engine/mutate/patch/patches.go | 14 +- .../mutate/patch/strategicMergePatch.go | 34 +- pkg/engine/mutation.go | 5 +- pkg/engine/mutation_test.go | 60 ++-- pkg/engine/validation.go | 5 +- pkg/engine/validation_test.go | 68 ++-- pkg/event/events.go | 17 +- pkg/metrics/parsers.go | 2 +- pkg/policy/policy_controller.go | 4 +- pkg/utils/annotations.go | 4 +- pkg/utils/annotations_test.go | 6 +- pkg/utils/report/results.go | 15 +- pkg/webhooks/resource/generation/handler.go | 6 +- pkg/webhooks/resource/updaterequest.go | 2 +- pkg/webhooks/resource/validation_test.go | 4 +- pkg/webhooks/utils/block.go | 6 +- pkg/webhooks/utils/block_test.go | 60 +--- pkg/webhooks/utils/error.go | 2 +- pkg/webhooks/utils/event.go | 5 +- pkg/webhooks/utils/warning.go | 4 +- pkg/webhooks/utils/warning_test.go | 36 +- 54 files changed, 718 insertions(+), 826 deletions(-) delete mode 100644 pkg/engine/internal/response.go diff --git a/cmd/cli/kubectl-kyverno/apply/report_test.go b/cmd/cli/kubectl-kyverno/apply/report_test.go index fba892c16c..818fdbe66b 100644 --- a/cmd/cli/kubectl-kyverno/apply/report_test.go +++ b/cmd/cli/kubectl-kyverno/apply/report_test.go @@ -3,6 +3,7 @@ package apply import ( "encoding/json" "testing" + "time" kyverno "github.com/kyverno/kyverno/api/kyverno/v1" preport "github.com/kyverno/kyverno/api/policyreport/v1alpha2" @@ -83,8 +84,6 @@ var rawPolicy = []byte(` } `) -var rawEngRes = []byte(`{"PatchedResource":{"apiVersion":"v1","kind":"Pod","metadata":{"name":"nginx1","namespace":"default"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"IfNotPresent","name":"nginx","resources":{"limits":{"cpu":"200m","memory":"100Mi"},"requests":{"cpu":"100m","memory":"50Mi"}}}]}},"PolicyResponse":{"policy":{"name":"pod-requirements","namespace":""},"resource":{"kind":"Pod","apiVersion":"v1","namespace":"default","name":"nginx1","uid":""},"processingTime":974958,"rulesAppliedCount":2,"policyExecutionTimestamp":1630527712,"rules":[{"name":"pods-require-account","type":"Validation","message":"validation error: User pods must include an account for charging. Rule pods-require-account failed at path /metadata/labels/","status":"fail","processingTime":28833,"ruleExecutionTimestamp":1630527712},{"name":"pods-require-limits","type":"Validation","message":"validation rule 'pods-require-limits' passed.","status":"pass","processingTime":578625,"ruleExecutionTimestamp":1630527712}],"ValidationFailureAction":"audit"}}`) - func Test_buildPolicyReports(t *testing.T) { rc := &kyvCommon.ResultCounts{} var pvInfos []common.Info @@ -92,10 +91,18 @@ func Test_buildPolicyReports(t *testing.T) { err := json.Unmarshal(rawPolicy, &policy) assert.NilError(t, err) - var er engineapi.EngineResponse - err = json.Unmarshal(rawEngRes, &er) + er := engineapi.EngineResponse{} er.Policy = &policy - assert.NilError(t, err) + er.PolicyResponse.Add(time.Now(), time.Now(), *engineapi.RuleFail( + "pods-require-account", + engineapi.Validation, + "validation error: User pods must include an account for charging. Rule pods-require-account failed at path /metadata/labels/"), + ) + er.PolicyResponse.Add(time.Now(), time.Now(), *engineapi.RulePass( + "pods-require-limits", + engineapi.Validation, + "validation rule 'pods-require-limits' passed."), + ) info := kyvCommon.ProcessValidateEngineResponse(&policy, &er, "", rc, true, false) pvInfos = append(pvInfos, info) @@ -129,10 +136,18 @@ func Test_buildPolicyResults(t *testing.T) { err := json.Unmarshal(rawPolicy, &policy) assert.NilError(t, err) - var er engineapi.EngineResponse - err = json.Unmarshal(rawEngRes, &er) + er := engineapi.EngineResponse{} er.Policy = &policy - assert.NilError(t, err) + er.PolicyResponse.Add(time.Now(), time.Now(), *engineapi.RuleFail( + "pods-require-account", + engineapi.Validation, + "validation error: User pods must include an account for charging. Rule pods-require-account failed at path /metadata/labels/"), + ) + er.PolicyResponse.Add(time.Now(), time.Now(), *engineapi.RulePass( + "pods-require-limits", + engineapi.Validation, + "validation rule 'pods-require-limits' passed."), + ) info := kyvCommon.ProcessValidateEngineResponse(&policy, &er, "", rc, true, false) pvInfos = append(pvInfos, info) diff --git a/cmd/cli/kubectl-kyverno/test/test_command.go b/cmd/cli/kubectl-kyverno/test/test_command.go index ef1508d540..c4d019c8bc 100644 --- a/cmd/cli/kubectl-kyverno/test/test_command.go +++ b/cmd/cli/kubectl-kyverno/test/test_command.go @@ -440,7 +440,7 @@ func buildPolicyResults(engineResponses []*engineapi.EngineResponse, testResults var rules []string for _, rule := range resp.PolicyResponse.Rules { - rules = append(rules, rule.Name) + rules = append(rules, rule.Name()) } result := policyreportv1alpha2.PolicyReportResult{ @@ -542,14 +542,14 @@ func buildPolicyResults(engineResponses []*engineapi.EngineResponse, testResults } for _, rule := range resp.PolicyResponse.Rules { - if rule.Type != engineapi.Generation || test.Rule != rule.Name { + if rule.RuleType() != engineapi.Generation || test.Rule != rule.Name() { continue } var resultsKey []string var resultKey string var result policyreportv1alpha2.PolicyReportResult - resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name, resourceNamespace, resourceKind, resourceName) + resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName) for _, key := range resultsKey { if val, ok := results[key]; ok { result = val @@ -558,14 +558,14 @@ func buildPolicyResults(engineResponses []*engineapi.EngineResponse, testResults continue } - if rule.Status == engineapi.RuleStatusSkip { + if rule.Status() == engineapi.RuleStatusSkip { result.Result = policyreportv1alpha2.StatusSkip - } else if rule.Status == engineapi.RuleStatusError { + } else if rule.Status() == engineapi.RuleStatusError { result.Result = policyreportv1alpha2.StatusError } else { var x string result.Result = policyreportv1alpha2.StatusFail - x = getAndCompareResource(test.GeneratedResource, rule.GeneratedResource, isGit, policyResourcePath, fs, true) + x = getAndCompareResource(test.GeneratedResource, rule.GeneratedResource(), isGit, policyResourcePath, fs, true) if x == "pass" { result.Result = policyreportv1alpha2.StatusPass } @@ -576,14 +576,14 @@ func buildPolicyResults(engineResponses []*engineapi.EngineResponse, testResults } for _, rule := range resp.PolicyResponse.Rules { - if rule.Type != engineapi.Mutation { + if rule.RuleType() != engineapi.Mutation { continue } var resultsKey []string var resultKey string var result policyreportv1alpha2.PolicyReportResult - resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name, resourceNamespace, resourceKind, resourceName) + resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName) for _, key := range resultsKey { if val, ok := results[key]; ok { result = val @@ -592,9 +592,9 @@ func buildPolicyResults(engineResponses []*engineapi.EngineResponse, testResults continue } - if rule.Status == engineapi.RuleStatusSkip { + if rule.Status() == engineapi.RuleStatusSkip { result.Result = policyreportv1alpha2.StatusSkip - } else if rule.Status == engineapi.RuleStatusError { + } else if rule.Status() == engineapi.RuleStatusError { result.Result = policyreportv1alpha2.StatusError } else { var x string @@ -710,8 +710,8 @@ func getAndCompareResource(path string, engineResource unstructured.Unstructured func buildMessage(resp *engineapi.EngineResponse) string { var bldr strings.Builder for _, ruleResp := range resp.PolicyResponse.Rules { - fmt.Fprintf(&bldr, " %s: %s \n", ruleResp.Name, ruleResp.Status) - fmt.Fprintf(&bldr, " %s \n", ruleResp.Message) + fmt.Fprintf(&bldr, " %s: %s \n", ruleResp.Name(), ruleResp.Status()) + fmt.Fprintf(&bldr, " %s \n", ruleResp.Message()) } return bldr.String() diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go index dadb1dcfa3..8f90f21f2a 100644 --- a/cmd/cli/kubectl-kyverno/utils/common/common.go +++ b/cmd/cli/kubectl-kyverno/utils/common/common.go @@ -708,15 +708,15 @@ func ProcessValidateEngineResponse(policy kyvernov1.PolicyInterface, validateRes } for i, valResponseRule := range validateResponse.PolicyResponse.Rules { - if policyRule.Name == valResponseRule.Name { + if policyRule.Name == valResponseRule.Name() { ruleFoundInEngineResponse = true vrule := kyvernov1.ViolatedRule{ - Name: valResponseRule.Name, - Type: string(valResponseRule.Type), - Message: valResponseRule.Message, + Name: valResponseRule.Name(), + Type: string(valResponseRule.RuleType()), + Message: valResponseRule.Message(), } - switch valResponseRule.Status { + switch valResponseRule.Status() { case engineapi.RuleStatusPass: rc.Pass++ vrule.Status = policyreportv1alpha2.StatusPass @@ -747,7 +747,7 @@ func ProcessValidateEngineResponse(policy kyvernov1.PolicyInterface, validateRes printCount++ } - fmt.Printf("%d. %s: %s \n", i+1, valResponseRule.Name, valResponseRule.Message) + fmt.Printf("%d. %s: %s \n", i+1, valResponseRule.Name(), valResponseRule.Message()) } case engineapi.RuleStatusError: @@ -801,17 +801,17 @@ func updateResultCounts(policy kyvernov1.PolicyInterface, engineResponse *engine for _, policyRule := range autogen.ComputeRules(policy) { ruleFoundInEngineResponse := false for i, ruleResponse := range engineResponse.PolicyResponse.Rules { - if policyRule.Name == ruleResponse.Name { + if policyRule.Name == ruleResponse.Name() { ruleFoundInEngineResponse = true - if ruleResponse.Status == engineapi.RuleStatusPass { + if ruleResponse.Status() == engineapi.RuleStatusPass { rc.Pass++ } else { if printCount < 1 { fmt.Println("\ninvalid resource", "policy", policy.GetName(), "resource", resPath) printCount++ } - fmt.Printf("%d. %s - %s\n", i+1, ruleResponse.Name, ruleResponse.Message) + fmt.Printf("%d. %s - %s\n", i+1, ruleResponse.Name(), ruleResponse.Message()) if auditWarn && engineResponse.GetValidationFailureAction().Audit() { rc.Warn++ @@ -877,23 +877,23 @@ func processMutateEngineResponse(c ApplyPolicyConfig, mutateResponse *engineapi. for _, policyRule := range autogen.ComputeRules(c.Policy) { ruleFoundInEngineResponse := false for i, mutateResponseRule := range mutateResponse.PolicyResponse.Rules { - if policyRule.Name == mutateResponseRule.Name { + if policyRule.Name == mutateResponseRule.Name() { ruleFoundInEngineResponse = true - if mutateResponseRule.Status == engineapi.RuleStatusPass { + if mutateResponseRule.Status() == engineapi.RuleStatusPass { c.Rc.Pass++ printMutatedRes = true - } else if mutateResponseRule.Status == engineapi.RuleStatusSkip { + } else if mutateResponseRule.Status() == engineapi.RuleStatusSkip { fmt.Printf("\nskipped mutate policy %s -> resource %s", c.Policy.GetName(), resPath) c.Rc.Skip++ - } else if mutateResponseRule.Status == engineapi.RuleStatusError { - fmt.Printf("\nerror while applying mutate policy %s -> resource %s\nerror: %s", c.Policy.GetName(), resPath, mutateResponseRule.Message) + } else if mutateResponseRule.Status() == engineapi.RuleStatusError { + fmt.Printf("\nerror while applying mutate policy %s -> resource %s\nerror: %s", c.Policy.GetName(), resPath, mutateResponseRule.Message()) c.Rc.Error++ } else { if printCount < 1 { fmt.Printf("\nfailed to apply mutate policy %s -> resource %s", c.Policy.GetName(), resPath) printCount++ } - fmt.Printf("%d. %s - %s \n", i+1, mutateResponseRule.Name, mutateResponseRule.Message) + fmt.Printf("%d. %s - %s \n", i+1, mutateResponseRule.Name(), mutateResponseRule.Message()) c.Rc.Fail++ } continue @@ -1089,7 +1089,7 @@ func handleGeneratePolicy(generateResponse *engineapi.EngineResponse, policyCont objects := []runtime.Object{&resource} resources := []*unstructured.Unstructured{} for _, rule := range generateResponse.PolicyResponse.Rules { - if path, ok := ruleToCloneSourceResource[rule.Name]; ok { + if path, ok := ruleToCloneSourceResource[rule.Name()]; ok { resourceBytes, err := getFileBytes(path) if err != nil { fmt.Printf("failed to get resource bytes\n") @@ -1128,20 +1128,17 @@ func handleGeneratePolicy(generateResponse *engineapi.EngineResponse, policyCont var newRuleResponse []engineapi.RuleResponse for _, rule := range generateResponse.PolicyResponse.Rules { - genResource, err := c.ApplyGeneratePolicy(log.Log, &policyContext, gr, []string{rule.Name}) + genResource, err := c.ApplyGeneratePolicy(log.Log, &policyContext, gr, []string{rule.Name()}) if err != nil { - rule.Status = engineapi.RuleStatusError return nil, err } unstrGenResource, err := c.GetUnstrResource(genResource[0]) if err != nil { - rule.Status = engineapi.RuleStatusError return nil, err } - rule.GeneratedResource = *unstrGenResource - newRuleResponse = append(newRuleResponse, rule) + newRuleResponse = append(newRuleResponse, *rule.WithGeneratedResource(*unstrGenResource)) } return newRuleResponse, nil diff --git a/pkg/background/generate/generate.go b/pkg/background/generate/generate.go index d8d2624e59..b2060c1875 100644 --- a/pkg/background/generate/generate.go +++ b/pkg/background/generate/generate.go @@ -223,7 +223,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u var applicableRules []string // Removing UR if rule is failed. Used when the generate condition failed but ur exist for _, r := range engineResponse.PolicyResponse.Rules { - if r.Status != engineapi.RuleStatusPass { + if r.Status() != engineapi.RuleStatusPass { logger.V(4).Info("querying all update requests") selector := labels.SelectorFromSet(labels.Set(map[string]string{ kyvernov1beta1.URGeneratePolicyLabel: engineResponse.Policy.GetName(), @@ -244,7 +244,7 @@ func (c *GenerateController) applyGenerate(resource unstructured.Unstructured, u } } } else { - applicableRules = append(applicableRules, r.Name) + applicableRules = append(applicableRules, r.Name()) } } diff --git a/pkg/background/mutate/mutate.go b/pkg/background/mutate/mutate.go index dac4261856..0280d846f3 100644 --- a/pkg/background/mutate/mutate.go +++ b/pkg/background/mutate/mutate.go @@ -149,17 +149,16 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e er := c.engine.Mutate(context.TODO(), policyContext) for _, r := range er.PolicyResponse.Rules { - patched := r.PatchedTarget - patchedTargetSubresourceName := r.PatchedTargetSubresourceName - switch r.Status { + patched, parentGVR, patchedSubresource := r.PatchedTarget() + switch r.Status() { case engineapi.RuleStatusFail, engineapi.RuleStatusError, engineapi.RuleStatusWarn: - err := fmt.Errorf("failed to mutate existing resource, rule response%v: %s", r.Status, r.Message) + err := fmt.Errorf("failed to mutate existing resource, rule response%v: %s", r.Status(), r.Message()) logger.Error(err, "") errs = append(errs, err) c.report(err, ur.Spec.Policy, rule.Name, patched) case engineapi.RuleStatusSkip: - logger.Info("mutate existing rule skipped", "rule", r.Name, "message", r.Message) + logger.Info("mutate existing rule skipped", "rule", r.Name(), "message", r.Message()) c.report(err, ur.Spec.Policy, rule.Name, patched) case engineapi.RuleStatusPass: @@ -171,18 +170,18 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e } if patchedNew == nil { - logger.Error(ErrEmptyPatch, "", "rule", r.Name, "message", r.Message) + logger.Error(ErrEmptyPatch, "", "rule", r.Name(), "message", r.Message()) errs = append(errs, err) continue } - if r.Status == engineapi.RuleStatusPass { + if r.Status() == engineapi.RuleStatusPass { patchedNew.SetResourceVersion(patched.GetResourceVersion()) var updateErr error - if patchedTargetSubresourceName == "status" { + if patchedSubresource == "status" { _, updateErr = c.client.UpdateStatusResource(context.TODO(), patchedNew.GetAPIVersion(), patchedNew.GetKind(), patchedNew.GetNamespace(), patchedNew.Object, false) - } else if patchedTargetSubresourceName != "" { - parentResourceGVR := r.PatchedTargetParentResourceGVR + } else if patchedSubresource != "" { + parentResourceGVR := parentGVR parentResourceGV := schema.GroupVersion{Group: parentResourceGVR.Group, Version: parentResourceGVR.Version} parentResourceGVK, err := c.client.Discovery().GetGVKFromGVR(parentResourceGV.WithResource(parentResourceGVR.Resource)) if err != nil { @@ -190,7 +189,7 @@ func (c *MutateExistingController) ProcessUR(ur *kyvernov1beta1.UpdateRequest) e errs = append(errs, err) continue } - _, updateErr = c.client.UpdateResource(context.TODO(), parentResourceGV.String(), parentResourceGVK.Kind, patchedNew.GetNamespace(), patchedNew.Object, false, patchedTargetSubresourceName) + _, updateErr = c.client.UpdateResource(context.TODO(), parentResourceGV.String(), parentResourceGVK.Kind, patchedNew.GetNamespace(), patchedNew.Object, false, patchedSubresource) } else { _, updateErr = c.client.UpdateResource(context.TODO(), patchedNew.GetAPIVersion(), patchedNew.GetKind(), patchedNew.GetNamespace(), patchedNew.Object, false) } @@ -261,7 +260,7 @@ func addAnnotation(policy kyvernov1.PolicyInterface, patched *unstructured.Unstr patchedNew = patched var rulePatches []utils.RulePatch - for _, patch := range r.Patches { + for _, patch := range r.Patches() { var patchmap map[string]interface{} if err := json.Unmarshal(patch, &patchmap); err != nil { return nil, fmt.Errorf("failed to parse JSON patch bytes: %v", err) @@ -272,7 +271,7 @@ func addAnnotation(policy kyvernov1.PolicyInterface, patched *unstructured.Unstr Op string `json:"op"` Path string `json:"path"` }{ - RuleName: r.Name, + RuleName: r.Name(), Op: patchmap["op"].(string), Path: patchmap["path"].(string), } diff --git a/pkg/controllers/report/utils/events.go b/pkg/controllers/report/utils/events.go index 222af092c9..52952c0897 100644 --- a/pkg/controllers/report/utils/events.go +++ b/pkg/controllers/report/utils/events.go @@ -34,8 +34,7 @@ func generateSuccessEvents(log logr.Logger, ers ...engineapi.EngineResponse) (ev func generateExceptionEvents(log logr.Logger, ers ...engineapi.EngineResponse) (eventInfos []event.Info) { for _, er := range ers { for i, ruleResp := range er.PolicyResponse.Rules { - isException := ruleResp.Exception != nil - if ruleResp.Status == engineapi.RuleStatusSkip && isException { + if ruleResp.Status() == engineapi.RuleStatusSkip && ruleResp.IsException() { eventInfos = append(eventInfos, event.NewPolicyExceptionEvents(er, &er.PolicyResponse.Rules[i], event.PolicyController)...) } } @@ -59,7 +58,7 @@ func generateFailEventsPerEr(log logr.Logger, er engineapi.EngineResponse) []eve "name", er.Resource.GetName(), ) for i, rule := range er.PolicyResponse.Rules { - if rule.Status != engineapi.RuleStatusPass && rule.Status != engineapi.RuleStatusSkip { + if rule.Status() != engineapi.RuleStatusPass && rule.Status() != engineapi.RuleStatusSkip { eventResource := event.NewResourceViolationEvent(event.PolicyController, event.PolicyViolation, er, &er.PolicyResponse.Rules[i]) eventInfos = append(eventInfos, eventResource) eventPolicy := event.NewPolicyFailEvent(event.PolicyController, event.PolicyViolation, er, &er.PolicyResponse.Rules[i], false) diff --git a/pkg/engine/api/engineresponse.go b/pkg/engine/api/engineresponse.go index 1b68d87006..18444a1dca 100644 --- a/pkg/engine/api/engineresponse.go +++ b/pkg/engine/api/engineresponse.go @@ -124,9 +124,7 @@ func (er EngineResponse) IsNil() bool { func (er EngineResponse) GetPatches() [][]byte { var patches [][]byte for _, r := range er.PolicyResponse.Rules { - if r.Patches != nil { - patches = append(patches, r.Patches...) - } + patches = append(patches, r.Patches()...) } return patches } @@ -161,7 +159,7 @@ func (er EngineResponse) getRules(predicate func(RuleResponse) bool) []string { var rules []string for _, r := range er.PolicyResponse.Rules { if predicate(r) { - rules = append(rules, r.Name) + rules = append(rules, r.Name()) } } return rules @@ -171,7 +169,7 @@ func (er EngineResponse) getRulesWithErrors(predicate func(RuleResponse) bool) [ var rules []string for _, r := range er.PolicyResponse.Rules { if predicate(r) { - rules = append(rules, fmt.Sprintf("%s: %s", r.Name, r.Message)) + rules = append(rules, fmt.Sprintf("%s: %s", r.Name(), r.Message())) } } return rules diff --git a/pkg/engine/api/engineresponse_test.go b/pkg/engine/api/engineresponse_test.go index aa90934e80..668b5e1dc4 100644 --- a/pkg/engine/api/engineresponse_test.go +++ b/pkg/engine/api/engineresponse_test.go @@ -113,18 +113,18 @@ func TestEngineResponse_IsOneOf(t *testing.T) { }{{ fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("", Validation, ""), + }, }, }, args: args{ @@ -134,9 +134,9 @@ func TestEngineResponse_IsOneOf(t *testing.T) { }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("", Validation, ""), + }, }, }, args: args{ @@ -146,9 +146,9 @@ func TestEngineResponse_IsOneOf(t *testing.T) { }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("", Validation, ""), + }, }, }, args: args{ @@ -158,9 +158,9 @@ func TestEngineResponse_IsOneOf(t *testing.T) { }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("", Validation, ""), + }, }, }, args: args{ @@ -199,45 +199,45 @@ func TestEngineResponse_IsSuccessful(t *testing.T) { }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusPass, - }}, + Rules: []RuleResponse{ + *RulePass("", Validation, ""), + }, }, }, want: true, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusWarn, - }}, + Rules: []RuleResponse{ + *RuleWarn("", Validation, ""), + }, }, }, want: true, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusError, - }}, + Rules: []RuleResponse{ + *RuleError("", Validation, "", nil), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusSkip, - }}, + Rules: []RuleResponse{ + *RuleSkip("", Validation, ""), + }, }, }, want: true, @@ -273,45 +273,45 @@ func TestEngineResponse_IsSkipped(t *testing.T) { }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusPass, - }}, + Rules: []RuleResponse{ + *RulePass("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusWarn, - }}, + Rules: []RuleResponse{ + *RuleWarn("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusError, - }}, + Rules: []RuleResponse{ + *RuleError("", Validation, "", nil), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusSkip, - }}, + Rules: []RuleResponse{ + *RuleSkip("", Validation, ""), + }, }, }, want: true, @@ -347,45 +347,45 @@ func TestEngineResponse_IsFailed(t *testing.T) { }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusPass, - }}, + Rules: []RuleResponse{ + *RulePass("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("", Validation, ""), + }, }, }, want: true, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusWarn, - }}, + Rules: []RuleResponse{ + *RuleWarn("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusError, - }}, + Rules: []RuleResponse{ + *RuleError("", Validation, "", nil), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusSkip, - }}, + Rules: []RuleResponse{ + *RuleSkip("", Validation, ""), + }, }, }, want: false, @@ -421,45 +421,45 @@ func TestEngineResponse_IsError(t *testing.T) { }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusPass, - }}, + Rules: []RuleResponse{ + *RulePass("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusWarn, - }}, + Rules: []RuleResponse{ + *RuleWarn("", Validation, ""), + }, }, }, want: false, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusError, - }}, + Rules: []RuleResponse{ + *RuleError("", Validation, "", nil), + }, }, }, want: true, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Status: RuleStatusSkip, - }}, + Rules: []RuleResponse{ + *RuleSkip("", Validation, ""), + }, }, }, want: false, @@ -493,76 +493,63 @@ func TestEngineResponse_GetFailedRules(t *testing.T) { }{{ fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "skip", - Status: RuleStatusSkip, - }}, + Rules: []RuleResponse{ + *RuleSkip("skip", Validation, ""), + }, }, }, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "warn", - Status: RuleStatusWarn, - }}, + Rules: []RuleResponse{ + *RuleWarn("warn", Validation, ""), + }, }, }, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "pass", - Status: RuleStatusPass, - }}, + Rules: []RuleResponse{ + *RulePass("pass", Validation, ""), + }, }, }, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "fail", - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("fail", Validation, ""), + }, }, }, want: []string{"fail"}, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "fail-1", - Status: RuleStatusFail, - }, { - Name: "fail-2", - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("fail-1", Validation, ""), + *RuleFail("fail-2", Validation, ""), + }, }, }, want: []string{"fail-1", "fail-2"}, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "fail-1", - Status: RuleStatusFail, - }, { - Name: "error-1", - Status: RuleStatusError, - }}, + Rules: []RuleResponse{ + *RuleFail("fail-1", Validation, ""), + *RuleError("error-1", Validation, "", nil), + }, }, }, want: []string{"fail-1", "error-1"}, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "error-1", - Status: RuleStatusError, - }, { - Name: "error-2", - Status: RuleStatusError, - }}, + Rules: []RuleResponse{ + *RuleError("error-1", Validation, "", nil), + *RuleError("error-2", Validation, "", nil), + }, }, }, want: []string{"error-1", "error-2"}, @@ -596,113 +583,91 @@ func TestEngineResponse_GetSuccessRules(t *testing.T) { }{{ fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "skip", - Status: RuleStatusSkip, - }}, + Rules: []RuleResponse{ + *RuleSkip("skip", Validation, ""), + }, }, }, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "warn", - Status: RuleStatusWarn, - }}, + Rules: []RuleResponse{ + *RuleWarn("warn", Validation, ""), + }, }, }, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "pass-1", - Status: RuleStatusPass, - }, { - Name: "pass-2", - Status: RuleStatusPass, - }}, + Rules: []RuleResponse{ + *RulePass("pass-1", Validation, ""), + *RulePass("pass-2", Validation, ""), + }, }, }, want: []string{"pass-1", "pass-2"}, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "pass", - Status: RuleStatusPass, - }}, + Rules: []RuleResponse{ + *RulePass("pass", Validation, ""), + }, }, }, want: []string{"pass"}, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "pass", - Status: RuleStatusPass, - }, { - Name: "fail", - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RulePass("pass", Validation, ""), + *RuleFail("fail", Validation, ""), + }, }, }, want: []string{"pass"}, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "pass", - Status: RuleStatusPass, - }, { - Name: "skip", - Status: RuleStatusSkip, - }}, + Rules: []RuleResponse{ + *RulePass("pass", Validation, ""), + *RuleSkip("skip", Validation, ""), + }, }, }, want: []string{"pass"}, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "fail", - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("fail", Validation, ""), + }, }, }, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "fail-1", - Status: RuleStatusFail, - }, { - Name: "fail-2", - Status: RuleStatusFail, - }}, + Rules: []RuleResponse{ + *RuleFail("fail-1", Validation, ""), + *RuleFail("fail-2", Validation, ""), + }, }, }, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "fail-1", - Status: RuleStatusFail, - }, { - Name: "error-1", - Status: RuleStatusError, - }}, + Rules: []RuleResponse{ + *RuleFail("fail-1", Validation, ""), + *RuleError("error-1", Validation, "", nil), + }, }, }, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{ - Name: "error-1", - Status: RuleStatusError, - }, { - Name: "error-2", - Status: RuleStatusError, - }}, + Rules: []RuleResponse{ + *RuleError("error-1", Validation, "", nil), + *RuleError("error-2", Validation, "", nil), + }, }, }, }} @@ -984,20 +949,21 @@ func TestEngineResponse_GetPatches(t *testing.T) { }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{}, { - Patches: [][]byte{{0, 1, 2}, {3, 4, 5}}, - }}, + Rules: []RuleResponse{ + {}, + *RuleResponse{}.WithPatches([][]byte{{0, 1, 2}, {3, 4, 5}}...), + }, }, }, want: [][]byte{{0, 1, 2}, {3, 4, 5}}, }, { fields: fields{ PolicyResponse: PolicyResponse{ - Rules: []RuleResponse{{}, { - Patches: [][]byte{{0, 1, 2}, {3, 4, 5}}, - }, { - Patches: [][]byte{{7, 8, 9}}, - }}, + Rules: []RuleResponse{ + {}, + *RuleResponse{}.WithPatches([][]byte{{0, 1, 2}, {3, 4, 5}}...), + *RuleResponse{}.WithPatches([][]byte{{7, 8, 9}}...), + }, }, }, want: [][]byte{{0, 1, 2}, {3, 4, 5}, {7, 8, 9}}, diff --git a/pkg/engine/api/policyresponse.go b/pkg/engine/api/policyresponse.go index e31ee327a2..772ef5e7b0 100644 --- a/pkg/engine/api/policyresponse.go +++ b/pkg/engine/api/policyresponse.go @@ -1,5 +1,7 @@ package api +import "time" + // PolicyResponse policy application response type PolicyResponse struct { // Stats contains policy statistics @@ -8,11 +10,12 @@ type PolicyResponse struct { Rules []RuleResponse } -func (pr *PolicyResponse) Add(rr RuleResponse) { - pr.Rules = append(pr.Rules, rr) - if rr.Status == RuleStatusPass || rr.Status == RuleStatusFail { +func (pr *PolicyResponse) Add(startTime, endTime time.Time, response RuleResponse) { + pr.Rules = append(pr.Rules, response.WithStats(startTime, endTime)) + status := response.Status() + if status == RuleStatusPass || status == RuleStatusFail { pr.Stats.RulesAppliedCount++ - } else if rr.Status == RuleStatusError { + } else if status == RuleStatusError { pr.Stats.RulesErrorCount++ } } diff --git a/pkg/engine/api/ruleresponse.go b/pkg/engine/api/ruleresponse.go index bf2c7b471c..1b16efc673 100644 --- a/pkg/engine/api/ruleresponse.go +++ b/pkg/engine/api/ruleresponse.go @@ -2,6 +2,7 @@ package api import ( "fmt" + "time" kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1" pssutils "github.com/kyverno/kyverno/pkg/pss/utils" @@ -22,36 +23,145 @@ type PodSecurityChecks struct { // RuleResponse details for each rule application type RuleResponse struct { - // Name is the rule name specified in policy - Name string - // Type is the rule type (Mutation,Generation,Validation) for Kyverno Policy - Type RuleType - // Message is the message response from the rule application - Message string - // Patches are JSON patches, for mutation rules - Patches [][]byte - // GeneratedResource is the generated by the generate rules of a policy - GeneratedResource unstructured.Unstructured - // Status rule status - Status RuleStatus - // Stats contains rule statistics - Stats ExecutionStats - // PatchedTarget is the patched resource for mutate.targets - PatchedTarget *unstructured.Unstructured - // PatchedTargetSubresourceName is the name of the subresource which is patched, empty if the resource patched is not a subresource. - PatchedTargetSubresourceName string - // PatchedTargetParentResourceGVR is the GVR of the parent resource of the PatchedTarget. This is only populated when PatchedTarget is a subresource. - PatchedTargetParentResourceGVR metav1.GroupVersionResource - // PodSecurityChecks contains pod security checks (only if this is a pod security rule) - PodSecurityChecks *PodSecurityChecks - // Exception is the exception applied (if any) - Exception *kyvernov2alpha1.PolicyException + // name is the rule name specified in policy + name string + // ruleType is the rule type (Mutation,Generation,Validation) for Kyverno Policy + ruleType RuleType + // message is the message response from the rule application + message string + // status rule status + status RuleStatus + // patches are JSON patches, for mutation rules + patches [][]byte + // stats contains rule statistics + stats ExecutionStats + // generatedResource is the generated by the generate rules of a policy + generatedResource unstructured.Unstructured + // patchedTarget is the patched resource for mutate.targets + patchedTarget *unstructured.Unstructured + // patchedTargetParentResourceGVR is the GVR of the parent resource of the PatchedTarget. This is only populated when PatchedTarget is a subresource. + patchedTargetParentResourceGVR metav1.GroupVersionResource + // patchedTargetSubresourceName is the name of the subresource which is patched, empty if the resource patched is not a subresource. + patchedTargetSubresourceName string + // podSecurityChecks contains pod security checks (only if this is a pod security rule) + podSecurityChecks *PodSecurityChecks + // exception is the exception applied (if any) + exception *kyvernov2alpha1.PolicyException +} + +func NewRuleResponse(name string, ruleType RuleType, msg string, status RuleStatus) *RuleResponse { + return &RuleResponse{ + name: name, + ruleType: ruleType, + message: msg, + status: status, + } +} + +func RuleError(name string, ruleType RuleType, msg string, err error) *RuleResponse { + if err != nil { + return NewRuleResponse(name, ruleType, fmt.Sprintf("%s: %s", msg, err.Error()), RuleStatusError) + } + return NewRuleResponse(name, ruleType, msg, RuleStatusError) +} + +func RuleSkip(name string, ruleType RuleType, msg string) *RuleResponse { + return NewRuleResponse(name, ruleType, msg, RuleStatusSkip) +} + +func RuleWarn(name string, ruleType RuleType, msg string) *RuleResponse { + return NewRuleResponse(name, ruleType, msg, RuleStatusWarn) +} + +func RulePass(name string, ruleType RuleType, msg string) *RuleResponse { + return NewRuleResponse(name, ruleType, msg, RuleStatusPass) +} + +func RuleFail(name string, ruleType RuleType, msg string) *RuleResponse { + return NewRuleResponse(name, ruleType, msg, RuleStatusFail) +} + +func (r RuleResponse) WithException(exception *kyvernov2alpha1.PolicyException) *RuleResponse { + r.exception = exception + return &r +} + +func (r RuleResponse) WithPodSecurityChecks(checks PodSecurityChecks) *RuleResponse { + r.podSecurityChecks = &checks + return &r +} + +func (r RuleResponse) WithPatchedTarget(patchedTarget *unstructured.Unstructured, gvr metav1.GroupVersionResource, subresource string) *RuleResponse { + r.patchedTarget = patchedTarget + r.patchedTargetParentResourceGVR = gvr + r.patchedTargetSubresourceName = subresource + return &r +} + +func (r RuleResponse) WithGeneratedResource(resource unstructured.Unstructured) *RuleResponse { + r.generatedResource = resource + return &r +} + +func (r RuleResponse) WithPatches(patches ...[]byte) *RuleResponse { + r.patches = patches + return &r +} + +func (r RuleResponse) WithStats(startTime, endTime time.Time) RuleResponse { + r.stats = NewExecutionStats(startTime) + r.stats.Done(endTime) + return r +} + +func (r *RuleResponse) Stats() ExecutionStats { + return r.stats +} + +func (r *RuleResponse) Exception() *kyvernov2alpha1.PolicyException { + return r.exception +} + +func (r *RuleResponse) IsException() bool { + return r.exception != nil +} + +func (r *RuleResponse) PodSecurityChecks() *PodSecurityChecks { + return r.podSecurityChecks +} + +func (r *RuleResponse) PatchedTarget() (*unstructured.Unstructured, metav1.GroupVersionResource, string) { + return r.patchedTarget, r.patchedTargetParentResourceGVR, r.patchedTargetSubresourceName +} + +func (r *RuleResponse) GeneratedResource() unstructured.Unstructured { + return r.generatedResource +} + +func (r *RuleResponse) Patches() [][]byte { + return r.patches +} + +func (r *RuleResponse) Message() string { + return r.message +} + +func (r *RuleResponse) Name() string { + return r.name +} + +func (r *RuleResponse) RuleType() RuleType { + return r.ruleType +} + +func (r *RuleResponse) Status() RuleStatus { + return r.status } // HasStatus checks if rule status is in a given list -func (r RuleResponse) HasStatus(status ...RuleStatus) bool { +func (r *RuleResponse) HasStatus(status ...RuleStatus) bool { for _, s := range status { - if r.Status == s { + if r.status == s { return true } } @@ -59,6 +169,6 @@ func (r RuleResponse) HasStatus(status ...RuleStatus) bool { } // String implements Stringer interface -func (r RuleResponse) String() string { - return fmt.Sprintf("rule %s (%s): %v", r.Name, r.Type, r.Message) +func (r *RuleResponse) String() string { + return fmt.Sprintf("rule %s (%s): %v", r.name, r.ruleType, r.message) } diff --git a/pkg/engine/api/ruleresponse_test.go b/pkg/engine/api/ruleresponse_test.go index 5dd6c2b8ed..c1bef933a8 100644 --- a/pkg/engine/api/ruleresponse_test.go +++ b/pkg/engine/api/ruleresponse_test.go @@ -2,24 +2,14 @@ package api import ( "testing" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) func TestRuleResponse_String(t *testing.T) { type fields struct { - Name string - Type RuleType - Message string - Patches [][]byte - GeneratedResource unstructured.Unstructured - Status RuleStatus - Stats ExecutionStats - PatchedTarget *unstructured.Unstructured - PatchedTargetSubresourceName string - PatchedTargetParentResourceGVR metav1.GroupVersionResource - PodSecurityChecks *PodSecurityChecks + Name string + Type RuleType + Message string + Status RuleStatus } tests := []struct { name string @@ -56,19 +46,12 @@ func TestRuleResponse_String(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - rr := RuleResponse{ - Name: tt.fields.Name, - Type: tt.fields.Type, - Message: tt.fields.Message, - Patches: tt.fields.Patches, - GeneratedResource: tt.fields.GeneratedResource, - Status: tt.fields.Status, - Stats: tt.fields.Stats, - PatchedTarget: tt.fields.PatchedTarget, - PatchedTargetSubresourceName: tt.fields.PatchedTargetSubresourceName, - PatchedTargetParentResourceGVR: tt.fields.PatchedTargetParentResourceGVR, - PodSecurityChecks: tt.fields.PodSecurityChecks, - } + rr := NewRuleResponse( + tt.fields.Name, + tt.fields.Type, + tt.fields.Message, + tt.fields.Status, + ) if got := rr.String(); got != tt.want { t.Errorf("RuleResponse.ToString() = %v, want %v", got, tt.want) } @@ -78,17 +61,10 @@ func TestRuleResponse_String(t *testing.T) { func TestRuleResponse_HasStatus(t *testing.T) { type fields struct { - Name string - Type RuleType - Message string - Patches [][]byte - GeneratedResource unstructured.Unstructured - Status RuleStatus - Stats ExecutionStats - PatchedTarget *unstructured.Unstructured - PatchedTargetSubresourceName string - PatchedTargetParentResourceGVR metav1.GroupVersionResource - PodSecurityChecks *PodSecurityChecks + Name string + Type RuleType + Message string + Status RuleStatus } type args struct { status []RuleStatus @@ -138,19 +114,12 @@ func TestRuleResponse_HasStatus(t *testing.T) { }} for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - r := RuleResponse{ - Name: tt.fields.Name, - Type: tt.fields.Type, - Message: tt.fields.Message, - Patches: tt.fields.Patches, - GeneratedResource: tt.fields.GeneratedResource, - Status: tt.fields.Status, - Stats: tt.fields.Stats, - PatchedTarget: tt.fields.PatchedTarget, - PatchedTargetSubresourceName: tt.fields.PatchedTargetSubresourceName, - PatchedTargetParentResourceGVR: tt.fields.PatchedTargetParentResourceGVR, - PodSecurityChecks: tt.fields.PodSecurityChecks, - } + r := NewRuleResponse( + tt.fields.Name, + tt.fields.Type, + tt.fields.Message, + tt.fields.Status, + ) if got := r.HasStatus(tt.args.status...); got != tt.want { t.Errorf("RuleResponse.HasStatus() = %v, want %v", got, tt.want) } diff --git a/pkg/engine/background.go b/pkg/engine/background.go index a1b05ece66..37c04c833d 100644 --- a/pkg/engine/background.go +++ b/pkg/engine/background.go @@ -39,7 +39,7 @@ func (e *engine) filterRules( logger := internal.LoggerWithRule(logger, rule) if ruleResp := e.filterRule(rule, logger, policyContext); ruleResp != nil { resp.Rules = append(resp.Rules, *ruleResp) - if applyRules == kyvernov1.ApplyOne && ruleResp.Status != engineapi.RuleStatusSkip { + if applyRules == kyvernov1.ApplyOne && ruleResp.Status() != engineapi.RuleStatusSkip { break } } @@ -67,8 +67,6 @@ func (e *engine) filterRule( return ruleResp } - startTime := time.Now() - newResource := policyContext.NewResource() oldResource := policyContext.OldResource() admissionInfo := policyContext.AdmissionInfo() @@ -81,15 +79,7 @@ func (e *engine) filterRule( if ruleType == engineapi.Generation { // if the oldResource matched, return "false" to delete GR for it if err = engineutils.MatchesResourceDescription(oldResource, rule, admissionInfo, namespaceLabels, policy.GetNamespace(), gvk, subresource, policyContext.Operation()); err == nil { - return &engineapi.RuleResponse{ - Name: rule.Name, - Type: ruleType, - Status: engineapi.RuleStatusFail, - Stats: engineapi.ExecutionStats{ - ProcessingTime: time.Since(startTime), - Timestamp: startTime.Unix(), - }, - } + return engineapi.RuleFail(rule.Name, ruleType, "") } } logger.V(4).Info("rule not matched", "reason", err.Error()) @@ -122,17 +112,9 @@ func (e *engine) filterRule( // evaluate pre-conditions if !variables.EvaluateConditions(logger, ctx, copyConditions) { logger.V(4).Info("skip rule as preconditions are not met", "rule", ruleCopy.Name) - return internal.RuleSkip(*ruleCopy, ruleType, "") + return engineapi.RuleSkip(ruleCopy.Name, ruleType, "") } // build rule Response - return &engineapi.RuleResponse{ - Name: ruleCopy.Name, - Type: ruleType, - Status: engineapi.RuleStatusPass, - Stats: engineapi.ExecutionStats{ - ProcessingTime: time.Since(startTime), - Timestamp: startTime.Unix(), - }, - } + return engineapi.RulePass(ruleCopy.Name, ruleType, "") } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index d954bb4118..d2162f462d 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -227,13 +227,13 @@ func (e *engine) invokeRuleHandler( return resource, nil } if handlerFactory == nil { - return resource, handlers.RuleResponses(internal.RuleError(rule, ruleType, "failed to instantiate handler", nil)) + return resource, handlers.WithError(rule, ruleType, "failed to instantiate handler", nil) } else if handler, err := handlerFactory(); err != nil { - return resource, handlers.RuleResponses(internal.RuleError(rule, ruleType, "failed to instantiate handler", err)) + return resource, handlers.WithError(rule, ruleType, "failed to instantiate handler", err) } else if handler != nil { // check if there's an exception if ruleResp := e.hasPolicyExceptions(logger, ruleType, policyContext, rule); ruleResp != nil { - return resource, handlers.RuleResponses(ruleResp) + return resource, handlers.WithResponses(ruleResp) } // load rule context contextLoader := e.ContextLoader(policyContext.Policy(), rule) @@ -243,15 +243,15 @@ func (e *engine) invokeRuleHandler( } else { logger.Error(err, "failed to load context") } - return resource, handlers.RuleResponses(internal.RuleError(rule, ruleType, "failed to load context", err)) + return resource, handlers.WithError(rule, ruleType, "failed to load context", err) } // check preconditions preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext.JSONContext(), rule.GetAnyAllConditions()) if err != nil { - return resource, handlers.RuleResponses(internal.RuleError(rule, ruleType, "failed to evaluate preconditions", err)) + return resource, handlers.WithError(rule, ruleType, "failed to evaluate preconditions", err) } if !preconditionsPassed { - return resource, handlers.RuleResponses(internal.RuleSkip(rule, ruleType, "preconditions not met")) + return resource, handlers.WithSkip(rule, ruleType, "preconditions not met") } // process handler return handler.Process(ctx, logger, policyContext, resource, rule, contextLoader) diff --git a/pkg/engine/exceptions.go b/pkg/engine/exceptions.go index cd0aad8b6d..33d303b3c1 100644 --- a/pkg/engine/exceptions.go +++ b/pkg/engine/exceptions.go @@ -8,7 +8,6 @@ import ( kyvernov2alpha1 "github.com/kyverno/kyverno/api/kyverno/v2alpha1" "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" - "github.com/kyverno/kyverno/pkg/engine/internal" matched "github.com/kyverno/kyverno/pkg/utils/match" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/tools/cache" @@ -79,18 +78,19 @@ func (e *engine) hasPolicyExceptions( ) *engineapi.RuleResponse { // if matches, check if there is a corresponding policy exception exception, err := matchesException(e.exceptionSelector, ctx, rule, e.configuration) - var response *engineapi.RuleResponse - // if we found an exception - if err == nil && exception != nil { - key, err := cache.MetaNamespaceKeyFunc(exception) - if err != nil { - logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName()) - response = internal.RuleError(rule, ruleType, "failed to compute exception key", err) - } else { - logger.V(3).Info("policy rule skipped due to policy exception", "exception", key) - response = internal.RuleSkip(rule, ruleType, "rule skipped due to policy exception "+key) - response.Exception = exception - } + if err != nil { + logger.Error(err, "failed to match exceptions") + return nil + } + if exception == nil { + return nil + } + key, err := cache.MetaNamespaceKeyFunc(exception) + if err != nil { + logger.Error(err, "failed to compute policy exception key", "namespace", exception.GetNamespace(), "name", exception.GetName()) + return engineapi.RuleError(rule.Name, ruleType, "failed to compute exception key", err) + } else { + logger.V(3).Info("policy rule skipped due to policy exception", "exception", key) + return engineapi.RuleSkip(rule.Name, ruleType, "rule skipped due to policy exception "+key).WithException(exception) } - return response } diff --git a/pkg/engine/forceMutate.go b/pkg/engine/forceMutate.go index afde293a53..3bb9190d33 100644 --- a/pkg/engine/forceMutate.go +++ b/pkg/engine/forceMutate.go @@ -86,8 +86,8 @@ func applyForEachMutate(name string, foreach []kyvernov1.ForEachMutation, resour func applyPatches(name string, mergePatch apiextensions.JSON, jsonPatch string, resource unstructured.Unstructured, logger logr.Logger) (unstructured.Unstructured, error) { patcher := mutate.NewPatcher(name, mergePatch, jsonPatch, resource, logger) resp, mutatedResource := patcher.Patch() - if resp.Status != engineapi.RuleStatusPass { - return mutatedResource, fmt.Errorf("mutate status %q: %s", resp.Status, resp.Message) + if resp.Status() != engineapi.RuleStatusPass { + return mutatedResource, fmt.Errorf("mutate status %q: %s", resp.Status(), resp.Message()) } return mutatedResource, nil diff --git a/pkg/engine/handlers/handler.go b/pkg/engine/handlers/handler.go index e155201f75..d30cda1b35 100644 --- a/pkg/engine/handlers/handler.go +++ b/pkg/engine/handlers/handler.go @@ -20,7 +20,23 @@ type Handler interface { ) (unstructured.Unstructured, []engineapi.RuleResponse) } -func RuleResponses(rrs ...*engineapi.RuleResponse) []engineapi.RuleResponse { +func WithError(rule kyvernov1.Rule, ruleType engineapi.RuleType, msg string, err error) []engineapi.RuleResponse { + return WithResponses(engineapi.RuleError(rule.Name, ruleType, msg, err)) +} + +func WithSkip(rule kyvernov1.Rule, ruleType engineapi.RuleType, msg string) []engineapi.RuleResponse { + return WithResponses(engineapi.RuleSkip(rule.Name, ruleType, msg)) +} + +func WithPass(rule kyvernov1.Rule, ruleType engineapi.RuleType, msg string) []engineapi.RuleResponse { + return WithResponses(engineapi.RulePass(rule.Name, ruleType, msg)) +} + +func WithFail(rule kyvernov1.Rule, ruleType engineapi.RuleType, msg string) []engineapi.RuleResponse { + return WithResponses(engineapi.RuleFail(rule.Name, ruleType, msg)) +} + +func WithResponses(rrs ...*engineapi.RuleResponse) []engineapi.RuleResponse { var out []engineapi.RuleResponse for _, rr := range rrs { if rr != nil { diff --git a/pkg/engine/handlers/mutation/common.go b/pkg/engine/handlers/mutation/common.go index d4b71c105f..6b834a0145 100644 --- a/pkg/engine/handlers/mutation/common.go +++ b/pkg/engine/handlers/mutation/common.go @@ -134,15 +134,21 @@ func (f *forEachMutator) mutateElements(ctx context.Context, foreach kyvernov1.F } func buildRuleResponse(rule *kyvernov1.Rule, mutateResp *mutate.Response, info resourceInfo) *engineapi.RuleResponse { - resp := internal.RuleResponse(*rule, engineapi.Mutation, mutateResp.Message, mutateResp.Status) - if resp.Status == engineapi.RuleStatusPass { - resp.Patches = mutateResp.Patches - resp.Message = buildSuccessMessage(mutateResp.PatchedResource) + message := mutateResp.Message + if mutateResp.Status == engineapi.RuleStatusPass { + message = buildSuccessMessage(mutateResp.PatchedResource) } - if len(rule.Mutation.Targets) != 0 { - resp.PatchedTarget = &mutateResp.PatchedResource - resp.PatchedTargetSubresourceName = info.subresource - resp.PatchedTargetParentResourceGVR = info.parentResourceGVR + resp := engineapi.NewRuleResponse( + rule.Name, + engineapi.Mutation, + message, + mutateResp.Status, + ) + if mutateResp.Status == engineapi.RuleStatusPass { + resp = resp.WithPatches(mutateResp.Patches...) + if len(rule.Mutation.Targets) != 0 { + resp = resp.WithPatchedTarget(&mutateResp.PatchedResource, info.parentResourceGVR, info.subresource) + } } return resp } diff --git a/pkg/engine/handlers/mutation/mutate_existing.go b/pkg/engine/handlers/mutation/mutate_existing.go index 6bc321d6c5..ddc0b42d79 100644 --- a/pkg/engine/handlers/mutation/mutate_existing.go +++ b/pkg/engine/handlers/mutation/mutate_existing.go @@ -37,7 +37,7 @@ func (h mutateExistingHandler) Process( logger.V(3).Info("processing mutate rule") targets, err := loadTargets(h.client, rule.Mutation.Targets, policyContext, logger) if err != nil { - rr := internal.RuleError(rule, engineapi.Mutation, "", err) + rr := engineapi.RuleError(rule.Name, engineapi.Mutation, "", err) responses = append(responses, *rr) } @@ -52,19 +52,19 @@ func (h mutateExistingHandler) Process( } // load target specific context if err := contextLoader(ctx, target.context, policyContext.JSONContext()); err != nil { - rr := internal.RuleError(rule, engineapi.Mutation, "failed to load context", err) + rr := engineapi.RuleError(rule.Name, engineapi.Mutation, "failed to load context", err) responses = append(responses, *rr) continue } // load target specific preconditions preconditionsPassed, err := internal.CheckPreconditions(logger, policyContext.JSONContext(), target.preconditions) if err != nil { - rr := internal.RuleError(rule, engineapi.Mutation, "failed to evaluate preconditions", err) + rr := engineapi.RuleError(rule.Name, engineapi.Mutation, "failed to evaluate preconditions", err) responses = append(responses, *rr) continue } if !preconditionsPassed { - rr := internal.RuleSkip(rule, engineapi.Mutation, "preconditions not met") + rr := engineapi.RuleSkip(rule.Name, engineapi.Mutation, "preconditions not met") responses = append(responses, *rr) continue } diff --git a/pkg/engine/handlers/mutation/mutate_image.go b/pkg/engine/handlers/mutation/mutate_image.go index a28fe82e27..98037cc325 100644 --- a/pkg/engine/handlers/mutation/mutate_image.go +++ b/pkg/engine/handlers/mutation/mutate_image.go @@ -61,8 +61,8 @@ func (h mutateImageHandler) Process( jsonContext := policyContext.JSONContext() ruleCopy, err := substituteVariables(rule, jsonContext, logger) if err != nil { - return resource, handlers.RuleResponses( - internal.RuleError(rule, engineapi.ImageVerify, "failed to substitute variables", err), + return resource, handlers.WithResponses( + engineapi.RuleError(rule.Name, engineapi.ImageVerify, "failed to substitute variables", err), ) } iv := internal.NewImageVerifier(logger, h.rclient, policyContext, *ruleCopy, h.ivm) @@ -70,7 +70,7 @@ func (h mutateImageHandler) Process( for _, imageVerify := range ruleCopy.VerifyImages { engineResponses = append(engineResponses, iv.Verify(ctx, imageVerify, h.images, h.configuration)...) } - return resource, handlers.RuleResponses(engineResponses...) + return resource, handlers.WithResponses(engineResponses...) } func substituteVariables(rule kyvernov1.Rule, ctx enginecontext.EvalInterface, logger logr.Logger) (*kyvernov1.Rule, error) { diff --git a/pkg/engine/handlers/mutation/mutate_resource.go b/pkg/engine/handlers/mutation/mutate_resource.go index 9493bd5ec6..4ceaaf0437 100644 --- a/pkg/engine/handlers/mutation/mutate_resource.go +++ b/pkg/engine/handlers/mutation/mutate_resource.go @@ -56,5 +56,5 @@ func (h mutateResourceHandler) Process( if mutateResp == nil { return resource, nil } - return mutateResp.PatchedResource, handlers.RuleResponses(buildRuleResponse(&rule, mutateResp, resourceInfo)) + return mutateResp.PatchedResource, handlers.WithResponses(buildRuleResponse(&rule, mutateResp, resourceInfo)) } diff --git a/pkg/engine/handlers/validation/validate_image.go b/pkg/engine/handlers/validation/validate_image.go index 2e638e7d10..44466c2e49 100644 --- a/pkg/engine/handlers/validation/validate_image.go +++ b/pkg/engine/handlers/validation/validate_image.go @@ -9,7 +9,6 @@ import ( "github.com/kyverno/kyverno/pkg/config" engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/handlers" - "github.com/kyverno/kyverno/pkg/engine/internal" engineutils "github.com/kyverno/kyverno/pkg/engine/utils" apiutils "github.com/kyverno/kyverno/pkg/utils/api" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -57,13 +56,13 @@ func (h validateImageHandler) Process( logger.V(4).Info("validating image", "image", image) if err := validateImage(policyContext, imageVerify, name, imageInfo, logger); err != nil { - return resource, handlers.RuleResponses(internal.RuleResponse(rule, engineapi.ImageVerify, err.Error(), engineapi.RuleStatusFail)) + return resource, handlers.WithFail(rule, engineapi.ImageVerify, err.Error()) } } } } logger.V(4).Info("validated image", "rule", rule.Name) - return resource, handlers.RuleResponses(internal.RulePass(rule, engineapi.Validation, "image verified")) + return resource, handlers.WithPass(rule, engineapi.Validation, "image verified") } func validateImage(ctx engineapi.PolicyContext, imageVerify *kyvernov1.ImageVerification, name string, imageInfo apiutils.ImageInfo, log logr.Logger) error { diff --git a/pkg/engine/handlers/validation/validate_manifest.go b/pkg/engine/handlers/validation/validate_manifest.go index 7bc679c481..ec2dc5d8e9 100644 --- a/pkg/engine/handlers/validation/validate_manifest.go +++ b/pkg/engine/handlers/validation/validate_manifest.go @@ -62,13 +62,13 @@ func (h validateManifestHandler) Process( verified, reason, err := h.verifyManifest(ctx, logger, policyContext, *rule.Validation.Manifests) if err != nil { logger.V(3).Info("verifyManifest return err", "error", err.Error()) - return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "error occurred during manifest verification", err)) + return resource, handlers.WithError(rule, engineapi.Validation, "error occurred during manifest verification", err) } logger.V(3).Info("verifyManifest result", "verified", strconv.FormatBool(verified), "reason", reason) if !verified { - return resource, handlers.RuleResponses(internal.RuleResponse(rule, engineapi.Validation, reason, engineapi.RuleStatusFail)) + return resource, handlers.WithFail(rule, engineapi.Validation, reason) } - return resource, handlers.RuleResponses(internal.RulePass(rule, engineapi.Validation, reason)) + return resource, handlers.WithPass(rule, engineapi.Validation, reason) } func (h validateManifestHandler) verifyManifest( diff --git a/pkg/engine/handlers/validation/validate_pss.go b/pkg/engine/handlers/validation/validate_pss.go index 06a63c5905..3dff326587 100644 --- a/pkg/engine/handlers/validation/validate_pss.go +++ b/pkg/engine/handlers/validation/validate_pss.go @@ -9,7 +9,6 @@ import ( kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/handlers" - "github.com/kyverno/kyverno/pkg/engine/internal" "github.com/kyverno/kyverno/pkg/pss" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" @@ -36,7 +35,7 @@ func (h validatePssHandler) Process( podSecurity := rule.Validation.PodSecurity podSpec, metadata, err := getSpec(resource) if err != nil { - return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "Error while getting new resource", err)) + return resource, handlers.WithError(rule, engineapi.Validation, "Error while getting new resource", err) } pod := &corev1.Pod{ Spec: *podSpec, @@ -44,23 +43,23 @@ func (h validatePssHandler) Process( } allowed, pssChecks, err := pss.EvaluatePod(podSecurity, pod) if err != nil { - return resource, handlers.RuleResponses(internal.RuleError(rule, engineapi.Validation, "failed to parse pod security api version", err)) + return resource, handlers.WithError(rule, engineapi.Validation, "failed to parse pod security api version", err) } - podSecurityChecks := &engineapi.PodSecurityChecks{ + podSecurityChecks := engineapi.PodSecurityChecks{ Level: podSecurity.Level, Version: podSecurity.Version, Checks: pssChecks, } if allowed { msg := fmt.Sprintf("Validation rule '%s' passed.", rule.Name) - rspn := internal.RulePass(rule, engineapi.Validation, msg) - rspn.PodSecurityChecks = podSecurityChecks - return resource, handlers.RuleResponses(rspn) + return resource, handlers.WithResponses( + engineapi.RulePass(rule.Name, engineapi.Validation, msg).WithPodSecurityChecks(podSecurityChecks), + ) } else { msg := fmt.Sprintf(`Validation rule '%s' failed. It violates PodSecurity "%s:%s": %s`, rule.Name, podSecurity.Level, podSecurity.Version, pss.FormatChecksPrint(pssChecks)) - rspn := internal.RuleResponse(rule, engineapi.Validation, msg, engineapi.RuleStatusFail) - rspn.PodSecurityChecks = podSecurityChecks - return resource, handlers.RuleResponses(rspn) + return resource, handlers.WithResponses( + engineapi.RuleFail(rule.Name, engineapi.Validation, msg).WithPodSecurityChecks(podSecurityChecks), + ) } } diff --git a/pkg/engine/handlers/validation/validate_resource.go b/pkg/engine/handlers/validation/validate_resource.go index 3a6ff7170b..7205c191f9 100644 --- a/pkg/engine/handlers/validation/validate_resource.go +++ b/pkg/engine/handlers/validation/validate_resource.go @@ -36,7 +36,7 @@ func (h validateResourceHandler) Process( contextLoader engineapi.EngineContextLoader, ) (unstructured.Unstructured, []engineapi.RuleResponse) { v := newValidator(logger, contextLoader, policyContext, rule) - return resource, handlers.RuleResponses(v.validate(ctx)) + return resource, handlers.WithResponses(v.validate(ctx)) } type validator struct { @@ -99,14 +99,14 @@ func newForEachValidator( func (v *validator) validate(ctx context.Context) *engineapi.RuleResponse { if err := v.loadContext(ctx); err != nil { - return internal.RuleError(v.rule, engineapi.Validation, "failed to load context", err) + return engineapi.RuleError(v.rule.Name, engineapi.Validation, "failed to load context", err) } preconditionsPassed, err := internal.CheckPreconditions(v.log, v.policyContext.JSONContext(), v.anyAllConditions) if err != nil { - return internal.RuleError(v.rule, engineapi.Validation, "failed to evaluate preconditions", err) + return engineapi.RuleError(v.rule.Name, engineapi.Validation, "failed to evaluate preconditions", err) } if !preconditionsPassed { - return internal.RuleSkip(v.rule, engineapi.Validation, "preconditions not met") + return engineapi.RuleSkip(v.rule.Name, engineapi.Validation, "preconditions not met") } if v.deny != nil { @@ -115,7 +115,7 @@ func (v *validator) validate(ctx context.Context) *engineapi.RuleResponse { if v.pattern != nil || v.anyPattern != nil { if err = v.substitutePatterns(); err != nil { - return internal.RuleError(v.rule, engineapi.Validation, "variable substitution failed", err) + return engineapi.RuleError(v.rule.Name, engineapi.Validation, "variable substitution failed", err) } ruleResponse := v.validateResourceWithRule() @@ -140,7 +140,7 @@ func (v *validator) validateForEach(ctx context.Context) *engineapi.RuleResponse continue } resp, count := v.validateElements(ctx, foreach, elements, foreach.ElementScope) - if resp.Status != engineapi.RuleStatusPass { + if resp.Status() != engineapi.RuleStatusPass { return resp } applyCount += count @@ -149,9 +149,9 @@ func (v *validator) validateForEach(ctx context.Context) *engineapi.RuleResponse if v.forEach == nil { return nil } - return internal.RuleSkip(v.rule, engineapi.Validation, "rule skipped") + return engineapi.RuleSkip(v.rule.Name, engineapi.Validation, "rule skipped") } - return internal.RulePass(v.rule, engineapi.Validation, "rule passed") + return engineapi.RulePass(v.rule.Name, engineapi.Validation, "rule passed") } func (v *validator) validateElements(ctx context.Context, foreach kyvernov1.ForEachValidation, elements []interface{}, elementScope *bool) (*engineapi.RuleResponse, int) { @@ -168,38 +168,40 @@ func (v *validator) validateElements(ctx context.Context, foreach kyvernov1.ForE policyContext := v.policyContext.Copy() if err := engineutils.AddElementToContext(policyContext, element, index, v.nesting, elementScope); err != nil { v.log.Error(err, "failed to add element to context") - return internal.RuleError(v.rule, engineapi.Validation, "failed to process foreach", err), applyCount + return engineapi.RuleError(v.rule.Name, engineapi.Validation, "failed to process foreach", err), applyCount } foreachValidator, err := newForEachValidator(foreach, v.contextLoader, v.nesting+1, v.rule, policyContext, v.log) if err != nil { v.log.Error(err, "failed to create foreach validator") - return internal.RuleError(v.rule, engineapi.Validation, "failed to create foreach validator", err), applyCount + return engineapi.RuleError(v.rule.Name, engineapi.Validation, "failed to create foreach validator", err), applyCount } r := foreachValidator.validate(ctx) if r == nil { v.log.V(2).Info("skip rule due to empty result") continue - } else if r.Status == engineapi.RuleStatusSkip { - v.log.V(2).Info("skip rule", "reason", r.Message) + } + status := r.Status() + if status == engineapi.RuleStatusSkip { + v.log.V(2).Info("skip rule", "reason", r.Message()) continue - } else if r.Status != engineapi.RuleStatusPass { - if r.Status == engineapi.RuleStatusError { + } else if status != engineapi.RuleStatusPass { + if status == engineapi.RuleStatusError { if index < len(elements)-1 { continue } - msg := fmt.Sprintf("validation failure: %v", r.Message) - return internal.RuleResponse(v.rule, engineapi.Validation, msg, r.Status), applyCount + msg := fmt.Sprintf("validation failure: %v", r.Message()) + return engineapi.NewRuleResponse(v.rule.Name, engineapi.Validation, msg, status), applyCount } - msg := fmt.Sprintf("validation failure: %v", r.Message) - return internal.RuleResponse(v.rule, engineapi.Validation, msg, r.Status), applyCount + msg := fmt.Sprintf("validation failure: %v", r.Message()) + return engineapi.NewRuleResponse(v.rule.Name, engineapi.Validation, msg, status), applyCount } applyCount++ } - return internal.RulePass(v.rule, engineapi.Validation, ""), applyCount + return engineapi.RulePass(v.rule.Name, engineapi.Validation, ""), applyCount } func (v *validator) loadContext(ctx context.Context) error { @@ -216,12 +218,12 @@ func (v *validator) loadContext(ctx context.Context) error { func (v *validator) validateDeny() *engineapi.RuleResponse { if deny, err := internal.CheckDenyPreconditions(v.log, v.policyContext.JSONContext(), v.deny.GetAnyAllConditions()); err != nil { - return internal.RuleError(v.rule, engineapi.Validation, "failed to check deny preconditions", err) + return engineapi.RuleError(v.rule.Name, engineapi.Validation, "failed to check deny preconditions", err) } else { if deny { - return internal.RuleResponse(v.rule, engineapi.Validation, v.getDenyMessage(deny), engineapi.RuleStatusFail) + return engineapi.RuleFail(v.rule.Name, engineapi.Validation, v.getDenyMessage(deny)) } - return internal.RulePass(v.rule, engineapi.Validation, v.getDenyMessage(deny)) + return engineapi.RulePass(v.rule.Name, engineapi.Validation, v.getDenyMessage(deny)) } } @@ -267,22 +269,22 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *engine v.log.V(3).Info("validation error", "path", pe.Path, "error", err.Error()) if pe.Skip { - return internal.RuleSkip(v.rule, engineapi.Validation, pe.Error()) + return engineapi.RuleSkip(v.rule.Name, engineapi.Validation, pe.Error()) } if pe.Path == "" { - return internal.RuleResponse(v.rule, engineapi.Validation, v.buildErrorMessage(err, ""), engineapi.RuleStatusError) + return engineapi.RuleError(v.rule.Name, engineapi.Validation, v.buildErrorMessage(err, ""), nil) } - return internal.RuleResponse(v.rule, engineapi.Validation, v.buildErrorMessage(err, pe.Path), engineapi.RuleStatusFail) + return engineapi.RuleFail(v.rule.Name, engineapi.Validation, v.buildErrorMessage(err, pe.Path)) } - return internal.RuleResponse(v.rule, engineapi.Validation, v.buildErrorMessage(err, pe.Path), engineapi.RuleStatusError) + return engineapi.RuleError(v.rule.Name, engineapi.Validation, v.buildErrorMessage(err, pe.Path), nil) } v.log.V(4).Info("successfully processed rule") msg := fmt.Sprintf("validation rule '%s' passed.", v.rule.Name) - return internal.RulePass(v.rule, engineapi.Validation, msg) + return engineapi.RulePass(v.rule.Name, engineapi.Validation, msg) } if v.anyPattern != nil { @@ -292,14 +294,14 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *engine anyPatterns, err := deserializeAnyPattern(v.anyPattern) if err != nil { - return internal.RuleError(v.rule, engineapi.Validation, "failed to deserialize anyPattern, expected type array", err) + return engineapi.RuleError(v.rule.Name, engineapi.Validation, "failed to deserialize anyPattern, expected type array", err) } for idx, pattern := range anyPatterns { err := validate.MatchPattern(v.log, resource.Object, pattern) if err == nil { msg := fmt.Sprintf("validation rule '%s' anyPattern[%d] passed.", v.rule.Name, idx) - return internal.RulePass(v.rule, engineapi.Validation, msg) + return engineapi.RulePass(v.rule.Name, engineapi.Validation, msg) } if pe, ok := err.(*validate.PatternError); ok { @@ -327,7 +329,7 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *engine errorStr = append(errorStr, err.Error()) } v.log.V(4).Info(fmt.Sprintf("Validation rule '%s' skipped. %s", v.rule.Name, errorStr)) - return internal.RuleSkip(v.rule, engineapi.Validation, strings.Join(errorStr, " ")) + return engineapi.RuleSkip(v.rule.Name, engineapi.Validation, strings.Join(errorStr, " ")) } else if len(failedAnyPatternsErrors) > 0 { var errorStr []string for _, err := range failedAnyPatternsErrors { @@ -336,11 +338,11 @@ func (v *validator) validatePatterns(resource unstructured.Unstructured) *engine v.log.V(4).Info(fmt.Sprintf("Validation rule '%s' failed. %s", v.rule.Name, errorStr)) msg := buildAnyPatternErrorMessage(v.rule, errorStr) - return internal.RuleResponse(v.rule, engineapi.Validation, msg, engineapi.RuleStatusFail) + return engineapi.RuleFail(v.rule.Name, engineapi.Validation, msg) } } - return internal.RulePass(v.rule, engineapi.Validation, v.rule.Validation.Message) + return engineapi.RulePass(v.rule.Name, engineapi.Validation, v.rule.Validation.Message) } func deserializeAnyPattern(anyPattern apiextensions.JSON) ([]interface{}, error) { diff --git a/pkg/engine/image_verify.go b/pkg/engine/image_verify.go index 8424f16e7b..513d2b03dc 100644 --- a/pkg/engine/image_verify.go +++ b/pkg/engine/image_verify.go @@ -53,10 +53,9 @@ func (e *engine) verifyAndPatchImages( engineapi.ImageVerify, ) matchedResource = resource + endTime := time.Now() for _, ruleResp := range ruleResp { - ruleResp := ruleResp - internal.AddRuleResponse(&resp, &ruleResp, startTime) - logger.V(4).Info("finished processing rule", "processingTime", ruleResp.Stats.ProcessingTime.String()) + resp.Add(startTime, endTime, ruleResp) } if applyRules == kyvernov1.ApplyOne && resp.Stats.RulesAppliedCount > 0 { break diff --git a/pkg/engine/image_verify_test.go b/pkg/engine/image_verify_test.go index 4e2c189df5..e69188f1e5 100644 --- a/pkg/engine/image_verify_test.go +++ b/pkg/engine/image_verify_test.go @@ -194,9 +194,9 @@ func Test_CosignMockAttest(t *testing.T) { er, ivm := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass, fmt.Sprintf("expected: %v, got: %v, failure: %v", - engineapi.RuleStatusPass, er.PolicyResponse.Rules[0].Status, er.PolicyResponse.Rules[0].Message)) + engineapi.RuleStatusPass, er.PolicyResponse.Rules[0].Status(), er.PolicyResponse.Rules[0].Message())) assert.Equal(t, ivm.IsEmpty(), false) assert.Equal(t, ivm.IsVerified("ghcr.io/jimbugwadia/pause2:latest"), true) } @@ -208,7 +208,7 @@ func Test_CosignMockAttest_fail(t *testing.T) { er, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail) } func buildContext(t *testing.T, policy, resource string, oldResource string) *PolicyContext { @@ -465,7 +465,7 @@ func Test_ConfigMapMissingFailure(t *testing.T) { cosign.ClearMock() resp, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), resolver, policyContext, cfg) assert.Equal(t, len(resp.PolicyResponse.Rules), 1) - assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError, resp.PolicyResponse.Rules[0].Message) + assert.Equal(t, resp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusError, resp.PolicyResponse.Rules[0].Message()) } func Test_SignatureGoodSigned(t *testing.T) { @@ -474,9 +474,9 @@ func Test_SignatureGoodSigned(t *testing.T) { cosign.ClearMock() engineResp, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) - assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message) - assert.Equal(t, len(engineResp.PolicyResponse.Rules[0].Patches), 1) - patch := engineResp.PolicyResponse.Rules[0].Patches[0] + assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message()) + assert.Equal(t, len(engineResp.PolicyResponse.Rules[0].Patches()), 1) + patch := engineResp.PolicyResponse.Rules[0].Patches()[0] assert.Equal(t, string(patch), "{\"op\":\"replace\",\"path\":\"/spec/containers/0/image\",\"value\":\"ghcr.io/kyverno/test-verify-image:signed@sha256:b31bfb4d0213f254d361e0079deaaebefa4f82ba7aa76ef82e90b4935ad5b105\"}") } @@ -486,7 +486,7 @@ func Test_SignatureUnsigned(t *testing.T) { policyContext := buildContext(t, testSampleSingleKeyPolicy, unsigned, "") engineResp, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) - assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message) + assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message()) } func Test_SignatureWrongKey(t *testing.T) { @@ -495,7 +495,7 @@ func Test_SignatureWrongKey(t *testing.T) { policyContext := buildContext(t, testSampleSingleKeyPolicy, otherKey, "") engineResp, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) - assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message) + assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message()) } func Test_SignaturesMultiKey(t *testing.T) { @@ -506,7 +506,7 @@ func Test_SignaturesMultiKey(t *testing.T) { policyContext := buildContext(t, policy, testSampleResource, "") engineResp, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) - assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message) + assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message()) } func Test_SignaturesMultiKeyFail(t *testing.T) { @@ -516,7 +516,7 @@ func Test_SignaturesMultiKeyFail(t *testing.T) { policyContext := buildContext(t, policy, testSampleResource, "") engineResp, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) - assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message) + assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail, engineResp.PolicyResponse.Rules[0].Message()) } func Test_SignaturesMultiKeyOneGoodKey(t *testing.T) { @@ -527,7 +527,7 @@ func Test_SignaturesMultiKeyOneGoodKey(t *testing.T) { policyContext := buildContext(t, policy, testSampleResource, "") engineResp, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(engineResp.PolicyResponse.Rules), 1) - assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message) + assert.Equal(t, engineResp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass, engineResp.PolicyResponse.Rules[0].Message()) } func Test_SignaturesMultiKeyZeroGoodKey(t *testing.T) { @@ -538,7 +538,7 @@ func Test_SignaturesMultiKeyZeroGoodKey(t *testing.T) { policyContext := buildContext(t, policy, testSampleResource, "") resp, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(resp.PolicyResponse.Rules), 1) - assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail, resp.PolicyResponse.Rules[0].Message) + assert.Equal(t, resp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail, resp.PolicyResponse.Rules[0].Message()) } func Test_RuleSelectorImageVerify(t *testing.T) { @@ -554,14 +554,14 @@ func Test_RuleSelectorImageVerify(t *testing.T) { resp, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(resp.PolicyResponse.Rules), 2) - assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, resp.PolicyResponse.Rules[0].Message) - assert.Equal(t, resp.PolicyResponse.Rules[1].Status, engineapi.RuleStatusFail, resp.PolicyResponse.Rules[1].Message) + assert.Equal(t, resp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass, resp.PolicyResponse.Rules[0].Message()) + assert.Equal(t, resp.PolicyResponse.Rules[1].Status(), engineapi.RuleStatusFail, resp.PolicyResponse.Rules[1].Message()) applyOne := kyverno.ApplyOne spec.ApplyRules = &applyOne resp, _ = testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(resp.PolicyResponse.Rules), 1) - assert.Equal(t, resp.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass, resp.PolicyResponse.Rules[0].Message) + assert.Equal(t, resp.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass, resp.PolicyResponse.Rules[0].Message()) } func newStaticKeyRule(name, imageReference, key string) *kyverno.Rule { @@ -665,7 +665,7 @@ func Test_NestedAttestors(t *testing.T) { policyContext := buildContext(t, policy, testSampleResource, "") err, _ := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(err.PolicyResponse.Rules), 1) - assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) + assert.Equal(t, err.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass) policy = strings.Replace(testNestedAttestorPolicy, "KEY1", testVerifyImageKey, -1) policy = strings.Replace(policy, "KEY2", testOtherKey, -1) @@ -673,7 +673,7 @@ func Test_NestedAttestors(t *testing.T) { policyContext = buildContext(t, policy, testSampleResource, "") err, _ = testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(err.PolicyResponse.Rules), 1) - assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) + assert.Equal(t, err.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail) policy = strings.Replace(testNestedAttestorPolicy, "KEY1", testVerifyImageKey, -1) policy = strings.Replace(policy, "KEY2", testOtherKey, -1) @@ -681,7 +681,7 @@ func Test_NestedAttestors(t *testing.T) { policyContext = buildContext(t, policy, testSampleResource, "") err, _ = testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(err.PolicyResponse.Rules), 1) - assert.Equal(t, err.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) + assert.Equal(t, err.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass) } func Test_ExpandKeys(t *testing.T) { @@ -773,7 +773,7 @@ func Test_MarkImageVerified(t *testing.T) { engineResponse, verifiedImages := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1) - assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) + assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass) assert.Assert(t, verifiedImages.Data != nil) assert.Equal(t, len(verifiedImages.Data), 1) @@ -864,7 +864,7 @@ func Test_ParsePEMDelimited(t *testing.T) { engineResponse, verifiedImages := testVerifyAndPatchImages(context.TODO(), registryclient.NewOrDie(), nil, policyContext, cfg) assert.Equal(t, len(engineResponse.PolicyResponse.Rules), 1) - assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) + assert.Equal(t, engineResponse.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass) assert.Assert(t, verifiedImages.Data != nil) assert.Equal(t, len(verifiedImages.Data), 1) diff --git a/pkg/engine/internal/imageverifier.go b/pkg/engine/internal/imageverifier.go index 136f204e1f..2e0d46dac4 100644 --- a/pkg/engine/internal/imageverifier.go +++ b/pkg/engine/internal/imageverifier.go @@ -197,7 +197,7 @@ func (iv *ImageVerifier) Verify( if HasImageVerifiedAnnotationChanged(iv.policyContext, iv.logger) { msg := engineapi.ImageVerifyAnnotationKey + " annotation cannot be changed" iv.logger.Info("image verification error", "reason", msg) - responses = append(responses, RuleResponse(iv.rule, engineapi.ImageVerify, msg, engineapi.RuleStatusFail)) + responses = append(responses, engineapi.RuleFail(iv.rule.Name, engineapi.ImageVerify, msg)) continue } @@ -219,12 +219,12 @@ func (iv *ImageVerifier) Verify( if imageVerify.MutateDigest { patch, retrievedDigest, err := iv.handleMutateDigest(ctx, digest, imageInfo) if err != nil { - responses = append(responses, RuleError(iv.rule, engineapi.ImageVerify, "failed to update digest", err)) + responses = append(responses, engineapi.RuleError(iv.rule.Name, engineapi.ImageVerify, "failed to update digest", err)) } else if patch != nil { if ruleResp == nil { - ruleResp = RulePass(iv.rule, engineapi.ImageVerify, "mutated image digest") + ruleResp = engineapi.RulePass(iv.rule.Name, engineapi.ImageVerify, "mutated image digest") } - ruleResp.Patches = append(ruleResp.Patches, patch) + ruleResp = ruleResp.WithPatches(patch) imageInfo.Digest = retrievedDigest image = imageInfo.String() } @@ -232,7 +232,7 @@ func (iv *ImageVerifier) Verify( if ruleResp != nil { if len(imageVerify.Attestors) > 0 || len(imageVerify.Attestations) > 0 { - iv.ivm.Add(image, ruleResp.Status == engineapi.RuleStatusPass) + iv.ivm.Add(image, ruleResp.Status() == engineapi.RuleStatusPass) } responses = append(responses, ruleResp) } @@ -253,14 +253,14 @@ func (iv *ImageVerifier) verifyImage( iv.logger.V(2).Info("verifying image signatures", "image", image, "attestors", len(imageVerify.Attestors), "attestations", len(imageVerify.Attestations)) if err := iv.policyContext.JSONContext().AddImageInfo(imageInfo, cfg); err != nil { iv.logger.Error(err, "failed to add image to context") - return RuleError(iv.rule, engineapi.ImageVerify, fmt.Sprintf("failed to add image to context %s", image), err), "" + return engineapi.RuleError(iv.rule.Name, engineapi.ImageVerify, fmt.Sprintf("failed to add image to context %s", image), err), "" } if len(imageVerify.Attestors) > 0 { if !matchImageReferences(imageVerify.ImageReferences, image) { return nil, "" } ruleResp, cosignResp := iv.verifyAttestors(ctx, imageVerify.Attestors, imageVerify, imageInfo, "") - if ruleResp.Status != engineapi.RuleStatusPass { + if ruleResp.Status() != engineapi.RuleStatusPass { return ruleResp, "" } if len(imageVerify.Attestations) == 0 { @@ -299,10 +299,10 @@ func (iv *ImageVerifier) verifyAttestors( } } if cosignResponse == nil { - return RuleError(iv.rule, engineapi.ImageVerify, "invalid response", fmt.Errorf("nil")), nil + return engineapi.RuleError(iv.rule.Name, engineapi.ImageVerify, "invalid response", fmt.Errorf("nil")), nil } msg := fmt.Sprintf("verified image signatures for %s", image) - return RulePass(iv.rule, engineapi.ImageVerify, msg), cosignResponse + return engineapi.RulePass(iv.rule.Name, engineapi.ImageVerify, msg), cosignResponse } // handle registry network errors as a rule error (instead of a policy failure) @@ -310,9 +310,9 @@ func (iv *ImageVerifier) handleRegistryErrors(image string, err error) *engineap msg := fmt.Sprintf("failed to verify image %s: %s", image, err.Error()) var netErr *net.OpError if errors.As(err, &netErr) { - return RuleError(iv.rule, engineapi.ImageVerify, fmt.Sprintf("failed to verify image %s", image), err) + return engineapi.RuleError(iv.rule.Name, engineapi.ImageVerify, fmt.Sprintf("failed to verify image %s", image), err) } - return RuleResponse(iv.rule, engineapi.ImageVerify, msg, engineapi.RuleStatusFail) + return engineapi.RuleFail(iv.rule.Name, engineapi.ImageVerify, msg) } func (iv *ImageVerifier) verifyAttestations( @@ -326,7 +326,7 @@ func (iv *ImageVerifier) verifyAttestations( path := fmt.Sprintf(".attestations[%d]", i) if attestation.PredicateType == "" { - return RuleResponse(iv.rule, engineapi.ImageVerify, path+": missing predicateType", engineapi.RuleStatusFail), "" + return engineapi.RuleFail(iv.rule.Name, engineapi.ImageVerify, path+": missing predicateType"), "" } if len(attestation.Attestors) == 0 { @@ -356,7 +356,7 @@ func (iv *ImageVerifier) verifyAttestations( attestationError = iv.verifyAttestation(cosignResp.Statements, attestation, imageInfo) if attestationError != nil { attestationError = fmt.Errorf("%s: %w", entryPath+subPath, attestationError) - return RuleResponse(iv.rule, engineapi.ImageVerify, attestationError.Error(), engineapi.RuleStatusFail), "" + return engineapi.RuleFail(iv.rule.Name, engineapi.ImageVerify, attestationError.Error()), "" } verifiedCount++ @@ -368,7 +368,7 @@ func (iv *ImageVerifier) verifyAttestations( if verifiedCount < requiredCount { msg := fmt.Sprintf("image attestations verification failed, verifiedCount: %v, requiredCount: %v", verifiedCount, requiredCount) - return RuleResponse(iv.rule, engineapi.ImageVerify, msg, engineapi.RuleStatusFail), "" + return engineapi.RuleFail(iv.rule.Name, engineapi.ImageVerify, msg), "" } } @@ -377,7 +377,7 @@ func (iv *ImageVerifier) verifyAttestations( msg := fmt.Sprintf("verified image attestations for %s", image) iv.logger.V(2).Info(msg) - return RulePass(iv.rule, engineapi.ImageVerify, msg), imageInfo.Digest + return engineapi.RulePass(iv.rule.Name, engineapi.ImageVerify, msg), imageInfo.Digest } func (iv *ImageVerifier) verifyAttestorSet( diff --git a/pkg/engine/internal/response.go b/pkg/engine/internal/response.go deleted file mode 100644 index 38fd9e5041..0000000000 --- a/pkg/engine/internal/response.go +++ /dev/null @@ -1,56 +0,0 @@ -package internal - -import ( - "fmt" - "time" - - kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" - engineapi "github.com/kyverno/kyverno/pkg/engine/api" -) - -func RuleError(rule kyvernov1.Rule, ruleType engineapi.RuleType, msg string, err error) *engineapi.RuleResponse { - return RuleResponse(rule, ruleType, fmt.Sprintf("%s: %s", msg, err.Error()), engineapi.RuleStatusError) -} - -func RuleSkip(rule kyvernov1.Rule, ruleType engineapi.RuleType, msg string) *engineapi.RuleResponse { - return RuleResponse(rule, ruleType, msg, engineapi.RuleStatusSkip) -} - -func RulePass(rule kyvernov1.Rule, ruleType engineapi.RuleType, msg string) *engineapi.RuleResponse { - return RuleResponse(rule, ruleType, msg, engineapi.RuleStatusPass) -} - -func RuleResponse(rule kyvernov1.Rule, ruleType engineapi.RuleType, msg string, status engineapi.RuleStatus) *engineapi.RuleResponse { - resp := &engineapi.RuleResponse{ - Name: rule.Name, - Type: ruleType, - Message: msg, - Status: status, - } - return resp -} - -func AddRuleResponse(resp *engineapi.PolicyResponse, ruleResp *engineapi.RuleResponse, startTime time.Time) { - ruleResp.Stats.ProcessingTime = time.Since(startTime) - ruleResp.Stats.Timestamp = startTime.Unix() - resp.Rules = append(resp.Rules, *ruleResp) - if ruleResp.Status == engineapi.RuleStatusPass || ruleResp.Status == engineapi.RuleStatusFail { - resp.Stats.RulesAppliedCount++ - } else if ruleResp.Status == engineapi.RuleStatusError { - resp.Stats.RulesErrorCount++ - } -} - -func BuildResponse(ctx engineapi.PolicyContext, resp *engineapi.EngineResponse, startTime time.Time) *engineapi.EngineResponse { - if resp.PatchedResource.Object == nil { - // for delete requests patched resource will be oldResource since newResource is empty - resource := ctx.NewResource() - if resource.Object == nil { - resource = ctx.OldResource() - } - resp.PatchedResource = resource - } - resp.Stats.ProcessingTime = time.Since(startTime) - resp.Stats.Timestamp = startTime.Unix() - return resp -} diff --git a/pkg/engine/metrics.go b/pkg/engine/metrics.go index 641ab08834..2e102d449a 100644 --- a/pkg/engine/metrics.go +++ b/pkg/engine/metrics.go @@ -35,10 +35,10 @@ func (e *engine) reportMetrics( resourceKind := resourceSpec.GetKind() resourceNamespace := resourceSpec.GetNamespace() for _, rule := range response.PolicyResponse.Rules { - ruleName := rule.Name + ruleName := rule.Name() ruleType := metrics.ParseRuleTypeFromEngineRuleResponse(rule) var ruleResult metrics.RuleResult - switch rule.Status { + switch rule.Status() { case engineapi.RuleStatusPass: ruleResult = metrics.Pass case engineapi.RuleStatusFail: @@ -88,7 +88,7 @@ func (e *engine) reportMetrics( attribute.String("rule_type", string(ruleType)), attribute.String("rule_execution_cause", string(executionCause)), } - e.durationHistogram.Record(ctx, rule.Stats.ProcessingTime.Seconds(), commonLabels...) + e.durationHistogram.Record(ctx, rule.Stats().ProcessingTime.Seconds(), commonLabels...) } } } diff --git a/pkg/engine/mutate/mutation.go b/pkg/engine/mutate/mutation.go index 5186cb13f2..fe997dbdc6 100644 --- a/pkg/engine/mutate/mutation.go +++ b/pkg/engine/mutate/mutation.go @@ -48,11 +48,11 @@ func Mutate(rule *kyvernov1.Rule, ctx context.Interface, resource unstructured.U } resp, patchedResource := patcher.Patch() - if resp.Status != engineapi.RuleStatusPass { - return NewResponse(resp.Status, resource, nil, resp.Message) + if resp.Status() != engineapi.RuleStatusPass { + return NewResponse(resp.Status(), resource, nil, resp.Message()) } - if resp.Patches == nil { + if resp.Patches() == nil { return NewResponse(engineapi.RuleStatusSkip, resource, nil, "no patches applied") } @@ -66,7 +66,7 @@ func Mutate(rule *kyvernov1.Rule, ctx context.Interface, resource unstructured.U } } - return NewResponse(engineapi.RuleStatusPass, patchedResource, resp.Patches, resp.Message) + return NewResponse(engineapi.RuleStatusPass, patchedResource, resp.Patches(), resp.Message()) } func ForEach(name string, foreach kyvernov1.ForEachMutation, policyContext engineapi.PolicyContext, resource unstructured.Unstructured, element interface{}, logger logr.Logger) *Response { @@ -82,11 +82,11 @@ func ForEach(name string, foreach kyvernov1.ForEachMutation, policyContext engin } resp, patchedResource := patcher.Patch() - if resp.Status != engineapi.RuleStatusPass { - return NewResponse(resp.Status, unstructured.Unstructured{}, nil, resp.Message) + if resp.Status() != engineapi.RuleStatusPass { + return NewResponse(resp.Status(), unstructured.Unstructured{}, nil, resp.Message()) } - if resp.Patches == nil { + if resp.Patches() == nil { return NewResponse(engineapi.RuleStatusSkip, unstructured.Unstructured{}, nil, "no patches applied") } @@ -94,7 +94,7 @@ func ForEach(name string, foreach kyvernov1.ForEachMutation, policyContext engin return NewErrorResponse("failed to update patched resource in the JSON context", err) } - return NewResponse(engineapi.RuleStatusPass, patchedResource, resp.Patches, resp.Message) + return NewResponse(engineapi.RuleStatusPass, patchedResource, resp.Patches(), resp.Message()) } func substituteAllInForEach(fe kyvernov1.ForEachMutation, ctx context.Interface, logger logr.Logger) (*kyvernov1.ForEachMutation, error) { diff --git a/pkg/engine/mutate/mutation_test.go b/pkg/engine/mutate/mutation_test.go index 223b4186b0..280e0b28a8 100644 --- a/pkg/engine/mutate/mutation_test.go +++ b/pkg/engine/mutate/mutation_test.go @@ -48,20 +48,10 @@ const endpointsDocument string = `{ func applyPatches(rule *types.Rule, resource unstructured.Unstructured) (*engineapi.RuleResponse, unstructured.Unstructured) { mutateResp := Mutate(rule, context.NewContext(), resource, logr.Discard()) - if mutateResp.Status != engineapi.RuleStatusPass { - return &engineapi.RuleResponse{ - Type: engineapi.Mutation, - Status: mutateResp.Status, - Message: mutateResp.Message, - }, resource + return engineapi.NewRuleResponse("", engineapi.Mutation, mutateResp.Message, mutateResp.Status), resource } - - return &engineapi.RuleResponse{ - Type: engineapi.Mutation, - Status: engineapi.RuleStatusPass, - Patches: mutateResp.Patches, - }, mutateResp.PatchedResource + return engineapi.RulePass("", engineapi.Mutation, mutateResp.Message).WithPatches(mutateResp.Patches...), mutateResp.PatchedResource } func TestProcessPatches_EmptyPatches(t *testing.T) { @@ -72,8 +62,8 @@ func TestProcessPatches_EmptyPatches(t *testing.T) { } rr, _ := applyPatches(emptyRule, *resourceUnstructured) - assert.Equal(t, rr.Status, engineapi.RuleStatusError) - assert.Assert(t, len(rr.Patches) == 0) + assert.Equal(t, rr.Status(), engineapi.RuleStatusError) + assert.Assert(t, len(rr.Patches()) == 0) } func makeAddIsMutatedLabelPatch() jsonPatch { @@ -106,15 +96,15 @@ func makeRuleWithPatches(t *testing.T, patches []jsonPatch) *types.Rule { func TestProcessPatches_EmptyDocument(t *testing.T) { rule := makeRuleWithPatch(t, makeAddIsMutatedLabelPatch()) rr, _ := applyPatches(rule, unstructured.Unstructured{}) - assert.Equal(t, rr.Status, engineapi.RuleStatusFail) - assert.Assert(t, len(rr.Patches) == 0) + assert.Equal(t, rr.Status(), engineapi.RuleStatusFail) + assert.Assert(t, len(rr.Patches()) == 0) } func TestProcessPatches_AllEmpty(t *testing.T) { emptyRule := &types.Rule{} rr, _ := applyPatches(emptyRule, unstructured.Unstructured{}) - assert.Equal(t, rr.Status, engineapi.RuleStatusError) - assert.Assert(t, len(rr.Patches) == 0) + assert.Equal(t, rr.Status(), engineapi.RuleStatusError) + assert.Assert(t, len(rr.Patches()) == 0) } func TestProcessPatches_AddPathDoesntExist(t *testing.T) { @@ -126,8 +116,8 @@ func TestProcessPatches_AddPathDoesntExist(t *testing.T) { t.Error(err) } rr, _ := applyPatches(rule, *resourceUnstructured) - assert.Equal(t, rr.Status, engineapi.RuleStatusSkip) - assert.Assert(t, len(rr.Patches) == 0) + assert.Equal(t, rr.Status(), engineapi.RuleStatusSkip) + assert.Assert(t, len(rr.Patches()) == 0) } func TestProcessPatches_RemovePathDoesntExist(t *testing.T) { @@ -138,8 +128,8 @@ func TestProcessPatches_RemovePathDoesntExist(t *testing.T) { t.Error(err) } rr, _ := applyPatches(rule, *resourceUnstructured) - assert.Equal(t, rr.Status, engineapi.RuleStatusSkip) - assert.Assert(t, len(rr.Patches) == 0) + assert.Equal(t, rr.Status(), engineapi.RuleStatusSkip) + assert.Assert(t, len(rr.Patches()) == 0) } func TestProcessPatches_AddAndRemovePathsDontExist_EmptyResult(t *testing.T) { @@ -151,8 +141,8 @@ func TestProcessPatches_AddAndRemovePathsDontExist_EmptyResult(t *testing.T) { t.Error(err) } rr, _ := applyPatches(rule, *resourceUnstructured) - assert.Equal(t, rr.Status, engineapi.RuleStatusPass) - assert.Equal(t, len(rr.Patches), 1) + assert.Equal(t, rr.Status(), engineapi.RuleStatusPass) + assert.Equal(t, len(rr.Patches()), 1) } func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_NotEmptyResult(t *testing.T) { @@ -166,9 +156,9 @@ func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_NotEmptyResul } rr, _ := applyPatches(rule, *resourceUnstructured) - assert.Equal(t, rr.Status, engineapi.RuleStatusPass) - assert.Assert(t, len(rr.Patches) != 0) - assertEqStringAndData(t, `{"path":"/metadata/labels/label3","op":"add","value":"label3Value"}`, rr.Patches[0]) + assert.Equal(t, rr.Status(), engineapi.RuleStatusPass) + assert.Assert(t, len(rr.Patches()) != 0) + assertEqStringAndData(t, `{"path":"/metadata/labels/label3","op":"add","value":"label3Value"}`, rr.Patches()[0]) } func TestProcessPatches_RemovePathDoesntExist_EmptyResult(t *testing.T) { @@ -179,8 +169,8 @@ func TestProcessPatches_RemovePathDoesntExist_EmptyResult(t *testing.T) { t.Error(err) } rr, _ := applyPatches(rule, *resourceUnstructured) - assert.Equal(t, rr.Status, engineapi.RuleStatusSkip) - assert.Assert(t, len(rr.Patches) == 0) + assert.Equal(t, rr.Status(), engineapi.RuleStatusSkip) + assert.Assert(t, len(rr.Patches()) == 0) } func TestProcessPatches_RemovePathDoesntExist_NotEmptyResult(t *testing.T) { @@ -192,9 +182,9 @@ func TestProcessPatches_RemovePathDoesntExist_NotEmptyResult(t *testing.T) { t.Error(err) } rr, _ := applyPatches(rule, *resourceUnstructured) - assert.Equal(t, rr.Status, engineapi.RuleStatusPass) - assert.Assert(t, len(rr.Patches) == 1) - assertEqStringAndData(t, `{"path":"/metadata/labels/label2","op":"add","value":"label2Value"}`, rr.Patches[0]) + assert.Equal(t, rr.Status(), engineapi.RuleStatusPass) + assert.Assert(t, len(rr.Patches()) == 1) + assertEqStringAndData(t, `{"path":"/metadata/labels/label2","op":"add","value":"label2Value"}`, rr.Patches()[0]) } func assertEqStringAndData(t *testing.T, str string, data []byte) { diff --git a/pkg/engine/mutate/patch/patchJSON6902.go b/pkg/engine/mutate/patch/patchJSON6902.go index f241a7f4be..ccfa8b4c2f 100644 --- a/pkg/engine/mutate/patch/patchJSON6902.go +++ b/pkg/engine/mutate/patch/patchJSON6902.go @@ -12,58 +12,42 @@ import ( ) // ProcessPatchJSON6902 ... -func ProcessPatchJSON6902(ruleName string, patchesJSON6902 []byte, resource unstructured.Unstructured, log logr.Logger) (resp engineapi.RuleResponse, patchedResource unstructured.Unstructured) { +func ProcessPatchJSON6902(ruleName string, patchesJSON6902 []byte, resource unstructured.Unstructured, log logr.Logger) (engineapi.RuleResponse, unstructured.Unstructured) { logger := log.WithValues("rule", ruleName) startTime := time.Now() logger.V(4).Info("started JSON6902 patch", "startTime", startTime) - resp.Name = ruleName - resp.Type = engineapi.Mutation defer func() { - resp.Stats.ProcessingTime = time.Since(startTime) - resp.Stats.Timestamp = startTime.Unix() - logger.V(4).Info("applied JSON6902 patch", "processingTime", resp.Stats.ProcessingTime.String()) + logger.V(4).Info("applied JSON6902 patch", "processingTime", time.Since(startTime)) }() - resourceRaw, err := resource.MarshalJSON() if err != nil { - resp.Status = engineapi.RuleStatusFail logger.Error(err, "failed to marshal resource") - resp.Message = fmt.Sprintf("failed to marshal resource: %v", err) - return resp, resource + return *engineapi.RuleFail(ruleName, engineapi.Mutation, fmt.Sprintf("failed to marshal resource: %v", err)), resource } - patchedResourceRaw, err := applyPatchesWithOptions(resourceRaw, patchesJSON6902) if err != nil { - resp.Status = engineapi.RuleStatusFail logger.Error(err, "failed to apply JSON Patch") - resp.Message = fmt.Sprintf("failed to apply JSON Patch: %v", err) - return resp, resource + return *engineapi.RuleFail(ruleName, engineapi.Mutation, fmt.Sprintf("failed to apply JSON Patch: %v", err)), resource } - patchesBytes, err := generatePatches(resourceRaw, patchedResourceRaw) if err != nil { - resp.Status = engineapi.RuleStatusFail logger.Error(err, "unable generate patch bytes from base and patched document, apply patchesJSON6902 directly") - resp.Message = fmt.Sprintf("unable generate patch bytes from base and patched document, apply patchesJSON6902 directly: %v", err) - return resp, resource + return *engineapi.RuleFail( + ruleName, + engineapi.Mutation, + fmt.Sprintf("unable generate patch bytes from base and patched document, apply patchesJSON6902 directly: %v", err), + ), resource } - for _, p := range patchesBytes { log.V(4).Info("generated JSON Patch (RFC 6902)", "patch", string(p)) } - + var patchedResource unstructured.Unstructured err = patchedResource.UnmarshalJSON(patchedResourceRaw) if err != nil { logger.Error(err, "failed to unmarshal resource") - resp.Status = engineapi.RuleStatusFail - resp.Message = fmt.Sprintf("failed to unmarshal resource: %v", err) - return resp, resource + return *engineapi.RuleFail(ruleName, engineapi.Mutation, fmt.Sprintf("failed to unmarshal resource: %v", err)), resource } - - resp.Status = engineapi.RuleStatusPass - resp.Message = string("applied JSON Patch") - resp.Patches = patchesBytes - return resp, patchedResource + return *engineapi.RulePass(ruleName, engineapi.Mutation, "applied JSON Patch").WithPatches(patchesBytes...), patchedResource } func applyPatchesWithOptions(resource, patch []byte) ([]byte, error) { diff --git a/pkg/engine/mutate/patch/patchJSON6902_test.go b/pkg/engine/mutate/patch/patchJSON6902_test.go index 4d755682f1..0fbadc762d 100644 --- a/pkg/engine/mutate/patch/patchJSON6902_test.go +++ b/pkg/engine/mutate/patch/patchJSON6902_test.go @@ -51,12 +51,12 @@ func TestTypeConversion(t *testing.T) { assert.Nil(t, err) // apply patches resp, _ := ProcessPatchJSON6902("type-conversion", jsonPatches, resource, logr.Discard()) - if !assert.Equal(t, engineapi.RuleStatusPass, resp.Status) { - t.Fatal(resp.Message) + if !assert.Equal(t, engineapi.RuleStatusPass, resp.Status()) { + t.Fatal(resp.Message()) } - assert.Equal(t, expectedPatches, resp.Patches, - fmt.Sprintf("expectedPatches: %s\ngeneratedPatches: %s", string(expectedPatches[0]), string(resp.Patches[0]))) + assert.Equal(t, expectedPatches, resp.Patches(), + fmt.Sprintf("expectedPatches: %s\ngeneratedPatches: %s", string(expectedPatches[0]), string(resp.Patches()[0]))) } func TestJsonPatch(t *testing.T) { diff --git a/pkg/engine/mutate/patch/patches.go b/pkg/engine/mutate/patch/patches.go index 53f0ca832e..89b572c6be 100644 --- a/pkg/engine/mutate/patch/patches.go +++ b/pkg/engine/mutate/patch/patches.go @@ -50,16 +50,16 @@ func NewPatchesJSON6902(ruleName string, patches string, patchedResource unstruc } } -func (h patchesJSON6902Handler) Patch() (resp engineapi.RuleResponse, patchedResource unstructured.Unstructured) { - resp.Name = h.ruleName - resp.Type = engineapi.Mutation - +func (h patchesJSON6902Handler) Patch() (engineapi.RuleResponse, unstructured.Unstructured) { patchesJSON6902, err := ConvertPatchesToJSON(h.patches) if err != nil { - resp.Status = engineapi.RuleStatusFail + resp := engineapi.RuleFail( + h.ruleName, + engineapi.Mutation, + err.Error(), + ) h.logger.Error(err, "error in type conversion") - resp.Message = err.Error() - return resp, unstructured.Unstructured{} + return *resp, unstructured.Unstructured{} } return ProcessPatchJSON6902(h.ruleName, patchesJSON6902, h.patchedResource, h.logger) diff --git a/pkg/engine/mutate/patch/strategicMergePatch.go b/pkg/engine/mutate/patch/strategicMergePatch.go index fe22291e14..a78591910c 100644 --- a/pkg/engine/mutate/patch/strategicMergePatch.go +++ b/pkg/engine/mutate/patch/strategicMergePatch.go @@ -15,49 +15,38 @@ import ( ) // ProcessStrategicMergePatch ... -func ProcessStrategicMergePatch(ruleName string, overlay interface{}, resource unstructured.Unstructured, log logr.Logger) (resp engineapi.RuleResponse, patchedResource unstructured.Unstructured) { +func ProcessStrategicMergePatch(ruleName string, overlay interface{}, resource unstructured.Unstructured, log logr.Logger) (engineapi.RuleResponse, unstructured.Unstructured) { startTime := time.Now() logger := log.WithName("ProcessStrategicMergePatch").WithValues("rule", ruleName) logger.V(4).Info("started applying strategicMerge patch", "startTime", startTime) - resp.Name = ruleName - resp.Type = engineapi.Mutation defer func() { - resp.Stats.ProcessingTime = time.Since(startTime) - resp.Stats.Timestamp = startTime.Unix() - logger.V(4).Info("finished applying strategicMerge patch", "processingTime", resp.Stats.ProcessingTime.String()) + logger.V(4).Info("finished applying strategicMerge patch", "processingTime", time.Since(startTime)) }() overlayBytes, err := json.Marshal(overlay) if err != nil { - resp.Status = engineapi.RuleStatusFail logger.Error(err, "failed to marshal resource") - resp.Message = fmt.Sprintf("failed to process patchStrategicMerge: %v", err) - return resp, resource + return *engineapi.RuleFail(ruleName, engineapi.Mutation, fmt.Sprintf("failed to process patchStrategicMerge: %v", err)), resource } base, err := json.Marshal(resource.Object) if err != nil { - resp.Status = engineapi.RuleStatusFail logger.Error(err, "failed to marshal resource") - resp.Message = fmt.Sprintf("failed to process patchStrategicMerge: %v", err) - return resp, resource + return *engineapi.RuleFail(ruleName, engineapi.Mutation, fmt.Sprintf("failed to process patchStrategicMerge: %v", err)), resource } patchedBytes, err := strategicMergePatch(logger, string(base), string(overlayBytes)) if err != nil { log.Error(err, "failed to apply patchStrategicMerge") msg := fmt.Sprintf("failed to apply patchStrategicMerge: %v", err) - resp.Status = engineapi.RuleStatusFail - resp.Message = msg - return resp, resource + return *engineapi.RuleFail(ruleName, engineapi.Mutation, msg), resource } + var patchedResource unstructured.Unstructured err = patchedResource.UnmarshalJSON(patchedBytes) if err != nil { logger.Error(err, "failed to unmarshal resource") - resp.Status = engineapi.RuleStatusFail - resp.Message = fmt.Sprintf("failed to process patchStrategicMerge: %v", err) - return resp, resource + return *engineapi.RuleFail(ruleName, engineapi.Mutation, fmt.Sprintf("failed to process patchStrategicMerge: %v", err)), resource } log.V(6).Info("generating JSON patches from patched resource", "patchedResource", patchedResource.Object) @@ -65,20 +54,15 @@ func ProcessStrategicMergePatch(ruleName string, overlay interface{}, resource u jsonPatches, err := generatePatches(base, patchedBytes) if err != nil { msg := fmt.Sprintf("failed to generated JSON patches from patched resource: %v", err.Error()) - resp.Status = engineapi.RuleStatusFail log.V(2).Info(msg) - resp.Message = msg - return resp, patchedResource + return *engineapi.RuleFail(ruleName, engineapi.Mutation, msg), patchedResource } for _, p := range jsonPatches { log.V(5).Info("generated patch", "patch", string(p)) } - resp.Status = engineapi.RuleStatusPass - resp.Patches = jsonPatches - resp.Message = "applied strategic merge patch" - return resp, patchedResource + return *engineapi.RulePass(ruleName, engineapi.Mutation, "applied strategic merge patch").WithPatches(jsonPatches...), patchedResource } func strategicMergePatch(logger logr.Logger, base, overlay string) ([]byte, error) { diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 53ed1770a4..b178e129fd 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -50,10 +50,9 @@ func (e *engine) mutate( engineapi.Mutation, ) matchedResource = resource + endTime := time.Now() for _, ruleResp := range ruleResp { - ruleResp := ruleResp - internal.AddRuleResponse(&resp, &ruleResp, startTime) - logger.V(4).Info("finished processing rule", "processingTime", ruleResp.Stats.ProcessingTime.String()) + resp.Add(startTime, endTime, ruleResp) } if applyRules == kyvernov1.ApplyOne && resp.Stats.RulesAppliedCount > 0 { break diff --git a/pkg/engine/mutation_test.go b/pkg/engine/mutation_test.go index 8b49b84be5..ec6e2ca6ab 100644 --- a/pkg/engine/mutation_test.go +++ b/pkg/engine/mutation_test.go @@ -124,9 +124,9 @@ func Test_VariableSubstitutionPatchStrategicMerge(t *testing.T) { t.Log(string(expectedPatch)) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) - t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) - if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { + assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches()), 1) + t.Log(string(er.PolicyResponse.Rules[0].Patches()[0])) + if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches()[0]) { t.Error("patches dont match") } } @@ -195,7 +195,7 @@ func Test_variableSubstitutionPathNotExist(t *testing.T) { er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path")) + assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message(), "Unknown key \"name1\" in path")) } func Test_variableSubstitutionCLI(t *testing.T) { @@ -291,10 +291,10 @@ func Test_variableSubstitutionCLI(t *testing.T) { ), ) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) + assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches()), 1) t.Log(string(expectedPatch)) - t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) - if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { + t.Log(string(er.PolicyResponse.Rules[0].Patches()[0])) + if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches()[0]) { t.Error("patches don't match") } } @@ -399,11 +399,11 @@ func Test_chained_rules(t *testing.T) { assert.Equal(t, containers[0].(map[string]interface{})["image"], "otherregistry.corp.com/foo/bash:5.0") assert.Equal(t, len(er.PolicyResponse.Rules), 2) - assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) - assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches), 1) + assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches()), 1) + assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches()), 1) - assert.Equal(t, string(er.PolicyResponse.Rules[0].Patches[0]), `{"op":"replace","path":"/spec/containers/0/image","value":"myregistry.corp.com/foo/bash:5.0"}`) - assert.Equal(t, string(er.PolicyResponse.Rules[1].Patches[0]), `{"op":"replace","path":"/spec/containers/0/image","value":"otherregistry.corp.com/foo/bash:5.0"}`) + assert.Equal(t, string(er.PolicyResponse.Rules[0].Patches()[0]), `{"op":"replace","path":"/spec/containers/0/image","value":"myregistry.corp.com/foo/bash:5.0"}`) + assert.Equal(t, string(er.PolicyResponse.Rules[1].Patches()[0]), `{"op":"replace","path":"/spec/containers/0/image","value":"otherregistry.corp.com/foo/bash:5.0"}`) } func Test_precondition(t *testing.T) { @@ -480,8 +480,8 @@ func Test_precondition(t *testing.T) { er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, enginetest.ContextLoaderFactory(nil, nil)) t.Log(string(expectedPatch)) - t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) - if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { + t.Log(string(er.PolicyResponse.Rules[0].Patches()[0])) + if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches()[0]) { t.Error("patches don't match") } } @@ -574,8 +574,8 @@ func Test_nonZeroIndexNumberPatchesJson6902(t *testing.T) { er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, enginetest.ContextLoaderFactory(nil, nil)) t.Log(string(expectedPatch)) - t.Log(string(er.PolicyResponse.Rules[0].Patches[0])) - if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches[0]) { + t.Log(string(er.PolicyResponse.Rules[0].Patches()[0])) + if !reflect.DeepEqual(expectedPatch, er.PolicyResponse.Rules[0].Patches()[0]) { t.Error("patches don't match") } } @@ -664,7 +664,7 @@ func Test_foreach(t *testing.T) { er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass) containers, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "containers") assert.NilError(t, err) @@ -766,7 +766,7 @@ func Test_foreach_element_mutation(t *testing.T) { er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass) containers, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "containers") assert.NilError(t, err) @@ -887,7 +887,7 @@ func Test_Container_InitContainer_foreach(t *testing.T) { er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass) containers, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "containers") assert.NilError(t, err) @@ -992,7 +992,7 @@ func Test_foreach_order_mutation_(t *testing.T) { er := testApplyPolicyToResource(t, policyRaw, resourceRaw) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass) containers, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "containers") assert.NilError(t, err) @@ -1140,8 +1140,8 @@ func Test_mutate_nested_foreach(t *testing.T) { er := testApplyPolicyToResource(t, policyRaw, resourceRaw) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusPass) - assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 2) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusPass) + assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches()), 2) tlsArr, _, err := unstructured.NestedSlice(er.PatchedResource.Object, "spec", "tls") assert.NilError(t, err) @@ -1580,9 +1580,9 @@ func Test_mutate_existing_resources(t *testing.T) { er := testMutate(context.TODO(), dclient, registryclient.NewOrDie(), policyContext, nil) for _, rr := range er.PolicyResponse.Rules { - for i, p := range rr.Patches { - assert.Equal(t, test.patches[i], string(p), "test %s failed:\nGot %s\nExpected: %s", test.name, rr.Patches[i], test.patches[i]) - assert.Equal(t, rr.Status, engineapi.RuleStatusPass, rr.Status) + for i, p := range rr.Patches() { + assert.Equal(t, test.patches[i], string(p), "test %s failed:\nGot %s\nExpected: %s", test.name, rr.Patches()[i], test.patches[i]) + assert.Equal(t, rr.Status(), engineapi.RuleStatusPass, rr.Status()) } } } @@ -1686,13 +1686,13 @@ func Test_RuleSelectorMutate(t *testing.T) { er := testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 2) - assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) - assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches), 1) + assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches()), 1) + assert.Equal(t, len(er.PolicyResponse.Rules[1].Patches()), 1) - if !reflect.DeepEqual(expectedPatch1, er.PolicyResponse.Rules[0].Patches[0]) { + if !reflect.DeepEqual(expectedPatch1, er.PolicyResponse.Rules[0].Patches()[0]) { t.Error("rule 1 patches dont match") } - if !reflect.DeepEqual(expectedPatch2, er.PolicyResponse.Rules[1].Patches[0]) { + if !reflect.DeepEqual(expectedPatch2, er.PolicyResponse.Rules[1].Patches()[0]) { t.Errorf("rule 2 patches dont match") } @@ -1701,9 +1701,9 @@ func Test_RuleSelectorMutate(t *testing.T) { er = testMutate(context.TODO(), nil, registryclient.NewOrDie(), policyContext, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches), 1) + assert.Equal(t, len(er.PolicyResponse.Rules[0].Patches()), 1) - if !reflect.DeepEqual(expectedPatch1, er.PolicyResponse.Rules[0].Patches[0]) { + if !reflect.DeepEqual(expectedPatch1, er.PolicyResponse.Rules[0].Patches()[0]) { t.Error("rule 1 patches dont match") } } diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 5d24afd024..140fbbfef3 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -68,10 +68,9 @@ func (e *engine) validate( engineapi.Validation, ) matchedResource = resource + endTime := time.Now() for _, ruleResp := range ruleResp { - ruleResp := ruleResp - internal.AddRuleResponse(&resp, &ruleResp, startTime) - logger.V(4).Info("finished processing rule", "processingTime", ruleResp.Stats.ProcessingTime.String()) + resp.Add(startTime, endTime, ruleResp) } if applyRules == kyvernov1.ApplyOne && resp.Stats.RulesAppliedCount > 0 { break diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index 57593863a4..5f3e18bf57 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -138,7 +138,7 @@ func TestValidate_image_tag_fail(t *testing.T) { er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, !er.IsSuccessful()) @@ -238,7 +238,7 @@ func TestValidate_image_tag_pass(t *testing.T) { } er := testValidate(context.TODO(), registryclient.NewOrDie(), NewPolicyContextWithJsonContext(kyverno.Create, enginecontext.NewContext()).WithPolicy(&policy).WithNewResource(*resourceUnstructured), cfg, nil) for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, er.IsSuccessful()) } @@ -315,7 +315,7 @@ func TestValidate_Fail_anyPattern(t *testing.T) { msgs := []string{"validation error: A namespace is required. rule check-default-namespace[0] failed at path /metadata/namespace/ rule check-default-namespace[1] failed at path /metadata/namespace/"} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } } @@ -397,7 +397,7 @@ func TestValidate_host_network_port(t *testing.T) { msgs := []string{"validation error: Host network and port are not allowed. rule validate-host-network-port failed at path /spec/containers/0/ports/0/hostPort/"} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, !er.IsSuccessful()) } @@ -487,7 +487,7 @@ func TestValidate_anchor_arraymap_pass(t *testing.T) { msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, er.IsSuccessful()) } @@ -575,7 +575,7 @@ func TestValidate_anchor_arraymap_fail(t *testing.T) { msgs := []string{"validation error: Host path '/var/lib/' is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/path/"} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, !er.IsSuccessful()) } @@ -645,7 +645,7 @@ func TestValidate_anchor_map_notfound(t *testing.T) { msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, er.IsSuccessful()) } @@ -718,7 +718,7 @@ func TestValidate_anchor_map_found_valid(t *testing.T) { msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, er.IsSuccessful()) @@ -792,7 +792,7 @@ func TestValidate_inequality_List_Processing(t *testing.T) { msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, er.IsSuccessful()) @@ -872,7 +872,7 @@ func TestValidate_inequality_List_ProcessingBrackets(t *testing.T) { msgs := []string{"validation rule 'pod rule 2' passed."} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, er.IsSuccessful()) @@ -946,7 +946,7 @@ func TestValidate_anchor_map_found_invalid(t *testing.T) { msgs := []string{"validation error: pod: validate run as non root user. rule pod rule 2 failed at path /spec/securityContext/runAsNonRoot/"} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, !er.IsSuccessful()) } @@ -1021,8 +1021,8 @@ func TestValidate_AnchorList_pass(t *testing.T) { msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { - t.Log(r.Message) - assert.Equal(t, r.Message, msgs[index]) + t.Log(r.Message()) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, er.IsSuccessful()) } @@ -1236,7 +1236,7 @@ func TestValidate_existenceAnchor_pass(t *testing.T) { msgs := []string{"validation rule 'pod image rule' passed."} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, er.IsSuccessful()) } @@ -1324,7 +1324,7 @@ func TestValidate_negationAnchor_deny(t *testing.T) { msgs := []string{"validation error: Host path is not allowed. rule validate-host-path failed at path /spec/volumes/0/hostPath/"} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, !er.IsSuccessful()) } @@ -1411,7 +1411,7 @@ func TestValidate_negationAnchor_pass(t *testing.T) { msgs := []string{"validation rule 'validate-host-path' passed."} for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, er.IsSuccessful()) } @@ -1483,8 +1483,8 @@ func Test_VariableSubstitutionPathNotExistInPattern(t *testing.T) { er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError) - assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path")) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusError) + assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message(), "Unknown key \"name1\" in path")) } func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSubstitutionFails(t *testing.T) { @@ -1573,8 +1573,8 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_OnePatternStatisfiesButSu er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError) - assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path")) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusError) + assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message(), "Unknown key \"name1\" in path")) } func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) { @@ -1629,8 +1629,8 @@ func Test_VariableSubstitution_NotOperatorWithStringVariable(t *testing.T) { policyContext := NewPolicyContextWithJsonContext(kyverno.Create, ctx).WithPolicy(&policy).WithNewResource(*resourceUnstructured) er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) - assert.Equal(t, er.PolicyResponse.Rules[0].Message, "validation error: rule not-operator-with-variable-should-alway-fail-validation failed at path /spec/content/") + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail) + assert.Equal(t, er.PolicyResponse.Rules[0].Message(), "validation error: rule not-operator-with-variable-should-alway-fail-validation failed at path /spec/content/") } func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *testing.T) { @@ -1719,8 +1719,8 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathNotPresent(t *test er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) assert.Equal(t, len(er.PolicyResponse.Rules), 1) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusError) - assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message, "Unknown key \"name1\" in path")) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusError) + assert.Assert(t, strings.Contains(er.PolicyResponse.Rules[0].Message(), "Unknown key \"name1\" in path")) } func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatternSatisfy(t *testing.T) { @@ -1808,8 +1808,8 @@ func Test_VariableSubstitutionPathNotExistInAnyPattern_AllPathPresent_NonePatter policyContext := NewPolicyContextWithJsonContext(kyverno.Create, ctx).WithPolicy(&policy).WithNewResource(*resourceUnstructured) er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) - assert.Equal(t, er.PolicyResponse.Rules[0].Message, + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail) + assert.Equal(t, er.PolicyResponse.Rules[0].Message(), "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/") } @@ -1909,8 +1909,8 @@ func Test_VariableSubstitutionValidate_VariablesInMessageAreResolved(t *testing. policyContext := NewPolicyContextWithJsonContext(kyverno.Create, ctx).WithPolicy(&policy).WithNewResource(*resourceUnstructured) er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) - assert.Equal(t, er.PolicyResponse.Rules[0].Message, "The animal cow is not in the allowed list of animals.") + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail) + assert.Equal(t, er.PolicyResponse.Rules[0].Message(), "The animal cow is not in the allowed list of animals.") } func Test_Flux_Kustomization_PathNotPresent(t *testing.T) { @@ -1961,8 +1961,8 @@ func Test_Flux_Kustomization_PathNotPresent(t *testing.T) { er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, nil) for i, rule := range er.PolicyResponse.Rules { - assert.Equal(t, er.PolicyResponse.Rules[i].Status, test.expectedResults[i], "\ntest %s failed\nexpected: %s\nactual: %s", test.name, test.expectedResults[i], er.PolicyResponse.Rules[i].Status) - assert.Equal(t, er.PolicyResponse.Rules[i].Message, test.expectedMessages[i], "\ntest %s failed\nexpected: %s\nactual: %s", test.name, test.expectedMessages[i], rule.Message) + assert.Equal(t, er.PolicyResponse.Rules[i].Status(), test.expectedResults[i], "\ntest %s failed\nexpected: %s\nactual: %s", test.name, test.expectedResults[i], er.PolicyResponse.Rules[i].Status()) + assert.Equal(t, er.PolicyResponse.Rules[i].Message(), test.expectedMessages[i], "\ntest %s failed\nexpected: %s\nactual: %s", test.name, test.expectedMessages[i], rule.Message()) } } } @@ -2223,7 +2223,7 @@ func TestValidate_context_variable_substitution_CLI(t *testing.T) { ), ) for index, r := range er.PolicyResponse.Rules { - assert.Equal(t, r.Message, msgs[index]) + assert.Equal(t, r.Message(), msgs[index]) } assert.Assert(t, !er.IsSuccessful()) } @@ -3088,9 +3088,9 @@ func testForEach(t *testing.T, policyraw []byte, resourceRaw []byte, msg string, WithNewResource(*resourceUnstructured) er := testValidate(context.TODO(), registryclient.NewOrDie(), policyContext, cfg, contextLoader) - assert.Equal(t, er.PolicyResponse.Rules[0].Status, status) + assert.Equal(t, er.PolicyResponse.Rules[0].Status(), status) if msg != "" { - assert.Equal(t, er.PolicyResponse.Rules[0].Message, msg) + assert.Equal(t, er.PolicyResponse.Rules[0].Message(), msg) } } @@ -3151,7 +3151,7 @@ func Test_delete_ignore_pattern(t *testing.T) { engineResponseCreate := testValidate(context.TODO(), registryclient.NewOrDie(), policyContextCreate, cfg, nil) assert.Equal(t, len(engineResponseCreate.PolicyResponse.Rules), 1) - assert.Equal(t, engineResponseCreate.PolicyResponse.Rules[0].Status, engineapi.RuleStatusFail) + assert.Equal(t, engineResponseCreate.PolicyResponse.Rules[0].Status(), engineapi.RuleStatusFail) policyContextDelete := NewPolicyContextWithJsonContext(kyverno.Create, ctx). WithPolicy(&policy). diff --git a/pkg/event/events.go b/pkg/event/events.go index ee128c7ce5..4950e9bef2 100644 --- a/pkg/event/events.go +++ b/pkg/event/events.go @@ -28,13 +28,13 @@ func buildPolicyEventMessage(resp *engineapi.RuleResponse, resource engineapi.Re fmt.Fprintf(&b, "%s %s", resource.Kind, resource.Name) } - fmt.Fprintf(&b, ": [%s] %s", resp.Name, resp.Status) + fmt.Fprintf(&b, ": [%s] %s", resp.Name(), resp.Status()) if blocked { fmt.Fprintf(&b, " (blocked)") } - if resp.Message != "" { - fmt.Fprintf(&b, "; %s", resp.Message) + if resp.Message() != "" { + fmt.Fprintf(&b, "; %s", resp.Message()) } return b.String() @@ -73,7 +73,7 @@ func NewResourceViolationEvent(source Source, reason Reason, engineResponse engi defer bldr.Reset() fmt.Fprintf(&bldr, "policy %s/%s %s: %s", engineResponse.Policy.GetName(), - ruleResp.Name, ruleResp.Status, ruleResp.Message) + ruleResp.Name(), ruleResp.Status(), ruleResp.Message()) resource := engineResponse.GetResourceSpec() return Info{ @@ -124,13 +124,14 @@ func NewBackgroundSuccessEvent(policy, rule string, source Source, r *unstructur } func NewPolicyExceptionEvents(engineResponse engineapi.EngineResponse, ruleResp *engineapi.RuleResponse, source Source) []Info { - exceptionName, exceptionNamespace := ruleResp.Exception.GetName(), ruleResp.Exception.GetNamespace() - policyMessage := fmt.Sprintf("resource %s was skipped from rule %s due to policy exception %s/%s", resourceKey(engineResponse.PatchedResource), ruleResp.Name, exceptionNamespace, exceptionName) + exception := ruleResp.Exception() + exceptionName, exceptionNamespace := exception.GetName(), exception.GetNamespace() + policyMessage := fmt.Sprintf("resource %s was skipped from rule %s due to policy exception %s/%s", resourceKey(engineResponse.PatchedResource), ruleResp.Name(), exceptionNamespace, exceptionName) var exceptionMessage string if engineResponse.Policy.GetNamespace() == "" { - exceptionMessage = fmt.Sprintf("resource %s was skipped from policy rule %s/%s", resourceKey(engineResponse.PatchedResource), engineResponse.Policy.GetName(), ruleResp.Name) + exceptionMessage = fmt.Sprintf("resource %s was skipped from policy rule %s/%s", resourceKey(engineResponse.PatchedResource), engineResponse.Policy.GetName(), ruleResp.Name()) } else { - exceptionMessage = fmt.Sprintf("resource %s was skipped from policy rule %s/%s/%s", resourceKey(engineResponse.PatchedResource), engineResponse.Policy.GetNamespace(), engineResponse.Policy.GetName(), ruleResp.Name) + exceptionMessage = fmt.Sprintf("resource %s was skipped from policy rule %s/%s/%s", resourceKey(engineResponse.PatchedResource), engineResponse.Policy.GetNamespace(), engineResponse.Policy.GetName(), ruleResp.Name()) } policyEvent := Info{ Kind: getPolicyKind(engineResponse.Policy), diff --git a/pkg/metrics/parsers.go b/pkg/metrics/parsers.go index 35a0092a5d..09d092ce21 100644 --- a/pkg/metrics/parsers.go +++ b/pkg/metrics/parsers.go @@ -54,7 +54,7 @@ func ParseResourceRequestOperation(requestOperationStr string) (ResourceRequestO } func ParseRuleTypeFromEngineRuleResponse(rule engineapi.RuleResponse) RuleType { - switch rule.Type { + switch rule.RuleType() { case "Validation": return Validate case "Mutation": diff --git a/pkg/policy/policy_controller.go b/pkg/policy/policy_controller.go index 3c88c71779..9b7ba9c765 100644 --- a/pkg/policy/policy_controller.go +++ b/pkg/policy/policy_controller.go @@ -416,8 +416,8 @@ func (pc *PolicyController) handleUpdateRequest(ur *kyvernov1beta1.UpdateRequest } for _, ruleResponse := range engineResponse.PolicyResponse.Rules { - if ruleResponse.Status != engineapi.RuleStatusPass { - pc.log.Error(err, "can not create new UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule.Status", ruleResponse.Status) + if ruleResponse.Status() != engineapi.RuleStatusPass { + pc.log.Error(err, "can not create new UR on policy update", "policy", policy.GetName(), "rule", rule.Name, "rule.Status", ruleResponse.Status()) continue } diff --git a/pkg/utils/annotations.go b/pkg/utils/annotations.go index 6c9d0fb64b..f4698c9748 100644 --- a/pkg/utils/annotations.go +++ b/pkg/utils/annotations.go @@ -122,14 +122,14 @@ func annotationFromEngineResponses(engineResponses []engineapi.EngineResponse, l func annotationFromPolicyResponse(policyResponse engineapi.PolicyResponse, log logr.Logger) []RulePatch { var RulePatches []RulePatch for _, ruleInfo := range policyResponse.Rules { - for _, patch := range ruleInfo.Patches { + for _, patch := range ruleInfo.Patches() { var patchmap map[string]interface{} if err := json.Unmarshal(patch, &patchmap); err != nil { log.Error(err, "Failed to parse JSON patch bytes") continue } rp := RulePatch{ - RuleName: ruleInfo.Name, + RuleName: ruleInfo.Name(), Op: patchmap["op"].(string), Path: patchmap["path"].(string), } diff --git a/pkg/utils/annotations_test.go b/pkg/utils/annotations_test.go index 0e64b67611..b4b7af8318 100644 --- a/pkg/utils/annotations_test.go +++ b/pkg/utils/annotations_test.go @@ -20,11 +20,7 @@ func newPolicyResponse(rule string, patchesStr []string, status engineapi.RuleSt return engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: rule, - Patches: patches, - Status: status, - }, + *engineapi.NewRuleResponse(rule, engineapi.Mutation, "", status).WithPatches(patches...), }, } } diff --git a/pkg/utils/report/results.go b/pkg/utils/report/results.go index 83cec00177..f6185d1071 100644 --- a/pkg/utils/report/results.go +++ b/pkg/utils/report/results.go @@ -89,9 +89,9 @@ func EngineResponseToReportResults(response engineapi.EngineResponse) []policyre result := policyreportv1alpha2.PolicyReportResult{ Source: kyvernov1.ValueKyvernoApp, Policy: key, - Rule: ruleResult.Name, - Message: ruleResult.Message, - Result: toPolicyResult(ruleResult.Status), + Rule: ruleResult.Name(), + Message: ruleResult.Message(), + Result: toPolicyResult(ruleResult.Status()), Scored: annotations[kyvernov1.AnnotationPolicyScored] != "false", Timestamp: metav1.Timestamp{ Seconds: time.Now().Unix(), @@ -99,9 +99,10 @@ func EngineResponseToReportResults(response engineapi.EngineResponse) []policyre Category: annotations[kyvernov1.AnnotationPolicyCategory], Severity: severityFromString(annotations[kyvernov1.AnnotationPolicySeverity]), } - if ruleResult.PodSecurityChecks != nil { + pss := ruleResult.PodSecurityChecks() + if pss != nil { var controls []string - for _, check := range ruleResult.PodSecurityChecks.Checks { + for _, check := range pss.Checks { if !check.CheckResult.Allowed { controls = append(controls, check.ID) } @@ -109,8 +110,8 @@ func EngineResponseToReportResults(response engineapi.EngineResponse) []policyre if len(controls) > 0 { sort.Strings(controls) result.Properties = map[string]string{ - "standard": string(ruleResult.PodSecurityChecks.Level), - "version": ruleResult.PodSecurityChecks.Version, + "standard": string(pss.Level), + "version": pss.Version, "controls": strings.Join(controls, ","), } } diff --git a/pkg/webhooks/resource/generation/handler.go b/pkg/webhooks/resource/generation/handler.go index 1751574916..6fcfd79213 100644 --- a/pkg/webhooks/resource/generation/handler.go +++ b/pkg/webhooks/resource/generation/handler.go @@ -90,7 +90,7 @@ func getAppliedRules(policy kyvernov1.PolicyInterface, applied []engineapi.RuleR continue } for _, applied := range applied { - if applied.Name == rule.Name && applied.Type == engineapi.Generation { + if applied.Name() == rule.Name && applied.RuleType() == engineapi.Generation { rules = append(rules, rule) } } @@ -113,9 +113,9 @@ func (h *generationHandler) handleTrigger( } engineResponse := h.engine.ApplyBackgroundChecks(ctx, policyContext) for _, rule := range engineResponse.PolicyResponse.Rules { - if rule.Status == engineapi.RuleStatusPass { + if rule.Status() == engineapi.RuleStatusPass { appliedRules = append(appliedRules, rule) - } else if rule.Status == engineapi.RuleStatusFail { + } else if rule.Status() == engineapi.RuleStatusFail { failedRules = append(failedRules, rule) } } diff --git a/pkg/webhooks/resource/updaterequest.go b/pkg/webhooks/resource/updaterequest.go index 7f536906dc..b49a8a491e 100644 --- a/pkg/webhooks/resource/updaterequest.go +++ b/pkg/webhooks/resource/updaterequest.go @@ -50,7 +50,7 @@ func (h *resourceHandlers) handleMutateExisting(ctx context.Context, logger logr engineResponse := h.engine.ApplyBackgroundChecks(ctx, policyContext) for _, rule := range engineResponse.PolicyResponse.Rules { - if rule.Status == engineapi.RuleStatusPass { + if rule.Status() == engineapi.RuleStatusPass { rules = append(rules, rule) } } diff --git a/pkg/webhooks/resource/validation_test.go b/pkg/webhooks/resource/validation_test.go index d8208b7ab4..bbf2830556 100644 --- a/pkg/webhooks/resource/validation_test.go +++ b/pkg/webhooks/resource/validation_test.go @@ -1072,8 +1072,8 @@ func TestValidate_failure_action_overrides(t *testing.T) { ) if tc.blocked && tc.messages != nil { for _, r := range er.PolicyResponse.Rules { - msg := tc.messages[r.Name] - assert.Equal(t, r.Message, msg) + msg := tc.messages[r.Name()] + assert.Equal(t, r.Message(), msg) } } diff --git a/pkg/webhooks/utils/block.go b/pkg/webhooks/utils/block.go index ed0c724712..bebe764933 100644 --- a/pkg/webhooks/utils/block.go +++ b/pkg/webhooks/utils/block.go @@ -44,9 +44,9 @@ func GetBlockedMessages(engineResponses []engineapi.EngineResponse) string { for _, er := range engineResponses { ruleToReason := make(map[string]string) for _, rule := range er.PolicyResponse.Rules { - if rule.Status != engineapi.RuleStatusPass { - ruleToReason[rule.Name] = rule.Message - if rule.Status == engineapi.RuleStatusFail { + if rule.Status() != engineapi.RuleStatusPass { + ruleToReason[rule.Name()] = rule.Message() + if rule.Status() == engineapi.RuleStatusFail { hasViolations = true } } diff --git a/pkg/webhooks/utils/block_test.go b/pkg/webhooks/utils/block_test.go index 6a272c0d14..5830a58282 100644 --- a/pkg/webhooks/utils/block_test.go +++ b/pkg/webhooks/utils/block_test.go @@ -87,11 +87,7 @@ func TestBlockRequest(t *testing.T) { engineResponses: []engineapi.EngineResponse{ engineapi.NewEngineResponse(resource, enforcePolicy, nil, &engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-fail", - Status: engineapi.RuleStatusFail, - Message: "message fail", - }, + *engineapi.RuleFail("rule-fail", engineapi.Validation, "message fail"), }, }, time.Now()), }, @@ -105,11 +101,7 @@ func TestBlockRequest(t *testing.T) { engineResponses: []engineapi.EngineResponse{ engineapi.NewEngineResponse(resource, auditPolicy, nil, &engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-fail", - Status: engineapi.RuleStatusFail, - Message: "message fail", - }, + *engineapi.RuleFail("rule-fail", engineapi.Validation, "message fail"), }, }, time.Now()), }, @@ -123,11 +115,7 @@ func TestBlockRequest(t *testing.T) { engineResponses: []engineapi.EngineResponse{ engineapi.NewEngineResponse(resource, auditPolicy, nil, &engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-error", - Status: engineapi.RuleStatusError, - Message: "message error", - }, + *engineapi.RuleError("rule-error", engineapi.Validation, "message error", nil), }, }, time.Now()), }, @@ -141,11 +129,7 @@ func TestBlockRequest(t *testing.T) { engineResponses: []engineapi.EngineResponse{ engineapi.NewEngineResponse(resource, auditPolicy, nil, &engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-error", - Status: engineapi.RuleStatusError, - Message: "message error", - }, + *engineapi.RuleError("rule-error", engineapi.Validation, "message error", nil), }, }, time.Now()), }, @@ -159,11 +143,7 @@ func TestBlockRequest(t *testing.T) { engineResponses: []engineapi.EngineResponse{ engineapi.NewEngineResponse(resource, auditPolicy, nil, &engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-warning", - Status: engineapi.RuleStatusWarn, - Message: "message warning", - }, + *engineapi.NewRuleResponse("rule-warning", engineapi.Validation, "message warning", engineapi.RuleStatusWarn), }, }, time.Now()), }, @@ -177,11 +157,7 @@ func TestBlockRequest(t *testing.T) { engineResponses: []engineapi.EngineResponse{ engineapi.NewEngineResponse(resource, auditPolicy, nil, &engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-warning", - Status: engineapi.RuleStatusWarn, - Message: "message warning", - }, + *engineapi.NewRuleResponse("rule-warning", engineapi.Validation, "message warning", engineapi.RuleStatusWarn), }, }, time.Now()), }, @@ -229,11 +205,7 @@ func TestGetBlockedMessages(t *testing.T) { engineResponses: []engineapi.EngineResponse{ engineapi.NewEngineResponse(resource, enforcePolicy, nil, &engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-fail", - Status: engineapi.RuleStatusFail, - Message: "message fail", - }, + *engineapi.RuleFail("rule-fail", engineapi.Validation, "message fail"), }, }, time.Now()), }, @@ -245,11 +217,7 @@ func TestGetBlockedMessages(t *testing.T) { engineResponses: []engineapi.EngineResponse{ engineapi.NewEngineResponse(resource, enforcePolicy, nil, &engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-error", - Status: engineapi.RuleStatusError, - Message: "message error", - }, + *engineapi.RuleError("rule-error", engineapi.Validation, "message error", nil), }, }, time.Now()), }, @@ -261,16 +229,8 @@ func TestGetBlockedMessages(t *testing.T) { engineResponses: []engineapi.EngineResponse{ engineapi.NewEngineResponse(resource, enforcePolicy, nil, &engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-fail", - Status: engineapi.RuleStatusFail, - Message: "message fail", - }, - { - Name: "rule-error", - Status: engineapi.RuleStatusError, - Message: "message error", - }, + *engineapi.RuleFail("rule-fail", engineapi.Validation, "message fail"), + *engineapi.RuleError("rule-error", engineapi.Validation, "message error", nil), }, }, time.Now()), }, diff --git a/pkg/webhooks/utils/error.go b/pkg/webhooks/utils/error.go index 4c4f6b6cb4..b54be16193 100644 --- a/pkg/webhooks/utils/error.go +++ b/pkg/webhooks/utils/error.go @@ -16,7 +16,7 @@ func GetErrorMsg(engineReponses []engineapi.EngineResponse) string { resourceInfo = fmt.Sprintf("%s/%s/%s", er.Resource.GetKind(), er.Resource.GetNamespace(), er.Resource.GetName()) str = append(str, fmt.Sprintf("failed policy %s:", er.Policy.GetName())) for _, rule := range er.PolicyResponse.Rules { - if rule.Status != engineapi.RuleStatusPass { + if rule.Status() != engineapi.RuleStatusPass { str = append(str, rule.String()) } } diff --git a/pkg/webhooks/utils/event.go b/pkg/webhooks/utils/event.go index f7395af6c4..d3678d4828 100644 --- a/pkg/webhooks/utils/event.go +++ b/pkg/webhooks/utils/event.go @@ -21,7 +21,7 @@ func GenerateEvents(engineResponses []engineapi.EngineResponse, blocked bool) [] } if !er.IsSuccessful() { for i, ruleResp := range er.PolicyResponse.Rules { - if ruleResp.Status == engineapi.RuleStatusFail || ruleResp.Status == engineapi.RuleStatusError { + if ruleResp.Status() == engineapi.RuleStatusFail || ruleResp.Status() == engineapi.RuleStatusError { e := event.NewPolicyFailEvent(event.AdmissionController, event.PolicyViolation, er, &er.PolicyResponse.Rules[i], blocked) events = append(events, e) } @@ -32,8 +32,7 @@ func GenerateEvents(engineResponses []engineapi.EngineResponse, blocked bool) [] } } else if er.IsSkipped() { // Handle PolicyException Event for i, ruleResp := range er.PolicyResponse.Rules { - isException := ruleResp.Exception != nil - if ruleResp.Status == engineapi.RuleStatusSkip && !blocked && isException { + if ruleResp.Status() == engineapi.RuleStatusSkip && !blocked && ruleResp.IsException() { events = append(events, event.NewPolicyExceptionEvents(er, &er.PolicyResponse.Rules[i], event.AdmissionController)...) } } diff --git a/pkg/webhooks/utils/warning.go b/pkg/webhooks/utils/warning.go index 5390b871ce..9b9b15fae7 100644 --- a/pkg/webhooks/utils/warning.go +++ b/pkg/webhooks/utils/warning.go @@ -10,8 +10,8 @@ func GetWarningMessages(engineResponses []engineapi.EngineResponse) []string { var warnings []string for _, er := range engineResponses { for _, rule := range er.PolicyResponse.Rules { - if rule.Status != engineapi.RuleStatusPass && rule.Status != engineapi.RuleStatusSkip { - msg := fmt.Sprintf("policy %s.%s: %s", er.Policy.GetName(), rule.Name, rule.Message) + if rule.Status() != engineapi.RuleStatusPass && rule.Status() != engineapi.RuleStatusSkip { + msg := fmt.Sprintf("policy %s.%s: %s", er.Policy.GetName(), rule.Name(), rule.Message()) warnings = append(warnings, msg) } } diff --git a/pkg/webhooks/utils/warning_test.go b/pkg/webhooks/utils/warning_test.go index d5c6e570b1..d7e4c969a2 100644 --- a/pkg/webhooks/utils/warning_test.go +++ b/pkg/webhooks/utils/warning_test.go @@ -36,11 +36,7 @@ func TestGetWarningMessages(t *testing.T) { }, PolicyResponse: engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule", - Status: engineapi.RuleStatusWarn, - Message: "message warn", - }, + *engineapi.NewRuleResponse("rule", engineapi.Validation, "message warn", engineapi.RuleStatusWarn), }, }, }, @@ -59,31 +55,11 @@ func TestGetWarningMessages(t *testing.T) { }, PolicyResponse: engineapi.PolicyResponse{ Rules: []engineapi.RuleResponse{ - { - Name: "rule-pass", - Status: engineapi.RuleStatusPass, - Message: "message pass", - }, - { - Name: "rule-warn", - Status: engineapi.RuleStatusWarn, - Message: "message warn", - }, - { - Name: "rule-fail", - Status: engineapi.RuleStatusFail, - Message: "message fail", - }, - { - Name: "rule-error", - Status: engineapi.RuleStatusError, - Message: "message error", - }, - { - Name: "rule-skip", - Status: engineapi.RuleStatusSkip, - Message: "message skip", - }, + *engineapi.RulePass("rule-pass", engineapi.Validation, "message pass"), + *engineapi.NewRuleResponse("rule-warn", engineapi.Validation, "message warn", engineapi.RuleStatusWarn), + *engineapi.RuleFail("rule-fail", engineapi.Validation, "message fail"), + *engineapi.RuleError("rule-error", engineapi.Validation, "message error", nil), + *engineapi.RuleSkip("rule-skip", engineapi.Validation, "message skip"), }, }, },