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:
parent
2b4ac9d07b
commit
0c744db0e9
7 changed files with 96 additions and 83 deletions
|
@ -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 {
|
|
@ -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)
|
||||
// }
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
|||
package webhooks_test
|
||||
package policymanager
|
||||
|
||||
import (
|
||||
"testing"
|
43
pkg/policymanager/validate.go
Normal file
43
pkg/policymanager/validate.go
Normal 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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue