mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-29 02:45:06 +00:00
refactor: introduce report utils package and use it in cli apply (#8203)
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
b6c1718479
commit
33d5c81a7d
11 changed files with 612 additions and 720 deletions
|
@ -22,20 +22,20 @@ import (
|
|||
|
||||
// Status specifies state of a policy result
|
||||
const (
|
||||
StatusPass = "pass"
|
||||
StatusFail = "fail"
|
||||
StatusWarn = "warn"
|
||||
StatusError = "error"
|
||||
StatusSkip = "skip"
|
||||
StatusPass PolicyResult = "pass"
|
||||
StatusFail PolicyResult = "fail"
|
||||
StatusWarn PolicyResult = "warn"
|
||||
StatusError PolicyResult = "error"
|
||||
StatusSkip PolicyResult = "skip"
|
||||
)
|
||||
|
||||
// Severity specifies priority of a policy result
|
||||
const (
|
||||
SeverityCritical = "critical"
|
||||
SeverityHigh = "high"
|
||||
SeverityMedium = "medium"
|
||||
SeverityLow = "low"
|
||||
SeverityInfo = "info"
|
||||
SeverityCritical PolicySeverity = "critical"
|
||||
SeverityHigh PolicySeverity = "high"
|
||||
SeverityMedium PolicySeverity = "medium"
|
||||
SeverityLow PolicySeverity = "low"
|
||||
SeverityInfo PolicySeverity = "info"
|
||||
)
|
||||
|
||||
// PolicyReportSummary provides a status count summary
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test/api"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/color"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
|
||||
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"
|
||||
"github.com/kyverno/kyverno/pkg/autogen"
|
||||
|
@ -509,12 +510,12 @@ func printSkippedAndInvalidPolicies(skipInvalidPolicies SkippedInvalidPolicies)
|
|||
}
|
||||
|
||||
func printReport(engineResponses []engineapi.EngineResponse, auditWarn bool) {
|
||||
clustered, namespaced := buildPolicyReports(auditWarn, engineResponses...)
|
||||
clustered, namespaced := reportutils.ComputePolicyReports(auditWarn, engineResponses...)
|
||||
if len(clustered) > 0 || len(namespaced) > 0 {
|
||||
fmt.Println(divider)
|
||||
fmt.Println("POLICY REPORT:")
|
||||
fmt.Println(divider)
|
||||
report := mergeClusterReport(clustered, namespaced)
|
||||
report := reportutils.MergeClusterReports(clustered, namespaced)
|
||||
yamlReport, _ := yaml.Marshal(report)
|
||||
fmt.Println(string(yamlReport))
|
||||
} else {
|
||||
|
|
|
@ -7,14 +7,15 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
preport "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
reportutils "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/report"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func Test_Apply(t *testing.T) {
|
||||
type TestCase struct {
|
||||
gitBranch string
|
||||
expectedPolicyReports []preport.PolicyReport
|
||||
expectedPolicyReports []policyreportv1alpha2.PolicyReport
|
||||
config ApplyCommandConfig
|
||||
stdinFile string
|
||||
}
|
||||
|
@ -23,334 +24,282 @@ func Test_Apply(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
defer func() { _ = os.Remove(localFileName) }()
|
||||
|
||||
testcases := []*TestCase{
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
testcases := []*TestCase{{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{localFileName},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{localFileName},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/apply/policies"},
|
||||
ResourcePaths: []string{"../../../../test/cli/apply/resource"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 1,
|
||||
Skip: 8,
|
||||
Error: 0,
|
||||
Warn: 2,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/apply/policies"},
|
||||
ResourcePaths: []string{"../../../../test/cli/apply/resource"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
AuditWarn: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 1,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 1,
|
||||
Skip: 8,
|
||||
Error: 0,
|
||||
Warn: 2,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
AuditWarn: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"-"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
AuditWarn: true,
|
||||
},
|
||||
stdinFile: "../../../../test/best_practices/disallow_latest_tag.yaml",
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 1,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 1,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"-"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
AuditWarn: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"-"},
|
||||
PolicyReport: true,
|
||||
AuditWarn: true,
|
||||
},
|
||||
stdinFile: "../../../../test/resources/pod_with_latest_tag.yaml",
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 1,
|
||||
},
|
||||
},
|
||||
stdinFile: "../../../../test/best_practices/disallow_latest_tag.yaml",
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 1,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"-"},
|
||||
PolicyReport: true,
|
||||
AuditWarn: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"https://github.com/kyverno/policies/openshift/team-validate-ns-name/"},
|
||||
ResourcePaths: []string{"../../../../test/openshift/team-validate-ns-name.yaml"},
|
||||
GitBranch: "main",
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
stdinFile: "../../../../test/resources/pod_with_latest_tag.yaml",
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 1,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"https://github.com/kyverno/policies/openshift/team-validate-ns-name/"},
|
||||
ResourcePaths: []string{"../../../../test/openshift/team-validate-ns-name.yaml"},
|
||||
GitBranch: "main",
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/apply/policies-set"},
|
||||
ResourcePaths: []string{"../../../../test/cli/apply/resources-set"},
|
||||
Variables: []string{"request.operation=UPDATE"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 0,
|
||||
Skip: 4,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/apply/policies-set"},
|
||||
ResourcePaths: []string{"../../../../test/cli/apply/resources-set"},
|
||||
Variables: []string{"request.operation=UPDATE"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment1.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 0,
|
||||
Skip: 4,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment1.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment2.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 0,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment2.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod1.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 0,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod1.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod2.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 0,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod2.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment1.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 0,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment1.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment2.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 0,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 1,
|
||||
Fail: 0,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment2.yaml"},
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"https://github.com/kyverno/policies/best-practices/require-labels/", "../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
GitBranch: "main",
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 1,
|
||||
Skip: 2,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 0,
|
||||
Fail: 1,
|
||||
Skip: 0,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"https://github.com/kyverno/policies/best-practices/require-labels/", "../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
GitBranch: "main",
|
||||
PolicyReport: true,
|
||||
},
|
||||
{
|
||||
// Same as the above test case but the policy paths are reordered
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml", "https://github.com/kyverno/policies/best-practices/require-labels/"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
GitBranch: "main",
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []preport.PolicyReport{
|
||||
{
|
||||
Summary: preport.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 1,
|
||||
Skip: 2,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 1,
|
||||
Skip: 2,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}, {
|
||||
// Same as the above test case but the policy paths are reordered
|
||||
config: ApplyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../test/best_practices/disallow_latest_tag.yaml", "https://github.com/kyverno/policies/best-practices/require-labels/"},
|
||||
ResourcePaths: []string{"../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
GitBranch: "main",
|
||||
PolicyReport: true,
|
||||
},
|
||||
expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{
|
||||
Summary: policyreportv1alpha2.PolicyReportSummary{
|
||||
Pass: 2,
|
||||
Fail: 1,
|
||||
Skip: 2,
|
||||
Error: 0,
|
||||
Warn: 0,
|
||||
},
|
||||
}},
|
||||
}}
|
||||
|
||||
compareSummary := func(expected policyreportv1alpha2.PolicyReportSummary, actual policyreportv1alpha2.PolicyReportSummary, desc string) {
|
||||
assert.Equal(t, actual.Pass, expected.Pass, desc)
|
||||
assert.Equal(t, actual.Fail, expected.Fail, desc)
|
||||
assert.Equal(t, actual.Skip, expected.Skip, desc)
|
||||
assert.Equal(t, actual.Warn, expected.Warn, desc)
|
||||
assert.Equal(t, actual.Error, expected.Error, desc)
|
||||
}
|
||||
|
||||
compareSummary := func(expected preport.PolicyReportSummary, actual preport.PolicyReportSummary, desc string) {
|
||||
assert.Equal(t, int64(actual.Pass), int64(expected.Pass), desc)
|
||||
assert.Equal(t, int64(actual.Fail), int64(expected.Fail), desc)
|
||||
assert.Equal(t, int64(actual.Skip), int64(expected.Skip), desc)
|
||||
assert.Equal(t, int64(actual.Warn), int64(expected.Warn), desc)
|
||||
assert.Equal(t, int64(actual.Error), int64(expected.Error), desc)
|
||||
}
|
||||
|
||||
verifyTestcase := func(t *testing.T, tc *TestCase, compareSummary func(preport.PolicyReportSummary, preport.PolicyReportSummary, string)) {
|
||||
verifyTestcase := func(t *testing.T, tc *TestCase, compareSummary func(policyreportv1alpha2.PolicyReportSummary, policyreportv1alpha2.PolicyReportSummary, string)) {
|
||||
if tc.stdinFile != "" {
|
||||
oldStdin := os.Stdin
|
||||
input, err := os.OpenFile(tc.stdinFile, os.O_RDONLY, 0)
|
||||
|
@ -369,12 +318,16 @@ func Test_Apply(t *testing.T) {
|
|||
}
|
||||
|
||||
defer func() { osExit = os.Exit }()
|
||||
_, _, _, info, err := tc.config.applyCommandHelper()
|
||||
_, _, _, responses, err := tc.config.applyCommandHelper()
|
||||
assert.NilError(t, err, desc)
|
||||
|
||||
clustered, _ := buildPolicyReports(tc.config.AuditWarn, info...)
|
||||
clustered, _ := reportutils.ComputePolicyReports(tc.config.AuditWarn, responses...)
|
||||
assert.Assert(t, len(clustered) > 0, "policy reports should not be empty: %s", desc)
|
||||
for i, resp := range clustered {
|
||||
combined := []policyreportv1alpha2.ClusterPolicyReport{
|
||||
reportutils.MergeClusterReports(clustered, nil),
|
||||
}
|
||||
assert.Equal(t, len(combined), len(tc.expectedPolicyReports))
|
||||
for i, resp := range combined {
|
||||
compareSummary(tc.expectedPolicyReports[i].Summary, resp.Summary, desc)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
package apply
|
||||
|
||||
import (
|
||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
reportutils "github.com/kyverno/kyverno/pkg/utils/report"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func mergeClusterReport(
|
||||
clustered []policyreportv1alpha2.ClusterPolicyReport,
|
||||
namespaced []policyreportv1alpha2.PolicyReport,
|
||||
) policyreportv1alpha2.ClusterPolicyReport {
|
||||
var results []policyreportv1alpha2.PolicyReportResult
|
||||
for _, report := range clustered {
|
||||
results = append(results, report.Results...)
|
||||
}
|
||||
for _, report := range namespaced {
|
||||
if report.GetNamespace() != "" {
|
||||
continue
|
||||
}
|
||||
results = append(results, report.Results...)
|
||||
}
|
||||
return policyreportv1alpha2.ClusterPolicyReport{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ClusterPolicyReport",
|
||||
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterpolicyreport,
|
||||
},
|
||||
Results: results,
|
||||
Summary: reportutils.CalculateSummary(results),
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
package apply
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
report "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
"gotest.tools/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func Test_mergeClusterReport(t *testing.T) {
|
||||
clustered := []policyreportv1alpha2.ClusterPolicyReport{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ClusterPolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cpolr-4",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "cpolr-4",
|
||||
Result: report.StatusFail,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ClusterPolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cpolr-5",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "cpolr-5",
|
||||
Result: report.StatusFail,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
namespaced := []policyreportv1alpha2.PolicyReport{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "PolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ns-polr-1",
|
||||
Namespace: "ns-polr",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "ns-polr-1",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 10),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "PolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ns-polr-2",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "ns-polr-2",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 5),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "PolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "polr-3",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "polr-3",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 1),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectedResults := []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "cpolr-4",
|
||||
Result: report.StatusFail,
|
||||
},
|
||||
{
|
||||
Policy: "cpolr-5",
|
||||
Result: report.StatusFail,
|
||||
},
|
||||
{
|
||||
Policy: "ns-polr-2",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 5),
|
||||
},
|
||||
{
|
||||
Policy: "polr-3",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 1),
|
||||
},
|
||||
}
|
||||
|
||||
cpolr := mergeClusterReport(clustered, namespaced)
|
||||
|
||||
assert.Assert(t, cpolr.APIVersion == report.SchemeGroupVersion.String(), cpolr.Kind)
|
||||
assert.Assert(t, cpolr.Kind == "ClusterPolicyReport", cpolr.Kind)
|
||||
|
||||
assert.Assert(t, reflect.DeepEqual(cpolr.Results, expectedResults), cpolr.Results)
|
||||
|
||||
assert.Assert(t, cpolr.Summary.Pass == 2, cpolr.Summary.Pass)
|
||||
assert.Assert(t, cpolr.Summary.Fail == 2, cpolr.Summary.Fail)
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
package apply
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
preport "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
var rawPolicy = []byte(`
|
||||
{
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "pod-requirements",
|
||||
"annotations": {
|
||||
"pod-policies.kyverno.io/autogen-controllers": "none",
|
||||
"policies.kyverno.io/severity": "medium",
|
||||
"policies.kyverno.io/category": "Pod Security Standards (Restricted)"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"background": false,
|
||||
"validationFailureAction": "audit",
|
||||
"rules": [
|
||||
{
|
||||
"name": "pods-require-account",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "User pods must include an account for charging",
|
||||
"pattern": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"account": "*?"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pods-require-limits",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "CPU and memory resource requests and limits are required for user pods",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"resources": {
|
||||
"requests": {
|
||||
"memory": "?*",
|
||||
"cpu": "?*"
|
||||
},
|
||||
"limits": {
|
||||
"memory": "?*",
|
||||
"cpu": "?*"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
func Test_buildPolicyReports(t *testing.T) {
|
||||
var policy kyverno.ClusterPolicy
|
||||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
er := engineapi.EngineResponse{}
|
||||
er = er.WithPolicy(engineapi.NewKyvernoPolicy(&policy))
|
||||
er.PolicyResponse.Add(
|
||||
engineapi.ExecutionStats{},
|
||||
*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/",
|
||||
),
|
||||
*engineapi.RulePass(
|
||||
"pods-require-limits",
|
||||
engineapi.Validation,
|
||||
"validation rule 'pods-require-limits' passed.",
|
||||
),
|
||||
)
|
||||
|
||||
clustered, namespaced := buildPolicyReports(false, er)
|
||||
assert.Assert(t, len(clustered) == 1, len(clustered))
|
||||
assert.Assert(t, len(namespaced) == 0, len(namespaced))
|
||||
{
|
||||
report := clustered[0]
|
||||
assert.Assert(t, report.GetName() == clusterpolicyreport)
|
||||
assert.Assert(t, report.Kind == "ClusterPolicyReport")
|
||||
assert.Assert(t, len(report.Results) == 2)
|
||||
assert.Equal(t, string(report.Results[0].Severity), "medium")
|
||||
assert.Equal(t, report.Results[0].Category, "Pod Security Standards (Restricted)")
|
||||
assert.Assert(t, report.Summary.Pass == 1, report.Summary.Pass)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildPolicyResults(t *testing.T) {
|
||||
var policy kyverno.ClusterPolicy
|
||||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
|
||||
er := engineapi.EngineResponse{}
|
||||
er = er.WithPolicy(engineapi.NewKyvernoPolicy(&policy))
|
||||
er.PolicyResponse.Add(
|
||||
engineapi.ExecutionStats{}, *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/",
|
||||
),
|
||||
*engineapi.RulePass(
|
||||
"pods-require-limits",
|
||||
engineapi.Validation,
|
||||
"validation rule 'pods-require-limits' passed.",
|
||||
),
|
||||
)
|
||||
|
||||
results := buildPolicyResults(false, er)
|
||||
|
||||
for _, result := range results {
|
||||
assert.Assert(t, len(result) == 2, len(result))
|
||||
for _, r := range result {
|
||||
switch r.Rule {
|
||||
case "pods-require-limits":
|
||||
assert.Assert(t, r.Result == preport.StatusPass)
|
||||
case "pods-require-account":
|
||||
assert.Assert(t, r.Result == preport.StatusFail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -77,31 +77,31 @@ func TestSeverity(t *testing.T) {
|
|||
}, {
|
||||
name: "critical",
|
||||
annotations: map[string]string{
|
||||
kyverno.AnnotationPolicySeverity: policyreportv1alpha2.SeverityCritical,
|
||||
kyverno.AnnotationPolicySeverity: "critical",
|
||||
},
|
||||
want: policyreportv1alpha2.SeverityCritical,
|
||||
}, {
|
||||
name: "high",
|
||||
annotations: map[string]string{
|
||||
kyverno.AnnotationPolicySeverity: policyreportv1alpha2.SeverityHigh,
|
||||
kyverno.AnnotationPolicySeverity: "high",
|
||||
},
|
||||
want: policyreportv1alpha2.SeverityHigh,
|
||||
}, {
|
||||
name: "medium",
|
||||
annotations: map[string]string{
|
||||
kyverno.AnnotationPolicySeverity: policyreportv1alpha2.SeverityMedium,
|
||||
kyverno.AnnotationPolicySeverity: "medium",
|
||||
},
|
||||
want: policyreportv1alpha2.SeverityMedium,
|
||||
}, {
|
||||
name: "low",
|
||||
annotations: map[string]string{
|
||||
kyverno.AnnotationPolicySeverity: policyreportv1alpha2.SeverityLow,
|
||||
kyverno.AnnotationPolicySeverity: "low",
|
||||
},
|
||||
want: policyreportv1alpha2.SeverityLow,
|
||||
}, {
|
||||
name: "info",
|
||||
annotations: map[string]string{
|
||||
kyverno.AnnotationPolicySeverity: policyreportv1alpha2.SeverityInfo,
|
||||
kyverno.AnnotationPolicySeverity: "info",
|
||||
},
|
||||
want: policyreportv1alpha2.SeverityInfo,
|
||||
}, {
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
package apply
|
||||
package report
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kyverno/kyverno/api/kyverno"
|
||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
annotationsutils "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/annotations"
|
||||
|
@ -14,71 +10,25 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const clusterpolicyreport = "clusterpolicyreport"
|
||||
|
||||
// resps is the engine responses generated for a single policy
|
||||
func buildPolicyReports(auditWarn bool, engineResponses ...engineapi.EngineResponse) ([]policyreportv1alpha2.ClusterPolicyReport, []policyreportv1alpha2.PolicyReport) {
|
||||
var clustered []policyreportv1alpha2.ClusterPolicyReport
|
||||
var namespaced []policyreportv1alpha2.PolicyReport
|
||||
resultsMap := buildPolicyResults(auditWarn, engineResponses...)
|
||||
for scope, result := range resultsMap {
|
||||
if scope == clusterpolicyreport {
|
||||
report := policyreportv1alpha2.ClusterPolicyReport{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
||||
Kind: "ClusterPolicyReport",
|
||||
},
|
||||
Results: result,
|
||||
Summary: reportutils.CalculateSummary(result),
|
||||
}
|
||||
report.SetName(scope)
|
||||
clustered = append(clustered, report)
|
||||
} else {
|
||||
report := policyreportv1alpha2.PolicyReport{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
||||
Kind: "PolicyReport",
|
||||
},
|
||||
Results: result,
|
||||
Summary: reportutils.CalculateSummary(result),
|
||||
}
|
||||
policyNamespace := strings.ReplaceAll(scope, "policyreport-ns-", "")
|
||||
report.SetName(scope)
|
||||
report.SetNamespace(policyNamespace)
|
||||
namespaced = append(namespaced, report)
|
||||
}
|
||||
}
|
||||
|
||||
return clustered, namespaced
|
||||
}
|
||||
|
||||
// buildPolicyResults returns a string-PolicyReportResult map
|
||||
// the key of the map is one of "clusterpolicyreport", "policyreport-ns-<namespace>"
|
||||
func buildPolicyResults(auditWarn bool, engineResponses ...engineapi.EngineResponse) map[string][]policyreportv1alpha2.PolicyReportResult {
|
||||
results := make(map[string][]policyreportv1alpha2.PolicyReportResult)
|
||||
now := metav1.Timestamp{Seconds: time.Now().Unix()}
|
||||
|
||||
func ComputePolicyReportResultsPerPolicy(auditWarn bool, engineResponses ...engineapi.EngineResponse) map[engineapi.GenericPolicy][]policyreportv1alpha2.PolicyReportResult {
|
||||
results := make(map[engineapi.GenericPolicy][]policyreportv1alpha2.PolicyReportResult)
|
||||
for _, engineResponse := range engineResponses {
|
||||
if len(engineResponse.PolicyResponse.Rules) == 0 {
|
||||
continue
|
||||
}
|
||||
policy := engineResponse.Policy()
|
||||
policyName := policy.GetName()
|
||||
policyNamespace := policy.GetNamespace()
|
||||
audit := engineResponse.GetValidationFailureAction().Audit()
|
||||
scored := annotationsutils.Scored(policy.GetAnnotations())
|
||||
category := annotationsutils.Category(policy.GetAnnotations())
|
||||
severity := annotationsutils.Severity(policy.GetAnnotations())
|
||||
|
||||
var appname string
|
||||
if policyNamespace != "" {
|
||||
appname = fmt.Sprintf("policyreport-ns-%s", policyNamespace)
|
||||
} else {
|
||||
appname = clusterpolicyreport
|
||||
}
|
||||
|
||||
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{
|
||||
{
|
||||
|
@ -89,11 +39,10 @@ func buildPolicyResults(auditWarn bool, engineResponses ...engineapi.EngineRespo
|
|||
UID: engineResponse.Resource.GetUID(),
|
||||
},
|
||||
},
|
||||
Scored: true,
|
||||
Scored: scored,
|
||||
Category: category,
|
||||
Severity: severity,
|
||||
}
|
||||
|
||||
if ruleResponse.Status() == engineapi.RuleStatusSkip {
|
||||
result.Result = policyreportv1alpha2.StatusSkip
|
||||
} else if ruleResponse.Status() == engineapi.RuleStatusError {
|
||||
|
@ -101,25 +50,80 @@ func buildPolicyResults(auditWarn bool, engineResponses ...engineapi.EngineRespo
|
|||
} else if ruleResponse.Status() == engineapi.RuleStatusPass {
|
||||
result.Result = policyreportv1alpha2.StatusPass
|
||||
} else if ruleResponse.Status() == engineapi.RuleStatusFail {
|
||||
if !scored {
|
||||
result.Result = policyreportv1alpha2.StatusWarn
|
||||
} else if auditWarn && engineResponse.GetValidationFailureAction().Audit() {
|
||||
if !scored || (audit && auditWarn) {
|
||||
result.Result = policyreportv1alpha2.StatusWarn
|
||||
} else {
|
||||
result.Result = policyreportv1alpha2.StatusFail
|
||||
}
|
||||
} else {
|
||||
fmt.Println(ruleResponse)
|
||||
result.Result = policyreportv1alpha2.StatusError
|
||||
}
|
||||
if policy.GetType() == engineapi.KyvernoPolicyType {
|
||||
result.Rule = ruleResponse.Name()
|
||||
}
|
||||
result.Message = ruleResponse.Message()
|
||||
result.Source = kyverno.ValueKyvernoApp
|
||||
result.Timestamp = now
|
||||
results[appname] = append(results[appname], result)
|
||||
result.Timestamp = metav1.Timestamp{Seconds: ruleResponse.Stats().Timestamp()}
|
||||
results[policy] = append(results[policy], result)
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
func ComputePolicyReports(auditWarn bool, engineResponses ...engineapi.EngineResponse) ([]policyreportv1alpha2.ClusterPolicyReport, []policyreportv1alpha2.PolicyReport) {
|
||||
var clustered []policyreportv1alpha2.ClusterPolicyReport
|
||||
var namespaced []policyreportv1alpha2.PolicyReport
|
||||
perPolicyResults := ComputePolicyReportResultsPerPolicy(auditWarn, engineResponses...)
|
||||
for policy, results := range perPolicyResults {
|
||||
if policy.GetNamespace() == "" {
|
||||
report := policyreportv1alpha2.ClusterPolicyReport{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
||||
Kind: "ClusterPolicyReport",
|
||||
},
|
||||
Results: results,
|
||||
Summary: reportutils.CalculateSummary(results),
|
||||
}
|
||||
report.SetName(policy.GetName())
|
||||
clustered = append(clustered, report)
|
||||
} else {
|
||||
report := policyreportv1alpha2.PolicyReport{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
||||
Kind: "PolicyReport",
|
||||
},
|
||||
Results: results,
|
||||
Summary: reportutils.CalculateSummary(results),
|
||||
}
|
||||
report.SetName(policy.GetName())
|
||||
report.SetNamespace(policy.GetNamespace())
|
||||
namespaced = append(namespaced, report)
|
||||
}
|
||||
}
|
||||
return clustered, namespaced
|
||||
}
|
||||
|
||||
func MergeClusterReports(clustered []policyreportv1alpha2.ClusterPolicyReport, namespaced []policyreportv1alpha2.PolicyReport) policyreportv1alpha2.ClusterPolicyReport {
|
||||
var results []policyreportv1alpha2.PolicyReportResult
|
||||
for _, report := range clustered {
|
||||
results = append(results, report.Results...)
|
||||
}
|
||||
// TODO why this ?
|
||||
for _, report := range namespaced {
|
||||
if report.GetNamespace() != "" {
|
||||
continue
|
||||
}
|
||||
results = append(results, report.Results...)
|
||||
}
|
||||
return policyreportv1alpha2.ClusterPolicyReport{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ClusterPolicyReport",
|
||||
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "merged",
|
||||
},
|
||||
Results: results,
|
||||
Summary: reportutils.CalculateSummary(results),
|
||||
}
|
||||
}
|
251
cmd/cli/kubectl-kyverno/utils/report/report_test.go
Normal file
251
cmd/cli/kubectl-kyverno/utils/report/report_test.go
Normal file
|
@ -0,0 +1,251 @@
|
|||
package report
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
kyverno "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
report "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||
"gotest.tools/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var rawPolicy = []byte(`
|
||||
{
|
||||
"apiVersion": "kyverno.io/v1",
|
||||
"kind": "ClusterPolicy",
|
||||
"metadata": {
|
||||
"name": "pod-requirements",
|
||||
"annotations": {
|
||||
"pod-policies.kyverno.io/autogen-controllers": "none",
|
||||
"policies.kyverno.io/severity": "medium",
|
||||
"policies.kyverno.io/category": "Pod Security Standards (Restricted)"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"background": false,
|
||||
"validationFailureAction": "audit",
|
||||
"rules": [
|
||||
{
|
||||
"name": "pods-require-account",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "User pods must include an account for charging",
|
||||
"pattern": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"account": "*?"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "pods-require-limits",
|
||||
"match": {
|
||||
"resources": {
|
||||
"kinds": [
|
||||
"Pod"
|
||||
]
|
||||
}
|
||||
},
|
||||
"validate": {
|
||||
"message": "CPU and memory resource requests and limits are required for user pods",
|
||||
"pattern": {
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"resources": {
|
||||
"requests": {
|
||||
"memory": "?*",
|
||||
"cpu": "?*"
|
||||
},
|
||||
"limits": {
|
||||
"memory": "?*",
|
||||
"cpu": "?*"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
func TestComputePolicyReports(t *testing.T) {
|
||||
var policy kyverno.ClusterPolicy
|
||||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
er := engineapi.EngineResponse{}
|
||||
er = er.WithPolicy(engineapi.NewKyvernoPolicy(&policy))
|
||||
er.PolicyResponse.Add(
|
||||
engineapi.ExecutionStats{},
|
||||
*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/",
|
||||
),
|
||||
*engineapi.RulePass(
|
||||
"pods-require-limits",
|
||||
engineapi.Validation,
|
||||
"validation rule 'pods-require-limits' passed.",
|
||||
),
|
||||
)
|
||||
clustered, namespaced := ComputePolicyReports(false, er)
|
||||
assert.Equal(t, len(clustered), 1)
|
||||
assert.Equal(t, len(namespaced), 0)
|
||||
{
|
||||
report := clustered[0]
|
||||
assert.Equal(t, report.GetName(), policy.GetName())
|
||||
assert.Equal(t, report.Kind, "ClusterPolicyReport")
|
||||
assert.Equal(t, len(report.Results), 2)
|
||||
assert.Equal(t, report.Results[0].Severity, policyreportv1alpha2.SeverityMedium)
|
||||
assert.Equal(t, report.Results[0].Category, "Pod Security Standards (Restricted)")
|
||||
assert.Equal(t, report.Summary.Pass, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputePolicyReportResultsPerPolicy(t *testing.T) {
|
||||
var policy kyverno.ClusterPolicy
|
||||
err := json.Unmarshal(rawPolicy, &policy)
|
||||
assert.NilError(t, err)
|
||||
er := engineapi.EngineResponse{}
|
||||
er = er.WithPolicy(engineapi.NewKyvernoPolicy(&policy))
|
||||
er.PolicyResponse.Add(
|
||||
engineapi.ExecutionStats{}, *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/",
|
||||
),
|
||||
*engineapi.RulePass(
|
||||
"pods-require-limits",
|
||||
engineapi.Validation,
|
||||
"validation rule 'pods-require-limits' passed.",
|
||||
),
|
||||
)
|
||||
results := ComputePolicyReportResultsPerPolicy(false, er)
|
||||
for _, result := range results {
|
||||
assert.Equal(t, len(result), 2)
|
||||
for _, r := range result {
|
||||
switch r.Rule {
|
||||
case "pods-require-limits":
|
||||
assert.Equal(t, r.Result, policyreportv1alpha2.StatusPass)
|
||||
case "pods-require-account":
|
||||
assert.Equal(t, r.Result, policyreportv1alpha2.StatusFail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeClusterReport(t *testing.T) {
|
||||
clustered := []policyreportv1alpha2.ClusterPolicyReport{{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ClusterPolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cpolr-4",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "cpolr-4",
|
||||
Result: report.StatusFail,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ClusterPolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "cpolr-5",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "cpolr-5",
|
||||
Result: report.StatusFail,
|
||||
},
|
||||
},
|
||||
}}
|
||||
namespaced := []policyreportv1alpha2.PolicyReport{{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "PolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ns-polr-1",
|
||||
Namespace: "ns-polr",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "ns-polr-1",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 10),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "PolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ns-polr-2",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "ns-polr-2",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 5),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "PolicyReport",
|
||||
APIVersion: report.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "polr-3",
|
||||
},
|
||||
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||
{
|
||||
Policy: "polr-3",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 1),
|
||||
},
|
||||
},
|
||||
}}
|
||||
expectedResults := []policyreportv1alpha2.PolicyReportResult{{
|
||||
Policy: "cpolr-4",
|
||||
Result: report.StatusFail,
|
||||
}, {
|
||||
Policy: "cpolr-5",
|
||||
Result: report.StatusFail,
|
||||
}, {
|
||||
Policy: "ns-polr-2",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 5),
|
||||
}, {
|
||||
Policy: "polr-3",
|
||||
Result: report.StatusPass,
|
||||
Resources: make([]corev1.ObjectReference, 1),
|
||||
}}
|
||||
cpolr := MergeClusterReports(clustered, namespaced)
|
||||
assert.Equal(t, cpolr.APIVersion, report.SchemeGroupVersion.String())
|
||||
assert.Equal(t, cpolr.Kind, "ClusterPolicyReport")
|
||||
assert.DeepEqual(t, cpolr.Results, expectedResults)
|
||||
assert.Equal(t, cpolr.Summary.Pass, 2)
|
||||
assert.Equal(t, cpolr.Summary.Fail, 2)
|
||||
}
|
|
@ -34,28 +34,28 @@ type KyvernoPolicy struct {
|
|||
policy kyvernov1.PolicyInterface
|
||||
}
|
||||
|
||||
func (p KyvernoPolicy) GetPolicy() interface{} {
|
||||
func (p *KyvernoPolicy) GetPolicy() interface{} {
|
||||
return p.policy
|
||||
}
|
||||
|
||||
func (p KyvernoPolicy) GetType() PolicyType {
|
||||
func (p *KyvernoPolicy) GetType() PolicyType {
|
||||
return KyvernoPolicyType
|
||||
}
|
||||
|
||||
func (p KyvernoPolicy) GetName() string {
|
||||
func (p *KyvernoPolicy) GetName() string {
|
||||
return p.policy.GetName()
|
||||
}
|
||||
|
||||
func (p KyvernoPolicy) GetNamespace() string {
|
||||
func (p *KyvernoPolicy) GetNamespace() string {
|
||||
return p.policy.GetNamespace()
|
||||
}
|
||||
|
||||
func (p KyvernoPolicy) GetAnnotations() map[string]string {
|
||||
func (p *KyvernoPolicy) GetAnnotations() map[string]string {
|
||||
return p.policy.GetAnnotations()
|
||||
}
|
||||
|
||||
func NewKyvernoPolicy(pol kyvernov1.PolicyInterface) KyvernoPolicy {
|
||||
return KyvernoPolicy{
|
||||
func NewKyvernoPolicy(pol kyvernov1.PolicyInterface) GenericPolicy {
|
||||
return &KyvernoPolicy{
|
||||
policy: pol,
|
||||
}
|
||||
}
|
||||
|
@ -64,28 +64,28 @@ type ValidatingAdmissionPolicy struct {
|
|||
policy v1alpha1.ValidatingAdmissionPolicy
|
||||
}
|
||||
|
||||
func (p ValidatingAdmissionPolicy) GetPolicy() interface{} {
|
||||
func (p *ValidatingAdmissionPolicy) GetPolicy() interface{} {
|
||||
return p.policy
|
||||
}
|
||||
|
||||
func (p ValidatingAdmissionPolicy) GetType() PolicyType {
|
||||
func (p *ValidatingAdmissionPolicy) GetType() PolicyType {
|
||||
return ValidatingAdmissionPolicyType
|
||||
}
|
||||
|
||||
func (p ValidatingAdmissionPolicy) GetName() string {
|
||||
func (p *ValidatingAdmissionPolicy) GetName() string {
|
||||
return p.policy.GetName()
|
||||
}
|
||||
|
||||
func (p ValidatingAdmissionPolicy) GetNamespace() string {
|
||||
func (p *ValidatingAdmissionPolicy) GetNamespace() string {
|
||||
return p.policy.GetNamespace()
|
||||
}
|
||||
|
||||
func (p ValidatingAdmissionPolicy) GetAnnotations() map[string]string {
|
||||
func (p *ValidatingAdmissionPolicy) GetAnnotations() map[string]string {
|
||||
return p.policy.GetAnnotations()
|
||||
}
|
||||
|
||||
func NewValidatingAdmissionPolicy(pol v1alpha1.ValidatingAdmissionPolicy) ValidatingAdmissionPolicy {
|
||||
return ValidatingAdmissionPolicy{
|
||||
func NewValidatingAdmissionPolicy(pol v1alpha1.ValidatingAdmissionPolicy) GenericPolicy {
|
||||
return &ValidatingAdmissionPolicy{
|
||||
policy: pol,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func SortReportResults(results []policyreportv1alpha2.PolicyReportResult) {
|
|||
|
||||
func CalculateSummary(results []policyreportv1alpha2.PolicyReportResult) (summary policyreportv1alpha2.PolicyReportSummary) {
|
||||
for _, res := range results {
|
||||
switch string(res.Result) {
|
||||
switch res.Result {
|
||||
case policyreportv1alpha2.StatusPass:
|
||||
summary.Pass++
|
||||
case policyreportv1alpha2.StatusFail:
|
||||
|
@ -72,15 +72,15 @@ func toPolicyResult(status engineapi.RuleStatus) policyreportv1alpha2.PolicyResu
|
|||
|
||||
func SeverityFromString(severity string) policyreportv1alpha2.PolicySeverity {
|
||||
switch severity {
|
||||
case policyreportv1alpha2.SeverityCritical:
|
||||
case "critical":
|
||||
return policyreportv1alpha2.SeverityCritical
|
||||
case policyreportv1alpha2.SeverityHigh:
|
||||
case "high":
|
||||
return policyreportv1alpha2.SeverityHigh
|
||||
case policyreportv1alpha2.SeverityMedium:
|
||||
case "medium":
|
||||
return policyreportv1alpha2.SeverityMedium
|
||||
case policyreportv1alpha2.SeverityLow:
|
||||
case "low":
|
||||
return policyreportv1alpha2.SeverityLow
|
||||
case policyreportv1alpha2.SeverityInfo:
|
||||
case "info":
|
||||
return policyreportv1alpha2.SeverityInfo
|
||||
}
|
||||
return ""
|
||||
|
|
Loading…
Add table
Reference in a new issue