From b7524467a3a8fa22bc8c9f3a1e911fb1e2f38a71 Mon Sep 17 00:00:00 2001 From: Yuvraj <10830562+evalsocket@users.noreply.github.com> Date: Fri, 4 Sep 2020 03:04:23 +0530 Subject: [PATCH] Reconcile Generate request on policy update (#1096) * policy report crd added * added namespaced rule * remove extra field from crd * revert crd change * remove policy report chnages * remove policy report chnages * remove policy report chnages * remove policy report chnages * added logic for gr * revert changes * fixed generate rules * fixed generate rules * fixed generate rules * fixed generate rules * remove extra logs * remove extra logs * fixed e2e test * remove extra logs * crd issue resolved * added check for sync * add labels update * add label update * added permission to role * roles added to helm * roles added to helm --- charts/kyverno/templates/clusterrole.yaml | 2 + cmd/kyverno/main.go | 1 + definitions/crds/crds.yaml | 312 +++++++++++----------- definitions/install.yaml | 2 + definitions/install_debug.yaml | 2 + definitions/k8s-resource/rbac.yaml | 2 + pkg/engine/response/response.go | 1 + pkg/generate/cleanup/controller.go | 4 +- pkg/generate/controller.go | 6 +- pkg/generate/generate.go | 145 ++++++---- pkg/policy/controller.go | 47 +++- pkg/webhooks/generate/generate.go | 3 +- pkg/webhooks/generation.go | 17 +- 13 files changed, 313 insertions(+), 231 deletions(-) diff --git a/charts/kyverno/templates/clusterrole.yaml b/charts/kyverno/templates/clusterrole.yaml index 25e6ff2c86..2f27846027 100644 --- a/charts/kyverno/templates/clusterrole.yaml +++ b/charts/kyverno/templates/clusterrole.yaml @@ -131,6 +131,7 @@ rules: - limitranges - clusterroles - rolebindings + - namespaces - clusterrolebindings {{- range .Values.generatecontrollerExtraResources }} - {{ . }} @@ -139,6 +140,7 @@ rules: - create - update - delete + - list - get # dynamic watches on trigger resources for generate rules # re-evaluate the policy if the resource is updated diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go index d3f9bbe0cc..83ed6b7808 100644 --- a/cmd/kyverno/main.go +++ b/cmd/kyverno/main.go @@ -194,6 +194,7 @@ func main() { pInformer.Kyverno().V1().Policies(), pInformer.Kyverno().V1().ClusterPolicyViolations(), pInformer.Kyverno().V1().PolicyViolations(), + pInformer.Kyverno().V1().GenerateRequests(), configData, eventGenerator, pvgen, diff --git a/definitions/crds/crds.yaml b/definitions/crds/crds.yaml index d57c2721a4..4efe95fb44 100644 --- a/definitions/crds/crds.yaml +++ b/definitions/crds/crds.yaml @@ -14,7 +14,7 @@ spec: plural: clusterpolicies singular: clusterpolicy shortNames: - - cpol + - cpol subresources: status: {} validation: @@ -23,14 +23,14 @@ spec: status: {} spec: required: - - rules + - rules properties: - # default values to be handled by user + # default values to be handled by user validationFailureAction: type: string - enum: - - enforce # blocks the resorce api-reques if a rule fails. - - audit # allows resource creation and reports the failed validation rules as violations. Default + enum: + - enforce # blocks the resorce api-reques if a rule fails. + - audit # allows resource creation and reports the failed validation rules as violations. Default background: type: boolean rules: @@ -38,15 +38,15 @@ spec: items: type: object required: - - name - - match + - name + - match properties: name: type: string match: type: object required: - - resources + - resources properties: roles: type: array @@ -61,8 +61,8 @@ spec: items: type: object required: - - kind - - name + - kind + - name properties: kind: type: string @@ -101,8 +101,8 @@ spec: items: type: object required: - - key - - operator + - key + - operator properties: key: type: string @@ -128,8 +128,8 @@ spec: items: type: object required: - - kind - - name + - kind + - name properties: kind: type: string @@ -167,8 +167,8 @@ spec: items: type: object required: - - key - - operator + - key + - operator properties: key: type: string @@ -183,9 +183,9 @@ spec: items: type: object required: - - key # can be of any type - - operator # typed - - value # can be of any type + - key # can be of any type + - operator # typed + - value # can be of any type mutate: type: object properties: @@ -198,17 +198,17 @@ spec: items: type: object required: - - path - - op + - path + - op properties: path: type: string op: type: string enum: - - add - - replace - - remove + - add + - replace + - remove value: {} validate: type: object @@ -231,24 +231,24 @@ spec: operator: type: string enum: - - Equal - - Equals - - NotEqual - - NotEquals - - In - - NotIn + - Equal + - Equals + - NotEqual + - NotEquals + - In + - NotIn key: type: string value: anyOf: - - type: string - - type: array - items: {} + - type: string + - type: array + items: {} generate: type: object required: - - kind - - name + - kind + - name properties: apiVersion: type: string @@ -260,11 +260,11 @@ spec: type: string synchronize: type: boolean - clone: + clone: type: object required: - - namespace - - name + - namespace + - name properties: namespace: type: string @@ -288,7 +288,7 @@ spec: plural: policies singular: policy shortNames: - - pol + - pol subresources: status: {} validation: @@ -297,14 +297,14 @@ spec: status: {} spec: required: - - rules + - rules properties: - # default values to be handled by user + # default values to be handled by user validationFailureAction: type: string - enum: - - enforce # blocks the resorce api-reques if a rule fails. - - audit # allows resource creation and reports the failed validation rules as violations. Default + enum: + - enforce # blocks the resorce api-reques if a rule fails. + - audit # allows resource creation and reports the failed validation rules as violations. Default background: type: boolean rules: @@ -312,15 +312,15 @@ spec: items: type: object required: - - name - - match + - name + - match properties: name: type: string match: type: object required: - - resources + - resources properties: roles: type: array @@ -335,8 +335,8 @@ spec: items: type: object required: - - kind - - name + - kind + - name properties: kind: type: string @@ -367,8 +367,8 @@ spec: items: type: object required: - - key - - operator + - key + - operator properties: key: type: string @@ -394,8 +394,8 @@ spec: items: type: object required: - - kind - - name + - kind + - name properties: kind: type: string @@ -425,8 +425,8 @@ spec: items: type: object required: - - key - - operator + - key + - operator properties: key: type: string @@ -441,9 +441,9 @@ spec: items: type: object required: - - key # can be of any type - - operator # typed - - value # can be of any type + - key # can be of any type + - operator # typed + - value # can be of any type mutate: type: object properties: @@ -456,17 +456,17 @@ spec: items: type: object required: - - path - - op + - path + - op properties: path: type: string op: type: string enum: - - add - - replace - - remove + - add + - replace + - remove value: {} validate: type: object @@ -489,24 +489,24 @@ spec: operator: type: string enum: - - Equal - - Equals - - NotEqual - - NotEquals - - In - - NotIn + - Equal + - Equals + - NotEqual + - NotEquals + - In + - NotIn key: type: string value: anyOf: - - type: string - - type: array - items: {} + - type: string + - type: array + items: {} generate: type: object required: - - kind - - name + - kind + - name properties: apiVersion: type: string @@ -518,11 +518,11 @@ spec: type: string synchronize: type: boolean - clone: + clone: type: object required: - - namespace - - name + - namespace + - name properties: namespace: type: string @@ -546,41 +546,41 @@ spec: plural: clusterpolicyviolations singular: clusterpolicyviolation shortNames: - - cpolv + - cpolv subresources: status: {} additionalPrinterColumns: - - name: Policy - type: string - description: The policy that resulted in the violation - JSONPath: .spec.policy - - name: ResourceKind - type: string - description: The resource kind that cause the violation - JSONPath: .spec.resource.kind - - name: ResourceName - type: string - description: The resource name that caused the violation - JSONPath: .spec.resource.name - - name: Age - type: date - JSONPath: .metadata.creationTimestamp + - name: Policy + type: string + description: The policy that resulted in the violation + JSONPath: .spec.policy + - name: ResourceKind + type: string + description: The resource kind that cause the violation + JSONPath: .spec.resource.kind + - name: ResourceName + type: string + description: The resource name that caused the violation + JSONPath: .spec.resource.name + - name: Age + type: date + JSONPath: .metadata.creationTimestamp validation: openAPIV3Schema: properties: spec: required: - - policy - - resource - - rules + - policy + - resource + - rules properties: policy: type: string resource: type: object required: - - kind - - name + - kind + - name properties: kind: type: string @@ -591,9 +591,9 @@ spec: items: type: object required: - - name - - type - - message + - name + - type + - message properties: name: type: string @@ -618,41 +618,41 @@ spec: plural: policyviolations singular: policyviolation shortNames: - - polv + - polv subresources: status: {} additionalPrinterColumns: - - name: Policy - type: string - description: The policy that resulted in the violation - JSONPath: .spec.policy - - name: ResourceKind - type: string - description: The resource kind that cause the violation - JSONPath: .spec.resource.kind - - name: ResourceName - type: string - description: The resource name that caused the violation - JSONPath: .spec.resource.name - - name: Age - type: date - JSONPath: .metadata.creationTimestamp + - name: Policy + type: string + description: The policy that resulted in the violation + JSONPath: .spec.policy + - name: ResourceKind + type: string + description: The resource kind that cause the violation + JSONPath: .spec.resource.kind + - name: ResourceName + type: string + description: The resource name that caused the violation + JSONPath: .spec.resource.name + - name: Age + type: date + JSONPath: .metadata.creationTimestamp validation: openAPIV3Schema: properties: spec: required: - - policy - - resource - - rules + - policy + - resource + - rules properties: policy: type: string resource: type: object required: - - kind - - name + - kind + - name properties: kind: type: string @@ -663,9 +663,9 @@ spec: items: type: object required: - - name - - type - - message + - name + - type + - message properties: name: type: string @@ -690,52 +690,52 @@ spec: plural: generaterequests singular: generaterequest shortNames: - - gr + - gr subresources: status: {} additionalPrinterColumns: - - name: Policy - type: string - description: The policy that resulted in the violation - JSONPath: .spec.policy - - name: ResourceKind - type: string - description: The resource kind that cause the violation - JSONPath: .spec.resource.kind - - name: ResourceName - type: string - description: The resource name that caused the violation - JSONPath: .spec.resource.name - - name: ResourceNamespace - type: string - description: The resource namespace that caused the violation - JSONPath: .spec.resource.namespace - - name: status - type : string - description: Current state of generate request - JSONPath: .status.state - - name: Age - type: date - JSONPath: .metadata.creationTimestamp + - name: Policy + type: string + description: The policy that resulted in the violation + JSONPath: .spec.policy + - name: ResourceKind + type: string + description: The resource kind that cause the violation + JSONPath: .spec.resource.kind + - name: ResourceName + type: string + description: The resource name that caused the violation + JSONPath: .spec.resource.name + - name: ResourceNamespace + type: string + description: The resource namespace that caused the violation + JSONPath: .spec.resource.namespace + - name: status + type : string + description: Current state of generate request + JSONPath: .status.state + - name: Age + type: date + JSONPath: .metadata.creationTimestamp validation: openAPIV3Schema: properties: spec: required: - - policy - - resource + - policy + - resource properties: policy: type: string resource: type: object required: - - kind - - name + - kind + - name properties: kind: type: string - name: + name: type: string namespace: - type: string + type: string diff --git a/definitions/install.yaml b/definitions/install.yaml index b6e262c314..9564adeacf 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -832,8 +832,10 @@ rules: - rolebindings - clusterrolebindings - configmaps + - namespaces verbs: - watch + - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/definitions/install_debug.yaml b/definitions/install_debug.yaml index 738d7bea89..ca8d0baee2 100644 --- a/definitions/install_debug.yaml +++ b/definitions/install_debug.yaml @@ -832,8 +832,10 @@ rules: - rolebindings - clusterrolebindings - configmaps + - namespaces verbs: - watch + - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/definitions/k8s-resource/rbac.yaml b/definitions/k8s-resource/rbac.yaml index 60d08a993f..bab25f1201 100644 --- a/definitions/k8s-resource/rbac.yaml +++ b/definitions/k8s-resource/rbac.yaml @@ -158,8 +158,10 @@ rules: - rolebindings - clusterrolebindings - configmaps + - namespaces verbs: - watch + - list --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/pkg/engine/response/response.go b/pkg/engine/response/response.go index eeb37dd2cb..2809ee4abf 100644 --- a/pkg/engine/response/response.go +++ b/pkg/engine/response/response.go @@ -27,6 +27,7 @@ type PolicyResponse struct { Rules []RuleResponse `json:"rules"` // ValidationFailureAction: audit(default if not set),enforce ValidationFailureAction string + } //ResourceSpec resource action applied on diff --git a/pkg/generate/cleanup/controller.go b/pkg/generate/cleanup/controller.go index 95f5e3c60e..9e4f0e2031 100644 --- a/pkg/generate/cleanup/controller.go +++ b/pkg/generate/cleanup/controller.go @@ -175,12 +175,14 @@ func (c *Controller) deleteGR(obj interface{}) { for _, resource := range gr.Status.GeneratedResources { r, err := c.client.GetResource(resource.APIVersion, resource.Kind, resource.Namespace, resource.Name) if err != nil { - logger.Error(err, "Generated resource is not deleted", "Resource", r.GetName()) + logger.Error(err, "Generated resource is not deleted", "Resource", resource.Name) + return } labels := r.GetLabels() if labels["policy.kyverno.io/synchronize"] == "enable" { if err := c.client.DeleteResource(r.GetAPIVersion(), r.GetKind(), r.GetNamespace(), r.GetName(), false); err != nil { logger.Error(err, "Generated resource is not deleted", "Resource", r.GetName()) + return } } } diff --git a/pkg/generate/controller.go b/pkg/generate/controller.go index 4bbdcf2ffa..4eae6fbfbb 100644 --- a/pkg/generate/controller.go +++ b/pkg/generate/controller.go @@ -1,8 +1,6 @@ package generate import ( - "time" - "github.com/go-logr/logr" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned" @@ -20,6 +18,7 @@ import ( "k8s.io/client-go/informers" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" + "time" ) const ( @@ -204,7 +203,8 @@ func (c *Controller) deleteGR(obj interface{}) { for _, resource := range gr.Status.GeneratedResources { r, err := c.client.GetResource(resource.APIVersion, resource.Kind, resource.Namespace, resource.Name) if err != nil { - logger.Error(err, "Generated resource is not deleted", "Resource", r.GetName()) + logger.Error(err, "Generated resource is not deleted", "Resource", resource.Name) + continue } labels := r.GetLabels() if labels["policy.kyverno.io/synchronize"] == "enable" { diff --git a/pkg/generate/generate.go b/pkg/generate/generate.go index 03f3fd80aa..8bc18cea31 100644 --- a/pkg/generate/generate.go +++ b/pkg/generate/generate.go @@ -108,8 +108,8 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern logger.V(4).Info("policy does not apply to resource") return nil, fmt.Errorf("policy %s, dont not apply to resource %v", gr.Spec.Policy, gr.Spec.Resource) } - - for i, r := range engineResponse.PolicyResponse.Rules { + var rules []response.RuleResponse + for _, r := range engineResponse.PolicyResponse.Rules { if !r.Success { grList, err := c.kyvernoClient.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).List(metav1.ListOptions{}) if err != nil { @@ -123,14 +123,24 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern } } } - if len(engineResponse.PolicyResponse.Rules) > 1 { - engineResponse.PolicyResponse.Rules = append(engineResponse.PolicyResponse.Rules[:i], engineResponse.PolicyResponse.Rules[i+1:]...) - continue - } else if len(engineResponse.PolicyResponse.Rules) == 1 { - engineResponse.PolicyResponse.Rules = []response.RuleResponse{} + } else { + rules = append(rules, r) + } + } + engineResponse.PolicyResponse.Rules = []response.RuleResponse{} + + for _, v := range policy.Spec.Rules { + for _, r := range rules { + if policy.Name == engineResponse.PolicyResponse.Policy && r.Name == v.Name { + if len(v.MatchResources.ResourceDescription.Kinds) > 0 && (len(v.MatchResources.ResourceDescription.Annotations) == 0 || len(v.MatchResources.ResourceDescription.Selector.MatchLabels) == 0) { + continue + } else { + engineResponse.PolicyResponse.Rules = append(engineResponse.PolicyResponse.Rules, r) + } } } } + // Apply the generate rule on resource return c.applyGeneratePolicy(logger, policyContext, gr) } @@ -160,7 +170,23 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext engine.P continue } startTime := time.Now() - genResource, err := applyRule(log, c.client, rule, resource, ctx, policy.Name, gr) + + processExisting := false + + for _, v := range policy.Spec.Rules { + if policy.Name == gr.Spec.Policy { + if len(v.MatchResources.ResourceDescription.Kinds) > 0 && (len(v.MatchResources.ResourceDescription.Annotations) == 0 || len(v.MatchResources.ResourceDescription.Selector.MatchLabels) == 0) { + processExisting = func() bool { + rcreationTime := resource.GetCreationTimestamp() + pcreationTime := policy.GetCreationTimestamp() + return rcreationTime.Before(&pcreationTime) + }() + } + } + } + + genResource, err := applyRule(log, c.client, rule, resource, ctx, policy.Name, gr, processExisting) + if err != nil { return nil, err } @@ -217,7 +243,7 @@ func updateGenerateExecutionTime(newTime time.Duration, oldAverageTimeString str return time.Duration(newAverageTimeInNanoSeconds) * time.Nanosecond } -func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resource unstructured.Unstructured, ctx context.EvalInterface, policy string, gr kyverno.GenerateRequest) (kyverno.ResourceSpec, error) { +func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resource unstructured.Unstructured, ctx context.EvalInterface, policy string, gr kyverno.GenerateRequest, processExisting bool) (kyverno.ResourceSpec, error) { var rdata map[string]interface{} var err error var mode ResourceMode @@ -227,7 +253,6 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou if err != nil { return noGenResource, err } - // Variable substitutions // format : {{<variable_name}} // - if there is variables that are not defined the context -> results in error and rule is not applied @@ -255,7 +280,6 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou if err != nil { return noGenResource, err } - // Resource to be generated newGenResource := kyverno.ResourceSpec{ APIVersion: genAPIVersion, @@ -271,7 +295,6 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou if err != nil { return noGenResource, err } - if genData != nil { rdata, mode, err = manageData(log, genAPIVersion, genKind, genNamespace, genName, genData, client, resource) } else { @@ -286,7 +309,9 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou // existing resource contains the configuration return newGenResource, nil } - + if processExisting { + return noGenResource, nil + } logger := log.WithValues("genKind", genKind, "genAPIVersion", genAPIVersion, "genNamespace", genNamespace, "genName", genName) // build the resource template @@ -297,24 +322,23 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou if newResource.GetKind() == "" { newResource.SetKind(genKind) } + newResource.SetAPIVersion(genAPIVersion) // manage labels // - app.kubernetes.io/managed-by: kyverno // - kyverno.io/generated-by: kind/namespace/name (trigger resource) manageLabels(newResource, resource) - // Add Synchronize label label := newResource.GetLabels() - if rule.Generation.Synchronize { - label["policy.kyverno.io/synchronize"] = "enable" - } else { - label["policy.kyverno.io/synchronize"] = "disable" - } label["policy.kyverno.io/policy-name"] = policy label["policy.kyverno.io/gr-name"] = gr.Name newResource.SetLabels(label) - if mode == Create { + if rule.Generation.Synchronize { + label["policy.kyverno.io/synchronize"] = "enable" + } else { + label["policy.kyverno.io/synchronize"] = "disable" + } // Reset resource version newResource.SetResourceVersion("") // Create the resource @@ -327,25 +351,48 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou logger.V(4).Info("created new resource") } else if mode == Update { + var isUpdate bool label := newResource.GetLabels() - if label != nil { - if rule.Generation.Synchronize { - logger.V(4).Info("updating existing resource") - // Update the resource - _, err := client.UpdateResource(genAPIVersion, genKind, genNamespace, newResource, false) - if err != nil { - logger.Error(err, "updating existing resource") - // Failed to update resource - return noGenResource, err - } - logger.V(4).Info("updated new resource") - - } else { - logger.V(4).Info("Synchronize resource is disabled") + isUpdate = false + if rule.Generation.Synchronize { + if label["policy.kyverno.io/synchronize"] == "enable" { + isUpdate = true } } else { - logger.V(4).Info("Synchronize resource is disabled") + if label["policy.kyverno.io/synchronize"] == "enable" { + isUpdate = true + } } + if rule.Generation.Synchronize { + label["policy.kyverno.io/synchronize"] = "enable" + } else { + label["policy.kyverno.io/synchronize"] = "disable" + } + if isUpdate { + logger.V(4).Info("updating existing resource") + // Update the resource + newResource.SetLabels(label) + _, err := client.UpdateResource(genAPIVersion, genKind, genNamespace, newResource, false) + if err != nil { + logger.Error(err, "updating existing resource") + // Failed to update resource + return noGenResource, err + } + logger.V(4).Info("updated new resource") + }else{ + resource := &unstructured.Unstructured{} + resource.SetUnstructuredContent(rdata) + resource.SetLabels(label) + _, err := client.UpdateResource(genAPIVersion, genKind, genNamespace, resource, false) + if err != nil { + logger.Error(err, "updating existing resource") + // Failed to update resource + return noGenResource, err + } + logger.V(4).Info("updated new resource") + } + logger.V(4).Info("Synchronize resource is disabled") + } return newGenResource, nil } @@ -353,24 +400,19 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou func manageData(log logr.Logger, apiVersion, kind, namespace, name string, data map[string]interface{}, client *dclient.Client, resource unstructured.Unstructured) (map[string]interface{}, ResourceMode, error) { // check if resource to be generated exists obj, err := client.GetResource(apiVersion, kind, namespace, name) - if apierrors.IsNotFound(err) { - log.Error(err, "resource does not exist, will try to create", "genKind", kind, "genAPIVersion", apiVersion, "genNamespace", namespace, "genName", name) - return data, Create, nil - } if err != nil { + if apierrors.IsNotFound(err) { + log.Error(err, "resource does not exist, will try to create", "genKind", kind, "genAPIVersion", apiVersion, "genNamespace", namespace, "genName", name) + return data, Create, nil + } //something wrong while fetching resource // client-errors return nil, Skip, err } - // Resource exists; verfiy the content of the resource - err = checkResource(log, data, obj) - if err == nil { - // Existing resource does contain the mentioned configuration in spec, skip processing the resource as it is already in expected state - return nil, Skip, nil - } - log.Info("to be generated resoruce already exists, but is missing the specifeid configurations, will try to update", "genKind", kind, "genAPIVersion", apiVersion, "genNamespace", namespace, "genName", name) - return data, Update, nil - + updateObj := &unstructured.Unstructured{} + updateObj.SetUnstructuredContent(data) + updateObj.SetResourceVersion(obj.GetResourceVersion()) + return updateObj.UnstructuredContent(), Update, nil } func manageClone(log logr.Logger, apiVersion, kind, namespace, name string, clone map[string]interface{}, client *dclient.Client, resource unstructured.Unstructured) (map[string]interface{}, ResourceMode, error) { @@ -409,13 +451,6 @@ func manageClone(log logr.Logger, apiVersion, kind, namespace, name string, clon return obj.UnstructuredContent(), Update, nil } - //TODO: check this - if !apierrors.IsNotFound(err) { - log.Error(err, "reference/clone resource is not found", "genKind", kind, "genAPIVersion", apiVersion, "genNamespace", namespace, "genName", name) - //something wrong while fetching resource - return nil, Skip, err - } - // create the resource based on the reference clone return obj.UnstructuredContent(), Create, nil diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go index 8cfc6c18f0..da5391d426 100644 --- a/pkg/policy/controller.go +++ b/pkg/policy/controller.go @@ -1,6 +1,9 @@ package policy import ( + "fmt" + "k8s.io/apimachinery/pkg/labels" + "math/rand" "time" informers "k8s.io/client-go/informers/core/v1" @@ -58,6 +61,9 @@ type PolicyController struct { // npLister can list/get namespace policy from the shared informer's store npLister kyvernolister.PolicyLister + // grLister can list/get generate request from the shared informer's store + grLister kyvernolister.GenerateRequestLister + // pvLister can list/get policy violation from the shared informer's store cpvLister kyvernolister.ClusterPolicyViolationLister @@ -82,6 +88,8 @@ type PolicyController struct { // nsListerSynced returns true if the namespace store has been synced at least once nsListerSynced cache.InformerSynced + // grListerSynced returns true if the generate request store has been synced at least once + grListerSynced cache.InformerSynced // Resource manager, manages the mapping for already processed resource rm resourceManager @@ -91,6 +99,7 @@ type PolicyController struct { // policy violation generator pvGenerator policyviolation.GeneratorInterface + // resourceWebhookWatcher queues the webhook creation request, creates the webhook resourceWebhookWatcher *webhookconfig.ResourceWebhookRegister @@ -104,6 +113,7 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, npInformer kyvernoinformer.PolicyInformer, cpvInformer kyvernoinformer.ClusterPolicyViolationInformer, nspvInformer kyvernoinformer.PolicyViolationInformer, + grInformer kyvernoinformer.GenerateRequestInformer, configHandler config.Interface, eventGen event.Interface, pvGenerator policyviolation.GeneratorInterface, resourceWebhookWatcher *webhookconfig.ResourceWebhookRegister, @@ -162,12 +172,13 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset, pc.cpvLister = cpvInformer.Lister() pc.nspvLister = nspvInformer.Lister() pc.nsLister = namespaces.Lister() - + pc.grLister = grInformer.Lister() pc.pListerSynced = pInformer.Informer().HasSynced pc.npListerSynced = npInformer.Informer().HasSynced pc.cpvListerSynced = cpvInformer.Informer().HasSynced pc.nspvListerSynced = nspvInformer.Informer().HasSynced pc.nsListerSynced = namespaces.Informer().HasSynced + pc.grListerSynced = grInformer.Informer().HasSynced // resource manager // rebuild after 300 seconds/ 5 mins @@ -308,7 +319,7 @@ func (pc *PolicyController) Run(workers int, stopCh <-chan struct{}) { logger.Info("starting") defer logger.Info("shutting down") - if !cache.WaitForCacheSync(stopCh, pc.pListerSynced, pc.cpvListerSynced, pc.nspvListerSynced, pc.nsListerSynced) { + if !cache.WaitForCacheSync(stopCh, pc.pListerSynced, pc.cpvListerSynced, pc.nspvListerSynced, pc.nsListerSynced,pc.grListerSynced) { logger.Info("failed to sync informer cache") return } @@ -373,6 +384,11 @@ func (pc *PolicyController) syncPolicy(key string) error { namespace, key, isNamespacedPolicy := getIsNamespacedPolicy(key) var policy *kyverno.ClusterPolicy var err error + grList, err := pc.grLister.List(labels.Everything()) + if err != nil { + logger.Error(err, "failed to list generate request") + } + if !isNamespacedPolicy { policy, err = pc.pLister.Get(key) } else { @@ -380,9 +396,17 @@ func (pc *PolicyController) syncPolicy(key string) error { nspolicy, err = pc.npLister.Policies(namespace).Get(key) policy = convertPolicyToClusterPolicy(nspolicy) } - if errors.IsNotFound(err) { - go pc.deletePolicyViolations(key) + if errors.IsNotFound(err) { + for _, v := range grList { + if key == v.Spec.Policy { + err := pc.kyvernoClient.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).Delete(v.GetName(),&metav1.DeleteOptions{}) + if err != nil { + logger.Error(err, "failed to delete gr") + } + } + } + go pc.deletePolicyViolations(key) // remove webhook configurations if there are no policies if err := pc.removeResourceWebhookConfiguration(); err != nil { logger.Error(err, "failed to remove resource webhook configurations") @@ -390,11 +414,20 @@ func (pc *PolicyController) syncPolicy(key string) error { return nil } - - if err != nil { - return err + for _, v := range grList { + if policy.Name == v.Spec.Policy { + v.SetLabels(map[string]string{ + "policy-update" :fmt.Sprintf("revision-count-%d",rand.Intn(100000)), + }) + _,err := pc.kyvernoClient.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).Update(v) + if err != nil { + logger.Error(err, "failed to update gr") + return err + } + } } + pc.resourceWebhookWatcher.RegisterResourceWebhook() engineResponses := pc.processExistingResources(policy) diff --git a/pkg/webhooks/generate/generate.go b/pkg/webhooks/generate/generate.go index 6066b674e5..c2a707c266 100644 --- a/pkg/webhooks/generate/generate.go +++ b/pkg/webhooks/generate/generate.go @@ -90,6 +90,7 @@ func (g *Generator) processApply() { func (g *Generator) generate(grSpec kyverno.GenerateRequestSpec, action v1beta1.Operation) error { // create/update a generate request + if err := retryApplyResource(g.client, grSpec, g.log, action); err != nil { return err } @@ -125,10 +126,10 @@ func retryApplyResource(client *kyvernoclient.Clientset, } for i, v := range grList.Items { if grSpec.Policy == v.Spec.Policy && grSpec.Resource.Name == v.Spec.Resource.Name && grSpec.Resource.Kind == v.Spec.Resource.Kind && grSpec.Resource.Namespace == v.Spec.Resource.Namespace { - gr.SetLabels(map[string]string{ "resources-update": "true", }) + v.Spec.Context = gr.Spec.Context v.Spec.Policy = gr.Spec.Policy v.Spec.Resource = gr.Spec.Resource diff --git a/pkg/webhooks/generation.go b/pkg/webhooks/generation.go index dab3ef07eb..5f1a910c23 100644 --- a/pkg/webhooks/generation.go +++ b/pkg/webhooks/generation.go @@ -47,10 +47,11 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic } // engine.Generate returns a list of rules that are applicable on this resource + var rules []response.RuleResponse for _, policy := range policies { policyContext.Policy = *policy engineResponse := engine.Generate(policyContext) - for i, rule := range engineResponse.PolicyResponse.Rules { + for _, rule := range engineResponse.PolicyResponse.Rules { if !rule.Success { grList, err := ws.kyvernoClient.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).List(metav1.ListOptions{}) if err != nil { @@ -64,15 +65,14 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic } } } - if len(engineResponse.PolicyResponse.Rules) > 1 { - engineResponse.PolicyResponse.Rules = append(engineResponse.PolicyResponse.Rules[:i], engineResponse.PolicyResponse.Rules[i+1:]...) - continue - } else if len(engineResponse.PolicyResponse.Rules) == 1 { - engineResponse.PolicyResponse.Rules = []response.RuleResponse{} - } + }else{ + rules = append(rules,rule) } } - if len(engineResponse.PolicyResponse.Rules) > 0 { + + + if len(rules) > 0 { + engineResponse.PolicyResponse.Rules = rules // some generate rules do apply to the resource engineResponses = append(engineResponses, engineResponse) ws.statusListener.Send(generateStats{ @@ -81,6 +81,7 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic } } + // Adds Generate Request to a channel(queue size 1000) to generators if failedResponse := applyGenerateRequest(ws.grGenerator, userRequestInfo, request.Operation, engineResponses...); err != nil { // report failure event