diff --git a/pkg/common/common.go b/pkg/common/common.go index 155e5f708b..3a73bdb731 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -2,6 +2,7 @@ package common import ( "encoding/json" + "strings" "github.com/go-logr/logr" enginutils "github.com/kyverno/kyverno/pkg/engine/utils" @@ -64,3 +65,15 @@ func GetNamespaceLabels(namespaceObj *v1.Namespace, logger logr.Logger) map[stri } return namespaceUnstructured.GetLabels() } + +// GetKindFromGVK - get kind and APIVersion from GVK +func GetKindFromGVK(str string) (apiVersion string, kind string) { + if strings.Count(str, "/") == 0 { + return "", str + } + splitString := strings.Split(str, "/") + if strings.Count(str, "/") == 1 { + return splitString[0], splitString[1] + } + return splitString[0] + "/" + splitString[1], splitString[2] +} diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go index 994a04e021..ee9fc72c06 100644 --- a/pkg/engine/generation.go +++ b/pkg/engine/generation.go @@ -21,14 +21,15 @@ func filterRules(policyContext *PolicyContext) *response.EngineResponse { kind := policyContext.NewResource.GetKind() name := policyContext.NewResource.GetName() namespace := policyContext.NewResource.GetNamespace() - + apiVersion := policyContext.NewResource.GetAPIVersion() resp := &response.EngineResponse{ PolicyResponse: response.PolicyResponse{ Policy: policyContext.Policy.Name, Resource: response.ResourceSpec{ - Kind: kind, - Name: name, - Namespace: namespace, + Kind: kind, + Name: name, + Namespace: namespace, + APIVersion: apiVersion, }, }, } diff --git a/pkg/engine/utils.go b/pkg/engine/utils.go index 5c1281b3bd..d2d697c9fa 100644 --- a/pkg/engine/utils.go +++ b/pkg/engine/utils.go @@ -28,10 +28,21 @@ type EngineStats struct { RulesAppliedCount int } -func checkKind(kinds []string, resourceKind string) bool { +func checkKind(kinds []string, resource unstructured.Unstructured) bool { for _, kind := range kinds { - if resourceKind == kind { - return true + SplitGVK := strings.Split(kind, "/") + if len(SplitGVK) == 1 { + if resource.GetKind() == kind { + return true + } + } else if len(SplitGVK) == 2 { + if resource.GroupVersionKind().Kind == SplitGVK[1] && resource.GroupVersionKind().Version == SplitGVK[0] { + return true + } + } else { + if resource.GroupVersionKind().Group == SplitGVK[0] && resource.GroupVersionKind().Kind == SplitGVK[2] && (resource.GroupVersionKind().Version == SplitGVK[1] || resource.GroupVersionKind().Version == "*") { + return true + } } } @@ -113,7 +124,7 @@ func doesResourceMatchConditionBlock(conditionBlock kyverno.ResourceDescription, var errs []error if len(conditionBlock.Kinds) > 0 { - if !checkKind(conditionBlock.Kinds, resource.GetKind()) { + if !checkKind(conditionBlock.Kinds, resource) { errs = append(errs, fmt.Errorf("kind does not match %v", conditionBlock.Kinds)) } } diff --git a/pkg/event/controller.go b/pkg/event/controller.go index 447cdb2e10..f10090b19b 100644 --- a/pkg/event/controller.go +++ b/pkg/event/controller.go @@ -218,7 +218,7 @@ func (gen *Generator) syncHandler(key Info) error { func (gen *Generator) getResource(key Info) (obj *unstructured.Unstructured, err error) { lister, ok := gen.resCache.GetGVRCache(key.Kind) if !ok { - if lister, err = gen.resCache.CreateResourceInformer(key.Kind); err != nil { + if lister, err = gen.resCache.CreateGVKInformer(key.Kind); err != nil { return nil, err } } diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index a93076c7e3..0efb2fbde2 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -51,15 +51,14 @@ func (pc *PolicyController) processExistingResources(policy *kyverno.ClusterPoli } } -func (pc *PolicyController) registerResource(kind string) (err error) { - genericCache, ok := pc.resCache.GetGVRCache(kind) +func (pc *PolicyController) registerResource(gvk string) (err error) { + genericCache, ok := pc.resCache.GetGVRCache(gvk) if !ok { - if genericCache, err = pc.resCache.CreateResourceInformer(kind); err != nil { - return fmt.Errorf("failed to create informer for %s: %v", kind, err) + if genericCache, err = pc.resCache.CreateGVKInformer(gvk); err != nil { + return fmt.Errorf("failed to create informer for %s: %v", gvk, err) } } - - pc.rm.RegisterScope(kind, genericCache.IsNamespaced()) + pc.rm.RegisterScope(gvk, genericCache.IsNamespaced()) return nil } diff --git a/pkg/resourcecache/main.go b/pkg/resourcecache/main.go index 096549ae20..b902c2f945 100644 --- a/pkg/resourcecache/main.go +++ b/pkg/resourcecache/main.go @@ -12,7 +12,7 @@ import ( // ResourceCache - allows the creation, deletion and saving the resource informers as a cache type ResourceCache interface { CreateInformers(resources ...string) []error - CreateResourceInformer(resource string) (GenericCache, error) + CreateGVKInformer(kind string) (GenericCache, error) StopResourceInformer(resource string) GetGVRCache(resource string) (GenericCache, bool) } diff --git a/pkg/resourcecache/resourcecache.go b/pkg/resourcecache/resourcecache.go index 132cde7d69..8cd3af4a17 100644 --- a/pkg/resourcecache/resourcecache.go +++ b/pkg/resourcecache/resourcecache.go @@ -2,45 +2,21 @@ package resourcecache import ( "fmt" + + "github.com/kyverno/kyverno/pkg/common" ) // CreateInformers ... func (resc *resourceCache) CreateInformers(resources ...string) []error { var errs []error for _, resource := range resources { - if _, err := resc.CreateResourceInformer(resource); err != nil { + if _, err := resc.CreateGVKInformer(resource); err != nil { errs = append(errs, fmt.Errorf("failed to create informer for %s: %v", resource, err)) } } return errs } -// CreateResourceInformer creates informer for the given resource -func (resc *resourceCache) CreateResourceInformer(resource string) (GenericCache, error) { - gc, ok := resc.GetGVRCache(resource) - if ok { - return gc, nil - } - - apiResource, gvr, err := resc.dclient.DiscoveryClient.FindResource("", resource) - if err != nil { - return nil, fmt.Errorf("cannot find API resource %s", resource) - } - - stopCh := make(chan struct{}) - genInformer := resc.dinformer.ForResource(gvr) - gvrIface := NewGVRCache(gvr, apiResource.Namespaced, stopCh, genInformer) - - resc.gvrCache.Set(resource, gvrIface) - resc.dinformer.Start(stopCh) - - if synced := resc.dinformer.WaitForCacheSync(stopCh); !synced[gvr] { - return nil, fmt.Errorf("informer for %s hasn't synced", gvr) - } - - return gvrIface, nil -} - // StopResourceInformer - delete the given resource information from ResourceCache and stop watching for the given resource func (resc *resourceCache) StopResourceInformer(resource string) { res, ok := resc.GetGVRCache(resource) @@ -61,3 +37,29 @@ func (resc *resourceCache) GetGVRCache(resource string) (GenericCache, bool) { return nil, false } + +// CreateGVKInformer creates informer for the given gvk +func (resc *resourceCache) CreateGVKInformer(gvk string) (GenericCache, error) { + gc, ok := resc.GetGVRCache(gvk) + if ok { + return gc, nil + } + gv, k := common.GetKindFromGVK(gvk) + apiResource, gvr, err := resc.dclient.DiscoveryClient.FindResource(gv, k) + if err != nil { + return nil, fmt.Errorf("cannot find API resource %s", gvk) + } + + stopCh := make(chan struct{}) + genInformer := resc.dinformer.ForResource(gvr) + gvrIface := NewGVRCache(gvr, apiResource.Namespaced, stopCh, genInformer) + + resc.gvrCache.Set(gvk, gvrIface) + resc.dinformer.Start(stopCh) + + if synced := resc.dinformer.WaitForCacheSync(stopCh); !synced[gvr] { + return nil, fmt.Errorf("informer for %s hasn't synced", gvr) + } + + return gvrIface, nil +} diff --git a/pkg/webhooks/generation.go b/pkg/webhooks/generation.go index d76a85907f..c929d0e45b 100644 --- a/pkg/webhooks/generation.go +++ b/pkg/webhooks/generation.go @@ -310,9 +310,10 @@ func transform(userRequestInfo kyverno.RequestInfo, er *response.EngineResponse) gr := kyverno.GenerateRequestSpec{ Policy: er.PolicyResponse.Policy, Resource: kyverno.ResourceSpec{ - Kind: er.PolicyResponse.Resource.Kind, - Namespace: er.PolicyResponse.Resource.Namespace, - Name: er.PolicyResponse.Resource.Name, + Kind: er.PolicyResponse.Resource.Kind, + Namespace: er.PolicyResponse.Resource.Namespace, + Name: er.PolicyResponse.Resource.Name, + APIVersion: er.PolicyResponse.Resource.APIVersion, }, Context: kyverno.GenerateRequestContext{ UserRequestInfo: userRequestInfo,