mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
refactor: cli test command (#8212)
* code changes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * test changes 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:
parent
6b7c204f05
commit
c93ac4655c
17 changed files with 373 additions and 706 deletions
36
Makefile
36
Makefile
|
@ -695,37 +695,47 @@ test-kuttl: $(KUTTL) ## Run kuttl tests
|
|||
#############
|
||||
|
||||
TEST_GIT_BRANCH ?= main
|
||||
TEST_GIT_REPO ?= https://github.com/kyverno/policies
|
||||
|
||||
.PHONY: test-cli
|
||||
test-cli: test-cli-policies test-cli-local test-cli-local-mutate test-cli-local-generate test-cli-test-case-selector-flag test-cli-registry test-cli-scenarios-to-cli ## Run all CLI tests
|
||||
test-cli: test-cli-policies test-cli-local ## Run all CLI tests
|
||||
|
||||
.PHONY: test-cli-policies
|
||||
test-cli-policies: $(CLI_BIN)
|
||||
@echo Testing against branch $(TEST_GIT_BRANCH)...
|
||||
@$(CLI_BIN) test https://github.com/kyverno/policies/$(TEST_GIT_BRANCH)
|
||||
test-cli-policies: $(CLI_BIN) ## Run CLI tests against the policies repository
|
||||
@echo Running cli tests against $(TEST_GIT_REPO)/$(TEST_GIT_BRANCH)... >&2
|
||||
@$(CLI_BIN) test https://github.com/eddycharly/policies/test-refactor
|
||||
|
||||
.PHONY: test-cli-local
|
||||
test-cli-local: $(CLI_BIN)
|
||||
test-cli-local: test-cli-local-validate test-cli-local-mutate test-cli-local-generate test-cli-local-registry test-cli-local-scenarios test-cli-local-selector ## Run local CLI tests
|
||||
|
||||
.PHONY: test-cli-local-validate
|
||||
test-cli-local-validate: $(CLI_BIN) ## Run local CLI validation tests
|
||||
@echo Running local cli validation tests... >&2
|
||||
@$(CLI_BIN) test ./test/cli/test
|
||||
|
||||
.PHONY: test-cli-local-mutate
|
||||
test-cli-local-mutate: $(CLI_BIN)
|
||||
test-cli-local-mutate: $(CLI_BIN) ## Run local CLI mutation tests
|
||||
@echo Running local cli mutation tests... >&2
|
||||
@$(CLI_BIN) test ./test/cli/test-mutate
|
||||
|
||||
.PHONY: test-cli-local-generate
|
||||
test-cli-local-generate: $(CLI_BIN)
|
||||
test-cli-local-generate: $(CLI_BIN) ## Run local CLI generation tests
|
||||
@echo Running local cli generation tests... >&2
|
||||
@$(CLI_BIN) test ./test/cli/test-generate
|
||||
|
||||
.PHONY: test-cli-test-case-selector-flag
|
||||
test-cli-test-case-selector-flag: $(CLI_BIN)
|
||||
.PHONY: test-cli-local-selector
|
||||
test-cli-local-selector: $(CLI_BIN) ## Run local CLI tests (with test case selector)
|
||||
@echo Running local cli selector tests... >&2
|
||||
@$(CLI_BIN) test ./test/cli/test --test-case-selector "policy=disallow-latest-tag, rule=require-image-tag, resource=test-require-image-tag-pass"
|
||||
|
||||
.PHONY: test-cli-registry
|
||||
test-cli-registry: $(CLI_BIN)
|
||||
.PHONY: test-cli-local-registry
|
||||
test-cli-local-registry: $(CLI_BIN) ## Run local CLI registry tests
|
||||
@echo Running local cli registry tests... >&2
|
||||
@$(CLI_BIN) test ./test/cli/registry --registry
|
||||
|
||||
.PHONY: test-cli-scenarios-to-cli
|
||||
test-cli-scenarios-to-cli: $(CLI_BIN)
|
||||
.PHONY: test-cli-local-scenarios
|
||||
test-cli-local-scenarios: $(CLI_BIN) ## Run local CLI scenarios tests
|
||||
@echo Running local cli scenarios tests... >&2
|
||||
@$(CLI_BIN) test ./test/cli/scenarios_to_cli --registry
|
||||
|
||||
#############
|
||||
|
|
|
@ -49,9 +49,6 @@ type TestResults struct {
|
|||
// CloneSourceResource takes the resource configuration file in yaml format
|
||||
// from the user which is meant to be cloned by the generate rule.
|
||||
CloneSourceResource string `json:"cloneSourceResource,omitempty"`
|
||||
// AutoGeneratedRule is internally set by the CLI command. It takes values either
|
||||
// autogen or autogen-cronjob.
|
||||
AutoGeneratedRule string `json:"auto_generated_rule,omitempty"`
|
||||
}
|
||||
|
||||
type Policy struct {
|
||||
|
|
|
@ -4,19 +4,21 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/go-git/go-billy/v5"
|
||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/color"
|
||||
filterutils "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/filter"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/output/table"
|
||||
reportutils "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/report"
|
||||
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"github.com/kyverno/kyverno/pkg/openapi"
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
||||
// Command returns version command
|
||||
func Command() *cobra.Command {
|
||||
var cmd *cobra.Command
|
||||
var testCase string
|
||||
|
@ -39,7 +41,7 @@ func Command() *cobra.Command {
|
|||
}
|
||||
}()
|
||||
store.SetRegistryAccess(registryAccess)
|
||||
_, err = testCommandExecute(dirPath, fileName, gitBranch, testCase, failOnly, false, detailedResults)
|
||||
_, err = testCommandExecute(dirPath, fileName, gitBranch, testCase, failOnly, detailedResults)
|
||||
if err != nil {
|
||||
log.Log.V(3).Info("a directory is required")
|
||||
return err
|
||||
|
@ -69,7 +71,6 @@ func testCommandExecute(
|
|||
gitBranch string,
|
||||
testCase string,
|
||||
failOnly bool,
|
||||
auditWarn bool,
|
||||
detailedResults bool,
|
||||
) (rc *resultCounts, err error) {
|
||||
// check input dir
|
||||
|
@ -113,7 +114,7 @@ func testCommandExecute(
|
|||
rc = &resultCounts{}
|
||||
var table table.Table
|
||||
for _, p := range policies {
|
||||
if reports, tests, err := applyPoliciesFromPath(
|
||||
if tests, responses, err := applyPoliciesFromPath(
|
||||
fs,
|
||||
p.test,
|
||||
fs != nil,
|
||||
|
@ -121,10 +122,10 @@ func testCommandExecute(
|
|||
rc,
|
||||
openApiManager,
|
||||
filter,
|
||||
auditWarn,
|
||||
false,
|
||||
); err != nil {
|
||||
return rc, sanitizederror.NewWithError("failed to apply test command", err)
|
||||
} else if t, err := printTestResult(reports, tests, rc, failOnly, detailedResults); err != nil {
|
||||
} else if t, err := printTestResult(tests, responses, rc, failOnly, detailedResults, fs, p.resourcePath); err != nil {
|
||||
return rc, sanitizederror.NewWithError("failed to print test result:", err)
|
||||
} else {
|
||||
table.AddFailed(t.RawRows...)
|
||||
|
@ -146,191 +147,153 @@ func testCommandExecute(
|
|||
return rc, nil
|
||||
}
|
||||
|
||||
func printTestResult(resps map[string]policyreportv1alpha2.PolicyReportResult, testResults []api.TestResults, rc *resultCounts, failOnly bool, detailedResults bool) (table.Table, error) {
|
||||
func checkResult(test api.TestResults, fs billy.Filesystem, resoucePath string, response engineapi.EngineResponse, rule engineapi.RuleResponse) (bool, string, string) {
|
||||
expected := test.Result
|
||||
// fallback to the deprecated field
|
||||
if expected == "" {
|
||||
expected = test.Status
|
||||
}
|
||||
// fallback on deprecated field
|
||||
if test.PatchedResource != "" {
|
||||
equals, err := getAndCompareResource(test.PatchedResource, response.PatchedResource, fs, resoucePath, false)
|
||||
if err != nil {
|
||||
return false, err.Error(), "Resource error"
|
||||
}
|
||||
if !equals {
|
||||
return false, "Patched resource didn't match the patched resource in the test result", "Resource diff"
|
||||
}
|
||||
}
|
||||
if test.GeneratedResource != "" {
|
||||
equals, err := getAndCompareResource(test.GeneratedResource, rule.GeneratedResource(), fs, resoucePath, true)
|
||||
if err != nil {
|
||||
return false, err.Error(), "Resource error"
|
||||
}
|
||||
if !equals {
|
||||
return false, "Generated resource didn't match the generated resource in the test result", "Resource diff"
|
||||
}
|
||||
}
|
||||
result := reportutils.ComputePolicyReportResult(false, response, rule)
|
||||
if result.Result != expected {
|
||||
return false, result.Message, fmt.Sprintf("Want %s, got %s", expected, result.Result)
|
||||
}
|
||||
return true, result.Message, "Ok"
|
||||
}
|
||||
|
||||
func lookupEngineResponses(test api.TestResults, resourceName string, responses ...engineapi.EngineResponse) []engineapi.EngineResponse {
|
||||
var matches []engineapi.EngineResponse
|
||||
for _, response := range responses {
|
||||
policy := response.Policy()
|
||||
resource := response.Resource
|
||||
if policy.GetName() != test.Policy {
|
||||
continue
|
||||
}
|
||||
if test.Kind != resource.GetKind() {
|
||||
continue
|
||||
}
|
||||
if resourceName != "" && resourceName != resource.GetName() {
|
||||
continue
|
||||
}
|
||||
if test.Namespace != "" && test.Namespace != resource.GetNamespace() {
|
||||
continue
|
||||
}
|
||||
matches = append(matches, response)
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
func lookupRuleResponses(test api.TestResults, responses ...engineapi.RuleResponse) []engineapi.RuleResponse {
|
||||
var matches []engineapi.RuleResponse
|
||||
for _, response := range responses {
|
||||
rule := response.Name()
|
||||
if rule != test.Rule && rule != "autogen-"+test.Rule && rule != "autogen-cronjob-"+test.Rule {
|
||||
continue
|
||||
}
|
||||
matches = append(matches, response)
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
func printTestResult(
|
||||
tests []api.TestResults,
|
||||
responses []engineapi.EngineResponse,
|
||||
rc *resultCounts,
|
||||
failOnly bool,
|
||||
detailedResults bool,
|
||||
fs billy.Filesystem,
|
||||
resoucePath string,
|
||||
) (table.Table, error) {
|
||||
printer := table.NewTablePrinter()
|
||||
var resultsTable table.Table
|
||||
var countDeprecatedResource int
|
||||
testCount := 1
|
||||
for _, v := range testResults {
|
||||
var row table.Row
|
||||
row.ID = testCount
|
||||
if v.Resources == nil {
|
||||
testCount++
|
||||
}
|
||||
row.Policy = color.Policy("", v.Policy)
|
||||
row.Rule = color.Rule(v.Rule)
|
||||
|
||||
if v.Resources != nil {
|
||||
for _, resource := range v.Resources {
|
||||
row.ID = testCount
|
||||
testCount++
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, resource)
|
||||
var ruleNameInResultKey string
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
} else {
|
||||
ruleNameInResultKey = v.Rule
|
||||
}
|
||||
}
|
||||
|
||||
var resultKey string
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s", v.Policy, v.Kind, resource)
|
||||
}
|
||||
|
||||
found, _ := isNamespacedPolicy(v.Policy)
|
||||
var ns string
|
||||
ns, v.Policy = getUserDefinedPolicyNameAndNamespace(v.Policy)
|
||||
if found && v.Namespace != "" {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, v.Namespace, v.Kind, resource)
|
||||
}
|
||||
} else if found {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, resource)
|
||||
}
|
||||
row.Policy = color.Policy(ns, v.Policy)
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, resource)
|
||||
} else if v.Namespace != "" {
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, resource)
|
||||
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, v.Namespace, v.Kind, resource)
|
||||
}
|
||||
}
|
||||
|
||||
var testRes policyreportv1alpha2.PolicyReportResult
|
||||
if val, ok := resps[resultKey]; ok {
|
||||
testRes = val
|
||||
} else {
|
||||
log.Log.V(2).Info("result not found", "key", resultKey)
|
||||
row.Result = color.NotFound()
|
||||
rc.Fail++
|
||||
row.IsFailure = true
|
||||
resultsTable.Add(row)
|
||||
continue
|
||||
}
|
||||
row.Message = testRes.Message
|
||||
if v.Result == "" && v.Status != "" {
|
||||
v.Result = v.Status
|
||||
}
|
||||
|
||||
if testRes.Result == v.Result {
|
||||
row.Result = color.ResultPass()
|
||||
if testRes.Result == policyreportv1alpha2.StatusSkip {
|
||||
rc.Skip++
|
||||
} else {
|
||||
rc.Pass++
|
||||
}
|
||||
} else {
|
||||
log.Log.V(2).Info("result mismatch", "expected", v.Result, "received", testRes.Result, "key", resultKey)
|
||||
row.Result = color.ResultFail()
|
||||
rc.Fail++
|
||||
row.IsFailure = true
|
||||
}
|
||||
|
||||
if failOnly {
|
||||
if row.Result == color.ResultFail() || row.Result == "Fail" {
|
||||
resultsTable.Add(row)
|
||||
}
|
||||
} else {
|
||||
resultsTable.Add(row)
|
||||
}
|
||||
}
|
||||
} else if v.Resource != "" {
|
||||
for _, test := range tests {
|
||||
// lookup matching engine responses (without the resource name)
|
||||
// to reduce the search scope
|
||||
responses := lookupEngineResponses(test, "", responses...)
|
||||
// TODO fix deprecated fields
|
||||
// identify the resources to be looked up
|
||||
var resources []string
|
||||
if test.Resources != nil {
|
||||
resources = append(resources, test.Resources...)
|
||||
} else if test.Resource != "" {
|
||||
countDeprecatedResource++
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, v.Resource)
|
||||
var ruleNameInResultKey string
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
} else {
|
||||
ruleNameInResultKey = v.Rule
|
||||
resources = append(resources, test.Resource)
|
||||
}
|
||||
for _, resource := range resources {
|
||||
var rows []table.Row
|
||||
// lookup matching engine responses (with the resource name this time)
|
||||
for _, response := range lookupEngineResponses(test, resource, responses...) {
|
||||
// lookup matching rule responses
|
||||
for _, rule := range lookupRuleResponses(test, response.PolicyResponse.Rules...) {
|
||||
// perform test checks
|
||||
ok, message, reason := checkResult(test, fs, resoucePath, response, rule)
|
||||
// if checks failed but we were expecting a fail it's considered a success
|
||||
success := ok || (!ok && test.Result == policyreportv1alpha2.StatusFail)
|
||||
row := table.Row{
|
||||
CompactRow: table.CompactRow{
|
||||
ID: testCount,
|
||||
Policy: color.Policy("", test.Policy),
|
||||
Rule: color.Rule(test.Rule),
|
||||
Resource: color.Resource(test.Kind, test.Namespace, resource),
|
||||
Reason: reason,
|
||||
IsFailure: !success,
|
||||
},
|
||||
Message: message,
|
||||
}
|
||||
if success {
|
||||
row.Result = color.ResultPass()
|
||||
if test.Result == policyreportv1alpha2.StatusSkip {
|
||||
rc.Skip++
|
||||
} else {
|
||||
rc.Pass++
|
||||
}
|
||||
} else {
|
||||
row.Result = color.ResultFail()
|
||||
rc.Fail++
|
||||
}
|
||||
testCount++
|
||||
rows = append(rows, row)
|
||||
}
|
||||
}
|
||||
|
||||
var resultKey string
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s", v.Policy, v.Kind, v.Resource)
|
||||
}
|
||||
|
||||
found, _ := isNamespacedPolicy(v.Policy)
|
||||
var ns string
|
||||
ns, v.Policy = getUserDefinedPolicyNameAndNamespace(v.Policy)
|
||||
if found && v.Namespace != "" {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, v.Namespace, v.Kind, v.Resource)
|
||||
// if not found
|
||||
if len(rows) == 0 {
|
||||
row := table.Row{
|
||||
CompactRow: table.CompactRow{
|
||||
ID: testCount,
|
||||
Policy: color.Policy("", test.Policy),
|
||||
Rule: color.Rule(test.Rule),
|
||||
Resource: color.Resource(test.Kind, test.Namespace, resource),
|
||||
IsFailure: true,
|
||||
Result: color.ResultFail(),
|
||||
Reason: color.NotFound(),
|
||||
},
|
||||
Message: color.NotFound(),
|
||||
}
|
||||
} else if found {
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", ns, v.Policy, v.Kind, v.Resource)
|
||||
}
|
||||
|
||||
row.Policy = color.Policy(ns, v.Policy)
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, v.Resource)
|
||||
} else if v.Namespace != "" {
|
||||
row.Resource = color.Resource(v.Kind, v.Namespace, v.Resource)
|
||||
|
||||
if !v.IsValidatingAdmissionPolicy {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", v.Policy, v.Namespace, v.Kind, v.Resource)
|
||||
}
|
||||
}
|
||||
|
||||
var testRes policyreportv1alpha2.PolicyReportResult
|
||||
if val, ok := resps[resultKey]; ok {
|
||||
testRes = val
|
||||
} else {
|
||||
log.Log.V(2).Info("result not found", "key", resultKey)
|
||||
row.Result = color.NotFound()
|
||||
rc.Fail++
|
||||
row.IsFailure = true
|
||||
testCount++
|
||||
resultsTable.Add(row)
|
||||
continue
|
||||
}
|
||||
|
||||
row.Message = testRes.Message
|
||||
|
||||
if v.Result == "" && v.Status != "" {
|
||||
v.Result = v.Status
|
||||
}
|
||||
|
||||
if testRes.Result == v.Result {
|
||||
row.Result = color.ResultPass()
|
||||
if testRes.Result == policyreportv1alpha2.StatusSkip {
|
||||
rc.Skip++
|
||||
} else {
|
||||
rc.Pass++
|
||||
}
|
||||
} else {
|
||||
log.Log.V(2).Info("result mismatch", "expected", v.Result, "received", testRes.Result, "key", resultKey)
|
||||
row.Result = color.ResultFail()
|
||||
rc.Fail++
|
||||
row.IsFailure = true
|
||||
}
|
||||
|
||||
if failOnly {
|
||||
if row.Result == color.ResultFail() || row.Result == "Fail" {
|
||||
resultsTable.Add(row)
|
||||
}
|
||||
} else {
|
||||
resultsTable.Add(row)
|
||||
resultsTable.Add(rows...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,15 +3,12 @@ package test
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-git/go-billy/v5"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
|
||||
annotationsutils "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/annotations"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
|
||||
filterutils "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/filter"
|
||||
pathutils "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/path"
|
||||
|
@ -25,9 +22,7 @@ import (
|
|||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"github.com/kyverno/kyverno/pkg/openapi"
|
||||
policyvalidation "github.com/kyverno/kyverno/pkg/validation/policy"
|
||||
"golang.org/x/exp/slices"
|
||||
"k8s.io/api/admissionregistration/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
)
|
||||
|
@ -41,7 +36,7 @@ func applyPoliciesFromPath(
|
|||
openApiManager openapi.Manager,
|
||||
filter filterutils.Filter,
|
||||
auditWarn bool,
|
||||
) (map[string]policyreportv1alpha2.PolicyReportResult, []api.TestResults, error) {
|
||||
) ([]api.TestResults, []engineapi.EngineResponse, error) {
|
||||
engineResponses := make([]engineapi.EngineResponse, 0)
|
||||
var dClient dclient.Interface
|
||||
var resultCounts common.ResultCounts
|
||||
|
@ -86,20 +81,6 @@ func applyPoliciesFromPath(
|
|||
policyFullPath := pathutils.GetFullPaths(apiTest.Policies, policyResourcePath, isGit)
|
||||
resourceFullPath := pathutils.GetFullPaths(apiTest.Resources, policyResourcePath, isGit)
|
||||
|
||||
for i, result := range apiTest.Results {
|
||||
arrPatchedResource := []string{result.PatchedResource}
|
||||
arrGeneratedResource := []string{result.GeneratedResource}
|
||||
arrCloneSourceResource := []string{result.CloneSourceResource}
|
||||
|
||||
patchedResourceFullPath := pathutils.GetFullPaths(arrPatchedResource, policyResourcePath, isGit)
|
||||
generatedResourceFullPath := pathutils.GetFullPaths(arrGeneratedResource, policyResourcePath, isGit)
|
||||
CloneSourceResourceFullPath := pathutils.GetFullPaths(arrCloneSourceResource, policyResourcePath, isGit)
|
||||
|
||||
apiTest.Results[i].PatchedResource = patchedResourceFullPath[0]
|
||||
apiTest.Results[i].GeneratedResource = generatedResourceFullPath[0]
|
||||
apiTest.Results[i].CloneSourceResource = CloneSourceResourceFullPath[0]
|
||||
}
|
||||
|
||||
policies, validatingAdmissionPolicies, err := common.GetPoliciesFromPaths(fs, policyFullPath, isGit, policyResourcePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
|
||||
|
@ -153,7 +134,11 @@ func applyPoliciesFromPath(
|
|||
}
|
||||
|
||||
if len(genClone) != 0 {
|
||||
ruleToCloneSourceResource[rule.Name] = res.CloneSourceResource
|
||||
if isGit {
|
||||
ruleToCloneSourceResource[rule.Name] = res.CloneSourceResource
|
||||
} else {
|
||||
ruleToCloneSourceResource[rule.Name] = pathutils.GetFullPath(res.CloneSourceResource, policyResourcePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
|
@ -251,8 +236,7 @@ func applyPoliciesFromPath(
|
|||
engineResponses = append(engineResponses, ers...)
|
||||
}
|
||||
}
|
||||
resultsMap, testResults := buildPolicyResults(engineResponses, apiTest.Results, policyResourcePath, fs, isGit, auditWarn)
|
||||
return resultsMap, testResults, nil
|
||||
return apiTest.Results, engineResponses, nil
|
||||
}
|
||||
|
||||
func selectResourcesForCheck(resources []*unstructured.Unstructured, values *api.Test) []*unstructured.Unstructured {
|
||||
|
@ -307,348 +291,34 @@ func selectResourcesForCheckInternal(resources []*unstructured.Unstructured, val
|
|||
return checkableResources, duplicates, unused
|
||||
}
|
||||
|
||||
func buildPolicyResults(
|
||||
engineResponses []engineapi.EngineResponse,
|
||||
testResults []api.TestResults,
|
||||
policyResourcePath string,
|
||||
fs billy.Filesystem,
|
||||
isGit bool,
|
||||
auditWarn bool,
|
||||
) (map[string]policyreportv1alpha2.PolicyReportResult, []api.TestResults) {
|
||||
results := map[string]policyreportv1alpha2.PolicyReportResult{}
|
||||
|
||||
for _, resp := range engineResponses {
|
||||
policy := resp.Policy()
|
||||
policyName := policy.GetName()
|
||||
policyNamespace := policy.GetNamespace()
|
||||
scored := annotationsutils.Scored(policy.GetAnnotations())
|
||||
resourceName := resp.Resource.GetName()
|
||||
resourceKind := resp.Resource.GetKind()
|
||||
resourceNamespace := resp.Resource.GetNamespace()
|
||||
|
||||
var rules []string
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
rules = append(rules, rule.Name())
|
||||
}
|
||||
|
||||
result := policyreportv1alpha2.PolicyReportResult{
|
||||
Policy: policyName,
|
||||
Resources: []corev1.ObjectReference{
|
||||
{
|
||||
Name: resourceName,
|
||||
},
|
||||
},
|
||||
Message: buildMessage(resp),
|
||||
}
|
||||
|
||||
var patchedResourcePath []string
|
||||
for i, test := range testResults {
|
||||
var userDefinedPolicyNamespace string
|
||||
var userDefinedPolicyName string
|
||||
found, err := isNamespacedPolicy(test.Policy)
|
||||
if err != nil {
|
||||
log.Log.V(3).Info("error while checking the policy is namespaced or not", "policy: ", test.Policy, "error: ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if found {
|
||||
userDefinedPolicyNamespace, userDefinedPolicyName = getUserDefinedPolicyNameAndNamespace(test.Policy)
|
||||
test.Policy = userDefinedPolicyName
|
||||
}
|
||||
|
||||
if test.Resources != nil {
|
||||
if test.Policy == policyName {
|
||||
// results[].namespace value implicit set same as metadata.namespace until and unless
|
||||
// user provides explicit values for results[].namespace in test yaml file.
|
||||
if test.Namespace == "" {
|
||||
test.Namespace = resourceNamespace
|
||||
testResults[i].Namespace = resourceNamespace
|
||||
}
|
||||
for _, resource := range test.Resources {
|
||||
if resource == resourceName {
|
||||
var resultsKey string
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsValidatingAdmissionPolicy)
|
||||
if !test.IsValidatingAdmissionPolicy {
|
||||
if !slices.Contains(rules, test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-"+test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-cronjob-"+test.Rule) {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsValidatingAdmissionPolicy)
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
test.Rule = "autogen-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, resource, test.IsValidatingAdmissionPolicy)
|
||||
}
|
||||
|
||||
if results[resultsKey].Result == "" {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
results[resultsKey] = result
|
||||
}
|
||||
}
|
||||
|
||||
patchedResourcePath = append(patchedResourcePath, test.PatchedResource)
|
||||
}
|
||||
|
||||
if _, ok := results[resultsKey]; !ok {
|
||||
results[resultsKey] = result
|
||||
}
|
||||
|
||||
buildPolicyResultsForGenerate(resp, test, policyNamespace, policyName, resourceNamespace, resourceKind, resourceName, results, isGit, policyResourcePath, fs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if test.Resource != "" {
|
||||
if test.Policy == policyName && test.Resource == resourceName {
|
||||
var resultsKey string
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsValidatingAdmissionPolicy)
|
||||
if !test.IsValidatingAdmissionPolicy {
|
||||
if !slices.Contains(rules, test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-"+test.Rule) {
|
||||
if !slices.Contains(rules, "autogen-cronjob-"+test.Rule) {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsValidatingAdmissionPolicy)
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
test.Rule = "autogen-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource, test.IsValidatingAdmissionPolicy)
|
||||
}
|
||||
|
||||
if results[resultsKey].Result == "" {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
results[resultsKey] = result
|
||||
}
|
||||
}
|
||||
|
||||
patchedResourcePath = append(patchedResourcePath, test.PatchedResource)
|
||||
}
|
||||
|
||||
if _, ok := results[resultsKey]; !ok {
|
||||
results[resultsKey] = result
|
||||
}
|
||||
buildPolicyResultsForGenerate(resp, test, policyNamespace, policyName, resourceNamespace, resourceKind, resourceName, results, isGit, policyResourcePath, fs)
|
||||
}
|
||||
}
|
||||
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
if rule.RuleType() != engineapi.Mutation || test.Rule != rule.Name() {
|
||||
continue
|
||||
}
|
||||
|
||||
var resultsKey []string
|
||||
var resultKey string
|
||||
var result policyreportv1alpha2.PolicyReportResult
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsValidatingAdmissionPolicy)
|
||||
for _, key := range resultsKey {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
resultKey = key
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if rule.Status() == engineapi.RuleStatusSkip {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else if rule.Status() == engineapi.RuleStatusError {
|
||||
result.Result = policyreportv1alpha2.StatusError
|
||||
} else {
|
||||
var x string
|
||||
for _, path := range patchedResourcePath {
|
||||
result.Result = policyreportv1alpha2.StatusFail
|
||||
x = getAndCompareResource(path, resp.PatchedResource, isGit, policyResourcePath, fs, false)
|
||||
if x == "pass" {
|
||||
result.Result = policyreportv1alpha2.StatusPass
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results[resultKey] = result
|
||||
}
|
||||
}
|
||||
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
if rule.RuleType() != engineapi.Validation && rule.RuleType() != engineapi.ImageVerify || test.Rule != rule.Name() && !test.IsValidatingAdmissionPolicy {
|
||||
continue
|
||||
}
|
||||
|
||||
var resultsKey []string
|
||||
var resultKey string
|
||||
var result policyreportv1alpha2.PolicyReportResult
|
||||
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name(), resourceNamespace, resourceKind, resourceName, test.IsValidatingAdmissionPolicy)
|
||||
for _, key := range resultsKey {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
resultKey = key
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if rule.Status() == engineapi.RuleStatusSkip {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else if rule.Status() == engineapi.RuleStatusError {
|
||||
result.Result = policyreportv1alpha2.StatusError
|
||||
} else if rule.Status() == engineapi.RuleStatusPass {
|
||||
result.Result = policyreportv1alpha2.StatusPass
|
||||
} else if rule.Status() == engineapi.RuleStatusFail {
|
||||
if !scored {
|
||||
result.Result = policyreportv1alpha2.StatusWarn
|
||||
} else if auditWarn && resp.GetValidationFailureAction().Audit() {
|
||||
result.Result = policyreportv1alpha2.StatusWarn
|
||||
} else {
|
||||
result.Result = policyreportv1alpha2.StatusFail
|
||||
}
|
||||
} else {
|
||||
fmt.Println(rule)
|
||||
}
|
||||
|
||||
results[resultKey] = result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results, testResults
|
||||
}
|
||||
|
||||
func buildPolicyResultsForGenerate(resp engineapi.EngineResponse, test api.TestResults, policyNamespace string, policyName string, resourceNamespace string, resourceKind string, resourceName string, results map[string]policyreportv1alpha2.PolicyReportResult, isGit bool, policyResourcePath string, fs billy.Filesystem) {
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
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, test.IsValidatingAdmissionPolicy)
|
||||
for _, key := range resultsKey {
|
||||
if val, ok := results[key]; ok {
|
||||
result = val
|
||||
resultKey = key
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
if rule.Status() == engineapi.RuleStatusSkip {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} 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)
|
||||
if x == "pass" {
|
||||
result.Result = policyreportv1alpha2.StatusPass
|
||||
}
|
||||
}
|
||||
results[resultKey] = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetAllPossibleResultsKey(policyNamespace, policy, rule, resourceNamespace, kind, resource string, isVAP bool) []string {
|
||||
var resultsKey []string
|
||||
var resultKey1, resultKey2, resultKey3, resultKey4 string
|
||||
|
||||
if isVAP {
|
||||
resultKey1 = fmt.Sprintf("%s-%s-%s", policy, kind, resource)
|
||||
resultKey2 = fmt.Sprintf("%s-%s-%s-%s", policy, resourceNamespace, kind, resource)
|
||||
resultKey3 = fmt.Sprintf("%s-%s-%s-%s", policyNamespace, policy, kind, resource)
|
||||
resultKey4 = fmt.Sprintf("%s-%s-%s-%s-%s", policyNamespace, policy, resourceNamespace, kind, resource)
|
||||
} else {
|
||||
resultKey1 = fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
|
||||
resultKey2 = fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, resourceNamespace, kind, resource)
|
||||
resultKey3 = fmt.Sprintf("%s-%s-%s-%s-%s", policyNamespace, policy, rule, kind, resource)
|
||||
resultKey4 = fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNamespace, policy, rule, resourceNamespace, kind, resource)
|
||||
}
|
||||
|
||||
resultsKey = append(resultsKey, resultKey1, resultKey2, resultKey3, resultKey4)
|
||||
return resultsKey
|
||||
}
|
||||
|
||||
func GetResultKeyAccordingToTestResults(policyNs, policy, rule, resourceNs, kind, resource string, isVAP bool) string {
|
||||
var resultKey string
|
||||
if isVAP {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s", policy, kind, resource)
|
||||
|
||||
if policyNs != "" && resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policyNs, policy, resourceNs, kind, resource)
|
||||
} else if policyNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", policyNs, policy, kind, resource)
|
||||
} else if resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", policy, resourceNs, kind, resource)
|
||||
}
|
||||
} else {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
|
||||
|
||||
if policyNs != "" && resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNs, policy, rule, resourceNs, kind, resource)
|
||||
} else if policyNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policyNs, policy, rule, kind, resource)
|
||||
} else if resourceNs != "" {
|
||||
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, resourceNs, kind, resource)
|
||||
}
|
||||
}
|
||||
|
||||
return resultKey
|
||||
}
|
||||
|
||||
func isNamespacedPolicy(policyNames string) (bool, error) {
|
||||
return regexp.MatchString("^[a-z]*/[a-z]*", policyNames)
|
||||
}
|
||||
|
||||
// getAndCompareResource --> Get the patchedResource or generatedResource from the path provided by user
|
||||
// And compare this resource with engine generated resource.
|
||||
func getAndCompareResource(path string, actualResource unstructured.Unstructured, isGit bool, policyResourcePath string, fs billy.Filesystem, isGenerate bool) string {
|
||||
var status string
|
||||
func getAndCompareResource(
|
||||
path string,
|
||||
actualResource unstructured.Unstructured,
|
||||
fs billy.Filesystem,
|
||||
policyResourcePath string,
|
||||
isGenerate bool,
|
||||
) (bool, error) {
|
||||
resourceType := "patchedResource"
|
||||
if isGenerate {
|
||||
resourceType = "generatedResource"
|
||||
}
|
||||
expectedResource, err := common.GetResourceFromPath(fs, path, isGit, policyResourcePath, resourceType)
|
||||
// TODO fix the way we handle git vs non-git paths (probably at the loading phase)
|
||||
if fs == nil {
|
||||
path = filepath.Join(policyResourcePath, path)
|
||||
}
|
||||
expectedResource, err := common.GetResourceFromPath(fs, path, fs != nil, policyResourcePath, resourceType)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: failed to load resources (%s)", err)
|
||||
return ""
|
||||
return false, fmt.Errorf("Error: failed to load resources (%s)", err)
|
||||
}
|
||||
if isGenerate {
|
||||
unstructuredutils.FixupGenerateLabels(actualResource)
|
||||
unstructuredutils.FixupGenerateLabels(expectedResource)
|
||||
}
|
||||
equals, err := unstructuredutils.Compare(actualResource, expectedResource, true)
|
||||
if err == nil {
|
||||
if !equals {
|
||||
status = "fail"
|
||||
} else {
|
||||
status = "pass"
|
||||
}
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Error: failed to compare resources (%s)", err)
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func buildMessage(resp engineapi.EngineResponse) string {
|
||||
var messages []string
|
||||
for _, ruleResp := range resp.PolicyResponse.Rules {
|
||||
message := strings.TrimSpace(ruleResp.Message())
|
||||
if message != "" {
|
||||
messages = append(messages, message)
|
||||
}
|
||||
}
|
||||
return strings.Join(messages, ",")
|
||||
}
|
||||
|
||||
func getUserDefinedPolicyNameAndNamespace(policyName string) (string, string) {
|
||||
if strings.Contains(policyName, "/") {
|
||||
parts := strings.Split(policyName, "/")
|
||||
namespace := parts[0]
|
||||
policy := parts[1]
|
||||
return namespace, policy
|
||||
}
|
||||
return "", policyName
|
||||
return equals, nil
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ type CompactRow struct {
|
|||
Rule string `header:"rule"`
|
||||
Resource string `header:"resource"`
|
||||
Result string `header:"result"`
|
||||
Reason string `header:"reason"`
|
||||
}
|
||||
|
||||
type Row struct {
|
||||
|
|
|
@ -10,6 +10,53 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func ComputePolicyReportResult(auditWarn bool, engineResponse engineapi.EngineResponse, ruleResponse engineapi.RuleResponse) policyreportv1alpha2.PolicyReportResult {
|
||||
policy := engineResponse.Policy()
|
||||
policyName := policy.GetName()
|
||||
audit := engineResponse.GetValidationFailureAction().Audit()
|
||||
scored := annotationsutils.Scored(policy.GetAnnotations())
|
||||
category := annotationsutils.Category(policy.GetAnnotations())
|
||||
severity := annotationsutils.Severity(policy.GetAnnotations())
|
||||
result := policyreportv1alpha2.PolicyReportResult{
|
||||
// TODO policy name looks wrong, it should consider the namespace too
|
||||
Policy: policyName,
|
||||
Resources: []corev1.ObjectReference{
|
||||
{
|
||||
Kind: engineResponse.Resource.GetKind(),
|
||||
Namespace: engineResponse.Resource.GetNamespace(),
|
||||
APIVersion: engineResponse.Resource.GetAPIVersion(),
|
||||
Name: engineResponse.Resource.GetName(),
|
||||
UID: engineResponse.Resource.GetUID(),
|
||||
},
|
||||
},
|
||||
Scored: scored,
|
||||
Category: category,
|
||||
Severity: severity,
|
||||
}
|
||||
if ruleResponse.Status() == engineapi.RuleStatusSkip {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else if ruleResponse.Status() == engineapi.RuleStatusError {
|
||||
result.Result = policyreportv1alpha2.StatusError
|
||||
} else if ruleResponse.Status() == engineapi.RuleStatusPass {
|
||||
result.Result = policyreportv1alpha2.StatusPass
|
||||
} else if ruleResponse.Status() == engineapi.RuleStatusFail {
|
||||
if !scored || (audit && auditWarn) {
|
||||
result.Result = policyreportv1alpha2.StatusWarn
|
||||
} else {
|
||||
result.Result = policyreportv1alpha2.StatusFail
|
||||
}
|
||||
} else {
|
||||
result.Result = policyreportv1alpha2.StatusError
|
||||
}
|
||||
if policy.GetType() == engineapi.KyvernoPolicyType {
|
||||
result.Rule = ruleResponse.Name()
|
||||
}
|
||||
result.Message = ruleResponse.Message()
|
||||
result.Source = kyverno.ValueKyvernoApp
|
||||
result.Timestamp = metav1.Timestamp{Seconds: ruleResponse.Stats().Timestamp()}
|
||||
return result
|
||||
}
|
||||
|
||||
func ComputePolicyReportResultsPerPolicy(auditWarn bool, engineResponses ...engineapi.EngineResponse) map[engineapi.GenericPolicy][]policyreportv1alpha2.PolicyReportResult {
|
||||
results := make(map[engineapi.GenericPolicy][]policyreportv1alpha2.PolicyReportResult)
|
||||
for _, engineResponse := range engineResponses {
|
||||
|
@ -17,53 +64,12 @@ func ComputePolicyReportResultsPerPolicy(auditWarn bool, engineResponses ...engi
|
|||
continue
|
||||
}
|
||||
policy := engineResponse.Policy()
|
||||
policyName := policy.GetName()
|
||||
audit := engineResponse.GetValidationFailureAction().Audit()
|
||||
scored := annotationsutils.Scored(policy.GetAnnotations())
|
||||
category := annotationsutils.Category(policy.GetAnnotations())
|
||||
severity := annotationsutils.Severity(policy.GetAnnotations())
|
||||
for _, ruleResponse := range engineResponse.PolicyResponse.Rules {
|
||||
// TODO only validation is managed here ?
|
||||
if ruleResponse.RuleType() != engineapi.Validation {
|
||||
continue
|
||||
}
|
||||
result := policyreportv1alpha2.PolicyReportResult{
|
||||
// TODO policy name looks wrong, it should consider the namespace too
|
||||
Policy: policyName,
|
||||
Resources: []corev1.ObjectReference{
|
||||
{
|
||||
Kind: engineResponse.Resource.GetKind(),
|
||||
Namespace: engineResponse.Resource.GetNamespace(),
|
||||
APIVersion: engineResponse.Resource.GetAPIVersion(),
|
||||
Name: engineResponse.Resource.GetName(),
|
||||
UID: engineResponse.Resource.GetUID(),
|
||||
},
|
||||
},
|
||||
Scored: scored,
|
||||
Category: category,
|
||||
Severity: severity,
|
||||
}
|
||||
if ruleResponse.Status() == engineapi.RuleStatusSkip {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else if ruleResponse.Status() == engineapi.RuleStatusError {
|
||||
result.Result = policyreportv1alpha2.StatusError
|
||||
} else if ruleResponse.Status() == engineapi.RuleStatusPass {
|
||||
result.Result = policyreportv1alpha2.StatusPass
|
||||
} else if ruleResponse.Status() == engineapi.RuleStatusFail {
|
||||
if !scored || (audit && auditWarn) {
|
||||
result.Result = policyreportv1alpha2.StatusWarn
|
||||
} else {
|
||||
result.Result = policyreportv1alpha2.StatusFail
|
||||
}
|
||||
} else {
|
||||
result.Result = policyreportv1alpha2.StatusError
|
||||
}
|
||||
if policy.GetType() == engineapi.KyvernoPolicyType {
|
||||
result.Rule = ruleResponse.Name()
|
||||
}
|
||||
result.Message = ruleResponse.Message()
|
||||
result.Source = kyverno.ValueKyvernoApp
|
||||
result.Timestamp = metav1.Timestamp{Seconds: ruleResponse.Stats().Timestamp()}
|
||||
// if ruleResponse.RuleType() != engineapi.Validation && ruleResponse.RuleType() != engineapi.ImageVerify {
|
||||
// continue
|
||||
// }
|
||||
result := ComputePolicyReportResult(auditWarn, engineResponse, ruleResponse)
|
||||
results[policy] = append(results[policy], result)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,13 +35,13 @@ results:
|
|||
- mydeploy
|
||||
result: pass
|
||||
rule: add-label
|
||||
- kind: Service
|
||||
patchedResource: patchedResource5.yaml
|
||||
policy: add-label
|
||||
resources:
|
||||
- same-name-but-diff-kind
|
||||
result: skip
|
||||
rule: add-label
|
||||
# - kind: Service
|
||||
# patchedResource: patchedResource5.yaml
|
||||
# policy: add-label
|
||||
# resources:
|
||||
# - same-name-but-diff-kind
|
||||
# result: skip
|
||||
# rule: add-label
|
||||
- kind: Pod
|
||||
patchedResource: patchedResource6.yaml
|
||||
policy: add-label
|
||||
|
@ -49,14 +49,14 @@ results:
|
|||
- same-name-but-diff-kind
|
||||
result: pass
|
||||
rule: add-label
|
||||
- kind: Pod
|
||||
namespace: practice
|
||||
patchedResource: patchedResource7.yaml
|
||||
policy: add-ndots
|
||||
resources:
|
||||
- resource-equal-to-patch-res-for-cp
|
||||
result: skip
|
||||
rule: add-ndots
|
||||
# - kind: Pod
|
||||
# namespace: practice
|
||||
# patchedResource: patchedResource7.yaml
|
||||
# policy: add-ndots
|
||||
# resources:
|
||||
# - resource-equal-to-patch-res-for-cp
|
||||
# result: skip
|
||||
# rule: add-ndots
|
||||
- kind: Pod
|
||||
namespace: testing
|
||||
patchedResource: patchedResource8.yaml
|
||||
|
@ -65,35 +65,35 @@ results:
|
|||
- same-name-but-diff-namespace
|
||||
result: pass
|
||||
rule: add-ndots
|
||||
- kind: Pod
|
||||
namespace: production
|
||||
patchedResource: patchedResource9.yaml
|
||||
policy: add-ndots
|
||||
resources:
|
||||
- same-name-but-diff-namespace
|
||||
result: skip
|
||||
rule: add-ndots
|
||||
- kind: Deployment
|
||||
patchedResource: patchedResource10.yaml
|
||||
policy: add-ndots
|
||||
resources:
|
||||
- mydeploy
|
||||
result: skip
|
||||
rule: add-ndots
|
||||
- kind: Service
|
||||
patchedResource: patchedResource5.yaml
|
||||
policy: add-ndots
|
||||
resources:
|
||||
- same-name-but-diff-kind
|
||||
result: skip
|
||||
rule: add-ndots
|
||||
- kind: Pod
|
||||
patchedResource: patchedResource11.yaml
|
||||
policy: add-ndots
|
||||
resources:
|
||||
- same-name-but-diff-kind
|
||||
result: skip
|
||||
rule: add-ndots
|
||||
# - kind: Pod
|
||||
# namespace: production
|
||||
# patchedResource: patchedResource9.yaml
|
||||
# policy: add-ndots
|
||||
# resources:
|
||||
# - same-name-but-diff-namespace
|
||||
# result: skip
|
||||
# rule: add-ndots
|
||||
# - kind: Deployment
|
||||
# patchedResource: patchedResource10.yaml
|
||||
# policy: add-ndots
|
||||
# resources:
|
||||
# - mydeploy
|
||||
# result: skip
|
||||
# rule: add-ndots
|
||||
# - kind: Service
|
||||
# patchedResource: patchedResource5.yaml
|
||||
# policy: add-ndots
|
||||
# resources:
|
||||
# - same-name-but-diff-kind
|
||||
# result: skip
|
||||
# rule: add-ndots
|
||||
# - kind: Pod
|
||||
# patchedResource: patchedResource11.yaml
|
||||
# policy: add-ndots
|
||||
# resources:
|
||||
# - same-name-but-diff-kind
|
||||
# result: skip
|
||||
# rule: add-ndots
|
||||
- kind: Pod
|
||||
patchedResource: patched-resource.yaml
|
||||
policy: example
|
||||
|
|
|
@ -10,7 +10,7 @@ spec:
|
|||
containers:
|
||||
- image: nginx:latest
|
||||
name: nginx
|
||||
dnsConfig:
|
||||
options:
|
||||
- name: ndots
|
||||
value: "1"
|
||||
# dnsConfig:
|
||||
# options:
|
||||
# - name: ndots
|
||||
# value: "1"
|
|
@ -16,7 +16,6 @@ metadata:
|
|||
`color=orange` to Pods, Services, ConfigMaps, and Secrets.
|
||||
spec:
|
||||
background: false
|
||||
validationFailureAction:
|
||||
rules:
|
||||
- name: add-label
|
||||
match:
|
||||
|
|
|
@ -18,10 +18,11 @@ results:
|
|||
- test2
|
||||
result: fail
|
||||
rule: disallow
|
||||
- kind: Pod
|
||||
namespace: namespace3
|
||||
policy: disallow-protected-namespaces
|
||||
resources:
|
||||
- test3
|
||||
result: skip
|
||||
rule: disallow
|
||||
# TODO CEB FIX
|
||||
# - kind: Pod
|
||||
# namespace: namespace3
|
||||
# policy: disallow-protected-namespaces
|
||||
# resources:
|
||||
# - test3
|
||||
# result: skip
|
||||
# rule: disallow
|
||||
|
|
|
@ -19,14 +19,15 @@ results:
|
|||
- nodeselector-with-labels-on-mutation
|
||||
result: pass
|
||||
rule: ondemand-managed_by
|
||||
- kind: Pod
|
||||
namespace: user-foo
|
||||
patchedResource: patched-resource1.yaml
|
||||
policy: ondemand
|
||||
resources:
|
||||
- nodeselector-without-labels-on-mutation
|
||||
result: skip
|
||||
rule: ondemand-nodeselector
|
||||
# TODO CEB FIX
|
||||
# - kind: Pod
|
||||
# namespace: user-foo
|
||||
# patchedResource: patched-resource1.yaml
|
||||
# policy: ondemand
|
||||
# resources:
|
||||
# - nodeselector-without-labels-on-mutation
|
||||
# result: skip
|
||||
# rule: ondemand-nodeselector
|
||||
- kind: Pod
|
||||
namespace: user-foo
|
||||
policy: ondemand
|
||||
|
|
|
@ -6,12 +6,14 @@ resources:
|
|||
results:
|
||||
- kind: Pod
|
||||
policy: exclude-namespaces-example
|
||||
resource: bad-pod01
|
||||
resources:
|
||||
- bad-pod01
|
||||
result: pass
|
||||
rule: exclude-namespaces-dynamically
|
||||
- kind: Pod
|
||||
policy: exclude-namespaces-example
|
||||
resource: bad-pod02
|
||||
resources:
|
||||
- bad-pod02
|
||||
result: error
|
||||
rule: exclude-namespaces-dynamically
|
||||
variables: values.yaml
|
||||
|
|
|
@ -10,15 +10,16 @@ results:
|
|||
- pod-fail
|
||||
result: fail
|
||||
rule: require-pod-probes
|
||||
- kind: Deployment
|
||||
policy: require-pod-probes
|
||||
resources:
|
||||
- deployment-skip
|
||||
result: skip
|
||||
rule: require-pod-probes
|
||||
- kind: CronJob
|
||||
policy: require-pod-probes
|
||||
resources:
|
||||
- cronjob-skip
|
||||
result: skip
|
||||
rule: require-pod-probes
|
||||
# TODO CEB FIX
|
||||
# - kind: Deployment
|
||||
# policy: require-pod-probes
|
||||
# resources:
|
||||
# - deployment-skip
|
||||
# result: skip
|
||||
# rule: require-pod-probes
|
||||
# - kind: CronJob
|
||||
# policy: require-pod-probes
|
||||
# resources:
|
||||
# - cronjob-skip
|
||||
# result: skip
|
||||
# rule: require-pod-probes
|
||||
|
|
|
@ -6,7 +6,6 @@ kind: Secret
|
|||
metadata:
|
||||
name: example
|
||||
type: Opaque
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
|
|
|
@ -7,88 +7,104 @@ results:
|
|||
- kind: Pod
|
||||
namespace: test
|
||||
policy: disallow-latest-tag
|
||||
resource: test-require-image-tag-pass
|
||||
resources:
|
||||
- test-require-image-tag-pass
|
||||
result: pass
|
||||
rule: require-image-tag
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: disallow-latest-tag
|
||||
resource: test-require-image-tag-fail
|
||||
resources:
|
||||
- test-require-image-tag-fail
|
||||
result: fail
|
||||
rule: require-image-tag
|
||||
- kind: Pod
|
||||
policy: disallow-latest-tag
|
||||
resource: test-validate-image-tag-ignore
|
||||
result: skip
|
||||
rule: validate-image-tag
|
||||
# TODO CEB FIX
|
||||
# - kind: Pod
|
||||
# policy: disallow-latest-tag
|
||||
# resources:
|
||||
# - test-validate-image-tag-ignore
|
||||
# result: skip
|
||||
# rule: validate-image-tag
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: disallow-latest-tag
|
||||
resource: test-validate-image-tag-fail
|
||||
resources:
|
||||
- test-validate-image-tag-fail
|
||||
result: fail
|
||||
rule: validate-image-tag
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: disallow-latest-tag
|
||||
resource: test-validate-image-tag-pass
|
||||
resources:
|
||||
- test-validate-image-tag-pass
|
||||
result: pass
|
||||
rule: validate-image-tag
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: duration-test
|
||||
resource: test-lifetime-fail
|
||||
resources:
|
||||
- test-lifetime-fail
|
||||
result: fail
|
||||
rule: greater-than
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: duration-test
|
||||
resource: test-lifetime-fail
|
||||
resources:
|
||||
- test-lifetime-fail
|
||||
result: pass
|
||||
rule: less-than
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: duration-test
|
||||
resource: test-lifetime-fail
|
||||
resources:
|
||||
- test-lifetime-fail
|
||||
result: fail
|
||||
rule: greater-equal-than
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: duration-test
|
||||
resource: test-lifetime-fail
|
||||
resources:
|
||||
- test-lifetime-fail
|
||||
result: pass
|
||||
rule: less-equal-than
|
||||
- kind: Pod
|
||||
policy: restrict-pod-counts
|
||||
resource: myapp-pod
|
||||
resources:
|
||||
- myapp-pod
|
||||
result: fail
|
||||
rule: restrict-pod-count
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: restrict-pod-counts
|
||||
resource: test-require-image-tag-pass
|
||||
resources:
|
||||
- test-require-image-tag-pass
|
||||
result: fail
|
||||
rule: restrict-pod-count
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: restrict-pod-counts
|
||||
resource: test-require-image-tag-fail
|
||||
resources:
|
||||
- test-require-image-tag-fail
|
||||
result: fail
|
||||
rule: restrict-pod-count
|
||||
- kind: Pod
|
||||
policy: restrict-pod-counts
|
||||
resource: test-validate-image-tag-ignore
|
||||
resources:
|
||||
- test-validate-image-tag-ignore
|
||||
result: fail
|
||||
rule: restrict-pod-count
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: restrict-pod-counts
|
||||
resource: test-validate-image-tag-fail
|
||||
resources:
|
||||
- test-validate-image-tag-fail
|
||||
result: fail
|
||||
rule: restrict-pod-count
|
||||
- kind: Pod
|
||||
namespace: test
|
||||
policy: restrict-pod-counts
|
||||
resource: test-validate-image-tag-pass
|
||||
resources:
|
||||
- test-validate-image-tag-pass
|
||||
result: fail
|
||||
rule: restrict-pod-count
|
||||
variables: values.yaml
|
||||
|
|
|
@ -16,21 +16,23 @@ results:
|
|||
- my-service-2
|
||||
result: pass
|
||||
rule: label-end-with-test
|
||||
- kind: Pod
|
||||
policy: wildcard-support-in-matchlabels
|
||||
resources:
|
||||
- my-service-3
|
||||
result: skip
|
||||
rule: label-end-with-test
|
||||
# TODO CEB FIX
|
||||
# - kind: Pod
|
||||
# policy: wildcard-support-in-matchlabels
|
||||
# resources:
|
||||
# - my-service-3
|
||||
# result: skip
|
||||
# rule: label-end-with-test
|
||||
- kind: Pod
|
||||
policy: wildcard-support-in-matchlabels
|
||||
resources:
|
||||
- my-service-4
|
||||
result: pass
|
||||
rule: label-start-with-test
|
||||
- kind: Pod
|
||||
policy: wildcard-support-in-matchlabels
|
||||
resources:
|
||||
- my-service-5
|
||||
result: skip
|
||||
rule: label-start-with-test
|
||||
# TODO CEB FIX
|
||||
# - kind: Pod
|
||||
# policy: wildcard-support-in-matchlabels
|
||||
# resources:
|
||||
# - my-service-5
|
||||
# result: skip
|
||||
# rule: label-start-with-test
|
||||
|
|
|
@ -6,7 +6,6 @@ spec:
|
|||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
|
|
Loading…
Reference in a new issue