From a14828246d40a00d3d21d6015155d241e46ae178 Mon Sep 17 00:00:00 2001 From: Mohan B E <54951236+b-entangled@users.noreply.github.com> Date: Fri, 7 Aug 2020 09:47:33 +0530 Subject: [PATCH] Feature/api version 852 (#1028) * apiVersion support for generate * added apiVersion to crds --- charts/kyverno/crds/crds.yaml | 2 + cmd/initContainer/main.go | 4 +- definitions/crds/crds.yaml | 2 + definitions/install.yaml | 2 + definitions/install_debug.yaml | 2 + go.sum | 1 - pkg/api/kyverno/v1/types.go | 3 ++ pkg/auth/auth.go | 2 +- pkg/checker/status.go | 8 +-- pkg/dclient/certificates.go | 20 +++---- pkg/dclient/client.go | 87 ++++++++++++++++++++----------- pkg/dclient/client_test.go | 12 ++--- pkg/dclient/utils.go | 7 ++- pkg/event/controller.go | 2 +- pkg/generate/cleanup/cleanup.go | 4 +- pkg/generate/generate.go | 50 ++++++++++-------- pkg/generate/resource.go | 2 +- pkg/kyverno/apply/command.go | 2 +- pkg/policy/existing.go | 4 +- pkg/policyviolation/common.go | 2 +- pkg/testrunner/scenario.go | 4 +- pkg/utils/util.go | 2 +- pkg/webhookconfig/checker.go | 2 +- pkg/webhookconfig/registration.go | 14 ++--- pkg/webhookconfig/resource.go | 4 +- 25 files changed, 147 insertions(+), 97 deletions(-) diff --git a/charts/kyverno/crds/crds.yaml b/charts/kyverno/crds/crds.yaml index 67f3d96d9d..9d88128f55 100644 --- a/charts/kyverno/crds/crds.yaml +++ b/charts/kyverno/crds/crds.yaml @@ -87,6 +87,8 @@ spec: type: object generate: properties: + apiVersion: + type: string clone: properties: name: diff --git a/cmd/initContainer/main.go b/cmd/initContainer/main.go index f21d6737b1..24fc507436 100644 --- a/cmd/initContainer/main.go +++ b/cmd/initContainer/main.go @@ -107,7 +107,7 @@ func removeWebhookIfExists(client *client.Client, kind string, name string) erro logger := log.Log.WithName("removeExistingWebhook").WithValues("kind", kind, "name", name) var err error // Get resource - _, err = client.GetResource(kind, "", name) + _, err = client.GetResource("", kind, "", name) if errors.IsNotFound(err) { logger.V(4).Info("resource not found") return nil @@ -117,7 +117,7 @@ func removeWebhookIfExists(client *client.Client, kind string, name string) erro return err } // Delete resource - err = client.DeleteResource(kind, "", name, false) + err = client.DeleteResource("", kind, "", name, false) if err != nil { logger.Error(err, "failed to delete resource") return err diff --git a/definitions/crds/crds.yaml b/definitions/crds/crds.yaml index 68bc2dc79f..bc09ff4d5b 100644 --- a/definitions/crds/crds.yaml +++ b/definitions/crds/crds.yaml @@ -247,6 +247,8 @@ spec: - kind - name properties: + apiVersion: + type: string kind: type: string name: diff --git a/definitions/install.yaml b/definitions/install.yaml index 41886e42b5..af16d1956a 100644 --- a/definitions/install.yaml +++ b/definitions/install.yaml @@ -92,6 +92,8 @@ spec: type: object generate: properties: + apiVersion: + type: string clone: properties: name: diff --git a/definitions/install_debug.yaml b/definitions/install_debug.yaml index 497c17f5c2..4fe4ccfca7 100644 --- a/definitions/install_debug.yaml +++ b/definitions/install_debug.yaml @@ -92,6 +92,8 @@ spec: type: object generate: properties: + apiVersion: + type: string clone: properties: name: diff --git a/go.sum b/go.sum index 979747b506..248e3e1206 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,4 @@ 9fans.net/go v0.0.0-20181112161441-237454027057/go.mod h1:diCsxrliIURU9xsYtjCp5AbpQKqdhKmf0ujWDUSkfoY= -<<<<<<< Updated upstream cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= diff --git a/pkg/api/kyverno/v1/types.go b/pkg/api/kyverno/v1/types.go index f2d08f62b7..2bece206e5 100644 --- a/pkg/api/kyverno/v1/types.go +++ b/pkg/api/kyverno/v1/types.go @@ -361,6 +361,9 @@ type PolicyViolationSpec struct { // ResourceSpec information to identify the resource type ResourceSpec struct { + // Specifies resource apiVersionm + // +optional + APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` // Specifies resource kind // +optional Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 8f1ef24e1e..ec11b24153 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -67,7 +67,7 @@ func (o *CanIOptions) RunAccessCheck() (bool, error) { logger := o.log.WithValues("kind", sar.Kind, "namespace", sar.Namespace, "name", sar.Name) // Create the Resource - resp, err := o.client.CreateResource("SelfSubjectAccessReview", "", sar, false) + resp, err := o.client.CreateResource("", "SelfSubjectAccessReview", "", sar, false) if err != nil { logger.Error(err, "failed to create resource") return false, err diff --git a/pkg/checker/status.go b/pkg/checker/status.go index 89768a303c..4979191644 100644 --- a/pkg/checker/status.go +++ b/pkg/checker/status.go @@ -56,7 +56,7 @@ func (vc StatusControl) setStatus(status string) error { logger := vc.log.WithValues("name", deployName, "namespace", deployNamespace) var ann map[string]string var err error - deploy, err := vc.client.GetResource("Deployment", deployNamespace, deployName) + deploy, err := vc.client.GetResource("", "Deployment", deployNamespace, deployName) if err != nil { logger.Error(err, "failed to get deployment") return err @@ -83,7 +83,7 @@ func (vc StatusControl) setStatus(status string) error { deploy.SetAnnotations(ann) // update counter - _, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false) + _, err = vc.client.UpdateResource("", "Deployment", deployNamespace, deploy, false) if err != nil { logger.Error(err, "failed to update deployment annotation", "key", annWebhookStatus, "val", status) return err @@ -109,7 +109,7 @@ func (vc StatusControl) IncrementAnnotation() error { logger := vc.log var ann map[string]string var err error - deploy, err := vc.client.GetResource("Deployment", deployNamespace, deployName) + deploy, err := vc.client.GetResource("", "Deployment", deployNamespace, deployName) if err != nil { logger.Error(err, "failed to find Kyverno", "deployment", deployName, "namespace", deployNamespace) return err @@ -138,7 +138,7 @@ func (vc StatusControl) IncrementAnnotation() error { deploy.SetAnnotations(ann) // update counter - _, err = vc.client.UpdateResource("Deployment", deployNamespace, deploy, false) + _, err = vc.client.UpdateResource("", "Deployment", deployNamespace, deploy, false) if err != nil { logger.Error(err, fmt.Sprintf("failed to update annotation %s for deployment %s in namespace %s", annCounter, deployName, deployNamespace)) return err diff --git a/pkg/dclient/certificates.go b/pkg/dclient/certificates.go index 125dc7cec3..eeba6eb26e 100644 --- a/pkg/dclient/certificates.go +++ b/pkg/dclient/certificates.go @@ -75,14 +75,14 @@ func (c *Client) submitAndApproveCertificateRequest(req *certificates.Certificat if err != nil { return nil, err } - csrList, err := c.ListResource(CSRs, "", nil) + csrList, err := c.ListResource("", CSRs, "", nil) if err != nil { return nil, fmt.Errorf("Unable to list existing certificate requests: %v", err) } for _, csr := range csrList.Items { if csr.GetName() == req.ObjectMeta.Name { - err := c.DeleteResource(CSRs, "", csr.GetName(), false) + err := c.DeleteResource("", CSRs, "", csr.GetName(), false) if err != nil { return nil, fmt.Errorf("Unable to delete existing certificate request: %v", err) } @@ -91,7 +91,7 @@ func (c *Client) submitAndApproveCertificateRequest(req *certificates.Certificat } } - unstrRes, err := c.CreateResource(CSRs, "", req, false) + unstrRes, err := c.CreateResource("", CSRs, "", req, false) if err != nil { return nil, err } @@ -120,7 +120,7 @@ func (c *Client) fetchCertificateFromRequest(req *certificates.CertificateSignin // TODO: react of SIGINT and SIGTERM timeStart := time.Now() for time.Since(timeStart) < time.Duration(maxWaitSeconds)*time.Second { - unstrR, err := c.GetResource(CSRs, "", req.ObjectMeta.Name) + unstrR, err := c.GetResource("", CSRs, "", req.ObjectMeta.Name) if err != nil { return nil, err } @@ -151,7 +151,7 @@ func (c *Client) ReadRootCASecret() (result []byte) { return result } sname := generateRootCASecretName(certProps) - stlsca, err := c.GetResource(Secrets, certProps.Namespace, sname) + stlsca, err := c.GetResource("", Secrets, certProps.Namespace, sname) if err != nil { return result } @@ -177,7 +177,7 @@ const rootCAKey string = "rootCA.crt" func (c *Client) ReadTlsPair(props tls.TlsCertificateProps) *tls.TlsPemPair { logger := c.log.WithName("ReadTlsPair") sname := generateTLSPairSecretName(props) - unstrSecret, err := c.GetResource(Secrets, props.Namespace, sname) + unstrSecret, err := c.GetResource("", Secrets, props.Namespace, sname) if err != nil { logger.Error(err, "Failed to get secret", "name", sname, "namespace", props.Namespace) return nil @@ -188,7 +188,7 @@ func (c *Client) ReadTlsPair(props tls.TlsCertificateProps) *tls.TlsPemPair { annotations := unstrSecret.GetAnnotations() if _, ok := annotations[selfSignedAnnotation]; ok { sname := generateRootCASecretName(props) - _, err := c.GetResource(Secrets, props.Namespace, sname) + _, err := c.GetResource("", Secrets, props.Namespace, sname) if err != nil { logger.Error(err, "Root CA secret is required while using self-signed certificates TLS pair, defaulting to generating new TLS pair", "name", sname, "namespace", props.Namespace) return nil @@ -218,7 +218,7 @@ func (c *Client) ReadTlsPair(props tls.TlsCertificateProps) *tls.TlsPemPair { func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPemPair) error { logger := c.log.WithName("WriteTlsPair") name := generateTLSPairSecretName(props) - _, err := c.GetResource(Secrets, props.Namespace, name) + _, err := c.GetResource("", Secrets, props.Namespace, name) if err != nil { secret := &v1.Secret{ TypeMeta: metav1.TypeMeta{ @@ -236,7 +236,7 @@ func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPem Type: v1.SecretTypeTLS, } - _, err := c.CreateResource(Secrets, props.Namespace, secret, false) + _, err := c.CreateResource("", Secrets, props.Namespace, secret, false) if err == nil { logger.Info("secret created", "name", name, "namespace", props.Namespace) } @@ -250,7 +250,7 @@ func (c *Client) WriteTlsPair(props tls.TlsCertificateProps, pemPair *tls.TlsPem secret.Data[v1.TLSCertKey] = pemPair.Certificate secret.Data[v1.TLSPrivateKeyKey] = pemPair.PrivateKey - _, err = c.UpdateResource(Secrets, props.Namespace, secret, false) + _, err = c.UpdateResource("", Secrets, props.Namespace, secret, false) if err != nil { return err } diff --git a/pkg/dclient/client.go b/pkg/dclient/client.go index 7c27f01b2a..6d9e6429b0 100644 --- a/pkg/dclient/client.go +++ b/pkg/dclient/client.go @@ -74,7 +74,7 @@ func (c *Client) NewDynamicSharedInformerFactory(defaultResync time.Duration) dy //GetKubePolicyDeployment returns kube policy depoyment value func (c *Client) GetKubePolicyDeployment() (*apps.Deployment, error) { - kubePolicyDeployment, err := c.GetResource("Deployment", config.KubePolicyNamespace, config.KubePolicyDeploymentName) + kubePolicyDeployment, err := c.GetResource("", "Deployment", config.KubePolicyNamespace, config.KubePolicyDeploymentName) if err != nil { return nil, err } @@ -97,13 +97,13 @@ func (c *Client) GetCSRInterface() (csrtype.CertificateSigningRequestInterface, return c.kclient.CertificatesV1beta1().CertificateSigningRequests(), nil } -func (c *Client) getInterface(resource string) dynamic.NamespaceableResourceInterface { - return c.client.Resource(c.getGroupVersionMapper(resource)) +func (c *Client) getInterface(apiVersion string, kind string) dynamic.NamespaceableResourceInterface { + return c.client.Resource(c.getGroupVersionMapper(apiVersion, kind)) } -func (c *Client) getResourceInterface(kind string, namespace string) dynamic.ResourceInterface { +func (c *Client) getResourceInterface(apiVersion string, kind string, namespace string) dynamic.ResourceInterface { // Get the resource interface from kind - namespaceableInterface := c.getInterface(kind) + namespaceableInterface := c.getInterface(apiVersion, kind) // Get the namespacable interface var resourceInteface dynamic.ResourceInterface if namespace != "" { @@ -115,18 +115,22 @@ func (c *Client) getResourceInterface(kind string, namespace string) dynamic.Res } // Keep this a stateful as the resource list will be based on the kubernetes version we connect to -func (c *Client) getGroupVersionMapper(kind string) schema.GroupVersionResource { - return c.DiscoveryClient.GetGVRFromKind(kind) +func (c *Client) getGroupVersionMapper(apiVersion string, kind string) schema.GroupVersionResource { + if apiVersion == "" { + return c.DiscoveryClient.GetGVRFromKind(kind) + } + return c.DiscoveryClient.GetGVRFromAPIVersionKind(apiVersion, kind) + } // GetResource returns the resource in unstructured/json format -func (c *Client) GetResource(kind string, namespace string, name string, subresources ...string) (*unstructured.Unstructured, error) { - return c.getResourceInterface(kind, namespace).Get(name, meta.GetOptions{}, subresources...) +func (c *Client) GetResource(apiVersion string, kind string, namespace string, name string, subresources ...string) (*unstructured.Unstructured, error) { + return c.getResourceInterface(apiVersion, kind, namespace).Get(name, meta.GetOptions{}, subresources...) } //PatchResource patches the resource -func (c *Client) PatchResource(kind string, namespace string, name string, patch []byte) (*unstructured.Unstructured, error) { - return c.getResourceInterface(kind, namespace).Patch(name, patchTypes.JSONPatchType, patch, meta.PatchOptions{}) +func (c *Client) PatchResource(apiVersion string, kind string, namespace string, name string, patch []byte) (*unstructured.Unstructured, error) { + return c.getResourceInterface(apiVersion, kind, namespace).Patch(name, patchTypes.JSONPatchType, patch, meta.PatchOptions{}) } // GetDynamicInterface fetches underlying dynamic interface @@ -136,59 +140,59 @@ func (c *Client) GetDynamicInterface() dynamic.Interface { // ListResource returns the list of resources in unstructured/json format // Access items using []Items -func (c *Client) ListResource(kind string, namespace string, lselector *meta.LabelSelector) (*unstructured.UnstructuredList, error) { +func (c *Client) ListResource(apiVersion string, kind string, namespace string, lselector *meta.LabelSelector) (*unstructured.UnstructuredList, error) { options := meta.ListOptions{} if lselector != nil { options = meta.ListOptions{LabelSelector: helperv1.FormatLabelSelector(lselector)} } - return c.getResourceInterface(kind, namespace).List(options) + return c.getResourceInterface(apiVersion, kind, namespace).List(options) } // DeleteResource deletes the specified resource -func (c *Client) DeleteResource(kind string, namespace string, name string, dryRun bool) error { +func (c *Client) DeleteResource(apiVersion string, kind string, namespace string, name string, dryRun bool) error { options := meta.DeleteOptions{} if dryRun { options = meta.DeleteOptions{DryRun: []string{meta.DryRunAll}} } - return c.getResourceInterface(kind, namespace).Delete(name, &options) + return c.getResourceInterface(apiVersion, kind, namespace).Delete(name, &options) } // CreateResource creates object for the specified resource/namespace -func (c *Client) CreateResource(kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) { +func (c *Client) CreateResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) { options := meta.CreateOptions{} if dryRun { options = meta.CreateOptions{DryRun: []string{meta.DryRunAll}} } // convert typed to unstructured obj if unstructuredObj := convertToUnstructured(obj); unstructuredObj != nil { - return c.getResourceInterface(kind, namespace).Create(unstructuredObj, options) + return c.getResourceInterface(apiVersion, kind, namespace).Create(unstructuredObj, options) } return nil, fmt.Errorf("Unable to create resource ") } // UpdateResource updates object for the specified resource/namespace -func (c *Client) UpdateResource(kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) { +func (c *Client) UpdateResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) { options := meta.UpdateOptions{} if dryRun { options = meta.UpdateOptions{DryRun: []string{meta.DryRunAll}} } // convert typed to unstructured obj if unstructuredObj := convertToUnstructured(obj); unstructuredObj != nil { - return c.getResourceInterface(kind, namespace).Update(unstructuredObj, options) + return c.getResourceInterface(apiVersion, kind, namespace).Update(unstructuredObj, options) } return nil, fmt.Errorf("Unable to update resource ") } // UpdateStatusResource updates the resource "status" subresource -func (c *Client) UpdateStatusResource(kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) { +func (c *Client) UpdateStatusResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) { options := meta.UpdateOptions{} if dryRun { options = meta.UpdateOptions{DryRun: []string{meta.DryRunAll}} } // convert typed to unstructured obj if unstructuredObj := convertToUnstructured(obj); unstructuredObj != nil { - return c.getResourceInterface(kind, namespace).UpdateStatus(unstructuredObj, options) + return c.getResourceInterface(apiVersion, kind, namespace).UpdateStatus(unstructuredObj, options) } return nil, fmt.Errorf("Unable to update resource ") } @@ -221,8 +225,9 @@ func convertToCSR(obj *unstructured.Unstructured) (*certificates.CertificateSign //IDiscovery provides interface to mange Kind and GVR mapping type IDiscovery interface { - FindResource(kind string) (*meta.APIResource, schema.GroupVersionResource, error) + FindResource(apiVersion string, kind string) (*meta.APIResource, schema.GroupVersionResource, error) GetGVRFromKind(kind string) schema.GroupVersionResource + GetGVRFromAPIVersionKind(apiVersion string, kind string) schema.GroupVersionResource GetServerVersion() (*version.Info, error) OpenAPISchema() (*openapi_v2.Document, error) } @@ -265,7 +270,7 @@ func (c ServerPreferredResources) OpenAPISchema() (*openapi_v2.Document, error) // GetGVRFromKind get the Group Version Resource from kind func (c ServerPreferredResources) GetGVRFromKind(kind string) schema.GroupVersionResource { - _, gvr, err := c.FindResource(kind) + _, gvr, err := c.FindResource("", kind) if err != nil { c.log.Info("schema not found", "kind", kind) return schema.GroupVersionResource{} @@ -274,6 +279,17 @@ func (c ServerPreferredResources) GetGVRFromKind(kind string) schema.GroupVersio return gvr } +// GetGVRFromAPIVersionKind get the Group Version Resource from APIVersion and kind +func (c ServerPreferredResources) GetGVRFromAPIVersionKind(apiVersion string, kind string) schema.GroupVersionResource { + _, gvr, err := c.FindResource(apiVersion, kind) + if err != nil { + c.log.Info("schema not found", "kind", kind, "apiVersion", apiVersion, "Error : ", err) + return schema.GroupVersionResource{} + } + + return gvr +} + // GetServerVersion returns the server version of the cluster func (c ServerPreferredResources) GetServerVersion() (*version.Info, error) { return c.cachedClient.ServerVersion() @@ -281,15 +297,15 @@ func (c ServerPreferredResources) GetServerVersion() (*version.Info, error) { // FindResource finds an API resource that matches 'kind'. If the resource is not // found and the Cache is not fresh, the cache is invalidated and a retry is attempted -func (c ServerPreferredResources) FindResource(kind string) (*meta.APIResource, schema.GroupVersionResource, error) { - r, gvr, err := c.findResource(kind) +func (c ServerPreferredResources) FindResource(apiVersion string, kind string) (*meta.APIResource, schema.GroupVersionResource, error) { + r, gvr, err := c.findResource(apiVersion, kind) if err == nil { return r, gvr, nil } if !c.cachedClient.Fresh() { c.cachedClient.Invalidate() - if r, gvr, err = c.findResource(kind); err == nil { + if r, gvr, err = c.findResource(apiVersion, kind); err == nil { return r, gvr, nil } } @@ -297,18 +313,29 @@ func (c ServerPreferredResources) FindResource(kind string) (*meta.APIResource, return nil, schema.GroupVersionResource{}, err } -func (c ServerPreferredResources) findResource(k string) (*meta.APIResource, schema.GroupVersionResource, error) { - serverresources, err := c.cachedClient.ServerPreferredResources() +func (c ServerPreferredResources) findResource(apiVersion string, kind string) (*meta.APIResource, schema.GroupVersionResource, error) { + var serverresources []*meta.APIResourceList + var err error + if apiVersion == "" { + serverresources, err = c.cachedClient.ServerPreferredResources() + } else { + serverresources, err = c.cachedClient.ServerResources() + } + if err != nil { c.log.Error(err, "failed to get registered preferred resources") return nil, schema.GroupVersionResource{}, err } for _, serverresource := range serverresources { + + if apiVersion != "" && serverresource.GroupVersion != apiVersion { + continue + } for _, resource := range serverresource.APIResources { // skip the resource names with "/", to avoid comparison with subresources - if resource.Kind == k && !strings.Contains(resource.Name, "/") { + if resource.Kind == kind && !strings.Contains(resource.Name, "/") { gv, err := schema.ParseGroupVersion(serverresource.GroupVersion) if err != nil { c.log.Error(err, "failed to parse groupVersion", "groupVersion", serverresource.GroupVersion) @@ -320,5 +347,5 @@ func (c ServerPreferredResources) findResource(k string) (*meta.APIResource, sch } } - return nil, schema.GroupVersionResource{}, fmt.Errorf("kind '%s' not found", k) + return nil, schema.GroupVersionResource{}, fmt.Errorf("kind '%s' not found in apiVersion '%s'", kind, apiVersion) } diff --git a/pkg/dclient/client_test.go b/pkg/dclient/client_test.go index bc559446c5..b9f25902d8 100644 --- a/pkg/dclient/client_test.go +++ b/pkg/dclient/client_test.go @@ -63,32 +63,32 @@ func newFixture(t *testing.T) *fixture { func TestCRUDResource(t *testing.T) { f := newFixture(t) // Get Resource - _, err := f.client.GetResource("thekind", "ns-foo", "name-foo") + _, err := f.client.GetResource("", "thekind", "ns-foo", "name-foo") if err != nil { t.Errorf("GetResource not working: %s", err) } // List Resources - _, err = f.client.ListResource("thekind", "ns-foo", nil) + _, err = f.client.ListResource("", "thekind", "ns-foo", nil) if err != nil { t.Errorf("ListResource not working: %s", err) } // DeleteResouce - err = f.client.DeleteResource("thekind", "ns-foo", "name-bar", false) + err = f.client.DeleteResource("", "thekind", "ns-foo", "name-bar", false) if err != nil { t.Errorf("DeleteResouce not working: %s", err) } // CreateResource - _, err = f.client.CreateResource("thekind", "ns-foo", newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"), false) + _, err = f.client.CreateResource("", "thekind", "ns-foo", newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"), false) if err != nil { t.Errorf("CreateResource not working: %s", err) } // UpdateResource - _, err = f.client.UpdateResource("thekind", "ns-foo", newUnstructuredWithSpec("group/version", "TheKind", "ns-foo", "name-foo1", map[string]interface{}{"foo": "bar"}), false) + _, err = f.client.UpdateResource("", "thekind", "ns-foo", newUnstructuredWithSpec("group/version", "TheKind", "ns-foo", "name-foo1", map[string]interface{}{"foo": "bar"}), false) if err != nil { t.Errorf("UpdateResource not working: %s", err) } // UpdateStatusResource - _, err = f.client.UpdateStatusResource("thekind", "ns-foo", newUnstructuredWithSpec("group/version", "TheKind", "ns-foo", "name-foo1", map[string]interface{}{"foo": "status"}), false) + _, err = f.client.UpdateStatusResource("", "thekind", "ns-foo", newUnstructuredWithSpec("group/version", "TheKind", "ns-foo", "name-foo1", map[string]interface{}{"foo": "status"}), false) if err != nil { t.Errorf("UpdateStatusResource not working: %s", err) } diff --git a/pkg/dclient/utils.go b/pkg/dclient/utils.go index 0ed2459901..5e2e28f573 100644 --- a/pkg/dclient/utils.go +++ b/pkg/dclient/utils.go @@ -78,7 +78,12 @@ func (c *fakeDiscoveryClient) GetGVRFromKind(kind string) schema.GroupVersionRes return c.getGVR(resource) } -func (c *fakeDiscoveryClient) FindResource(kind string) (*meta.APIResource, schema.GroupVersionResource, error) { +func (c *fakeDiscoveryClient) GetGVRFromAPIVersionKind(apiVersion string, kind string) schema.GroupVersionResource { + resource := strings.ToLower(kind) + "s" + return c.getGVR(resource) +} + +func (c *fakeDiscoveryClient) FindResource(apiVersion string, kind string) (*meta.APIResource, schema.GroupVersionResource, error) { return nil, schema.GroupVersionResource{}, fmt.Errorf("Not implemented") } diff --git a/pkg/event/controller.go b/pkg/event/controller.go index 480bd29862..8dd35cfdf2 100644 --- a/pkg/event/controller.go +++ b/pkg/event/controller.go @@ -184,7 +184,7 @@ func (gen *Generator) syncHandler(key Info) error { return err } default: - robj, err = gen.client.GetResource(key.Kind, key.Namespace, key.Name) + robj, err = gen.client.GetResource("", key.Kind, key.Namespace, key.Name) if err != nil { logger.Error(err, "failed to get resource", "kind", key.Kind, "name", key.Name, "namespace", key.Namespace) return err diff --git a/pkg/generate/cleanup/cleanup.go b/pkg/generate/cleanup/cleanup.go index de1758aeba..735b524273 100644 --- a/pkg/generate/cleanup/cleanup.go +++ b/pkg/generate/cleanup/cleanup.go @@ -26,7 +26,7 @@ func (c *Controller) processGR(gr kyverno.GenerateRequest) error { } func ownerResourceExists(log logr.Logger, client *dclient.Client, gr kyverno.GenerateRequest) bool { - _, err := client.GetResource(gr.Spec.Resource.Kind, gr.Spec.Resource.Namespace, gr.Spec.Resource.Name) + _, err := client.GetResource("", gr.Spec.Resource.Kind, gr.Spec.Resource.Namespace, gr.Spec.Resource.Name) // trigger resources has been deleted if apierrors.IsNotFound(err) { return false @@ -41,7 +41,7 @@ func ownerResourceExists(log logr.Logger, client *dclient.Client, gr kyverno.Gen func deleteGeneratedResources(log logr.Logger, client *dclient.Client, gr kyverno.GenerateRequest) error { for _, genResource := range gr.Status.GeneratedResources { - err := client.DeleteResource(genResource.Kind, genResource.Namespace, genResource.Name, false) + err := client.DeleteResource("", genResource.Kind, genResource.Namespace, genResource.Name, false) if apierrors.IsNotFound(err) { log.Error(err, "resource not foundl will not delete", "genKind", gr.Spec.Resource.Kind, "genNamespace", gr.Spec.Resource.Namespace, "genName", gr.Spec.Resource.Name) continue diff --git a/pkg/generate/generate.go b/pkg/generate/generate.go index 7d5a24db70..e7c0530ad5 100644 --- a/pkg/generate/generate.go +++ b/pkg/generate/generate.go @@ -18,7 +18,7 @@ import ( ) func (c *Controller) processGR(gr *kyverno.GenerateRequest) error { - logger := c.log.WithValues("name", gr.Name, "policy", gr.Spec.Policy, "kind", gr.Spec.Resource.Kind, "namespace", gr.Spec.Resource.Namespace, "name", gr.Spec.Resource.Name) + logger := c.log.WithValues("name", gr.Name, "policy", gr.Spec.Policy, "kind", gr.Spec.Resource.Kind, "apiVersion", gr.Spec.Resource.APIVersion, "namespace", gr.Spec.Resource.Namespace, "name", gr.Spec.Resource.Name) var err error var resource *unstructured.Unstructured var genResources []kyverno.ResourceSpec @@ -43,7 +43,7 @@ func (c *Controller) processGR(gr *kyverno.GenerateRequest) error { } func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyverno.GenerateRequest) ([]kyverno.ResourceSpec, error) { - logger := c.log.WithValues("name", gr.Name, "policy", gr.Spec.Policy, "kind", gr.Spec.Resource.Kind, "namespace", gr.Spec.Resource.Namespace, "name", gr.Spec.Resource.Name) + logger := c.log.WithValues("name", gr.Name, "policy", gr.Spec.Policy, "kind", gr.Spec.Resource.Kind, "apiVersion", gr.Spec.Resource.APIVersion, "namespace", gr.Spec.Resource.Namespace, "name", gr.Spec.Resource.Name) // Get the list of rules to be applied // get policy // build context @@ -53,14 +53,14 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern if err != nil { if apierrors.IsNotFound(err) { for _, e := range gr.Status.GeneratedResources { - resp, err := c.client.GetResource(e.Kind, e.Namespace, e.Name) + resp, err := c.client.GetResource(e.APIVersion, e.Kind, e.Namespace, e.Name) if err != nil { logger.Error(err, "Generated resource failed to get", "Resource", e.Name) } labels := resp.GetLabels() if labels["policy.kyverno.io/synchronize"] == "enable" { - if err := c.client.DeleteResource(resp.GetKind(), resp.GetNamespace(), resp.GetName(), false); err != nil { + if err := c.client.DeleteResource(resp.GetAPIVersion(), resp.GetKind(), resp.GetNamespace(), resp.GetName(), false); err != nil { logger.Error(err, "Generated resource is not deleted", "Resource", e.Name) } } @@ -231,11 +231,17 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou return noGenResource, err } + genAPIVersion, _, err := unstructured.NestedString(genUnst.Object, "apiVersion") + if err != nil { + return noGenResource, err + } + // Resource to be generated newGenResource := kyverno.ResourceSpec{ - Kind: genKind, - Namespace: genNamespace, - Name: genName, + APIVersion: genAPIVersion, + Kind: genKind, + Namespace: genNamespace, + Name: genName, } genData, _, err := unstructured.NestedMap(genUnst.Object, "data") if err != nil { @@ -247,9 +253,9 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou } if genData != nil { - rdata, mode, err = manageData(log, genKind, genNamespace, genName, genData, client, resource) + rdata, mode, err = manageData(log, genAPIVersion, genKind, genNamespace, genName, genData, client, resource) } else { - rdata, mode, err = manageClone(log, genKind, genNamespace, genName, genCopy, client, resource) + rdata, mode, err = manageClone(log, genAPIVersion, genKind, genNamespace, genName, genCopy, client, resource) } if err != nil { return noGenResource, err @@ -275,12 +281,13 @@ 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) - logger := log.WithValues("genKind", genKind, "genNamespace", genNamespace, "genName", genName) + logger := log.WithValues("genKind", genKind, "genAPIVersion", genAPIVersion, "genNamespace", genNamespace, "genName", genName) // Add Synchronize label label := newResource.GetLabels() @@ -297,7 +304,7 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou newResource.SetResourceVersion("") // Create the resource logger.V(4).Info("creating new resource") - _, err = client.CreateResource(genKind, genNamespace, newResource, false) + _, err = client.CreateResource(genAPIVersion, genKind, genNamespace, newResource, false) if err != nil { // Failed to create resource return noGenResource, err @@ -310,7 +317,7 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou if rule.Generation.Synchronize { logger.V(4).Info("updating existing resource") // Update the resource - _, err := client.UpdateResource(genKind, genNamespace, newResource, false) + _, err := client.UpdateResource(genAPIVersion, genKind, genNamespace, newResource, false) if err != nil { logger.Error(err, "updating existing resource") // Failed to update resource @@ -328,11 +335,11 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou return newGenResource, nil } -func manageData(log logr.Logger, kind, namespace, name string, data map[string]interface{}, client *dclient.Client, resource unstructured.Unstructured) (map[string]interface{}, ResourceMode, error) { +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(kind, namespace, name) + 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, "genNamespace", namespace, "genName", name) + 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 { @@ -346,12 +353,12 @@ func manageData(log logr.Logger, kind, namespace, name string, data map[string]i // 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, "genNamespace", namespace, "genName", name) + 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 } -func manageClone(log logr.Logger, kind, namespace, name string, clone map[string]interface{}, client *dclient.Client, resource unstructured.Unstructured) (map[string]interface{}, ResourceMode, error) { +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) { newRNs, _, err := unstructured.NestedString(clone, "namespace") if err != nil { return nil, Skip, err @@ -360,6 +367,7 @@ func manageClone(log logr.Logger, kind, namespace, name string, clone map[string if err != nil { return nil, Skip, err } + // Short-circuit if the resource to be generated and the clone is the same if newRNs == namespace && newRName == name { // attempting to clone it self, this will fail -> short-ciruit it @@ -367,13 +375,13 @@ func manageClone(log logr.Logger, kind, namespace, name string, clone map[string } // check if the resource as reference in clone exists? - obj, err := client.GetResource(kind, newRNs, newRName) + obj, err := client.GetResource(apiVersion, kind, newRNs, newRName) if err != nil { - return nil, Skip, fmt.Errorf("reference clone resource %s/%s/%s not found. %v", kind, newRNs, newRName, err) + return nil, Skip, fmt.Errorf("reference clone resource %s/%s/%s/%s not found. %v", apiVersion, kind, newRNs, newRName, err) } // check if resource to be generated exists - newResource, err := client.GetResource(kind, namespace, name) + newResource, err := client.GetResource(apiVersion, kind, namespace, name) if err == nil { obj.SetUID(newResource.GetUID()) obj.SetSelfLink(newResource.GetSelfLink()) @@ -388,7 +396,7 @@ func manageClone(log logr.Logger, kind, namespace, name string, clone map[string //TODO: check this if !apierrors.IsNotFound(err) { - log.Error(err, "reference/clone resource is not found", "genKind", kind, "genNamespace", namespace, "genName", name) + 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 } diff --git a/pkg/generate/resource.go b/pkg/generate/resource.go index 7d43eb3cb1..c7348a22db 100644 --- a/pkg/generate/resource.go +++ b/pkg/generate/resource.go @@ -7,5 +7,5 @@ import ( ) func getResource(client *dclient.Client, resourceSpec kyverno.ResourceSpec) (*unstructured.Unstructured, error) { - return client.GetResource(resourceSpec.Kind, resourceSpec.Namespace, resourceSpec.Name) + return client.GetResource(resourceSpec.APIVersion, resourceSpec.Kind, resourceSpec.Namespace, resourceSpec.Name) } diff --git a/pkg/kyverno/apply/command.go b/pkg/kyverno/apply/command.go index f3e8a5881a..cfadd54f8a 100644 --- a/pkg/kyverno/apply/command.go +++ b/pkg/kyverno/apply/command.go @@ -190,7 +190,7 @@ func getResourcesOfTypeFromCluster(resourceTypes []string, dClient *client.Clien var resources []*unstructured.Unstructured for _, kind := range resourceTypes { - resourceList, err := dClient.ListResource(kind, "", nil) + resourceList, err := dClient.ListResource("", kind, "", nil) if err != nil { return nil, err } diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go index 2365ae1e8a..1e3dbe7e62 100644 --- a/pkg/policy/existing.go +++ b/pkg/policy/existing.go @@ -65,7 +65,7 @@ func (pc *PolicyController) listResources(policy *kyverno.ClusterPolicy) map[str for _, rule := range policy.Spec.Rules { for _, k := range rule.MatchResources.Kinds { - resourceSchema, _, err := pc.client.DiscoveryClient.FindResource(k) + resourceSchema, _, err := pc.client.DiscoveryClient.FindResource("", k) if err != nil { pc.log.Error(err, "failed to find resource", "kind", k) continue @@ -179,7 +179,7 @@ func getResourcesPerNamespace(kind string, client *client.Client, namespace stri namespace = "" } - list, err := client.ListResource(kind, namespace, ls) + list, err := client.ListResource("", kind, namespace, ls) if err != nil { log.Error(err, "failed to list resources", "kind", kind, "namespace", namespace) return nil diff --git a/pkg/policyviolation/common.go b/pkg/policyviolation/common.go index 4fdc20ace8..1b6271206f 100644 --- a/pkg/policyviolation/common.go +++ b/pkg/policyviolation/common.go @@ -44,7 +44,7 @@ func retryGetResource(client *client.Client, rspec kyverno.ResourceSpec) (*unstr var obj *unstructured.Unstructured var err error getResource := func() error { - obj, err = client.GetResource(rspec.Kind, rspec.Namespace, rspec.Name) + obj, err = client.GetResource("", rspec.Kind, rspec.Namespace, rspec.Name) log.Log.V(4).Info(fmt.Sprintf("retry %v getting %s/%s/%s", i, rspec.Kind, rspec.Namespace, rspec.Name)) i++ return err diff --git a/pkg/testrunner/scenario.go b/pkg/testrunner/scenario.go index 33e58656ac..7982a91f1a 100644 --- a/pkg/testrunner/scenario.go +++ b/pkg/testrunner/scenario.go @@ -169,14 +169,14 @@ func runTestCase(t *testing.T, tc scaseT) bool { } func createNamespace(client *client.Client, ns *unstructured.Unstructured) error { - _, err := client.CreateResource("Namespace", "", ns, false) + _, err := client.CreateResource("", "Namespace", "", ns, false) return err } func validateGeneratedResources(t *testing.T, client *client.Client, policy kyverno.ClusterPolicy, namespace string, expected []kyverno.ResourceSpec) { t.Log("--validate if resources are generated---") // list of expected generated resources for _, resource := range expected { - if _, err := client.GetResource(resource.Kind, namespace, resource.Name); err != nil { + if _, err := client.GetResource("", resource.Kind, namespace, resource.Name); err != nil { t.Errorf("generated resource %s/%s/%s not found. %v", resource.Kind, namespace, resource.Name, err) } } diff --git a/pkg/utils/util.go b/pkg/utils/util.go index 13920a02c4..055aa458ab 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -88,7 +88,7 @@ func CleanupOldCrd(client *dclient.Client, log logr.Logger) { logger := log.WithName("CleanupOldCrd") gvr := client.DiscoveryClient.GetGVRFromKind("NamespacedPolicyViolation") if !reflect.DeepEqual(gvr, (schema.GroupVersionResource{})) { - if err := client.DeleteResource("CustomResourceDefinition", "", "namespacedpolicyviolations.kyverno.io", false); err != nil { + if err := client.DeleteResource("", "CustomResourceDefinition", "", "namespacedpolicyviolations.kyverno.io", false); err != nil { logger.Error(err, "Failed to remove prevous CRD", "kind", "namespacedpolicyviolation") } } diff --git a/pkg/webhookconfig/checker.go b/pkg/webhookconfig/checker.go index 50a8c100f2..7fea9922e7 100644 --- a/pkg/webhookconfig/checker.go +++ b/pkg/webhookconfig/checker.go @@ -70,7 +70,7 @@ func (wrc *WebhookRegistrationClient) removeVerifyWebhookMutatingWebhookConfig(w } logger := wrc.log.WithValues("name", mutatingConfig) - err = wrc.client.DeleteResource(MutatingWebhookConfigurationKind, "", mutatingConfig, false) + err = wrc.client.DeleteResource("", MutatingWebhookConfigurationKind, "", mutatingConfig, false) if errorsapi.IsNotFound(err) { logger.V(5).Info("verify webhook configuration not found") return diff --git a/pkg/webhookconfig/registration.go b/pkg/webhookconfig/registration.go index 5573af61b1..012837b6c6 100644 --- a/pkg/webhookconfig/registration.go +++ b/pkg/webhookconfig/registration.go @@ -112,7 +112,7 @@ func (wrc *WebhookRegistrationClient) CreateResourceMutatingWebhookConfiguration // clientConfig - service config = wrc.constructMutatingWebhookConfig(caData) } - _, err := wrc.client.CreateResource(MutatingWebhookConfigurationKind, "", *config, false) + _, err := wrc.client.CreateResource("", MutatingWebhookConfigurationKind, "", *config, false) if errorsapi.IsAlreadyExists(err) { logger.V(6).Info("resource mutating webhook configuration already exists", "name", config.Name) return nil @@ -143,7 +143,7 @@ func (wrc *WebhookRegistrationClient) CreateResourceValidatingWebhookConfigurati } logger := wrc.log.WithValues("kind", ValidatingWebhookConfigurationKind, "name", config.Name) - _, err := wrc.client.CreateResource(ValidatingWebhookConfigurationKind, "", *config, false) + _, err := wrc.client.CreateResource("", ValidatingWebhookConfigurationKind, "", *config, false) if errorsapi.IsAlreadyExists(err) { logger.V(6).Info("resource validating webhook configuration already exists", "name", config.Name) return nil @@ -179,7 +179,7 @@ func (wrc *WebhookRegistrationClient) createPolicyValidatingWebhookConfiguration logger := wrc.log.WithValues("kind", ValidatingWebhookConfigurationKind, "name", config.Name) // create validating webhook configuration resource - if _, err := wrc.client.CreateResource(ValidatingWebhookConfigurationKind, "", *config, false); err != nil { + if _, err := wrc.client.CreateResource("", ValidatingWebhookConfigurationKind, "", *config, false); err != nil { return err } logger.V(4).Info("created resource") @@ -207,7 +207,7 @@ func (wrc *WebhookRegistrationClient) createPolicyMutatingWebhookConfiguration() } // create mutating webhook configuration resource - if _, err := wrc.client.CreateResource(MutatingWebhookConfigurationKind, "", *config, false); err != nil { + if _, err := wrc.client.CreateResource("", MutatingWebhookConfigurationKind, "", *config, false); err != nil { return err } wrc.log.V(4).Info("reated Mutating Webhook Configuration", "name", config.Name) @@ -236,7 +236,7 @@ func (wrc *WebhookRegistrationClient) createVerifyMutatingWebhookConfiguration() } // create mutating webhook configuration resource - if _, err := wrc.client.CreateResource(MutatingWebhookConfigurationKind, "", *config, false); err != nil { + if _, err := wrc.client.CreateResource("", MutatingWebhookConfigurationKind, "", *config, false); err != nil { return err } @@ -295,7 +295,7 @@ func (wrc *WebhookRegistrationClient) removePolicyMutatingWebhookConfiguration(w } logger := wrc.log.WithValues("name", mutatingConfig) - err := wrc.client.DeleteResource(MutatingWebhookConfigurationKind, "", mutatingConfig, false) + err := wrc.client.DeleteResource("", MutatingWebhookConfigurationKind, "", mutatingConfig, false) if errorsapi.IsNotFound(err) { logger.V(5).Info("policy mutating webhook configuration not found") return @@ -321,7 +321,7 @@ func (wrc *WebhookRegistrationClient) removePolicyValidatingWebhookConfiguration logger := wrc.log.WithValues("name", validatingConfig) logger.V(4).Info("removing validating webhook configuration") - err := wrc.client.DeleteResource(ValidatingWebhookConfigurationKind, "", validatingConfig, false) + err := wrc.client.DeleteResource("", ValidatingWebhookConfigurationKind, "", validatingConfig, false) if errorsapi.IsNotFound(err) { logger.V(5).Info("policy validating webhook configuration not found") return diff --git a/pkg/webhookconfig/resource.go b/pkg/webhookconfig/resource.go index 164b46573c..d513888618 100644 --- a/pkg/webhookconfig/resource.go +++ b/pkg/webhookconfig/resource.go @@ -70,7 +70,7 @@ func (wrc *WebhookRegistrationClient) RemoveResourceMutatingWebhookConfiguration configName := wrc.GetResourceMutatingWebhookConfigName() logger := wrc.log.WithValues("kind", MutatingWebhookConfigurationKind, "name", configName) // delete webhook configuration - err := wrc.client.DeleteResource(MutatingWebhookConfigurationKind, "", configName, false) + err := wrc.client.DeleteResource("", MutatingWebhookConfigurationKind, "", configName, false) if errors.IsNotFound(err) { logger.V(4).Info("webhook configuration not found") return @@ -144,7 +144,7 @@ func (wrc *WebhookRegistrationClient) GetResourceValidatingWebhookConfigName() s func (wrc *WebhookRegistrationClient) RemoveResourceValidatingWebhookConfiguration() { configName := wrc.GetResourceValidatingWebhookConfigName() logger := wrc.log.WithValues("kind", ValidatingWebhookConfigurationKind, "name", configName) - err := wrc.client.DeleteResource(ValidatingWebhookConfigurationKind, "", configName, false) + err := wrc.client.DeleteResource("", ValidatingWebhookConfigurationKind, "", configName, false) if errors.IsNotFound(err) { logger.V(5).Info("webhook configuration not found") return