diff --git a/main.go b/main.go index 4e04177455..3d9b70e43c 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,6 @@ import ( policyclientset "github.com/nirmata/kube-policy/pkg/client/clientset/versioned" informers "github.com/nirmata/kube-policy/pkg/client/informers/externalversions" - policyengine "github.com/nirmata/kube-policy/pkg/policyengine" policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation" event "github.com/nirmata/kube-policy/pkg/event" @@ -46,11 +45,9 @@ func main() { eventController := event.NewEventController(kubeclient, policyInformer.Lister(), nil) violationBuilder := policyviolation.NewPolicyViolationBuilder(kubeclient, policyInformer.Lister(), policyClientset, eventController, nil) - policyEngine := policyengine.NewPolicyEngine(kubeclient, nil) policyController := policycontroller.NewPolicyController(policyClientset, policyInformer, - policyEngine, violationBuilder, eventController, nil, @@ -59,7 +56,6 @@ func main() { mutationWebhook, err := webhooks.CreateMutationWebhook(clientConfig, kubeclient, policyInformer.Lister(), - violationBuilder, eventController, nil) if err != nil { diff --git a/pkg/policyengine/generate.go b/pkg/policyengine/generate.go new file mode 100644 index 0000000000..c7f696e50b --- /dev/null +++ b/pkg/policyengine/generate.go @@ -0,0 +1,85 @@ +package policyengine + +import ( + "fmt" + "log" + + types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" + "github.com/nirmata/kube-policy/pkg/policyengine/mutation" +) + +// Generate should be called to process generate rules on the resource +func Generate(logger *log.Logger, policy types.Policy, rawResource []byte) ([]GenerateReturnData, error) { + patchingSets := mutation.GetPolicyPatchingSets(policy) + generatedList := []GenerateReturnData{} + for ruleIdx, rule := range policy.Spec.Rules { + err := rule.Validate() + if err != nil { + logger.Printf("Invalid rule detected: #%d in policy %s, err: %v\n", ruleIdx, policy.ObjectMeta.Name, err) + continue + } + if ok, err := mutation.IsRuleApplicableToResource(rawResource, rule.Resource); !ok { + logger.Printf("Rule %d of policy %s is not applicable to the request", ruleIdx, policy.Name) + return nil, err + } + resourceKind := mutation.ParseKindFromObject(rawResource) + + // configMapGenerator and secretGenerator can be applied only to namespaces + if resourceKind == "Namespace" { + generatedData, err := applyRuleGenerators(rawResource, rule) + if err != nil && patchingSets == mutation.PatchingSetsStopOnError { + return nil, fmt.Errorf("Failed to apply generators from rule #%d: %s", ruleIdx, err) + } + generatedList = append(generatedList, generatedData...) + } + } + return generatedList, nil +} + +// Applies "configMapGenerator" and "secretGenerator" described in PolicyRule +func applyRuleGenerators(rawResource []byte, rule types.PolicyRule) ([]GenerateReturnData, error) { + returnData := []GenerateReturnData{} + namespaceName := mutation.ParseNameFromObject(rawResource) + var generator *types.PolicyConfigGenerator + // Apply config map generator rule + generator, err := applyConfigGenerator(rule.ConfigMapGenerator, namespaceName, "ConfigMap") + if err != nil { + return returnData, err + } + returnData = append(returnData, GenerateReturnData{namespaceName, "ConfigMap", *generator}) + // Apply secrets generator rule + generator, err = applyConfigGenerator(rule.SecretGenerator, namespaceName, "Secret") + if err != nil { + return returnData, err + } + returnData = append(returnData, GenerateReturnData{namespaceName, "Secret", *generator}) + + return returnData, nil +} + +// Creates resourceKind (ConfigMap or Secret) with parameters specified in generator in cluster specified in request. +func applyConfigGenerator(generator *types.PolicyConfigGenerator, namespace string, configKind string) (*types.PolicyConfigGenerator, error) { + if generator == nil { + return nil, nil + } + err := generator.Validate() + if err != nil { + return nil, fmt.Errorf("Generator for '%s' is invalid: %s", configKind, err) + } + switch configKind { + case "ConfigMap": + return generator, nil + // err = kubeClient.GenerateConfigMap(*generator, namespace) + case "Secret": + return generator, nil + default: + return nil, fmt.Errorf("Unsupported config Kind '%s'", configKind) + } +} + +//GenerateReturnData holds the generator details +type GenerateReturnData struct { + namespace string + configKind string + generator types.PolicyConfigGenerator +} diff --git a/pkg/policyengine/mutation.go b/pkg/policyengine/mutation.go index 85483bf53a..8af3ac1056 100644 --- a/pkg/policyengine/mutation.go +++ b/pkg/policyengine/mutation.go @@ -1,30 +1,31 @@ package policyengine import ( - "errors" "fmt" + "log" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" "github.com/nirmata/kube-policy/pkg/policyengine/mutation" ) -func (p *policyEngine) ProcessMutation(policy types.Policy, rawResource []byte) ([]mutation.PatchBytes, error) { +// Mutation should be claed to process the mutation rules on the resource +//TODO return []event.Info +func Mutation(logger *log.Logger, policy types.Policy, rawResource []byte) ([]mutation.PatchBytes, error) { patchingSets := mutation.GetPolicyPatchingSets(policy) var policyPatches []mutation.PatchBytes for ruleIdx, 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) + 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.Resource); !ok { - p.logger.Printf("Rule %d of policy %s is not applicable to the request", ruleIdx, policy.Name) + logger.Printf("Rule %d of policy %s is not applicable to the request", ruleIdx, policy.Name) return nil, err } - err = p.applyRuleGenerators(rawResource, rule) if err != nil && patchingSets == mutation.PatchingSetsStopOnError { return nil, fmt.Errorf("Failed to apply generators from rule #%s: %v", rule.Name, err) } @@ -36,10 +37,10 @@ func (p *policyEngine) ProcessMutation(policy types.Policy, rawResource []byte) if rulePatchesProcessed != nil { policyPatches = append(policyPatches, rulePatchesProcessed...) - p.logger.Printf("Rule %d: prepared %d patches", ruleIdx, len(rulePatchesProcessed)) + logger.Printf("Rule %d: prepared %d patches", ruleIdx, len(rulePatchesProcessed)) // TODO: add PolicyApplied events per rule for policy and resource } else { - p.logger.Printf("Rule %d: no patches prepared", ruleIdx) + logger.Printf("Rule %d: no patches prepared", ruleIdx) } } @@ -50,47 +51,3 @@ func (p *policyEngine) ProcessMutation(policy types.Policy, rawResource []byte) return policyPatches, nil } - -// Applies "configMapGenerator" and "secretGenerator" described in PolicyRule -func (p *policyEngine) applyRuleGenerators(rawResource []byte, rule types.PolicyRule) 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.ConfigMapGenerator, namespaceName, "ConfigMap") - if err == nil { - err = p.applyConfigGenerator(rule.SecretGenerator, namespaceName, "Secret") - } - return err - } - return nil -} - -// Creates resourceKind (ConfigMap or Secret) with parameters specified in generator in cluster specified in request. -func (p *policyEngine) applyConfigGenerator(generator *types.PolicyConfigGenerator, namespace string, configKind string) error { - if generator == nil { - return nil - } - - err := generator.Validate() - if err != nil { - return errors.New(fmt.Sprintf("Generator for '%s' is invalid: %s", configKind, err)) - } - - switch configKind { - case "ConfigMap": - err = p.kubeClient.GenerateConfigMap(*generator, namespace) - case "Secret": - err = p.kubeClient.GenerateSecret(*generator, namespace) - default: - err = errors.New(fmt.Sprintf("Unsupported config Kind '%s'", configKind)) - } - - if err != nil { - return errors.New(fmt.Sprintf("Unable to apply generator for %s '%s/%s' : %s", configKind, namespace, generator.Name, err)) - } - - return nil -} diff --git a/pkg/policyengine/policyengine.go b/pkg/policyengine/policyengine.go index 67a9084a51..ee35367c48 100644 --- a/pkg/policyengine/policyengine.go +++ b/pkg/policyengine/policyengine.go @@ -1,103 +1,17 @@ package policyengine import ( - "fmt" - "log" - - kubeClient "github.com/nirmata/kube-policy/kubeclient" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" - event "github.com/nirmata/kube-policy/pkg/event" - "github.com/nirmata/kube-policy/pkg/policyengine/mutation" - policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation" + "github.com/nirmata/kube-policy/pkg/event" + "github.com/nirmata/kube-policy/pkg/policyviolation" ) -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) - ProcessMutation(policy types.Policy, rawResource []byte) ([]mutation.PatchBytes, error) +// 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 - ProcessValidation(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 - ProcessExisting(policy types.Policy, rawResource []byte) ([]policyviolation.Info, []event.Info, error) -} - -type policyEngine struct { - kubeClient *kubeClient.KubeClient - logger *log.Logger -} - -func NewPolicyEngine(kubeClient *kubeClient.KubeClient, logger *log.Logger) PolicyEngine { - return &policyEngine{ - kubeClient: kubeClient, - logger: logger, - } -} - -func (p *policyEngine) ProcessExisting(policy types.Policy, rawResource []byte) ([]policyviolation.Info, []event.Info, error) { - var violations []policyviolation.Info - var events []event.Info - - patchingSets := mutation.GetPolicyPatchingSets(policy) - - 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.Resource); !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, patchingSets) - 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.PolicyRule, rawResource []byte, patchingSets mutation.PatchingSets) ( - policyviolation.Info, []event.Info, error) { - - var violationInfo policyviolation.Info - var eventInfos []event.Info - - resourceKind := mutation.ParseKindFromObject(rawResource) - resourceName := mutation.ParseNameFromObject(rawResource) - resourceNamespace := mutation.ParseNamespaceFromObject(rawResource) - - rulePatchesProcessed, err := mutation.ProcessPatches(rule.Patches, nil, patchingSets) - 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 = policyviolation.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 +//TODO wrap the generate, mutation & validation functions for the existing resources +func ProcessExisting(policy types.Policy, rawResource []byte) ([]policyviolation.Info, []event.Info, error) { + return nil, nil, nil } diff --git a/pkg/policyengine/validation.go b/pkg/policyengine/validation.go index 282a4496a4..40e367706c 100644 --- a/pkg/policyengine/validation.go +++ b/pkg/policyengine/validation.go @@ -1,5 +1,13 @@ package policyengine -import types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" +import ( + types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" + "github.com/nirmata/kube-policy/pkg/event" + "github.com/nirmata/kube-policy/pkg/policyviolation" +) -func (p *policyEngine) ProcessValidation(policy types.Policy, rawResource []byte) {} +// Validation should be called to process validation rules on the resource +//TODO: validate should return a bool to specify if the validation was succesful or not +func Validation(policy types.Policy, rawResource []byte) (bool, []policyviolation.Info, []event.Info) { + return true, nil, nil +} diff --git a/policycontroller/policycontroller.go b/policycontroller/policycontroller.go index e4df2c7d08..624b2ac88e 100644 --- a/policycontroller/policycontroller.go +++ b/policycontroller/policycontroller.go @@ -11,7 +11,7 @@ import ( infomertypes "github.com/nirmata/kube-policy/pkg/client/informers/externalversions/policy/v1alpha1" lister "github.com/nirmata/kube-policy/pkg/client/listers/policy/v1alpha1" event "github.com/nirmata/kube-policy/pkg/event" - policyengine "github.com/nirmata/kube-policy/pkg/policyengine" + policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -27,7 +27,6 @@ type PolicyController struct { policyLister lister.PolicyLister policyInterface policyclientset.Interface policySynced cache.InformerSynced - policyEngine policyengine.PolicyEngine violationBuilder policyviolation.Generator eventBuilder event.Generator logger *log.Logger @@ -37,7 +36,6 @@ type PolicyController struct { // NewPolicyController from cmd args func NewPolicyController(policyInterface policyclientset.Interface, policyInformer infomertypes.PolicyInformer, - policyEngine policyengine.PolicyEngine, violationBuilder policyviolation.Generator, eventController event.Generator, logger *log.Logger, @@ -48,7 +46,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/policycontroller/processPolicy.go b/policycontroller/processPolicy.go index de4950bbea..2d4045b81a 100644 --- a/policycontroller/processPolicy.go +++ b/policycontroller/processPolicy.go @@ -5,9 +5,10 @@ import ( "fmt" types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" - event "github.com/nirmata/kube-policy/pkg/event" + "github.com/nirmata/kube-policy/pkg/event" + "github.com/nirmata/kube-policy/pkg/policyengine" "github.com/nirmata/kube-policy/pkg/policyengine/mutation" - policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation" + "github.com/nirmata/kube-policy/pkg/policyviolation" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -57,7 +58,7 @@ func (pc *PolicyController) processPolicy(policy types.Policy) ( continue } - violation, eventInfos, err := pc.policyEngine.ProcessExisting(policy, rawResource) + violation, eventInfos, err := policyengine.ProcessExisting(policy, rawResource) if err != nil { pc.logger.Printf("Failed to process rule %s, err: %v\n", rule.Name, err) continue diff --git a/webhooks/mutation.go b/webhooks/mutation.go index 1b74c35ae4..82fbe8bd0d 100644 --- a/webhooks/mutation.go +++ b/webhooks/mutation.go @@ -11,9 +11,8 @@ import ( types "github.com/nirmata/kube-policy/pkg/apis/policy/v1alpha1" policylister "github.com/nirmata/kube-policy/pkg/client/listers/policy/v1alpha1" event "github.com/nirmata/kube-policy/pkg/event" - policyengine "github.com/nirmata/kube-policy/pkg/policyengine" + "github.com/nirmata/kube-policy/pkg/policyengine" mutation "github.com/nirmata/kube-policy/pkg/policyengine/mutation" - policyviolation "github.com/nirmata/kube-policy/pkg/policyviolation" v1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -25,13 +24,11 @@ import ( // MutationWebhook is a data type that represents // business logic for resource mutation type MutationWebhook struct { - kubeclient *kubeclient.KubeClient - policyEngine policyengine.PolicyEngine - policyLister policylister.PolicyLister - registration *MutationWebhookRegistration - violationBuilder policyviolation.Generator - eventBuilder event.Generator - logger *log.Logger + kubeclient *kubeclient.KubeClient + policyLister policylister.PolicyLister + registration *MutationWebhookRegistration + eventBuilder event.Generator + logger *log.Logger } // Registers mutation webhook in cluster and creates object for this webhook @@ -39,7 +36,6 @@ func CreateMutationWebhook( clientConfig *rest.Config, kubeclient *kubeclient.KubeClient, policyLister policylister.PolicyLister, - violationBuilder policyviolation.Generator, eventController event.Generator, logger *log.Logger) (*MutationWebhook, error) { if clientConfig == nil || kubeclient == nil { @@ -59,16 +55,13 @@ func CreateMutationWebhook( if logger == nil { logger = log.New(os.Stdout, "Mutation WebHook: ", log.LstdFlags|log.Lshortfile) } - policyengine := policyengine.NewPolicyEngine(kubeclient, logger) return &MutationWebhook{ - kubeclient: kubeclient, - policyEngine: policyengine, - policyLister: policyLister, - registration: registration, - violationBuilder: violationBuilder, - eventBuilder: eventController, - logger: logger, + kubeclient: kubeclient, + policyLister: policyLister, + registration: registration, + eventBuilder: eventController, + logger: logger, }, nil } @@ -141,7 +134,7 @@ func (mw *MutationWebhook) Mutate(request *v1beta1.AdmissionRequest) *v1beta1.Ad // 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) ([]mutation.PatchBytes, error) { - return mw.policyEngine.ProcessMutation(policy, request.Object.Raw) + return policyengine.Mutation(mw.logger, policy, request.Object.Raw) } // kind is the type of object being manipulated, e.g. request.Kind.kind