1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 18:38:40 +00:00

NK-21: Added checking request by selector. Added tests for this logic. Added test policy file for selectors.

This commit is contained in:
belyshevdenis 2019-02-26 20:05:07 +02:00
parent ae952f73ab
commit 68e468a699
9 changed files with 162 additions and 30 deletions

View file

@ -102,9 +102,10 @@ func (c *PolicyController) deletePolicyHandler(resource interface{}) {
func (c *PolicyController) getResourceKey(resource interface{}) string {
if key, err := cache.MetaNamespaceKeyFunc(resource); err != nil {
c.logger.Printf("Error retrieving policy key: %v\n", err)
return ""
c.logger.Fatalf("Error retrieving policy key: %v\n", err)
} else {
return key
}
return ""
}

17
crd/selector-policy.yaml Normal file
View file

@ -0,0 +1,17 @@
apiVersion: nirmata.io/v1alpha1
kind : Policy
metadata:
name: selector-policy
spec:
failurePolicy: continueOnError
rules:
- resource:
kind: ConfigMap
selector:
matchLabels:
label1: test1
matchExpressions:
patches:
- path: /
op : Add
value : "20"

View file

@ -44,10 +44,10 @@ func main() {
fmt.Printf("Error running PolicyController! Error: %s\n", err)
}
fmt.Printf("Policy PolicyController has started")
fmt.Println("Policy Controller has started")
<-stopCh
server.Stop()
fmt.Printf("Policy PolicyController has stopped")
fmt.Println("Policy Controller has stopped")
}
func init() {

View file

@ -24,7 +24,7 @@ type PolicySpec struct {
// PolicyRule is policy rule that will be applied to resource
type PolicyRule struct {
Resource PolicyResource `json:"resource"`
Patches *[]PolicyPatch `json:"patches"`
Patches []PolicyPatch `json:"patches"`
ConfigMapGenerator *PolicyConfigGenerator `json:"configMapGenerator"`
SecretGenerator *PolicyConfigGenerator `json:"secretGenerator"`
}

View file

@ -0,0 +1,3 @@
cd "$(dirname "$0")"
kubectl create -f resources/test-configmap.yaml
kubectl delete -f resources/test-configmap.yaml

View file

@ -0,0 +1,10 @@
apiVersion: v1
data:
properties:
arms=3
kind: ConfigMap
metadata:
name: test-configmap
labels:
label1: test1
label2: test2

View file

@ -1,8 +1,11 @@
package webhooks
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/api/admission/v1beta1"
"encoding/json"
)
var supportedKinds = [...]string{
@ -35,20 +38,72 @@ func kindIsSupported(kind string) bool {
return false
}
// AdmissionIsRequired checks for admission if kind is supported
func AdmissionIsRequired(request *v1beta1.AdmissionRequest) bool {
// Here you can make additional hardcoded checks
return kindIsSupported(request.Kind.Kind)
}
func IsRuleApplicableToRequest(rule types.PolicyRule, request *v1beta1.AdmissionRequest) bool {
return IsRuleResourceFitsRequest(rule.Resource, request)
}
func IsRuleResourceFitsRequest(resource types.PolicyResource, request *v1beta1.AdmissionRequest) bool {
if resource.Kind != request.Kind.Kind {
// IsRuleApplicableToRequest checks requests kind, name and labels to fit the policy
func IsRuleApplicableToRequest(policyResource types.PolicyResource, request *v1beta1.AdmissionRequest) bool {
if policyResource.Selector == nil && policyResource.Name == nil {
// TODO: selector or name MUST be specified
return false
}
// TODO: resource.Name must be equal to request.Object.Raw -> /metadata/name
if policyResource.Kind != request.Kind.Kind {
return false
}
if request.Object.Raw != nil {
meta := parseMetadataFromObject(request.Object.Raw)
name := parseNameFromMetadata(meta)
if (policyResource.Name != nil && *policyResource.Name != name) {
return false
}
if policyResource.Selector != nil {
selector, err := metav1.LabelSelectorAsSelector(policyResource.Selector)
if err != nil {
// TODO: log that selector is invalid
return false
}
labelMap := parseLabelsFromMetadata(meta)
if !selector.Matches(labelMap) {
return false
}
}
}
return true
}
func parseMetadataFromObject(bytes []byte) map[string]interface{} {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
return objectJSON["metadata"].(map[string]interface{})
}
func parseLabelsFromMetadata(meta map[string]interface{}) labels.Set {
if interfaceMap, ok := meta["labels"].(map[string]interface{}); ok {
labelMap := make(labels.Set, len(interfaceMap))
for key, value := range interfaceMap {
labelMap[key] = value.(string)
}
return labelMap
}
return nil
}
func parseNameFromMetadata(meta map[string]interface{}) string {
if name, ok := meta["name"].(string); ok {
return name
}
return ""
}

View file

@ -50,38 +50,84 @@ func TestAdmissionIsRequired(t *testing.T) {
}
func TestIsRuleResourceFitsRequest_Kind(t *testing.T) {
resource := types.PolicyResource{
resourceName := "test-config-map"
resource := types.PolicyResource {
Kind: "ConfigMap",
Name: &resourceName,
}
request := v1beta1.AdmissionRequest{
Kind: metav1.GroupVersionKind{Kind: "ConfigMap"},
request := v1beta1.AdmissionRequest {
Kind: metav1.GroupVersionKind{ Kind: "ConfigMap" },
}
assertEq(t, true, webhooks.IsRuleResourceFitsRequest(resource, &request))
objectByteArray := []byte(`{"metadata":{"name":"test-config-map","namespace":"default","creationTimestamp":null,"labels":{"label1":"test1","label2":"test2"}}}`)
request.Object.Raw = objectByteArray
assertEq(t, true, webhooks.IsRuleApplicableToRequest(resource, &request))
resource.Kind = "Deployment"
assertEq(t, false, webhooks.IsRuleResourceFitsRequest(resource, &request))
assertEq(t, false, webhooks.IsRuleApplicableToRequest(resource, &request))
}
func TestIsRuleResourceFitsRequest_Name(t *testing.T) {
resourceName := "test-config-map"
resource := types.PolicyResource{
resource := types.PolicyResource {
Kind: "ConfigMap",
Name: &resourceName,
}
request := v1beta1.AdmissionRequest{
Kind: metav1.GroupVersionKind{Kind: "ConfigMap"},
Name: "test-config-map",
}
assertEq(t, true, webhooks.IsRuleResourceFitsRequest(resource, &request))
objectByteArray := []byte(`{"metadata":{"name":"test-config-map","namespace":"default","creationTimestamp":null,"labels":{"label1":"test1","label2":"test2"}}}`)
request.Object.Raw = objectByteArray
assertEq(t, true, webhooks.IsRuleApplicableToRequest(resource, &request))
resourceName = "test-config-map-new"
assertEq(t, false, webhooks.IsRuleResourceFitsRequest(resource, &request))
request.Name = "test-config-map-new"
assertEq(t, true, webhooks.IsRuleResourceFitsRequest(resource, &request))
request.Name = ""
assertEq(t, false, webhooks.IsRuleResourceFitsRequest(resource, &request))
assertEq(t, false, webhooks.IsRuleApplicableToRequest(resource, &request))
objectByteArray = []byte(`{"metadata":{"name":"test-config-map-new","namespace":"default","creationTimestamp":null,"labels":{"label1":"test1","label2":"test2"}}}`)
request.Object.Raw = objectByteArray
assertEq(t, true, webhooks.IsRuleApplicableToRequest(resource, &request))
objectByteArray = []byte(`{"metadata":{"name":"","namespace":"default","creationTimestamp":null,"labels":{"label1":"test1","label2":"test2"}}}`)
request.Object.Raw = objectByteArray
assertEq(t, false, webhooks.IsRuleApplicableToRequest(resource, &request))
}
func TestIsRuleApplicableToRequest(t *testing.T) {
// TODO
}
func TestIsRuleResourceFitsRequest_Selector(t *testing.T) {
resource := types.PolicyResource {
Kind: "ConfigMap",
Selector: &metav1.LabelSelector {
MatchLabels: map[string]string {
"label1" : "test1",
"label2" : "test2",
},
MatchExpressions: nil,
},
}
request := v1beta1.AdmissionRequest{
Kind: metav1.GroupVersionKind{Kind: "ConfigMap"},
}
objectByteArray := []byte(`{"metadata":{"name":"test-config-map","namespace":"default","creationTimestamp":null,"labels":{"label1":"test1","label2":"test2"}}}`)
request.Object.Raw = objectByteArray
assertEq(t, true, webhooks.IsRuleApplicableToRequest(resource, &request))
objectByteArray = []byte(`{"metadata":{"name":"test-config-map","namespace":"default","creationTimestamp":null,"labels":{"label3":"test1","label2":"test2"}}}`)
request.Object.Raw = objectByteArray
assertEq(t, false, webhooks.IsRuleApplicableToRequest(resource, &request))
resource = types.PolicyResource {
Kind: "ConfigMap",
Selector: &metav1.LabelSelector {
MatchLabels: map[string]string {
"label3" : "test1",
"label2" : "test2",
},
MatchExpressions: nil,
},
}
assertEq(t, true, webhooks.IsRuleApplicableToRequest(resource, &request))
// TODO: MatchExpressions tests should be done
}

View file

@ -37,7 +37,7 @@ func (mw *MutationWebhook) Mutate(request *v1beta1.AdmissionRequest, policies []
}
for ruleIdx, rule := range policy.Spec.Rules {
if IsRuleApplicableToRequest(rule, request) {
if IsRuleApplicableToRequest(rule.Resource, request) {
mw.logger.Printf("Applying policy %v, rule index = %v", policy.ObjectMeta.Name, ruleIdx)
rulePatches, err := mw.applyPolicyRule(request, rule)
/*
@ -82,7 +82,7 @@ func (mw *MutationWebhook) applyPolicyRule(request *v1beta1.AdmissionRequest, ru
}
if rule.Patches != nil {
for _, patch := range *rule.Patches {
for _, patch := range rule.Patches {
allPatches = append(allPatches, patch)
}
}