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 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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
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
|
- 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue