1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

Added Code to support the test command for mutate policy (#2279)

* Added test-e2e-local in the Makefile
* Added a proper Indentation
* Added 3 more fields
* Added getPolicyResourceFullPath function
* Updating the patchedResource path to full path
* Converts Namespaced policy to ClusterPolicy
* Added GetPatchedResourceFromPath function
* Added GetPatchedResource function
* Checks for namespaced-policy from policy name provided bu user
* Generalizing resultKey for both validate and mutate. Also added kind field to this key
* Added Type field to PolicySpec
* To handle mutate case when resource and patchedResource are equal
* fetch patchResource from path provided by user and compare it with engine patchedResource
* generating result by comparing patchedResource
* Added kind to resultKey
* Handles namespaced policy results
* Skip is required
* Added []*response.EngineResponse return type in ApplyPolicyOnResource function
* namespaced policy only surpasses resources having same namespace as policy
* apply command will print the patchedResource whereas test will not
* passing engineResponse instead of validateEngineResponse because it supports results for both validate and mutate case
* default namespace will printed in the output table if no namespace is being provided by the user
* Added e2e test for mutate policy and also examples for both type of policies
* Created a separate function to get resultKey
* Changes in the resultKey for validate case
* Added help description for test command in the cli
* fixes code for more test cases
* fixes code to support more cases and also added resources for e2e-test
* some small changes like adding brackets, clubbing 2 if cond into one, changing variable name, etc.
* Rearrange GetPatchedResourceFromPath function to get rid from repetion of same thing twice.
* Added kind in the result section of test.yaml for all test-cases
* engineResponse will handle different types of response
* GetPatchedResource() uses GetResource function to fetch patched resource

Signed-off-by: viveksahu26 <vivekkumarsahu650@gmail.com>
This commit is contained in:
vivek kumar sahu 2021-10-01 14:16:33 +05:30 committed by NoSkillGirl
parent c32002837d
commit ae6f6c327f
32 changed files with 780 additions and 52 deletions

View file

@ -198,6 +198,7 @@ test-e2e-local:
#Test TestCmd Policy #Test TestCmd Policy
test-cmd: cli test-cmd: cli
$(PWD)/$(CLI_PATH)/kyverno test https://github.com/kyverno/policies/main $(PWD)/$(CLI_PATH)/kyverno test https://github.com/kyverno/policies/main
$(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test-mutate
$(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test $(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test
$(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test-fail/missing-policy && exit 1 || exit 0 $(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test-fail/missing-policy && exit 1 || exit 0
$(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test-fail/missing-rule && exit 1 || exit 0 $(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test-fail/missing-rule && exit 1 || exit 0

View file

@ -75,10 +75,10 @@ func filterRule(rule kyverno.Rule, policyContext *PolicyContext) *response.RuleR
logger := log.Log.WithName("Generate").WithValues("policy", policy.Name, logger := log.Log.WithName("Generate").WithValues("policy", policy.Name,
"kind", newResource.GetKind(), "namespace", newResource.GetNamespace(), "name", newResource.GetName()) "kind", newResource.GetKind(), "namespace", newResource.GetNamespace(), "name", newResource.GetName())
if err = MatchesResourceDescription(newResource, rule, admissionInfo, excludeGroupRole, namespaceLabels); err != nil { if err = MatchesResourceDescription(newResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, ""); err != nil {
// if the oldResource matched, return "false" to delete GR for it // if the oldResource matched, return "false" to delete GR for it
if err = MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels); err == nil { if err = MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, ""); err == nil {
return &response.RuleResponse{ return &response.RuleResponse{
Name: rule.Name, Name: rule.Name,
Type: "Generation", Type: "Generation",

View file

@ -65,7 +65,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
excludeResource = policyContext.ExcludeGroupRole excludeResource = policyContext.ExcludeGroupRole
} }
if err = MatchesResourceDescription(patchedResource, rule, policyContext.AdmissionInfo, excludeResource, policyContext.NamespaceLabels); err != nil { if err = MatchesResourceDescription(patchedResource, rule, policyContext.AdmissionInfo, excludeResource, policyContext.NamespaceLabels, policyContext.Policy.Namespace); err != nil {
logger.V(4).Info("rule not matched", "reason", err.Error()) logger.V(4).Info("rule not matched", "reason", err.Error())
continue continue
} }

View file

@ -256,13 +256,16 @@ func matchSubjects(ruleSubjects []rbacv1.Subject, userInfo authenticationv1.User
} }
//MatchesResourceDescription checks if the resource matches resource description of the rule or not //MatchesResourceDescription checks if the resource matches resource description of the rule or not
func MatchesResourceDescription(resourceRef unstructured.Unstructured, ruleRef kyverno.Rule, admissionInfoRef kyverno.RequestInfo, dynamicConfig []string, namespaceLabels map[string]string) error { func MatchesResourceDescription(resourceRef unstructured.Unstructured, ruleRef kyverno.Rule, admissionInfoRef kyverno.RequestInfo, dynamicConfig []string, namespaceLabels map[string]string, policyNamespace string) error {
rule := *ruleRef.DeepCopy() rule := *ruleRef.DeepCopy()
resource := *resourceRef.DeepCopy() resource := *resourceRef.DeepCopy()
admissionInfo := *admissionInfoRef.DeepCopy() admissionInfo := *admissionInfoRef.DeepCopy()
var reasonsForFailure []error var reasonsForFailure []error
if policyNamespace != "" && policyNamespace != resourceRef.GetNamespace() {
return errors.New(" The policy and resource namespace are different. Therefore, policy skip this resource.")
}
if len(rule.MatchResources.Any) > 0 { if len(rule.MatchResources.Any) > 0 {
// inlcude object if ANY of the criterias match // inlcude object if ANY of the criterias match
// so if one matches then break from loop // so if one matches then break from loop

View file

@ -898,7 +898,7 @@ func TestMatchesResourceDescription(t *testing.T) {
resource, _ := utils.ConvertToUnstructured(tc.Resource) resource, _ := utils.ConvertToUnstructured(tc.Resource)
for _, rule := range policy.Spec.Rules { for _, rule := range policy.Spec.Rules {
err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, []string{}, nil) err := MatchesResourceDescription(*resource, rule, tc.AdmissionInfo, []string{}, nil, "")
if err != nil { if err != nil {
if !tc.areErrorsExpected { if !tc.areErrorsExpected {
t.Errorf("Testcase %d Unexpected error: %v", i+1, err) t.Errorf("Testcase %d Unexpected error: %v", i+1, err)
@ -966,7 +966,7 @@ func TestResourceDescriptionMatch_MultipleKind(t *testing.T) {
} }
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}} rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil { if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
t.Errorf("Testcase has failed due to the following:%v", err) t.Errorf("Testcase has failed due to the following:%v", err)
} }
@ -1027,7 +1027,7 @@ func TestResourceDescriptionMatch_Name(t *testing.T) {
} }
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}} rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil { if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
t.Errorf("Testcase has failed due to the following:%v", err) t.Errorf("Testcase has failed due to the following:%v", err)
} }
} }
@ -1087,7 +1087,7 @@ func TestResourceDescriptionMatch_Name_Regex(t *testing.T) {
} }
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}} rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil { if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
t.Errorf("Testcase has failed due to the following:%v", err) t.Errorf("Testcase has failed due to the following:%v", err)
} }
} }
@ -1155,7 +1155,7 @@ func TestResourceDescriptionMatch_Label_Expression_NotMatch(t *testing.T) {
} }
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}} rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil { if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
t.Errorf("Testcase has failed due to the following:%v", err) t.Errorf("Testcase has failed due to the following:%v", err)
} }
} }
@ -1224,7 +1224,7 @@ func TestResourceDescriptionMatch_Label_Expression_Match(t *testing.T) {
} }
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}} rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}}
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err != nil { if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err != nil {
t.Errorf("Testcase has failed due to the following:%v", err) t.Errorf("Testcase has failed due to the following:%v", err)
} }
} }
@ -1304,7 +1304,7 @@ func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) {
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription}, rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription},
ExcludeResources: kyverno.ExcludeResources{ResourceDescription: resourceDescriptionExclude}} ExcludeResources: kyverno.ExcludeResources{ResourceDescription: resourceDescriptionExclude}}
if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil); err == nil { if err := MatchesResourceDescription(*resource, rule, kyverno.RequestInfo{}, []string{}, nil, ""); err == nil {
t.Errorf("Testcase has failed due to the following:\n Function has returned no error, even though it was supposed to fail") t.Errorf("Testcase has failed due to the following:\n Function has returned no error, even though it was supposed to fail")
} }
} }

