mirror of
https://github.com/kyverno/kyverno.git
synced 2025-04-08 18:15:48 +00:00
annotation generation from policy controller
This commit is contained in:
parent
bd9e8585c7
commit
e5f208e303
11 changed files with 405 additions and 107 deletions
11
main.go
11
main.go
|
@ -4,6 +4,7 @@ import (
|
|||
"flag"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/annotations"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
controller "github.com/nirmata/kyverno/pkg/controller"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
|
@ -24,7 +25,6 @@ var (
|
|||
|
||||
func main() {
|
||||
defer glog.Flush()
|
||||
|
||||
printVersionInfo()
|
||||
clientConfig, err := createClientConfig(kubeconfig)
|
||||
if err != nil {
|
||||
|
@ -43,19 +43,20 @@ func main() {
|
|||
kubeInformer := utils.NewKubeInformerFactory(clientConfig)
|
||||
eventController := event.NewEventController(client, policyInformerFactory)
|
||||
violationBuilder := violation.NewPolicyViolationBuilder(client, policyInformerFactory, eventController)
|
||||
|
||||
annotationsController := annotations.NewAnnotationControler(client)
|
||||
policyController := controller.NewPolicyController(
|
||||
client,
|
||||
policyInformerFactory,
|
||||
violationBuilder,
|
||||
eventController)
|
||||
eventController,
|
||||
annotationsController)
|
||||
|
||||
genControler := gencontroller.NewGenController(client, eventController, policyInformerFactory, violationBuilder, kubeInformer.Core().V1().Namespaces())
|
||||
tlsPair, err := initTLSPemPair(clientConfig, client)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err)
|
||||
}
|
||||
server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, eventController, violationBuilder, filterK8Kinds)
|
||||
server, err := webhooks.NewWebhookServer(client, tlsPair, policyInformerFactory, eventController, violationBuilder, annotationsController, filterK8Kinds)
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to create webhook server: %v\n", err)
|
||||
}
|
||||
|
@ -71,6 +72,7 @@ func main() {
|
|||
kubeInformer.Start(stopCh)
|
||||
eventController.Run(stopCh)
|
||||
genControler.Run(stopCh)
|
||||
annotationsController.Run(stopCh)
|
||||
if err = policyController.Run(stopCh); err != nil {
|
||||
glog.Fatalf("Error running PolicyController: %v\n", err)
|
||||
}
|
||||
|
@ -84,6 +86,7 @@ func main() {
|
|||
server.Stop()
|
||||
genControler.Stop()
|
||||
eventController.Stop()
|
||||
annotationsController.Stop()
|
||||
policyController.Stop()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,24 +2,26 @@ package annotations
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"github.com/golang/glog"
|
||||
pinfo "github.com/nirmata/kyverno/pkg/info"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
//Policy information for annotations
|
||||
type Policy struct {
|
||||
Status string `json:"status"`
|
||||
Rules []Rule `json:"rules,omitempty"`
|
||||
// Key Type/Name
|
||||
MutationRules map[string]Rule `json:"mutationrules,omitempty"`
|
||||
ValidationRules map[string]Rule `json:"validationrules,omitempty"`
|
||||
GenerationRules map[string]Rule `json:"generationrules,omitempty"`
|
||||
}
|
||||
|
||||
//Rule information for annotations
|
||||
type Rule struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
Changes string `json:"changes"`
|
||||
}
|
||||
|
||||
|
@ -29,50 +31,117 @@ func getStatus(status bool) string {
|
|||
}
|
||||
return "Failure"
|
||||
}
|
||||
func getRules(rules []*info.RuleInfo) []Rule {
|
||||
var annrules []Rule
|
||||
|
||||
func getRules(rules []*pinfo.RuleInfo, ruleType pinfo.RuleType) map[string]Rule {
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
}
|
||||
annrules := make(map[string]Rule, 0)
|
||||
// var annrules map[string]Rule
|
||||
for _, r := range rules {
|
||||
annrule := Rule{Name: r.Name,
|
||||
Status: getStatus(r.IsSuccessful()),
|
||||
Type: r.RuleType.String()}
|
||||
//TODO: add mutation changes in policyInfo and in annotation
|
||||
annrules = append(annrules, annrule)
|
||||
if r.RuleType != ruleType {
|
||||
continue
|
||||
}
|
||||
annrules[r.Name] =
|
||||
Rule{Status: getStatus(r.IsSuccessful())}
|
||||
// //TODO: add mutation changes in policyInfo and in annotation
|
||||
// annrules = append(annrules, annrule)
|
||||
}
|
||||
return annrules
|
||||
}
|
||||
|
||||
func (p *Policy) updatePolicy(obj *Policy, ruleType info.RuleType) {
|
||||
func (p *Policy) updatePolicy(obj *Policy, ruleType pinfo.RuleType) bool {
|
||||
updates := false
|
||||
if p.Status != obj.Status {
|
||||
updates = true
|
||||
}
|
||||
p.Status = obj.Status
|
||||
p.updatePolicyRules(obj.Rules, ruleType)
|
||||
}
|
||||
|
||||
// Update rules of a given type
|
||||
func (p *Policy) updatePolicyRules(rules []Rule, ruleType info.RuleType) {
|
||||
var updatedRules []Rule
|
||||
//TODO: check the selecting update add advantage
|
||||
// filter rules for different type
|
||||
for _, r := range rules {
|
||||
if r.Type != ruleType.String() {
|
||||
updatedRules = append(updatedRules, r)
|
||||
// Check Mutation rules
|
||||
switch ruleType {
|
||||
case pinfo.Mutation:
|
||||
if p.compareMutationRules(obj.MutationRules) {
|
||||
updates = true
|
||||
}
|
||||
case pinfo.Validation:
|
||||
if p.compareValidationRules(obj.ValidationRules) {
|
||||
updates = true
|
||||
}
|
||||
case pinfo.Generation:
|
||||
if p.compareGenerationRules(obj.GenerationRules) {
|
||||
updates = true
|
||||
}
|
||||
}
|
||||
// Add rules for current type
|
||||
updatedRules = append(updatedRules, rules...)
|
||||
// set the rule
|
||||
p.Rules = updatedRules
|
||||
// If there are any updates then the annotation can be updated, can skip
|
||||
return updates
|
||||
}
|
||||
|
||||
func (p *Policy) compareMutationRules(rules map[string]Rule) bool {
|
||||
// check if the rules have changed
|
||||
if !reflect.DeepEqual(p.MutationRules, rules) {
|
||||
p.MutationRules = rules
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Policy) compareValidationRules(rules map[string]Rule) bool {
|
||||
// check if the rules have changed
|
||||
if !reflect.DeepEqual(p.ValidationRules, rules) {
|
||||
p.ValidationRules = rules
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Policy) compareGenerationRules(rules map[string]Rule) bool {
|
||||
// check if the rules have changed
|
||||
if !reflect.DeepEqual(p.GenerationRules, rules) {
|
||||
p.GenerationRules = rules
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// // Update rules of a given type
|
||||
// func (p *Policy) updatePolicyRules(rules map[string]Rule, ruleType info.RuleType) bool {
|
||||
// updates := false
|
||||
// // Check if new rules are present in existing rules
|
||||
// // for k, v := range rules {
|
||||
// // _,
|
||||
// // }
|
||||
|
||||
// var updatedRules []Rule
|
||||
// //TODO: check the selecting update add advantage
|
||||
// // filter rules for different type
|
||||
// for _, r := range rules {
|
||||
// if r.Type != ruleType.String() {
|
||||
// updatedRules = append(updatedRules, r)
|
||||
// }
|
||||
// }
|
||||
// // Add rules for current type
|
||||
// updatedRules = append(updatedRules, rules...)
|
||||
// // set the rule
|
||||
// p.Rules = updatedRules
|
||||
// }
|
||||
|
||||
// func (p *Policy) containsPolicyRules(rules []Rule, ruleType info.RuleType) {
|
||||
// for _, r := range rules {
|
||||
// }
|
||||
// }
|
||||
func newAnnotationForPolicy(pi *info.PolicyInfo) *Policy {
|
||||
|
||||
func newAnnotationForPolicy(pi *pinfo.PolicyInfo) *Policy {
|
||||
return &Policy{Status: getStatus(pi.IsSuccessful()),
|
||||
Rules: getRules(pi.Rules)}
|
||||
MutationRules: getRules(pi.Rules, pinfo.Mutation),
|
||||
ValidationRules: getRules(pi.Rules, pinfo.Validation),
|
||||
GenerationRules: getRules(pi.Rules, pinfo.Generation),
|
||||
}
|
||||
}
|
||||
|
||||
//AddPolicy will add policy annotation if not present or update if present
|
||||
func AddPolicy(obj *unstructured.Unstructured, pi *info.PolicyInfo, ruleType info.RuleType) error {
|
||||
// modifies obj
|
||||
// returns true, if there is any update -> caller need to update the obj
|
||||
// returns false, if there is no change -> caller can skip the update
|
||||
func AddPolicy(obj *unstructured.Unstructured, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) bool {
|
||||
PolicyObj := newAnnotationForPolicy(pi)
|
||||
// get annotation
|
||||
ann := obj.GetAnnotations()
|
||||
|
@ -81,41 +150,50 @@ func AddPolicy(obj *unstructured.Unstructured, pi *info.PolicyInfo, ruleType inf
|
|||
if !ok {
|
||||
PolicyByte, err := json.Marshal(PolicyObj)
|
||||
if err != nil {
|
||||
return err
|
||||
glog.Error(err)
|
||||
return false
|
||||
}
|
||||
// insert policy information
|
||||
ann[pi.Name] = string(PolicyByte)
|
||||
// set annotation back to unstr
|
||||
obj.SetAnnotations(ann)
|
||||
return nil
|
||||
return true
|
||||
}
|
||||
cPolicyObj := Policy{}
|
||||
err := json.Unmarshal([]byte(cPolicy), &cPolicyObj)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// update policy information inside the annotation
|
||||
// 1> policy status
|
||||
// 2> rule (name, status,changes,type)
|
||||
cPolicyObj.updatePolicy(PolicyObj, ruleType)
|
||||
if err != nil {
|
||||
return err
|
||||
// 2> Mutation, Validation, Generation
|
||||
if cPolicyObj.updatePolicy(PolicyObj, ruleType) {
|
||||
cPolicyByte, err := json.Marshal(cPolicyObj)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// update policy information
|
||||
ann[pi.Name] = string(cPolicyByte)
|
||||
// set annotation back to unstr
|
||||
obj.SetAnnotations(ann)
|
||||
return true
|
||||
}
|
||||
cPolicyByte, err := json.Marshal(cPolicyObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// update policy information
|
||||
ann[pi.Name] = string(cPolicyByte)
|
||||
// set annotation back to unstr
|
||||
obj.SetAnnotations(ann)
|
||||
return nil
|
||||
return false
|
||||
}
|
||||
|
||||
//RemovePolicy to remove annotations fro
|
||||
func RemovePolicy(obj *unstructured.Unstructured, policy string) {
|
||||
//RemovePolicy to remove annotations
|
||||
// return true -> if there was an entry and we deleted it
|
||||
// return false -> if there is no entry, caller need not update
|
||||
func RemovePolicy(obj *unstructured.Unstructured, policy string) bool {
|
||||
// get annotations
|
||||
ann := obj.GetAnnotations()
|
||||
if _, ok := ann[policy]; !ok {
|
||||
return false
|
||||
}
|
||||
delete(ann, policy)
|
||||
// set annotation back to unstr
|
||||
obj.SetAnnotations(ann)
|
||||
return true
|
||||
}
|
||||
|
||||
//ParseAnnotationsFromObject extracts annotations from the JSON obj
|
||||
|
@ -134,7 +212,7 @@ func ParseAnnotationsFromObject(bytes []byte) map[string]string {
|
|||
}
|
||||
|
||||
//AddPolicyJSONPatch generate JSON Patch to add policy informatino JSON patch
|
||||
func AddPolicyJSONPatch(ann map[string]string, pi *info.PolicyInfo, ruleType info.RuleType) ([]byte, error) {
|
||||
func AddPolicyJSONPatch(ann map[string]string, pi *pinfo.PolicyInfo, ruleType pinfo.RuleType) (map[string]string, []byte, error) {
|
||||
if ann == nil {
|
||||
ann = make(map[string]string, 0)
|
||||
}
|
||||
|
@ -143,12 +221,13 @@ func AddPolicyJSONPatch(ann map[string]string, pi *info.PolicyInfo, ruleType inf
|
|||
if !ok {
|
||||
PolicyByte, err := json.Marshal(PolicyObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
// insert policy information
|
||||
ann[pi.Name] = string(PolicyByte)
|
||||
// create add JSON patch
|
||||
return createAddJSONPatch(ann)
|
||||
jsonPatch, err := createAddJSONPatch(ann)
|
||||
return ann, jsonPatch, err
|
||||
}
|
||||
cPolicyObj := Policy{}
|
||||
err := json.Unmarshal([]byte(cPolicy), &cPolicyObj)
|
||||
|
@ -157,28 +236,31 @@ func AddPolicyJSONPatch(ann map[string]string, pi *info.PolicyInfo, ruleType inf
|
|||
// 2> rule (name, status,changes,type)
|
||||
cPolicyObj.updatePolicy(PolicyObj, ruleType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
cPolicyByte, err := json.Marshal(cPolicyObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
// update policy information
|
||||
ann[pi.Name] = string(cPolicyByte)
|
||||
// create update JSON patch
|
||||
return createReplaceJSONPatch(ann)
|
||||
jsonPatch, err := createReplaceJSONPatch(ann)
|
||||
return ann, jsonPatch, err
|
||||
}
|
||||
|
||||
//RemovePolicyJSONPatch remove JSON patch
|
||||
func RemovePolicyJSONPatch(ann map[string]string, policy string) ([]byte, error) {
|
||||
func RemovePolicyJSONPatch(ann map[string]string, policy string) (map[string]string, []byte, error) {
|
||||
if ann == nil {
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
delete(ann, policy)
|
||||
if len(ann) == 0 {
|
||||
return createRemoveJSONPatch(ann)
|
||||
jsonPatch, err := createRemoveJSONPatch(ann)
|
||||
return nil, jsonPatch, err
|
||||
}
|
||||
return createReplaceJSONPatch(ann)
|
||||
jsonPatch, err := createReplaceJSONPatch(ann)
|
||||
return ann, jsonPatch, err
|
||||
}
|
||||
|
||||
type patchMapValue struct {
|
||||
|
@ -195,6 +277,7 @@ func createRemoveJSONPatch(ann map[string]string) ([]byte, error) {
|
|||
return json.Marshal(payload)
|
||||
|
||||
}
|
||||
|
||||
func createAddJSONPatch(ann map[string]string) ([]byte, error) {
|
||||
if ann == nil {
|
||||
ann = make(map[string]string, 0)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestAddPatch(t *testing.T) {
|
||||
// Create
|
||||
objRaw := []byte(`{"kind":"Deployment","apiVersion":"apps/v1","metadata":{"name":"nginx-deployment","namespace":"default","creationTimestamp":null,"labels":{"app":"nginx"}},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"nginx"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"nginx"}},"spec":{"containers":[{"name":"nginx","image":"nginx:latest","ports":[{"containerPort":80,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"},{"name":"ghost","image":"ghost:latest","resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"Always"}],"restartPolicy":"Always","terminationGracePeriodSeconds":30,"dnsPolicy":"ClusterFirst","securityContext":{},"schedulerName":"default-scheduler"}},"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxUnavailable":"25%","maxSurge":"25%"}},"revisionHistoryLimit":10,"progressDeadlineSeconds":600},"status":{}}`)
|
||||
piRaw := []byte(`{"Name":"set-image-pull-policy","RKind":"Deployment","RName":"nginx-deployment","RNamespace":"default","ValidationFailureAction":"","Rules":[{"Name":"nginx-deployment","Msgs":["Rule nginx-deployment: Overlay succesfully applied."],"RuleType":0}]}`)
|
||||
ann := ParseAnnotationsFromObject(objRaw)
|
||||
|
@ -17,7 +18,20 @@ func TestAddPatch(t *testing.T) {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
patch, err := AddPolicyJSONPatch(ann, &pi, info.Mutation)
|
||||
ann, patch, err := AddPolicyJSONPatch(ann, &pi, info.Mutation)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(patch))
|
||||
// Update
|
||||
piRaw = []byte(`{"Name":"set-image-pull-policy","RKind":"Deployment","RName":"nginx-deployment","RNamespace":"default","ValidationFailureAction":"","Rules":[{"Name":"nginx-deployment","Msgs":["Rule nginx-deployment1: Overlay succesfully applied."],"RuleType":0}]}`)
|
||||
// ann = ParseAnnotationsFromObject(objRaw)
|
||||
pi = info.PolicyInfo{}
|
||||
err = json.Unmarshal(piRaw, &pi)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ann, patch, err = AddPolicyJSONPatch(ann, &pi, info.Mutation)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
101
pkg/annotations/controller.go
Normal file
101
pkg/annotations/controller.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
package annotations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
type controller struct {
|
||||
client *client.Client
|
||||
queue workqueue.RateLimitingInterface
|
||||
}
|
||||
|
||||
type Interface interface {
|
||||
Add(rkind, rns, rname string, patch []byte)
|
||||
}
|
||||
|
||||
type Controller interface {
|
||||
Interface
|
||||
Run(stopCh <-chan struct{})
|
||||
Stop()
|
||||
}
|
||||
|
||||
func NewAnnotationControler(client *client.Client) Controller {
|
||||
return &controller{
|
||||
client: client,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), annotationQueueName),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) Add(rkind, rns, rname string, patch []byte) {
|
||||
c.queue.Add(newInfo)
|
||||
}
|
||||
|
||||
func (c *controller) Run(stopCh <-chan struct{}) {
|
||||
defer utilruntime.HandleCrash()
|
||||
for i := 0; i < workerThreadCount; i++ {
|
||||
go wait.Until(c.runWorker, time.Second, stopCh)
|
||||
}
|
||||
glog.Info("Started annotation controller workers")
|
||||
}
|
||||
|
||||
func (c *controller) Stop() {
|
||||
defer c.queue.ShutDown()
|
||||
glog.Info("Shutting down annotation controller workers")
|
||||
}
|
||||
|
||||
func (c *controller) runWorker() {
|
||||
for c.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) processNextWorkItem() bool {
|
||||
obj, shutdown := c.queue.Get()
|
||||
if shutdown {
|
||||
return false
|
||||
}
|
||||
err := func(obj interface{}) error {
|
||||
defer c.queue.Done(obj)
|
||||
var key info
|
||||
var ok bool
|
||||
if key, ok = obj.(info); !ok {
|
||||
c.queue.Forget(obj)
|
||||
glog.Warningf("Expecting type info by got %v\n", obj)
|
||||
return nil
|
||||
}
|
||||
// Run the syncHandler, passing the resource and the policy
|
||||
if err := c.SyncHandler(key); err != nil {
|
||||
c.queue.AddRateLimited(key)
|
||||
return fmt.Errorf("error syncing '%s/%s/%s' : %s, requeuing annotation creation request", key.RKind, key.RNs, key.RName, err)
|
||||
}
|
||||
return nil
|
||||
}(obj)
|
||||
|
||||
if err != nil {
|
||||
glog.Warning(err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *controller) SyncHandler(key info) error {
|
||||
var err error
|
||||
// check if the resource is created
|
||||
_, err = c.client.GetResource(key.RKind, key.RNs, key.RName)
|
||||
if err != nil {
|
||||
glog.Errorf("Error creating annotation: unable to get resource %s/%s/%s, will retry: %s ", key.RKind, key.RNs, key.RName, err)
|
||||
return err
|
||||
}
|
||||
// if it is patch the resource
|
||||
_, err = c.client.PatchResource(key.RKind, key.RNs, key.RName, key.Patch)
|
||||
if err != nil {
|
||||
glog.Errorf("Error creating annotation: unable to get resource %s/%s/%s, will retry: %s", key.RKind, key.RNs, key.RName, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
20
pkg/annotations/utils.go
Normal file
20
pkg/annotations/utils.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package annotations
|
||||
|
||||
const annotationQueueName = "annotation-queue"
|
||||
const workerThreadCount = 1
|
||||
|
||||
type info struct {
|
||||
RKind string
|
||||
RNs string
|
||||
RName string
|
||||
Patch []byte
|
||||
}
|
||||
|
||||
func newInfo(rkind, rns, rname string, patch []byte) info {
|
||||
return info{
|
||||
RKind: rkind,
|
||||
RNs: rname,
|
||||
RName: rname,
|
||||
Patch: patch,
|
||||
}
|
||||
}
|
|
@ -5,6 +5,9 @@ import (
|
|||
"reflect"
|
||||
"time"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/annotations"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/engine"
|
||||
|
@ -26,27 +29,30 @@ import (
|
|||
|
||||
//PolicyController to manage Policy CRD
|
||||
type PolicyController struct {
|
||||
client *client.Client
|
||||
policyLister lister.PolicyLister
|
||||
policySynced cache.InformerSynced
|
||||
violationBuilder violation.Generator
|
||||
eventController event.Generator
|
||||
queue workqueue.RateLimitingInterface
|
||||
client *client.Client
|
||||
policyLister lister.PolicyLister
|
||||
policySynced cache.InformerSynced
|
||||
violationBuilder violation.Generator
|
||||
eventController event.Generator
|
||||
annotationsController annotations.Controller
|
||||
queue workqueue.RateLimitingInterface
|
||||
}
|
||||
|
||||
// NewPolicyController from cmd args
|
||||
func NewPolicyController(client *client.Client,
|
||||
policyInformer sharedinformer.PolicyInformer,
|
||||
violationBuilder violation.Generator,
|
||||
eventController event.Generator) *PolicyController {
|
||||
eventController event.Generator,
|
||||
annotationsController annotations.Controller) *PolicyController {
|
||||
|
||||
controller := &PolicyController{
|
||||
client: client,
|
||||
policyLister: policyInformer.GetLister(),
|
||||
policySynced: policyInformer.GetInfomer().HasSynced,
|
||||
violationBuilder: violationBuilder,
|
||||
eventController: eventController,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), policyWorkQueueName),
|
||||
client: client,
|
||||
policyLister: policyInformer.GetLister(),
|
||||
policySynced: policyInformer.GetInfomer().HasSynced,
|
||||
violationBuilder: violationBuilder,
|
||||
eventController: eventController,
|
||||
annotationsController: annotationsController,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), policyWorkQueueName),
|
||||
}
|
||||
|
||||
policyInformer.GetInfomer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
|
@ -81,6 +87,7 @@ func (pc *PolicyController) deletePolicyHandler(resource interface{}) {
|
|||
glog.Error("error decoding object, invalid type")
|
||||
return
|
||||
}
|
||||
//TODO: need to clear annotations on the resources
|
||||
glog.Infof("policy deleted: %s", object.GetName())
|
||||
}
|
||||
|
||||
|
@ -190,9 +197,62 @@ func (pc *PolicyController) syncHandler(obj interface{}) error {
|
|||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
|
||||
// add annotations to resources
|
||||
pc.createAnnotations(policyInfos)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) {
|
||||
for _, pi := range policyInfos {
|
||||
var patch []byte
|
||||
//get resource
|
||||
obj, err := pc.client.GetResource(pi.RKind, pi.RNamespace, pi.RName)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
// add annotation for policy application
|
||||
ann := obj.GetAnnotations()
|
||||
// Mutation rules
|
||||
ann, mpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Mutation)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
// Validation rules
|
||||
ann, vpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Validation)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
if mpatch == nil && mpatch == nil {
|
||||
//nothing to patch
|
||||
continue
|
||||
}
|
||||
// merge the patches
|
||||
if mpatch != nil && vpatch != nil {
|
||||
patch, err = jsonpatch.MergePatch(mpatch, vpatch)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if mpatch == nil {
|
||||
patch = vpatch
|
||||
} else {
|
||||
patch = vpatch
|
||||
}
|
||||
|
||||
// add the anotation to the resource
|
||||
_, err = pc.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, patch)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *PolicyController) createEventsAndViolations(policyInfos []*info.PolicyInfo) ([]*event.Info, []*violation.Info) {
|
||||
events := []*event.Info{}
|
||||
violations := []*violation.Info{}
|
||||
|
@ -228,9 +288,6 @@ func (pc *PolicyController) createEventsAndViolations(policyInfos []*info.Policy
|
|||
}
|
||||
|
||||
if !policyInfo.IsSuccessful() {
|
||||
// Event
|
||||
// list of failed rules : ruleNames
|
||||
|
||||
e := event.NewEvent("Policy", "", policyInfo.Name, event.PolicyViolation, event.FResourcePolcy, policyInfo.RNamespace+"/"+policyInfo.RName, concatFailedRules(frules))
|
||||
events = append(events, e)
|
||||
// Violation
|
||||
|
|
|
@ -42,13 +42,12 @@ type Controller interface {
|
|||
func NewEventController(client *client.Client,
|
||||
shareInformer sharedinformer.PolicyInformer) Controller {
|
||||
|
||||
controller := &controller{
|
||||
return &controller{
|
||||
client: client,
|
||||
policyLister: shareInformer.GetLister(),
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), eventWorkQueueName),
|
||||
recorder: initRecorder(client),
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
||||
func initRecorder(client *client.Client) record.EventRecorder {
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
jsonpatch "github.com/evanphx/json-patch"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/annotations"
|
||||
engine "github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/info"
|
||||
v1beta1 "k8s.io/api/admission/v1beta1"
|
||||
|
@ -78,6 +77,7 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
|||
} else {
|
||||
annPatches, err = jsonpatch.MergePatch(annPatches, annPatch)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
fmt.Println("Mergining docs")
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
@ -113,14 +113,3 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest) *v1be
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func addAnnotationsToResource(rawResource []byte, pi *info.PolicyInfo, ruleType info.RuleType) []byte {
|
||||
// get annotations
|
||||
ann := annotations.ParseAnnotationsFromObject(rawResource)
|
||||
patch, err := annotations.AddPolicyJSONPatch(ann, pi, ruleType)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
return patch
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package webhooks
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/nirmata/kyverno/pkg/annotations"
|
||||
"github.com/nirmata/kyverno/pkg/violation"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
@ -60,3 +61,14 @@ func newEventInfoFromPolicyInfo(policyInfoList []*info.PolicyInfo, onUpdate bool
|
|||
}
|
||||
return eventsInfo, violations
|
||||
}
|
||||
|
||||
func addAnnotationsToResource(rawResource []byte, pi *info.PolicyInfo, ruleType info.RuleType) []byte {
|
||||
// get annotations
|
||||
ann := annotations.ParseAnnotationsFromObject(rawResource)
|
||||
ann, patch, err := annotations.AddPolicyJSONPatch(ann, pi, ruleType)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil
|
||||
}
|
||||
return patch
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/nirmata/kyverno/pkg/annotations"
|
||||
"github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
|
||||
"github.com/nirmata/kyverno/pkg/config"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
|
@ -24,12 +25,13 @@ import (
|
|||
// WebhookServer contains configured TLS server with MutationWebhook.
|
||||
// MutationWebhook gets policies from policyController and takes control of the cluster with kubeclient.
|
||||
type WebhookServer struct {
|
||||
server http.Server
|
||||
client *client.Client
|
||||
policyLister v1alpha1.PolicyLister
|
||||
eventController event.Generator
|
||||
violationBuilder violation.Generator
|
||||
filterKinds []string
|
||||
server http.Server
|
||||
client *client.Client
|
||||
policyLister v1alpha1.PolicyLister
|
||||
eventController event.Generator
|
||||
violationBuilder violation.Generator
|
||||
annotationsController annotations.Controller
|
||||
filterKinds []string
|
||||
}
|
||||
|
||||
// NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
|
||||
|
@ -40,6 +42,7 @@ func NewWebhookServer(
|
|||
shareInformer sharedinformer.PolicyInformer,
|
||||
eventController event.Generator,
|
||||
violationBuilder violation.Generator,
|
||||
annotationsController annotations.Controller,
|
||||
filterKinds []string) (*WebhookServer, error) {
|
||||
|
||||
if tlsPair == nil {
|
||||
|
@ -54,11 +57,12 @@ func NewWebhookServer(
|
|||
tlsConfig.Certificates = []tls.Certificate{pair}
|
||||
|
||||
ws := &WebhookServer{
|
||||
client: client,
|
||||
policyLister: shareInformer.GetLister(),
|
||||
eventController: eventController,
|
||||
violationBuilder: violationBuilder,
|
||||
filterKinds: parseKinds(filterKinds),
|
||||
client: client,
|
||||
policyLister: shareInformer.GetLister(),
|
||||
eventController: eventController,
|
||||
violationBuilder: violationBuilder,
|
||||
annotationsController: annotationsController,
|
||||
filterKinds: parseKinds(filterKinds),
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(config.MutatingWebhookServicePath, ws.serve)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package webhooks
|
||||
|
||||
import (
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/golang/glog"
|
||||
engine "github.com/nirmata/kyverno/pkg/engine"
|
||||
"github.com/nirmata/kyverno/pkg/event"
|
||||
|
@ -26,15 +27,16 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
Allowed: true,
|
||||
}
|
||||
}
|
||||
rname := engine.ParseNameFromObject(request.Object.Raw)
|
||||
rns := engine.ParseNamespaceFromObject(request.Object.Raw)
|
||||
rkind := engine.ParseKindFromObject(request.Object.Raw)
|
||||
|
||||
var annPatches []byte
|
||||
for _, policy := range policies {
|
||||
|
||||
if !StringInSlice(request.Kind.Kind, getApplicableKindsForPolicy(policy)) {
|
||||
continue
|
||||
}
|
||||
rname := engine.ParseNameFromObject(request.Object.Raw)
|
||||
rns := engine.ParseNamespaceFromObject(request.Object.Raw)
|
||||
rkind := engine.ParseKindFromObject(request.Object.Raw)
|
||||
|
||||
policyInfo := info.NewPolicyInfo(policy.Name,
|
||||
rkind,
|
||||
|
@ -73,6 +75,17 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
}
|
||||
}
|
||||
policyInfos = append(policyInfos, policyInfo)
|
||||
annPatch := addAnnotationsToResource(request.Object.Raw, policyInfo, info.Mutation)
|
||||
if annPatch != nil {
|
||||
if annPatches == nil {
|
||||
annPatches = annPatch
|
||||
} else {
|
||||
annPatches, err = jsonpatch.MergePatch(annPatches, annPatch)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(policyInfos) > 0 && len(policyInfos[0].Rules) != 0 {
|
||||
|
@ -82,7 +95,10 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest) *v1
|
|||
ws.violationBuilder.Add(violations...)
|
||||
ws.eventController.Add(eventsInfo...)
|
||||
}
|
||||
|
||||
// add annotations
|
||||
if annPatches != nil {
|
||||
ws.annotationsController.Add(rkind, rns, rname, annPatches)
|
||||
}
|
||||
// If Validation fails then reject the request
|
||||
ok, msg := isAdmSuccesful(policyInfos)
|
||||
// violations are created if "report" flag is set
|
||||
|
|
Loading…
Add table
Reference in a new issue