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

fix: map APIResource in FindResources results (#6611)

Signed-off-by: Charles-Edouard Brétéché <charles.edouard@nirmata.com>
This commit is contained in:
Charles-Edouard Brétéché 2023-03-17 12:21:26 +01:00 committed by GitHub
parent fc453b1faa
commit 0b56613d59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 57 additions and 46 deletions

View file

@ -11,7 +11,6 @@ import (
"github.com/kyverno/kyverno/pkg/utils/wildcard" "github.com/kyverno/kyverno/pkg/utils/wildcard"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/discovery" "k8s.io/client-go/discovery"
) )
@ -35,7 +34,7 @@ func (gvrs GroupVersionResourceSubresource) WithSubResource(subresource string)
// IDiscovery provides interface to mange Kind and GVR mapping // IDiscovery provides interface to mange Kind and GVR mapping
type IDiscovery interface { type IDiscovery interface {
FindResources(group, version, kind, subresource string) ([]GroupVersionResourceSubresource, error) FindResources(group, version, kind, subresource string) (map[GroupVersionResourceSubresource]metav1.APIResource, error)
FindResource(groupVersion string, kind string) (apiResource, parentAPIResource *metav1.APIResource, gvr schema.GroupVersionResource, err error) FindResource(groupVersion string, kind string) (apiResource, parentAPIResource *metav1.APIResource, gvr schema.GroupVersionResource, err error)
// TODO: there's no mapping from GVK to GVR, this is very error prone // TODO: there's no mapping from GVK to GVR, this is very error prone
GetGVRFromGVK(schema.GroupVersionKind) (schema.GroupVersionResource, error) GetGVRFromGVK(schema.GroupVersionKind) (schema.GroupVersionResource, error)
@ -159,7 +158,7 @@ func (c serverResources) FindResource(groupVersion string, kind string) (apiReso
return nil, nil, schema.GroupVersionResource{}, err return nil, nil, schema.GroupVersionResource{}, err
} }
func (c serverResources) FindResources(group, version, kind, subresource string) ([]GroupVersionResourceSubresource, error) { func (c serverResources) FindResources(group, version, kind, subresource string) (map[GroupVersionResourceSubresource]metav1.APIResource, error) {
resources, err := c.findResources(group, version, kind, subresource) resources, err := c.findResources(group, version, kind, subresource)
if err != nil { if err != nil {
if !c.cachedClient.Fresh() { if !c.cachedClient.Fresh() {
@ -170,7 +169,7 @@ func (c serverResources) FindResources(group, version, kind, subresource string)
return resources, err return resources, err
} }
func (c serverResources) findResources(group, version, kind, subresource string) ([]GroupVersionResourceSubresource, error) { func (c serverResources) findResources(group, version, kind, subresource string) (map[GroupVersionResourceSubresource]metav1.APIResource, error) {
_, serverGroupsAndResources, err := c.cachedClient.ServerGroupsAndResources() _, serverGroupsAndResources, err := c.cachedClient.ServerGroupsAndResources()
if err != nil && !strings.Contains(err.Error(), "Got empty response for") { if err != nil && !strings.Contains(err.Error(), "Got empty response for") {
if discovery.IsGroupDiscoveryFailedError(err) { if discovery.IsGroupDiscoveryFailedError(err) {
@ -195,7 +194,7 @@ func (c serverResources) findResources(group, version, kind, subresource string)
Kind: kind, Kind: kind,
} }
} }
resources := sets.New[GroupVersionResourceSubresource]() resources := map[GroupVersionResourceSubresource]metav1.APIResource{}
// first match resouces // first match resouces
for _, list := range serverGroupsAndResources { for _, list := range serverGroupsAndResources {
gv, err := schema.ParseGroupVersion(list.GroupVersion) gv, err := schema.ParseGroupVersion(list.GroupVersion)
@ -206,23 +205,24 @@ func (c serverResources) findResources(group, version, kind, subresource string)
if !strings.Contains(resource.Name, "/") { if !strings.Contains(resource.Name, "/") {
gvk := getGVK(gv, resource.Group, resource.Version, resource.Kind) gvk := getGVK(gv, resource.Group, resource.Version, resource.Kind)
if wildcard.Match(group, gvk.Group) && wildcard.Match(version, gvk.Version) && wildcard.Match(kind, gvk.Kind) { if wildcard.Match(group, gvk.Group) && wildcard.Match(version, gvk.Version) && wildcard.Match(kind, gvk.Kind) {
resources.Insert(GroupVersionResourceSubresource{ gvrs := GroupVersionResourceSubresource{
GroupVersionResource: gvk.GroupVersion().WithResource(resource.Name), GroupVersionResource: gv.WithResource(resource.Name),
}) }
resources[gvrs] = resource
} }
} }
} }
} }
} }
// second match subresouces if necessary // second match subresouces if necessary
subresources := sets.New[GroupVersionResourceSubresource]() subresources := map[GroupVersionResourceSubresource]metav1.APIResource{}
if subresource != "" { if subresource != "" {
for _, list := range serverGroupsAndResources { for _, list := range serverGroupsAndResources {
for _, resource := range list.APIResources { for _, resource := range list.APIResources {
for parent := range resources { for parent := range resources {
if wildcard.Match(parent.Resource+"/"+subresource, resource.Name) { if wildcard.Match(parent.Resource+"/"+subresource, resource.Name) {
parts := strings.Split(resource.Name, "/") parts := strings.Split(resource.Name, "/")
subresources.Insert(parent.WithSubResource(parts[1])) subresources[parent.WithSubResource(parts[1])] = resource
break break
} }
} }
@ -230,7 +230,7 @@ func (c serverResources) findResources(group, version, kind, subresource string)
} }
} }
// third if no resource matched, try again but consider subresources this time // third if no resource matched, try again but consider subresources this time
if resources.Len() == 0 { if len(resources) == 0 {
for _, list := range serverGroupsAndResources { for _, list := range serverGroupsAndResources {
gv, err := schema.ParseGroupVersion(list.GroupVersion) gv, err := schema.ParseGroupVersion(list.GroupVersion)
if err != nil { if err != nil {
@ -240,21 +240,25 @@ func (c serverResources) findResources(group, version, kind, subresource string)
gvk := getGVK(gv, resource.Group, resource.Version, resource.Kind) gvk := getGVK(gv, resource.Group, resource.Version, resource.Kind)
if wildcard.Match(group, gvk.Group) && wildcard.Match(version, gvk.Version) && wildcard.Match(kind, gvk.Kind) { if wildcard.Match(group, gvk.Group) && wildcard.Match(version, gvk.Version) && wildcard.Match(kind, gvk.Kind) {
parts := strings.Split(resource.Name, "/") parts := strings.Split(resource.Name, "/")
resources.Insert(GroupVersionResourceSubresource{ gvrs := GroupVersionResourceSubresource{
GroupVersionResource: gv.WithResource(parts[0]), GroupVersionResource: gv.WithResource(parts[0]),
SubResource: parts[1], SubResource: parts[1],
}) }
resources[gvrs] = resource
} }
} }
} }
} }
} }
if kind == "*" && subresource == "*" { if kind == "*" && subresource == "*" {
return resources.Union(subresources).UnsortedList(), nil for key, value := range subresources {
resources[key] = value
}
return resources, nil
} else if subresource != "" { } else if subresource != "" {
return subresources.UnsortedList(), nil return subresources, nil
} }
return resources.UnsortedList(), nil return resources, nil
} }
func (c serverResources) findResource(groupVersion string, kind string) (apiResource, parentAPIResource *metav1.APIResource, func (c serverResources) findResource(groupVersion string, kind string) (apiResource, parentAPIResource *metav1.APIResource,

View file

@ -81,7 +81,7 @@ func (c *fakeDiscoveryClient) FindResource(groupVersion string, kind string) (ap
return nil, nil, schema.GroupVersionResource{}, fmt.Errorf("not implemented") return nil, nil, schema.GroupVersionResource{}, fmt.Errorf("not implemented")
} }
func (c *fakeDiscoveryClient) FindResources(group, version, kind, subresource string) ([]GroupVersionResourceSubresource, error) { func (c *fakeDiscoveryClient) FindResources(group, version, kind, subresource string) (map[GroupVersionResourceSubresource]metav1.APIResource, error) {
return nil, fmt.Errorf("not implemented") return nil, fmt.Errorf("not implemented")
} }

View file

@ -854,7 +854,9 @@ func (c *controller) mergeWebhook(dst *webhook, policy kyvernov1.PolicyInterface
logger.Error(err, "unable to find resource", "group", group, "version", version, "kind", kind, "subresource", subresource) logger.Error(err, "unable to find resource", "group", group, "version", version, "kind", kind, "subresource", subresource)
continue continue
} }
gvrsList = append(gvrsList, gvrss...) for gvrs := range gvrss {
gvrsList = append(gvrsList, gvrs)
}
} }
} }
for _, gvr := range gvrsList { for _, gvr := range gvrsList {

View file

@ -82,7 +82,7 @@ func getTargets(client dclient.Interface, target kyvernov1.ResourceSpec, ctx eng
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, gvrs := range gvrss { for gvrs := range gvrss {
dyn := client.GetDynamicInterface().Resource(gvrs.GroupVersionResource) dyn := client.GetDynamicInterface().Resource(gvrs.GroupVersionResource)
var sub []string var sub []string
if gvrs.SubResource != "" { if gvrs.SubResource != "" {

View file

@ -1213,16 +1213,16 @@ func validateKinds(kinds []string, mock, backgroundScanningEnabled, isValidation
for _, k := range kinds { for _, k := range kinds {
if !mock { if !mock {
group, version, kind, subresource := kubeutils.ParseKindSelector(k) group, version, kind, subresource := kubeutils.ParseKindSelector(k)
gvrs, err := client.Discovery().FindResources(group, version, kind, subresource) gvrss, err := client.Discovery().FindResources(group, version, kind, subresource)
if err != nil { if err != nil {
return fmt.Errorf("unable to convert GVK to GVR for kinds %s, err: %s", k, err) return fmt.Errorf("unable to convert GVK to GVR for kinds %s, err: %s", k, err)
} }
if len(gvrs) == 0 { if len(gvrss) == 0 {
return fmt.Errorf("unable to convert GVK to GVR for kinds %s", k) return fmt.Errorf("unable to convert GVK to GVR for kinds %s", k)
} }
if backgroundScanningEnabled { if backgroundScanningEnabled {
for _, gvr := range gvrs { for gvrs := range gvrss {
if strings.Contains(gvr.Resource, "/") { if strings.Contains(gvrs.Resource, "/") {
return fmt.Errorf("background scan enabled with subresource %s", subresource) return fmt.Errorf("background scan enabled with subresource %s", subresource)
} }
} }

View file

@ -4,10 +4,11 @@ import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
"github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/clients/dclient"
"github.com/kyverno/kyverno/pkg/utils/wildcard" "github.com/kyverno/kyverno/pkg/utils/wildcard"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
type ResourceFinder interface { type ResourceFinder interface {
FindResources(group, version, kind, subresource string) ([]dclient.GroupVersionResourceSubresource, error) FindResources(group, version, kind, subresource string) (map[dclient.GroupVersionResourceSubresource]metav1.APIResource, error)
} }
// Cache get method use for to get policy names and mostly use to test cache testcases // Cache get method use for to get policy names and mostly use to test cache testcases

View file

@ -33,7 +33,7 @@ func Test_All(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
// get // get
mutate := pCache.get(Mutate, gvr, "") mutate := pCache.get(Mutate, gvr, "")
if len(mutate) != 1 { if len(mutate) != 1 {
@ -69,7 +69,7 @@ func Test_Add_Duplicate_Policy(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
mutate := pCache.get(Mutate, gvr, "") mutate := pCache.get(Mutate, gvr, "")
if len(mutate) != 1 { if len(mutate) != 1 {
t.Errorf("expected 1 mutate policy, found %v", len(mutate)) t.Errorf("expected 1 mutate policy, found %v", len(mutate))
@ -102,7 +102,7 @@ func Test_Add_Validate_Audit(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
validateEnforce := pCache.get(ValidateEnforce, gvr, "") validateEnforce := pCache.get(ValidateEnforce, gvr, "")
if len(validateEnforce) != 0 { if len(validateEnforce) != 0 {
t.Errorf("expected 0 validate (enforce) policy, found %v", len(validateEnforce)) t.Errorf("expected 0 validate (enforce) policy, found %v", len(validateEnforce))
@ -899,7 +899,7 @@ func Test_Ns_All(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
// get // get
mutate := pCache.get(Mutate, gvr, nspace) mutate := pCache.get(Mutate, gvr, nspace)
if len(mutate) != 1 { if len(mutate) != 1 {
@ -935,7 +935,7 @@ func Test_Ns_Add_Duplicate_Policy(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
mutate := pCache.get(Mutate, gvr, nspace) mutate := pCache.get(Mutate, gvr, nspace)
if len(mutate) != 1 { if len(mutate) != 1 {
t.Errorf("expected 1 mutate policy, found %v", len(mutate)) t.Errorf("expected 1 mutate policy, found %v", len(mutate))
@ -968,7 +968,7 @@ func Test_Ns_Add_Validate_Audit(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
validateEnforce := pCache.get(ValidateEnforce, gvr, nspace) validateEnforce := pCache.get(ValidateEnforce, gvr, nspace)
if len(validateEnforce) != 0 { if len(validateEnforce) != 0 {
t.Errorf("expected 0 validate (enforce) policy, found %v", len(validateEnforce)) t.Errorf("expected 0 validate (enforce) policy, found %v", len(validateEnforce))
@ -1011,7 +1011,7 @@ func Test_GVk_Cache(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
generate := pCache.get(Generate, gvr, "") generate := pCache.get(Generate, gvr, "")
if len(generate) != 1 { if len(generate) != 1 {
t.Errorf("expected 1 generate policy, found %v", len(generate)) t.Errorf("expected 1 generate policy, found %v", len(generate))
@ -1049,7 +1049,7 @@ func Test_Add_Validate_Enforce(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
validateEnforce := pCache.get(ValidateEnforce, gvr, nspace) validateEnforce := pCache.get(ValidateEnforce, gvr, nspace)
if len(validateEnforce) != 1 { if len(validateEnforce) != 1 {
t.Errorf("expected 1 validate policy, found %v", len(validateEnforce)) t.Errorf("expected 1 validate policy, found %v", len(validateEnforce))
@ -1090,7 +1090,7 @@ func Test_Mutate_Policy(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
// get // get
mutate := pCache.get(Mutate, gvr, "") mutate := pCache.get(Mutate, gvr, "")
if len(mutate) != 1 { if len(mutate) != 1 {
@ -1112,7 +1112,7 @@ func Test_Generate_Policy(t *testing.T) {
group, version, kind, subresource := kubeutils.ParseKindSelector(kind) group, version, kind, subresource := kubeutils.ParseKindSelector(kind)
gvrs, err := finder.FindResources(group, version, kind, subresource) gvrs, err := finder.FindResources(group, version, kind, subresource)
assert.NilError(t, err) assert.NilError(t, err)
for _, gvr := range gvrs { for gvr := range gvrs {
// get // get
generate := pCache.get(Generate, gvr, "") generate := pCache.get(Generate, gvr, "")
if len(generate) != 1 { if len(generate) != 1 {

View file

@ -107,7 +107,9 @@ func (m *policyMap) set(key string, policy kyvernov1.PolicyInterface, client Res
logger.Error(err, "failed to fetch resource group versions", "group", group, "version", version, "kind", kind) logger.Error(err, "failed to fetch resource group versions", "group", group, "version", version, "kind", kind)
errs = append(errs, err) errs = append(errs, err)
} else { } else {
entries.Insert(gvrss...) for gvrs := range gvrss {
entries.Insert(gvrs)
}
} }
} }
if entries.Len() > 0 { if entries.Len() > 0 {

View file

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/kyverno/kyverno/pkg/clients/dclient" "github.com/kyverno/kyverno/pkg/clients/dclient"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
@ -33,28 +34,29 @@ var (
type TestResourceFinder struct{} type TestResourceFinder struct{}
func (TestResourceFinder) FindResources(group, version, kind, subresource string) ([]dclient.GroupVersionResourceSubresource, error) { func (TestResourceFinder) FindResources(group, version, kind, subresource string) (map[dclient.GroupVersionResourceSubresource]metav1.APIResource, error) {
var dummy metav1.APIResource
switch kind { switch kind {
case "Pod": case "Pod":
return []dclient.GroupVersionResourceSubresource{podsGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{podsGVRS: dummy}, nil
case "Namespace": case "Namespace":
return []dclient.GroupVersionResourceSubresource{namespacesGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{namespacesGVRS: dummy}, nil
case "ClusterRole": case "ClusterRole":
return []dclient.GroupVersionResourceSubresource{clusterrolesGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{clusterrolesGVRS: dummy}, nil
case "Deployment": case "Deployment":
return []dclient.GroupVersionResourceSubresource{deploymentsGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{deploymentsGVRS: dummy}, nil
case "StatefulSet": case "StatefulSet":
return []dclient.GroupVersionResourceSubresource{statefulsetsGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{statefulsetsGVRS: dummy}, nil
case "DaemonSet": case "DaemonSet":
return []dclient.GroupVersionResourceSubresource{daemonsetsGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{daemonsetsGVRS: dummy}, nil
case "ReplicaSet": case "ReplicaSet":
return []dclient.GroupVersionResourceSubresource{replicasetsGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{replicasetsGVRS: dummy}, nil
case "Job": case "Job":
return []dclient.GroupVersionResourceSubresource{jobsGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{jobsGVRS: dummy}, nil
case "ReplicationController": case "ReplicationController":
return []dclient.GroupVersionResourceSubresource{replicationcontrollersGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{replicationcontrollersGVRS: dummy}, nil
case "CronJob": case "CronJob":
return []dclient.GroupVersionResourceSubresource{cronjobsGVRS}, nil return map[dclient.GroupVersionResourceSubresource]metav1.APIResource{cronjobsGVRS: dummy}, nil
} }
return nil, fmt.Errorf("not found: %s", kind) return nil, fmt.Errorf("not found: %s", kind)
} }