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:
parent
ae952f73ab
commit
68e468a699
9 changed files with 162 additions and 30 deletions
|
@ -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
17
crd/selector-policy.yaml
Normal 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"
|
4
main.go
4
main.go
|
@ -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() {
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
3
scripts/create-test-configmap.sh
Executable file
3
scripts/create-test-configmap.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
cd "$(dirname "$0")"
|
||||
kubectl create -f resources/test-configmap.yaml
|
||||
kubectl delete -f resources/test-configmap.yaml
|
10
scripts/resources/test-configmap.yaml
Normal file
10
scripts/resources/test-configmap.yaml
Normal file
|
@ -0,0 +1,10 @@
|
|||
apiVersion: v1
|
||||
data:
|
||||
properties:
|
||||
arms=3
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-configmap
|
||||
labels:
|
||||
label1: test1
|
||||
label2: test2
|
|
@ -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 ""
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue