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:
parent
4f3798862a
commit
af4b85d3a8
4 changed files with 130 additions and 14 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
91
pkg/kyverno/common/common_test.go
Normal file
91
pkg/kyverno/common/common_test.go
Normal 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())
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue