mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 18:38:40 +00:00
Support PolicyExceptions with CLI (#9525)
* loding policyExecptions from func Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * adding PolicyExceptions in crds Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * adding PolicyExceptions in GetPolicy function Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * adding policyexceptions in Load function Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * resolve error becuase of now Getpolicy return policyexceptions Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * added -exception flag loaded policyexception Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * added policyexceptions in processor and NewEngine Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * Revert "added -exception flag loaded policyexception" This reverts commitf53b205c08
. * Revert "Added support for PolicyExceptions for apply command " This reverts commit82689ea0c1
. * Update cmd/cli/kubectl-kyverno/commands/test/test.go loading exceptions with policies Co-authored-by: Mariam Fahmy <mariamfahmy66@gmail.com> Signed-off-by: Sanskar Gurdasani <92817635+Sanskarzz@users.noreply.github.com> * updated GetFullPaths function and remove unnecessary code Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * added tests for loading exceptions in GetPolicy function Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * added tests for loading policy exceptions Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * Used selector in List function Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * generated cli crd Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * updated loadpolicy_test tests and corrected kind Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * resolved unit test error in path_test.go file Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * limiting the usage of exceptions to ValidatingAdmissionPolicies Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> * remove changes in common code Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * fixes Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> * codegen Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> --------- Signed-off-by: Sanskarzz <sanskar.gur@gmail.com> Signed-off-by: Sanskar Gurdasani <92817635+Sanskarzz@users.noreply.github.com> Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com> Co-authored-by: Mariam Fahmy <mariamfahmy66@gmail.com> Co-authored-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
parent
273a0a52f9
commit
231e7a681e
11 changed files with 130 additions and 41 deletions
|
@ -38,6 +38,9 @@ type Test struct {
|
|||
|
||||
// Values are the values to be used in the test
|
||||
Values *ValuesSpec `json:"values,omitempty"`
|
||||
|
||||
// Policy Exceptions are the policy exceptions to be used in the test
|
||||
PolicyExceptions []string `json:"exceptions,omitempty"`
|
||||
}
|
||||
|
||||
type CheckResult struct {
|
||||
|
|
|
@ -13,8 +13,10 @@ import (
|
|||
"github.com/go-git/go-billy/v5/memfs"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/command"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/deprecations"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/exception"
|
||||
"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/policy"
|
||||
|
@ -41,12 +43,12 @@ import (
|
|||
|
||||
const divider = "----------------------------------------------------------------------"
|
||||
|
||||
type SkippedInvalidPolicies struct {
|
||||
type skippedInvalidPolicies struct {
|
||||
skipped []string
|
||||
invalid []string
|
||||
}
|
||||
|
||||
type ApplyCommandConfig struct {
|
||||
type applyCommandConfig struct {
|
||||
KubeConfig string
|
||||
Context string
|
||||
Namespace string
|
||||
|
@ -64,11 +66,12 @@ type ApplyCommandConfig struct {
|
|||
GitBranch string
|
||||
warnExitCode int
|
||||
warnNoPassed bool
|
||||
exception []string
|
||||
}
|
||||
|
||||
func Command() *cobra.Command {
|
||||
var removeColor, detailedResults, table bool
|
||||
applyCommandConfig := &ApplyCommandConfig{}
|
||||
applyCommandConfig := &applyCommandConfig{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "apply",
|
||||
Short: command.FormatDescription(true, websiteUrl, false, description...),
|
||||
|
@ -114,10 +117,11 @@ func Command() *cobra.Command {
|
|||
cmd.Flags().BoolVar(&removeColor, "remove-color", false, "Remove any color from output")
|
||||
cmd.Flags().BoolVar(&detailedResults, "detailed-results", false, "If set to true, display detailed results")
|
||||
cmd.Flags().BoolVarP(&table, "table", "t", false, "Show results in table format")
|
||||
cmd.Flags().StringSliceVar(&applyCommandConfig.exception, "exception", nil, "Policy exception to be considered when evaluating policies against resources")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
||||
func (c *applyCommandConfig) applyCommandHelper(out io.Writer) (*processor.ResultCounts, []*unstructured.Unstructured, skippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
||||
rc, resources1, skipInvalidPolicies, responses1, err := c.checkArguments()
|
||||
if err != nil {
|
||||
return rc, resources1, skipInvalidPolicies, responses1, err
|
||||
|
@ -156,13 +160,21 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul
|
|||
if err != nil {
|
||||
return rc, resources1, skipInvalidPolicies, responses1, err
|
||||
}
|
||||
exceptions, err := exception.Load(c.exception...)
|
||||
if err != nil {
|
||||
return rc, resources1, skipInvalidPolicies, responses1, fmt.Errorf("Error: failed to load exceptions (%s)", err)
|
||||
}
|
||||
if !c.Stdin {
|
||||
var policyRulesCount int
|
||||
for _, policy := range policies {
|
||||
policyRulesCount += len(autogen.ComputeRules(policy))
|
||||
}
|
||||
policyRulesCount += len(vaps)
|
||||
fmt.Fprintf(out, "\nApplying %d policy rule(s) to %d resource(s)...\n", policyRulesCount, len(resources))
|
||||
if len(exceptions) > 0 {
|
||||
fmt.Fprintf(out, "\nApplying %d policy rule(s) to %d resource(s) with %d exception(s)...\n", policyRulesCount, len(resources), len(exceptions))
|
||||
} else {
|
||||
fmt.Fprintf(out, "\nApplying %d policy rule(s) to %d resource(s)...\n", policyRulesCount, len(resources))
|
||||
}
|
||||
}
|
||||
|
||||
rc, resources1, responses1, err = c.applyPolicytoResource(
|
||||
|
@ -171,6 +183,7 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul
|
|||
variables,
|
||||
policies,
|
||||
resources,
|
||||
exceptions,
|
||||
&skipInvalidPolicies,
|
||||
dClient,
|
||||
userInfo,
|
||||
|
@ -189,7 +202,7 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul
|
|||
return rc, resources1, skipInvalidPolicies, responses, nil
|
||||
}
|
||||
|
||||
func (c *ApplyCommandConfig) getMutateLogPathIsDir(skipInvalidPolicies SkippedInvalidPolicies) (*processor.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)
|
||||
if err != nil {
|
||||
return nil, nil, skipInvalidPolicies, nil, fmt.Errorf("failed to create file/folder (%w)", err), false
|
||||
|
@ -197,7 +210,7 @@ func (c *ApplyCommandConfig) getMutateLogPathIsDir(skipInvalidPolicies SkippedIn
|
|||
return nil, nil, skipInvalidPolicies, nil, err, mutateLogPathIsDir
|
||||
}
|
||||
|
||||
func (c *ApplyCommandConfig) applyValidatingAdmissionPolicytoResource(
|
||||
func (c *applyCommandConfig) applyValidatingAdmissionPolicytoResource(
|
||||
vaps []v1alpha1.ValidatingAdmissionPolicy,
|
||||
vapBindings []v1alpha1.ValidatingAdmissionPolicyBinding,
|
||||
resources []*unstructured.Unstructured,
|
||||
|
@ -223,13 +236,14 @@ func (c *ApplyCommandConfig) applyValidatingAdmissionPolicytoResource(
|
|||
return responses, nil
|
||||
}
|
||||
|
||||
func (c *ApplyCommandConfig) applyPolicytoResource(
|
||||
func (c *applyCommandConfig) applyPolicytoResource(
|
||||
out io.Writer,
|
||||
store *store.Store,
|
||||
vars *variables.Variables,
|
||||
policies []kyvernov1.PolicyInterface,
|
||||
resources []*unstructured.Unstructured,
|
||||
skipInvalidPolicies *SkippedInvalidPolicies,
|
||||
exceptions []*kyvernov2beta1.PolicyException,
|
||||
skipInvalidPolicies *skippedInvalidPolicies,
|
||||
dClient dclient.Interface,
|
||||
userInfo *v1beta1.RequestInfo,
|
||||
mutateLogPathIsDir bool,
|
||||
|
@ -262,6 +276,7 @@ func (c *ApplyCommandConfig) applyPolicytoResource(
|
|||
Store: store,
|
||||
Policies: validPolicies,
|
||||
Resource: *resource,
|
||||
PolicyExceptions: exceptions,
|
||||
MutateLogPath: c.MutateLogPath,
|
||||
MutateLogPathIsDir: mutateLogPathIsDir,
|
||||
Variables: vars,
|
||||
|
@ -285,7 +300,7 @@ func (c *ApplyCommandConfig) applyPolicytoResource(
|
|||
return &rc, resources, responses, nil
|
||||
}
|
||||
|
||||
func (c *ApplyCommandConfig) loadResources(out io.Writer, policies []kyvernov1.PolicyInterface, vap []v1alpha1.ValidatingAdmissionPolicy, dClient dclient.Interface) ([]*unstructured.Unstructured, error) {
|
||||
func (c *applyCommandConfig) loadResources(out io.Writer, policies []kyvernov1.PolicyInterface, vap []v1alpha1.ValidatingAdmissionPolicy, dClient dclient.Interface) ([]*unstructured.Unstructured, error) {
|
||||
resources, err := common.GetResourceAccordingToResourcePath(out, nil, c.ResourcePaths, c.Cluster, policies, vap, dClient, c.Namespace, c.PolicyReport, "")
|
||||
if err != nil {
|
||||
return resources, fmt.Errorf("failed to load resources (%w)", err)
|
||||
|
@ -293,7 +308,7 @@ func (c *ApplyCommandConfig) loadResources(out io.Writer, policies []kyvernov1.P
|
|||
return resources, nil
|
||||
}
|
||||
|
||||
func (c *ApplyCommandConfig) loadPolicies(skipInvalidPolicies SkippedInvalidPolicies) (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, []kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) {
|
||||
func (c *applyCommandConfig) loadPolicies(skipInvalidPolicies skippedInvalidPolicies) (*processor.ResultCounts, []*unstructured.Unstructured, skippedInvalidPolicies, []engineapi.EngineResponse, []kyvernov1.PolicyInterface, []v1alpha1.ValidatingAdmissionPolicy, []v1alpha1.ValidatingAdmissionPolicyBinding, error) {
|
||||
// load policies
|
||||
var policies []kyvernov1.PolicyInterface
|
||||
var vaps []v1alpha1.ValidatingAdmissionPolicy
|
||||
|
@ -301,13 +316,11 @@ func (c *ApplyCommandConfig) loadPolicies(skipInvalidPolicies SkippedInvalidPoli
|
|||
|
||||
for _, path := range c.PolicyPaths {
|
||||
isGit := source.IsGit(path)
|
||||
|
||||
if isGit {
|
||||
gitSourceURL, err := url.Parse(path)
|
||||
if err != nil {
|
||||
return nil, nil, skipInvalidPolicies, nil, nil, nil, nil, fmt.Errorf("failed to load policies (%w)", err)
|
||||
}
|
||||
|
||||
pathElems := strings.Split(gitSourceURL.Path[1:], "/")
|
||||
if len(pathElems) <= 1 {
|
||||
err := fmt.Errorf("invalid URL path %s - expected https://<any_git_source_domain>/:owner/:repository/:branch (without --git-branch flag) OR https://<any_git_source_domain>/:owner/:repository/:directory (with --git-branch flag)", gitSourceURL.Path)
|
||||
|
@ -349,7 +362,7 @@ func (c *ApplyCommandConfig) loadPolicies(skipInvalidPolicies SkippedInvalidPoli
|
|||
return nil, nil, skipInvalidPolicies, nil, policies, vaps, vapBindings, nil
|
||||
}
|
||||
|
||||
func (c *ApplyCommandConfig) initStoreAndClusterClient(store *store.Store, skipInvalidPolicies SkippedInvalidPolicies) (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error, dclient.Interface) {
|
||||
func (c *applyCommandConfig) initStoreAndClusterClient(store *store.Store, skipInvalidPolicies skippedInvalidPolicies) (*processor.ResultCounts, []*unstructured.Unstructured, skippedInvalidPolicies, []engineapi.EngineResponse, error, dclient.Interface) {
|
||||
store.SetLocal(true)
|
||||
store.SetRegistryAccess(c.RegistryAccess)
|
||||
if c.Cluster {
|
||||
|
@ -378,7 +391,7 @@ func (c *ApplyCommandConfig) initStoreAndClusterClient(store *store.Store, skipI
|
|||
return nil, nil, skipInvalidPolicies, nil, err, dClient
|
||||
}
|
||||
|
||||
func (c *ApplyCommandConfig) cleanPreviousContent(mutateLogPathIsDir bool, skipInvalidPolicies SkippedInvalidPolicies) (*processor.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)
|
||||
// the truncation of files for the case when mutateLogPath is dir, is handled under pkg/kyverno/apply/common.go
|
||||
if !mutateLogPathIsDir && c.MutateLogPath != "" {
|
||||
|
@ -392,8 +405,8 @@ func (c *ApplyCommandConfig) cleanPreviousContent(mutateLogPathIsDir bool, skipI
|
|||
return nil, nil, skipInvalidPolicies, nil, nil
|
||||
}
|
||||
|
||||
func (c *ApplyCommandConfig) checkArguments() (*processor.ResultCounts, []*unstructured.Unstructured, SkippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
||||
var skipInvalidPolicies SkippedInvalidPolicies
|
||||
func (c *applyCommandConfig) checkArguments() (*processor.ResultCounts, []*unstructured.Unstructured, skippedInvalidPolicies, []engineapi.EngineResponse, error) {
|
||||
var skipInvalidPolicies skippedInvalidPolicies
|
||||
if c.ValuesFile != "" && c.Variables != nil {
|
||||
return nil, nil, skipInvalidPolicies, nil, fmt.Errorf("pass the values either using set flag or values_file flag")
|
||||
}
|
||||
|
@ -409,7 +422,7 @@ func (c *ApplyCommandConfig) checkArguments() (*processor.ResultCounts, []*unstr
|
|||
return nil, nil, skipInvalidPolicies, nil, nil
|
||||
}
|
||||
|
||||
func printSkippedAndInvalidPolicies(out io.Writer, skipInvalidPolicies SkippedInvalidPolicies) {
|
||||
func printSkippedAndInvalidPolicies(out io.Writer, skipInvalidPolicies skippedInvalidPolicies) {
|
||||
if len(skipInvalidPolicies.skipped) > 0 {
|
||||
fmt.Fprintln(out, divider)
|
||||
fmt.Fprintln(out, "Policies Skipped (as required variables are not provided by the user):")
|
||||
|
|
|
@ -18,7 +18,7 @@ func Test_Apply(t *testing.T) {
|
|||
type TestCase struct {
|
||||
gitBranch string
|
||||
expectedPolicyReports []policyreportv1alpha2.PolicyReport
|
||||
config ApplyCommandConfig
|
||||
config applyCommandConfig
|
||||
stdinFile string
|
||||
}
|
||||
// copy disallow_latest_tag.yaml to local path
|
||||
|
@ -28,7 +28,7 @@ func Test_Apply(t *testing.T) {
|
|||
|
||||
testcases := []*TestCase{
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -44,7 +44,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{localFileName},
|
||||
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -60,7 +60,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -76,7 +76,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/cli/apply/policies"},
|
||||
ResourcePaths: []string{"../../../../../test/cli/apply/resource"},
|
||||
PolicyReport: true,
|
||||
|
@ -92,7 +92,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -109,7 +109,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"-"},
|
||||
ResourcePaths: []string{"../../../../../test/resources/pod_with_latest_tag.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -127,7 +127,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"-"},
|
||||
PolicyReport: true,
|
||||
|
@ -163,7 +163,7 @@ func Test_Apply(t *testing.T) {
|
|||
// }},
|
||||
// },
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/cli/apply/policies-set"},
|
||||
ResourcePaths: []string{"../../../../../test/cli/apply/resources-set"},
|
||||
Variables: []string{"request.operation=UPDATE"},
|
||||
|
@ -180,7 +180,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment1.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -196,7 +196,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployments-replica/deployment2.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -212,7 +212,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod1.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -228,7 +228,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/disallow-host-path/pod2.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -244,7 +244,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment1.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -260,7 +260,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/policy.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/cli/test-validating-admission-policy/check-deployment-labels/deployment2.yaml"},
|
||||
PolicyReport: true,
|
||||
|
@ -276,7 +276,7 @@ func Test_Apply(t *testing.T) {
|
|||
}},
|
||||
},
|
||||
{
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"https://github.com/kyverno/policies/best-practices/require-labels/", "../../../../../test/best_practices/disallow_latest_tag.yaml"},
|
||||
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
GitBranch: "main",
|
||||
|
@ -294,7 +294,7 @@ func Test_Apply(t *testing.T) {
|
|||
},
|
||||
{
|
||||
// Same as the above test case but the policy paths are reordered
|
||||
config: ApplyCommandConfig{
|
||||
config: applyCommandConfig{
|
||||
PolicyPaths: []string{"../../../../../test/best_practices/disallow_latest_tag.yaml", "https://github.com/kyverno/policies/best-practices/require-labels/"},
|
||||
ResourcePaths: []string{"../../../../../test/resources/pod_with_version_tag.yaml"},
|
||||
GitBranch: "main",
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/deprecations"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/exception"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/path"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/policy"
|
||||
|
@ -74,6 +75,17 @@ func runTest(out io.Writer, testCase test.TestCase, registryAccess bool, auditWa
|
|||
fmt.Fprintln(out, " Warning: found duplicated resource", dup.Kind, dup.Name, dup.Namespace)
|
||||
}
|
||||
}
|
||||
// exceptions
|
||||
fmt.Fprintln(out, " Loading exceptions", "...")
|
||||
exceptionFullPath := path.GetFullPaths(testCase.Test.PolicyExceptions, testDir, isGit)
|
||||
exceptions, err := exception.Load(exceptionFullPath...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error: failed to load exceptions (%s)", err)
|
||||
}
|
||||
// Validates that exceptions cannot be used with ValidatingAdmissionPolicies.
|
||||
if len(validatingAdmissionPolicies) > 0 && len(exceptions) > 0 {
|
||||
return nil, fmt.Errorf("Error: Currently, the use of exceptions in conjunction with ValidatingAdmissionPolicies is not supported.")
|
||||
}
|
||||
// init store
|
||||
var store store.Store
|
||||
store.SetLocal(true)
|
||||
|
@ -81,7 +93,11 @@ func runTest(out io.Writer, testCase test.TestCase, registryAccess bool, auditWa
|
|||
if vars != nil {
|
||||
vars.SetInStore(&store)
|
||||
}
|
||||
fmt.Fprintln(out, " Applying", len(policies)+len(validatingAdmissionPolicies), pluralize.Pluralize(len(policies)+len(validatingAdmissionPolicies), "policy", "policies"), "to", len(uniques), pluralize.Pluralize(len(uniques), "resource", "resources"), "...")
|
||||
if len(exceptions) > 0 {
|
||||
fmt.Fprintln(out, " Applying", len(policies)+len(validatingAdmissionPolicies), pluralize.Pluralize(len(policies)+len(validatingAdmissionPolicies), "policy", "policies"), "to", len(uniques), pluralize.Pluralize(len(uniques), "resource", "resources"), "with", len(exceptions), pluralize.Pluralize(len(exceptions), "exception", "exceptions"), "...")
|
||||
} else {
|
||||
fmt.Fprintln(out, " Applying", len(policies)+len(validatingAdmissionPolicies), pluralize.Pluralize(len(policies)+len(validatingAdmissionPolicies), "policy", "policies"), "to", len(uniques), pluralize.Pluralize(len(uniques), "resource", "resources"), "...")
|
||||
}
|
||||
// TODO document the code below
|
||||
ruleToCloneSourceResource := map[string]string{}
|
||||
for _, policy := range policies {
|
||||
|
@ -144,6 +160,7 @@ func runTest(out io.Writer, testCase test.TestCase, registryAccess bool, auditWa
|
|||
Store: &store,
|
||||
Policies: validPolicies,
|
||||
Resource: *resource,
|
||||
PolicyExceptions: exceptions,
|
||||
MutateLogPath: "",
|
||||
Variables: vars,
|
||||
UserInfo: userInfo,
|
||||
|
|
|
@ -24,6 +24,12 @@ spec:
|
|||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
exceptions:
|
||||
description: Policy Exceptions are the policy exceptions to be used in
|
||||
the test
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
|
|
|
@ -24,6 +24,12 @@ spec:
|
|||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
exceptions:
|
||||
description: Policy Exceptions are the policy exceptions to be used in
|
||||
the test
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
|
|
|
@ -2,6 +2,8 @@ package exception
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
|
||||
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
|
||||
|
@ -19,7 +21,23 @@ var (
|
|||
exceptionV2 = schema.GroupVersion(kyvernov2.GroupVersion).WithKind("PolicyException")
|
||||
)
|
||||
|
||||
func Load(content []byte) ([]*kyvernov2beta1.PolicyException, error) {
|
||||
func Load(paths ...string) ([]*kyvernov2beta1.PolicyException, error) {
|
||||
var out []*kyvernov2beta1.PolicyException
|
||||
for _, path := range paths {
|
||||
bytes, err := os.ReadFile(filepath.Clean(path))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read yaml (%w)", err)
|
||||
}
|
||||
exceptions, err := load(bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load exceptions (%w)", err)
|
||||
}
|
||||
out = append(out, exceptions...)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func load(content []byte) ([]*kyvernov2beta1.PolicyException, error) {
|
||||
documents, err := yamlutils.SplitDocuments(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_Load(t *testing.T) {
|
||||
func Test_load(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
policies string
|
||||
|
@ -31,7 +31,7 @@ func Test_Load(t *testing.T) {
|
|||
bytes, err := os.ReadFile(tt.policies)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
if res, err := Load(bytes); (err != nil) != tt.wantErr {
|
||||
if res, err := load(bytes); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Load() error = %v, wantErr %v", err, tt.wantErr)
|
||||
} else if len(res) != tt.wantLoaded {
|
||||
t.Errorf("Load() loaded amount = %v, wantLoaded %v", len(res), tt.wantLoaded)
|
||||
|
|
21
cmd/cli/kubectl-kyverno/processor/exceptions.go
Normal file
21
cmd/cli/kubectl-kyverno/processor/exceptions.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package processor
|
||||
|
||||
import (
|
||||
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
type policyExceptionLister struct {
|
||||
exceptions []*kyvernov2beta1.PolicyException
|
||||
}
|
||||
|
||||
func (l *policyExceptionLister) List(selector labels.Selector) ([]*kyvernov2beta1.PolicyException, error) {
|
||||
var out []*kyvernov2beta1.PolicyException
|
||||
for _, exception := range l.exceptions {
|
||||
exceptionLabels := labels.Set(exception.GetLabels())
|
||||
if selector.Matches(exceptionLabels) {
|
||||
out = append(out, exception)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
|
@ -11,6 +11,7 @@ import (
|
|||
json_patch "github.com/evanphx/json-patch/v5"
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
|
||||
kyvernov2beta1 "github.com/kyverno/kyverno/api/kyverno/v2beta1"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/apis/v1alpha1"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log"
|
||||
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/store"
|
||||
|
@ -38,6 +39,7 @@ type PolicyProcessor struct {
|
|||
Store *store.Store
|
||||
Policies []kyvernov1.PolicyInterface
|
||||
Resource unstructured.Unstructured
|
||||
PolicyExceptions []*kyvernov2beta1.PolicyException
|
||||
MutateLogPath string
|
||||
MutateLogPathIsDir bool
|
||||
Variables *variables.Variables
|
||||
|
@ -59,11 +61,13 @@ func (p *PolicyProcessor) ApplyPoliciesOnResource() ([]engineapi.EngineResponse,
|
|||
jp := jmespath.New(cfg)
|
||||
resource := p.Resource
|
||||
namespaceLabels := p.NamespaceSelectorMap[p.Resource.GetNamespace()]
|
||||
policyExceptionLister := &policyExceptionLister{
|
||||
exceptions: p.PolicyExceptions,
|
||||
}
|
||||
var client engineapi.Client
|
||||
if p.Client != nil {
|
||||
client = adapters.Client(p.Client)
|
||||
}
|
||||
|
||||
rclient := p.Store.GetRegistryClient()
|
||||
if rclient == nil {
|
||||
rclient = registryclient.NewOrDie()
|
||||
|
@ -76,7 +80,7 @@ func (p *PolicyProcessor) ApplyPoliciesOnResource() ([]engineapi.EngineResponse,
|
|||
factories.DefaultRegistryClientFactory(adapters.RegistryClient(rclient), nil),
|
||||
imageverifycache.DisabledImageVerifyCache(),
|
||||
store.ContextLoaderFactory(p.Store, nil),
|
||||
nil,
|
||||
policyExceptionLister,
|
||||
"",
|
||||
)
|
||||
gvk, subresource := resource.GroupVersionKind(), ""
|
||||
|
|
|
@ -41,6 +41,7 @@ kyverno apply [flags]
|
|||
-c, --cluster Checks if policies should be applied to cluster in the current context
|
||||
--context string The name of the kubeconfig context to use
|
||||
--detailed-results If set to true, display detailed results
|
||||
--exception strings Policy exception to be considered when evaluating policies against resources
|
||||
-b, --git-branch string test git repository branch
|
||||
-h, --help help for apply
|
||||
--kubeconfig string path to kubeconfig file with authorization and master location information
|
||||
|
|
Loading…
Add table
Reference in a new issue