mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
refactor: introduce cli processor package (#8281)
* refactor: introduce cli processor package Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * tests Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * counts Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
04e21da671
commit
e98bfd1cd9
13 changed files with 531 additions and 490 deletions
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/output/color"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/output/color"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/processor"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/source"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/source"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/userinfo"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/userinfo"
|
||||||
cobrautils "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/cobra"
|
cobrautils "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/cobra"
|
||||||
|
@ -127,7 +128,7 @@ func Command() *cobra.Command {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApplyCommandConfig) applyCommandHelper() (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
func (c *ApplyCommandConfig) applyCommandHelper() (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
||||||
rc, uu, skipInvalidPolicies, er, err := c.checkArguments()
|
rc, uu, skipInvalidPolicies, er, err := c.checkArguments()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rc, uu, skipInvalidPolicies, er, err
|
return rc, uu, skipInvalidPolicies, er, err
|
||||||
|
@ -178,7 +179,7 @@ func (c *ApplyCommandConfig) applyCommandHelper() (*common.ResultCounts, []*unst
|
||||||
return rc, resources, skipInvalidPolicies, er, nil
|
return rc, resources, skipInvalidPolicies, er, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApplyCommandConfig) getMutateLogPathIsDir(skipInvalidPolicies SkippedInvalidPolicies) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error, bool) {
|
func (c *ApplyCommandConfig) getMutateLogPathIsDir(skipInvalidPolicies SkippedInvalidPolicies) (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error, bool) {
|
||||||
mutateLogPathIsDir, err := checkMutateLogPath(c.MutateLogPath)
|
mutateLogPathIsDir, err := checkMutateLogPath(c.MutateLogPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !sanitizederror.IsErrorSanitized(err) {
|
if !sanitizederror.IsErrorSanitized(err) {
|
||||||
|
@ -189,20 +190,16 @@ func (c *ApplyCommandConfig) getMutateLogPathIsDir(skipInvalidPolicies SkippedIn
|
||||||
return nil, nil, skipInvalidPolicies, nil, err, mutateLogPathIsDir
|
return nil, nil, skipInvalidPolicies, nil, err, mutateLogPathIsDir
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApplyCommandConfig) applyValidatingAdmissionPolicytoResource(validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, resources []*unstructured.Unstructured, rc *common.ResultCounts, dClient dclient.Interface, subresources []valuesapi.Subresource, skipInvalidPolicies SkippedInvalidPolicies, responses []engineapi.EngineResponse) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
func (c *ApplyCommandConfig) applyValidatingAdmissionPolicytoResource(validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, resources []*unstructured.Unstructured, rc *processor.ResultCounts, dClient dclient.Interface, subresources []valuesapi.Subresource, skipInvalidPolicies SkippedInvalidPolicies, responses []engineapi.EngineResponse) (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
||||||
validatingAdmissionPolicy := common.ValidatingAdmissionPolicies{}
|
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
for _, policy := range validatingAdmissionPolicies {
|
for _, policy := range validatingAdmissionPolicies {
|
||||||
applyPolicyConfig := common.ApplyPolicyConfig{
|
processor := processor.ValidatingAdmissionPolicyProcessor{
|
||||||
ValidatingAdmissionPolicy: policy,
|
ValidatingAdmissionPolicy: policy,
|
||||||
Resource: resource,
|
Resource: resource,
|
||||||
PolicyReport: c.PolicyReport,
|
PolicyReport: c.PolicyReport,
|
||||||
Rc: rc,
|
Rc: rc,
|
||||||
Client: dClient,
|
|
||||||
AuditWarn: c.AuditWarn,
|
|
||||||
Subresources: subresources,
|
|
||||||
}
|
}
|
||||||
ers, err := validatingAdmissionPolicy.ApplyPolicyOnResource(applyPolicyConfig)
|
ers, err := processor.ApplyPolicyOnResource()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rc, resources, skipInvalidPolicies, responses, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
return rc, resources, skipInvalidPolicies, responses, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
||||||
}
|
}
|
||||||
|
@ -212,7 +209,7 @@ func (c *ApplyCommandConfig) applyValidatingAdmissionPolicytoResource(validating
|
||||||
return rc, resources, skipInvalidPolicies, responses, nil
|
return rc, resources, skipInvalidPolicies, responses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApplyCommandConfig) applyPolicytoResource(variables map[string]string, policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, resources []*unstructured.Unstructured, openApiManager openapi.Manager, skipInvalidPolicies SkippedInvalidPolicies, valuesMap map[string]map[string]valuesapi.Resource, dClient dclient.Interface, subresources []valuesapi.Subresource, globalValMap map[string]string, userInfo *v1beta1.RequestInfo, mutateLogPathIsDir bool, namespaceSelectorMap map[string]map[string]string) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
func (c *ApplyCommandConfig) applyPolicytoResource(variables map[string]string, policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, resources []*unstructured.Unstructured, openApiManager openapi.Manager, skipInvalidPolicies SkippedInvalidPolicies, valuesMap map[string]map[string]valuesapi.Resource, dClient dclient.Interface, subresources []valuesapi.Subresource, globalValMap map[string]string, userInfo *v1beta1.RequestInfo, mutateLogPathIsDir bool, namespaceSelectorMap map[string]map[string]string) (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
||||||
if len(variables) != 0 {
|
if len(variables) != 0 {
|
||||||
variables = common.SetInStoreContext(policies, variables)
|
variables = common.SetInStoreContext(policies, variables)
|
||||||
}
|
}
|
||||||
|
@ -226,7 +223,7 @@ func (c *ApplyCommandConfig) applyPolicytoResource(variables map[string]string,
|
||||||
fmt.Printf("\nApplying %d policy rule(s) to %d resource(s)...\n", policyRulesCount, len(resources))
|
fmt.Printf("\nApplying %d policy rule(s) to %d resource(s)...\n", policyRulesCount, len(resources))
|
||||||
}
|
}
|
||||||
|
|
||||||
var rc common.ResultCounts
|
var rc processor.ResultCounts
|
||||||
var responses []engineapi.EngineResponse
|
var responses []engineapi.EngineResponse
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
for _, policy := range policies {
|
for _, policy := range policies {
|
||||||
|
@ -257,7 +254,7 @@ func (c *ApplyCommandConfig) applyPolicytoResource(variables map[string]string,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &rc, resources, skipInvalidPolicies, responses, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.GetName(), resource.GetName()), err)
|
return &rc, resources, skipInvalidPolicies, responses, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.GetName(), resource.GetName()), err)
|
||||||
}
|
}
|
||||||
applyPolicyConfig := common.ApplyPolicyConfig{
|
processor := processor.PolicyProcessor{
|
||||||
Policy: policy,
|
Policy: policy,
|
||||||
Resource: resource,
|
Resource: resource,
|
||||||
MutateLogPath: c.MutateLogPath,
|
MutateLogPath: c.MutateLogPath,
|
||||||
|
@ -273,11 +270,11 @@ func (c *ApplyCommandConfig) applyPolicytoResource(variables map[string]string,
|
||||||
AuditWarn: c.AuditWarn,
|
AuditWarn: c.AuditWarn,
|
||||||
Subresources: subresources,
|
Subresources: subresources,
|
||||||
}
|
}
|
||||||
ers, err := common.ApplyPolicyOnResource(applyPolicyConfig)
|
ers, err := processor.ApplyPolicyOnResource()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &rc, resources, skipInvalidPolicies, responses, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
return &rc, resources, skipInvalidPolicies, responses, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName()).Error(), err)
|
||||||
}
|
}
|
||||||
responses = append(responses, processSkipEngineResponses(ers, applyPolicyConfig)...)
|
responses = append(responses, processSkipEngineResponses(ers)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &rc, resources, skipInvalidPolicies, responses, nil
|
return &rc, resources, skipInvalidPolicies, responses, nil
|
||||||
|
@ -292,7 +289,7 @@ func (c *ApplyCommandConfig) loadResources(policies []kyvernov1.PolicyInterface,
|
||||||
return resources
|
return resources
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApplyCommandConfig) loadPolicies(skipInvalidPolicies SkippedInvalidPolicies) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error, []kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy) {
|
func (c *ApplyCommandConfig) loadPolicies(skipInvalidPolicies SkippedInvalidPolicies) (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error, []kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy) {
|
||||||
// load policies
|
// load policies
|
||||||
var policies []kyvernov1.PolicyInterface
|
var policies []kyvernov1.PolicyInterface
|
||||||
var validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy
|
var validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy
|
||||||
|
@ -345,7 +342,7 @@ func (c *ApplyCommandConfig) loadPolicies(skipInvalidPolicies SkippedInvalidPoli
|
||||||
return nil, nil, skipInvalidPolicies, nil, nil, policies, validatingAdmissionPolicies
|
return nil, nil, skipInvalidPolicies, nil, nil, policies, validatingAdmissionPolicies
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApplyCommandConfig) initStoreAndClusterClient(skipInvalidPolicies SkippedInvalidPolicies) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error, dclient.Interface) {
|
func (c *ApplyCommandConfig) initStoreAndClusterClient(skipInvalidPolicies SkippedInvalidPolicies) (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error, dclient.Interface) {
|
||||||
store.SetLocal(true)
|
store.SetLocal(true)
|
||||||
store.SetRegistryAccess(c.RegistryAccess)
|
store.SetRegistryAccess(c.RegistryAccess)
|
||||||
if c.Cluster {
|
if c.Cluster {
|
||||||
|
@ -374,7 +371,7 @@ func (c *ApplyCommandConfig) initStoreAndClusterClient(skipInvalidPolicies Skipp
|
||||||
return nil, nil, skipInvalidPolicies, nil, err, dClient
|
return nil, nil, skipInvalidPolicies, nil, err, dClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApplyCommandConfig) cleanPreviousContent(mutateLogPathIsDir bool, skipInvalidPolicies SkippedInvalidPolicies) (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
func (c *ApplyCommandConfig) cleanPreviousContent(mutateLogPathIsDir bool, skipInvalidPolicies SkippedInvalidPolicies) (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
||||||
// empty the previous contents of the file just in case if the file already existed before with some content(so as to perform overwrites)
|
// 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
|
// the truncation of files for the case when mutateLogPath is dir, is handled under pkg/kyverno/apply/common.go
|
||||||
if !mutateLogPathIsDir && c.MutateLogPath != "" {
|
if !mutateLogPathIsDir && c.MutateLogPath != "" {
|
||||||
|
@ -391,7 +388,7 @@ func (c *ApplyCommandConfig) cleanPreviousContent(mutateLogPathIsDir bool, skipI
|
||||||
return nil, nil, skipInvalidPolicies, nil, nil
|
return nil, nil, skipInvalidPolicies, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApplyCommandConfig) checkArguments() (*common.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
func (c *ApplyCommandConfig) checkArguments() (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
||||||
var skipInvalidPolicies SkippedInvalidPolicies
|
var skipInvalidPolicies SkippedInvalidPolicies
|
||||||
if c.ValuesFile != "" && c.Variables != nil {
|
if c.ValuesFile != "" && c.Variables != nil {
|
||||||
return nil, nil, skipInvalidPolicies, nil, sanitizederror.New("pass the values either using set flag or values_file flag")
|
return nil, nil, skipInvalidPolicies, nil, sanitizederror.New("pass the values either using set flag or values_file flag")
|
||||||
|
@ -442,21 +439,21 @@ func printReport(engineResponses []engineapi.EngineResponse, auditWarn bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func printViolations(rc *common.ResultCounts) {
|
func printViolations(rc *processor.ResultCounts) {
|
||||||
fmt.Printf("\npass: %d, fail: %d, warn: %d, error: %d, skip: %d \n", rc.Pass, rc.Fail, rc.Warn, rc.Error, rc.Skip)
|
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) {
|
func exit(rc *processor.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 {
|
||||||
osExit(warnExitCode)
|
osExit(warnExitCode)
|
||||||
} else if rc.Pass == 0 && warnNoPassed {
|
} else if rc.Pass() == 0 && warnNoPassed {
|
||||||
osExit(warnExitCode)
|
osExit(warnExitCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func processSkipEngineResponses(responses []engineapi.EngineResponse, c common.ApplyPolicyConfig) []engineapi.EngineResponse {
|
func processSkipEngineResponses(responses []engineapi.EngineResponse) []engineapi.EngineResponse {
|
||||||
var processedEngineResponses []engineapi.EngineResponse
|
var processedEngineResponses []engineapi.EngineResponse
|
||||||
for _, response := range responses {
|
for _, response := range responses {
|
||||||
if !response.IsEmpty() {
|
if !response.IsEmpty() {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/output/pluralize"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/output/pluralize"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/processor"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/test"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/userinfo"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/userinfo"
|
||||||
|
@ -114,7 +115,7 @@ func runTest(openApiManager openapi.Manager, testCase test.TestCase, auditWarn b
|
||||||
// execute engine
|
// execute engine
|
||||||
fmt.Println(" Applying", len(policies), pluralize.Pluralize(len(policies), "policy", "policies"), "to", len(uniques), pluralize.Pluralize(len(uniques), "resource", "resources"), "...")
|
fmt.Println(" Applying", len(policies), pluralize.Pluralize(len(policies), "policy", "policies"), "to", len(uniques), pluralize.Pluralize(len(uniques), "resource", "resources"), "...")
|
||||||
var engineResponses []engineapi.EngineResponse
|
var engineResponses []engineapi.EngineResponse
|
||||||
var resultCounts common.ResultCounts
|
var resultCounts processor.ResultCounts
|
||||||
// TODO loop through resources first, then through policies second
|
// TODO loop through resources first, then through policies second
|
||||||
for _, policy := range policies {
|
for _, policy := range policies {
|
||||||
// TODO we should return this info to the caller
|
// TODO we should return this info to the caller
|
||||||
|
@ -149,7 +150,7 @@ func runTest(openApiManager openapi.Manager, testCase test.TestCase, auditWarn b
|
||||||
)
|
)
|
||||||
return nil, sanitizederror.NewWithError(message, err)
|
return nil, sanitizederror.NewWithError(message, err)
|
||||||
}
|
}
|
||||||
applyPolicyConfig := common.ApplyPolicyConfig{
|
processor := processor.PolicyProcessor{
|
||||||
Policy: policy,
|
Policy: policy,
|
||||||
Resource: resource,
|
Resource: resource,
|
||||||
MutateLogPath: "",
|
MutateLogPath: "",
|
||||||
|
@ -162,7 +163,7 @@ func runTest(openApiManager openapi.Manager, testCase test.TestCase, auditWarn b
|
||||||
Client: dClient,
|
Client: dClient,
|
||||||
Subresources: subresources,
|
Subresources: subresources,
|
||||||
}
|
}
|
||||||
ers, err := common.ApplyPolicyOnResource(applyPolicyConfig)
|
ers, err := processor.ApplyPolicyOnResource()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
message := fmt.Sprintf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName())
|
message := fmt.Sprintf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName())
|
||||||
return nil, sanitizederror.NewWithError(message, err)
|
return nil, sanitizederror.NewWithError(message, err)
|
||||||
|
@ -170,18 +171,15 @@ func runTest(openApiManager openapi.Manager, testCase test.TestCase, auditWarn b
|
||||||
engineResponses = append(engineResponses, ers...)
|
engineResponses = append(engineResponses, ers...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
validatingAdmissionPolicy := common.ValidatingAdmissionPolicies{}
|
|
||||||
for _, policy := range validatingAdmissionPolicies {
|
for _, policy := range validatingAdmissionPolicies {
|
||||||
for _, resource := range uniques {
|
for _, resource := range uniques {
|
||||||
applyPolicyConfig := common.ApplyPolicyConfig{
|
processor := processor.ValidatingAdmissionPolicyProcessor{
|
||||||
ValidatingAdmissionPolicy: policy,
|
ValidatingAdmissionPolicy: policy,
|
||||||
Resource: resource,
|
Resource: resource,
|
||||||
PolicyReport: true,
|
PolicyReport: true,
|
||||||
Rc: &resultCounts,
|
Rc: &resultCounts,
|
||||||
Client: dClient,
|
|
||||||
Subresources: subresources,
|
|
||||||
}
|
}
|
||||||
ers, err := validatingAdmissionPolicy.ApplyPolicyOnResource(applyPolicyConfig)
|
ers, err := processor.ApplyPolicyOnResource()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
message := fmt.Sprintf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName())
|
message := fmt.Sprintf("failed to apply policy %v on resource %v", policy.GetName(), resource.GetName())
|
||||||
return nil, sanitizederror.NewWithError(message, err)
|
return nil, sanitizederror.NewWithError(message, err)
|
||||||
|
|
104
cmd/cli/kubectl-kyverno/processor/generate.go
Normal file
104
cmd/cli/kubectl-kyverno/processor/generate.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
|
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
|
||||||
|
"github.com/kyverno/kyverno/pkg/background/generate"
|
||||||
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||||
|
"github.com/kyverno/kyverno/pkg/config"
|
||||||
|
"github.com/kyverno/kyverno/pkg/engine"
|
||||||
|
"github.com/kyverno/kyverno/pkg/engine/adapters"
|
||||||
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
|
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
||||||
|
"github.com/kyverno/kyverno/pkg/imageverifycache"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handleGeneratePolicy(generateResponse *engineapi.EngineResponse, policyContext engine.PolicyContext, ruleToCloneSourceResource map[string]string) ([]engineapi.RuleResponse, error) {
|
||||||
|
newResource := policyContext.NewResource()
|
||||||
|
objects := []runtime.Object{&newResource}
|
||||||
|
resources := []*unstructured.Unstructured{}
|
||||||
|
for _, rule := range generateResponse.PolicyResponse.Rules {
|
||||||
|
if path, ok := ruleToCloneSourceResource[rule.Name()]; ok {
|
||||||
|
resourceBytes, err := resource.GetFileBytes(path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to get resource bytes\n")
|
||||||
|
} else {
|
||||||
|
resources, err = resource.GetUnstructuredResources(resourceBytes)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to convert resource bytes to unstructured format\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, res := range resources {
|
||||||
|
objects = append(objects, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := initializeMockController(objects)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error at controller")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gr := kyvernov1beta1.UpdateRequest{
|
||||||
|
Spec: kyvernov1beta1.UpdateRequestSpec{
|
||||||
|
Type: kyvernov1beta1.Generate,
|
||||||
|
Policy: generateResponse.Policy().GetName(),
|
||||||
|
Resource: kyvernov1.ResourceSpec{
|
||||||
|
Kind: generateResponse.Resource.GetKind(),
|
||||||
|
Namespace: generateResponse.Resource.GetNamespace(),
|
||||||
|
Name: generateResponse.Resource.GetName(),
|
||||||
|
APIVersion: generateResponse.Resource.GetAPIVersion(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var newRuleResponse []engineapi.RuleResponse
|
||||||
|
|
||||||
|
for _, rule := range generateResponse.PolicyResponse.Rules {
|
||||||
|
genResource, err := c.ApplyGeneratePolicy(log.Log.V(2), &policyContext, gr, []string{rule.Name()})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if genResource != nil {
|
||||||
|
unstrGenResource, err := c.GetUnstrResource(genResource[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newRuleResponse = append(newRuleResponse, *rule.WithGeneratedResource(*unstrGenResource))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newRuleResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeMockController(objects []runtime.Object) (*generate.GenerateController, error) {
|
||||||
|
client, err := dclient.NewFakeClient(runtime.NewScheme(), nil, objects...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to mock dynamic client")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client.SetDiscovery(dclient.NewFakeDiscoveryClient(nil))
|
||||||
|
cfg := config.NewDefaultConfiguration(false)
|
||||||
|
c := generate.NewGenerateControllerWithOnlyClient(client, engine.NewEngine(
|
||||||
|
cfg,
|
||||||
|
config.NewDefaultMetricsConfiguration(),
|
||||||
|
jmespath.New(cfg),
|
||||||
|
adapters.Client(client),
|
||||||
|
nil,
|
||||||
|
imageverifycache.DisabledImageVerifyCache(),
|
||||||
|
store.ContextLoaderFactory(nil),
|
||||||
|
nil,
|
||||||
|
"",
|
||||||
|
))
|
||||||
|
return c, nil
|
||||||
|
}
|
|
@ -1,15 +1,20 @@
|
||||||
package common
|
package processor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
|
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||||
|
valuesapi "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/values"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
||||||
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
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/cmd/cli/kubectl-kyverno/utils/store"
|
||||||
"github.com/kyverno/kyverno/pkg/autogen"
|
"github.com/kyverno/kyverno/pkg/autogen"
|
||||||
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||||
"github.com/kyverno/kyverno/pkg/config"
|
"github.com/kyverno/kyverno/pkg/config"
|
||||||
"github.com/kyverno/kyverno/pkg/engine"
|
"github.com/kyverno/kyverno/pkg/engine"
|
||||||
"github.com/kyverno/kyverno/pkg/engine/adapters"
|
"github.com/kyverno/kyverno/pkg/engine/adapters"
|
||||||
|
@ -19,24 +24,43 @@ import (
|
||||||
"github.com/kyverno/kyverno/pkg/imageverifycache"
|
"github.com/kyverno/kyverno/pkg/imageverifycache"
|
||||||
"github.com/kyverno/kyverno/pkg/registryclient"
|
"github.com/kyverno/kyverno/pkg/registryclient"
|
||||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||||
|
yamlv2 "gopkg.in/yaml.v2"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ApplyPolicyOnResource - function to apply policy on resource
|
type PolicyProcessor struct {
|
||||||
func ApplyPolicyOnResource(c ApplyPolicyConfig) ([]engineapi.EngineResponse, error) {
|
Policy kyvernov1.PolicyInterface
|
||||||
|
Resource *unstructured.Unstructured
|
||||||
|
MutateLogPath string
|
||||||
|
MutateLogPathIsDir bool
|
||||||
|
Variables map[string]interface{}
|
||||||
|
UserInfo *kyvernov1beta1.RequestInfo
|
||||||
|
PolicyReport bool
|
||||||
|
NamespaceSelectorMap map[string]map[string]string
|
||||||
|
Stdin bool
|
||||||
|
Rc *ResultCounts
|
||||||
|
PrintPatchResource bool
|
||||||
|
RuleToCloneSourceResource map[string]string
|
||||||
|
Client dclient.Interface
|
||||||
|
AuditWarn bool
|
||||||
|
Subresources []valuesapi.Subresource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PolicyProcessor) ApplyPolicyOnResource() ([]engineapi.EngineResponse, error) {
|
||||||
jp := jmespath.New(config.NewDefaultConfiguration(false))
|
jp := jmespath.New(config.NewDefaultConfiguration(false))
|
||||||
|
|
||||||
var engineResponses []engineapi.EngineResponse
|
var engineResponses []engineapi.EngineResponse
|
||||||
namespaceLabels := make(map[string]string)
|
namespaceLabels := make(map[string]string)
|
||||||
operation := kyvernov1.Create
|
operation := kyvernov1.Create
|
||||||
|
|
||||||
if c.Variables["request.operation"] == "DELETE" {
|
if p.Variables["request.operation"] == "DELETE" {
|
||||||
operation = kyvernov1.Delete
|
operation = kyvernov1.Delete
|
||||||
}
|
}
|
||||||
|
|
||||||
policyWithNamespaceSelector := false
|
policyWithNamespaceSelector := false
|
||||||
OuterLoop:
|
OuterLoop:
|
||||||
for _, p := range autogen.ComputeRules(c.Policy) {
|
for _, p := range autogen.ComputeRules(p.Policy) {
|
||||||
if p.MatchResources.ResourceDescription.NamespaceSelector != nil ||
|
if p.MatchResources.ResourceDescription.NamespaceSelector != nil ||
|
||||||
p.ExcludeResources.ResourceDescription.NamespaceSelector != nil {
|
p.ExcludeResources.ResourceDescription.NamespaceSelector != nil {
|
||||||
policyWithNamespaceSelector = true
|
policyWithNamespaceSelector = true
|
||||||
|
@ -69,17 +93,17 @@ OuterLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
if policyWithNamespaceSelector {
|
if policyWithNamespaceSelector {
|
||||||
resourceNamespace := c.Resource.GetNamespace()
|
resourceNamespace := p.Resource.GetNamespace()
|
||||||
namespaceLabels = c.NamespaceSelectorMap[c.Resource.GetNamespace()]
|
namespaceLabels = p.NamespaceSelectorMap[p.Resource.GetNamespace()]
|
||||||
if resourceNamespace != "default" && len(namespaceLabels) < 1 {
|
if resourceNamespace != "default" && len(namespaceLabels) < 1 {
|
||||||
return engineResponses, sanitizederror.NewWithError(fmt.Sprintf("failed to get namespace labels for resource %s. use --values-file flag to pass the namespace labels", c.Resource.GetName()), nil)
|
return engineResponses, sanitizederror.NewWithError(fmt.Sprintf("failed to get namespace labels for resource %s. use --values-file flag to pass the namespace labels", p.Resource.GetName()), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resPath := fmt.Sprintf("%s/%s/%s", c.Resource.GetNamespace(), c.Resource.GetKind(), c.Resource.GetName())
|
resPath := fmt.Sprintf("%s/%s/%s", p.Resource.GetNamespace(), p.Resource.GetKind(), p.Resource.GetName())
|
||||||
log.Log.V(3).Info("applying policy on resource", "policy", c.Policy.GetName(), "resource", resPath)
|
log.Log.V(3).Info("applying policy on resource", "policy", p.Policy.GetName(), "resource", resPath)
|
||||||
|
|
||||||
resourceRaw, err := c.Resource.MarshalJSON()
|
resourceRaw, err := p.Resource.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error(err, "failed to marshal resource")
|
log.Log.Error(err, "failed to marshal resource")
|
||||||
}
|
}
|
||||||
|
@ -96,8 +120,8 @@ OuterLoop:
|
||||||
cfg := config.NewDefaultConfiguration(false)
|
cfg := config.NewDefaultConfiguration(false)
|
||||||
gvk, subresource := updatedResource.GroupVersionKind(), ""
|
gvk, subresource := updatedResource.GroupVersionKind(), ""
|
||||||
// If --cluster flag is not set, then we need to find the top level resource GVK and subresource
|
// If --cluster flag is not set, then we need to find the top level resource GVK and subresource
|
||||||
if c.Client == nil {
|
if p.Client == nil {
|
||||||
for _, s := range c.Subresources {
|
for _, s := range p.Subresources {
|
||||||
subgvk := schema.GroupVersionKind{
|
subgvk := schema.GroupVersionKind{
|
||||||
Group: s.APIResource.Group,
|
Group: s.APIResource.Group,
|
||||||
Version: s.APIResource.Version,
|
Version: s.APIResource.Version,
|
||||||
|
@ -115,8 +139,8 @@ OuterLoop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var client engineapi.Client
|
var client engineapi.Client
|
||||||
if c.Client != nil {
|
if p.Client != nil {
|
||||||
client = adapters.Client(c.Client)
|
client = adapters.Client(p.Client)
|
||||||
}
|
}
|
||||||
rclient := registryclient.NewOrDie()
|
rclient := registryclient.NewOrDie()
|
||||||
eng := engine.NewEngine(
|
eng := engine.NewEngine(
|
||||||
|
@ -134,7 +158,7 @@ OuterLoop:
|
||||||
jp,
|
jp,
|
||||||
*updatedResource,
|
*updatedResource,
|
||||||
operation,
|
operation,
|
||||||
c.UserInfo,
|
p.UserInfo,
|
||||||
cfg,
|
cfg,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -142,11 +166,11 @@ OuterLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
policyContext = policyContext.
|
policyContext = policyContext.
|
||||||
WithPolicy(c.Policy).
|
WithPolicy(p.Policy).
|
||||||
WithNamespaceLabels(namespaceLabels).
|
WithNamespaceLabels(namespaceLabels).
|
||||||
WithResourceKind(gvk, subresource)
|
WithResourceKind(gvk, subresource)
|
||||||
|
|
||||||
for key, value := range c.Variables {
|
for key, value := range p.Variables {
|
||||||
err = policyContext.JSONContext().AddVariable(key, value)
|
err = policyContext.JSONContext().AddVariable(key, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error(err, "failed to add variable to context")
|
log.Log.Error(err, "failed to add variable to context")
|
||||||
|
@ -157,7 +181,7 @@ OuterLoop:
|
||||||
combineRuleResponses(mutateResponse)
|
combineRuleResponses(mutateResponse)
|
||||||
engineResponses = append(engineResponses, mutateResponse)
|
engineResponses = append(engineResponses, mutateResponse)
|
||||||
|
|
||||||
err = processMutateEngineResponse(c, &mutateResponse, resPath)
|
err = p.processMutateEngineResponse(mutateResponse, resPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !sanitizederror.IsErrorSanitized(err) {
|
if !sanitizederror.IsErrorSanitized(err) {
|
||||||
return engineResponses, sanitizederror.NewWithError("failed to print mutated result", err)
|
return engineResponses, sanitizederror.NewWithError("failed to print mutated result", err)
|
||||||
|
@ -171,7 +195,7 @@ OuterLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
var policyHasValidate bool
|
var policyHasValidate bool
|
||||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
for _, rule := range autogen.ComputeRules(p.Policy) {
|
||||||
if rule.HasValidate() || rule.HasVerifyImageChecks() {
|
if rule.HasValidate() || rule.HasVerifyImageChecks() {
|
||||||
policyHasValidate = true
|
policyHasValidate = true
|
||||||
}
|
}
|
||||||
|
@ -190,7 +214,7 @@ OuterLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
var policyHasGenerate bool
|
var policyHasGenerate bool
|
||||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
for _, rule := range autogen.ComputeRules(p.Policy) {
|
||||||
if rule.HasGenerate() {
|
if rule.HasGenerate() {
|
||||||
policyHasGenerate = true
|
policyHasGenerate = true
|
||||||
}
|
}
|
||||||
|
@ -199,7 +223,7 @@ OuterLoop:
|
||||||
if policyHasGenerate {
|
if policyHasGenerate {
|
||||||
generateResponse := eng.ApplyBackgroundChecks(context.TODO(), policyContext)
|
generateResponse := eng.ApplyBackgroundChecks(context.TODO(), policyContext)
|
||||||
if !generateResponse.IsEmpty() {
|
if !generateResponse.IsEmpty() {
|
||||||
newRuleResponse, err := handleGeneratePolicy(&generateResponse, *policyContext, c.RuleToCloneSourceResource)
|
newRuleResponse, err := handleGeneratePolicy(&generateResponse, *policyContext, p.RuleToCloneSourceResource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Log.Error(err, "failed to apply generate policy")
|
log.Log.Error(err, "failed to apply generate policy")
|
||||||
} else {
|
} else {
|
||||||
|
@ -208,82 +232,67 @@ OuterLoop:
|
||||||
combineRuleResponses(generateResponse)
|
combineRuleResponses(generateResponse)
|
||||||
engineResponses = append(engineResponses, generateResponse)
|
engineResponses = append(engineResponses, generateResponse)
|
||||||
}
|
}
|
||||||
updateResultCounts(c.Policy, &generateResponse, resPath, c.Rc, c.AuditWarn)
|
p.Rc.addGenerateResponse(p.AuditWarn, resPath, generateResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
processEngineResponses(engineResponses, c)
|
p.Rc.addEngineResponses(p.AuditWarn, engineResponses...)
|
||||||
|
|
||||||
return engineResponses, nil
|
return engineResponses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func combineRuleResponses(imageResponse engineapi.EngineResponse) engineapi.EngineResponse {
|
func (p *PolicyProcessor) processMutateEngineResponse(response engineapi.EngineResponse, resourcePath string) error {
|
||||||
if imageResponse.PolicyResponse.RulesAppliedCount() == 0 {
|
printMutatedRes := p.Rc.addMutateResponse(resourcePath, response)
|
||||||
return imageResponse
|
if printMutatedRes && p.PrintPatchResource {
|
||||||
|
yamlEncodedResource, err := yamlv2.Marshal(response.PatchedResource.Object)
|
||||||
|
if err != nil {
|
||||||
|
return sanitizederror.NewWithError("failed to marshal", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.MutateLogPath == "" {
|
||||||
|
mutatedResource := string(yamlEncodedResource) + string("\n---")
|
||||||
|
if len(strings.TrimSpace(mutatedResource)) > 0 {
|
||||||
|
if !p.Stdin {
|
||||||
|
fmt.Printf("\nmutate policy %s applied to %s:", p.Policy.GetName(), resourcePath)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n" + mutatedResource + "\n")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := p.printMutatedOutput(string(yamlEncodedResource))
|
||||||
|
if err != nil {
|
||||||
|
return sanitizederror.NewWithError("failed to print mutated result", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n\nMutation:\nMutation has been applied successfully. Check the files.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
completeRuleResponses := imageResponse.PolicyResponse.Rules
|
}
|
||||||
var combineRuleResponses []engineapi.RuleResponse
|
|
||||||
|
func (p *PolicyProcessor) printMutatedOutput(yaml string) error {
|
||||||
ruleNameType := make(map[string][]engineapi.RuleResponse)
|
var file *os.File
|
||||||
for _, rsp := range completeRuleResponses {
|
mutateLogPath := filepath.Clean(p.MutateLogPath)
|
||||||
key := rsp.Name() + ";" + string(rsp.RuleType())
|
filename := p.Resource.GetName() + "-mutated"
|
||||||
ruleNameType[key] = append(ruleNameType[key], rsp)
|
if !p.MutateLogPathIsDir {
|
||||||
}
|
// truncation for the case when mutateLogPath is a file (not a directory) is handled under pkg/kyverno/apply/test_command.go
|
||||||
|
f, err := os.OpenFile(mutateLogPath, os.O_APPEND|os.O_WRONLY, 0o600) // #nosec G304
|
||||||
for key, ruleResponses := range ruleNameType {
|
if err != nil {
|
||||||
tokens := strings.Split(key, ";")
|
return err
|
||||||
ruleName := tokens[0]
|
}
|
||||||
ruleType := tokens[1]
|
file = f
|
||||||
var failRuleResponses []engineapi.RuleResponse
|
} else {
|
||||||
var errorRuleResponses []engineapi.RuleResponse
|
f, err := os.OpenFile(filepath.Join(mutateLogPath, filename+".yaml"), os.O_CREATE|os.O_WRONLY, 0o600) // #nosec G304
|
||||||
var passRuleResponses []engineapi.RuleResponse
|
if err != nil {
|
||||||
var skipRuleResponses []engineapi.RuleResponse
|
return err
|
||||||
|
}
|
||||||
ruleMesssage := ""
|
file = f
|
||||||
for _, rsp := range ruleResponses {
|
}
|
||||||
if rsp.Status() == engineapi.RuleStatusFail {
|
if _, err := file.Write([]byte(yaml + "\n---\n\n")); err != nil {
|
||||||
failRuleResponses = append(failRuleResponses, rsp)
|
if err := file.Close(); err != nil {
|
||||||
} else if rsp.Status() == engineapi.RuleStatusError {
|
log.Log.Error(err, "failed to close file")
|
||||||
errorRuleResponses = append(errorRuleResponses, rsp)
|
}
|
||||||
} else if rsp.Status() == engineapi.RuleStatusPass {
|
return err
|
||||||
passRuleResponses = append(passRuleResponses, rsp)
|
}
|
||||||
} else if rsp.Status() == engineapi.RuleStatusSkip {
|
if err := file.Close(); err != nil {
|
||||||
skipRuleResponses = append(skipRuleResponses, rsp)
|
return err
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
if len(errorRuleResponses) > 0 {
|
|
||||||
for _, errRsp := range errorRuleResponses {
|
|
||||||
ruleMesssage += errRsp.Message() + ";"
|
|
||||||
}
|
|
||||||
errorResponse := engineapi.NewRuleResponse(ruleName, engineapi.RuleType(ruleType), ruleMesssage, engineapi.RuleStatusError)
|
|
||||||
combineRuleResponses = append(combineRuleResponses, *errorResponse)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(failRuleResponses) > 0 {
|
|
||||||
for _, failRsp := range failRuleResponses {
|
|
||||||
ruleMesssage += failRsp.Message() + ";"
|
|
||||||
}
|
|
||||||
failResponse := engineapi.NewRuleResponse(ruleName, engineapi.RuleType(ruleType), ruleMesssage, engineapi.RuleStatusFail)
|
|
||||||
combineRuleResponses = append(combineRuleResponses, *failResponse)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(passRuleResponses) > 0 {
|
|
||||||
for _, passRsp := range passRuleResponses {
|
|
||||||
ruleMesssage += passRsp.Message() + ";"
|
|
||||||
}
|
|
||||||
passResponse := engineapi.NewRuleResponse(ruleName, engineapi.RuleType(ruleType), ruleMesssage, engineapi.RuleStatusPass)
|
|
||||||
combineRuleResponses = append(combineRuleResponses, *passResponse)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, skipRsp := range skipRuleResponses {
|
|
||||||
ruleMesssage += skipRsp.Message() + ";"
|
|
||||||
}
|
|
||||||
skipResponse := engineapi.NewRuleResponse(ruleName, engineapi.RuleType(ruleType), ruleMesssage, engineapi.RuleStatusSkip)
|
|
||||||
combineRuleResponses = append(combineRuleResponses, *skipResponse)
|
|
||||||
}
|
|
||||||
imageResponse.PolicyResponse.Rules = combineRuleResponses
|
|
||||||
return imageResponse
|
|
||||||
}
|
}
|
171
cmd/cli/kubectl-kyverno/processor/result.go
Normal file
171
cmd/cli/kubectl-kyverno/processor/result.go
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy/annotations"
|
||||||
|
"github.com/kyverno/kyverno/pkg/autogen"
|
||||||
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
|
"k8s.io/api/admissionregistration/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResultCounts struct {
|
||||||
|
pass int
|
||||||
|
fail int
|
||||||
|
warn int
|
||||||
|
err int
|
||||||
|
skip int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc ResultCounts) Pass() int { return rc.pass }
|
||||||
|
func (rc ResultCounts) Fail() int { return rc.fail }
|
||||||
|
func (rc ResultCounts) Warn() int { return rc.warn }
|
||||||
|
func (rc ResultCounts) Error() int { return rc.err }
|
||||||
|
func (rc ResultCounts) Skip() int { return rc.skip }
|
||||||
|
|
||||||
|
func (rc *ResultCounts) addEngineResponses(auditWarn bool, responses ...engineapi.EngineResponse) {
|
||||||
|
for _, response := range responses {
|
||||||
|
rc.addEngineResponse(auditWarn, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *ResultCounts) addEngineResponse(auditWarn bool, response engineapi.EngineResponse) {
|
||||||
|
if !response.IsEmpty() {
|
||||||
|
genericPolicy := response.Policy()
|
||||||
|
if polType := genericPolicy.GetType(); polType == engineapi.ValidatingAdmissionPolicyType {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
policy := genericPolicy.GetPolicy().(kyvernov1.PolicyInterface)
|
||||||
|
scored := annotations.Scored(policy.GetAnnotations())
|
||||||
|
for _, rule := range autogen.ComputeRules(policy) {
|
||||||
|
if rule.HasValidate() || rule.HasVerifyImageChecks() || rule.HasVerifyImages() {
|
||||||
|
ruleFoundInEngineResponse := false
|
||||||
|
for _, valResponseRule := range response.PolicyResponse.Rules {
|
||||||
|
if rule.Name == valResponseRule.Name() {
|
||||||
|
ruleFoundInEngineResponse = true
|
||||||
|
switch valResponseRule.Status() {
|
||||||
|
case engineapi.RuleStatusPass:
|
||||||
|
rc.pass++
|
||||||
|
case engineapi.RuleStatusFail:
|
||||||
|
if !scored {
|
||||||
|
rc.warn++
|
||||||
|
break
|
||||||
|
} else if auditWarn && response.GetValidationFailureAction().Audit() {
|
||||||
|
rc.warn++
|
||||||
|
} else {
|
||||||
|
rc.fail++
|
||||||
|
}
|
||||||
|
case engineapi.RuleStatusError:
|
||||||
|
rc.err++
|
||||||
|
case engineapi.RuleStatusWarn:
|
||||||
|
rc.warn++
|
||||||
|
case engineapi.RuleStatusSkip:
|
||||||
|
rc.skip++
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ruleFoundInEngineResponse {
|
||||||
|
rc.skip++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *ResultCounts) addGenerateResponse(auditWarn bool, resPath string, response engineapi.EngineResponse) {
|
||||||
|
genericPolicy := response.Policy()
|
||||||
|
if polType := genericPolicy.GetType(); polType == engineapi.ValidatingAdmissionPolicyType {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
policy := genericPolicy.GetPolicy().(kyvernov1.PolicyInterface)
|
||||||
|
printCount := 0
|
||||||
|
for _, policyRule := range autogen.ComputeRules(policy) {
|
||||||
|
ruleFoundInEngineResponse := false
|
||||||
|
for i, ruleResponse := range response.PolicyResponse.Rules {
|
||||||
|
if policyRule.Name == ruleResponse.Name() {
|
||||||
|
ruleFoundInEngineResponse = true
|
||||||
|
if ruleResponse.Status() == engineapi.RuleStatusPass {
|
||||||
|
rc.pass++
|
||||||
|
} else {
|
||||||
|
if printCount < 1 {
|
||||||
|
fmt.Println("\ninvalid resource", "policy", policy.GetName(), "resource", resPath)
|
||||||
|
printCount++
|
||||||
|
}
|
||||||
|
fmt.Printf("%d. %s - %s\n", i+1, ruleResponse.Name(), ruleResponse.Message())
|
||||||
|
if auditWarn && response.GetValidationFailureAction().Audit() {
|
||||||
|
rc.warn++
|
||||||
|
} else {
|
||||||
|
rc.fail++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ruleFoundInEngineResponse {
|
||||||
|
rc.skip++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *ResultCounts) addMutateResponse(resourcePath string, response engineapi.EngineResponse) bool {
|
||||||
|
genericPolicy := response.Policy()
|
||||||
|
if polType := genericPolicy.GetType(); polType == engineapi.ValidatingAdmissionPolicyType {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
policy := genericPolicy.GetPolicy().(kyvernov1.PolicyInterface)
|
||||||
|
var policyHasMutate bool
|
||||||
|
for _, rule := range autogen.ComputeRules(policy) {
|
||||||
|
if rule.HasMutate() {
|
||||||
|
policyHasMutate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !policyHasMutate {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
printCount := 0
|
||||||
|
printMutatedRes := false
|
||||||
|
for _, policyRule := range autogen.ComputeRules(policy) {
|
||||||
|
ruleFoundInEngineResponse := false
|
||||||
|
for i, mutateResponseRule := range response.PolicyResponse.Rules {
|
||||||
|
if policyRule.Name == mutateResponseRule.Name() {
|
||||||
|
ruleFoundInEngineResponse = true
|
||||||
|
if mutateResponseRule.Status() == engineapi.RuleStatusPass {
|
||||||
|
rc.pass++
|
||||||
|
printMutatedRes = true
|
||||||
|
} else if mutateResponseRule.Status() == engineapi.RuleStatusSkip {
|
||||||
|
fmt.Printf("\nskipped mutate policy %s -> resource %s", policy.GetName(), resourcePath)
|
||||||
|
rc.skip++
|
||||||
|
} else if mutateResponseRule.Status() == engineapi.RuleStatusError {
|
||||||
|
fmt.Printf("\nerror while applying mutate policy %s -> resource %s\nerror: %s", policy.GetName(), resourcePath, mutateResponseRule.Message())
|
||||||
|
rc.err++
|
||||||
|
} else {
|
||||||
|
if printCount < 1 {
|
||||||
|
fmt.Printf("\nfailed to apply mutate policy %s -> resource %s", policy.GetName(), resourcePath)
|
||||||
|
printCount++
|
||||||
|
}
|
||||||
|
fmt.Printf("%d. %s - %s \n", i+1, mutateResponseRule.Name(), mutateResponseRule.Message())
|
||||||
|
rc.fail++
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ruleFoundInEngineResponse {
|
||||||
|
rc.skip++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return printMutatedRes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rc *ResultCounts) addValidatingAdmissionResponse(vap v1alpha1.ValidatingAdmissionPolicy, engineResponse engineapi.EngineResponse) {
|
||||||
|
for _, ruleResp := range engineResponse.PolicyResponse.Rules {
|
||||||
|
if ruleResp.Status() == engineapi.RuleStatusPass {
|
||||||
|
rc.pass++
|
||||||
|
} else if ruleResp.Status() == engineapi.RuleStatusFail {
|
||||||
|
rc.fail++
|
||||||
|
} else if ruleResp.Status() == engineapi.RuleStatusError {
|
||||||
|
rc.err++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
cmd/cli/kubectl-kyverno/processor/utils.go
Normal file
79
cmd/cli/kubectl-kyverno/processor/utils.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func combineRuleResponses(imageResponse engineapi.EngineResponse) engineapi.EngineResponse {
|
||||||
|
if imageResponse.PolicyResponse.RulesAppliedCount() == 0 {
|
||||||
|
return imageResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
completeRuleResponses := imageResponse.PolicyResponse.Rules
|
||||||
|
var combineRuleResponses []engineapi.RuleResponse
|
||||||
|
|
||||||
|
ruleNameType := make(map[string][]engineapi.RuleResponse)
|
||||||
|
for _, rsp := range completeRuleResponses {
|
||||||
|
key := rsp.Name() + ";" + string(rsp.RuleType())
|
||||||
|
ruleNameType[key] = append(ruleNameType[key], rsp)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, ruleResponses := range ruleNameType {
|
||||||
|
tokens := strings.Split(key, ";")
|
||||||
|
ruleName := tokens[0]
|
||||||
|
ruleType := tokens[1]
|
||||||
|
var failRuleResponses []engineapi.RuleResponse
|
||||||
|
var errorRuleResponses []engineapi.RuleResponse
|
||||||
|
var passRuleResponses []engineapi.RuleResponse
|
||||||
|
var skipRuleResponses []engineapi.RuleResponse
|
||||||
|
|
||||||
|
ruleMesssage := ""
|
||||||
|
for _, rsp := range ruleResponses {
|
||||||
|
if rsp.Status() == engineapi.RuleStatusFail {
|
||||||
|
failRuleResponses = append(failRuleResponses, rsp)
|
||||||
|
} else if rsp.Status() == engineapi.RuleStatusError {
|
||||||
|
errorRuleResponses = append(errorRuleResponses, rsp)
|
||||||
|
} else if rsp.Status() == engineapi.RuleStatusPass {
|
||||||
|
passRuleResponses = append(passRuleResponses, rsp)
|
||||||
|
} else if rsp.Status() == engineapi.RuleStatusSkip {
|
||||||
|
skipRuleResponses = append(skipRuleResponses, rsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errorRuleResponses) > 0 {
|
||||||
|
for _, errRsp := range errorRuleResponses {
|
||||||
|
ruleMesssage += errRsp.Message() + ";"
|
||||||
|
}
|
||||||
|
errorResponse := engineapi.NewRuleResponse(ruleName, engineapi.RuleType(ruleType), ruleMesssage, engineapi.RuleStatusError)
|
||||||
|
combineRuleResponses = append(combineRuleResponses, *errorResponse)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(failRuleResponses) > 0 {
|
||||||
|
for _, failRsp := range failRuleResponses {
|
||||||
|
ruleMesssage += failRsp.Message() + ";"
|
||||||
|
}
|
||||||
|
failResponse := engineapi.NewRuleResponse(ruleName, engineapi.RuleType(ruleType), ruleMesssage, engineapi.RuleStatusFail)
|
||||||
|
combineRuleResponses = append(combineRuleResponses, *failResponse)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(passRuleResponses) > 0 {
|
||||||
|
for _, passRsp := range passRuleResponses {
|
||||||
|
ruleMesssage += passRsp.Message() + ";"
|
||||||
|
}
|
||||||
|
passResponse := engineapi.NewRuleResponse(ruleName, engineapi.RuleType(ruleType), ruleMesssage, engineapi.RuleStatusPass)
|
||||||
|
combineRuleResponses = append(combineRuleResponses, *passResponse)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, skipRsp := range skipRuleResponses {
|
||||||
|
ruleMesssage += skipRsp.Message() + ";"
|
||||||
|
}
|
||||||
|
skipResponse := engineapi.NewRuleResponse(ruleName, engineapi.RuleType(ruleType), ruleMesssage, engineapi.RuleStatusSkip)
|
||||||
|
combineRuleResponses = append(combineRuleResponses, *skipResponse)
|
||||||
|
}
|
||||||
|
imageResponse.PolicyResponse.Rules = combineRuleResponses
|
||||||
|
return imageResponse
|
||||||
|
}
|
21
cmd/cli/kubectl-kyverno/processor/vap_processor.go
Normal file
21
cmd/cli/kubectl-kyverno/processor/vap_processor.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package processor
|
||||||
|
|
||||||
|
import (
|
||||||
|
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
||||||
|
"github.com/kyverno/kyverno/pkg/validatingadmissionpolicy"
|
||||||
|
"k8s.io/api/admissionregistration/v1alpha1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ValidatingAdmissionPolicyProcessor struct {
|
||||||
|
ValidatingAdmissionPolicy v1alpha1.ValidatingAdmissionPolicy
|
||||||
|
Resource *unstructured.Unstructured
|
||||||
|
PolicyReport bool
|
||||||
|
Rc *ResultCounts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ValidatingAdmissionPolicyProcessor) ApplyPolicyOnResource() ([]engineapi.EngineResponse, error) {
|
||||||
|
engineResp := validatingadmissionpolicy.Validate(p.ValidatingAdmissionPolicy, *p.Resource)
|
||||||
|
p.Rc.addValidatingAdmissionResponse(p.ValidatingAdmissionPolicy, engineResp)
|
||||||
|
return []engineapi.EngineResponse{engineResp}, nil
|
||||||
|
}
|
|
@ -9,59 +9,19 @@ import (
|
||||||
|
|
||||||
"github.com/go-git/go-billy/v5"
|
"github.com/go-git/go-billy/v5"
|
||||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||||
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
|
||||||
valuesapi "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/values"
|
valuesapi "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/values"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy/annotations"
|
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/source"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/source"
|
||||||
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
|
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"
|
"github.com/kyverno/kyverno/pkg/autogen"
|
||||||
"github.com/kyverno/kyverno/pkg/background/generate"
|
|
||||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
||||||
"github.com/kyverno/kyverno/pkg/config"
|
|
||||||
"github.com/kyverno/kyverno/pkg/engine"
|
|
||||||
"github.com/kyverno/kyverno/pkg/engine/adapters"
|
|
||||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
|
||||||
"github.com/kyverno/kyverno/pkg/engine/jmespath"
|
|
||||||
"github.com/kyverno/kyverno/pkg/imageverifycache"
|
|
||||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||||
yamlv2 "gopkg.in/yaml.v2"
|
|
||||||
"k8s.io/api/admissionregistration/v1alpha1"
|
"k8s.io/api/admissionregistration/v1alpha1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ResultCounts struct {
|
|
||||||
Pass int
|
|
||||||
Fail int
|
|
||||||
Warn int
|
|
||||||
Error int
|
|
||||||
Skip int
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApplyPolicyConfig struct {
|
|
||||||
Policy kyvernov1.PolicyInterface
|
|
||||||
ValidatingAdmissionPolicy v1alpha1.ValidatingAdmissionPolicy
|
|
||||||
Resource *unstructured.Unstructured
|
|
||||||
MutateLogPath string
|
|
||||||
MutateLogPathIsDir bool
|
|
||||||
Variables map[string]interface{}
|
|
||||||
UserInfo *kyvernov1beta1.RequestInfo
|
|
||||||
PolicyReport bool
|
|
||||||
NamespaceSelectorMap map[string]map[string]string
|
|
||||||
Stdin bool
|
|
||||||
Rc *ResultCounts
|
|
||||||
PrintPatchResource bool
|
|
||||||
RuleToCloneSourceResource map[string]string
|
|
||||||
Client dclient.Interface
|
|
||||||
AuditWarn bool
|
|
||||||
Subresources []valuesapi.Subresource
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveDuplicateAndObjectVariables - remove duplicate variables
|
// RemoveDuplicateAndObjectVariables - remove duplicate variables
|
||||||
func RemoveDuplicateAndObjectVariables(matches [][]string) string {
|
func RemoveDuplicateAndObjectVariables(matches [][]string) string {
|
||||||
var variableStr string
|
var variableStr string
|
||||||
|
@ -78,37 +38,6 @@ func RemoveDuplicateAndObjectVariables(matches [][]string) string {
|
||||||
return variableStr
|
return variableStr
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintMutatedOutput - function to print output in provided file or directory
|
|
||||||
func PrintMutatedOutput(mutateLogPath string, mutateLogPathIsDir bool, yaml string, fileName string) error {
|
|
||||||
var f *os.File
|
|
||||||
var err error
|
|
||||||
yaml = yaml + ("\n---\n\n")
|
|
||||||
|
|
||||||
mutateLogPath = filepath.Clean(mutateLogPath)
|
|
||||||
if !mutateLogPathIsDir {
|
|
||||||
// truncation for the case when mutateLogPath is a file (not a directory) is handled under pkg/kyverno/apply/test_command.go
|
|
||||||
f, err = os.OpenFile(mutateLogPath, os.O_APPEND|os.O_WRONLY, 0o600) // #nosec G304
|
|
||||||
} else {
|
|
||||||
f, err = os.OpenFile(mutateLogPath+"/"+fileName+".yaml", os.O_CREATE|os.O_WRONLY, 0o600) // #nosec G304
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := f.Write([]byte(yaml)); err != nil {
|
|
||||||
closeErr := f.Close()
|
|
||||||
if closeErr != nil {
|
|
||||||
log.Log.Error(closeErr, "failed to close file")
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := f.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetResourceAccordingToResourcePath - get resources according to the resource path
|
// GetResourceAccordingToResourcePath - get resources according to the resource path
|
||||||
func GetResourceAccordingToResourcePath(fs billy.Filesystem, resourcePaths []string,
|
func GetResourceAccordingToResourcePath(fs billy.Filesystem, resourcePaths []string,
|
||||||
cluster bool, policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, dClient dclient.Interface, namespace string, policyReport bool, isGit bool, policyResourcePath string,
|
cluster bool, policies []kyvernov1.PolicyInterface, validatingAdmissionPolicies []v1alpha1.ValidatingAdmissionPolicy, dClient dclient.Interface, namespace string, policyReport bool, isGit bool, policyResourcePath string,
|
||||||
|
@ -164,108 +93,6 @@ func GetResourceAccordingToResourcePath(fs billy.Filesystem, resourcePaths []str
|
||||||
return resources, err
|
return resources, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateResultCounts(policy kyvernov1.PolicyInterface, engineResponse *engineapi.EngineResponse, resPath string, rc *ResultCounts, auditWarn bool) {
|
|
||||||
printCount := 0
|
|
||||||
for _, policyRule := range autogen.ComputeRules(policy) {
|
|
||||||
ruleFoundInEngineResponse := false
|
|
||||||
for i, ruleResponse := range engineResponse.PolicyResponse.Rules {
|
|
||||||
if policyRule.Name == ruleResponse.Name() {
|
|
||||||
ruleFoundInEngineResponse = true
|
|
||||||
|
|
||||||
if ruleResponse.Status() == engineapi.RuleStatusPass {
|
|
||||||
rc.Pass++
|
|
||||||
} else {
|
|
||||||
if printCount < 1 {
|
|
||||||
fmt.Println("\ninvalid resource", "policy", policy.GetName(), "resource", resPath)
|
|
||||||
printCount++
|
|
||||||
}
|
|
||||||
fmt.Printf("%d. %s - %s\n", i+1, ruleResponse.Name(), ruleResponse.Message())
|
|
||||||
|
|
||||||
if auditWarn && engineResponse.GetValidationFailureAction().Audit() {
|
|
||||||
rc.Warn++
|
|
||||||
} else {
|
|
||||||
rc.Fail++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ruleFoundInEngineResponse {
|
|
||||||
rc.Skip++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func processMutateEngineResponse(c ApplyPolicyConfig, mutateResponse *engineapi.EngineResponse, resPath string) error {
|
|
||||||
var policyHasMutate bool
|
|
||||||
for _, rule := range autogen.ComputeRules(c.Policy) {
|
|
||||||
if rule.HasMutate() {
|
|
||||||
policyHasMutate = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !policyHasMutate {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
printCount := 0
|
|
||||||
printMutatedRes := false
|
|
||||||
for _, policyRule := range autogen.ComputeRules(c.Policy) {
|
|
||||||
ruleFoundInEngineResponse := false
|
|
||||||
for i, mutateResponseRule := range mutateResponse.PolicyResponse.Rules {
|
|
||||||
if policyRule.Name == mutateResponseRule.Name() {
|
|
||||||
ruleFoundInEngineResponse = true
|
|
||||||
if mutateResponseRule.Status() == engineapi.RuleStatusPass {
|
|
||||||
c.Rc.Pass++
|
|
||||||
printMutatedRes = true
|
|
||||||
} else if mutateResponseRule.Status() == engineapi.RuleStatusSkip {
|
|
||||||
fmt.Printf("\nskipped mutate policy %s -> resource %s", c.Policy.GetName(), resPath)
|
|
||||||
c.Rc.Skip++
|
|
||||||
} else if mutateResponseRule.Status() == engineapi.RuleStatusError {
|
|
||||||
fmt.Printf("\nerror while applying mutate policy %s -> resource %s\nerror: %s", c.Policy.GetName(), resPath, mutateResponseRule.Message())
|
|
||||||
c.Rc.Error++
|
|
||||||
} else {
|
|
||||||
if printCount < 1 {
|
|
||||||
fmt.Printf("\nfailed to apply mutate policy %s -> resource %s", c.Policy.GetName(), resPath)
|
|
||||||
printCount++
|
|
||||||
}
|
|
||||||
fmt.Printf("%d. %s - %s \n", i+1, mutateResponseRule.Name(), mutateResponseRule.Message())
|
|
||||||
c.Rc.Fail++
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ruleFoundInEngineResponse {
|
|
||||||
c.Rc.Skip++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if printMutatedRes && c.PrintPatchResource {
|
|
||||||
yamlEncodedResource, err := yamlv2.Marshal(mutateResponse.PatchedResource.Object)
|
|
||||||
if err != nil {
|
|
||||||
return sanitizederror.NewWithError("failed to marshal", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.MutateLogPath == "" {
|
|
||||||
mutatedResource := string(yamlEncodedResource) + string("\n---")
|
|
||||||
if len(strings.TrimSpace(mutatedResource)) > 0 {
|
|
||||||
if !c.Stdin {
|
|
||||||
fmt.Printf("\nmutate policy %s applied to %s:", c.Policy.GetName(), resPath)
|
|
||||||
}
|
|
||||||
fmt.Printf("\n" + mutatedResource + "\n")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := PrintMutatedOutput(c.MutateLogPath, c.MutateLogPathIsDir, string(yamlEncodedResource), c.Resource.GetName()+"-mutated")
|
|
||||||
if err != nil {
|
|
||||||
return sanitizederror.NewWithError("failed to print mutated result", err)
|
|
||||||
}
|
|
||||||
fmt.Printf("\n\nMutation:\nMutation has been applied successfully. Check the files.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetKindsFromPolicy(policy kyvernov1.PolicyInterface, subresources []valuesapi.Subresource, dClient dclient.Interface) map[string]struct{} {
|
func GetKindsFromPolicy(policy kyvernov1.PolicyInterface, subresources []valuesapi.Subresource, dClient dclient.Interface) map[string]struct{} {
|
||||||
kindOnwhichPolicyIsApplied := make(map[string]struct{})
|
kindOnwhichPolicyIsApplied := make(map[string]struct{})
|
||||||
for _, rule := range autogen.ComputeRules(policy) {
|
for _, rule := range autogen.ComputeRules(policy) {
|
||||||
|
@ -328,91 +155,6 @@ func getSubresourceKind(groupVersion, parentKind, subresourceName string, subres
|
||||||
return "", sanitizederror.NewWithError(fmt.Sprintf("subresource %s not found for parent resource %s", subresourceName, parentKind), nil)
|
return "", sanitizederror.NewWithError(fmt.Sprintf("subresource %s not found for parent resource %s", subresourceName, parentKind), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initializeMockController initializes a basic Generate Controller with a fake dynamic client.
|
|
||||||
func initializeMockController(objects []runtime.Object) (*generate.GenerateController, error) {
|
|
||||||
client, err := dclient.NewFakeClient(runtime.NewScheme(), nil, objects...)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to mock dynamic client")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
client.SetDiscovery(dclient.NewFakeDiscoveryClient(nil))
|
|
||||||
cfg := config.NewDefaultConfiguration(false)
|
|
||||||
c := generate.NewGenerateControllerWithOnlyClient(client, engine.NewEngine(
|
|
||||||
cfg,
|
|
||||||
config.NewDefaultMetricsConfiguration(),
|
|
||||||
jmespath.New(cfg),
|
|
||||||
adapters.Client(client),
|
|
||||||
nil,
|
|
||||||
imageverifycache.DisabledImageVerifyCache(),
|
|
||||||
store.ContextLoaderFactory(nil),
|
|
||||||
nil,
|
|
||||||
"",
|
|
||||||
))
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleGeneratePolicy returns a new RuleResponse with the Kyverno generated resource configuration by applying the generate rule.
|
|
||||||
func handleGeneratePolicy(generateResponse *engineapi.EngineResponse, policyContext engine.PolicyContext, ruleToCloneSourceResource map[string]string) ([]engineapi.RuleResponse, error) {
|
|
||||||
newResource := policyContext.NewResource()
|
|
||||||
objects := []runtime.Object{&newResource}
|
|
||||||
resources := []*unstructured.Unstructured{}
|
|
||||||
for _, rule := range generateResponse.PolicyResponse.Rules {
|
|
||||||
if path, ok := ruleToCloneSourceResource[rule.Name()]; ok {
|
|
||||||
resourceBytes, err := resource.GetFileBytes(path)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("failed to get resource bytes\n")
|
|
||||||
} else {
|
|
||||||
resources, err = resource.GetUnstructuredResources(resourceBytes)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("failed to convert resource bytes to unstructured format\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, res := range resources {
|
|
||||||
objects = append(objects, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := initializeMockController(objects)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("error at controller")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
gr := kyvernov1beta1.UpdateRequest{
|
|
||||||
Spec: kyvernov1beta1.UpdateRequestSpec{
|
|
||||||
Type: kyvernov1beta1.Generate,
|
|
||||||
Policy: generateResponse.Policy().GetName(),
|
|
||||||
Resource: kyvernov1.ResourceSpec{
|
|
||||||
Kind: generateResponse.Resource.GetKind(),
|
|
||||||
Namespace: generateResponse.Resource.GetNamespace(),
|
|
||||||
Name: generateResponse.Resource.GetName(),
|
|
||||||
APIVersion: generateResponse.Resource.GetAPIVersion(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var newRuleResponse []engineapi.RuleResponse
|
|
||||||
|
|
||||||
for _, rule := range generateResponse.PolicyResponse.Rules {
|
|
||||||
genResource, err := c.ApplyGeneratePolicy(log.Log.V(2), &policyContext, gr, []string{rule.Name()})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if genResource != nil {
|
|
||||||
unstrGenResource, err := c.GetUnstrResource(genResource[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
newRuleResponse = append(newRuleResponse, *rule.WithGeneratedResource(*unstrGenResource))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newRuleResponse, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetGitBranchOrPolicyPaths(gitBranch, repoURL string, policyPaths ...string) (string, string) {
|
func GetGitBranchOrPolicyPaths(gitBranch, repoURL string, policyPaths ...string) (string, string) {
|
||||||
var gitPathToYamls string
|
var gitPathToYamls string
|
||||||
if gitBranch == "" {
|
if gitBranch == "" {
|
||||||
|
@ -436,48 +178,3 @@ func GetGitBranchOrPolicyPaths(gitBranch, repoURL string, policyPaths ...string)
|
||||||
}
|
}
|
||||||
return gitBranch, gitPathToYamls
|
return gitBranch, gitPathToYamls
|
||||||
}
|
}
|
||||||
|
|
||||||
func processEngineResponses(responses []engineapi.EngineResponse, c ApplyPolicyConfig) {
|
|
||||||
for _, response := range responses {
|
|
||||||
if !response.IsEmpty() {
|
|
||||||
pol := response.Policy()
|
|
||||||
if polType := pol.GetType(); polType == engineapi.ValidatingAdmissionPolicyType {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
scored := annotations.Scored(c.Policy.GetAnnotations())
|
|
||||||
for _, rule := range autogen.ComputeRules(pol.GetPolicy().(kyvernov1.PolicyInterface)) {
|
|
||||||
if rule.HasValidate() || rule.HasVerifyImageChecks() || rule.HasVerifyImages() {
|
|
||||||
ruleFoundInEngineResponse := false
|
|
||||||
for _, valResponseRule := range response.PolicyResponse.Rules {
|
|
||||||
if rule.Name == valResponseRule.Name() {
|
|
||||||
ruleFoundInEngineResponse = true
|
|
||||||
switch valResponseRule.Status() {
|
|
||||||
case engineapi.RuleStatusPass:
|
|
||||||
c.Rc.Pass++
|
|
||||||
case engineapi.RuleStatusFail:
|
|
||||||
if !scored {
|
|
||||||
c.Rc.Warn++
|
|
||||||
break
|
|
||||||
} else if c.AuditWarn && response.GetValidationFailureAction().Audit() {
|
|
||||||
c.Rc.Warn++
|
|
||||||
} else {
|
|
||||||
c.Rc.Fail++
|
|
||||||
}
|
|
||||||
case engineapi.RuleStatusError:
|
|
||||||
c.Rc.Error++
|
|
||||||
case engineapi.RuleStatusWarn:
|
|
||||||
c.Rc.Warn++
|
|
||||||
case engineapi.RuleStatusSkip:
|
|
||||||
c.Rc.Skip++
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !ruleFoundInEngineResponse {
|
|
||||||
c.Rc.Skip++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
valuesapi "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/values"
|
valuesapi "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/values"
|
||||||
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/processor"
|
||||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
|
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource"
|
||||||
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
|
@ -55,6 +56,13 @@ var policyNamespaceSelector = []byte(`{
|
||||||
`)
|
`)
|
||||||
|
|
||||||
func Test_NamespaceSelector(t *testing.T) {
|
func Test_NamespaceSelector(t *testing.T) {
|
||||||
|
type ResultCounts struct {
|
||||||
|
pass int
|
||||||
|
fail int
|
||||||
|
warn int
|
||||||
|
err int
|
||||||
|
skip int
|
||||||
|
}
|
||||||
type TestCase struct {
|
type TestCase struct {
|
||||||
policy []byte
|
policy []byte
|
||||||
resource []byte
|
resource []byte
|
||||||
|
@ -72,11 +80,11 @@ func Test_NamespaceSelector(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: ResultCounts{
|
result: ResultCounts{
|
||||||
Pass: 0,
|
pass: 0,
|
||||||
Fail: 1,
|
fail: 1,
|
||||||
Warn: 0,
|
warn: 0,
|
||||||
Error: 0,
|
err: 0,
|
||||||
Skip: 2,
|
skip: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -88,19 +96,19 @@ func Test_NamespaceSelector(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
result: ResultCounts{
|
result: ResultCounts{
|
||||||
Pass: 1,
|
pass: 1,
|
||||||
Fail: 1,
|
fail: 1,
|
||||||
Warn: 0,
|
warn: 0,
|
||||||
Error: 0,
|
err: 0,
|
||||||
Skip: 4,
|
skip: 4,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
rc := &ResultCounts{}
|
rc := &processor.ResultCounts{}
|
||||||
for _, tc := range testcases {
|
for _, tc := range testcases {
|
||||||
policyArray, _, _ := yamlutils.GetPolicy(tc.policy)
|
policyArray, _, _ := yamlutils.GetPolicy(tc.policy)
|
||||||
resourceArray, _ := resource.GetUnstructuredResources(tc.resource)
|
resourceArray, _ := resource.GetUnstructuredResources(tc.resource)
|
||||||
applyPolicyConfig := ApplyPolicyConfig{
|
processor := processor.PolicyProcessor{
|
||||||
Policy: policyArray[0],
|
Policy: policyArray[0],
|
||||||
Resource: resourceArray[0],
|
Resource: resourceArray[0],
|
||||||
MutateLogPath: "",
|
MutateLogPath: "",
|
||||||
|
@ -108,13 +116,13 @@ func Test_NamespaceSelector(t *testing.T) {
|
||||||
NamespaceSelectorMap: tc.namespaceSelectorMap,
|
NamespaceSelectorMap: tc.namespaceSelectorMap,
|
||||||
Rc: rc,
|
Rc: rc,
|
||||||
}
|
}
|
||||||
ApplyPolicyOnResource(applyPolicyConfig)
|
processor.ApplyPolicyOnResource()
|
||||||
assert.Equal(t, int64(rc.Pass), int64(tc.result.Pass))
|
assert.Equal(t, int64(rc.Pass()), int64(tc.result.pass))
|
||||||
assert.Equal(t, int64(rc.Fail), int64(tc.result.Fail))
|
assert.Equal(t, int64(rc.Fail()), int64(tc.result.fail))
|
||||||
// TODO: autogen rules seem to not be present when autogen internals is disabled
|
// TODO: autogen rules seem to not be present when autogen internals is disabled
|
||||||
assert.Equal(t, int64(rc.Skip), int64(tc.result.Skip))
|
assert.Equal(t, int64(rc.Skip()), int64(tc.result.skip))
|
||||||
assert.Equal(t, int64(rc.Warn), int64(tc.result.Warn))
|
assert.Equal(t, int64(rc.Warn()), int64(tc.result.warn))
|
||||||
assert.Equal(t, int64(rc.Error), int64(tc.result.Error))
|
assert.Equal(t, int64(rc.Error()), int64(tc.result.err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PolicyInterface interface {
|
|
||||||
ApplyPolicyOnResource(c ApplyPolicyConfig) ([]engineapi.EngineResponse, error)
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kyverno/kyverno/pkg/clients/dclient"
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ResourceInterface abstracts the matched resources by either Kyverno Policies or Validating Admission Policies
|
|
||||||
type ResourceInterface interface {
|
|
||||||
FetchResourcesFromPolicy(resourcePaths []string, dClient dclient.Interface, namespace string, policyReport bool) ([]*unstructured.Unstructured, error)
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
|
|
||||||
"github.com/kyverno/kyverno/pkg/validatingadmissionpolicy"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ValidatingAdmissionPolicies struct{}
|
|
||||||
|
|
||||||
func (p *ValidatingAdmissionPolicies) ApplyPolicyOnResource(c ApplyPolicyConfig) ([]engineapi.EngineResponse, error) {
|
|
||||||
engineResp := validatingadmissionpolicy.Validate(c.ValidatingAdmissionPolicy, *c.Resource)
|
|
||||||
ruleResp := engineResp.PolicyResponse.Rules[0]
|
|
||||||
|
|
||||||
if ruleResp.Status() == engineapi.RuleStatusPass {
|
|
||||||
c.Rc.Pass++
|
|
||||||
} else if ruleResp.Status() == engineapi.RuleStatusFail {
|
|
||||||
c.Rc.Fail++
|
|
||||||
} else if ruleResp.Status() == engineapi.RuleStatusError {
|
|
||||||
c.Rc.Error++
|
|
||||||
}
|
|
||||||
|
|
||||||
return []engineapi.EngineResponse{engineResp}, nil
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue