1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-31 03:45:17 +00:00

process existing for generate & annotations

This commit is contained in:
shivkumar dudhani 2019-07-23 03:07:11 -04:00
parent 4e40a49133
commit cdc17f999d
8 changed files with 105 additions and 39 deletions

View file

@ -51,7 +51,7 @@ func main() {
eventController,
annotationsController)
genControler := gencontroller.NewGenController(client, eventController, policyInformerFactory, violationBuilder, kubeInformer.Core().V1().Namespaces())
genControler := gencontroller.NewGenController(client, eventController, policyInformerFactory, violationBuilder, kubeInformer.Core().V1().Namespaces(), annotationsController)
tlsPair, err := initTLSPemPair(clientConfig, client)
if err != nil {
glog.Fatalf("Failed to initialize TLS key/certificate pair: %v\n", err)

View file

@ -225,7 +225,14 @@ func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) {
if err != nil {
glog.Error(err)
}
if mpatch == nil && vpatch == nil {
// Generation rules
ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Validation)
if err != nil {
glog.Error(err)
}
if mpatch == nil && vpatch == nil && gpatch == nil {
//nothing to patch
continue
}
@ -237,11 +244,21 @@ func (pc *PolicyController) createAnnotations(policyInfos []*info.PolicyInfo) {
continue
}
}
if mpatch == nil {
patch = vpatch
} else {
patch = mpatch
}
// generation
if gpatch != nil {
patch, err = jsonpatch.MergePatch(patch, gpatch)
if err != nil {
glog.Error(err)
continue
}
}
// add the anotation to the resource
_, err = pc.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, patch)
if err != nil {

View file

@ -10,22 +10,14 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// 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 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
// ProcessExisting checks for mutation and validation violations of existing resources
func ProcessExisting(client *client.Client, policy *types.Policy) []*info.PolicyInfo {
glog.Infof("Applying policy %s on existing resources", policy.Name)
// key uid
resourceMap := map[string]*resourceInfo{}
resourceMap := map[string]resourceInfo{}
for _, rule := range policy.Spec.Rules {
for _, k := range rule.Kinds {
if k == "Namespace" {
// REWORK: will be handeled by namespace controller
continue
}
// kind -> resource
gvr := client.DiscoveryClient.GetGVRFromKind(k)
// label selectors
@ -34,6 +26,10 @@ func ProcessExisting(client *client.Client, policy *types.Policy) []*info.Policy
if rule.ResourceDescription.Namespace != nil {
namespace = *rule.ResourceDescription.Namespace
}
if k == "Namespace" {
namespace = ""
}
list, err := client.ListResource(k, namespace, rule.ResourceDescription.Selector)
if err != nil {
glog.Errorf("unable to list resource for %s with label selector %s", gvr.Resource, rule.Selector.String())
@ -49,7 +45,7 @@ func ProcessExisting(client *client.Client, policy *types.Policy) []*info.Policy
continue
}
}
ri := &resourceInfo{resource: &res, gvk: &metav1.GroupVersionKind{Group: gvk.Group,
ri := resourceInfo{resource: res, gvk: &metav1.GroupVersionKind{Group: gvk.Group,
Version: gvk.Version,
Kind: gvk.Kind}}
@ -74,7 +70,7 @@ func ProcessExisting(client *client.Client, policy *types.Policy) []*info.Policy
return policyInfos
}
func applyPolicy(client *client.Client, policy *types.Policy, res *resourceInfo) (*info.PolicyInfo, error) {
func applyPolicy(client *client.Client, policy *types.Policy, res resourceInfo) (*info.PolicyInfo, error) {
policyInfo := info.NewPolicyInfo(policy.Name, res.gvk.Kind, res.resource.GetName(), res.resource.GetNamespace(), policy.Spec.ValidationFailureAction)
glog.Infof("Applying policy %s with %d rules\n", policy.ObjectMeta.Name, len(policy.Spec.Rules))
rawResource, err := res.resource.MarshalJSON()
@ -93,7 +89,12 @@ func applyPolicy(client *client.Client, policy *types.Policy, res *resourceInfo)
if err != nil {
return nil, err
}
// Generate rule managed by generation controller
if res.gvk.Kind == "Namespace" {
// Generation
gruleInfos := GenerateNew(client, policy, res.resource)
policyInfo.AddRuleInfos(gruleInfos)
}
return policyInfo, nil
}

View file

@ -9,13 +9,12 @@ import (
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/info"
"github.com/nirmata/kyverno/pkg/utils"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)
//GenerateNew apply generation rules on a resource
func GenerateNew(client *client.Client, policy *v1alpha1.Policy, ns *corev1.Namespace) []*info.RuleInfo {
func GenerateNew(client *client.Client, policy *v1alpha1.Policy, ns unstructured.Unstructured) []*info.RuleInfo {
ris := []*info.RuleInfo{}
for _, rule := range policy.Spec.Rules {
if rule.Generation == nil {
@ -35,14 +34,14 @@ func GenerateNew(client *client.Client, policy *v1alpha1.Policy, ns *corev1.Name
return ris
}
func applyRuleGeneratorNew(client *client.Client, ns *corev1.Namespace, gen *v1alpha1.Generation) error {
func applyRuleGeneratorNew(client *client.Client, ns unstructured.Unstructured, gen *v1alpha1.Generation) error {
var err error
resource := &unstructured.Unstructured{}
var rdata map[string]interface{}
if gen.Data != nil {
// 1> Check if resource exists
obj, err := client.GetResource(gen.Kind, ns.Name, gen.Name)
obj, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
if err == nil {
// 2> If already exsists, then verify the content is contained
// found the resource
@ -64,7 +63,7 @@ func applyRuleGeneratorNew(client *client.Client, ns *corev1.Namespace, gen *v1a
}
if gen.Clone != nil {
// 1> Check if resource exists
_, err := client.GetResource(gen.Kind, ns.Name, gen.Name)
_, err := client.GetResource(gen.Kind, ns.GetName(), gen.Name)
if err == nil {
return nil
}
@ -77,11 +76,11 @@ func applyRuleGeneratorNew(client *client.Client, ns *corev1.Namespace, gen *v1a
}
resource.SetUnstructuredContent(rdata)
resource.SetName(gen.Name)
resource.SetNamespace(ns.Name)
resource.SetNamespace(ns.GetName())
// Reset resource version
resource.SetResourceVersion("")
_, err = client.CreateResource(gen.Kind, ns.Name, resource, false)
_, err = client.CreateResource(gen.Kind, ns.GetName(), resource, false)
if err != nil {
return err
}

View file

@ -253,6 +253,6 @@ func convertToFloat(value interface{}) (float64, error) {
}
type resourceInfo struct {
resource *unstructured.Unstructured
resource unstructured.Unstructured
gvk *metav1.GroupVersionKind
}

View file

@ -197,6 +197,7 @@ func valFromReferenceToString(value interface{}, operator string) (string, error
}
}
//FormAbsolutePath returns absolute path
func FormAbsolutePath(referencePath, absolutePath string) string {
if filepath.IsAbs(referencePath) {
return referencePath

View file

@ -7,6 +7,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/annotations"
policyLister "github.com/nirmata/kyverno/pkg/client/listers/policy/v1alpha1"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/event"
@ -22,13 +23,14 @@ import (
//Controller watches the 'Namespace' resource creation/update and applied the generation rules on them
type Controller struct {
client *client.Client
namespaceLister v1CoreLister.NamespaceLister
namespaceSynced cache.InformerSynced
policyLister policyLister.PolicyLister
eventController event.Generator
violationBuilder violation.Generator
workqueue workqueue.RateLimitingInterface
client *client.Client
namespaceLister v1CoreLister.NamespaceLister
namespaceSynced cache.InformerSynced
policyLister policyLister.PolicyLister
eventController event.Generator
violationBuilder violation.Generator
annotationsController annotations.Controller
workqueue workqueue.RateLimitingInterface
}
//NewGenController returns a new Controller to manage generation rules
@ -36,17 +38,19 @@ func NewGenController(client *client.Client,
eventController event.Generator,
policyInformer policySharedInformer.PolicyInformer,
violationBuilder violation.Generator,
namespaceInformer v1Informer.NamespaceInformer) *Controller {
namespaceInformer v1Informer.NamespaceInformer,
annotationsController annotations.Controller) *Controller {
// create the controller
controller := &Controller{
client: client,
namespaceLister: namespaceInformer.Lister(),
namespaceSynced: namespaceInformer.Informer().HasSynced,
policyLister: policyInformer.GetLister(),
eventController: eventController,
violationBuilder: violationBuilder,
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), wqNamespace),
client: client,
namespaceLister: namespaceInformer.Lister(),
namespaceSynced: namespaceInformer.Informer().HasSynced,
policyLister: policyInformer.GetLister(),
eventController: eventController,
violationBuilder: violationBuilder,
annotationsController: annotationsController,
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), wqNamespace),
}
namespaceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: controller.createNamespaceHandler,
@ -77,7 +81,7 @@ func (c *Controller) enqueueNamespace(obj interface{}) {
func (c *Controller) Run(stopCh <-chan struct{}) error {
if ok := cache.WaitForCacheSync(stopCh, c.namespaceSynced); !ok {
return fmt.Errorf("faield to wait for caches to sync")
return fmt.Errorf("failed to wait for caches to sync")
}
for i := 0; i < workerCount; i++ {

View file

@ -4,7 +4,10 @@ import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/annotations"
v1alpha1 "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
"github.com/nirmata/kyverno/pkg/engine"
event "github.com/nirmata/kyverno/pkg/event"
@ -12,6 +15,7 @@ import (
violation "github.com/nirmata/kyverno/pkg/violation"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
)
func (c *Controller) processNamespace(ns *corev1.Namespace) error {
@ -62,9 +66,19 @@ func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) {
"",
p.Spec.ValidationFailureAction) // Namespace has no namespace..WOW
ruleInfos := engine.GenerateNew(c.client, p, ns)
// convert to unstructured
unstrMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns)
if err != nil {
glog.Error(err)
return
}
unstObj := unstructured.Unstructured{Object: unstrMap}
ruleInfos := engine.GenerateNew(c.client, p, unstObj)
policyInfo.AddRuleInfos(ruleInfos)
// generate annotations
c.createAnnotations(policyInfo)
if !policyInfo.IsSuccessful() {
glog.Infof("Failed to apply policy %s on resource %s %s", p.Name, ns.Kind, ns.Name)
for _, r := range ruleInfos {
@ -78,9 +92,11 @@ func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) {
if onViolation {
glog.Infof("Adding violation for generation rule of policy %s\n", policyInfo.Name)
// Policy Violation
v := violation.BuldNewViolation(policyInfo.Name, policyInfo.RKind, policyInfo.RNamespace, policyInfo.RName, event.PolicyViolation.String(), policyInfo.GetFailedRules())
c.violationBuilder.Add(v)
} else {
// Event
eventInfo = event.NewEvent(policyKind, "", policyInfo.Name, event.RequestBlocked,
event.FPolicyApplyBlockCreate, policyInfo.RName, policyInfo.GetRuleNames(false))
@ -100,3 +116,31 @@ func (c *Controller) processPolicy(ns *corev1.Namespace, p *v1alpha1.Policy) {
c.eventController.Add(eventInfo)
}
func (c *Controller) createAnnotations(pi *info.PolicyInfo) {
//get resource
obj, err := c.client.GetResource(pi.RKind, pi.RNamespace, pi.RName)
if err != nil {
glog.Error(err)
return
}
// add annotation for policy application
ann := obj.GetAnnotations()
// Generation rules
ann, gpatch, err := annotations.AddPolicyJSONPatch(ann, pi, info.Mutation)
if err != nil {
glog.Error(err)
return
}
if gpatch == nil {
// nothing to patch
return
}
// add the anotation to the resource
_, err = c.client.PatchResource(pi.RKind, pi.RNamespace, pi.RName, gpatch)
if err != nil {
glog.Error(err)
return
}
}