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

Merge pull request #1405 from NoSkillGirl/bug/1374_generate_rule_with_synchronization

enqueing gr on getting deleted
This commit is contained in:
Jim Bugwadia 2020-12-31 16:40:01 -08:00 committed by GitHub
commit 5edb8b3ee6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 373 additions and 106 deletions

View file

@ -9,6 +9,7 @@ import (
"os"
"time"
backwardcompatibility "github.com/kyverno/kyverno/pkg/backward_compatibility"
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions"
"github.com/kyverno/kyverno/pkg/config"
@ -328,6 +329,7 @@ func main() {
log.Log.WithName("WebhookServer"),
openAPIController,
rCache,
grc,
)
if err != nil {
@ -355,6 +357,9 @@ func main() {
// verifies if the admission control is enabled and active
server.RunAsync(stopCh)
go backwardcompatibility.AddLabels(pclient, pInformer.Kyverno().V1().GenerateRequests())
go backwardcompatibility.AddCloneLabel(client, pInformer.Kyverno().V1().ClusterPolicies())
<-stopCh
// by default http.Server waits indefinitely for connections to return to idle and then shuts down

1
go.mod
View file

@ -27,6 +27,7 @@ require (
github.com/onsi/gomega v1.8.1
github.com/ory/go-acc v0.2.6 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/common v0.4.1
github.com/sirupsen/logrus v1.6.0 // indirect
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.5.1

4
go.sum
View file

@ -40,7 +40,9 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrU
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/ahmetb/gen-crd-api-reference-docs v0.1.5/go.mod h1:P/XzJ+c2+khJKNKABcm2biRwk2QAuwbLf8DlXuaL7WM=
github.com/alecthomas/participle v0.2.1/go.mod h1:SW6HZGeZgSIpcUWX3fXpfZhuaWHnmoD5KCVaqSaNTkk=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
@ -583,6 +585,7 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@ -896,6 +899,7 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -0,0 +1,146 @@
package backwardcompatibility
import (
"context"
"fmt"
"strings"
"time"
kyvernoclient "github.com/kyverno/kyverno/pkg/client/clientset/versioned"
kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions/kyverno/v1"
"github.com/kyverno/kyverno/pkg/config"
dclient "github.com/kyverno/kyverno/pkg/dclient"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"sigs.k8s.io/controller-runtime/pkg/log"
)
// AddLabels - adds labels to all the existing generate requests
func AddLabels(client *kyvernoclient.Clientset, grInformer kyvernoinformer.GenerateRequestInformer) {
// Get all the GR's that are existing
// Extract and Update all of them with the with the labels
grList, err := grInformer.Lister().List(labels.NewSelector())
if err != nil {
log.Log.Error(err, "failed to get generate request list")
return
}
for _, gr := range grList {
grLabels := gr.Labels
if grLabels == nil || len(grLabels) == 0 {
grLabels = make(map[string]string)
}
grLabels["generate.kyverno.io/policy-name"] = gr.Spec.Policy
grLabels["generate.kyverno.io/resource-name"] = gr.Spec.Resource.Name
grLabels["generate.kyverno.io/resource-kind"] = gr.Spec.Resource.Kind
grLabels["generate.kyverno.io/resource-namespace"] = gr.Spec.Resource.Namespace
gr.SetLabels(grLabels)
_, err = client.KyvernoV1().GenerateRequests(config.KyvernoNamespace).Update(context.TODO(), gr, metav1.UpdateOptions{})
if err != nil {
log.Log.V(4).Info(fmt.Sprintf("failed to update the GR %v. error: %v", gr.Name, err))
for n := 0; n <= 3; n++ {
log.Log.V(4).Info(fmt.Sprintf("retrying to get GR %v", gr.Name))
time.Sleep(100 * time.Millisecond)
errInGettingGR := addLabelForGR(gr.Name, gr.Namespace, client, grInformer)
if errInGettingGR != nil {
continue
} else {
break
}
}
}
}
return
}
func addLabelForGR(name string, namespace string, client *kyvernoclient.Clientset, grInformer kyvernoinformer.GenerateRequestInformer) error {
gr, err := grInformer.Lister().GenerateRequests(namespace).Get(name)
if err != nil {
log.Log.Error(err, fmt.Sprintf("failed to update the GR %v", name))
return err
}
grLabels := gr.Labels
if grLabels == nil || len(grLabels) == 0 {
grLabels = make(map[string]string)
}
grLabels["generate.kyverno.io/policy-name"] = gr.Spec.Policy
grLabels["generate.kyverno.io/resource-name"] = gr.Spec.Resource.Name
grLabels["generate.kyverno.io/resource-kind"] = gr.Spec.Resource.Kind
grLabels["generate.kyverno.io/resource-namespace"] = gr.Spec.Resource.Namespace
gr.SetLabels(grLabels)
_, err = client.KyvernoV1().GenerateRequests(config.KyvernoNamespace).Update(context.TODO(), gr, metav1.UpdateOptions{})
if err != nil {
log.Log.Error(err, fmt.Sprintf("failed to update the GR %v", gr.Name))
return err
}
return nil
}
// AddCloneLabel - add label to the source resource about the new clone
func AddCloneLabel(client *dclient.Client, pInformer kyvernoinformer.ClusterPolicyInformer) {
// Get all the Generate Policies which has clone
// Get the resource with Kind, NameSpace, Name
// Add Policy name if label not found
policies, err := pInformer.Lister().List(labels.NewSelector())
if err != nil {
log.Log.Error(err, "failed to get policies")
return
}
for _, policy := range policies {
for _, rule := range policy.Spec.Rules {
if rule.HasGenerate() {
clone := rule.Generation.Clone
if clone.Name != "" {
namespace := clone.Namespace
name := clone.Name
kind := rule.Generation.Kind
obj, err := client.GetResource("", kind, namespace, name)
if err != nil {
log.Log.Error(err, fmt.Sprintf("source not found name:%v namespace:%v kind:%v", name, namespace, kind))
continue
}
updateSource := true
// add label
label := obj.GetLabels()
if len(label) == 0 {
label = make(map[string]string)
label["generate.kyverno.io/clone-policy-name"] = policy.GetName()
} else {
if label["generate.kyverno.io/clone-policy-name"] != "" {
policyNames := label["generate.kyverno.io/clone-policy-name"]
if !strings.Contains(policyNames, policy.GetName()) {
policyNames = policyNames + "," + policy.GetName()
label["generate.kyverno.io/clone-policy-name"] = policyNames
} else {
updateSource = false
}
} else {
label["generate.kyverno.io/clone-policy-name"] = policy.GetName()
}
}
if updateSource {
log.Log.V(4).Info("updating existing clone source")
obj.SetLabels(label)
_, err = client.UpdateResource(obj.GetAPIVersion(), kind, namespace, obj, false)
if err != nil {
log.Log.Error(err, fmt.Sprintf("failed to update source name:%v namespace:%v kind:%v\n", obj.GetName(), obj.GetNamespace(), obj.GetKind()))
return
}
log.Log.V(4).Info(fmt.Sprintf("updated source name:%v namespace:%v kind:%v\n", obj.GetName(), obj.GetNamespace(), obj.GetKind()))
}
}
}
}
}
}

View file

@ -144,10 +144,10 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
if !r.Success {
logger.V(4).Info("querying all generate requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{
"policyName": engineResponse.PolicyResponse.Policy,
"resourceName": engineResponse.PolicyResponse.Resource.Name,
"resourceKind": engineResponse.PolicyResponse.Resource.Kind,
"ResourceNamespace": engineResponse.PolicyResponse.Resource.Namespace,
"generate.kyverno.io/policy-name": engineResponse.PolicyResponse.Policy,
"generate.kyverno.io/resource-name": engineResponse.PolicyResponse.Resource.Name,
"generate.kyverno.io/resource-kind": engineResponse.PolicyResponse.Resource.Kind,
"generate.kyverno.io/resource-namespace": engineResponse.PolicyResponse.Resource.Namespace,
}))
grList, err := c.grLister.List(selector)
if err != nil {
@ -179,9 +179,7 @@ func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateReque
return statusControl.Success(gr, genResources)
}
func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext engine.PolicyContext, gr kyverno.GenerateRequest, applicableRules []string) ([]kyverno.ResourceSpec, error) {
// List of generatedResources
var genResources []kyverno.ResourceSpec
func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext engine.PolicyContext, gr kyverno.GenerateRequest, applicableRules []string) (genResources []kyverno.ResourceSpec, err error) {
// Get the response as the actions to be performed on the resource
// - - substitute values
policy := policyContext.Policy
@ -203,14 +201,13 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext engine.P
startTime := time.Now()
processExisting := false
var genResource kyverno.ResourceSpec
if len(rule.MatchResources.Kinds) > 0 {
if len(rule.MatchResources.Annotations) == 0 && rule.MatchResources.Selector == nil {
processExisting = func() bool {
rcreationTime := resource.GetCreationTimestamp()
pcreationTime := policy.GetCreationTimestamp()
return rcreationTime.Before(&pcreationTime)
}()
rcreationTime := resource.GetCreationTimestamp()
pcreationTime := policy.GetCreationTimestamp()
processExisting = rcreationTime.Before(&pcreationTime)
}
}
@ -220,15 +217,16 @@ func (c *Controller) applyGeneratePolicy(log logr.Logger, policyContext engine.P
return nil, err
}
genResource, err := applyRule(log, c.client, rule, resource, jsonContext, policy.Name, gr, processExisting)
if err != nil {
log.Error(err, "failed to apply generate rule", "policy", policy.Name,
"rule", rule.Name, "resource", resource.GetName())
return nil, err
if !processExisting {
genResource, err = applyRule(log, c.client, rule, resource, jsonContext, policy.Name, gr)
if err != nil {
log.Error(err, "failed to apply generate rule", "policy", policy.Name,
"rule", rule.Name, "resource", resource.GetName())
return nil, err
}
ruleNameToProcessingTime[rule.Name] = time.Since(startTime)
genResources = append(genResources, genResource)
}
ruleNameToProcessingTime[rule.Name] = time.Since(startTime)
genResources = append(genResources, genResource)
}
if gr.Status.State == "" && len(genResources) > 0 {
@ -300,12 +298,11 @@ func getResourceInfo(object map[string]interface{}) (kind, name, namespace, apiv
return
}
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) {
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
var noGenResource kyverno.ResourceSpec
genUnst, err := getUnstrRule(rule.Generation.DeepCopy())
if err != nil {
return noGenResource, err
@ -347,7 +344,7 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
}
if genClone != nil && len(genClone) != 0 {
rdata, mode, err = manageClone(logger, genAPIVersion, genKind, genNamespace, genName, genClone, client)
rdata, mode, err = manageClone(logger, genAPIVersion, genKind, genNamespace, genName, policy, genClone, client)
} else {
rdata, mode, err = manageData(logger, genAPIVersion, genKind, genNamespace, genName, genData, client)
}
@ -364,10 +361,6 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
return newGenResource, nil
}
if processExisting {
return noGenResource, nil
}
// build the resource template
newResource := &unstructured.Unstructured{}
newResource.SetUnstructuredContent(rdata)
@ -386,7 +379,7 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
label := newResource.GetLabels()
label["policy.kyverno.io/policy-name"] = policy
label["policy.kyverno.io/gr-name"] = gr.Name
newResource.SetLabels(label)
delete(label, "generate.kyverno.io/clone-policy-name")
if mode == Create {
if rule.Generation.Synchronize {
label["policy.kyverno.io/synchronize"] = "enable"
@ -396,6 +389,7 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
// Reset resource version
newResource.SetResourceVersion("")
newResource.SetLabels(label)
// Create the resource
_, err = client.CreateResource(genAPIVersion, genKind, genNamespace, newResource, false)
if err != nil {
@ -405,7 +399,6 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
logger.V(2).Info("generated target resource")
} else if mode == Update {
label := newResource.GetLabels()
if rule.Generation.Synchronize {
label["policy.kyverno.io/synchronize"] = "enable"
} else {
@ -417,7 +410,7 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
newResource.SetLabels(label)
_, err := client.UpdateResource(genAPIVersion, genKind, genNamespace, newResource, false)
if err != nil {
logger.Error(err, "updating existing resource")
logger.Error(err, "failed to update resource")
return noGenResource, err
}
logger.V(2).Info("updated target resource")
@ -450,7 +443,7 @@ func manageData(log logr.Logger, apiVersion, kind, namespace, name string, data
return updateObj.UnstructuredContent(), Update, nil
}
func manageClone(log logr.Logger, apiVersion, kind, namespace, name string, clone map[string]interface{}, client *dclient.Client) (map[string]interface{}, ResourceMode, error) {
func manageClone(log logr.Logger, apiVersion, kind, namespace, name, policy string, clone map[string]interface{}, client *dclient.Client) (map[string]interface{}, ResourceMode, error) {
rNamespace, _, err := unstructured.NestedString(clone, "namespace")
if err != nil {
return nil, Skip, fmt.Errorf("failed to find source namespace: %v", err)

View file

@ -343,3 +343,8 @@ func (c *Controller) syncGenerateRequest(key string) error {
return c.processGR(gr)
}
// EnqueueGenerateRequestFromWebhook - enqueing generate requests from webhook
func (c *Controller) EnqueueGenerateRequestFromWebhook(gr *kyverno.GenerateRequest) {
c.enqueueGenerateRequest(gr)
}

View file

@ -120,6 +120,46 @@ func Validate(policyRaw []byte, client *dclient.Client, mock bool, openAPIContro
if !isLabelAndAnnotationsString(rule) {
return fmt.Errorf("labels and annotations supports only string values, \"use double quotes around the non string values\"")
}
// add label to source mentioned in policy
if !mock && rule.Generation.Clone.Name != "" {
obj, err := client.GetResource("", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
if err != nil {
log.Log.Error(err, fmt.Sprintf("source resource %s/%s/%s not found.", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name))
continue
}
updateSource := true
label := obj.GetLabels()
if len(label) == 0 {
label = make(map[string]string)
label["generate.kyverno.io/clone-policy-name"] = p.GetName()
} else {
if label["generate.kyverno.io/clone-policy-name"] != "" {
policyNames := label["generate.kyverno.io/clone-policy-name"]
if !strings.Contains(policyNames, p.GetName()) {
policyNames = policyNames + "," + p.GetName()
label["generate.kyverno.io/clone-policy-name"] = policyNames
} else {
updateSource = false
}
} else {
label["generate.kyverno.io/clone-policy-name"] = p.GetName()
}
}
if updateSource {
log.Log.V(4).Info("updating existing clone source")
obj.SetLabels(label)
_, err = client.UpdateResource(obj.GetAPIVersion(), rule.Generation.Kind, rule.Generation.Clone.Namespace, obj, false)
if err != nil {
log.Log.Error(err, "failed to update source name:%v namespace:%v kind:%v", obj.GetName(), obj.GetNamespace(), obj.GetKind())
continue
}
log.Log.V(4).Info("updated source name:%v namespace:%v kind:%v", obj.GetName(), obj.GetNamespace(), obj.GetKind())
}
}
}
if !mock {

View file

@ -437,14 +437,20 @@ func deleteGR(kyvernoClient *kyvernoclient.Clientset, policyKey string, grList [
}
func updateGR(kyvernoClient *kyvernoclient.Clientset, policyKey string, grList []*kyverno.GenerateRequest, logger logr.Logger) {
for _, v := range grList {
if policyKey == v.Spec.Policy {
v.SetLabels(map[string]string{
"policy-update": fmt.Sprintf("revision-count-%d", rand.Intn(100000)),
})
_, err := kyvernoClient.KyvernoV1().GenerateRequests(config.KyvernoNamespace).Update(context.TODO(), v, metav1.UpdateOptions{})
for _, gr := range grList {
if policyKey == gr.Spec.Policy {
grLabels := gr.Labels
if grLabels == nil || len(grLabels) == 0 {
grLabels = make(map[string]string)
}
grLabels["policy-update"] = fmt.Sprintf("revision-count-%d", rand.Intn(100000))
// gr.SetLabels(map[string]string{
// "policy-update": fmt.Sprintf("revision-count-%d", rand.Intn(100000)),
// })
gr.SetLabels(grLabels)
_, err := kyvernoClient.KyvernoV1().GenerateRequests(config.KyvernoNamespace).Update(context.TODO(), gr, metav1.UpdateOptions{})
if err != nil {
logger.Error(err, "failed to update gr", "name", v.GetName())
logger.Error(err, "failed to update gr", "name", gr.GetName())
}
}
}

View file

@ -141,10 +141,10 @@ func retryApplyResource(client *kyvernoclient.Clientset, grSpec kyverno.Generate
if action == v1beta1.Create || action == v1beta1.Update {
log.V(4).Info("querying all generate requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{
"policyName": grSpec.Policy,
"resourceName": grSpec.Resource.Name,
"resourceKind": grSpec.Resource.Kind,
"ResourceNamespace": grSpec.Resource.Namespace,
"generate.kyverno.io/policy-name": grSpec.Policy,
"generate.kyverno.io/resource-name": grSpec.Resource.Name,
"generate.kyverno.io/resource-kind": grSpec.Resource.Kind,
"generate.kyverno.io/resource-namespace": grSpec.Resource.Namespace,
}))
grList, err := grLister.List(selector)
if err != nil {
@ -153,28 +153,31 @@ func retryApplyResource(client *kyvernoclient.Clientset, grSpec kyverno.Generate
}
for _, v := range grList {
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.KyvernoNamespace).Update(context.TODO(), v, metav1.UpdateOptions{})
if err != nil {
return err
}
isExist = true
grLabels := gr.Labels
if grLabels == nil || len(grLabels) == 0 {
grLabels = make(map[string]string)
}
grLabels["resources-update"] = "true"
gr.SetLabels(grLabels)
v.Spec.Context = gr.Spec.Context
v.Spec.Policy = gr.Spec.Policy
v.Spec.Resource = gr.Spec.Resource
_, err = client.KyvernoV1().GenerateRequests(config.KyvernoNamespace).Update(context.TODO(), v, metav1.UpdateOptions{})
if err != nil {
return err
}
isExist = true
}
if !isExist {
gr.SetGenerateName("gr-")
gr.SetLabels(map[string]string{
"policyName": grSpec.Policy,
"resourceName": grSpec.Resource.Name,
"resourceKind": grSpec.Resource.Kind,
"ResourceNamespace": grSpec.Resource.Namespace,
"generate.kyverno.io/policy-name": grSpec.Policy,
"generate.kyverno.io/resource-name": grSpec.Resource.Name,
"generate.kyverno.io/resource-kind": grSpec.Resource.Kind,
"generate.kyverno.io/resource-namespace": grSpec.Resource.Namespace,
})
_, err = client.KyvernoV1().GenerateRequests(config.KyvernoNamespace).Create(context.TODO(), &gr, metav1.CreateOptions{})
if err != nil {

View file

@ -2,6 +2,7 @@ package webhooks
import (
contextdefault "context"
"encoding/json"
"fmt"
"reflect"
"sort"
@ -16,6 +17,7 @@ import (
"github.com/kyverno/kyverno/pkg/engine"
"github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/engine/response"
enginutils "github.com/kyverno/kyverno/pkg/engine/utils"
"github.com/kyverno/kyverno/pkg/event"
kyvernoutils "github.com/kyverno/kyverno/pkg/utils"
"github.com/kyverno/kyverno/pkg/webhooks/generate"
@ -30,68 +32,122 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
logger := ws.log.WithValues("action", "generation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
logger.V(4).Info("incoming request")
var engineResponses []*response.EngineResponse
if request.Operation == v1beta1.Create || request.Operation == v1beta1.Update {
if len(policies) == 0 {
return
}
// convert RAW to unstructured
new, old, err := kyvernoutils.ExtractResources(nil, request)
if err != nil {
logger.Error(err, "failed to extract resource")
}
if len(policies) == 0 {
return
}
// convert RAW to unstructured
new, old, err := kyvernoutils.ExtractResources(nil, request)
if err != nil {
logger.Error(err, "failed to extract resource")
}
policyContext := engine.PolicyContext{
NewResource: new,
OldResource: old,
AdmissionInfo: userRequestInfo,
ExcludeGroupRole: dynamicConfig.GetExcludeGroupRole(),
ExcludeResourceFunc: ws.configHandler.ToFilter,
ResourceCache: ws.resCache,
JSONContext: ctx,
}
policyContext := engine.PolicyContext{
NewResource: new,
OldResource: old,
AdmissionInfo: userRequestInfo,
ExcludeGroupRole: dynamicConfig.GetExcludeGroupRole(),
ExcludeResourceFunc: ws.configHandler.ToFilter,
ResourceCache: ws.resCache,
JSONContext: ctx,
}
for _, policy := range policies {
var rules []response.RuleResponse
policyContext.Policy = *policy
engineResponse := engine.Generate(policyContext)
for _, rule := range engineResponse.PolicyResponse.Rules {
if !rule.Success {
ws.deleteGR(logger, engineResponse)
continue
for _, policy := range policies {
var rules []response.RuleResponse
policyContext.Policy = *policy
engineResponse := engine.Generate(policyContext)
for _, rule := range engineResponse.PolicyResponse.Rules {
if !rule.Success {
ws.deleteGR(logger, engineResponse)
continue
}
rules = append(rules, rule)
}
rules = append(rules, rule)
if len(rules) > 0 {
engineResponse.PolicyResponse.Rules = rules
// some generate rules do apply to the resource
engineResponses = append(engineResponses, engineResponse)
ws.statusListener.Update(generateStats{
resp: engineResponse,
})
}
}
if len(rules) > 0 {
engineResponse.PolicyResponse.Rules = rules
// some generate rules do apply to the resource
engineResponses = append(engineResponses, engineResponse)
ws.statusListener.Update(generateStats{
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
for _, failedGR := range failedResponse {
events := failedEvents(fmt.Errorf("failed to create Generate Request: %v", failedGR.err), failedGR.gr, new)
ws.eventGen.Add(events...)
}
}
}
// 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
for _, failedGR := range failedResponse {
events := failedEvents(fmt.Errorf("failed to create Generate Request: %v", failedGR.err), failedGR.gr, new)
ws.eventGen.Add(events...)
}
if request.Operation == v1beta1.Update {
ws.handleUpdate(request)
}
return
}
//HandleUpdate handles admission-requests for update
func (ws *WebhookServer) handleUpdate(request *v1beta1.AdmissionRequest) {
logger := ws.log.WithValues("action", "generation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
resource, err := enginutils.ConvertToUnstructured(request.OldObject.Raw)
if err != nil {
logger.Error(err, "failed to convert object resource to unstructured format")
}
resLabels := resource.GetLabels()
if resLabels["generate.kyverno.io/clone-policy-name"] != "" {
policyNames := strings.Split(resLabels["generate.kyverno.io/clone-policy-name"], ",")
for _, policyName := range policyNames {
selector := labels.SelectorFromSet(labels.Set(map[string]string{
"generate.kyverno.io/policy-name": policyName,
}))
grList, err := ws.grLister.List(selector)
if err != nil {
logger.Error(err, "failed to get generate request for the resource", "label", "generate.kyverno.io/policy-name")
}
for _, gr := range grList {
ws.grController.EnqueueGenerateRequestFromWebhook(gr)
}
}
}
}
//HandleDelete handles admission-requests for delete
func (ws *WebhookServer) handleDelete(request *v1beta1.AdmissionRequest) {
logger := ws.log.WithValues("action", "generation", "uid", request.UID, "kind", request.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
resource, err := enginutils.ConvertToUnstructured(request.OldObject.Raw)
if err != nil {
logger.Error(err, "failed to convert object resource to unstructured format")
}
r, _ := json.Marshal(resource)
fmt.Println(string(r))
resLabels := resource.GetLabels()
if resLabels["app.kubernetes.io/managed-by"] == "kyverno" && resLabels["policy.kyverno.io/synchronize"] == "enable" && request.Operation == v1beta1.Delete {
grName := resLabels["policy.kyverno.io/gr-name"]
gr, err := ws.grLister.Get(grName)
if err != nil {
logger.Error(err, "failed to get generate request", "name", grName)
}
ws.grController.EnqueueGenerateRequestFromWebhook(gr)
}
}
func (ws *WebhookServer) deleteGR(logger logr.Logger, engineResponse *response.EngineResponse) {
logger.V(4).Info("querying all generate requests")
selector := labels.SelectorFromSet(labels.Set(map[string]string{
"policyName": engineResponse.PolicyResponse.Policy,
"resourceName": engineResponse.PolicyResponse.Resource.Name,
"resourceKind": engineResponse.PolicyResponse.Resource.Kind,
"ResourceNamespace": engineResponse.PolicyResponse.Resource.Namespace,
"generate.kyverno.io/policy-name": engineResponse.PolicyResponse.Policy,
"generate.kyverno.io/resource-name": engineResponse.PolicyResponse.Resource.Name,
"generate.kyverno.io/resource-kind": engineResponse.PolicyResponse.Resource.Kind,
"generate.kyverno.io/resource-namespace": engineResponse.PolicyResponse.Resource.Namespace,
}))
grList, err := ws.grLister.List(selector)

View file

@ -20,6 +20,7 @@ import (
client "github.com/kyverno/kyverno/pkg/dclient"
context2 "github.com/kyverno/kyverno/pkg/engine/context"
"github.com/kyverno/kyverno/pkg/event"
"github.com/kyverno/kyverno/pkg/generate"
"github.com/kyverno/kyverno/pkg/openapi"
"github.com/kyverno/kyverno/pkg/policycache"
"github.com/kyverno/kyverno/pkg/policyreport"
@ -29,7 +30,7 @@ import (
userinfo "github.com/kyverno/kyverno/pkg/userinfo"
"github.com/kyverno/kyverno/pkg/utils"
"github.com/kyverno/kyverno/pkg/webhookconfig"
"github.com/kyverno/kyverno/pkg/webhooks/generate"
webhookgenerate "github.com/kyverno/kyverno/pkg/webhooks/generate"
v1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
rbacinformer "k8s.io/client-go/informers/rbac/v1"
@ -104,7 +105,7 @@ type WebhookServer struct {
prGenerator policyreport.GeneratorInterface
// generate request generator
grGenerator *generate.Generator
grGenerator *webhookgenerate.Generator
auditHandler AuditHandler
@ -116,6 +117,8 @@ type WebhookServer struct {
// resCache - controls creation and fetching of resource informer cache
resCache resourcecache.ResourceCacheIface
grController *generate.Controller
}
// NewWebhookServer creates new instance of WebhookServer accordingly to given configuration
@ -137,13 +140,14 @@ func NewWebhookServer(
statusSync policystatus.Listener,
configHandler config.Interface,
prGenerator policyreport.GeneratorInterface,
grGenerator *generate.Generator,
grGenerator *webhookgenerate.Generator,
auditHandler AuditHandler,
supportMutateValidate bool,
cleanUp chan<- struct{},
log logr.Logger,
openAPIController *openapi.Controller,
resCache resourcecache.ResourceCacheIface,
grc *generate.Controller,
) (*WebhookServer, error) {
if tlsPair == nil {
@ -182,6 +186,7 @@ func NewWebhookServer(
webhookMonitor: webhookMonitor,
prGenerator: prGenerator,
grGenerator: grGenerator,
grController: grc,
auditHandler: auditHandler,
log: log,
openAPIController: openAPIController,
@ -372,6 +377,9 @@ func (ws *WebhookServer) ResourceMutation(request *v1beta1.AdmissionRequest) *v1
func (ws *WebhookServer) resourceValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
logger := ws.log.WithName("Validate").WithValues("uid", request.UID, "kind", request.Kind.Kind, "namespace", request.Namespace, "name", request.Name, "operation", request.Operation)
if request.Operation == v1beta1.Delete {
ws.handleDelete(request)
}
if !ws.supportMutateValidate {
logger.Info("mutate and validate rules are not supported prior to Kubernetes 1.14.0")