1
0
Fork 0
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 commit f53b205c08.

* Revert "Added support for PolicyExceptions for apply command "

This reverts commit 82689ea0c1.

* 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:
Sanskar Gurdasani 2024-02-01 03:58:14 +05:30 committed by GitHub
parent 273a0a52f9
commit 231e7a681e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 130 additions and 41 deletions

View file

@ -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 {

View file

@ -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):")

View file

@ -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",

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View 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
}

View file

@ -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(), ""

View file

@ -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