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:
parent
c32002837d
commit
ae6f6c327f
32 changed files with 780 additions and 52 deletions
1
Makefile
1
Makefile
|
@ -198,6 +198,7 @@ test-e2e-local:
|
|||
#Test TestCmd Policy
|
||||
test-cmd: cli
|
||||
$(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-fail/missing-policy && exit 1 || exit 0
|
||||
$(PWD)/$(CLI_PATH)/kyverno test ./test/cli/test-fail/missing-rule && exit 1 || exit 0
|
||||
|
|
|
@ -75,10 +75,10 @@ func filterRule(rule kyverno.Rule, policyContext *PolicyContext) *response.RuleR
|
|||
logger := log.Log.WithName("Generate").WithValues("policy", policy.Name,
|
||||
"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 err = MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels); err == nil {
|
||||
if err = MatchesResourceDescription(oldResource, rule, admissionInfo, excludeGroupRole, namespaceLabels, ""); err == nil {
|
||||
return &response.RuleResponse{
|
||||
Name: rule.Name,
|
||||
Type: "Generation",
|
||||
|
|
|
@ -65,7 +65,7 @@ func Mutate(policyContext *PolicyContext) (resp *response.EngineResponse) {
|
|||
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())
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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()
|
||||
resource := *resourceRef.DeepCopy()
|
||||
admissionInfo := *admissionInfoRef.DeepCopy()
|
||||
|
||||
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 {
|
||||
// inlcude object if ANY of the criterias match
|
||||
// so if one matches then break from loop
|
||||
|
|
|
@ -898,7 +898,7 @@ func TestMatchesResourceDescription(t *testing.T) {
|
|||
resource, _ := utils.ConvertToUnstructured(tc.Resource)
|
||||
|
||||
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 !tc.areErrorsExpected {
|
||||
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}}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -1027,7 +1027,7 @@ func TestResourceDescriptionMatch_Name(t *testing.T) {
|
|||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1087,7 +1087,7 @@ func TestResourceDescriptionMatch_Name_Regex(t *testing.T) {
|
|||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1155,7 +1155,7 @@ func TestResourceDescriptionMatch_Label_Expression_NotMatch(t *testing.T) {
|
|||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1224,7 +1224,7 @@ func TestResourceDescriptionMatch_Label_Expression_Match(t *testing.T) {
|
|||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1304,7 +1304,7 @@ func TestResourceDescriptionExclude_Label_Expression_Match(t *testing.T) {
|
|||
rule := kyverno.Rule{MatchResources: kyverno.MatchResources{ResourceDescription: resourceDescription},
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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 {
|
||||
return true
|
||||
}
|
||||
|
||||
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 {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
_, 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 {
|
||||
return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
|
||||
}
|
||||
|
|
|
@ -524,7 +524,7 @@ func MutatePolices(policies []*v1.ClusterPolicy) ([]*v1.ClusterPolicy, error) {
|
|||
|
||||
// ApplyPolicyOnResource - function to apply policy on resource
|
||||
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
|
||||
|
||||
|
@ -533,7 +533,7 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
|
|||
}
|
||||
|
||||
namespaceLabels := make(map[string]string)
|
||||
|
||||
var engineResponse *response.EngineResponse
|
||||
policyWithNamespaceSelector := false
|
||||
for _, p := range policy.Spec.Rules {
|
||||
if p.MatchResources.ResourceDescription.NamespaceSelector != nil ||
|
||||
|
@ -547,7 +547,7 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
|
|||
resourceNamespace := resource.GetNamespace()
|
||||
namespaceLabels = namespaceSelectorMap[resource.GetNamespace()]
|
||||
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})
|
||||
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 !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)
|
||||
info = ProcessValidateEngineResponse(policy, validateResponse, resPath, rc, policyReport)
|
||||
}
|
||||
if validateResponse != nil {
|
||||
engineResponse = validateResponse
|
||||
}
|
||||
|
||||
var policyHasGenerate bool
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
|
@ -624,10 +631,13 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
|
|||
NamespaceLabels: namespaceLabels,
|
||||
}
|
||||
generateResponse := engine.Generate(policyContext)
|
||||
if validateResponse != nil {
|
||||
engineResponse = generateResponse
|
||||
}
|
||||
processGenerateEngineResponse(policy, generateResponse, resPath, rc)
|
||||
}
|
||||
|
||||
return validateResponse, info, nil
|
||||
return engineResponse, info, nil
|
||||
}
|
||||
|
||||
// PrintMutatedOutput - function to print output in provided file or directory
|
||||
|
@ -876,7 +886,7 @@ func SetInStoreContext(mutatedPolicies []*v1.ClusterPolicy, variables map[string
|
|||
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
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
if rule.HasMutate() {
|
||||
|
@ -922,11 +932,12 @@ func processMutateEngineResponse(policy *v1.ClusterPolicy, mutateResponse *respo
|
|||
if mutateLogPath == "" {
|
||||
mutatedResource := string(yamlEncodedResource) + string("\n---")
|
||||
if len(strings.TrimSpace(mutatedResource)) > 0 {
|
||||
if !stdin {
|
||||
if !stdin && printPatchResource {
|
||||
fmt.Printf("\nmutate policy %s applied to %s:", policy.Name, resPath)
|
||||
}
|
||||
fmt.Printf("\n" + mutatedResource)
|
||||
fmt.Printf("\n")
|
||||
if printPatchResource {
|
||||
fmt.Printf("\n" + mutatedResource)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err := PrintMutatedOutput(mutateLogPath, mutateLogPathIsDir, string(yamlEncodedResource), resourceName+"-mutated")
|
||||
|
@ -993,3 +1004,31 @@ func GetKindsFromPolicy(policy *v1.ClusterPolicy) map[string]struct{} {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ func Test_NamespaceSelector(t *testing.T) {
|
|||
for _, tc := range testcases {
|
||||
policyArray, _ := ut.GetPolicy(tc.policy)
|
||||
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.Fail) == int64(tc.result.Fail))
|
||||
assert.Assert(t, int64(rc.Skip) == int64(tc.result.Skip))
|
||||
|
|
|
@ -275,6 +275,14 @@ func convertResourceToUnstructured(resourceYaml []byte) (*unstructured.Unstructu
|
|||
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
|
||||
func getKindsFromPolicy(rule v1.Rule) map[string]bool {
|
||||
var resourceTypesMap = make(map[string]bool)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -14,11 +15,13 @@ import (
|
|||
"github.com/fatih/color"
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/memfs"
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/kataras/tablewriter"
|
||||
report "github.com/kyverno/kyverno/pkg/api/policyreport/v1alpha2"
|
||||
client "github.com/kyverno/kyverno/pkg/dclient"
|
||||
"github.com/kyverno/kyverno/pkg/engine/response"
|
||||
"github.com/kyverno/kyverno/pkg/engine/utils"
|
||||
"github.com/kyverno/kyverno/pkg/generate"
|
||||
"github.com/kyverno/kyverno/pkg/kyverno/common"
|
||||
sanitizederror "github.com/kyverno/kyverno/pkg/kyverno/sanitizedError"
|
||||
"github.com/kyverno/kyverno/pkg/kyverno/store"
|
||||
|
@ -30,17 +33,105 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
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
|
||||
func Command() *cobra.Command {
|
||||
var cmd *cobra.Command
|
||||
var valuesFile, fileName string
|
||||
cmd = &cobra.Command{
|
||||
Use: "test",
|
||||
Short: "run tests from directory",
|
||||
Use: "test",
|
||||
Short: "run tests from directory",
|
||||
Long: longHelp,
|
||||
Example: exampleHelp,
|
||||
RunE: func(cmd *cobra.Command, dirPath []string) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
|
@ -76,6 +167,9 @@ type TestResults struct {
|
|||
Result report.PolicyResult `json:"result"`
|
||||
Status report.PolicyResult `json:"status"`
|
||||
Resource string `json:"resource"`
|
||||
Kind string `json:"kind"`
|
||||
Namespace string `json:"namespace"`
|
||||
PatchedResource string `json:"patchedResource"`
|
||||
AutoGeneratedRule string `json:"auto_generated_rule"`
|
||||
}
|
||||
|
||||
|
@ -235,19 +329,21 @@ func getLocalDirTestFiles(fs billy.Filesystem, path, fileName, valuesFile string
|
|||
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)
|
||||
now := metav1.Timestamp{Seconds: time.Now().Unix()}
|
||||
|
||||
for _, resp := range resps {
|
||||
policyName := resp.PolicyResponse.Policy.Name
|
||||
resourceName := resp.PolicyResponse.Resource.Name
|
||||
resourceKind := resp.PolicyResponse.Resource.Kind
|
||||
resourceNamespace := resp.PolicyResponse.Resource.Namespace
|
||||
policyNamespace := resp.PolicyResponse.Policy.Namespace
|
||||
|
||||
var rules []string
|
||||
for _, rule := range resp.PolicyResponse.Rules {
|
||||
rules = append(rules, rule.Name)
|
||||
}
|
||||
|
||||
result := report.PolicyReportResult{
|
||||
Policy: policyName,
|
||||
Resources: []*corev1.ObjectReference{
|
||||
|
@ -256,9 +352,19 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
var patcheResourcePath []string
|
||||
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 {
|
||||
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, "autogen-"+test.Rule) {
|
||||
if !util.ContainsString(rules, "autogen-cronjob-"+test.Rule) {
|
||||
|
@ -266,18 +372,55 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
|
|||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen-cronjob"
|
||||
test.Rule = "autogen-cronjob-" + test.Rule
|
||||
resultsKey = GetResultKeyAccordingToTestResults(userDefinedPolicyNamespace, test.Policy, test.Rule, test.Namespace, test.Kind, test.Resource)
|
||||
}
|
||||
} else {
|
||||
testResults[i].AutoGeneratedRule = "autogen"
|
||||
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 {
|
||||
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
|
||||
resultsKey := fmt.Sprintf("%s-%s-%s", info.PolicyName, rule.Name, infoResult.Resource.Name)
|
||||
if val, ok := results[resultsKey]; ok {
|
||||
result = val
|
||||
} else {
|
||||
continue
|
||||
var resultsKey []string
|
||||
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
|
||||
resultKey = resultK
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
result.Rule = rule.Name
|
||||
result.Result = report.PolicyResult(rule.Check)
|
||||
result.Source = policyreport.SourceValue
|
||||
result.Timestamp = now
|
||||
results[resultsKey] = result
|
||||
results[resultKey] = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -308,20 +456,89 @@ func buildPolicyResults(resps []*response.EngineResponse, testResults []TestResu
|
|||
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
|
||||
if !isGit {
|
||||
for _, p := range path {
|
||||
pol = append(pol, filepath.Join(policyResourcePath, p))
|
||||
pol = append(pol, getPolicyResourceFullPath(p, policyResourcePath, isGit))
|
||||
}
|
||||
return pol
|
||||
}
|
||||
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) {
|
||||
openAPIController, err := openapi.NewOpenAPIController()
|
||||
validateEngineResponses := make([]*response.EngineResponse, 0)
|
||||
engineResponses := make([]*response.EngineResponse, 0)
|
||||
var dClient *client.Client
|
||||
values := &Test{}
|
||||
var variablesString string
|
||||
|
@ -343,9 +560,15 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s
|
|||
return err
|
||||
}
|
||||
|
||||
fullPolicyPath := getPolicyResourceFullPath(values.Policies, policyResourcePath, isGit)
|
||||
fullResourcePath := getPolicyResourceFullPath(values.Resources, policyResourcePath, isGit)
|
||||
fullPolicyPath := getPolicyResourceFullPaths(values.Policies, 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)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
resultsMap, testResults := buildPolicyResults(validateEngineResponses, values.Results, pvInfos)
|
||||
|
||||
resultsMap, testResults := buildPolicyResults(engineResponses, values.Results, pvInfos, policyResourcePath, fs, isGit)
|
||||
resultErr := printTestResult(resultsMap, testResults, rc)
|
||||
if resultErr != nil {
|
||||
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.Policy = boldFgCyan.Sprintf(v.Policy)
|
||||
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
|
||||
if v.AutoGeneratedRule != "" {
|
||||
ruleNameInResultKey = fmt.Sprintf("%s-%s", v.AutoGeneratedRule, v.Rule)
|
||||
} else {
|
||||
ruleNameInResultKey = v.Rule
|
||||
}
|
||||
|
||||
resultKey := fmt.Sprintf("%s-%s-%s", v.Policy, ruleNameInResultKey, v.Resource)
|
||||
|
||||
resultKey := fmt.Sprintf("%s-%s-%s-%s", v.Policy, ruleNameInResultKey, v.Kind, 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
|
||||
if val, ok := resps[resultKey]; ok {
|
||||
testRes = val
|
||||
|
@ -465,7 +704,7 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
|
|||
}
|
||||
if testRes.Result == v.Result {
|
||||
if testRes.Result == report.StatusSkip {
|
||||
res.Result = boldGreen.Sprintf("Pass")
|
||||
res.Result = boldYellow.Sprintf("Skip")
|
||||
rc.Skip++
|
||||
} else {
|
||||
res.Result = boldGreen.Sprintf("Pass")
|
||||
|
@ -487,6 +726,7 @@ func printTestResult(resps map[string]report.PolicyReportResult, testResults []T
|
|||
}
|
||||
printer.HeaderBgColor = tablewriter.BgBlackColor
|
||||
printer.HeaderFgColor = tablewriter.FgGreenColor
|
||||
fmt.Printf("\n")
|
||||
printer.Print(table)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -40,6 +40,12 @@ func GetPolicy(bytes []byte) (clusterPolicies []*v1.ClusterPolicy, err error) {
|
|||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,4 +7,5 @@ results:
|
|||
- policy: missing
|
||||
rule: validate-image-tag
|
||||
resource: test
|
||||
kind: Pod
|
||||
result: pass
|
||||
|
|
|
@ -7,4 +7,5 @@ results:
|
|||
- policy: disallow-latest-tag
|
||||
rule: validate-image-tag
|
||||
resource: missing
|
||||
kind: Pod
|
||||
result: pass
|
||||
|
|
|
@ -7,4 +7,5 @@ results:
|
|||
- policy: disallow-latest-tag
|
||||
rule: missing
|
||||
resource: test
|
||||
kind: Pod
|
||||
status: pass
|
||||
|
|
16
test/cli/test-mutate/patchedResource1.yaml
Normal file
16
test/cli/test-mutate/patchedResource1.yaml
Normal 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"
|
25
test/cli/test-mutate/patchedResource10.yaml
Normal file
25
test/cli/test-mutate/patchedResource10.yaml
Normal 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"
|
10
test/cli/test-mutate/patchedResource11.yaml
Normal file
10
test/cli/test-mutate/patchedResource11.yaml
Normal 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
|
12
test/cli/test-mutate/patchedResource2.yaml
Normal file
12
test/cli/test-mutate/patchedResource2.yaml
Normal 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
|
12
test/cli/test-mutate/patchedResource3.yaml
Normal file
12
test/cli/test-mutate/patchedResource3.yaml
Normal 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
|
23
test/cli/test-mutate/patchedResource4.yaml
Normal file
23
test/cli/test-mutate/patchedResource4.yaml
Normal 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
|
||||
|
12
test/cli/test-mutate/patchedResource5.yaml
Normal file
12
test/cli/test-mutate/patchedResource5.yaml
Normal 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
|
11
test/cli/test-mutate/patchedResource6.yaml
Normal file
11
test/cli/test-mutate/patchedResource6.yaml
Normal 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
|
16
test/cli/test-mutate/patchedResource7.yaml
Normal file
16
test/cli/test-mutate/patchedResource7.yaml
Normal 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"
|
15
test/cli/test-mutate/patchedResource8.yaml
Normal file
15
test/cli/test-mutate/patchedResource8.yaml
Normal 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"
|
11
test/cli/test-mutate/patchedResource9.yaml
Normal file
11
test/cli/test-mutate/patchedResource9.yaml
Normal 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
|
62
test/cli/test-mutate/policy.yaml
Normal file
62
test/cli/test-mutate/policy.yaml
Normal 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"
|
100
test/cli/test-mutate/resource.yaml
Normal file
100
test/cli/test-mutate/resource.yaml
Normal 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
|
||||
|
||||
|
86
test/cli/test-mutate/test.yaml
Normal file
86
test/cli/test-mutate/test.yaml
Normal 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
|
|
@ -7,46 +7,54 @@ results:
|
|||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: pass
|
||||
kind: Pod
|
||||
resource: pod-with-labels
|
||||
|
||||
# TEST: Pod Missing Labels Should Fail
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: fail
|
||||
kind: Pod
|
||||
resource: pod-missing-labels
|
||||
|
||||
# TEST: Deployment with Labels Should Pass
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: pass
|
||||
kind: Deployment
|
||||
resource: deployment-with-labels
|
||||
|
||||
# TEST: Deployment with Labels Should Fail
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: fail
|
||||
kind: Deployment
|
||||
resource: deployment-missing-labels
|
||||
|
||||
# TEST: StatefulSet with Labels Should Pass
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: pass
|
||||
kind: StatefulSet
|
||||
resource: StatefulSet-with-labels
|
||||
|
||||
# TEST: StatefulSet with Labels Should fail
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: fail
|
||||
kind: StatefulSet
|
||||
resource: StatefulSet-without-labels
|
||||
|
||||
# TEST: Cronjob with Labels Should pass
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: pass
|
||||
kind: CronJob
|
||||
resource: cronjob-with-labels
|
||||
|
||||
# TEST: Cronjob without Labels Should fail
|
||||
- policy: require-common-labels
|
||||
rule: check-for-labels
|
||||
result: fail
|
||||
kind: CronJob
|
||||
resource: cronjob-without-labels
|
||||
|
|
|
@ -7,20 +7,25 @@ results:
|
|||
- policy: disallow-latest-tag
|
||||
rule: require-image-tag
|
||||
resource: test-require-image-tag-pass
|
||||
kind: Pod
|
||||
status: pass
|
||||
- policy: disallow-latest-tag
|
||||
rule: require-image-tag
|
||||
resource: test-require-image-tag-fail
|
||||
kind: Pod
|
||||
status: fail
|
||||
- policy: disallow-latest-tag
|
||||
rule: validate-image-tag
|
||||
resource: test-validate-image-tag-ignore
|
||||
kind: Pod
|
||||
status: skip
|
||||
- policy: disallow-latest-tag
|
||||
rule: validate-image-tag
|
||||
resource: test-validate-image-tag-fail
|
||||
kind: Pod
|
||||
status: fail
|
||||
- policy: disallow-latest-tag
|
||||
rule: validate-image-tag
|
||||
resource: test-validate-image-tag-pass
|
||||
kind: Pod
|
||||
status: pass
|
||||
|
|
|
@ -11,18 +11,22 @@ results:
|
|||
- policy: cm-variable-example
|
||||
rule: example-configmap-lookup
|
||||
resource: test-env-test
|
||||
kind: Pod
|
||||
result: pass
|
||||
- policy: cm-variable-example
|
||||
rule: example-configmap-lookup
|
||||
resource: test-env-dev
|
||||
kind: Pod
|
||||
result: fail
|
||||
- policy: cm-array-example
|
||||
rule: validate-role-annotation
|
||||
resource: test-web
|
||||
kind: Pod
|
||||
result: fail
|
||||
- policy: cm-array-example
|
||||
rule: validate-role-annotation
|
||||
resource: test-app
|
||||
kind: Pod
|
||||
result: pass
|
||||
- policy: cm-blk-scalar-example
|
||||
rule: validate-blk-role-annotation
|
||||
|
|
Loading…
Reference in a new issue