From 3f293d826668624bd09aedf7ebb506a87f30d015 Mon Sep 17 00:00:00 2001 From: shuting Date: Tue, 14 May 2019 11:24:40 -0700 Subject: [PATCH 1/3] initial commit, remove kubeclient from policy engine --- main.go | 3 - pkg/controller/controller.go | 4 - pkg/controller/processPolicy.go | 3 +- pkg/engine/engine.go | 158 ++++++++++++++---------------- pkg/engine/generation.go | 13 +-- pkg/engine/mutation.go | 15 +-- pkg/engine/mutation/patches.go | 3 +- pkg/engine/validation.go | 2 +- pkg/webhooks/admission_test.go | 3 +- pkg/webhooks/registration_test.go | 25 +++-- pkg/webhooks/server.go | 5 +- 11 files changed, 110 insertions(+), 124 deletions(-) diff --git a/main.go b/main.go index df638fe689..d56d79195e 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( policyclientset "github.com/nirmata/kube-policy/pkg/client/clientset/versioned" informers "github.com/nirmata/kube-policy/pkg/client/informers/externalversions" controller "github.com/nirmata/kube-policy/pkg/controller" - engine "github.com/nirmata/kube-policy/pkg/engine" event "github.com/nirmata/kube-policy/pkg/event" violation "github.com/nirmata/kube-policy/pkg/violation" "github.com/nirmata/kube-policy/pkg/webhooks" @@ -43,11 +42,9 @@ func main() { eventController := event.NewEventController(kubeclient, policyInformer.Lister(), nil) violationBuilder := violation.NewPolicyViolationBuilder(kubeclient, policyInformer.Lister(), policyClientset, eventController, nil) - policyEngine := engine.NewPolicyEngine(kubeclient, nil) policyController := controller.NewPolicyController(policyClientset, policyInformer, - policyEngine, violationBuilder, eventController, nil, diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 1b952db03a..4dc8f11258 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -10,7 +10,6 @@ import ( policyclientset "github.com/nirmata/kube-policy/pkg/client/clientset/versioned" infomertypes "github.com/nirmata/kube-policy/pkg/client/informers/externalversions/policy/v1alpha1" lister "github.com/nirmata/kube-policy/pkg/client/listers/policy/v1alpha1" - engine "github.com/nirmata/kube-policy/pkg/engine" event "github.com/nirmata/kube-policy/pkg/event" violation "github.com/nirmata/kube-policy/pkg/violation" "k8s.io/apimachinery/pkg/api/errors" @@ -27,7 +26,6 @@ type PolicyController struct { policyLister lister.PolicyLister policyInterface policyclientset.Interface policySynced cache.InformerSynced - policyEngine engine.PolicyEngine violationBuilder violation.Generator eventBuilder event.Generator logger *log.Logger @@ -37,7 +35,6 @@ type PolicyController struct { // NewPolicyController from cmd args func NewPolicyController(policyInterface policyclientset.Interface, policyInformer infomertypes.PolicyInformer, - policyEngine engine.PolicyEngine, violationBuilder violation.Generator, eventController event.Generator, logger *log.Logger, @@ -48,7 +45,6 @@ func NewPolicyController(policyInterface policyclientset.Interface, policyLister: policyInformer.Lister(), policyInterface: policyInterface, policySynced: policyInformer.Informer().HasSynced, - policyEngine: policyEngine, violationBuilder: violationBuilder, eventBuilder: eventController, logger: logger, diff --git a/pkg/controller/processPolicy.go b/pkg/controller/processPolicy.go index 84bdf6dbfb..6f7bfe7d4c 100644 --- a/pkg/controller/processPolicy.go +++ b/pkg/controller/processPolicy.go @@ -5,6 +5,7 @@ import ( "fmt" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" + engine "github.com/nirmata/kube-policy/pkg/engine" "github.com/nirmata/kube-policy/pkg/engine/mutation" event "github.com/nirmata/kube-policy/pkg/event" violation "github.com/nirmata/kube-policy/pkg/violation" @@ -57,7 +58,7 @@ func (pc *PolicyController) processPolicy(policy types.Policy) ( continue } - violation, eventInfos, err := pc.policyEngine.ProcessExisting(policy, rawResource) + violation, eventInfos, err := engine.ProcessExisting(policy, rawResource) if err != nil { pc.logger.Printf("Failed to process rule %s, err: %v\n", rule.Name, err) continue diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 7101b73222..39d6cd7afe 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -1,105 +1,97 @@ package engine import ( - "fmt" - "log" - - kubeClient "github.com/nirmata/kube-policy/kubeclient" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" - "github.com/nirmata/kube-policy/pkg/engine/mutation" - event "github.com/nirmata/kube-policy/pkg/event" - violation "github.com/nirmata/kube-policy/pkg/violation" + "github.com/nirmata/kube-policy/pkg/event" + "github.com/nirmata/kube-policy/pkg/violation" ) -type PolicyEngine interface { - // ProcessMutation should be called from admission contoller - // when there is an creation / update of the resource - // ProcessMutation(policy types.Policy, rawResource []byte) (patchBytes []byte, events []Events, err error) - Mutate(policy types.Policy, rawResource []byte) []mutation.PatchBytes +// As the logic to process the policies in stateless, we do not need to define struct and implement behaviors for it +// Instead we expose them as standalone functions passing the logger and the required atrributes +// The each function returns the changes that need to be applied on the resource +// the caller is responsible to apply the changes to the resource - // ProcessValidation should be called from admission contoller - // when there is an creation / update of the resource - Validate(policy types.Policy, rawResource []byte) - - // ProcessExisting should be called from policy controller - // when there is an create / update of the policy - // we should process the policy on matched resources, generate violations accordingly - // TODO: This method should not be in PolicyEngine. Validate will do this work instead - ProcessExisting(policy types.Policy, rawResource []byte) ([]violation.Info, []event.Info, error) - - // TODO: Add Generate method - // Generate() +func ProcessExisting(policy types.Policy, rawResource []byte) ([]violation.Info, []event.Info, error) { + return nil, nil, nil } -type policyEngine struct { - kubeClient *kubeClient.KubeClient - logger *log.Logger -} +// type PolicyEngine interface { +// // ProcessMutation should be called from admission contoller +// // when there is an creation / update of the resource +// // ProcessMutation(policy types.Policy, rawResource []byte) (patchBytes []byte, events []Events, err error) +// Mutate(policy types.Policy, rawResource []byte) []mutation.PatchBytes -func NewPolicyEngine(kubeClient *kubeClient.KubeClient, logger *log.Logger) PolicyEngine { - return &policyEngine{ - kubeClient: kubeClient, - logger: logger, - } -} +// // ProcessValidation should be called from admission contoller +// // when there is an creation / update of the resource +// Validate(policy types.Policy, rawResource []byte) -func (p *policyEngine) ProcessExisting(policy types.Policy, rawResource []byte) ([]violation.Info, []event.Info, error) { - var violations []violation.Info - var events []event.Info +// // ProcessExisting should be called from policy controller +// // when there is an create / update of the policy +// // we should process the policy on matched resources, generate violations accordingly +// // TODO: This method should not be in PolicyEngine. Validate will do this work instead +// ProcessExisting(policy types.Policy, rawResource []byte) ([]violation.Info, []event.Info, error) - for _, rule := range policy.Spec.Rules { - err := rule.Validate() - if err != nil { - p.logger.Printf("Invalid rule detected: #%s in policy %s, err: %v\n", rule.Name, policy.ObjectMeta.Name, err) - continue - } +// // TODO: Add Generate method +// // Generate() +// } - if ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.ResourceDescription); !ok { - p.logger.Printf("Rule %s of policy %s is not applicable to the request", rule.Name, policy.Name) - return nil, nil, err - } +// func (p *policyEngine) ProcessExisting(policy types.Policy, rawResource []byte) ([]violation.Info, []event.Info, error) { +// var violations []violation.Info +// var events []event.Info - violation, eventInfos, err := p.processRuleOnResource(policy.Name, rule, rawResource) - if err != nil { - p.logger.Printf("Failed to process rule %s, err: %v\n", rule.Name, err) - continue - } - // } else { - // policyPatches = append(policyPatches, processedPatches...) - // } - violations = append(violations, violation) - events = append(events, eventInfos...) - } - return violations, events, nil -} +// for _, rule := range policy.Spec.Rules { +// err := rule.Validate() +// if err != nil { +// p.logger.Printf("Invalid rule detected: #%s in policy %s, err: %v\n", rule.Name, policy.ObjectMeta.Name, err) +// continue +// } -func (p *policyEngine) processRuleOnResource(policyName string, rule types.Rule, rawResource []byte) ( - violation.Info, []event.Info, error) { +// if ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.ResourceDescription); !ok { +// p.logger.Printf("Rule %s of policy %s is not applicable to the request", rule.Name, policy.Name) +// return nil, nil, err +// } - var violationInfo violation.Info - var eventInfos []event.Info +// violation, eventInfos, err := p.processRuleOnResource(policy.Name, rule, rawResource) +// if err != nil { +// p.logger.Printf("Failed to process rule %s, err: %v\n", rule.Name, err) +// continue +// } +// // } else { +// // policyPatches = append(policyPatches, processedPatches...) +// // } +// violations = append(violations, violation) +// events = append(events, eventInfos...) +// } +// return violations, events, nil +// } - resourceKind := mutation.ParseKindFromObject(rawResource) - resourceName := mutation.ParseNameFromObject(rawResource) - resourceNamespace := mutation.ParseNamespaceFromObject(rawResource) +// func (p *policyEngine) processRuleOnResource(policyName string, rule types.Rule, rawResource []byte) ( +// violation.Info, []event.Info, error) { - rulePatchesProcessed, err := mutation.ProcessPatches(rule.Mutation.Patches, nil) - if err != nil { - return violationInfo, eventInfos, fmt.Errorf("Failed to process patches from rule %s: %v", rule.Name, err) - } +// var violationInfo violation.Info +// var eventInfos []event.Info - if rulePatchesProcessed != nil { - log.Printf("Rule %s: prepared %d patches", rule.Name, len(rulePatchesProcessed)) +// resourceKind := mutation.ParseKindFromObject(rawResource) +// resourceName := mutation.ParseNameFromObject(rawResource) +// resourceNamespace := mutation.ParseNamespaceFromObject(rawResource) - violationInfo = violation.NewViolation(policyName, resourceKind, resourceNamespace+"/"+resourceName, rule.Name) - // add a violation to queue +// rulePatchesProcessed, err := mutation.ProcessPatches(rule.Mutation.Patches, nil) +// if err != nil { +// return violationInfo, eventInfos, fmt.Errorf("Failed to process patches from rule %s: %v", rule.Name, err) +// } - // add an event to policy - //TODO: event msg - eventInfos = append(eventInfos, event.NewEvent("Policy", policyName, event.PolicyViolation, event.FResourcePolcy)) - // add an event to resource - eventInfos = append(eventInfos, event.NewEvent(resourceKind, resourceNamespace+"/"+resourceName, event.PolicyViolation, event.FResourcePolcy)) - } +// if rulePatchesProcessed != nil { +// log.Printf("Rule %s: prepared %d patches", rule.Name, len(rulePatchesProcessed)) - return violationInfo, eventInfos, nil -} +// violationInfo = violation.NewViolation(policyName, resourceKind, resourceNamespace+"/"+resourceName, rule.Name) +// // add a violation to queue + +// // add an event to policy +// //TODO: event msg +// eventInfos = append(eventInfos, event.NewEvent("Policy", policyName, event.PolicyViolation, event.FResourcePolcy)) +// // add an event to resource +// eventInfos = append(eventInfos, event.NewEvent(resourceKind, resourceNamespace+"/"+resourceName, event.PolicyViolation, event.FResourcePolcy)) +// } + +// return violationInfo, eventInfos, nil +// } diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index dc8f7cb231..dbee932f6c 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -10,16 +10,16 @@ import ( // TODO: To be reworked due to spec policy-v2 // Applies "configMapGenerator" and "secretGenerator" described in PolicyRule -func (p *policyEngine) applyRuleGenerators(rawResource []byte, rule kubepolicy.Rule) error { +func applyRuleGenerators(rawResource []byte, rule kubepolicy.Rule) error { kind := mutation.ParseKindFromObject(rawResource) // configMapGenerator and secretGenerator can be applied only to namespaces if kind == "Namespace" { namespaceName := mutation.ParseNameFromObject(rawResource) - err := p.applyConfigGenerator(rule.Generation, namespaceName, "ConfigMap") + err := applyConfigGenerator(rule.Generation, namespaceName, "ConfigMap") if err == nil { - err = p.applyConfigGenerator(rule.Generation, namespaceName, "Secret") + err = applyConfigGenerator(rule.Generation, namespaceName, "Secret") } return err } @@ -27,7 +27,7 @@ func (p *policyEngine) applyRuleGenerators(rawResource []byte, rule kubepolicy.R } // Creates resourceKind (ConfigMap or Secret) with parameters specified in generator in cluster specified in request. -func (p *policyEngine) applyConfigGenerator(generator *kubepolicy.Generation, namespace string, configKind string) error { +func applyConfigGenerator(generator *kubepolicy.Generation, namespace string, configKind string) error { if generator == nil { return nil } @@ -37,11 +37,12 @@ func (p *policyEngine) applyConfigGenerator(generator *kubepolicy.Generation, na return fmt.Errorf("Generator for '%s' is invalid: %s", configKind, err) } + // TODO: switch configKind { case "ConfigMap": - err = p.kubeClient.GenerateConfigMap(*generator, namespace) + // err = p.kubeClient.GenerateConfigMap(*generator, namespace) case "Secret": - err = p.kubeClient.GenerateSecret(*generator, namespace) + // err = p.kubeClient.GenerateSecret(*generator, namespace) default: err = fmt.Errorf("Unsupported config Kind '%s'", configKind) } diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index cb45603ca6..cdef0c8bc6 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -1,12 +1,15 @@ package engine import ( + "log" + kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" "github.com/nirmata/kube-policy/pkg/engine/mutation" ) // Mutate performs mutation. Overlay first and then mutation patches -func (p *policyEngine) Mutate(policy kubepolicy.Policy, rawResource []byte) []mutation.PatchBytes { +// TODO: pass in logger? +func Mutate(policy kubepolicy.Policy, rawResource []byte) []mutation.PatchBytes { var policyPatches []mutation.PatchBytes for i, rule := range policy.Spec.Rules { @@ -18,18 +21,18 @@ func (p *policyEngine) Mutate(policy kubepolicy.Policy, rawResource []byte) []mu err := rule.Validate() if err != nil { - p.logger.Printf("Rule has invalid structure: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) + log.Printf("Rule has invalid structure: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) continue } ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.ResourceDescription) if err != nil { - p.logger.Printf("Rule has invalid data: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) + log.Printf("Rule has invalid data: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) continue } if !ok { - p.logger.Printf("Rule is not applicable t the request: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) + log.Printf("Rule is not applicable t the request: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) continue } @@ -38,7 +41,7 @@ func (p *policyEngine) Mutate(policy kubepolicy.Policy, rawResource []byte) []mu if rule.Mutation.Overlay != nil { overlayPatches, err := mutation.ProcessOverlay(rule.Mutation.Overlay, rawResource) if err != nil { - p.logger.Printf("Overlay application failed: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) + log.Printf("Overlay application failed: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) } else { policyPatches = append(policyPatches, overlayPatches...) } @@ -49,7 +52,7 @@ func (p *policyEngine) Mutate(policy kubepolicy.Policy, rawResource []byte) []mu if rule.Mutation.Patches != nil { processedPatches, err := mutation.ProcessPatches(rule.Mutation.Patches, rawResource) if err != nil { - p.logger.Printf("Patches application failed: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) + log.Printf("Patches application failed: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) } else { policyPatches = append(policyPatches, processedPatches...) } diff --git a/pkg/engine/mutation/patches.go b/pkg/engine/mutation/patches.go index 0b3015020d..273530c220 100644 --- a/pkg/engine/mutation/patches.go +++ b/pkg/engine/mutation/patches.go @@ -10,8 +10,7 @@ import ( type PatchBytes []byte -// Test patches on given document according to given sets. -// Returns array from separate patches that can be applied to the document +// ProcessPatches Returns array from separate patches that can be applied to the document // Returns error ONLY in case when creation of resource should be denied. func ProcessPatches(patches []kubepolicy.Patch, resource []byte) ([]PatchBytes, error) { if len(resource) == 0 { diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index 1014c3b529..dd67137b6f 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -2,4 +2,4 @@ package engine import types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" -func (p *policyEngine) Validate(policy types.Policy, rawResource []byte) {} +func Validate(policy types.Policy, rawResource []byte) {} diff --git a/pkg/webhooks/admission_test.go b/pkg/webhooks/admission_test.go index c1ffbad608..8a7f1fbec3 100644 --- a/pkg/webhooks/admission_test.go +++ b/pkg/webhooks/admission_test.go @@ -1,9 +1,10 @@ package webhooks_test import ( - "gotest.tools/assert" "testing" + "gotest.tools/assert" + types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" "github.com/nirmata/kube-policy/webhooks" v1beta1 "k8s.io/api/admission/v1beta1" diff --git a/pkg/webhooks/registration_test.go b/pkg/webhooks/registration_test.go index a095756838..6a798cad1d 100644 --- a/pkg/webhooks/registration_test.go +++ b/pkg/webhooks/registration_test.go @@ -1,21 +1,20 @@ package webhooks_test import ( - "gotest.tools/assert" + "bytes" "io/ioutil" "testing" - "bytes" - - "github.com/nirmata/kube-policy/webhooks" + "github.com/nirmata/kube-policy/pkg/webhooks" + "gotest.tools/assert" rest "k8s.io/client-go/rest" ) func TestExtractCA_EmptyBundle(t *testing.T) { CAFile := "resources/CAFile" - config := &rest.Config { - TLSClientConfig: rest.TLSClientConfig { + config := &rest.Config{ + TLSClientConfig: rest.TLSClientConfig{ CAData: nil, CAFile: CAFile, }, @@ -30,8 +29,8 @@ func TestExtractCA_EmptyBundle(t *testing.T) { func TestExtractCA_EmptyCAFile(t *testing.T) { CABundle := []byte(`LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1ETXhPVEUwTURjd05Gb1hEVEk1TURNeE5qRTBNRGN3TkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTStQClVLVmExcm9tQndOZzdqNnBBSGo5TDQ4RVJpdEplRzRXM1pUYmNMNWNKbnVTQmFsc1h1TWpQTGZmbUV1VEZIdVAKenRqUlBEUHcreEg1d3VTWFF2U0tIaXF2VE1pUm9DSlJFa09sQXpIa1dQM0VrdnUzNzRqZDVGV3Q3NEhnRk91cApIZ1ZwdUxPblczK2NDVE5iQ3VkeDFMVldRbGgwQzJKbm1Lam5uS1YrTkxzNFJVaVk1dk91ekpuNHl6QldLRjM2CmJLZ3ZDOVpMWlFSM3dZcnJNZWllYzBnWVY2VlJtaGgxSjRDV3V1UWd0ckM2d2NJanFWZFdEUlJyNHFMdEtDcDIKQVNIZmNieitwcEdHblJ5Z2FzcWNJdnpiNUVwV3NIRGtHRStUUW5WQ0JmTmsxN0NEOTZBQ1pmRWVybzEvWE16MgpRbzZvcUE0dnF5ZkdWWVU5RVZFQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFNWFVpUVJpdUc4cGdzcHMrZTdGZWdCdEJOZEcKZlFUdHVLRWFUZ0U0RjQwamJ3UmdrN25DTHlsSHgvRG04aVRRQmsyWjR4WnNuY0huRys4SkwrckRLdlJBSE5iVQpsYnpReXA1V3FwdjdPcThwZ01wU0o5bTdVY3BGZmRVZkorNW43aXFnTGdMb3lhNmtRVTR2Rk0yTE1rWjI5NVpxCmVId0hnREo5Z3IwWGNyOWM1L2tRdkxFc2Z2WU5QZVhuamNyWXlDb2JNcVduSElxeVd3cHM1VTJOaGgraXhSZEIKbzRRL3RJS04xOU93WGZBaVc5SENhNzZMb3ZXaUhPU2UxVnFzK1h1N1A5ckx4eW1vQm91aFcxVmZ0bUo5Qy9vTAp3cFVuNnlXRCttY0tkZ3J5QTFjTWJ4Q281bUd6YTNLaFk1QTd5eDQ1cThkSEIzTWU4d0FCam1wWEs0ST0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=`) - config := &rest.Config { - TLSClientConfig: rest.TLSClientConfig { + config := &rest.Config{ + TLSClientConfig: rest.TLSClientConfig{ CAData: CABundle, CAFile: "", }, @@ -42,8 +41,8 @@ func TestExtractCA_EmptyCAFile(t *testing.T) { } func TestExtractCA_EmptyConfig(t *testing.T) { - config := &rest.Config { - TLSClientConfig: rest.TLSClientConfig { + config := &rest.Config{ + TLSClientConfig: rest.TLSClientConfig{ CAData: nil, CAFile: "", }, @@ -54,8 +53,8 @@ func TestExtractCA_EmptyConfig(t *testing.T) { } func TestExtractCA_InvalidFile(t *testing.T) { - config := &rest.Config { - TLSClientConfig: rest.TLSClientConfig { + config := &rest.Config{ + TLSClientConfig: rest.TLSClientConfig{ CAData: nil, CAFile: "somenonexistingfile", }, @@ -63,4 +62,4 @@ func TestExtractCA_InvalidFile(t *testing.T) { actual := webhooks.ExtractCA(config) assert.Assert(t, actual == nil) -} \ No newline at end of file +} diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index 310bb097a4..ccaa255dcc 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -29,7 +29,6 @@ import ( // MutationWebhook gets policies from policyController and takes control of the cluster with kubeclient. type WebhookServer struct { server http.Server - policyEngine engine.PolicyEngine policyLister policylister.PolicyLister logger *log.Logger } @@ -55,10 +54,8 @@ func NewWebhookServer( return nil, err } tlsConfig.Certificates = []tls.Certificate{pair} - policyEngine := engine.NewPolicyEngine(kubeclient, logger) ws := &WebhookServer{ - policyEngine: policyEngine, policyLister: policyLister, logger: logger, } @@ -185,7 +182,7 @@ func (ws *WebhookServer) Mutate(request *v1beta1.AdmissionRequest) *v1beta1.Admi for _, policy := range policies { ws.logger.Printf("Applying policy %s with %d rules", policy.ObjectMeta.Name, len(policy.Spec.Rules)) - policyPatches := ws.policyEngine.Mutate(policy, request.Object.Raw) + policyPatches := engine.Mutate(policy, request.Object.Raw) allPatches = append(allPatches, policyPatches...) if len(policyPatches) > 0 { From a36bb2fbacd30f09540b410f3b13dc7cd5360fc0 Mon Sep 17 00:00:00 2001 From: shuting Date: Tue, 14 May 2019 18:20:41 -0700 Subject: [PATCH 2/3] Implement Generate() --- main.go | 2 +- pkg/apis/policy/v1alpha1/utils.go | 4 +- pkg/engine/engine.go | 89 +++-------------------------- pkg/engine/generation.go | 95 +++++++++++++++++++------------ pkg/engine/mutation.go | 4 +- pkg/engine/validation.go | 2 +- pkg/webhooks/server.go | 2 - 7 files changed, 72 insertions(+), 126 deletions(-) diff --git a/main.go b/main.go index 28aa03bb5d..3fd428f433 100644 --- a/main.go +++ b/main.go @@ -59,7 +59,7 @@ func main() { log.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err) } - server, err := webhooks.NewWebhookServer(tlsPair, kubeclient, policyInformer.Lister(), nil) + server, err := webhooks.NewWebhookServer(tlsPair, policyInformer.Lister(), nil) if err != nil { log.Fatalf("Unable to create webhook server: %v\n", err) } diff --git a/pkg/apis/policy/v1alpha1/utils.go b/pkg/apis/policy/v1alpha1/utils.go index 695d5dca6e..653513a232 100644 --- a/pkg/apis/policy/v1alpha1/utils.go +++ b/pkg/apis/policy/v1alpha1/utils.go @@ -74,8 +74,8 @@ func (pcf *CopyFrom) Validate() error { // Validate returns error if generator is configured incompletely func (pcg *Generation) Validate() error { - if pcg.Name == "" { - return errors.New("The generator is unnamed") + if pcg.Name == "" || pcg.Kind == "" { + return errors.New("Name or/and Kind of generator is not specified") } return pcg.CopyFrom.Validate() diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 39d6cd7afe..b4227d0487 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -12,86 +12,11 @@ import ( // the caller is responsible to apply the changes to the resource func ProcessExisting(policy types.Policy, rawResource []byte) ([]violation.Info, []event.Info, error) { - return nil, nil, nil + var violations []violation.Info + var events []event.Info + + // TODO: + // Mutate() + // Validate() + return violations, events, nil } - -// type PolicyEngine interface { -// // ProcessMutation should be called from admission contoller -// // when there is an creation / update of the resource -// // ProcessMutation(policy types.Policy, rawResource []byte) (patchBytes []byte, events []Events, err error) -// Mutate(policy types.Policy, rawResource []byte) []mutation.PatchBytes - -// // ProcessValidation should be called from admission contoller -// // when there is an creation / update of the resource -// Validate(policy types.Policy, rawResource []byte) - -// // ProcessExisting should be called from policy controller -// // when there is an create / update of the policy -// // we should process the policy on matched resources, generate violations accordingly -// // TODO: This method should not be in PolicyEngine. Validate will do this work instead -// ProcessExisting(policy types.Policy, rawResource []byte) ([]violation.Info, []event.Info, error) - -// // TODO: Add Generate method -// // Generate() -// } - -// func (p *policyEngine) ProcessExisting(policy types.Policy, rawResource []byte) ([]violation.Info, []event.Info, error) { -// var violations []violation.Info -// var events []event.Info - -// for _, rule := range policy.Spec.Rules { -// err := rule.Validate() -// if err != nil { -// p.logger.Printf("Invalid rule detected: #%s in policy %s, err: %v\n", rule.Name, policy.ObjectMeta.Name, err) -// continue -// } - -// if ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.ResourceDescription); !ok { -// p.logger.Printf("Rule %s of policy %s is not applicable to the request", rule.Name, policy.Name) -// return nil, nil, err -// } - -// violation, eventInfos, err := p.processRuleOnResource(policy.Name, rule, rawResource) -// if err != nil { -// p.logger.Printf("Failed to process rule %s, err: %v\n", rule.Name, err) -// continue -// } -// // } else { -// // policyPatches = append(policyPatches, processedPatches...) -// // } -// violations = append(violations, violation) -// events = append(events, eventInfos...) -// } -// return violations, events, nil -// } - -// func (p *policyEngine) processRuleOnResource(policyName string, rule types.Rule, rawResource []byte) ( -// violation.Info, []event.Info, error) { - -// var violationInfo violation.Info -// var eventInfos []event.Info - -// resourceKind := mutation.ParseKindFromObject(rawResource) -// resourceName := mutation.ParseNameFromObject(rawResource) -// resourceNamespace := mutation.ParseNamespaceFromObject(rawResource) - -// rulePatchesProcessed, err := mutation.ProcessPatches(rule.Mutation.Patches, nil) -// if err != nil { -// return violationInfo, eventInfos, fmt.Errorf("Failed to process patches from rule %s: %v", rule.Name, err) -// } - -// if rulePatchesProcessed != nil { -// log.Printf("Rule %s: prepared %d patches", rule.Name, len(rulePatchesProcessed)) - -// violationInfo = violation.NewViolation(policyName, resourceKind, resourceNamespace+"/"+resourceName, rule.Name) -// // add a violation to queue - -// // add an event to policy -// //TODO: event msg -// eventInfos = append(eventInfos, event.NewEvent("Policy", policyName, event.PolicyViolation, event.FResourcePolcy)) -// // add an event to resource -// eventInfos = append(eventInfos, event.NewEvent(resourceKind, resourceNamespace+"/"+resourceName, event.PolicyViolation, event.FResourcePolcy)) -// } - -// return violationInfo, eventInfos, nil -// } diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index dbee932f6c..e5cb259942 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -2,54 +2,77 @@ package engine import ( "fmt" + "log" kubepolicy "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" "github.com/nirmata/kube-policy/pkg/engine/mutation" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// TODO: To be reworked due to spec policy-v2 - -// Applies "configMapGenerator" and "secretGenerator" described in PolicyRule -func applyRuleGenerators(rawResource []byte, rule kubepolicy.Rule) error { - kind := mutation.ParseKindFromObject(rawResource) - - // configMapGenerator and secretGenerator can be applied only to namespaces - if kind == "Namespace" { - namespaceName := mutation.ParseNameFromObject(rawResource) - - err := applyConfigGenerator(rule.Generation, namespaceName, "ConfigMap") - if err == nil { - err = applyConfigGenerator(rule.Generation, namespaceName, "Secret") - } - return err - } - return nil +type GenerationResponse struct { + Generator *kubepolicy.Generation + Namespace string } -// Creates resourceKind (ConfigMap or Secret) with parameters specified in generator in cluster specified in request. -func applyConfigGenerator(generator *kubepolicy.Generation, namespace string, configKind string) error { - if generator == nil { +// Generate should be called to process generate rules on the resource +// TODO: extend kubeclient(will change to dynamic client) to create resources +func Generate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) []GenerationResponse { + // configMapGenerator and secretGenerator can be applied only to namespaces + if gvk.Kind != "Namespace" { return nil } + var generateResps []GenerationResponse + + for i, rule := range policy.Spec.Rules { + + // Checks for preconditions + // TODO: Rework PolicyEngine interface that it receives not a policy, but mutation object for + // Mutate, validation for Validate and so on. It will allow to bring this checks outside of PolicyEngine + // to common part as far as they present for all: mutation, validation, generation + + err := rule.Validate() + if err != nil { + log.Printf("Rule has invalid structure: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) + continue + } + + ok, err := mutation.ResourceMeetsRules(rawResource, rule.ResourceDescription, gvk) + if err != nil { + log.Printf("Rule has invalid data: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) + continue + } + + if !ok { + log.Printf("Rule is not applicable to the request: rule name = %s in policy %s \n", rule.Name, policy.ObjectMeta.Name) + continue + } + + generateResps, err = applyRuleGenerator(rawResource, rule.Generation) + if err != nil { + log.Printf("Failed to apply rule generator: %v", err) + } else { + generateResps = append(generateResps, generateResps...) + } + } + + return generateResps +} + +// Applies "configMapGenerator" and "secretGenerator" described in PolicyRule +// TODO: plan to support all kinds of generator +func applyRuleGenerator(rawResource []byte, generator *kubepolicy.Generation) ([]GenerationResponse, error) { + var generateResps []GenerationResponse + if generator == nil { + return nil, nil + } + err := generator.Validate() if err != nil { - return fmt.Errorf("Generator for '%s' is invalid: %s", configKind, err) + return nil, fmt.Errorf("Generator for '%s' is invalid: %s", generator.Kind, err) } - // TODO: - switch configKind { - case "ConfigMap": - // err = p.kubeClient.GenerateConfigMap(*generator, namespace) - case "Secret": - // err = p.kubeClient.GenerateSecret(*generator, namespace) - default: - err = fmt.Errorf("Unsupported config Kind '%s'", configKind) - } - - if err != nil { - return fmt.Errorf("Unable to apply generator for %s '%s/%s' : %s", configKind, namespace, generator.Name, err) - } - - return nil + namespaceName := mutation.ParseNameFromObject(rawResource) + generateResps = append(generateResps, GenerationResponse{generator, namespaceName}) + return generateResps, nil } diff --git a/pkg/engine/mutation.go b/pkg/engine/mutation.go index 15a06b5f23..fc83006bba 100644 --- a/pkg/engine/mutation.go +++ b/pkg/engine/mutation.go @@ -9,7 +9,7 @@ import ( ) // Mutate performs mutation. Overlay first and then mutation patches -// TODO: pass in logger? +// TODO: return events and violations func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersionKind) []mutation.PatchBytes { var policyPatches []mutation.PatchBytes @@ -33,7 +33,7 @@ func Mutate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVersio } if !ok { - log.Printf("Rule is not applicable t the request: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) + log.Printf("Rule is not applicable to the request: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) continue } diff --git a/pkg/engine/validation.go b/pkg/engine/validation.go index ed9328296d..c22005e99b 100644 --- a/pkg/engine/validation.go +++ b/pkg/engine/validation.go @@ -35,7 +35,7 @@ func Validate(policy kubepolicy.Policy, rawResource []byte, gvk metav1.GroupVers } if !ok { - log.Printf("Rule is not applicable t the request: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) + log.Printf("Rule is not applicable to the request: rule number = %d, rule name = %s in policy %s, err: %v\n", i, rule.Name, policy.ObjectMeta.Name, err) continue } diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go index c37f252936..b74a6a36e8 100644 --- a/pkg/webhooks/server.go +++ b/pkg/webhooks/server.go @@ -13,7 +13,6 @@ import ( "time" "github.com/nirmata/kube-policy/config" - "github.com/nirmata/kube-policy/kubeclient" policylister "github.com/nirmata/kube-policy/pkg/client/listers/policy/v1alpha1" engine "github.com/nirmata/kube-policy/pkg/engine" "github.com/nirmata/kube-policy/pkg/engine/mutation" @@ -35,7 +34,6 @@ type WebhookServer struct { // Policy Controller and Kubernetes Client should be initialized in configuration func NewWebhookServer( tlsPair *tlsutils.TlsPemPair, - kubeClient *kubeclient.KubeClient, policyLister policylister.PolicyLister, logger *log.Logger) (*WebhookServer, error) { if logger == nil { From b4bb7e0e698ad950cda48d5ed2694bb678df0d73 Mon Sep 17 00:00:00 2001 From: shuting Date: Tue, 14 May 2019 18:22:13 -0700 Subject: [PATCH 3/3] Add comfigmap generator example --- .../policy-cm-test.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 examples/ConfigMapGenerator-SecretGenerator/policy-cm-test.yaml diff --git a/examples/ConfigMapGenerator-SecretGenerator/policy-cm-test.yaml b/examples/ConfigMapGenerator-SecretGenerator/policy-cm-test.yaml new file mode 100644 index 0000000000..9e1056611b --- /dev/null +++ b/examples/ConfigMapGenerator-SecretGenerator/policy-cm-test.yaml @@ -0,0 +1,18 @@ +apiVersion : kubepolicy.nirmata.io/v1alpha1 +kind: Policy +metadata : + name: "policy-configmapgenerator-test" +spec: + rules: + - name: "Policy ConfigMap sample rule" + resource: + kind : Namespace + name: "ns2" + generate: + kind: ConfigMap + name: copied-cm + copyFrom: + namespace: default + name: game-config + data: + secretData: "data from cmg" \ No newline at end of file