View file

@ -198,13 +198,13 @@ func validateResourceWithRule(log logr.Logger, ctx *PolicyContext, rule kyverno.
// matches checks if either the new or old resource satisfies the filter conditions defined in the rule // matches checks if either the new or old resource satisfies the filter conditions defined in the rule
func matches(logger logr.Logger, rule kyverno.Rule, ctx *PolicyContext) bool { func matches(logger logr.Logger, rule kyverno.Rule, ctx *PolicyContext) bool {
err := MatchesResourceDescription(ctx.NewResource, rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels) err := MatchesResourceDescription(ctx.NewResource, rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels, "")
if err == nil { if err == nil {
return true return true
} }
if !reflect.DeepEqual(ctx.OldResource, unstructured.Unstructured{}) { if !reflect.DeepEqual(ctx.OldResource, unstructured.Unstructured{}) {
err := MatchesResourceDescription(ctx.OldResource, rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels) err := MatchesResourceDescription(ctx.OldResource, rule, ctx.AdmissionInfo, ctx.ExcludeGroupRole, ctx.NamespaceLabels, "")
if err == nil { if err == nil {
return true return true
} }

View file

@ -286,7 +286,7 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.Name, resource.GetName()), err) return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.Name, resource.GetName()), err)
} }
_, info, err := common.ApplyPolicyOnResource(policy, resource, mutateLogPath, mutateLogPathIsDir, thisPolicyResourceValues, policyReport, namespaceSelectorMap, stdin, rc) _, info, err := common.ApplyPolicyOnResource(policy, resource, mutateLogPath, mutateLogPathIsDir, thisPolicyResourceValues, policyReport, namespaceSelectorMap, stdin, rc, true)
if err != nil { if err != nil {
return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err) return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
} }

View file

@ -524,7 +524,7 @@ func MutatePolices(policies []*v1.ClusterPolicy) ([]*v1.ClusterPolicy, error) {
// ApplyPolicyOnResource - function to apply policy on resource // ApplyPolicyOnResource - function to apply policy on resource
func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unstructured, func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unstructured,
mutateLogPath string, mutateLogPathIsDir bool, variables map[string]string, policyReport bool, namespaceSelectorMap map[string]map[string]string, stdin bool, rc *ResultCounts) (*response.EngineResponse, policyreport.Info, error) { mutateLogPath string, mutateLogPathIsDir bool, variables map[string]string, policyReport bool, namespaceSelectorMap map[string]map[string]string, stdin bool, rc *ResultCounts, printPatchResource bool) (*response.EngineResponse, policyreport.Info, error) {
operationIsDelete := false operationIsDelete := false
@ -533,7 +533,7 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
} }
namespaceLabels := make(map[string]string) namespaceLabels := make(map[string]string)
var engineResponse *response.EngineResponse
policyWithNamespaceSelector := false policyWithNamespaceSelector := false
for _, p := range policy.Spec.Rules { for _, p := range policy.Spec.Rules {
if p.MatchResources.ResourceDescription.NamespaceSelector != nil || if p.MatchResources.ResourceDescription.NamespaceSelector != nil ||
@ -547,7 +547,7 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
resourceNamespace := resource.GetNamespace() resourceNamespace := resource.GetNamespace()
namespaceLabels = namespaceSelectorMap[resource.GetNamespace()] namespaceLabels = namespaceSelectorMap[resource.GetNamespace()]
if resourceNamespace != "default" && len(namespaceLabels) < 1 { if resourceNamespace != "default" && len(namespaceLabels) < 1 {
return &response.EngineResponse{}, policyreport.Info{}, sanitizederror.NewWithError(fmt.Sprintf("failed to get namesapce labels for resource %s. use --values-file flag to pass the namespace labels", resource.GetName()), nil) return engineResponse, policyreport.Info{}, sanitizederror.NewWithError(fmt.Sprintf("failed to get namesapce labels for resource %s. use --values-file flag to pass the namespace labels", resource.GetName()), nil)
} }
} }
@ -575,10 +575,14 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
} }
mutateResponse := engine.Mutate(&engine.PolicyContext{Policy: *policy, NewResource: *resource, JSONContext: ctx, NamespaceLabels: namespaceLabels}) mutateResponse := engine.Mutate(&engine.PolicyContext{Policy: *policy, NewResource: *resource, JSONContext: ctx, NamespaceLabels: namespaceLabels})
err = processMutateEngineResponse(policy, mutateResponse, resPath, rc, mutateLogPath, stdin, mutateLogPathIsDir, resource.GetName())
if mutateResponse != nil {
engineResponse = mutateResponse
}
err = processMutateEngineResponse(policy, mutateResponse, resPath, rc, mutateLogPath, stdin, mutateLogPathIsDir, resource.GetName(), printPatchResource)
if err != nil { if err != nil {
if !sanitizederror.IsErrorSanitized(err) { if !sanitizederror.IsErrorSanitized(err) {
return &response.EngineResponse{}, policyreport.Info{}, sanitizederror.NewWithError("failed to print mutated result", err) return engineResponse, policyreport.Info{}, sanitizederror.NewWithError("failed to print mutated result", err)
} }
} }
@ -604,6 +608,9 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
validateResponse = engine.Validate(policyCtx) validateResponse = engine.Validate(policyCtx)
info = ProcessValidateEngineResponse(policy, validateResponse, resPath, rc, policyReport) info = ProcessValidateEngineResponse(policy, validateResponse, resPath, rc, policyReport)
} }
if validateResponse != nil {
engineResponse = validateResponse
}
var policyHasGenerate bool var policyHasGenerate bool
for _, rule := range policy.Spec.Rules { for _, rule := range policy.Spec.Rules {
@ -624,10 +631,13 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
NamespaceLabels: namespaceLabels, NamespaceLabels: namespaceLabels,
} }
generateResponse := engine.Generate(policyContext) generateResponse := engine.Generate(policyContext)
if validateResponse != nil {
engineResponse = generateResponse
}
processGenerateEngineResponse(policy, generateResponse, resPath, rc) processGenerateEngineResponse(policy, generateResponse, resPath, rc)
} }
return validateResponse, info, nil return engineResponse, info, nil
} }
// PrintMutatedOutput - function to print output in provided file or directory // PrintMutatedOutput - function to print output in provided file or directory
@ -876,7 +886,7 @@ func SetInStoreContext(mutatedPolicies []*v1.ClusterPolicy, variables map[string
return variables return variables
} }
func processMutateEngineResponse(policy *v1.ClusterPolicy, mutateResponse *response.EngineResponse, resPath string, rc *ResultCounts, mutateLogPath string, stdin bool, mutateLogPathIsDir bool, resourceName string) error { func processMutateEngineResponse(policy *v1.ClusterPolicy, mutateResponse *response.EngineResponse, resPath string, rc *ResultCounts, mutateLogPath string, stdin bool, mutateLogPathIsDir bool, resourceName string, printPatchResource bool) error {
var policyHasMutate bool var policyHasMutate bool
for _, rule := range policy.Spec.Rules { for _, rule := range policy.Spec.Rules {
if rule.HasMutate() { if rule.HasMutate() {
@ -922,11 +932,12 @@ func processMutateEngineResponse(policy *v1.ClusterPolicy, mutateResponse *respo
if mutateLogPath == "" { if mutateLogPath == "" {
mutatedResource := string(yamlEncodedResource) + string("\n---") mutatedResource := string(yamlEncodedResource) + string("\n---")
if len(strings.TrimSpace(mutatedResource)) > 0 { if len(strings.TrimSpace(mutatedResource)) > 0 {
if !stdin { if !stdin && printPatchResource {
fmt.Printf("\nmutate policy %s applied to %s:", policy.Name, resPath) fmt.Printf("\nmutate policy %s applied to %s:", policy.Name, resPath)
} }
if printPatchResource {
fmt.Printf("\n" + mutatedResource) fmt.Printf("\n" + mutatedResource)
fmt.Printf("\n") }
} }
} else { } else {
err := PrintMutatedOutput(mutateLogPath, mutateLogPathIsDir, string(yamlEncodedResource), resourceName+"-mutated") err := PrintMutatedOutput(mutateLogPath, mutateLogPathIsDir, string(yamlEncodedResource), resourceName+"-mutated")
@ -993,3 +1004,31 @@ func GetKindsFromPolicy(policy *v1.ClusterPolicy) map[string]struct{} {
} }
return kindOnwhichPolicyIsApplied return kindOnwhichPolicyIsApplied
} }
//GetPatchedResourceFromPath - get patchedResource from given path
func GetPatchedResourceFromPath(fs billy.Filesystem, path string, isGit bool, policyResourcePath string) (unstructured.Unstructured, error) {
var patchedResourceBytes []byte
var patchedResource unstructured.Unstructured
var err error
if isGit {
if len(path) > 0 {
filep, err := fs.Open(filepath.Join(policyResourcePath, path))
if err != nil {
fmt.Printf("Unable to open patchedResource file: %s. \nerror: %s", path, err)
}
patchedResourceBytes, err = ioutil.ReadAll(filep)
}
} else {
patchedResourceBytes, err = getFileBytes(path)
}
if err != nil {
fmt.Printf("\n----------------------------------------------------------------------\nfailed to load patchedResource: %s. \nerror: %s\n----------------------------------------------------------------------\n", path, err)
return patchedResource, err
}
patchedResource, err = GetPatchedResource(patchedResourceBytes)
if err != nil {
return patchedResource, err
}
return patchedResource, nil
}

View file

@ -98,7 +98,7 @@ func Test_NamespaceSelector(t *testing.T) {
for _, tc := range testcases { for _, tc := range testcases {
policyArray, _ := ut.GetPolicy(tc.policy) policyArray, _ := ut.GetPolicy(tc.policy)
resourceArray, _ := GetResource(tc.resource) resourceArray, _ := GetResource(tc.resource)
ApplyPolicyOnResource(policyArray[0], resourceArray[0], "", false, nil, false, tc.namespaceSelectorMap, false, rc) ApplyPolicyOnResource(policyArray[0], resourceArray[0], "", false, nil, false, tc.namespaceSelectorMap, false, rc, false)
assert.Assert(t, int64(rc.Pass) == int64(tc.result.Pass)) assert.Assert(t, int64(rc.Pass) == int64(tc.result.Pass))
assert.Assert(t, int64(rc.Fail) == int64(tc.result.Fail)) assert.Assert(t, int64(rc.Fail) == int64(tc.result.Fail))
assert.Assert(t, int64(rc.Skip) == int64(tc.result.Skip)) assert.Assert(t, int64(rc.Skip) == int64(tc.result.Skip))

View file

@ -275,6 +275,14 @@ func convertResourceToUnstructured(resourceYaml []byte) (*unstructured.Unstructu
return resource, nil return resource, nil
} }
// GetPatchedResource converts raw bytes to unstructured object
func GetPatchedResource(patchResourceBytes []byte) (patchedResource unstructured.Unstructured, err error) {
getPatchedResource, err := GetResource(patchResourceBytes)
patchedResource = *getPatchedResource[0]
return patchedResource, nil
}
// getKindsFromPolicy will return the kinds from policy match block // getKindsFromPolicy will return the kinds from policy match block
func getKindsFromPolicy(rule v1.Rule) map[string]bool { func getKindsFromPolicy(rule v1.Rule) map[string]bool {
var resourceTypesMap = make(map[string]bool) var resourceTypesMap = make(map[string]bool)

View file

@ -7,6 +7,7 @@ import (
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"sort" "sort"
"strings" "strings"
"time" "time"
@ -14,11 +15,13 @@ import (
"github.com/fatih/color" "github.com/fatih/color"
"github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs" "github.com/go-git/go-billy/v5/memfs"
"github.com/go-logr/logr"
"github.com/kataras/tablewriter" "github.com/kataras/tablewriter"
report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha2" report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha2"
client "github.com/kyverno/kyverno/pkg/dclient" client "github.com/kyverno/kyverno/pkg/dclient"
"github.com/kyverno/kyverno/pkg/engine/response" "github.com/kyverno/kyverno/pkg/engine/response"
"github.com/kyverno/kyverno/pkg/engine/utils" "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/generate"
"github.com/kyverno/kyverno/pkg/kyverno/common" "github.com/kyverno/kyverno/pkg/kyverno/common"
sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError" sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
"github.com/kyverno/kyverno/pkg/kyverno/store" "github.com/kyverno/kyverno/pkg/kyverno/store"
@ -30,10 +33,96 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/yaml" "k8s.io/apimachinery/pkg/util/yaml"
log "sigs.k8s.io/controller-runtime/pkg/log" log "sigs.k8s.io/controller-runtime/pkg/log"
) )
var longHelp = `
Test command provides a facility to test policies on resources. For that, user needs to provide the path of the folder containing test.yaml file.
kyverno test /path/to/folderContaningTestYamls
or
kyverno test /path/to/githubRepository
The test.yaml file is configuration file for test command. It consists of 4 parts:-
"policies" (required) --> element lists one or more path of policies
"resources" (required) --> element lists one or more path of resources.
"variables" (optional) --> element with one variables files
"results" (required) --> element lists one more expected result.
`
var exampleHelp = `
For Validate Policy
test.yaml
- name: test-1
policies:
- <path>
- <path>
resources:
- <path>
- <path>
results:
- policy: <name>
rule: <name>
resource: <name>
namespace: <name> (OPTIONAL)
kind: <name>
result: <pass/fail/skip>
For more visit --> https://kyverno.io/docs/kyverno-cli/#test
For Mutate Policy
1) Policy (Namespaced-policy)
test.yaml
- name: test-1
policies:
- <path>
- <path>
resources:
- <path>
- <path>
results:
- policy: <policy_namespace>/<policy_name>
rule: <name>
resource: <name>
namespace: <name> (OPTIONAL)
patchedResource: <path>
kind: <name>
result: <pass/fail/skip>
2) ClusterPolicy(cluster-wide policy)
test.yaml
- name: test-1
policies:
- <path>
- <path>
resources:
- <path>
- <path>
results:
- policy: <name>
rule: <name>
resource: <name>
namespace: <name> (OPTIONAL)
kind: <name>
patchedResource: <path>
result: <pass/fail/skip>
NOTE:-
In the results section, policy(if ClusterPolicy) or <policy_namespace>/<policy_name>(if Policy), rule, resource, kind and result are mandatory fields for all type of policy.
pass --> patched Resource generated from engine equals to patched Resource provided by the user.
fail --> patched Resource generated from engine is not equals to patched provided by the user.
skip --> rule is not applied.
`
// Command returns version command // Command returns version command
func Command() *cobra.Command { func Command() *cobra.Command {
var cmd *cobra.Command var cmd *cobra.Command
@ -41,6 +130,8 @@ func Command() *cobra.Command {
cmd = &cobra.Command{ cmd = &cobra.Command{
Use: "test", Use: "test",
Short: "run tests from directory", Short: "run tests from directory",
Long: longHelp,
Example: exampleHelp,
RunE: func(cmd *cobra.Command, dirPath []string) (err error) { RunE: func(cmd *cobra.Command, dirPath []string) (err error) {
defer func() { defer func() {
if err != nil { if err != nil {
@ -76,6 +167,9 @@ type TestResults struct {
Result report.PolicyResult `json:"result"` Result report.PolicyResult `json:"result"`
Status report.PolicyResult `json:"status"` Status report.PolicyResult `json:"status"`
Resource string `json:"resource"` Resource string `json:"resource"`
Kind string `json:"kind"`
Namespace string `json:"namespace"`
PatchedResource string `json:"patchedResource"`
AutoGeneratedRule string `json:"auto_generated_rule"` AutoGeneratedRule string `json:"auto_generated_rule"`
} }
@ -235,19 +329,21 @@ func getLocalDirTestFiles(fs billy.Filesystem, path, fileName, valuesFile string
return errors return errors
} }
func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResults, infos []policyreport.Info) (map[string]report.PolicyReportResult, []TestResults) { func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResults, infos []policyreport.Info, policyResourcePath string, fs billy.Filesystem, isGit bool) (map[string]report.PolicyReportResult, []TestResults) {
results := make(map[string]report.PolicyReportResult) results := make(map[string]report.PolicyReportResult)
now := metav1.Timestamp{Seconds: time.Now().Unix()} now := metav1.Timestamp{Seconds: time.Now().Unix()}
for _, resp := range resps { for _, resp := range resps {
policyName := resp.PolicyResponse.Policy.Name policyName := resp.PolicyResponse.Policy.Name
resourceName := resp.PolicyResponse.Resource.Name resourceName := resp.PolicyResponse.Resource.Name
resourceKind := resp.PolicyResponse.Resource.Kind
resourceNamespace := resp.PolicyResponse.Resource.Namespace
policyNamespace := resp.PolicyResponse.Policy.Namespace
var rules []string var rules []string
for _, rule := range resp.PolicyResponse.Rules { for _, rule := range resp.PolicyResponse.Rules {
rules = append(rules, rule.Name) rules = append(rules, rule.Name)
} }
result := report.PolicyReportResult{ result := report.PolicyReportResult{
Policy: policyName, Policy: policyName,
Resources: []*corev1.ObjectReference{ Resources: []*corev1.ObjectReference{
@ -256,9 +352,19 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
}, },
}, },
} }
var patcheResourcePath []string
for i, test := range testResults { for i, test := range testResults {
var userDefinedPolicyNamespace string
var userDefinedPolicyName string
found, _ := isNamespacedPolicy(test.Policy)
if found {
userDefinedPolicyNamespace, userDefinedPolicyName = getUserDefinedPolicyNameAndNamespace(test.Policy)
test.Policy = userDefinedPolicyName
}
if test.Policy == policyName && test.Resource == resourceName { if test.Policy == policyName && test.Resource == resourceName {
var resultsKey string
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
if !util.ContainsString(rules, test.Rule) { if !util.ContainsString(rules, test.Rule) {
if !util.ContainsString(rules, "autogen-"+test.Rule) { if !util.ContainsString(rules, "autogen-"+test.Rule) {
if !util.ContainsString(rules, "autogen-cronjob-"+test.Rule) { if !util.ContainsString(rules, "autogen-cronjob-"+test.Rule) {
@ -266,18 +372,55 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
} else { } else {
testResults[i].AutoGeneratedRule = "autogen-cronjob" testResults[i].AutoGeneratedRule = "autogen-cronjob"
test.Rule = "autogen-cronjob-" + test.Rule test.Rule = "autogen-cronjob-" + test.Rule
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
} }
} else { } else {
testResults[i].AutoGeneratedRule = "autogen" testResults[i].AutoGeneratedRule = "autogen"
test.Rule = "autogen-" + test.Rule test.Rule = "autogen-" + test.Rule
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
}
if results[resultsKey].Result == "" {
result.Result = report.StatusSkip
results[resultsKey] = result
} }
} }
resultsKey := fmt.Sprintf("%s-%s-%s", test.Policy, test.Rule, test.Resource) patcheResourcePath = append(patcheResourcePath, test.PatchedResource)
if _, ok := results[resultsKey]; !ok { if _, ok := results[resultsKey]; !ok {
results[resultsKey] = result results[resultsKey] = result
} }
} }
}
for _, rule := range resp.PolicyResponse.Rules {
if rule.Type != utils.Mutation.String() {
continue
}
var resultsKey []string
var resultKey string
var result report.PolicyReportResult
resultsKey = GetAllPossibleResultsKey(policyNamespace, policyName, rule.Name, resourceNamespace, resourceKind, resourceName)
for _, resultK := range resultsKey {
if val, ok := results[resultK]; ok {
result = val
resultKey = resultK
} else {
continue
}
var x string
for _, path := range patcheResourcePath {
result.Result = report.StatusFail
x = getAndComparePatchedResource(path, resp.PatchedResource, isGit, policyResourcePath, fs)
if x == "pass" {
result.Result = report.StatusPass
break
}
}
results[resultKey] = result
}
} }
} }
@ -289,18 +432,23 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
} }
var result report.PolicyReportResult var result report.PolicyReportResult
resultsKey := fmt.Sprintf("%s-%s-%s", info.PolicyName, rule.Name, infoResult.Resource.Name) var resultsKey []string
if val, ok := results[resultsKey]; ok { var resultKey string
resultsKey = GetAllPossibleResultsKey("", info.PolicyName, rule.Name, infoResult.Resource.Namespace, infoResult.Resource.Kind, infoResult.Resource.Name)
for _, resultK := range resultsKey {
if val, ok := results[resultK]; ok {
result = val result = val
resultKey = resultK
} else { } else {
continue continue
} }
}
result.Rule = rule.Name result.Rule = rule.Name
result.Result = report.PolicyResult(rule.Check) result.Result = report.PolicyResult(rule.Check)
result.Source = policyreport.SourceValue result.Source = policyreport.SourceValue
result.Timestamp = now result.Timestamp = now
results[resultsKey] = result results[resultKey] = result
} }
} }
} }
@ -308,20 +456,89 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
return results, testResults return results, testResults
} }
func getPolicyResourceFullPath(path []string, policyResourcePath string, isGit bool) []string { func GetAllPossibleResultsKey(policyNamespace, policy, rule, namespace, kind, resource string) []string {
var resultsKey []string
resultKey1 := fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
resultKey2 := fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, namespace, kind, resource)
resultKey3 := fmt.Sprintf("%s-%s-%s-%s-%s", policyNamespace, policy, rule, kind, resource)
resultKey4 := fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNamespace, policy, rule, namespace, kind, resource)
resultsKey = append(resultsKey, resultKey1, resultKey2, resultKey3, resultKey4)
return resultsKey
}
func GetResultKeyAccordingToTestResults(policyNs, policy, rule, namespace, kind, resource string) string {
var resultKey string
resultKey = fmt.Sprintf("%s-%s-%s-%s", policy, rule, kind, resource)
if namespace != "" || policyNs != "" {
if policyNs != "" {
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policyNs, policy, rule, kind, resource)
if namespace != "" {
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", policyNs, policy, rule, namespace, kind, resource)
}
} else {
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", policy, rule, namespace, kind, resource)
}
}
return resultKey
}
func isNamespacedPolicy(policyNames string) (bool, error) {
return regexp.MatchString("^[a-z]*/[a-z]*", policyNames)
}
func getUserDefinedPolicyNameAndNamespace(policyName string) (string, string) {
policy := policyName
policy_n_ns := strings.Split(policyName, "/")
namespace := policy_n_ns[0]
policy = policy_n_ns[1]
return namespace, policy
}
// getAndComparePatchedResource --> Get the patchedResource from the path provided by user
// And compare this patchedResource with engine generated patcheResource.
func getAndComparePatchedResource(path string, enginePatchedResource unstructured.Unstructured, isGit bool, policyResourcePath string, fs billy.Filesystem) string {
var status string
patchedResources, err := common.GetPatchedResourceFromPath(fs, path, isGit, policyResourcePath)
if err != nil {
os.Exit(1)
}
var log logr.Logger
matched, err := generate.ValidateResourceWithPattern(log, enginePatchedResource.UnstructuredContent(), patchedResources.UnstructuredContent())
if err != nil {
status = "fail"
}
if matched == "" {
status = "pass"
}
return status
}
func getPolicyResourceFullPaths(path []string, policyResourcePath string, isGit bool) []string {
var pol []string var pol []string
if !isGit { if !isGit {
for _, p := range path { for _, p := range path {
pol = append(pol, filepath.Join(policyResourcePath, p)) pol = append(pol, getPolicyResourceFullPath(p, policyResourcePath, isGit))
} }
return pol return pol
} }
return path return path
} }
func getPolicyResourceFullPath(path string, policyResourcePath string, isGit bool) string {
var pol string
if !isGit {
pol = filepath.Join(policyResourcePath, path)
return pol
}
return path
}
func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile string, isGit bool, policyResourcePath string, rc *resultCounts) (err error) { func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile string, isGit bool, policyResourcePath string, rc *resultCounts) (err error) {
openAPIController, err := openapi.NewOpenAPIController() openAPIController, err := openapi.NewOpenAPIController()
validateEngineResponses := make([]*response.EngineResponse, 0) engineResponses := make([]*response.EngineResponse, 0)
var dClient *client.Client var dClient *client.Client
values := &Test{} values := &Test{}
var variablesString string var variablesString string
@ -343,9 +560,15 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s
return err return err
} }
fullPolicyPath := getPolicyResourceFullPath(values.Policies, policyResourcePath, isGit) fullPolicyPath := getPolicyResourceFullPaths(values.Policies, policyResourcePath, isGit)
fullResourcePath := getPolicyResourceFullPath(values.Resources, policyResourcePath, isGit) fullResourcePath := getPolicyResourceFullPaths(values.Resources, policyResourcePath, isGit)
for i, result := range values.Results {
var a []string
a = append(a, result.PatchedResource)
a = getPolicyResourceFullPaths(a, policyResourcePath, isGit)
values.Results[i].PatchedResource = a[0]
}
policies, err := common.GetPoliciesFromPaths(fs, fullPolicyPath, isGit, policyResourcePath) policies, err := common.GetPoliciesFromPaths(fs, fullPolicyPath, isGit, policyResourcePath)
if err != nil { if err != nil {
fmt.Printf("Error: failed to load policies\nCause: %s\n", err) fmt.Printf("Error: failed to load policies\nCause: %s\n", err)
@ -411,16 +634,15 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s
return sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.Name, resource.GetName()), err) return sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policy.Name, resource.GetName()), err)
} }
validateErs, info, err := common.ApplyPolicyOnResource(policy, resource, "", false, thisPolicyResourceValues, true, namespaceSelectorMap, false, &resultCounts) ers, info, err := common.ApplyPolicyOnResource(policy, resource, "", false, thisPolicyResourceValues, true, namespaceSelectorMap, false, &resultCounts, false)
if err != nil { if err != nil {
return sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err) return sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
} }
validateEngineResponses = append(validateEngineResponses, validateErs) engineResponses = append(engineResponses, ers)
pvInfos = append(pvInfos, info) pvInfos = append(pvInfos, info)
} }
} }
resultsMap, testResults := buildPolicyResults(validateEngineResponses, values.Results, pvInfos) resultsMap, testResults := buildPolicyResults(engineResponses, values.Results, pvInfos, policyResourcePath, fs, isGit)
resultErr := printTestResult(resultsMap, testResults, rc) resultErr := printTestResult(resultsMap, testResults, rc)
if resultErr != nil { if resultErr != nil {
return sanitizederror.NewWithError("Unable to genrate result. Error:", resultErr) return sanitizederror.NewWithError("Unable to genrate result. Error:", resultErr)
@ -440,17 +662,34 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
res.ID = i + 1 res.ID = i + 1
res.Policy = boldFgCyan.Sprintf(v.Policy) res.Policy = boldFgCyan.Sprintf(v.Policy)
res.Rule = boldFgCyan.Sprintf(v.Rule) res.Rule = boldFgCyan.Sprintf(v.Rule)
res.Resource = boldFgCyan.Sprintf(v.Resource) namespace := "default"
if v.Namespace != "" {
namespace = v.Namespace
}
res.Resource = boldFgCyan.Sprintf(namespace) + "/" + boldFgCyan.Sprintf(v.Kind) + "/" + boldFgCyan.Sprintf(v.Resource)
var ruleNameInResultKey string var ruleNameInResultKey string
if v.AutoGeneratedRule != "" { if v.AutoGeneratedRule != "" {
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule) ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
} else { } else {
ruleNameInResultKey = v.Rule ruleNameInResultKey = v.Rule
} }
resultKey := fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
resultKey := fmt.Sprintf("%s-%s-%s", v.Policy, ruleNameInResultKey, v.Resource) found, _ := isNamespacedPolicy(v.Policy)
if found || v.Namespace != "" {
if found {
var ns string
ns, v.Policy = getUserDefinedPolicyNameAndNamespace(v.Policy)
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Kind, v.Resource)
res.Policy = boldFgCyan.Sprintf(ns) + "/" + boldFgCyan.Sprintf(v.Policy)
res.Resource = boldFgCyan.Sprintf(namespace) + "/" + boldFgCyan.Sprintf(v.Kind) + "/" + boldFgCyan.Sprintf(v.Resource)
if v.Namespace != "" {
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s-%s", ns, v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
}
} else {
res.Resource = boldFgCyan.Sprintf(namespace) + "/" + boldFgCyan.Sprintf(v.Kind) + "/" + boldFgCyan.Sprintf(v.Resource)
resultKey = fmt.Sprintf("%s-%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Namespace, v.Kind, v.Resource)
}
}
var testRes report.PolicyReportResult var testRes report.PolicyReportResult
if val, ok := resps[resultKey]; ok { if val, ok := resps[resultKey]; ok {
testRes = val testRes = val
@ -465,7 +704,7 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
} }
if testRes.Result == v.Result { if testRes.Result == v.Result {
if testRes.Result == report.StatusSkip { if testRes.Result == report.StatusSkip {
res.Result = boldGreen.Sprintf("Pass") res.Result = boldYellow.Sprintf("Skip")
rc.Skip++ rc.Skip++
} else { } else {
res.Result = boldGreen.Sprintf("Pass") res.Result = boldGreen.Sprintf("Pass")
@ -487,6 +726,7 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
} }
printer.HeaderBgColor = tablewriter.BgBlackColor printer.HeaderBgColor = tablewriter.BgBlackColor
printer.HeaderFgColor = tablewriter.FgGreenColor printer.HeaderFgColor = tablewriter.FgGreenColor
fmt.Printf("\n")
printer.Print(table) printer.Print(table)
return nil return nil
} }

View file

@ -40,6 +40,12 @@ func GetPolicy(bytes []byte) (clusterPolicies []*v1.ClusterPolicy, err error) {
return nil, fmt.Errorf(msg) return nil, fmt.Errorf(msg)
} }
if (policy.Namespace != "" || policy.Namespace == "") && policy.Kind == "Policy" {
if policy.Namespace == "" {
policy.Namespace = "default"
}
policy.Kind = "ClusterPolicy"
}
clusterPolicies = append(clusterPolicies, policy) clusterPolicies = append(clusterPolicies, policy)
} }

View file

@ -7,4 +7,5 @@ results:
- policy: missing - policy: missing
rule: validate-image-tag rule: validate-image-tag
resource: test resource: test
kind: Pod
result: pass result: pass

View file

@ -7,4 +7,5 @@ results:
- policy: disallow-latest-tag - policy: disallow-latest-tag
rule: validate-image-tag rule: validate-image-tag
resource: missing resource: missing
kind: Pod
result: pass result: pass

View file

@ -7,4 +7,5 @@ results:
- policy: disallow-latest-tag - policy: disallow-latest-tag
rule: missing rule: missing
resource: test resource: test
kind: Pod
status: pass status: pass

View file

@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
labels:
foo: bar
color: orange
name: resource-equal-to-patch-res-for-cp
namespace: practice
spec:
containers:
- image: nginx:latest
name: nginx
dnsConfig:
options:
- name: ndots
value: "1"

View file

@ -0,0 +1,25 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: mydeploy
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
dnsConfig:
options:
- name: ndots
value: "1"

View file

@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: same-name-but-diff-kind
labels:
foo: bar
spec:
containers:
- name: nginx
image: nginx:latest

View file

@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: same-name-but-diff-namespace
labels:
foo: bar
color: orange
namespace: testing
spec:
containers:
- name: nginx
image: nginx:latest

View file

@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: same-name-but-diff-namespace
labels:
foo: bar
color: orange
namespace: production
spec:
containers:
- name: nginx
image: nginx:latest

View file

@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: mydeploy
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
color: orange
spec:
containers:
- image: nginx:1.14.2
name: nginx
ports:
- containerPort: 80

View file

@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: same-name-but-diff-kind
spec:
selector:
app: MyApp
ports:
- port: 80
targetPort: 80
nodePort: 30007
type: NodePort

View file

@ -0,0 +1,11 @@
apiVersion: v1
kind: Pod
metadata:
name: same-name-but-diff-kind
labels:
foo: bar
color: orange
spec:
containers:
- name: nginx
image: nginx:latest

View file

@ -0,0 +1,16 @@
apiVersion: v1
kind: Pod
metadata:
labels:
foo: bar
color: orange
name: resource-equal-to-patch-res-for-cp
namespace: practice
spec:
containers:
- image: nginx:latest
name: nginx
dnsConfig:
options:
- name: ndots
value: "1"

View file

@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
labels:
foo: bar
name: same-name-but-diff-namespace
namespace: testing
spec:
containers:
- image: nginx:latest
name: nginx
dnsConfig:
options:
- name: ndots
value: "1"

View file

@ -0,0 +1,11 @@
apiVersion: v1
kind: Pod
metadata:
name: same-name-but-diff-namespace
labels:
foo: bar
namespace: production
spec:
containers:
- name: nginx
image: nginx:latest

View file

@ -0,0 +1,62 @@
# Below there are both type of olicies: ClusterPolicy and Policy(Namespaced-Policy)
#ClusterPolicy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-label
annotations:
policies.kyverno.io/title: Add nodeSelector
policies.kyverno.io/category: Sample
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Labels are used as an important source of metadata describing objects in various ways
or triggering other functionality. Labels are also a very basic concept and should be
used throughout Kubernetes. This policy performs a simple mutation which adds a label
`color=orange` to Pods, Services, ConfigMaps, and Secrets.
spec:
background: false
validationFailureAction:
rules:
- name: add-label
match:
resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
labels:
color: orange
---
# Policy ( In testing namespace )
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: add-ndots
namespace: testing
annotations:
policies.kyverno.io/title: Add ndots
policies.kyverno.io/category: Sample
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
The ndots value controls where DNS lookups are first performed in a cluster
and needs to be set to a lower value than the default of 5 in some cases.
This policy mutates all Pods to add the ndots option with a value of 1.
spec:
background: false
rules:
- name: add-ndots
match:
resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
dnsConfig:
options:
- name: ndots
value: "1"

View file

@ -0,0 +1,100 @@
# resource == patchedResource
apiVersion: v1
kind: Pod
metadata:
labels:
foo: bar
color: orange
name: resource-equal-to-patch-res-for-cp
namespace: practice
spec:
containers:
- image: nginx:latest
name: nginx
---
# Resource with same name and diff. namespace
# Same namespace as namespaced-policy
apiVersion: v1
kind: Pod
metadata:
name: same-name-but-diff-namespace
labels:
foo: bar
namespace: testing
spec:
containers:
- name: nginx
image: nginx:latest
---
# Resource with same name and diff. namespace
# Namespace differ from namespaced-policy
apiVersion: v1
kind: Pod
metadata:
name: same-name-but-diff-namespace
labels:
foo: bar
namespace: production
spec:
containers:
- name: nginx
image: nginx:latest
---
# Deployment in default namespace
apiVersion: apps/v1
kind: Deployment
metadata:
name: mydeploy
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
---
# Resource (Service) with same name but different kind
apiVersion: v1
kind: Service
metadata:
name: same-name-but-diff-kind
spec:
selector:
app: MyApp
ports:
- port: 80
targetPort: 80
nodePort: 30007
type: NodePort
---
# Resource (Pod) with same name but different kind
apiVersion: v1
kind: Pod
metadata:
name: same-name-but-diff-kind
labels:
foo: bar
spec:
containers:
- name: nginx
image: nginx:latest

