From 2cdeac59883fcd03d75cb91bcda6e8a7f7b17a36 Mon Sep 17 00:00:00 2001
From: shivkumar dudhani <shivkumar@nirmata.com>
Date: Fri, 9 Aug 2019 19:12:50 -0700
Subject: [PATCH] start creation policy violation

---
 main.go                        | 15 ++++---
 pkg/policyviolation/helpers.go |  2 +
 pkg/webhooks/mutation.go       |  3 ++
 pkg/webhooks/report.go         | 75 ++++++++++++++++++++++++++++++++++
 pkg/webhooks/server.go         |  4 ++
 pkg/webhooks/validation.go     |  5 ++-
 6 files changed, 94 insertions(+), 10 deletions(-)

diff --git a/main.go b/main.go
index 6e3cab4839..b9e1d34bc8 100644
--- a/main.go
+++ b/main.go
@@ -31,13 +31,6 @@ func main() {
 		glog.Fatalf("Error building kubeconfig: %v\n", err)
 	}
 
-	// DYNAMIC CLIENT
-	// - client for all registered resources
-	client, err := client.NewClient(clientConfig)
-	if err != nil {
-		glog.Fatalf("Error creating client: %v\n", err)
-	}
-
 	// KYVENO CRD CLIENT
 	// access CRD resources
 	//		- Policy
@@ -46,6 +39,12 @@ func main() {
 	if err != nil {
 		glog.Fatalf("Error creating client: %v\n", err)
 	}
+	// DYNAMIC CLIENT
+	// - client for all registered resources
+	client, err := client.NewClient(clientConfig)
+	if err != nil {
+		glog.Fatalf("Error creating client: %v\n", err)
+	}
 
 	// KYVERNO CRD INFORMER
 	// watches CRD resources:
@@ -78,7 +77,7 @@ func main() {
 	if err != nil {
 		glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err)
 	}
-	server, err := webhooks.NewWebhookServer(client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), egen, filterK8Resources)
+	server, err := webhooks.NewWebhookServer(pclient, client, tlsPair, pInformer.Kyverno().V1alpha1().Policies(), egen, filterK8Resources)
 	if err != nil {
 		glog.Fatalf("Unable to create webhook server: %v\n", err)
 	}
diff --git a/pkg/policyviolation/helpers.go b/pkg/policyviolation/helpers.go
index 69d8d85ae4..ee3aa94bf2 100644
--- a/pkg/policyviolation/helpers.go
+++ b/pkg/policyviolation/helpers.go
@@ -13,5 +13,7 @@ func BuildPolicyViolation(policy string, resource kyverno.ResourceSpec, fRules [
 			ViolatedRules: fRules,
 		},
 	}
+	//TODO: check if this can be removed or use unstructured?
+	// pv.Kind = "PolicyViolation"
 	return pv
 }
diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go
index 5fd48d198c..99447f85e6 100644
--- a/pkg/webhooks/mutation.go
+++ b/pkg/webhooks/mutation.go
@@ -14,6 +14,8 @@ import (
 func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
 	var patches [][]byte
 	var policyInfos []info.PolicyInfo
+	// map to store the mutation changes on the resource
+	// mAnn := map[string]string{}
 	glog.V(4).Infof("Receive request in mutating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
 		request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
 
@@ -61,6 +63,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
 			}
 			continue
 		}
+		// build annotations per policy being applied to show the mutation changes
 		patches = append(patches, policyPatches...)
 		glog.V(4).Infof("Mutation from policy %s has applied succesfully to %s %s/%s", policy.Name, request.Kind.Kind, resource.GetNamespace(), resource.GetName())
 	}
diff --git a/pkg/webhooks/report.go b/pkg/webhooks/report.go
index f0e171e1d8..1f6b93087f 100644
--- a/pkg/webhooks/report.go
+++ b/pkg/webhooks/report.go
@@ -1,14 +1,18 @@
 package webhooks
 
 import (
+	"reflect"
 	"strings"
 
 	"github.com/nirmata/kyverno/pkg/annotations"
+	kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
+	kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned"
 	"github.com/nirmata/kyverno/pkg/violation"
 
 	"github.com/golang/glog"
 	"github.com/nirmata/kyverno/pkg/event"
 	"github.com/nirmata/kyverno/pkg/info"
+	"github.com/nirmata/kyverno/pkg/policyviolation"
 )
 
 //TODO: change validation from bool -> enum(validation, mutation)
@@ -75,3 +79,74 @@ func addAnnotationsToResource(rawResource []byte, pi *info.PolicyInfo, ruleType
 	}
 	return patch
 }
