Skip to content

Commit

Permalink
Support PolicyExceptions with CLI (kyverno#9525)
Browse files Browse the repository at this point in the history
* 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 f53b205.

* Revert "Added support for PolicyExceptions for apply command "

This reverts commit 82689ea.

* 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>
  • Loading branch information
3 people authored Jan 31, 2024
1 parent 273a0a5 commit 231e7a6
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 41 deletions.
3 changes: 3 additions & 0 deletions cmd/cli/kubectl-kyverno/apis/v1alpha1/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
49 changes: 31 additions & 18 deletions cmd/cli/kubectl-kyverno/commands/apply/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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...),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -171,6 +183,7 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul
variables,
policies,
resources,
exceptions,
&skipInvalidPolicies,
dClient,
userInfo,
Expand All @@ -189,15 +202,15 @@ 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
}
return nil, nil, skipInvalidPolicies, nil, err, mutateLogPathIsDir
}

func (c *ApplyCommandConfig) applyValidatingAdmissionPolicytoResource(
func (c *applyCommandConfig) applyValidatingAdmissionPolicytoResource(
vaps []v1alpha1.ValidatingAdmissionPolicy,
vapBindings []v1alpha1.ValidatingAdmissionPolicyBinding,
resources []*unstructured.Unstructured,
Expand All @@ -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,
Expand Down Expand Up @@ -262,6 +276,7 @@ func (c *ApplyCommandConfig) applyPolicytoResource(
Store: store,
Policies: validPolicies,
Resource: *resource,
PolicyExceptions: exceptions,
MutateLogPath: c.MutateLogPath,
MutateLogPathIsDir: mutateLogPathIsDir,
Variables: vars,
Expand All @@ -285,29 +300,27 @@ 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)
}
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
var vapBindings []v1alpha1.ValidatingAdmissionPolicyBinding

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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 != "" {
Expand All @@ -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")
}
Expand All @@ -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):")
Expand Down
34 changes: 17 additions & 17 deletions cmd/cli/kubectl-kyverno/commands/apply/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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"},
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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",
Expand All @@ -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",
Expand Down
Loading

0 comments on commit 231e7a6

Please sign in to comment.