1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-29 10:55:05 +00:00

Kyverno CLI - Namespace Selector (#1669)

* added struct for namespace selector

Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com>

* added logic for namespace selector

Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com>

* added test case

Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com>

* improved code

Signed-off-by: NoSkillGirl <singhpooja240393@gmail.com>
This commit is contained in:
Pooja Singh 2021-03-10 02:15:45 +05:30 committed by GitHub
parent 4f3798862a
commit af4b85d3a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 130 additions and 14 deletions

View file

@ -93,6 +93,13 @@ To apply policy with variables:
values:
<variable1 in policy2>: <value>
<variable2 in policy2>: <value>
namespaceSelector:
- name: <namespace1 name>
labels:
<label key>: <label value>
- name: <namespace2 name>
labels:
<label key>: <label value>
More info: https://kyverno.io/docs/kyverno-cli/
`
@ -147,7 +154,7 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError("pass the values either using set flag or values_file flag", err)
}
variables, valuesMap, err := common.GetVariable(variablesString, valuesFile, fs, false, "")
variables, valuesMap, namespaceSelectorMap, err := common.GetVariable(variablesString, valuesFile, fs, false, "")
if err != nil {
if !sanitizederror.IsErrorSanitized(err) {
return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError("failed to decode yaml", err)
@ -268,7 +275,7 @@ func applyCommandHelper(resourcePaths []string, cluster bool, policyReport bool,
return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError(fmt.Sprintf("policy %s have variables. pass the values for the variables using set/values_file flag", policy.Name), err)
}
ers, validateErs, responseError, rcErs, err := common.ApplyPolicyOnResource(policy, resource, mutateLogPath, mutateLogPathIsDir, thisPolicyResourceValues, policyReport)
ers, validateErs, responseError, rcErs, err := common.ApplyPolicyOnResource(policy, resource, mutateLogPath, mutateLogPathIsDir, thisPolicyResourceValues, policyReport, namespaceSelectorMap)
if err != nil {
return validateEngineResponses, rc, resources, skippedPolicies, sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
}

View file

@ -44,7 +44,13 @@ type Policy struct {
}
type Values struct {
Policies []Policy `json:"policies"`
Policies []Policy `json:"policies"`
NamespaceSelectors []NamespaceSelector `json:"namespaceSelector"`
}
type NamespaceSelector struct {
Name string `json:"name"`
Labels map[string]string `json:"labels"`
}
func GetPolicies(paths []string) (policies []*v1.ClusterPolicy, errors []error) {
@ -300,8 +306,9 @@ func RemoveDuplicateVariables(matches [][]string) string {
}
// GetVariable - get the variables from console/file
func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit bool, policyresoucePath string) (map[string]string, map[string]map[string]Resource, error) {
func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit bool, policyresoucePath string) (map[string]string, map[string]map[string]Resource, map[string]map[string]string, error) {
valuesMap := make(map[string]map[string]Resource)
namespaceSelectorMap := make(map[string]map[string]string)
variables := make(map[string]string)
var yamlFile []byte
var err error
@ -324,17 +331,17 @@ func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit
}
if err != nil {
return variables, valuesMap, sanitizederror.NewWithError("unable to read yaml", err)
return variables, valuesMap, namespaceSelectorMap, sanitizederror.NewWithError("unable to read yaml", err)
}
valuesBytes, err := yaml.ToJSON(yamlFile)
if err != nil {
return variables, valuesMap, sanitizederror.NewWithError("failed to convert json", err)
return variables, valuesMap, namespaceSelectorMap, sanitizederror.NewWithError("failed to convert json", err)
}
values := &Values{}
if err := json.Unmarshal(valuesBytes, values); err != nil {
return variables, valuesMap, sanitizederror.NewWithError("failed to decode yaml", err)
return variables, valuesMap, namespaceSelectorMap, sanitizederror.NewWithError("failed to decode yaml", err)
}
for _, p := range values.Policies {
@ -344,9 +351,13 @@ func GetVariable(variablesString, valuesFile string, fs billy.Filesystem, isGit
}
valuesMap[p.Name] = pmap
}
for _, n := range values.NamespaceSelectors {
namespaceSelectorMap[n.Name] = n.Labels
}
}
return variables, valuesMap, nil
return variables, valuesMap, namespaceSelectorMap, nil
}
// MutatePolices - function to apply mutation on policies
@ -369,12 +380,18 @@ func MutatePolices(policies []*v1.ClusterPolicy) ([]*v1.ClusterPolicy, error) {
// ApplyPolicyOnResource - function to apply policy on resource
func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unstructured,
mutateLogPath string, mutateLogPathIsDir bool, variables map[string]string, policyReport bool) ([]*response.EngineResponse, *response.EngineResponse, bool, bool, error) {
mutateLogPath string, mutateLogPathIsDir bool, variables map[string]string, policyReport bool, namespaceSelectorMap map[string]map[string]string) ([]*response.EngineResponse, *response.EngineResponse, bool, bool, error) {
responseError := false
rcError := false
engineResponses := make([]*response.EngineResponse, 0)
namespaceLabels := make(map[string]string)
resourceNamespace := resource.GetNamespace()
namespaceLabels = namespaceSelectorMap[resource.GetNamespace()]
if resourceNamespace != "default" && len(namespaceLabels) < 1 {
return engineResponses, &response.EngineResponse{}, responseError, rcError, sanitizederror.NewWithError(fmt.Sprintf("failed to get namesapce labels for resource %s. use --values-file flag to pass the namespace labels", resource.GetName()), nil)
}
resPath := fmt.Sprintf("%s/%s/%s", resource.GetNamespace(), resource.GetKind(), resource.GetName())
log.Log.V(3).Info("applying policy on resource", "policy", policy.Name, "resource", resPath)
@ -409,7 +426,7 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
ctx.AddJSON(jsonData)
}
mutateResponse := engine.Mutate(&engine.PolicyContext{Policy: *policy, NewResource: *resource, JSONContext: ctx})
mutateResponse := engine.Mutate(&engine.PolicyContext{Policy: *policy, NewResource: *resource, JSONContext: ctx, NamespaceLabels: namespaceLabels})
engineResponses = append(engineResponses, mutateResponse)
if !mutateResponse.IsSuccessful() {
@ -451,7 +468,7 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
}
}
policyCtx := &engine.PolicyContext{Policy: *policy, NewResource: mutateResponse.PatchedResource, JSONContext: ctx}
policyCtx := &engine.PolicyContext{Policy: *policy, NewResource: mutateResponse.PatchedResource, JSONContext: ctx, NamespaceLabels: namespaceLabels}
validateResponse := engine.Validate(policyCtx)
if !policyReport {
if !validateResponse.IsSuccessful() {
@ -481,7 +498,8 @@ func ApplyPolicyOnResource(policy *v1.ClusterPolicy, resource *unstructured.Unst
ExcludeResourceFunc: func(s1, s2, s3 string) bool {
return false
},
JSONContext: context.NewContext(),
JSONContext: context.NewContext(),
NamespaceLabels: namespaceLabels,
}
generateResponse := engine.Generate(policyContext)
engineResponses = append(engineResponses, generateResponse)

View file

@ -0,0 +1,91 @@
package common
import (
"testing"
ut "github.com/kyverno/kyverno/pkg/utils"
"gotest.tools/assert"
)
var policyNamespaceSeelector = []byte(`{
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {
"name": "enforce-pod-name"
},
"spec": {
"validationFailureAction": "audit",
"background": true,
"rules": [
{
"name": "validate-name",
"match": {
"resources": {
"kinds": [
"Pod"
],
"namespaceSelector": {
"matchExpressions": [
{
"key": "foo.com/managed-state",
"operator": "In",
"values": [
"managed"
]
}
]
}
}
},
"validate": {
"message": "The Pod must end with -nginx",
"pattern": {
"metadata": {
"name": "*-nginx"
}
}
}
}
]
}
}
`)
func Test_NamespaceSelector(t *testing.T) {
type TestCase struct {
policy []byte
resource []byte
namespaceSelectorMap map[string]map[string]string
sucess bool
}
testcases := []TestCase{
{
policy: policyNamespaceSeelector,
resource: []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"nginx","namespace":"test1"},"spec":{"containers":[{"image":"nginx:latest","name":"test-fail"}]}}`),
namespaceSelectorMap: map[string]map[string]string{
"test1": {
"foo.com/managed-state": "managed",
},
},
sucess: false,
},
{
policy: policyNamespaceSeelector,
resource: []byte(`{"apiVersion":"v1","kind":"Pod","metadata":{"name":"test-nginx","namespace":"test1"},"spec":{"containers":[{"image":"nginx:latest","name":"test-pass"}]}}`),
namespaceSelectorMap: map[string]map[string]string{
"test1": {
"foo.com/managed-state": "managed",
},
},
sucess: true,
},
}
for _, tc := range testcases {
policyArray, _ := ut.GetPolicy(tc.policy)
resourceArray, _ := GetResource(tc.resource)
_, validateErs, _, _, _ := ApplyPolicyOnResource(policyArray[0], resourceArray[0], "", false, nil, false, tc.namespaceSelectorMap)
assert.Assert(t, tc.sucess == validateErs.IsSuccessful())
}
}

View file

@ -269,7 +269,7 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s
fmt.Printf("\nExecuting %s...", values.Name)
_, valuesMap, err := common.GetVariable(variablesString, values.Variables, fs, isGit, policyresoucePath)
_, valuesMap, namespaceSelectorMap, err := common.GetVariable(variablesString, values.Variables, fs, isGit, policyresoucePath)
if err != nil {
if !sanitizederror.IsErrorSanitized(err) {
return sanitizederror.NewWithError("failed to decode yaml", err)
@ -334,7 +334,7 @@ func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, valuesFile s
return sanitizederror.NewWithError(fmt.Sprintf("policy %s have variables. pass the values for the variables using set/values_file flag", policy.Name), err)
}
ers, validateErs, _, _, err := common.ApplyPolicyOnResource(policy, resource, "", false, thisPolicyResourceValues, true)
ers, validateErs, _, _, err := common.ApplyPolicyOnResource(policy, resource, "", false, thisPolicyResourceValues, true, namespaceSelectorMap)
if err != nil {
return sanitizederror.NewWithError(fmt.Errorf("failed to apply policy %v on resource %v", policy.Name, resource.GetName()).Error(), err)
}