mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-15 20:20:22 +00:00
fix: remove cli dead code (#7748)
Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
d185e6f6a4
commit
7c553c4bd2
9 changed files with 329 additions and 523 deletions
|
@ -29,9 +29,11 @@ import (
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||||
yaml1 "sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const divider = "----------------------------------------------------------------------"
|
||||||
|
|
||||||
type SkippedInvalidPolicies struct {
|
type SkippedInvalidPolicies struct {
|
||||||
skipped []string
|
skipped []string
|
||||||
invalid []string
|
invalid []string
|
||||||
|
@ -85,42 +87,42 @@ To apply policy with variables:
|
||||||
Format of value.yaml:
|
Format of value.yaml:
|
||||||
|
|
||||||
policies:
|
policies:
|
||||||
- name: <policy1 name>
|
- name: <policy1 name>
|
||||||
rules:
|
rules:
|
||||||
- name: <rule1 name>
|
- name: <rule1 name>
|
||||||
values:
|
values:
|
||||||
<context variable1 in policy1 rule1>: <value>
|
<context variable1 in policy1 rule1>: <value>
|
||||||
<context variable2 in policy1 rule1>: <value>
|
<context variable2 in policy1 rule1>: <value>
|
||||||
- name: <rule2 name>
|
- name: <rule2 name>
|
||||||
|
values:
|
||||||
|
<context variable1 in policy1 rule2>: <value>
|
||||||
|
<context variable2 in policy1 rule2>: <value>
|
||||||
|
resources:
|
||||||
|
- name: <resource1 name>
|
||||||
values:
|
values:
|
||||||
<context variable1 in policy1 rule2>: <value>
|
<variable1 in policy1>: <value>
|
||||||
<context variable2 in policy1 rule2>: <value>
|
<variable2 in policy1>: <value>
|
||||||
resources:
|
- name: <resource2 name>
|
||||||
- name: <resource1 name>
|
values:
|
||||||
values:
|
<variable1 in policy1>: <value>
|
||||||
<variable1 in policy1>: <value>
|
<variable2 in policy1>: <value>
|
||||||
<variable2 in policy1>: <value>
|
- name: <policy2 name>
|
||||||
- name: <resource2 name>
|
resources:
|
||||||
values:
|
- name: <resource1 name>
|
||||||
<variable1 in policy1>: <value>
|
values:
|
||||||
<variable2 in policy1>: <value>
|
<variable1 in policy2>: <value>
|
||||||
- name: <policy2 name>
|
<variable2 in policy2>: <value>
|
||||||
resources:
|
- name: <resource2 name>
|
||||||
- name: <resource1 name>
|
values:
|
||||||
values:
|
<variable1 in policy2>: <value>
|
||||||
<variable1 in policy2>: <value>
|
<variable2 in policy2>: <value>
|
||||||
<variable2 in policy2>: <value>
|
|
||||||
- name: <resource2 name>
|
|
||||||
values:
|
|
||||||
<variable1 in policy2>: <value>
|
|
||||||
<variable2 in policy2>: <value>
|
|
||||||
namespaceSelector:
|
namespaceSelector:
|
||||||
- name: <namespace1 name>
|
- name: <namespace1 name>
|
||||||
labels:
|
labels:
|
||||||
<label key>: <label value>
|
<label key>: <label value>
|
||||||
- name: <namespace2 name>
|
- name: <namespace2 name>
|
||||||
labels:
|
labels:
|
||||||
<label key>: <label value>
|
<label key>: <label value>
|
||||||
# If policy is matching on Kind/Subresource, then this is required
|
# If policy is matching on Kind/Subresource, then this is required
|
||||||
subresources:
|
subresources:
|
||||||
- subresource:
|
- subresource:
|
||||||
|
@ -158,11 +160,17 @@ func Command() *cobra.Command {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
applyCommandConfig.PolicyPaths = policyPaths
|
applyCommandConfig.PolicyPaths = policyPaths
|
||||||
rc, resources, skipInvalidPolicies, pvInfos, err := applyCommandConfig.applyCommandHelper()
|
rc, _, skipInvalidPolicies, responses, err := applyCommandConfig.applyCommandHelper()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
PrintReportOrViolation(applyCommandConfig.PolicyReport, rc, applyCommandConfig.ResourcePaths, len(resources), skipInvalidPolicies, applyCommandConfig.Stdin, pvInfos, applyCommandConfig.warnExitCode, applyCommandConfig.warnNoPassed, applyCommandConfig.AuditWarn)
|
printSkippedAndInvalidPolicies(skipInvalidPolicies)
|
||||||
|
if applyCommandConfig.PolicyReport {
|
||||||
|
printReport(responses, applyCommandConfig.AuditWarn)
|
||||||
|
} else {
|
||||||
|
printViolations(rc)
|
||||||
|
}
|
||||||
|
exit(rc, applyCommandConfig.warnExitCode, applyCommandConfig.warnNoPassed)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -201,14 +209,34 @@ func (c *ApplyCommandConfig) applyCommandHelper() (*common.ResultCounts, []*unst
|
||||||
if len(c.ResourcePaths) == 0 && !c.Cluster {
|
if len(c.ResourcePaths) == 0 && !c.Cluster {
|
||||||
return nil, nil, skipInvalidPolicies, nil, sanitizederror.New("resource file(s) or cluster required")
|
return nil, nil, skipInvalidPolicies, nil, sanitizederror.New("resource file(s) or cluster required")
|
||||||
}
|
}
|
||||||
|
mutateLogPathIsDir, err := checkMutateLogPath(c.MutateLogPath)
|
||||||
store.SetLocal(true)
|
if err != nil {
|
||||||
store.SetRegistryAccess(c.RegistryAccess)
|
if !sanitizederror.IsErrorSanitized(err) {
|
||||||
if c.Cluster {
|
return nil, nil, skipInvalidPolicies, nil, sanitizederror.NewWithError("failed to create file/folder", err)
|
||||||
store.AllowApiCall(true)
|
}
|
||||||
|
return nil, nil, skipInvalidPolicies, nil, err
|
||||||
|
}
|
||||||
|
// empty the previous contents of the file just in case if the file already existed before with some content(so as to perform overwrites)
|
||||||
|
// the truncation of files for the case when mutateLogPath is dir, is handled under pkg/kyverno/apply/common.go
|
||||||
|
if !mutateLogPathIsDir && c.MutateLogPath != "" {
|
||||||
|
c.MutateLogPath = filepath.Clean(c.MutateLogPath)
|
||||||
|
// Necessary for us to include the file via variable as it is part of the CLI.
|
||||||
|
_, err := os.OpenFile(c.MutateLogPath, os.O_TRUNC|os.O_WRONLY, 0o600) // #nosec G304
|
||||||
|
if err != nil {
|
||||||
|
if !sanitizederror.IsErrorSanitized(err) {
|
||||||
|
return nil, nil, skipInvalidPolicies, nil, sanitizederror.NewWithError("failed to truncate the existing file at "+c.MutateLogPath, err)
|
||||||
|
}
|
||||||
|
return nil, nil, skipInvalidPolicies, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var userInfo v1beta1.RequestInfo
|
||||||
|
if c.UserInfoPath != "" {
|
||||||
|
userInfo, err = common.GetUserInfoFromPath(nil, c.UserInfoPath, false, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: failed to load request info\nCause: %s\n", err)
|
||||||
|
osExit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fs := memfs.New()
|
|
||||||
|
|
||||||
variables, globalValMap, valuesMap, namespaceSelectorMap, subresources, err := common.GetVariable(c.Variables, c.ValuesFile, nil, false, "")
|
variables, globalValMap, valuesMap, namespaceSelectorMap, subresources, err := common.GetVariable(c.Variables, c.ValuesFile, nil, false, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !sanitizederror.IsErrorSanitized(err) {
|
if !sanitizederror.IsErrorSanitized(err) {
|
||||||
|
@ -216,12 +244,17 @@ func (c *ApplyCommandConfig) applyCommandHelper() (*common.ResultCounts, []*unst
|
||||||
}
|
}
|
||||||
return nil, nil, skipInvalidPolicies, nil, err
|
return nil, nil, skipInvalidPolicies, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
openApiManager, err := openapi.NewManager(log.Log)
|
openApiManager, err := openapi.NewManager(log.Log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, skipInvalidPolicies, nil, sanitizederror.NewWithError("failed to initialize openAPIController", err)
|
return nil, nil, skipInvalidPolicies, nil, sanitizederror.NewWithError("failed to initialize openAPIController", err)
|
||||||
}
|
}
|
||||||
|
// init store
|
||||||
|
store.SetLocal(true)
|
||||||
|
store.SetRegistryAccess(c.RegistryAccess)
|
||||||
|
if c.Cluster {
|
||||||
|
store.AllowApiCall(true)
|
||||||
|
}
|
||||||
|
// init cluster client
|
||||||
var dClient dclient.Interface
|
var dClient dclient.Interface
|
||||||
if c.Cluster {
|
if c.Cluster {
|
||||||
restConfig, err := config.CreateClientConfigWithContext(c.KubeConfig, c.Context)
|
restConfig, err := config.CreateClientConfigWithContext(c.KubeConfig, c.Context)
|
||||||
|
@ -241,7 +274,8 @@ func (c *ApplyCommandConfig) applyCommandHelper() (*common.ResultCounts, []*unst
|
||||||
return nil, nil, skipInvalidPolicies, nil, err
|
return nil, nil, skipInvalidPolicies, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// load policies
|
||||||
|
fs := memfs.New()
|
||||||
var policies []kyvernov1.PolicyInterface
|
var policies []kyvernov1.PolicyInterface
|
||||||
var validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy
|
var validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy
|
||||||
|
|
||||||
|
@ -283,89 +317,26 @@ func (c *ApplyCommandConfig) applyCommandHelper() (*common.ResultCounts, []*unst
|
||||||
fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
|
fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
|
||||||
osExit(1)
|
osExit(1)
|
||||||
}
|
}
|
||||||
|
// load resources
|
||||||
mutateLogPathIsDir, err := checkMutateLogPath(c.MutateLogPath)
|
resources, err := common.GetResourceAccordingToResourcePath(nil, c.ResourcePaths, c.Cluster, policies, validatingAdmissionPolicies, dClient, c.Namespace, c.PolicyReport, false, "")
|
||||||
if err != nil {
|
|
||||||
if !sanitizederror.IsErrorSanitized(err) {
|
|
||||||
return nil, nil, skipInvalidPolicies, nil, sanitizederror.NewWithError("failed to create file/folder", err)
|
|
||||||
}
|
|
||||||
return nil, nil, skipInvalidPolicies, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty the previous contents of the file just in case if the file already existed before with some content(so as to perform overwrites)
|
|
||||||
// the truncation of files for the case when mutateLogPath is dir, is handled under pkg/kyverno/apply/common.go
|
|
||||||
if !mutateLogPathIsDir && c.MutateLogPath != "" {
|
|
||||||
c.MutateLogPath = filepath.Clean(c.MutateLogPath)
|
|
||||||
// Necessary for us to include the file via variable as it is part of the CLI.
|
|
||||||
_, err := os.OpenFile(c.MutateLogPath, os.O_TRUNC|os.O_WRONLY, 0o600) // #nosec G304
|
|
||||||
if err != nil {
|
|
||||||
if !sanitizederror.IsErrorSanitized(err) {
|
|
||||||
return nil, nil, skipInvalidPolicies, nil, sanitizederror.NewWithError("failed to truncate the existing file at "+c.MutateLogPath, err)
|
|
||||||
}
|
|
||||||
return nil, nil, skipInvalidPolicies, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = common.PrintMutatedPolicy(policies)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, skipInvalidPolicies, nil, sanitizederror.NewWithError("failed to marshal mutated policy", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resources, err := common.GetResourceAccordingToResourcePath(fs, c.ResourcePaths, c.Cluster, policies, validatingAdmissionPolicies, dClient, c.Namespace, c.PolicyReport, false, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
|
fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
|
||||||
osExit(1)
|
osExit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len(resources) > 1 || len(policies) > 1) && c.Variables != nil {
|
if (len(resources) > 1 || len(policies) > 1) && c.Variables != nil {
|
||||||
return nil, resources, skipInvalidPolicies, nil, sanitizederror.NewWithError("currently `set` flag supports variable for single policy applied on single resource ", nil)
|
return nil, resources, skipInvalidPolicies, nil, sanitizederror.NewWithError("currently `set` flag supports variable for single policy applied on single resource ", nil)
|
||||||
}
|
}
|
||||||
|
// init variables
|
||||||
// get the user info as request info from a different file
|
|
||||||
var userInfo v1beta1.RequestInfo
|
|
||||||
if c.UserInfoPath != "" {
|
|
||||||
userInfo, err = common.GetUserInfoFromPath(fs, c.UserInfoPath, false, "")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error: failed to load request info\nCause: %s\n", err)
|
|
||||||
osExit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(variables) != 0 {
|
if len(variables) != 0 {
|
||||||
variables = common.SetInStoreContext(policies, variables)
|
variables = common.SetInStoreContext(policies, variables)
|
||||||
}
|
}
|
||||||
|
|
||||||
var policyRulesCount, mutatedPolicyRulesCount int
|
if !c.Stdin {
|
||||||
for _, policy := range policies {
|
var policyRulesCount int
|
||||||
policyRulesCount += len(policy.GetSpec().Rules)
|
for _, policy := range policies {
|
||||||
}
|
policyRulesCount += len(autogen.ComputeRules(policy))
|
||||||
|
|
||||||
for _, policy := range policies {
|
|
||||||
mutatedPolicyRulesCount += len(policy.GetSpec().Rules)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgPolicyRules := "1 policy rule"
|
|
||||||
if policyRulesCount > 1 {
|
|
||||||
msgPolicyRules = fmt.Sprintf("%d policy rules", policyRulesCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
if mutatedPolicyRulesCount > policyRulesCount {
|
|
||||||
msgPolicyRules = fmt.Sprintf("%d policy rules", mutatedPolicyRulesCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgResources := "1 resource"
|
|
||||||
if len(resources) > 1 {
|
|
||||||
msgResources = fmt.Sprintf("%d resources", len(resources))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(policies) > 0 && len(resources) > 0 {
|
|
||||||
if !c.Stdin {
|
|
||||||
if mutatedPolicyRulesCount > policyRulesCount {
|
|
||||||
fmt.Printf("\nauto-generated pod policies\nApplying %s to %s...\n", msgPolicyRules, msgResources)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("\nApplying %s to %s...\n", msgPolicyRules, msgResources)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
fmt.Printf("\nApplying %d policy rule(s) to %d resource(s)...\n", policyRulesCount, len(resources))
|
||||||
}
|
}
|
||||||
|
|
||||||
var rc common.ResultCounts
|
var rc common.ResultCounts
|
||||||
|
@ -494,32 +465,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (*common.ResultCounts, []*unst
|
||||||
return &rc, resources, skipInvalidPolicies, responses, nil
|
return &rc, resources, skipInvalidPolicies, responses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkMutateLogPath - checking path for printing mutated resource (-o flag)
|
func printSkippedAndInvalidPolicies(skipInvalidPolicies SkippedInvalidPolicies) {
|
||||||
func checkMutateLogPath(mutateLogPath string) (mutateLogPathIsDir bool, err error) {
|
|
||||||
if mutateLogPath != "" {
|
|
||||||
spath := strings.Split(mutateLogPath, "/")
|
|
||||||
sfileName := strings.Split(spath[len(spath)-1], ".")
|
|
||||||
if sfileName[len(sfileName)-1] == "yml" || sfileName[len(sfileName)-1] == "yaml" {
|
|
||||||
mutateLogPathIsDir = false
|
|
||||||
} else {
|
|
||||||
mutateLogPathIsDir = true
|
|
||||||
}
|
|
||||||
|
|
||||||
err := createFileOrFolder(mutateLogPath, mutateLogPathIsDir)
|
|
||||||
if err != nil {
|
|
||||||
if !sanitizederror.IsErrorSanitized(err) {
|
|
||||||
return mutateLogPathIsDir, sanitizederror.NewWithError("failed to create file/folder.", err)
|
|
||||||
}
|
|
||||||
return mutateLogPathIsDir, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mutateLogPathIsDir, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintReportOrViolation - printing policy report/violations
|
|
||||||
func PrintReportOrViolation(policyReport bool, rc *common.ResultCounts, resourcePaths []string, resourcesLen int, skipInvalidPolicies SkippedInvalidPolicies, stdin bool, engineResponses []engineapi.EngineResponse, warnExitCode int, warnNoPassed bool, auditWarn bool) {
|
|
||||||
divider := "----------------------------------------------------------------------"
|
|
||||||
|
|
||||||
if len(skipInvalidPolicies.skipped) > 0 {
|
if len(skipInvalidPolicies.skipped) > 0 {
|
||||||
fmt.Println(divider)
|
fmt.Println(divider)
|
||||||
fmt.Println("Policies Skipped (as required variables are not provided by the user):")
|
fmt.Println("Policies Skipped (as required variables are not provided by the user):")
|
||||||
|
@ -536,26 +482,28 @@ func PrintReportOrViolation(policyReport bool, rc *common.ResultCounts, resource
|
||||||
}
|
}
|
||||||
fmt.Println(divider)
|
fmt.Println(divider)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if policyReport {
|
func printReport(engineResponses []engineapi.EngineResponse, auditWarn bool) {
|
||||||
resps := buildPolicyReports(auditWarn, engineResponses...)
|
clustered, namespaced := buildPolicyReports(auditWarn, engineResponses...)
|
||||||
if len(resps) > 0 || resourcesLen == 0 {
|
if len(clustered) > 0 || len(namespaced) > 0 {
|
||||||
fmt.Println(divider)
|
fmt.Println(divider)
|
||||||
fmt.Println("POLICY REPORT:")
|
fmt.Println("POLICY REPORT:")
|
||||||
fmt.Println(divider)
|
fmt.Println(divider)
|
||||||
report, _ := generateCLIRaw(resps)
|
report := mergeClusterReport(clustered, namespaced)
|
||||||
yamlReport, _ := yaml1.Marshal(report)
|
yamlReport, _ := yaml.Marshal(report)
|
||||||
fmt.Println(string(yamlReport))
|
fmt.Println(string(yamlReport))
|
||||||
} else {
|
|
||||||
fmt.Println(divider)
|
|
||||||
fmt.Println("POLICY REPORT: skip generating policy report (no validate policy found/resource skipped)")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if !stdin {
|
fmt.Println(divider)
|
||||||
fmt.Printf("\npass: %d, fail: %d, warn: %d, error: %d, skip: %d \n", rc.Pass, rc.Fail, rc.Warn, rc.Error, rc.Skip)
|
fmt.Println("POLICY REPORT: skip generating policy report (no validate policy found/resource skipped)")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printViolations(rc *common.ResultCounts) {
|
||||||
|
fmt.Printf("\npass: %d, fail: %d, warn: %d, error: %d, skip: %d \n", rc.Pass, rc.Fail, rc.Warn, rc.Error, rc.Skip)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exit(rc *common.ResultCounts, warnExitCode int, warnNoPassed bool) {
|
||||||
if rc.Fail > 0 || rc.Error > 0 {
|
if rc.Fail > 0 || rc.Error > 0 {
|
||||||
osExit(1)
|
osExit(1)
|
||||||
} else if rc.Warn > 0 && warnExitCode != 0 {
|
} else if rc.Warn > 0 && warnExitCode != 0 {
|
||||||
|
@ -564,50 +512,3 @@ func PrintReportOrViolation(policyReport bool, rc *common.ResultCounts, resource
|
||||||
osExit(warnExitCode)
|
osExit(warnExitCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// createFileOrFolder - creating file or folder according to path provided
|
|
||||||
func createFileOrFolder(mutateLogPath string, mutateLogPathIsDir bool) error {
|
|
||||||
mutateLogPath = filepath.Clean(mutateLogPath)
|
|
||||||
_, err := os.Stat(mutateLogPath)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
if !mutateLogPathIsDir {
|
|
||||||
// check the folder existence, then create the file
|
|
||||||
var folderPath string
|
|
||||||
s := strings.Split(mutateLogPath, "/")
|
|
||||||
|
|
||||||
if len(s) > 1 {
|
|
||||||
folderPath = mutateLogPath[:len(mutateLogPath)-len(s[len(s)-1])-1]
|
|
||||||
_, err := os.Stat(folderPath)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
errDir := os.MkdirAll(folderPath, 0o750)
|
|
||||||
if errDir != nil {
|
|
||||||
return sanitizederror.NewWithError("failed to create directory", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mutateLogPath = filepath.Clean(mutateLogPath)
|
|
||||||
// Necessary for us to create the file via variable as it is part of the CLI.
|
|
||||||
file, err := os.OpenFile(mutateLogPath, os.O_RDONLY|os.O_CREATE, 0o600) // #nosec G304
|
|
||||||
if err != nil {
|
|
||||||
return sanitizederror.NewWithError("failed to create file", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = file.Close()
|
|
||||||
if err != nil {
|
|
||||||
return sanitizederror.NewWithError("failed to close file", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
errDir := os.MkdirAll(mutateLogPath, 0o750)
|
|
||||||
if errDir != nil {
|
|
||||||
return sanitizederror.NewWithError("failed to create directory", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return sanitizederror.NewWithError("failed to describe file", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -212,15 +212,15 @@ func Test_Apply(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
compareSummary := func(expected preport.PolicyReportSummary, actual map[string]interface{}, desc string) {
|
compareSummary := func(expected preport.PolicyReportSummary, actual preport.PolicyReportSummary, desc string) {
|
||||||
assert.Equal(t, actual[preport.StatusPass].(int64), int64(expected.Pass), desc)
|
assert.Equal(t, int64(actual.Pass), int64(expected.Pass), desc)
|
||||||
assert.Equal(t, actual[preport.StatusFail].(int64), int64(expected.Fail), desc)
|
assert.Equal(t, int64(actual.Fail), int64(expected.Fail), desc)
|
||||||
assert.Equal(t, actual[preport.StatusSkip].(int64), int64(expected.Skip), desc)
|
assert.Equal(t, int64(actual.Skip), int64(expected.Skip), desc)
|
||||||
assert.Equal(t, actual[preport.StatusWarn].(int64), int64(expected.Warn), desc)
|
assert.Equal(t, int64(actual.Warn), int64(expected.Warn), desc)
|
||||||
assert.Equal(t, actual[preport.StatusError].(int64), int64(expected.Error), desc)
|
assert.Equal(t, int64(actual.Error), int64(expected.Error), desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyTestcase := func(t *testing.T, tc *TestCase, compareSummary func(preport.PolicyReportSummary, map[string]interface{}, string)) {
|
verifyTestcase := func(t *testing.T, tc *TestCase, compareSummary func(preport.PolicyReportSummary, preport.PolicyReportSummary, string)) {
|
||||||
if tc.stdinFile != "" {
|
if tc.stdinFile != "" {
|
||||||
oldStdin := os.Stdin
|
oldStdin := os.Stdin
|
||||||
input, err := os.OpenFile(tc.stdinFile, os.O_RDONLY, 0)
|
input, err := os.OpenFile(tc.stdinFile, os.O_RDONLY, 0)
|
||||||
|
@ -242,10 +242,10 @@ func Test_Apply(t *testing.T) {
|
||||||
_, _, _, info, err := tc.config.applyCommandHelper()
|
_, _, _, info, err := tc.config.applyCommandHelper()
|
||||||
assert.NilError(t, err, desc)
|
assert.NilError(t, err, desc)
|
||||||
|
|
||||||
resps := buildPolicyReports(tc.config.AuditWarn, info...)
|
clustered, _ := buildPolicyReports(tc.config.AuditWarn, info...)
|
||||||
assert.Assert(t, len(resps) > 0, "policy reports should not be empty: %s", desc)
|
assert.Assert(t, len(clustered) > 0, "policy reports should not be empty: %s", desc)
|
||||||
for i, resp := range resps {
|
for i, resp := range clustered {
|
||||||
compareSummary(tc.expectedPolicyReports[i].Summary, resp.UnstructuredContent()["summary"].(map[string]interface{}), desc)
|
compareSummary(tc.expectedPolicyReports[i].Summary, resp.Summary, desc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,98 +2,33 @@ package apply
|
||||||
|
|
||||||
import (
|
import (
|
||||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||||
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
reportutils "github.com/kyverno/kyverno/pkg/utils/report"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// generateCLIRaw merges all policy reports to a singe cluster policy report
|
func mergeClusterReport(
|
||||||
func generateCLIRaw(reports []*unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
clustered []policyreportv1alpha2.ClusterPolicyReport,
|
||||||
for _, report := range reports {
|
namespaced []policyreportv1alpha2.PolicyReport,
|
||||||
if report.GetNamespace() != "" {
|
) policyreportv1alpha2.ClusterPolicyReport {
|
||||||
report.SetNamespace("")
|
var results []policyreportv1alpha2.PolicyReportResult
|
||||||
}
|
for _, report := range clustered {
|
||||||
|
results = append(results, report.Results...)
|
||||||
}
|
}
|
||||||
|
for _, report := range namespaced {
|
||||||
return mergeClusterReport(reports)
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeClusterReport(reports []*unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
|
||||||
var resultsEntry []interface{}
|
|
||||||
res := &unstructured.Unstructured{}
|
|
||||||
res.SetName(clusterpolicyreport)
|
|
||||||
res.SetKind("ClusterPolicyReport")
|
|
||||||
res.SetAPIVersion(policyreportv1alpha2.SchemeGroupVersion.String())
|
|
||||||
|
|
||||||
for _, report := range reports {
|
|
||||||
if report.GetNamespace() != "" {
|
if report.GetNamespace() != "" {
|
||||||
// skip namespace report
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
results = append(results, report.Results...)
|
||||||
mergeResults(report, &resultsEntry)
|
|
||||||
}
|
}
|
||||||
|
return policyreportv1alpha2.ClusterPolicyReport{
|
||||||
if err := unstructured.SetNestedSlice(res.Object, resultsEntry, "results"); err != nil {
|
TypeMeta: metav1.TypeMeta{
|
||||||
return nil, sanitizederror.NewWithError("failed to set results entry", err)
|
Kind: "ClusterPolicyReport",
|
||||||
}
|
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
||||||
|
},
|
||||||
summary := updateSummary(resultsEntry)
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
if err := unstructured.SetNestedField(res.Object, summary, "summary"); err != nil {
|
Name: clusterpolicyreport,
|
||||||
return nil, sanitizederror.NewWithError("failed to set summary", err)
|
},
|
||||||
}
|
Results: results,
|
||||||
|
Summary: reportutils.CalculateSummary(results),
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergeResults(report *unstructured.Unstructured, results *[]interface{}) {
|
|
||||||
entries, ok, err := unstructured.NestedSlice(report.UnstructuredContent(), "results")
|
|
||||||
if err != nil {
|
|
||||||
log.Log.V(3).Info("failed to get results entry", "report", report.GetName(), "error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
*results = append(*results, entries...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSummary(results []interface{}) map[string]interface{} {
|
|
||||||
summary := make(map[string]interface{})
|
|
||||||
status := []string{policyreportv1alpha2.StatusPass, policyreportv1alpha2.StatusFail, policyreportv1alpha2.StatusError, policyreportv1alpha2.StatusSkip, policyreportv1alpha2.StatusWarn}
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
if _, ok := summary[status[i]].(int64); !ok {
|
|
||||||
summary[status[i]] = int64(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, result := range results {
|
|
||||||
typedResult, ok := result.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch typedResult["result"].(string) {
|
|
||||||
case policyreportv1alpha2.StatusPass:
|
|
||||||
pass, _ := summary[policyreportv1alpha2.StatusPass].(int64)
|
|
||||||
pass++
|
|
||||||
summary[policyreportv1alpha2.StatusPass] = pass
|
|
||||||
case policyreportv1alpha2.StatusFail:
|
|
||||||
fail, _ := summary[policyreportv1alpha2.StatusFail].(int64)
|
|
||||||
fail++
|
|
||||||
summary[policyreportv1alpha2.StatusFail] = fail
|
|
||||||
case policyreportv1alpha2.StatusWarn:
|
|
||||||
warn, _ := summary[policyreportv1alpha2.StatusWarn].(int64)
|
|
||||||
warn++
|
|
||||||
summary[policyreportv1alpha2.StatusWarn] = warn
|
|
||||||
case policyreportv1alpha2.StatusError:
|
|
||||||
e, _ := summary[policyreportv1alpha2.StatusError].(int64)
|
|
||||||
e++
|
|
||||||
summary[policyreportv1alpha2.StatusError] = e
|
|
||||||
case policyreportv1alpha2.StatusSkip:
|
|
||||||
skip, _ := summary[policyreportv1alpha2.StatusSkip].(int64)
|
|
||||||
skip++
|
|
||||||
summary[policyreportv1alpha2.StatusSkip] = skip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return summary
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,148 +4,127 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||||
report "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
report "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_mergeClusterReport(t *testing.T) {
|
func Test_mergeClusterReport(t *testing.T) {
|
||||||
reports := []*unstructured.Unstructured{
|
clustered := []policyreportv1alpha2.ClusterPolicyReport{
|
||||||
{
|
{
|
||||||
Object: map[string]interface{}{
|
TypeMeta: metav1.TypeMeta{
|
||||||
"apiVersion": report.SchemeGroupVersion.String(),
|
Kind: "ClusterPolicyReport",
|
||||||
"kind": "PolicyReport",
|
APIVersion: report.SchemeGroupVersion.String(),
|
||||||
"metadata": map[string]interface{}{
|
},
|
||||||
"name": "ns-polr-1",
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
"namespace": "ns-polr",
|
Name: "cpolr-4",
|
||||||
},
|
},
|
||||||
"results": []interface{}{
|
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||||
map[string]interface{}{
|
{
|
||||||
"policy": "ns-polr-1",
|
Policy: "cpolr-4",
|
||||||
"result": report.StatusPass,
|
Result: report.StatusFail,
|
||||||
"resources": make([]interface{}, 10),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Object: map[string]interface{}{
|
TypeMeta: metav1.TypeMeta{
|
||||||
"apiVersion": report.SchemeGroupVersion.String(),
|
Kind: "ClusterPolicyReport",
|
||||||
"kind": "PolicyReport",
|
APIVersion: report.SchemeGroupVersion.String(),
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "ns-polr-2",
|
|
||||||
},
|
|
||||||
"results": []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"policy": "ns-polr-2",
|
|
||||||
"result": report.StatusPass,
|
|
||||||
"resources": make([]interface{}, 5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
{
|
Name: "cpolr-5",
|
||||||
Object: map[string]interface{}{
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "polr-3",
|
|
||||||
},
|
|
||||||
"results": []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"policy": "polr-3",
|
|
||||||
"result": report.StatusPass,
|
|
||||||
"resources": make([]interface{}, 1),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||||
{
|
{
|
||||||
Object: map[string]interface{}{
|
Policy: "cpolr-5",
|
||||||
"apiVersion": report.SchemeGroupVersion.String(),
|
Result: report.StatusFail,
|
||||||
"kind": "ClusterPolicyReport",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cpolr-4",
|
|
||||||
},
|
|
||||||
"results": []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"policy": "cpolr-4",
|
|
||||||
"result": report.StatusFail,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Object: map[string]interface{}{
|
|
||||||
"apiVersion": report.SchemeGroupVersion.String(),
|
|
||||||
"kind": "ClusterPolicyReport",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cpolr-5",
|
|
||||||
},
|
|
||||||
"results": []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"policy": "cpolr-5",
|
|
||||||
"result": report.StatusFail,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedResults := []interface{}{
|
namespaced := []policyreportv1alpha2.PolicyReport{
|
||||||
map[string]interface{}{
|
{
|
||||||
"policy": "ns-polr-2",
|
TypeMeta: metav1.TypeMeta{
|
||||||
"result": report.StatusPass,
|
Kind: "PolicyReport",
|
||||||
"resources": make([]interface{}, 5),
|
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),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
{
|
||||||
"policy": "polr-3",
|
TypeMeta: metav1.TypeMeta{
|
||||||
"result": report.StatusPass,
|
Kind: "PolicyReport",
|
||||||
"resources": make([]interface{}, 1),
|
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),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
{
|
||||||
"policy": "cpolr-4",
|
TypeMeta: metav1.TypeMeta{
|
||||||
"result": report.StatusFail,
|
Kind: "PolicyReport",
|
||||||
},
|
APIVersion: report.SchemeGroupVersion.String(),
|
||||||
map[string]interface{}{
|
},
|
||||||
"policy": "cpolr-5",
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
"result": report.StatusFail,
|
Name: "polr-3",
|
||||||
|
},
|
||||||
|
Results: []policyreportv1alpha2.PolicyReportResult{
|
||||||
|
{
|
||||||
|
Policy: "polr-3",
|
||||||
|
Result: report.StatusPass,
|
||||||
|
Resources: make([]corev1.ObjectReference, 1),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cpolr, err := mergeClusterReport(reports)
|
expectedResults := []policyreportv1alpha2.PolicyReportResult{
|
||||||
assert.NilError(t, err)
|
{
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
assert.Assert(t, cpolr.GetAPIVersion() == report.SchemeGroupVersion.String(), cpolr.GetAPIVersion())
|
cpolr := mergeClusterReport(clustered, namespaced)
|
||||||
assert.Assert(t, cpolr.GetKind() == "ClusterPolicyReport", cpolr.GetKind())
|
|
||||||
|
|
||||||
entries, _, err := unstructured.NestedSlice(cpolr.UnstructuredContent(), "results")
|
assert.Assert(t, cpolr.APIVersion == report.SchemeGroupVersion.String(), cpolr.Kind)
|
||||||
assert.NilError(t, err)
|
assert.Assert(t, cpolr.Kind == "ClusterPolicyReport", cpolr.Kind)
|
||||||
|
|
||||||
assert.Assert(t, reflect.DeepEqual(entries, expectedResults), entries...)
|
assert.Assert(t, reflect.DeepEqual(cpolr.Results, expectedResults), cpolr.Results)
|
||||||
|
|
||||||
summary, _, err := unstructured.NestedMap(cpolr.UnstructuredContent(), "summary")
|
assert.Assert(t, cpolr.Summary.Pass == 2, cpolr.Summary.Pass)
|
||||||
assert.NilError(t, err)
|
assert.Assert(t, cpolr.Summary.Fail == 2, cpolr.Summary.Fail)
|
||||||
assert.Assert(t, summary[report.StatusPass].(int64) == 2, summary[report.StatusPass])
|
|
||||||
assert.Assert(t, summary[report.StatusFail].(int64) == 2, summary[report.StatusFail])
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_updateSummary(t *testing.T) {
|
|
||||||
results := []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"result": report.StatusPass,
|
|
||||||
"resources": make([]interface{}, 5),
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"result": report.StatusFail,
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"result": report.StatusFail,
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"result": report.StatusFail,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
summary := updateSummary(results)
|
|
||||||
assert.Assert(t, summary[report.StatusPass].(int64) == 1, summary[report.StatusPass])
|
|
||||||
assert.Assert(t, summary[report.StatusFail].(int64) == 3, summary[report.StatusFail])
|
|
||||||
}
|
}
|
||||||
|
|
78
cmd/cli/kubectl-kyverno/apply/log.go
Normal file
78
cmd/cli/kubectl-kyverno/apply/log.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package apply
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
||||||
|
)
|
||||||
|
|
||||||
|
// checkMutateLogPath - checking path for printing mutated resource (-o flag)
|
||||||
|
func checkMutateLogPath(mutateLogPath string) (mutateLogPathIsDir bool, err error) {
|
||||||
|
if mutateLogPath != "" {
|
||||||
|
spath := strings.Split(mutateLogPath, "/")
|
||||||
|
sfileName := strings.Split(spath[len(spath)-1], ".")
|
||||||
|
if sfileName[len(sfileName)-1] == "yml" || sfileName[len(sfileName)-1] == "yaml" {
|
||||||
|
mutateLogPathIsDir = false
|
||||||
|
} else {
|
||||||
|
mutateLogPathIsDir = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err := createFileOrFolder(mutateLogPath, mutateLogPathIsDir)
|
||||||
|
if err != nil {
|
||||||
|
if !sanitizederror.IsErrorSanitized(err) {
|
||||||
|
return mutateLogPathIsDir, sanitizederror.NewWithError("failed to create file/folder.", err)
|
||||||
|
}
|
||||||
|
return mutateLogPathIsDir, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mutateLogPathIsDir, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// createFileOrFolder - creating file or folder according to path provided
|
||||||
|
func createFileOrFolder(mutateLogPath string, mutateLogPathIsDir bool) error {
|
||||||
|
mutateLogPath = filepath.Clean(mutateLogPath)
|
||||||
|
_, err := os.Stat(mutateLogPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
if !mutateLogPathIsDir {
|
||||||
|
// check the folder existence, then create the file
|
||||||
|
var folderPath string
|
||||||
|
s := strings.Split(mutateLogPath, "/")
|
||||||
|
|
||||||
|
if len(s) > 1 {
|
||||||
|
folderPath = mutateLogPath[:len(mutateLogPath)-len(s[len(s)-1])-1]
|
||||||
|
_, err := os.Stat(folderPath)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
errDir := os.MkdirAll(folderPath, 0o750)
|
||||||
|
if errDir != nil {
|
||||||
|
return sanitizederror.NewWithError("failed to create directory", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutateLogPath = filepath.Clean(mutateLogPath)
|
||||||
|
// Necessary for us to create the file via variable as it is part of the CLI.
|
||||||
|
file, err := os.OpenFile(mutateLogPath, os.O_RDONLY|os.O_CREATE, 0o600) // #nosec G304
|
||||||
|
if err != nil {
|
||||||
|
return sanitizederror.NewWithError("failed to create file", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = file.Close()
|
||||||
|
if err != nil {
|
||||||
|
return sanitizederror.NewWithError("failed to close file", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errDir := os.MkdirAll(mutateLogPath, 0o750)
|
||||||
|
if errDir != nil {
|
||||||
|
return sanitizederror.NewWithError("failed to create directory", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return sanitizederror.NewWithError("failed to describe file", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package apply
|
package apply
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -9,65 +8,47 @@ import (
|
||||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
reportutils "github.com/kyverno/kyverno/pkg/utils/report"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const clusterpolicyreport = "clusterpolicyreport"
|
const clusterpolicyreport = "clusterpolicyreport"
|
||||||
|
|
||||||
// resps is the engine responses generated for a single policy
|
// resps is the engine responses generated for a single policy
|
||||||
func buildPolicyReports(auditWarn bool, engineResponses ...engineapi.EngineResponse) (res []*unstructured.Unstructured) {
|
func buildPolicyReports(auditWarn bool, engineResponses ...engineapi.EngineResponse) ([]policyreportv1alpha2.ClusterPolicyReport, []policyreportv1alpha2.PolicyReport) {
|
||||||
var raw []byte
|
var clustered []policyreportv1alpha2.ClusterPolicyReport
|
||||||
var err error
|
var namespaced []policyreportv1alpha2.PolicyReport
|
||||||
|
|
||||||
resultsMap := buildPolicyResults(auditWarn, engineResponses...)
|
resultsMap := buildPolicyResults(auditWarn, engineResponses...)
|
||||||
for scope, result := range resultsMap {
|
for scope, result := range resultsMap {
|
||||||
if scope == clusterpolicyreport {
|
if scope == clusterpolicyreport {
|
||||||
report := &policyreportv1alpha2.ClusterPolicyReport{
|
report := policyreportv1alpha2.ClusterPolicyReport{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
||||||
Kind: "ClusterPolicyReport",
|
Kind: "ClusterPolicyReport",
|
||||||
},
|
},
|
||||||
Results: result,
|
Results: result,
|
||||||
Summary: calculateSummary(result),
|
Summary: reportutils.CalculateSummary(result),
|
||||||
}
|
}
|
||||||
|
|
||||||
report.SetName(scope)
|
report.SetName(scope)
|
||||||
if raw, err = json.Marshal(report); err != nil {
|
clustered = append(clustered, report)
|
||||||
log.Log.V(3).Info("failed to serialize policy report", "name", report.Name, "scope", scope, "error", err)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
report := &policyreportv1alpha2.PolicyReport{
|
report := policyreportv1alpha2.PolicyReport{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
APIVersion: policyreportv1alpha2.SchemeGroupVersion.String(),
|
||||||
Kind: "PolicyReport",
|
Kind: "PolicyReport",
|
||||||
},
|
},
|
||||||
Results: result,
|
Results: result,
|
||||||
Summary: calculateSummary(result),
|
Summary: reportutils.CalculateSummary(result),
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := strings.ReplaceAll(scope, "policyreport-ns-", "")
|
ns := strings.ReplaceAll(scope, "policyreport-ns-", "")
|
||||||
report.SetName(scope)
|
report.SetName(scope)
|
||||||
report.SetNamespace(ns)
|
report.SetNamespace(ns)
|
||||||
|
namespaced = append(namespaced, report)
|
||||||
if raw, err = json.Marshal(report); err != nil {
|
|
||||||
log.Log.V(3).Info("failed to serialize policy report", "name", report.Name, "scope", scope, "error", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reportUnstructured, err := kubeutils.BytesToUnstructured(raw)
|
|
||||||
if err != nil {
|
|
||||||
log.Log.V(3).Info("failed to convert policy report", "scope", scope, "error", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, reportUnstructured)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return clustered, namespaced
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildPolicyResults returns a string-PolicyReportResult map
|
// buildPolicyResults returns a string-PolicyReportResult map
|
||||||
|
@ -150,21 +131,3 @@ func buildPolicyResults(auditWarn bool, engineResponses ...engineapi.EngineRespo
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateSummary(results []policyreportv1alpha2.PolicyReportResult) (summary policyreportv1alpha2.PolicyReportSummary) {
|
|
||||||
for _, res := range results {
|
|
||||||
switch string(res.Result) {
|
|
||||||
case policyreportv1alpha2.StatusPass:
|
|
||||||
summary.Pass++
|
|
||||||
case policyreportv1alpha2.StatusFail:
|
|
||||||
summary.Fail++
|
|
||||||
case "warn":
|
|
||||||
summary.Warn++
|
|
||||||
case "error":
|
|
||||||
summary.Error++
|
|
||||||
case "skip":
|
|
||||||
summary.Skip++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
preport "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
preport "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
|
||||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var rawPolicy = []byte(`
|
var rawPolicy = []byte(`
|
||||||
|
@ -102,25 +101,15 @@ func Test_buildPolicyReports(t *testing.T) {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
reports := buildPolicyReports(false, er)
|
clustered, namespaced := buildPolicyReports(false, er)
|
||||||
assert.Assert(t, len(reports) == 1, len(reports))
|
assert.Assert(t, len(clustered) == 1, len(clustered))
|
||||||
|
assert.Assert(t, len(namespaced) == 0, len(namespaced))
|
||||||
for _, report := range reports {
|
{
|
||||||
if report.GetNamespace() == "" {
|
report := clustered[0]
|
||||||
assert.Assert(t, report.GetName() == clusterpolicyreport)
|
assert.Assert(t, report.GetName() == clusterpolicyreport)
|
||||||
assert.Assert(t, report.GetKind() == "ClusterPolicyReport")
|
assert.Assert(t, report.Kind == "ClusterPolicyReport")
|
||||||
assert.Assert(t, len(report.UnstructuredContent()["results"].([]interface{})) == 2)
|
assert.Assert(t, len(report.Results) == 2)
|
||||||
assert.Assert(t,
|
assert.Assert(t, report.Summary.Pass == 1, report.Summary.Pass)
|
||||||
report.UnstructuredContent()["summary"].(map[string]interface{})[preport.StatusPass].(int64) == 1,
|
|
||||||
report.UnstructuredContent()["summary"].(map[string]interface{})[preport.StatusPass].(int64))
|
|
||||||
} else {
|
|
||||||
assert.Assert(t, report.GetName() == "policyreport-ns-default")
|
|
||||||
assert.Assert(t, report.GetKind() == "PolicyReport")
|
|
||||||
assert.Assert(t, len(report.UnstructuredContent()["results"].([]interface{})) == 2)
|
|
||||||
|
|
||||||
summary := report.UnstructuredContent()["summary"].(map[string]interface{})
|
|
||||||
assert.Assert(t, summary[preport.StatusPass].(int64) == 1, summary[preport.StatusPass].(int64))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,26 +147,3 @@ func Test_buildPolicyResults(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_calculateSummary(t *testing.T) {
|
|
||||||
results := []preport.PolicyReportResult{
|
|
||||||
{
|
|
||||||
Resources: make([]v1.ObjectReference, 5),
|
|
||||||
Result: preport.PolicyResult(preport.StatusPass),
|
|
||||||
},
|
|
||||||
{Result: preport.PolicyResult(preport.StatusFail)},
|
|
||||||
{Result: preport.PolicyResult(preport.StatusFail)},
|
|
||||||
{Result: preport.PolicyResult(preport.StatusFail)},
|
|
||||||
{
|
|
||||||
Resources: make([]v1.ObjectReference, 1),
|
|
||||||
Result: preport.PolicyResult(preport.StatusPass)},
|
|
||||||
{
|
|
||||||
Resources: make([]v1.ObjectReference, 4),
|
|
||||||
Result: preport.PolicyResult(preport.StatusPass),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
summary := calculateSummary(results)
|
|
||||||
assert.Assert(t, summary.Pass == 3)
|
|
||||||
assert.Assert(t, summary.Fail == 3)
|
|
||||||
}
|
|
||||||
|
|
|
@ -166,11 +166,6 @@ func applyPoliciesFromPath(
|
||||||
}
|
}
|
||||||
policies = filteredPolicies
|
policies = filteredPolicies
|
||||||
|
|
||||||
err = common.PrintMutatedPolicy(policies)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, sanitizederror.NewWithError("failed to print mutated policy", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resources, err := common.GetResourceAccordingToResourcePath(fs, resourceFullPath, false, policies, validatingAdmissionPolicies, dClient, "", false, isGit, policyResourcePath)
|
resources, err := common.GetResourceAccordingToResourcePath(fs, resourceFullPath, false, policies, validatingAdmissionPolicies, dClient, "", false, isGit, policyResourcePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
|
fmt.Printf("Error: failed to load resources\nCause: %s\n", err)
|
||||||
|
|
|
@ -651,17 +651,6 @@ func processMutateEngineResponse(c ApplyPolicyConfig, mutateResponse *engineapi.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintMutatedPolicy(mutatedPolicies []kyvernov1.PolicyInterface) error {
|
|
||||||
for _, policy := range mutatedPolicies {
|
|
||||||
p, err := json.Marshal(policy)
|
|
||||||
if err != nil {
|
|
||||||
return sanitizederror.NewWithError("failed to marsal mutated policy", err)
|
|
||||||
}
|
|
||||||
log.V(5).Info("mutated Policy:", string(p))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckVariableForPolicy(valuesMap map[string]map[string]values.Resource, globalValMap map[string]string, policyName string, resourceName string, resourceKind string, variables map[string]string, kindOnwhichPolicyIsApplied map[string]struct{}, variable string) (map[string]interface{}, error) {
|
func CheckVariableForPolicy(valuesMap map[string]map[string]values.Resource, globalValMap map[string]string, policyName string, resourceName string, resourceKind string, variables map[string]string, kindOnwhichPolicyIsApplied map[string]struct{}, variable string) (map[string]interface{}, error) {
|
||||||
// get values from file for this policy resource combination
|
// get values from file for this policy resource combination
|
||||||
thisPolicyResourceValues := make(map[string]interface{})
|
thisPolicyResourceValues := make(map[string]interface{})
|
||||||
|
|
Loading…
Add table
Reference in a new issue