diff --git a/pkg/kyverno/apply/apply_command.go b/pkg/kyverno/apply/apply_command.go index e3878e1100..4dfdfff698 100644 --- a/pkg/kyverno/apply/apply_command.go +++ b/pkg/kyverno/apply/apply_command.go @@ -150,7 +150,7 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool, return rc, resources, skippedPolicies, pvInfos, sanitizederror.NewWithError("pass the values either using set flag or values_file flag", err) } - variables, valuesMap, namespaceSelectorMap, err := common.GetVariable(variablesString, valuesFile, fs, false, "") + variables, globalValMap, valuesMap, namespaceSelectorMap, err := common.GetVariable(variablesString, valuesFile, fs, false, "") if err != nil { if !sanitizederror.IsErrorSanitized(err) { @@ -281,7 +281,7 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool, kindOnwhichPolicyIsApplied := common.GetKindsFromPolicy(policy) for _, resource := range resources { - thisPolicyResourceValues, err := common.CheckVariableForPolicy(valuesMap, policy.GetName(), resource.GetName(), resource.GetKind(), variables, kindOnwhichPolicyIsApplied, variable) + thisPolicyResourceValues, err := common.CheckVariableForPolicy(valuesMap, globalValMap, 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) } diff --git a/pkg/kyverno/common/common.go b/pkg/kyverno/common/common.go index cd284c21bb..10eb1ae4d0 100644 --- a/pkg/kyverno/common/common.go +++ b/pkg/kyverno/common/common.go @@ -57,6 +57,7 @@ type Rule struct { type Values struct { Policies []Policy `json:"policies"` + GlobalValues map[string]string `json:"globalValues"` NamespaceSelectors []NamespaceSelector `json:"namespaceSelector"` } @@ -392,11 +393,12 @@ func RemoveDuplicateAndObjectVariables(matches [][]string) string { return variableStr } -func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit bool, policyResourcePath string) (map[string]string, map[string]map[string]Resource, map[string]map[string]string, error) { +func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit bool, policyResourcePath string) (map[string]string, map[string]string, map[string]map[string]Resource, map[string]map[string]string, error) { valuesMapResource := make(map[string]map[string]Resource) valuesMapRule := make(map[string]map[string]Rule) namespaceSelectorMap := make(map[string]map[string]string) variables := make(map[string]string) + globalValMap := make(map[string]string) reqObjVars := "" var yamlFile []byte @@ -431,19 +433,21 @@ func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit } if err != nil { - return variables, valuesMapResource, namespaceSelectorMap, sanitizederror.NewWithError("unable to read yaml", err) + return variables, globalValMap, valuesMapResource, namespaceSelectorMap, sanitizederror.NewWithError("unable to read yaml", err) } valuesBytes, err := yaml.ToJSON(yamlFile) if err != nil { - return variables, valuesMapResource, namespaceSelectorMap, sanitizederror.NewWithError("failed to convert json", err) + return variables, globalValMap, valuesMapResource, namespaceSelectorMap, sanitizederror.NewWithError("failed to convert json", err) } values := &Values{} if err := json.Unmarshal(valuesBytes, values); err != nil { - return variables, valuesMapResource, namespaceSelectorMap, sanitizederror.NewWithError("failed to decode yaml", err) + return variables, globalValMap, valuesMapResource, namespaceSelectorMap, sanitizederror.NewWithError("failed to decode yaml", err) } + globalValMap = values.GlobalValues + for _, p := range values.Policies { resourceMap := make(map[string]Resource) for _, r := range p.Resources { @@ -497,7 +501,7 @@ func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit Policies: storePolices, }) - return variables, valuesMapResource, namespaceSelectorMap, nil + return variables, globalValMap, valuesMapResource, namespaceSelectorMap, nil } // MutatePolices - function to apply mutation on policies @@ -947,16 +951,27 @@ func PrintMutatedPolicy(mutatedPolicies []*v1.ClusterPolicy) error { 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) { +func CheckVariableForPolicy(valuesMap map[string]map[string]Resource, globalValMap map[string]string, 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 } + if thisPolicyResourceValues == nil && len(globalValMap) > 0 { + thisPolicyResourceValues = make(map[string]string) + } + + for k, v := range globalValMap { + if _, ok := thisPolicyResourceValues[k]; !ok { + 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 { diff --git a/pkg/kyverno/test/test_command.go b/pkg/kyverno/test/test_command.go index b277c2558c..251cb8cc92 100644 --- a/pkg/kyverno/test/test_command.go +++ b/pkg/kyverno/test/test_command.go @@ -335,7 +335,7 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s fmt.Printf("\nExecuting %s...", values.Name) - variables, valuesMap, namespaceSelectorMap, err := common.GetVariable(variablesString, values.Variables, fs, isGit, policyResourcePath) + variables, globalValMap, valuesMap, namespaceSelectorMap, err := common.GetVariable(variablesString, values.Variables, fs, isGit, policyResourcePath) if err != nil { if !sanitizederror.IsErrorSanitized(err) { return sanitizederror.NewWithError("failed to decode yaml", err) @@ -409,7 +409,7 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s kindOnwhichPolicyIsApplied := common.GetKindsFromPolicy(policy) for _, resource := range resources { - thisPolicyResourceValues, err := common.CheckVariableForPolicy(valuesMap, policy.GetName(), resource.GetName(), resource.GetKind(), variables, kindOnwhichPolicyIsApplied, variable) + thisPolicyResourceValues, err := common.CheckVariableForPolicy(valuesMap, globalValMap, 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) } diff --git a/test/cli/test/variables/cm-globalval-example.yaml b/test/cli/test/variables/cm-globalval-example.yaml new file mode 100644 index 0000000000..b206335567 --- /dev/null +++ b/test/cli/test/variables/cm-globalval-example.yaml @@ -0,0 +1,20 @@ +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: cm-globalval-example +spec: + validationFailureAction: enforce + background: false + rules: + - name: validate-mode + match: + resources: + kinds: + - Pod + validate: + message: "The value {{ request.mode }} for val1 is not equal to 'dev'." + deny: + conditions: + - key: "{{ request.mode }}" + operator: NotEquals + value: dev diff --git a/test/cli/test/variables/resources.yaml b/test/cli/test/variables/resources.yaml index 5347e5b877..55b389e949 100644 --- a/test/cli/test/variables/resources.yaml +++ b/test/cli/test/variables/resources.yaml @@ -63,3 +63,21 @@ spec: containers: - name: nginx image: nginx:1.12 +--- +apiVersion: v1 +kind: Pod +metadata: + name: test-global-prod +spec: + containers: + - name: nginx + image: nginx:latest +--- +apiVersion: v1 +kind: Pod +metadata: + name: test-global-dev +spec: + containers: + - name: nginx + image: nginx:1.12 diff --git a/test/cli/test/variables/test.yaml b/test/cli/test/variables/test.yaml index 0da04965dd..efb634a873 100644 --- a/test/cli/test/variables/test.yaml +++ b/test/cli/test/variables/test.yaml @@ -3,6 +3,7 @@ policies: - cm-variable-example.yaml - cm-array-example.yaml - cm-blk-scalar-example.yaml + - cm-globalval-example.yaml resources: - resources.yaml variables: variables.yaml @@ -31,3 +32,11 @@ results: rule: validate-blk-role-annotation resource: test-blk-app result: pass + - policy: cm-globalval-example + rule: validate-mode + resource: test-global-dev + result: pass + - policy: cm-globalval-example + rule: validate-mode + resource: test-global-prod + result: fail diff --git a/test/cli/test/variables/variables.yaml b/test/cli/test/variables/variables.yaml index 84af5f2025..db42f7c8f7 100644 --- a/test/cli/test/variables/variables.yaml +++ b/test/cli/test/variables/variables.yaml @@ -15,7 +15,7 @@ policies: rules: - name: validate-role-annotation values: - roles-dictionary.data.allowed-roles: "[\"app\",\"test\"]" + roles-dictionary.data.allowed-roles: '["app","test"]' resources: - name: test-web values: @@ -37,3 +37,10 @@ policies: - name: test-blk-app values: request.object.metadata.annotations.role: app + - name: cm-globalval-example + resources: + - name: test-global-prod + values: + request.mode: prod +globalValues: + request.mode: dev