View file

@ -0,0 +1,86 @@
name: add-nodeselector
policies:
- policy.yaml
resources:
- resource.yaml
results:
- policy: add-label
rule: add-label
resource: resource-equal-to-patch-res-for-cp
patchedResource: patchedResource1.yaml
kind: Pod
namespace: practice
result: skip
- policy: add-label
rule: add-label
resource: same-name-but-diff-namespace
patchedResource: patchedResource2.yaml
kind: Pod
namespace: testing
result: pass
- policy: add-label
rule: add-label
resource: same-name-but-diff-namespace
patchedResource: patchedResource3.yaml
kind: Pod
namespace: production
result: pass
- policy: add-label
rule: add-label
resource: mydeploy
patchedResource: patchedResource4.yaml
kind: Deployment
result: pass
- policy: add-label
rule: add-label
resource: same-name-but-diff-kind
patchedResource: patchedResource5.yaml
kind: Service
result: skip
- policy: add-label
rule: add-label
resource: same-name-but-diff-kind
patchedResource: patchedResource6.yaml
kind: Pod
result: pass
- policy: testing/add-ndots
rule: add-ndots
resource: resource-equal-to-patch-res-for-cp
namespace: practice
patchedResource: patchedResource7.yaml
kind: Pod
result: skip
- policy: testing/add-ndots
rule: add-ndots
resource: same-name-but-diff-namespace
patchedResource: patchedResource8.yaml
namespace: testing
kind: Pod
result: pass
- policy: testing/add-ndots
rule: add-ndots
resource: same-name-but-diff-namespace
patchedResource: patchedResource9.yaml
kind: Pod
namespace: production
result: skip
- policy: testing/add-ndots
rule: add-ndots
resource: mydeploy
patchedResource: patchedResource10.yaml
kind: Deployment
result: skip
- policy: testing/add-ndots
rule: add-ndots
resource: same-name-but-diff-kind
patchedResource: patchedResource5.yaml
kind: Service
result: skip
- policy: testing/add-ndots
rule: add-ndots
resource: same-name-but-diff-kind
patchedResource: patchedResource11.yaml
kind: Pod
result: skip

View file

@ -7,46 +7,54 @@ results:
- policy: require-common-labels - policy: require-common-labels
rule: check-for-labels rule: check-for-labels
result: pass result: pass
kind: Pod
resource: pod-with-labels resource: pod-with-labels
# TEST: Pod Missing Labels Should Fail # TEST: Pod Missing Labels Should Fail
- policy: require-common-labels - policy: require-common-labels
rule: check-for-labels rule: check-for-labels
result: fail result: fail
kind: Pod
resource: pod-missing-labels resource: pod-missing-labels
# TEST: Deployment with Labels Should Pass # TEST: Deployment with Labels Should Pass
- policy: require-common-labels - policy: require-common-labels
rule: check-for-labels rule: check-for-labels
result: pass result: pass
kind: Deployment
resource: deployment-with-labels resource: deployment-with-labels
# TEST: Deployment with Labels Should Fail # TEST: Deployment with Labels Should Fail
- policy: require-common-labels - policy: require-common-labels
rule: check-for-labels rule: check-for-labels
result: fail result: fail
kind: Deployment
resource: deployment-missing-labels resource: deployment-missing-labels
# TEST: StatefulSet with Labels Should Pass # TEST: StatefulSet with Labels Should Pass
- policy: require-common-labels - policy: require-common-labels
rule: check-for-labels rule: check-for-labels
result: pass result: pass
kind: StatefulSet
resource: StatefulSet-with-labels resource: StatefulSet-with-labels
# TEST: StatefulSet with Labels Should fail # TEST: StatefulSet with Labels Should fail
- policy: require-common-labels - policy: require-common-labels
rule: check-for-labels rule: check-for-labels
result: fail result: fail
kind: StatefulSet
resource: StatefulSet-without-labels resource: StatefulSet-without-labels
# TEST: Cronjob with Labels Should pass # TEST: Cronjob with Labels Should pass
- policy: require-common-labels - policy: require-common-labels
rule: check-for-labels rule: check-for-labels
result: pass result: pass
kind: CronJob
resource: cronjob-with-labels resource: cronjob-with-labels
# TEST: Cronjob without Labels Should fail # TEST: Cronjob without Labels Should fail
- policy: require-common-labels - policy: require-common-labels
rule: check-for-labels rule: check-for-labels
result: fail result: fail
kind: CronJob
resource: cronjob-without-labels resource: cronjob-without-labels

View file

@ -7,20 +7,25 @@ results:
- policy: disallow-latest-tag - policy: disallow-latest-tag
rule: require-image-tag rule: require-image-tag
resource: test-require-image-tag-pass resource: test-require-image-tag-pass
kind: Pod
status: pass status: pass
- policy: disallow-latest-tag - policy: disallow-latest-tag
rule: require-image-tag rule: require-image-tag
resource: test-require-image-tag-fail resource: test-require-image-tag-fail
kind: Pod
status: fail status: fail
- policy: disallow-latest-tag - policy: disallow-latest-tag
rule: validate-image-tag rule: validate-image-tag
resource: test-validate-image-tag-ignore resource: test-validate-image-tag-ignore
kind: Pod
status: skip status: skip
- policy: disallow-latest-tag - policy: disallow-latest-tag
rule: validate-image-tag rule: validate-image-tag
resource: test-validate-image-tag-fail resource: test-validate-image-tag-fail
kind: Pod
status: fail status: fail
- policy: disallow-latest-tag - policy: disallow-latest-tag
rule: validate-image-tag rule: validate-image-tag
resource: test-validate-image-tag-pass resource: test-validate-image-tag-pass
kind: Pod
status: pass status: pass

View file

@ -11,18 +11,22 @@ results:
- policy: cm-variable-example - policy: cm-variable-example
rule: example-configmap-lookup rule: example-configmap-lookup
resource: test-env-test resource: test-env-test
kind: Pod
result: pass result: pass
- policy: cm-variable-example - policy: cm-variable-example
rule: example-configmap-lookup rule: example-configmap-lookup
resource: test-env-dev resource: test-env-dev
kind: Pod
result: fail result: fail
- policy: cm-array-example - policy: cm-array-example
rule: validate-role-annotation rule: validate-role-annotation
resource: test-web resource: test-web
kind: Pod
result: fail result: fail
- policy: cm-array-example - policy: cm-array-example
rule: validate-role-annotation rule: validate-role-annotation
resource: test-app resource: test-app
kind: Pod
result: pass result: pass
- policy: cm-blk-scalar-example - policy: cm-blk-scalar-example
rule: validate-blk-role-annotation rule: validate-blk-role-annotation