1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-04-15 00:36:28 +00:00

refactor: engine rule response creation (#6784)

* 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>
This commit is contained in:
Charles-Edouard Brétéché 2023-04-05 12:35:38 +02:00 committed by GitHub
parent 6f8ef4fd30
commit 784ca07419
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 718 additions and 826 deletions

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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())
}
}

View file

@ -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),
}

View file

@ -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)

View file

@ -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

View file

@ -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}},

View file

@ -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++
}
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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, "")
}

View file

@ -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)

View file

@ -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
}

View file

@ -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

View file

@ -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 {

View file

@ -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
}

View file

@ -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
}

View file

@ -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) {

View file

@ -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))
}

View file

@ -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 {

View file

@ -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(

View file

@ -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),
)
}
}

View file

@ -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) {

View file

@ -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

View file

@ -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)

View file

@ -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(

View file

@ -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
}

View file

@ -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...)
}
}
}

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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)

View file

@ -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) {

View file

@ -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

View file

@ -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")
}
}

View file

@ -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

View file

@ -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).

View file

@ -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),

View file

@ -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":

View file

@ -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
}

View file

@ -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),
}

View file

@ -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...),
},
}
}

View file

@ -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, ","),
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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()),
},

View file

@ -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())
}
}

View file

@ -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)...)
}
}

View file

@ -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)
}
}

View file

@ -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"),
},
},
},