1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

move webhooks/patches.go webhooks/utils.go to pkg/policymanager/

This commit is contained in:
shuting 2019-05-07 16:50:39 -07:00
parent 2b4ac9d07b
commit 0c744db0e9
7 changed files with 96 additions and 83 deletions

View file

@ -1,4 +1,4 @@
package webhooks
package policymanager
import (
"encoding/json"
@ -20,6 +20,15 @@ const (
type PatchBytes []byte
func GetPolicyPatchingSets(policy types.Policy) PatchingSets {
// failurePolicy property is the only available way for now to define behavior on patching error.
// TODO: define new failurePolicy values specific for patching and other policy features.
if policy.Spec.FailurePolicy != nil && *policy.Spec.FailurePolicy == "continueOnError" {
return PatchingSetsContinueAlways
}
return PatchingSetsDefault
}
// Test patches on given document according to given sets.
// Returns array from separate patches that can be applied to the document
// Returns error ONLY in case when creation of resource should be denied.
@ -27,7 +36,6 @@ func ProcessPatches(patches []types.PolicyPatch, originalDocument []byte, sets P
if len(originalDocument) == 0 {
return nil, errors.New("Source document for patching is empty")
}
var appliedPatches []PatchBytes
patchedDocument := originalDocument
for _, patch := range patches {

View file

@ -1,10 +1,9 @@
package webhooks_test
package policymanager
import (
"gotest.tools/assert"
"testing"
"github.com/nirmata/kube-policy/webhooks"
"gotest.tools/assert"
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
)
@ -36,7 +35,7 @@ const endpointsDocument string = `{
func TestProcessPatches_EmptyPatches(t *testing.T) {
var empty []types.PolicyPatch
patches, err := webhooks.ProcessPatches(empty, []byte(endpointsDocument), webhooks.PatchingSetsDefault)
patches, err := ProcessPatches(empty, []byte(endpointsDocument), PatchingSetsDefault)
assert.NilError(t, err)
assert.Assert(t, len(patches) == 0)
}
@ -52,13 +51,13 @@ func makeAddIsMutatedLabelPatch() types.PolicyPatch {
func TestProcessPatches_EmptyDocument(t *testing.T) {
var patches []types.PolicyPatch
patches = append(patches, makeAddIsMutatedLabelPatch())
patchesBytes, err := webhooks.ProcessPatches(patches, nil, webhooks.PatchingSetsDefault)
patchesBytes, err := ProcessPatches(patches, nil, PatchingSetsDefault)
assert.Assert(t, err != nil)
assert.Assert(t, len(patchesBytes) == 0)
}
func TestProcessPatches_AllEmpty(t *testing.T) {
patchesBytes, err := webhooks.ProcessPatches(nil, nil, webhooks.PatchingSetsDefault)
patchesBytes, err := ProcessPatches(nil, nil, PatchingSetsDefault)
assert.Assert(t, err != nil)
assert.Assert(t, len(patchesBytes) == 0)
}
@ -67,7 +66,7 @@ func TestProcessPatches_AddPathDoesntExist_StopOnError(t *testing.T) {
patch := makeAddIsMutatedLabelPatch()
patch.Path = "/metadata/additional/is-mutated"
patches := []types.PolicyPatch{patch}
patchesBytes, err := webhooks.ProcessPatches(patches, []byte(endpointsDocument), webhooks.PatchingSetsStopOnError)
patchesBytes, err := ProcessPatches(patches, []byte(endpointsDocument), PatchingSetsStopOnError)
assert.Assert(t, err != nil)
assert.Assert(t, len(patchesBytes) == 0)
}
@ -76,7 +75,7 @@ func TestProcessPatches_AddPathDoesntExist_ContinueOnError(t *testing.T) {
patch := makeAddIsMutatedLabelPatch()
patch.Path = "/metadata/additional/is-mutated"
patches := []types.PolicyPatch{patch}
patchesBytes, err := webhooks.ProcessPatches(patches, []byte(endpointsDocument), webhooks.PatchingSetsContinueAlways)
patchesBytes, err := ProcessPatches(patches, []byte(endpointsDocument), PatchingSetsContinueAlways)
assert.NilError(t, err)
assert.Assert(t, len(patchesBytes) == 0)
}
@ -84,7 +83,7 @@ func TestProcessPatches_AddPathDoesntExist_ContinueOnError(t *testing.T) {
func TestProcessPatches_RemovePathDoesntExist_StopOnError(t *testing.T) {
patch := types.PolicyPatch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
patches := []types.PolicyPatch{patch}
patchesBytes, err := webhooks.ProcessPatches(patches, []byte(endpointsDocument), webhooks.PatchingSetsStopOnError)
patchesBytes, err := ProcessPatches(patches, []byte(endpointsDocument), PatchingSetsStopOnError)
assert.Assert(t, err != nil)
assert.Assert(t, len(patchesBytes) == 0)
}
@ -93,7 +92,7 @@ func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_EmptyResult(t
patch1 := types.PolicyPatch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
patch2 := types.PolicyPatch{Path: "/spec/labels/label3", Operation: "add", Value: "label3Value"}
patches := []types.PolicyPatch{patch1, patch2}
patchesBytes, err := webhooks.ProcessPatches(patches, []byte(endpointsDocument), webhooks.PatchingSetsContinueAlways)
patchesBytes, err := ProcessPatches(patches, []byte(endpointsDocument), PatchingSetsContinueAlways)
assert.NilError(t, err)
assert.Assert(t, len(patchesBytes) == 0)
}
@ -103,7 +102,7 @@ func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_NotEmptyResul
patch2 := types.PolicyPatch{Path: "/spec/labels/label2", Operation: "remove", Value: "label2Value"}
patch3 := types.PolicyPatch{Path: "/metadata/labels/label3", Operation: "add", Value: "label3Value"}
patches := []types.PolicyPatch{patch1, patch2, patch3}
patchesBytes, err := webhooks.ProcessPatches(patches, []byte(endpointsDocument), webhooks.PatchingSetsContinueAlways)
patchesBytes, err := ProcessPatches(patches, []byte(endpointsDocument), PatchingSetsContinueAlways)
assert.NilError(t, err)
assert.Assert(t, len(patchesBytes) == 1)
assertEqStringAndData(t, `{"path":"/metadata/labels/label3","op":"add","value":"label3Value"}`, patchesBytes[0])
@ -112,7 +111,7 @@ func TestProcessPatches_AddAndRemovePathsDontExist_ContinueOnError_NotEmptyResul
func TestProcessPatches_RemovePathDoesntExist_IgnoreRemoveFailures_EmptyResult(t *testing.T) {
patch := types.PolicyPatch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
patches := []types.PolicyPatch{patch}
patchesBytes, err := webhooks.ProcessPatches(patches, []byte(endpointsDocument), webhooks.PatchingSetsContinueOnRemoveFailure)
patchesBytes, err := ProcessPatches(patches, []byte(endpointsDocument), PatchingSetsContinueOnRemoveFailure)
assert.NilError(t, err)
assert.Assert(t, len(patchesBytes) == 0)
}
@ -121,8 +120,16 @@ func TestProcessPatches_RemovePathDoesntExist_IgnoreRemoveFailures_NotEmptyResul
patch1 := types.PolicyPatch{Path: "/metadata/labels/is-mutated", Operation: "remove"}
patch2 := types.PolicyPatch{Path: "/metadata/labels/label2", Operation: "add", Value: "label2Value"}
patches := []types.PolicyPatch{patch1, patch2}
patchesBytes, err := webhooks.ProcessPatches(patches, []byte(endpointsDocument), webhooks.PatchingSetsContinueOnRemoveFailure)
patchesBytes, err := ProcessPatches(patches, []byte(endpointsDocument), PatchingSetsContinueOnRemoveFailure)
assert.NilError(t, err)
assert.Assert(t, len(patchesBytes) == 1)
assertEqStringAndData(t, `{"path":"/metadata/labels/label2","op":"add","value":"label2Value"}`, patchesBytes[0])
}
// func TestProcessSamePatch_AddAndRemovePathsDontExist_ContinueOnError_EmptyResult(t *testing.T) {
// patch1 := types.PolicyPatch{Path: "/metadata/labels/label3", Operation: "add", Value: "label3Value"}
// patches := []types.PolicyPatch{patch1}
// patchesBytes, err := ProcessPatches(patches, []byte(endpointsDocument), PatchingSetsContinueAlways)
// assert.NilError(t, err)
// assert.Assert(t, len(patchesBytes) == 1)
// }

View file

@ -1,4 +1,4 @@
package webhooks
package policymanager
import (
"encoding/json"
@ -7,21 +7,21 @@ import (
"k8s.io/apimachinery/pkg/labels"
)
func parseMetadataFromObject(bytes []byte) map[string]interface{} {
func ParseMetadataFromObject(bytes []byte) map[string]interface{} {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
return objectJSON["metadata"].(map[string]interface{})
}
func parseKindFromObject(bytes []byte) string {
func ParseKindFromObject(bytes []byte) string {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
return objectJSON["kind"].(string)
}
func parseLabelsFromMetadata(meta map[string]interface{}) labels.Set {
func ParseLabelsFromMetadata(meta map[string]interface{}) labels.Set {
if interfaceMap, ok := meta["labels"].(map[string]interface{}); ok {
labelMap := make(labels.Set, len(interfaceMap))
@ -33,7 +33,7 @@ func parseLabelsFromMetadata(meta map[string]interface{}) labels.Set {
return nil
}
func parseNameFromObject(bytes []byte) string {
func ParseNameFromObject(bytes []byte) string {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
@ -45,7 +45,7 @@ func parseNameFromObject(bytes []byte) string {
return ""
}
func parseNamespaceFromObject(bytes []byte) string {
func ParseNamespaceFromObject(bytes []byte) string {
var objectJSON map[string]interface{}
json.Unmarshal(bytes, &objectJSON)
@ -58,7 +58,7 @@ func parseNamespaceFromObject(bytes []byte) string {
}
// returns true if policyResourceName is a regexp
func parseRegexPolicyResourceName(policyResourceName string) (string, bool) {
func ParseRegexPolicyResourceName(policyResourceName string) (string, bool) {
regex := strings.Split(policyResourceName, "regex:")
if len(regex) == 1 {
return regex[0], false

View file

@ -1,4 +1,4 @@
package webhooks_test
package policymanager
import (
"testing"

View file

@ -0,0 +1,43 @@
package policymanager
import (
"github.com/minio/minio/pkg/wildcard"
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// kind is the type of object being manipulated
// Checks requests kind, name and labels to fit the policy
func IsRuleApplicableToResource(kind string, resourceRaw []byte, policyResource types.PolicyResource) (bool, error) {
if policyResource.Kind != kind {
return false, nil
}
if resourceRaw != nil {
meta := ParseMetadataFromObject(resourceRaw)
name := ParseNameFromObject(resourceRaw)
if policyResource.Name != nil {
if !wildcard.Match(*policyResource.Name, name) {
return false, nil
}
}
if policyResource.Selector != nil {
selector, err := metav1.LabelSelectorAsSelector(policyResource.Selector)
if err != nil {
return false, err
}
labelMap := ParseLabelsFromMetadata(meta)
if !selector.Matches(labelMap) {
return false, nil
}
}
}
return true, nil
}

View file

@ -1,11 +1,10 @@
package webhooks
import (
"github.com/minio/minio/pkg/wildcard"
kubeclient "github.com/nirmata/kube-policy/kubeclient"
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
policymanager "github.com/nirmata/kube-policy/pkg/policymanager"
"k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func kindIsSupported(kind string) bool {
@ -25,41 +24,5 @@ func AdmissionIsRequired(request *v1beta1.AdmissionRequest) bool {
// Checks requests kind, name and labels to fit the policy
func IsRuleApplicableToRequest(policyResource types.PolicyResource, request *v1beta1.AdmissionRequest) (bool, error) {
return IsRuleApplicableToResource(request.Kind.Kind, request.Object.Raw, policyResource)
}
// kind is the type of object being manipulated
// Checks requests kind, name and labels to fit the policy
func IsRuleApplicableToResource(kind string, resourceRaw []byte, policyResource types.PolicyResource) (bool, error) {
if policyResource.Kind != kind {
return false, nil
}
if resourceRaw != nil {
meta := parseMetadataFromObject(resourceRaw)
name := parseNameFromObject(resourceRaw)
if policyResource.Name != nil {
if !wildcard.Match(*policyResource.Name, name) {
return false, nil
}
}
if policyResource.Selector != nil {
selector, err := metav1.LabelSelectorAsSelector(policyResource.Selector)
if err != nil {
return false, err
}
labelMap := parseLabelsFromMetadata(meta)
if !selector.Matches(labelMap) {
return false, nil
}
}
}
return true, nil
return policymanager.IsRuleApplicableToResource(request.Kind.Kind, request.Object.Raw, policyResource)
}

View file

@ -9,6 +9,7 @@ import (
controllerinterfaces "github.com/nirmata/kube-policy/controller/interfaces"
kubeclient "github.com/nirmata/kube-policy/kubeclient"
types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1"
policymanager "github.com/nirmata/kube-policy/pkg/policymanager"
v1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@ -65,7 +66,7 @@ func (mw *MutationWebhook) Mutate(request *v1beta1.AdmissionRequest) *v1beta1.Ad
return nil
}
var allPatches []PatchBytes
var allPatches []policymanager.PatchBytes
for _, policy := range policies {
mw.logger.Printf("Applying policy %s with %d rules", policy.ObjectMeta.Name, len(policy.Spec.Rules))
@ -79,8 +80,8 @@ func (mw *MutationWebhook) Mutate(request *v1beta1.AdmissionRequest) *v1beta1.Ad
}
if len(policyPatches) > 0 {
namespace := parseNamespaceFromObject(request.Object.Raw)
name := parseNameFromObject(request.Object.Raw)
namespace := policymanager.ParseNamespaceFromObject(request.Object.Raw)
name := policymanager.ParseNameFromObject(request.Object.Raw)
mw.controller.LogPolicyInfo(policy.Name, fmt.Sprintf("Applied to %s %s/%s", request.Kind.Kind, namespace, name))
mw.logger.Printf("%s applied to %s %s/%s", policy.Name, request.Kind.Kind, namespace, name)
@ -91,31 +92,22 @@ func (mw *MutationWebhook) Mutate(request *v1beta1.AdmissionRequest) *v1beta1.Ad
patchType := v1beta1.PatchTypeJSONPatch
return &v1beta1.AdmissionResponse{
Allowed: true,
Patch: JoinPatches(allPatches),
Patch: policymanager.JoinPatches(allPatches),
PatchType: &patchType,
}
}
func getPolicyPatchingSets(policy types.Policy) PatchingSets {
// failurePolicy property is the only available way for now to define behavior on patching error.
// TODO: define new failurePolicy values specific for patching and other policy features.
if policy.Spec.FailurePolicy != nil && *policy.Spec.FailurePolicy == "continueOnError" {
return PatchingSetsContinueAlways
}
return PatchingSetsDefault
}
// Applies all policy rules to the created object and returns list of processed JSON patches.
// May return nil patches if it is not necessary to create patches for requested object.
// Returns error ONLY in case when creation of resource should be denied.
func (mw *MutationWebhook) applyPolicyRules(request *v1beta1.AdmissionRequest, policy types.Policy) ([]PatchBytes, error) {
func (mw *MutationWebhook) applyPolicyRules(request *v1beta1.AdmissionRequest, policy types.Policy) ([]policymanager.PatchBytes, error) {
return mw.applyPolicyRulesOnResource(request.Kind.Kind, request.Object.Raw, policy)
}
// kind is the type of object being manipulated
func (mw *MutationWebhook) applyPolicyRulesOnResource(kind string, rawResource []byte, policy types.Policy) ([]PatchBytes, error) {
patchingSets := getPolicyPatchingSets(policy)
var policyPatches []PatchBytes
func (mw *MutationWebhook) applyPolicyRulesOnResource(kind string, rawResource []byte, policy types.Policy) ([]policymanager.PatchBytes, error) {
patchingSets := policymanager.GetPolicyPatchingSets(policy)
var policyPatches []policymanager.PatchBytes
for ruleIdx, rule := range policy.Spec.Rules {
err := rule.Validate()
@ -124,7 +116,7 @@ func (mw *MutationWebhook) applyPolicyRulesOnResource(kind string, rawResource [
continue
}
if ok, err := IsRuleApplicableToResource(kind, rawResource, rule.Resource); !ok {
if ok, err := policymanager.IsRuleApplicableToResource(kind, rawResource, rule.Resource); !ok {
mw.logger.Printf("Rule %d of policy %s is not applicable to the request", ruleIdx, policy.Name)
return nil, err
}
@ -132,12 +124,12 @@ func (mw *MutationWebhook) applyPolicyRulesOnResource(kind string, rawResource [
// configMapGenerator and secretGenerator can be applied only to namespaces
if kind == "Namespace" {
err = mw.applyRuleGenerators(rawResource, rule)
if err != nil && patchingSets == PatchingSetsStopOnError {
if err != nil && patchingSets == policymanager.PatchingSetsStopOnError {
return nil, fmt.Errorf("Failed to apply generators from rule #%d: %s", ruleIdx, err)
}
}
rulePatchesProcessed, err := ProcessPatches(rule.Patches, rawResource, patchingSets)
rulePatchesProcessed, err := policymanager.ProcessPatches(rule.Patches, rawResource, patchingSets)
if err != nil {
return nil, fmt.Errorf("Failed to process patches from rule #%d: %s", ruleIdx, err)
}
@ -160,7 +152,7 @@ func (mw *MutationWebhook) applyPolicyRulesOnResource(kind string, rawResource [
// Applies "configMapGenerator" and "secretGenerator" described in PolicyRule
func (mw *MutationWebhook) applyRuleGenerators(rawResource []byte, rule types.PolicyRule) error {
namespaceName := parseNameFromObject(rawResource)
namespaceName := policymanager.ParseNameFromObject(rawResource)
err := mw.applyConfigGenerator(rule.ConfigMapGenerator, namespaceName, "ConfigMap")
if err == nil {