+
+//buildAnnotation we add annotations for the successful application of JSON patches
+//TODO
+func buildAnnotation(mAnn map[string]string, pi *info.PolicyInfo) {
+	if len(pi.Rules) == 0 {
+		return
+	}
+	var mchanges []string
+
+	for _, r := range pi.Rules {
+		if r.Changes != "" {
+			// the rule generate a patch
+			// key policy name will be updated to right format during creation of annotations
+			mchanges = append(mchanges)
+		}
+	}
+}
+
+// buildPolicyViolationsForAPolicy returns a policy violation object if there are any rules that fail
+func buildPolicyViolationsForAPolicy(pi info.PolicyInfo) kyverno.PolicyViolation {
+	var fRules []kyverno.ViolatedRule
+	var pv kyverno.PolicyViolation
+	for _, r := range pi.Rules {
+		if !r.IsSuccessful() {
+			fRules = append(fRules, kyverno.ViolatedRule{Name: r.Name, Message: r.GetErrorString(), Type: r.RuleType.String()})
+		}
+	}
+	if len(fRules) > 0 {
+		glog.V(4).Infof("building policy violation for policy %s on resource %s/%s/%s", pi.Name, pi.RKind, pi.RNamespace, pi.RName)
+		// there is an error
+		pv = policyviolation.BuildPolicyViolation(pi.Name, kyverno.ResourceSpec{
+			Kind:      pi.RKind,
+			Namespace: pi.RNamespace,
+			Name:      pi.RName,
+		},
+			fRules,
+		)
+
+	}
+	return pv
+}
+
+//generatePolicyViolations generate policyViolation resources for the rules that failed
+func generatePolicyViolations(client *kyvernoclient.Clientset, policyInfos []info.PolicyInfo) {
+	var pvs []kyverno.PolicyViolation
+	for _, policyInfo := range policyInfos {
+		if !policyInfo.IsSuccessful() {
+			if pv := buildPolicyViolationsForAPolicy(policyInfo); !reflect.DeepEqual(pv, kyverno.PolicyViolation{}) {
+				pvs = append(pvs, pv)
+			}
+		}
+	}
+
+	if len(pvs) > 0 {
+		for _, newPv := range pvs {
+			// generate PolicyViolation objects
+			glog.V(4).Infof("creating policyViolation resource for policy %s and resource %s/%s/%s", newPv.Spec.Policy, newPv.Spec.Kind, newPv.Spec.Namespace, newPv.Spec.Name)
+
+			// check if there was a previous violation for policy & resource combination
+			//TODO: check for existing ov using label selectors on resource and policy
+			// curPv, err:= client.KyvernoV1alpha1().PolicyViolations().
+
+			// _, err := client.KyvernoV1alpha1().PolicyViolations().Create(&newPv)
+			// // _, err := client.CreateResource("PolicyViolation", "", &pv, false)
+			// if err != nil {
+			// 	glog.Error(err)
+			// 	continue
+			// }
+		}
+	}
+}
diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go
index cd36edf8d2..5ef509f639 100644
--- a/pkg/webhooks/server.go
+++ b/pkg/webhooks/server.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/golang/glog"
+	kyvernoclient "github.com/nirmata/kyverno/pkg/clientNew/clientset/versioned"
 	informer "github.com/nirmata/kyverno/pkg/clientNew/informers/externalversions/kyverno/v1alpha1"
 	lister "github.com/nirmata/kyverno/pkg/clientNew/listers/kyverno/v1alpha1"
 	"github.com/nirmata/kyverno/pkg/config"
@@ -26,6 +27,7 @@ import (
 type WebhookServer struct {
 	server            http.Server
 	client            *client.Client
+	kyvernoClient     *kyvernoclient.Clientset
 	pLister           lister.PolicyLister
 	eventGen          event.Interface
 	filterK8Resources []utils.K8Resource
@@ -34,6 +36,7 @@ type WebhookServer struct {
 // NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
 // Policy Controller and Kubernetes Client should be initialized in configuration
 func NewWebhookServer(
+	kyvernoClient *kyvernoclient.Clientset,
 	client *client.Client,
 	tlsPair *tlsutils.TlsPemPair,
 	pInformer informer.PolicyInformer,
@@ -53,6 +56,7 @@ func NewWebhookServer(
 
 	ws := &WebhookServer{
 		client:            client,
+		kyvernoClient:     kyvernoClient,
 		pLister:           pInformer.Lister(),
 		eventGen:          eventGen,
 		filterK8Resources: utils.ParseKinds(filterK8Resources),
diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go
index 75f12ffa2d..23e733e2eb 100644
--- a/pkg/webhooks/validation.go
+++ b/pkg/webhooks/validation.go
@@ -84,8 +84,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
 		ws.eventGen.Add(eventsInfo...)
 	}
 
-	// ADD POLICY VIOLATIONS
-
 	ok, msg := isAdmSuccesful(policyInfos)
 	if !ok && toBlock(policyInfos) {
 		return &v1beta1.AdmissionResponse{
@@ -96,6 +94,9 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
 		}
 	}
 
+	// ADD POLICY VIOLATIONS
+	generatePolicyViolations(ws.kyvernoClient, policyInfos)
+
 	return &v1beta1.AdmissionResponse{
 		Allowed: true,
 	}