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

Generate policy does not work on namespace update (#1085)

* added logic for handling generate request

* generate rules added

* added label condition for generate

* remove extra logs

* remove extra logs

* buf fixed

* bug fixed

* added logic for delete gr

* log fixed

* documentation changed

* remove best practices changes

* bug fix

* added best pratice
This commit is contained in:
Yuvraj 2020-08-31 23:55:13 +05:30 committed by GitHub
parent a827f88dc7
commit 2641120907
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 173 additions and 39 deletions

View file

@ -22,6 +22,12 @@ spec:
resources:
kinds:
- Namespace
exclude:
namespaces:
- "kube-system"
- "default"
- "kube-public"
- "kyverno"
generate:
synchronize: true
kind: ConfigMap
@ -49,6 +55,12 @@ spec:
resources:
kinds:
- Namespace
exclude:
namespaces:
- "kube-system"
- "default"
- "kube-public"
- "kyverno"
generate:
kind: ConfigMap # Kind of resource
name: default-config # Name of the new Resource
@ -75,7 +87,7 @@ spec:
purpose: mongo
````
In this example new namespaces will receive 2 new resources after its creation:
In this example each namespaces will receive 2 new resources:
* A `ConfigMap` cloned from `default/config-template`.
* A `Secret` with values `DB_USER` and `DB_PASSWORD`, and label `purpose: mongo`.
@ -94,6 +106,12 @@ spec:
kinds:
- Namespace
name: "*"
exclude:
namespaces:
- "kube-system"
- "default"
- "kube-public"
- "kyverno"
generate:
kind: NetworkPolicy
name: deny-all-traffic

View file

@ -35,7 +35,14 @@ func filterRule(rule kyverno.Rule, resource unstructured.Unstructured, admission
startTime := time.Now()
if err := MatchesResourceDescription(resource, rule, admissionInfo, excludeGroupRole); err != nil {
return nil
return &response.RuleResponse{
Name: rule.Name,
Type: "Generation",
Success: false,
RuleStats: response.RuleStats{
ProcessingTime: time.Since(startTime),
},
}
}
// operate on the copy of the conditions, as we perform variable substitution
copyConditions := copyConditions(rule.Conditions)

View file

@ -172,6 +172,18 @@ func (c *Controller) deleteGR(obj interface{}) {
return
}
}
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())
}
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())
}
}
}
logger.V(4).Info("deleting Generate Request CR", "name", gr.Name)
// sync Handler will remove it from the queue
c.enqueueGR(gr)
@ -262,6 +274,7 @@ func (c *Controller) syncGenerateRequest(key string) error {
if err != nil {
return err
}
_, err = c.pLister.Get(gr.Spec.Policy)
if err != nil {
if !errors.IsNotFound(err) {

View file

@ -201,6 +201,18 @@ func (c *Controller) deleteGR(obj interface{}) {
return
}
}
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())
}
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())
}
}
}
logger.Info("deleting generate request", "name", gr.Name)
// sync Handler will remove it from the queue
c.enqueueGR(gr)

View file

@ -3,18 +3,20 @@ package generate
import (
"encoding/json"
"fmt"
"reflect"
"time"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/config"
dclient "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
"github.com/nirmata/kyverno/pkg/engine/validate"
"github.com/nirmata/kyverno/pkg/engine/variables"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"reflect"
"time"
)
func (c *Controller) processGR(gr *kyverno.GenerateRequest) error {
@ -107,6 +109,28 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
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 {
if !r.Success {
grList, err := c.kyvernoClient.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).List(metav1.ListOptions{})
if err != nil {
continue
}
for _, v := range grList.Items {
if engineResponse.PolicyResponse.Policy == v.Spec.Policy && engineResponse.PolicyResponse.Resource.Name == v.Spec.Resource.Name && engineResponse.PolicyResponse.Resource.Kind == v.Spec.Resource.Kind && engineResponse.PolicyResponse.Resource.Namespace == v.Spec.Resource.Namespace{
err :=c.kyvernoClient.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).Delete(v.GetName(),&metav1.DeleteOptions{})
if err != nil {
logger.Error(err, " failed to delete generate request")
}
}
}
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{}
}
}
}
// Apply the generate rule on resource
return c.applyGeneratePolicy(logger, policyContext, gr)
}
@ -128,12 +152,7 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext engine.P
policy := policyContext.Policy
resource := policyContext.NewResource
ctx := policyContext.Context
// To manage existing resources, we compare the creation time for the default resiruce to be generated and policy creation time
processExisting := func() bool {
rcreationTime := resource.GetCreationTimestamp()
pcreationTime := policy.GetCreationTimestamp()
return rcreationTime.Before(&pcreationTime)
}()
// To manage existing resources, we compare the creation time for the default resource to be generated and policy creation time
ruleNameToProcessingTime := make(map[string]time.Duration)
for _, rule := range policy.Spec.Rules {
@ -141,7 +160,7 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext engine.P
continue
}
startTime := time.Now()
genResource, err := applyRule(log, c.client, rule, resource, ctx, processExisting, policy.Name)
genResource, err := applyRule(log, c.client, rule, resource, ctx, policy.Name,gr)
if err != nil {
return nil, err
}
@ -198,7 +217,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, processExisting bool, policy string) (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) (kyverno.ResourceSpec, error) {
var rdata map[string]interface{}
var err error
var mode ResourceMode
@ -258,6 +277,7 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
} else {
rdata, mode, err = manageClone(log, genAPIVersion, genKind, genNamespace, genName, genCopy, client, resource)
}
if err != nil {
return noGenResource, err
}
@ -266,13 +286,10 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
// existing resource contains the configuration
return newGenResource, nil
}
if processExisting {
// handle existing resources
// policy was generated after the resource
// we do not create new resource
return noGenResource, err
}
logger := log.WithValues("genKind", genKind, "genAPIVersion", genAPIVersion, "genNamespace", genNamespace, "genName", genName)
// build the resource template
newResource := &unstructured.Unstructured{}
@ -283,12 +300,10 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
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)
logger := log.WithValues("genKind", genKind, "genAPIVersion", genAPIVersion, "genNamespace", genNamespace, "genName", genName)
// Add Synchronize label
label := newResource.GetLabels()
@ -298,6 +313,7 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
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 {

View file

@ -4,14 +4,14 @@ import (
"fmt"
"time"
"k8s.io/api/admission/v1beta1"
backoff "github.com/cenkalti/backoff"
"github.com/go-logr/logr"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
"github.com/nirmata/kyverno/pkg/config"
"github.com/nirmata/kyverno/pkg/constant"
"k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
)
@ -111,20 +111,41 @@ func retryApplyResource(client *kyvernoclient.Clientset,
gr := kyverno.GenerateRequest{
Spec: grSpec,
}
gr.SetGenerateName("gr-")
gr.SetNamespace(config.KubePolicyNamespace)
// Initial state "Pending"
// TODO: status is not updated
// gr.Status.State = kyverno.Pending
// generate requests created in kyverno namespace
if action == v1beta1.Create {
_, err = client.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).Create(&gr)
}
if action == v1beta1.Update {
gr.SetLabels(map[string]string{
"resources-update": "true",
})
_, err = client.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).Update(&gr)
isExist := false
if action == v1beta1.Create || action == v1beta1.Update {
grList, err := client.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).List(metav1.ListOptions{})
if err != nil {
return err
}
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
_, err = client.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).Update(&grList.Items[i])
if err != nil {
return err
}
isExist = true
}
}
if !isExist {
gr.SetGenerateName("gr-")
_, err = client.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).Create(&gr)
if err != nil {
return err
}
}
}
log.V(4).Info("retrying update generate request CR", "retryCount", i, "name", gr.GetGenerateName(), "namespace", gr.GetNamespace())

