diff --git a/pkg/kyverno/apply/apply_command.go b/pkg/kyverno/apply/apply_command.go index 8ca0795536..42be26aa38 100644 --- a/pkg/kyverno/apply/apply_command.go +++ b/pkg/kyverno/apply/apply_command.go @@ -1,11 +1,9 @@ package apply import ( - "encoding/json" "fmt" "os" "path/filepath" - "reflect" "strings" "time" @@ -223,13 +221,9 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool, } } - for _, policy := range mutatedPolicies { - p, err := json.Marshal(policy) - if err != nil { - return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError("failed to marsal mutated policy", err) - } - log.Log.V(5).Info("mutated Policy:", string(p)) - + err = common.PrintMutatedPolicy(mutatedPolicies) + if err != nil { + return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError("failed to marsal mutated policy", err) } resources, err = common.GetResourceAccordingToResourcePath(fs, resourcePaths, cluster, mutatedPolicies, dClient, namespace, policyReport, false, "") @@ -284,32 +278,12 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool, } } - kindOnwhichPolicyIsApplied := make(map[string]struct{}) - for _, rule := range policy.Spec.Rules { - for _, kind := range rule.MatchResources.ResourceDescription.Kinds { - kindOnwhichPolicyIsApplied[kind] = struct{}{} - } - for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds { - kindOnwhichPolicyIsApplied[kind] = struct{}{} - } - } + kindOnwhichPolicyIsApplied := common.GetKindsFromPolicy(policy) for _, resource := range resources { - // get values from file for this policy resource combination - thisPolicyResourceValues := make(map[string]string) - if len(valuesMap[policy.GetName()]) != 0 && !reflect.DeepEqual(valuesMap[policy.GetName()][resource.GetName()], Resource{}) { - thisPolicyResourceValues = valuesMap[policy.GetName()][resource.GetName()].Values - } - - for k, v := range variables { - thisPolicyResourceValues[k] = v - } - - // skipping the variable check for non matching kind - if _, ok := kindOnwhichPolicyIsApplied[resource.GetKind()]; ok { - if len(variable) > 0 && len(thisPolicyResourceValues) == 0 && len(store.GetContext().Policies) == 0 { - 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) - } + thisPolicyResourceValues, err := common.CheckVariableForPolicy(valuesMap, policy.GetName(), resource.GetName(), resource.GetKind(), variables, kindOnwhichPolicyIsApplied, variable) + if err != nil { + 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) diff --git a/pkg/kyverno/common/common.go b/pkg/kyverno/common/common.go index b3ba950bae..e54fc15422 100644 --- a/pkg/kyverno/common/common.go +++ b/pkg/kyverno/common/common.go @@ -10,6 +10,7 @@ import ( "net/http" "os" "path/filepath" + "reflect" "strings" jsonpatch "github.com/evanphx/json-patch/v5" @@ -869,64 +870,108 @@ func SetInStoreContext(mutatedPolicies []*v1.ClusterPolicy, variables map[string } func processMutateEngineResponse(policy *v1.ClusterPolicy, mutateResponse *response.EngineResponse, resPath string, rc *ResultCounts, mutateLogPath string, stdin bool, mutateLogPathIsDir bool, resourceName string) error { - var policyHasMutate bool for _, rule := range policy.Spec.Rules { if rule.HasMutate() { policyHasMutate = true } } - if policyHasMutate { + if !policyHasMutate { + return nil + } - printCount := 0 - printMutatedRes := false - for _, policyRule := range policy.Spec.Rules { - ruleFoundInEngineResponse := false - for i, mutateResponseRule := range mutateResponse.PolicyResponse.Rules { - if policyRule.Name == mutateResponseRule.Name { - ruleFoundInEngineResponse = true - if mutateResponseRule.Success { - rc.Pass++ - printMutatedRes = true - } else { - if printCount < 1 { - fmt.Printf("\nFailed to apply mutate policy %s -> resource %s", policy.Name, resPath) - printCount++ - } - fmt.Printf("%d. %s - %s \n", i+1, mutateResponseRule.Name, mutateResponseRule.Message) - rc.Fail++ + printCount := 0 + printMutatedRes := false + for _, policyRule := range policy.Spec.Rules { + ruleFoundInEngineResponse := false + for i, mutateResponseRule := range mutateResponse.PolicyResponse.Rules { + if policyRule.Name == mutateResponseRule.Name { + ruleFoundInEngineResponse = true + if mutateResponseRule.Success { + rc.Pass++ + printMutatedRes = true + } else { + if printCount < 1 { + fmt.Printf("\nFailed to apply mutate policy %s -> resource %s", policy.Name, resPath) + printCount++ } - continue + fmt.Printf("%d. %s - %s \n", i+1, mutateResponseRule.Name, mutateResponseRule.Message) + rc.Fail++ } - } - if !ruleFoundInEngineResponse { - rc.Skip++ + continue } } + if !ruleFoundInEngineResponse { + rc.Skip++ + } + } - if printMutatedRes { - yamlEncodedResource, err := yamlv2.Marshal(mutateResponse.PatchedResource.Object) + if printMutatedRes { + yamlEncodedResource, err := yamlv2.Marshal(mutateResponse.PatchedResource.Object) + if err != nil { + return sanitizederror.NewWithError("failed to marshal", err) + } + + if mutateLogPath == "" { + mutatedResource := string(yamlEncodedResource) + string("\n---") + if len(strings.TrimSpace(mutatedResource)) > 0 { + if !stdin { + fmt.Printf("\nmutate policy %s applied to %s:", policy.Name, resPath) + } + fmt.Printf("\n" + mutatedResource) + fmt.Printf("\n") + } + } else { + err := PrintMutatedOutput(mutateLogPath, mutateLogPathIsDir, string(yamlEncodedResource), resourceName+"-mutated") if err != nil { - return sanitizederror.NewWithError("failed to marshal", err) - } - - if mutateLogPath == "" { - mutatedResource := string(yamlEncodedResource) + string("\n---") - if len(strings.TrimSpace(mutatedResource)) > 0 { - if !stdin { - fmt.Printf("\nmutate policy %s applied to %s:", policy.Name, resPath) - } - fmt.Printf("\n" + mutatedResource) - fmt.Printf("\n") - } - } else { - err := PrintMutatedOutput(mutateLogPath, mutateLogPathIsDir, string(yamlEncodedResource), resourceName+"-mutated") - if err != nil { - return sanitizederror.NewWithError("failed to print mutated result", err) - } - fmt.Printf("\n\nMutation:\nMutation has been applied successfully. Check the files.") + return sanitizederror.NewWithError("failed to print mutated result", err) } + fmt.Printf("\n\nMutation:\nMutation has been applied successfully. Check the files.") } } + + return nil +} + +func PrintMutatedPolicy(mutatedPolicies []*v1.ClusterPolicy) error { + for _, policy := range mutatedPolicies { + p, err := json.Marshal(policy) + if err != nil { + return sanitizederror.NewWithError("failed to marsal mutated policy", err) + } + log.Log.V(5).Info("mutated Policy:", string(p)) + } return nil } + +func CheckVariableForPolicy(valuesMap map[string]map[string]Resource, policyName string, resourceName string, resourceKind string, variables map[string]string, kindOnwhichPolicyIsApplied map[string]struct{}, variable string) (map[string]string, error) { + // get values from file for this policy resource combination + thisPolicyResourceValues := make(map[string]string) + if len(valuesMap[policyName]) != 0 && !reflect.DeepEqual(valuesMap[policyName][resourceName], Resource{}) { + thisPolicyResourceValues = valuesMap[policyName][resourceName].Values + } + for k, v := range variables { + thisPolicyResourceValues[k] = v + } + + // skipping the variable check for non matching kind + if _, ok := kindOnwhichPolicyIsApplied[resourceKind]; ok { + if len(variable) > 0 && len(thisPolicyResourceValues) == 0 && len(store.GetContext().Policies) == 0 { + return thisPolicyResourceValues, sanitizederror.NewWithError(fmt.Sprintf("policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag", policyName, resourceName), nil) + } + } + return thisPolicyResourceValues, nil +} + +func GetKindsFromPolicy(policy *v1.ClusterPolicy) map[string]struct{} { + var kindOnwhichPolicyIsApplied = make(map[string]struct{}) + for _, rule := range policy.Spec.Rules { + for _, kind := range rule.MatchResources.ResourceDescription.Kinds { + kindOnwhichPolicyIsApplied[kind] = struct{}{} + } + for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds { + kindOnwhichPolicyIsApplied[kind] = struct{}{} + } + } + return kindOnwhichPolicyIsApplied +} diff --git a/pkg/kyverno/test/test_command.go b/pkg/kyverno/test/test_command.go index 85341ca1c4..57939e15f5 100644 --- a/pkg/kyverno/test/test_command.go +++ b/pkg/kyverno/test/test_command.go @@ -7,7 +7,6 @@ import ( "net/url" "os" "path/filepath" - "reflect" "sort" "strings" "time" @@ -116,43 +115,51 @@ func testCommandExecute(dirPath []string, valuesFile string, fileName string) (r fs := memfs.New() rc = &resultCounts{} var testYamlCount int + if len(dirPath) == 0 { return rc, sanitizederror.NewWithError(fmt.Sprintf("a directory is required"), err) } + if strings.Contains(string(dirPath[0]), "https://") { gitURL, err := url.Parse(dirPath[0]) if err != nil { return rc, sanitizederror.NewWithError("failed to parse URL", err) } + pathElems := strings.Split(gitURL.Path[1:], "/") if len(pathElems) <= 1 { err := fmt.Errorf("invalid URL path %s - expected https://github.com/:owner/:repository/:branch", gitURL.Path) fmt.Printf("Error: failed to parse URL \nCause: %s\n", err) os.Exit(1) } + gitURL.Path = strings.Join([]string{pathElems[0], pathElems[1]}, "/") repoURL := gitURL.String() branch := strings.ReplaceAll(dirPath[0], repoURL+"/", "") if branch == "" { branch = "main" } + _, cloneErr := clone(repoURL, fs, branch) if cloneErr != nil { fmt.Printf("Error: failed to clone repository \nCause: %s\n", cloneErr) log.Log.V(3).Info(fmt.Sprintf("failed to clone repository %v as it is not valid", repoURL), "error", cloneErr) os.Exit(1) } + policyYamls, err := listYAMLs(fs, "/") if err != nil { return rc, sanitizederror.NewWithError("failed to list YAMLs in repository", err) } sort.Strings(policyYamls) + for _, yamlFilePath := range policyYamls { file, err := fs.Open(yamlFilePath) if err != nil { errors = append(errors, sanitizederror.NewWithError("Error: failed to open file", err)) continue } + if strings.Contains(file.Name(), fileName) { testYamlCount++ policyresoucePath := strings.Trim(yamlFilePath, fileName) @@ -161,23 +168,28 @@ func testCommandExecute(dirPath []string, valuesFile string, fileName string) (r errors = append(errors, sanitizederror.NewWithError("Error: failed to read file", err)) continue } + policyBytes, err := yaml.ToJSON(bytes) if err != nil { errors = append(errors, sanitizederror.NewWithError("failed to convert to JSON", err)) continue } + if err := applyPoliciesFromPath(fs, policyBytes, valuesFile, true, policyresoucePath, rc); err != nil { return rc, sanitizederror.NewWithError("failed to apply test command", err) } } } + if testYamlCount == 0 { fmt.Printf("\n No test yamls available \n") } + } else { path := filepath.Clean(dirPath[0]) errors = getLocalDirTestFiles(fs, path, fileName, valuesFile, rc) } + if len(errors) > 0 && log.Log.V(1).Enabled() { fmt.Printf("ignoring errors: \n") for _, e := range errors { @@ -329,13 +341,11 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s } } - for _, policy := range mutatedPolicies { - p, err := json.Marshal(policy) - if err != nil { - return sanitizederror.NewWithError("failed to marsal mutated policy", err) - } - log.Log.V(5).Info("mutated Policy:", string(p)) + err = common.PrintMutatedPolicy(mutatedPolicies) + if err != nil { + return sanitizederror.NewWithError("failed to print mutated policy", err) } + resources, err := common.GetResourceAccordingToResourcePath(fs, fullResourcePath, false, mutatedPolicies, dClient, "", false, isGit, policyResourcePath) if err != nil { fmt.Printf("Error: failed to load resources\nCause: %s\n", err) @@ -368,32 +378,12 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s matches := common.PolicyHasVariables(*policy) variable := common.RemoveDuplicateAndObjectVariables(matches) - - kindOnwhichPolicyIsApplied := make(map[string]struct{}) - for _, rule := range policy.Spec.Rules { - for _, kind := range rule.MatchResources.ResourceDescription.Kinds { - kindOnwhichPolicyIsApplied[kind] = struct{}{} - } - for _, kind := range rule.ExcludeResources.ResourceDescription.Kinds { - kindOnwhichPolicyIsApplied[kind] = struct{}{} - } - } + kindOnwhichPolicyIsApplied := common.GetKindsFromPolicy(policy) for _, resource := range resources { - - thisPolicyResourceValues := make(map[string]string) - if len(valuesMap[policy.GetName()]) != 0 && !reflect.DeepEqual(valuesMap[policy.GetName()][resource.GetName()], Resource{}) { - thisPolicyResourceValues = valuesMap[policy.GetName()][resource.GetName()].Values - } - for k, v := range variables { - thisPolicyResourceValues[k] = v - } - - // skipping the variable check for non matching kind - if _, ok := kindOnwhichPolicyIsApplied[resource.GetKind()]; ok { - if len(variable) > 0 && len(thisPolicyResourceValues) == 0 && len(store.GetContext().Policies) == 0 { - 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) - } + thisPolicyResourceValues, err := common.CheckVariableForPolicy(valuesMap, policy.GetName(), resource.GetName(), resource.GetKind(), variables, kindOnwhichPolicyIsApplied, variable) + if err != nil { + 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)