diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 49332c7646..908a95c1a9 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -1,6 +1,8 @@ package engine import ( + "errors" + jsonpatch "github.com/evanphx/json-patch" "github.com/golang/glog" types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" @@ -45,11 +47,13 @@ func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) return nil, err } // Validation - vruleInfos, err := Validate(*policy, rawResource, *res.Gvk) - policyInfo.AddRuleInfos(vruleInfos) - if err != nil { - return nil, err + response := Validate(*policy, rawResource, *res.Gvk) + if response != nil { + policyInfo.AddRuleInfos(response.RuleInfos) + } else { + return nil, errors.New("Failed to process validate rule, error parsing rawResource") } + if res.Gvk.Kind == "Namespace" { // Generation @@ -61,7 +65,10 @@ func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) } func mutation(p *types.Policy, rawResource []byte, gvk *metav1.GroupVersionKind) ([]*info.RuleInfo, error) { - patches, _, ruleInfos := Mutate(*p, rawResource, *gvk) + response := Mutate(*p, rawResource, *gvk) + patches := response.Patches + ruleInfos := response.RuleInfos + if len(ruleInfos) == 0 { // no rules were processed return nil, nil diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index a518e9fb4f..cc5d097a21 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -8,7 +8,7 @@ import ( ) // Mutate performs mutation. Overlay first and then mutation patches -func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([][]byte, []byte, []*info.RuleInfo) { +func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) *EngineResponse { var allPatches, rulePatches [][]byte var err error var errs []error @@ -69,5 +69,9 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio ris = append(ris, ri) } - return allPatches, patchedDocument, ris + return &EngineResponse{ + Patches: allPatches, + PatchedDocument: patchedDocument, + RuleInfos: ris, + } } diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index ecb03f2eac..d2700de56b 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -12,6 +12,7 @@ import ( types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1" client "github.com/nirmata/kyverno/pkg/dclient" + "github.com/nirmata/kyverno/pkg/info" "github.com/nirmata/kyverno/pkg/utils" v1helper "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -20,6 +21,12 @@ import ( "k8s.io/apimachinery/pkg/labels" ) +type EngineResponse struct { + Patches [][]byte + PatchedDocument []byte + RuleInfos []*info.RuleInfo +} + //ListResourcesThatApplyToPolicy returns list of resources that are filtered by policy rules func ListResourcesThatApplyToPolicy(client *client.Client, policy *types.Policy, filterK8Resources []utils.K8Resource) map[string]resourceInfo { // key uid diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 92a98e2451..0637dca710 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -17,13 +17,13 @@ import ( // Validate handles validating admission request // Checks the target resources for rules defined in the policy -func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) ([]*info.RuleInfo, error) { +func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) *EngineResponse { var resource interface{} ris := []*info.RuleInfo{} - err := json.Unmarshal(rawResource, &resource) - if err != nil { - return nil, err + if err := json.Unmarshal(rawResource, &resource); err != nil { + glog.Errorf("Failed to parse rawResource err: %v\n", err) + return nil } for _, rule := range policy.Spec.Rules { @@ -49,7 +49,9 @@ func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVers ris = append(ris, ri) } - return ris, nil + return &EngineResponse{ + RuleInfos: ris, + } } // validateResourceWithPattern is a start of element-by-element validation process diff --git a/pkg/engine/validation_test.go b/pkg/engine/validation_test.go index de1117ebf2..5a73e87dcb 100644 --- a/pkg/engine/validation_test.go +++ b/pkg/engine/validation_test.go @@ -1573,8 +1573,8 @@ func TestValidate_ServiceTest(t *testing.T) { gvk := metav1.GroupVersionKind{ Kind: "Service", } - _, err := Validate(policy, rawResource, gvk) - assert.Assert(t, err == nil) + res := Validate(policy, rawResource, gvk) + assert.Assert(t, res != nil) } func TestValidate_MapHasFloats(t *testing.T) { @@ -1672,6 +1672,6 @@ func TestValidate_MapHasFloats(t *testing.T) { Kind: "Deployment", } - _, err := Validate(policy, rawResource, gvk) - assert.NilError(t, err) + res := Validate(policy, rawResource, gvk) + assert.Assert(t, res != nil) } diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go index 41f78e177b..d0633793ec 100644 --- a/pkg/webhooks/mutation.go +++ b/pkg/webhooks/mutation.go @@ -9,14 +9,13 @@ import ( ) // HandleMutation handles mutating webhook admission request -func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, [][]byte, []byte) { - var allPatches, policyPatches [][]byte +func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool, *engine.EngineResponse) { + var allPatches [][]byte policyInfos := []*info.PolicyInfo{} - var ruleInfos []*info.RuleInfo - patchedDocument := request.Object.Raw + engineResponse := &engine.EngineResponse{PatchedDocument: request.Object.Raw} if request.Operation == v1beta1.Delete { - return true, nil, patchedDocument + return true, engineResponse } glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s", @@ -27,7 +26,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool // Unable to connect to policy Lister to access policies glog.Errorln("Unable to connect to policy controller to access policies. Mutation Rules are NOT being applied") glog.Warning(err) - return true, nil, patchedDocument + return true, engineResponse } rname := engine.ParseNameFromObject(request.Object.Raw) @@ -59,13 +58,12 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - policyPatches, patchedDocument, ruleInfos = engine.Mutate(*policy, patchedDocument, request.Kind) - - policyInfo.AddRuleInfos(ruleInfos) + engineResponse = engine.Mutate(*policy, engineResponse.PatchedDocument, request.Kind) + policyInfo.AddRuleInfos(engineResponse.RuleInfos) if !policyInfo.IsSuccessful() { glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - for _, r := range ruleInfos { + for _, r := range engineResponse.RuleInfos { glog.Warningf("%s: %s\n", r.Name, r.Msgs) } } else { @@ -74,7 +72,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool if err != nil { glog.Info(err) } - allPatches = append(allPatches, policyPatches...) + allPatches = append(allPatches, engineResponse.Patches...) glog.Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rns, rname) } policyInfos = append(policyInfos, policyInfo) @@ -93,9 +91,10 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) (bool ok, msg := isAdmSuccesful(policyInfos) if ok { - return true, allPatches, patchedDocument + engineResponse.Patches = allPatches + return true, engineResponse } glog.Errorf("Failed to mutate the resource: %s\n", msg) - return false, nil, patchedDocument + return false, engineResponse } diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index b166054355..393a6bf1f1 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -140,7 +140,7 @@ func (ws *WebhookServer) serve(w http.ResponseWriter, r *http.Request) { func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { var response *v1beta1.AdmissionResponse - allowed, allPatches, patchedDocument := ws.HandleMutation(request) + allowed, engineResponse := ws.HandleMutation(request) if !allowed { // TODO: add failure message to response return &v1beta1.AdmissionResponse{ @@ -148,10 +148,10 @@ func (ws *WebhookServer) HandleAdmissionRequest(request *v1beta1.AdmissionReques } } - response = ws.HandleValidation(request, patchedDocument) - if response.Allowed && len(allPatches) > 0 { + response = ws.HandleValidation(request, engineResponse.PatchedDocument) + if response.Allowed && len(engineResponse.Patches) > 0 { patchType := v1beta1.PatchTypeJSONPatch - response.Patch = engine.JoinPatches(allPatches) + response.Patch = engine.JoinPatches(engineResponse.Patches) response.PatchType = &patchType } diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go index abeca7f792..f0cecab966 100644 --- a/pkg/webhooks/validation.go +++ b/pkg/webhooks/validation.go @@ -57,19 +57,16 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, raw request.Kind.Kind, rns, rname, request.UID, request.Operation) glog.Infof("Validating resource %s/%s/%s with policy %s with %d rules", rkind, rns, rname, policy.ObjectMeta.Name, len(policy.Spec.Rules)) - ruleInfos, err := engine.Validate(*policy, rawResource, request.Kind) - if err != nil { - // This is not policy error - // but if unable to parse request raw resource - // TODO : create event ? dont think so - glog.Error(err) + engineResponse := engine.Validate(*policy, rawResource, request.Kind) + if engineResponse == nil { + glog.Errorln("Failed to process validate rule, error parsing rawResource") continue } - policyInfo.AddRuleInfos(ruleInfos) + policyInfo.AddRuleInfos(engineResponse.RuleInfos) if !policyInfo.IsSuccessful() { glog.Infof("Failed to apply policy %s on resource %s/%s", policy.Name, rname, rns) - for _, r := range ruleInfos { + for _, r := range engineResponse.RuleInfos { glog.Warningf("%s: %s\n", r.Name, r.Msgs) } } else { @@ -79,7 +76,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, raw glog.Info(err) } - if len(ruleInfos) > 0 { + if len(engineResponse.RuleInfos) > 0 { glog.Infof("Validation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, rname, rns) } } diff --git a/pkg/webhooks/webhookManager.go b/pkg/webhooks/webhookManager.go index 5f8ec0a760..37b0c50ce3 100644 --- a/pkg/webhooks/webhookManager.go +++ b/pkg/webhooks/webhookManager.go @@ -26,14 +26,13 @@ func (ws *WebhookServer) manageWebhookConfigurations(policy v1alpha1.Policy, op } func (ws *WebhookServer) registerWebhookConfigurations(policy v1alpha1.Policy) error { - for _, rule := range policy.Spec.Rules { - if rule.Mutation != nil && !ws.webhookRegistrationClient.MutationRegistered.IsSet() { - if err := ws.webhookRegistrationClient.RegisterMutatingWebhook(); err != nil { - return err - } - glog.Infof("Mutating webhook registered") + if !ws.webhookRegistrationClient.MutationRegistered.IsSet() { + if err := ws.webhookRegistrationClient.RegisterMutatingWebhook(); err != nil { + return err } + glog.Infof("Mutating webhook registered") } + return nil }