View file

@ -2,14 +2,9 @@ package webhooks
import (
"fmt"
"github.com/nirmata/kyverno/pkg/config"
"reflect"
"sort"
"strings"
"time"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
"github.com/nirmata/kyverno/pkg/config"
"github.com/nirmata/kyverno/pkg/engine"
"github.com/nirmata/kyverno/pkg/engine/context"
"github.com/nirmata/kyverno/pkg/engine/response"
@ -17,7 +12,12 @@ import (
"github.com/nirmata/kyverno/pkg/event"
"github.com/nirmata/kyverno/pkg/webhooks/generate"
v1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"reflect"
"sort"
"strings"
"time"
)
//HandleGenerate handles admission-requests for policies with generate rules
@ -50,6 +50,28 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
for _, policy := range policies {
policyContext.Policy = *policy
engineResponse := engine.Generate(policyContext)
for i, rule := range engineResponse.PolicyResponse.Rules {
if !rule.Success {
grList, err := ws.kyvernoClient.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).List(metav1.ListOptions{})
if err != nil {
logger.Error(err, "failed to list generate request")
}
for _, v := range grList.Items {
if engineResponse.PolicyResponse.Policy == v.Spec.Policy && engineResponse.PolicyResponse.Resource.Name == v.Spec.Resource.Name && engineResponse.PolicyResponse.Resource.Kind == v.Spec.Resource.Kind && engineResponse.PolicyResponse.Resource.Namespace == v.Spec.Resource.Namespace {
err := ws.kyvernoClient.KyvernoV1().GenerateRequests(config.KubePolicyNamespace).Delete(v.GetName(),&metav1.DeleteOptions{})
if err != nil {
logger.Error(err, "failed to update gr")
}
}
}
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{}
}
}
}
if len(engineResponse.PolicyResponse.Rules) > 0 {
// some generate rules do apply to the resource
engineResponses = append(engineResponses, engineResponse)
@ -57,8 +79,8 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
resp: engineResponse,
})
}
}
}
// 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

View file

@ -21,6 +21,12 @@ spec:
kinds:
- Namespace
name: "*"
exclude:
namespaces:
- "kube-system"
- "default"
- "kube-public"
- "kyverno"
generate:
kind: NetworkPolicy
name: default-deny-ingress
@ -31,4 +37,5 @@ spec:
podSelector: {}
policyTypes:
- Ingress
````

View file

@ -22,6 +22,12 @@ spec:
resources:
kinds:
- Namespace
exclude:
namespaces:
- "kube-system"
- "default"
- "kube-public"
- "kyverno"
generate:
kind: ResourceQuota
name: default-resourcequota

View file

@ -19,6 +19,12 @@ spec:
kinds:
- Namespace
name: "*"
exclude:
namespaces:
- "kube-system"
- "default"
- "kube-public"
- "kyverno"
generate:
kind: NetworkPolicy
name: default-deny-ingress

View file

@ -14,6 +14,12 @@ spec:
resources:
kinds:
- Namespace
exclude:
namespaces:
- "kube-system"
- "default"
- "kube-public"
- "kyverno"
generate:
kind: ResourceQuota
name: default-resourcequota