1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-04-18 02:06:52 +00:00

refactor: dclient package ()

* refactor: replace clientset by inteface

Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>

* refactor: dclient package

Signed-off-by: Charles-Edouard Brétéché <charled.breteche@gmail.com>
This commit is contained in:
Charles-Edouard Brétéché 2022-05-03 07:30:04 +02:00 committed by GitHub
parent 6e07acdd87
commit c79223393b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 473 additions and 481 deletions

View file

@ -170,7 +170,7 @@ func applyCommandHelper(resourcePaths []string, userInfoPath string, cluster boo
return rc, resources, skipInvalidPolicies, pvInfos, sanitizederror.NewWithError("failed to initialize openAPIController", err)
}
var dClient *client.Client
var dClient client.Interface
if cluster {
restConfig, err := kubernetesConfig.ToRESTConfig()
if err != nil {

View file

@ -1,12 +1,8 @@
package apply
import (
"reflect"
report "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
client "github.com/kyverno/kyverno/pkg/dclient"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/log"
)
@ -22,63 +18,6 @@ func generateCLIRaw(reports []*unstructured.Unstructured) (*unstructured.Unstruc
return mergeClusterReport(reports)
}
// generateToCluster updates the existing policy reports in the cluster
// creates new report if not exist
func generateToCluster(dClient *client.Client, reports []*unstructured.Unstructured) {
var clusterReports, namespaceReports []*unstructured.Unstructured
for _, report := range reports {
if report.GetNamespace() == "" {
clusterReports = append(clusterReports, report)
} else {
namespaceReports = append(namespaceReports, report)
}
}
if clusterReport, err := mergeClusterReport(clusterReports); err != nil {
log.Log.V(3).Info("failed to merge cluster report", "error", err)
} else {
if err := updateReport(dClient, clusterReport); err != nil {
log.Log.V(3).Info("failed to update policy report", "report", clusterReport.GetName(), "error", err)
}
}
for _, report := range namespaceReports {
if err := updateReport(dClient, report); err != nil {
log.Log.V(3).Info("failed to update policy report", "report", report.GetName(), "error", err)
}
}
}
func updateReport(dClient *client.Client, new *unstructured.Unstructured) error {
old, err := dClient.GetResource(new.GetAPIVersion(), new.GetKind(), new.GetNamespace(), new.GetName())
if err != nil {
if apierrors.IsNotFound(err) {
if _, err := dClient.CreateResource(new.GetAPIVersion(), new.GetKind(), new.GetNamespace(), new, false); err != nil {
return err
}
}
return err
}
oldResults, _, err := unstructured.NestedSlice(old.UnstructuredContent(), "results")
if err != nil {
log.Log.V(3).Info("failed to get results entry", "error", err)
}
newResults, _, err := unstructured.NestedSlice(new.UnstructuredContent(), "results")
if err != nil {
log.Log.V(3).Info("failed to get results entry", "error", err)
}
if reflect.DeepEqual(oldResults, newResults) {
log.Log.V(3).Info("policy report unchanged", "name", new.GetName())
return nil
}
_, err = dClient.UpdateResource(new.GetAPIVersion(), new.GetKind(), new.GetNamespace(), new, false)
return err
}
func mergeClusterReport(reports []*unstructured.Unstructured) (*unstructured.Unstructured, error) {
var resultsEntry []interface{}
res := &unstructured.Unstructured{}

View file

@ -713,7 +713,7 @@ func getFullPath(paths []string, policyResourcePath string, isGit bool) []string
func applyPoliciesFromPath(fs billy.Filesystem, policyBytes []byte, isGit bool, policyResourcePath string, rc *resultCounts, openAPIController *openapi.Controller, tf *testFilter) (err error) {
engineResponses := make([]*response.EngineResponse, 0)
var dClient *client.Client
var dClient client.Interface
values := &Test{}
var variablesString string
var pvInfos []policyreport.Info

View file

@ -650,7 +650,7 @@ func GetPoliciesFromPaths(fs billy.Filesystem, dirPath []string, isGit bool, pol
// GetResourceAccordingToResourcePath - get resources according to the resource path
func GetResourceAccordingToResourcePath(fs billy.Filesystem, resourcePaths []string,
cluster bool, policies []v1.PolicyInterface, dClient *client.Client, namespace string, policyReport bool, isGit bool, policyResourcePath string) (resources []*unstructured.Unstructured, err error) {
cluster bool, policies []v1.PolicyInterface, dClient client.Interface, namespace string, policyReport bool, isGit bool, policyResourcePath string) (resources []*unstructured.Unstructured, err error) {
if isGit {
resources, err = GetResourcesWithTest(fs, policies, resourcePaths, isGit, policyResourcePath)
if err != nil {

View file

@ -25,7 +25,7 @@ import (
// the resources are fetched from
// - local paths to resources, if given
// - the k8s cluster, if given
func GetResources(policies []v1.PolicyInterface, resourcePaths []string, dClient *client.Client, cluster bool, namespace string, policyReport bool) ([]*unstructured.Unstructured, error) {
func GetResources(policies []v1.PolicyInterface, resourcePaths []string, dClient client.Interface, cluster bool, namespace string, policyReport bool) ([]*unstructured.Unstructured, error) {
resources := make([]*unstructured.Unstructured, 0)
var err error
var resourceTypesMap = make(map[string]bool)
@ -58,7 +58,7 @@ func GetResources(policies []v1.PolicyInterface, resourcePaths []string, dClient
return resources, err
}
func whenClusterIsTrue(resourceTypes []string, dClient *client.Client, namespace string, resourcePaths []string, policyReport bool) ([]*unstructured.Unstructured, error) {
func whenClusterIsTrue(resourceTypes []string, dClient client.Interface, namespace string, resourcePaths []string, policyReport bool) ([]*unstructured.Unstructured, error) {
resources := make([]*unstructured.Unstructured, 0)
resourceMap, err := getResourcesOfTypeFromCluster(resourceTypes, dClient, namespace)
if err != nil {
@ -191,7 +191,7 @@ func GetResource(resourceBytes []byte) ([]*unstructured.Unstructured, error) {
return resources, nil
}
func getResourcesOfTypeFromCluster(resourceTypes []string, dClient *client.Client, namespace string) (map[string]*unstructured.Unstructured, error) {
func getResourcesOfTypeFromCluster(resourceTypes []string, dClient client.Interface, namespace string) (map[string]*unstructured.Unstructured, error) {
r := make(map[string]*unstructured.Unstructured)
for _, kind := range resourceTypes {

View file

@ -232,7 +232,7 @@ func acquireLeader(ctx context.Context, kubeClient kubernetes.Interface) error {
return err
}
func executeRequest(client *client.Client, kyvernoclient kyvernoclient.Interface, req request) error {
func executeRequest(client client.Interface, kyvernoclient kyvernoclient.Interface, req request) error {
switch req.kind {
case policyReportKind:
return removePolicyReport(client, req.kind)
@ -283,7 +283,7 @@ func gen(done <-chan struct{}, stopCh <-chan struct{}, requests ...request) <-ch
}
// processes the requests
func process(client *client.Client, kyvernoclient kyvernoclient.Interface, done <-chan struct{}, stopCh <-chan struct{}, requests <-chan request) <-chan error {
func process(client client.Interface, kyvernoclient kyvernoclient.Interface, done <-chan struct{}, stopCh <-chan struct{}, requests <-chan request) <-chan error {
logger := log.Log.WithName("process")
out := make(chan error)
go func() {
@ -337,7 +337,7 @@ func merge(done <-chan struct{}, stopCh <-chan struct{}, processes ...<-chan err
return out
}
func removeClusterPolicyReport(client *client.Client, kind string) error {
func removeClusterPolicyReport(client client.Interface, kind string) error {
logger := log.Log.WithName("removeClusterPolicyReport")
cpolrs, err := client.ListResource("", kind, "", policyreport.LabelSelector)
@ -352,7 +352,7 @@ func removeClusterPolicyReport(client *client.Client, kind string) error {
return nil
}
func removePolicyReport(client *client.Client, kind string) error {
func removePolicyReport(client client.Interface, kind string) error {
logger := log.Log.WithName("removePolicyReport")
polrs, err := client.ListResource("", kind, metav1.NamespaceAll, policyreport.LabelSelector)
@ -368,7 +368,7 @@ func removePolicyReport(client *client.Client, kind string) error {
return nil
}
func addClusterPolicyReportSelectorLabel(client *client.Client) {
func addClusterPolicyReportSelectorLabel(client client.Interface) {
logger := log.Log.WithName("addClusterPolicyReportSelectorLabel")
cpolrs, err := client.ListResource("", clusterPolicyReportKind, "", updateLabelSelector)
@ -384,7 +384,7 @@ func addClusterPolicyReportSelectorLabel(client *client.Client) {
}
}
func addPolicyReportSelectorLabel(client *client.Client) {
func addPolicyReportSelectorLabel(client client.Interface) {
logger := log.Log.WithName("addPolicyReportSelectorLabel")
polrs, err := client.ListResource("", policyReportKind, metav1.NamespaceAll, updateLabelSelector)
@ -400,7 +400,7 @@ func addPolicyReportSelectorLabel(client *client.Client) {
}
}
func removeReportChangeRequest(client *client.Client, kind string) error {
func removeReportChangeRequest(client client.Interface, kind string) error {
logger := log.Log.WithName("removeReportChangeRequest")
ns := config.KyvernoNamespace
@ -417,7 +417,7 @@ func removeReportChangeRequest(client *client.Client, kind string) error {
return nil
}
func removeClusterReportChangeRequest(client *client.Client, kind string) error {
func removeClusterReportChangeRequest(client client.Interface, kind string) error {
crcrList, err := client.ListResource("", kind, "", nil)
if err != nil {
log.Log.Error(err, "failed to list clusterReportChangeRequest")
@ -430,7 +430,7 @@ func removeClusterReportChangeRequest(client *client.Client, kind string) error
return nil
}
func deleteResource(client *client.Client, apiversion, kind, ns, name string) {
func deleteResource(client client.Interface, apiversion, kind, ns, name string) {
err := client.DeleteResource(apiversion, kind, ns, name, false)
if err != nil && !errors.IsNotFound(err) {
log.Log.Error(err, "failed to delete resource", "kind", kind, "name", name)
@ -440,7 +440,7 @@ func deleteResource(client *client.Client, apiversion, kind, ns, name string) {
log.Log.Info("successfully cleaned up resource", "kind", kind, "name", name)
}
func addSelectorLabel(client *client.Client, apiversion, kind, ns, name string) {
func addSelectorLabel(client client.Interface, apiversion, kind, ns, name string) {
res, err := client.GetResource(apiversion, kind, ns, name)
if err != nil && !errors.IsNotFound(err) {
log.Log.Error(err, "failed to get resource", "kind", kind, "name", name)

View file

@ -140,7 +140,7 @@ func main() {
// CRD CHECK
// - verify if Kyverno CRDs are available
if !utils.CRDsInstalled(client.DiscoveryClient) {
if !utils.CRDsInstalled(client.Discovery()) {
setupLog.Error(fmt.Errorf("CRDs not installed"), "Failed to access Kyverno CRDs")
os.Exit(1)
}
@ -515,7 +515,7 @@ func main() {
setupLog.Info("Kyverno shutdown successful")
}
func startOpenAPIController(client *dclient.Client, stopCh <-chan struct{}) *openapi.Controller {
func startOpenAPIController(client dclient.Interface, stopCh <-chan struct{}) *openapi.Controller {
openAPIController, err := openapi.NewOpenAPIController()
if err != nil {
setupLog.Error(err, "Failed to create openAPIController")

View file

@ -15,11 +15,11 @@ type CanIOptions struct {
namespace string
verb string
kind string
client *client.Client
client client.Interface
}
//NewCanI returns a new instance of operation access controller evaluator
func NewCanI(client *client.Client, kind, namespace, verb string) *CanIOptions {
func NewCanI(client client.Interface, kind, namespace, verb string) *CanIOptions {
return &CanIOptions{
namespace: namespace,
kind: kind,
@ -37,7 +37,7 @@ func NewCanI(client *client.Client, kind, namespace, verb string) *CanIOptions {
func (o *CanIOptions) RunAccessCheck() (bool, error) {
// get GroupVersionResource from RESTMapper
// get GVR from kind
gvr, err := o.client.DiscoveryClient.GetGVRFromKind(o.kind)
gvr, err := o.client.Discovery().GetGVRFromKind(o.kind)
if err != nil {
return false, fmt.Errorf("failed to get GVR for kind %s", o.kind)
}

View file

@ -17,7 +17,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func NewBackgroundContext(dclient *dclient.Client, ur *urkyverno.UpdateRequest,
func NewBackgroundContext(dclient dclient.Interface, ur *urkyverno.UpdateRequest,
policy kyverno.PolicyInterface, trigger *unstructured.Unstructured,
cfg config.Interface, namespaceLabels map[string]string, logger logr.Logger) (*engine.PolicyContext, bool, error) {

View file

@ -13,7 +13,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func GetResource(client *dclient.Client, urSpec urkyverno.UpdateRequestSpec, log logr.Logger) (*unstructured.Unstructured, error) {
func GetResource(client dclient.Interface, urSpec urkyverno.UpdateRequestSpec, log logr.Logger) (*unstructured.Unstructured, error) {
resourceSpec := urSpec.Resource
get := func() (*unstructured.Unstructured, error) {

View file

@ -44,7 +44,7 @@ func (c *Controller) processUR(ur urkyverno.UpdateRequest) error {
return nil
}
func ownerResourceExists(log logr.Logger, client *dclient.Client, ur urkyverno.UpdateRequest) bool {
func ownerResourceExists(log logr.Logger, client dclient.Interface, ur urkyverno.UpdateRequest) bool {
_, err := client.GetResource("", ur.Spec.Resource.Kind, ur.Spec.Resource.Namespace, ur.Spec.Resource.Name)
// trigger resources has been deleted
if apierrors.IsNotFound(err) {
@ -58,7 +58,7 @@ func ownerResourceExists(log logr.Logger, client *dclient.Client, ur urkyverno.U
return true
}
func deleteGeneratedResources(log logr.Logger, client *dclient.Client, ur urkyverno.UpdateRequest) error {
func deleteGeneratedResources(log logr.Logger, client dclient.Interface, ur urkyverno.UpdateRequest) error {
for _, genResource := range ur.Status.GeneratedResources {
err := client.DeleteResource("", genResource.Kind, genResource.Namespace, genResource.Name, false)
if err != nil && !apierrors.IsNotFound(err) {

View file

@ -34,7 +34,7 @@ const (
type Controller struct {
// dynamic client implementation
client *dclient.Client
client dclient.Interface
// typed client for kyverno CRDs
kyvernoClient kyvernoclient.Interface
@ -68,7 +68,7 @@ type Controller struct {
func NewController(
kubeClient kubernetes.Interface,
kyvernoclient kyvernoclient.Interface,
client *dclient.Client,
client dclient.Interface,
pInformer kyvernoinformer.ClusterPolicyInformer,
npInformer kyvernoinformer.PolicyInformer,
urInformer urkyvernoinformer.UpdateRequestInformer,

View file

@ -38,7 +38,7 @@ import (
type GenerateController struct {
// GenerateController updaterequest.GenerateController
client *dclient.Client
client dclient.Interface
// typed client for Kyverno CRDs
kyvernoClient kyvernoclient.Interface
@ -69,7 +69,7 @@ type GenerateController struct {
//NewGenerateController returns an instance of the Generate-Request Controller
func NewGenerateController(
kyvernoClient kyvernoclient.Interface,
client *dclient.Client,
client dclient.Interface,
policyLister kyvernolister.ClusterPolicyLister,
npolicyLister kyvernolister.PolicyLister,
urLister urlister.UpdateRequestNamespaceLister,
@ -370,7 +370,7 @@ 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, ur urkyverno.UpdateRequest) (kyverno.ResourceSpec, error) {
func applyRule(log logr.Logger, client dclient.Interface, rule kyverno.Rule, resource unstructured.Unstructured, ctx context.EvalInterface, policy string, ur urkyverno.UpdateRequest) (kyverno.ResourceSpec, error) {
var rdata map[string]interface{}
var err error
var mode ResourceMode
@ -519,7 +519,7 @@ func applyRule(log logr.Logger, client *dclient.Client, rule kyverno.Rule, resou
return newGenResource, nil
}
func manageData(log logr.Logger, apiVersion, kind, namespace, name string, data map[string]interface{}, client *dclient.Client) (map[string]interface{}, ResourceMode, error) {
func manageData(log logr.Logger, apiVersion, kind, namespace, name string, data map[string]interface{}, client dclient.Interface) (map[string]interface{}, ResourceMode, error) {
obj, err := client.GetResource(apiVersion, kind, namespace, name)
if err != nil {
if apierrors.IsNotFound(err) {
@ -542,7 +542,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, policy 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.Interface) (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

@ -26,7 +26,7 @@ import (
var ErrEmptyPatch error = fmt.Errorf("empty resource to patch")
type MutateExistingController struct {
client *dclient.Client
client dclient.Interface
// typed client for Kyverno CRDs
kyvernoClient kyvernoclient.Interface
@ -54,7 +54,7 @@ type MutateExistingController struct {
// NewMutateExistingController returns an instance of the MutateExistingController
func NewMutateExistingController(
kyvernoClient kyvernoclient.Interface,
client *dclient.Client,
client dclient.Interface,
policyLister kyvernolister.ClusterPolicyLister,
npolicyLister kyvernolister.PolicyLister,
urLister urlister.UpdateRequestNamespaceLister,

View file

@ -35,7 +35,7 @@ const (
// Controller manages the life-cycle for Generate-Requests and applies generate rule
type Controller struct {
// dynamic client implementation
client *dclient.Client
client dclient.Interface
// typed client for Kyverno CRDs
kyvernoClient kyvernoclient.Interface
@ -72,7 +72,7 @@ type Controller struct {
func NewController(
kubeClient kubernetes.Interface,
kyvernoClient kyvernoclient.Interface,
client *dclient.Client,
client dclient.Interface,
policyInformer kyvernoinformer.ClusterPolicyInformer,
npolicyInformer kyvernoinformer.PolicyInformer,
urInformer urkyvernoinformer.UpdateRequestInformer,

View file

@ -127,7 +127,7 @@ func RetryFunc(retryInterval, timeout time.Duration, run func() error, msg strin
}
}
func ProcessDeletePolicyForCloneGenerateRule(rules []kyverno.Rule, client *dclient.Client, pName string, logger logr.Logger) bool {
func ProcessDeletePolicyForCloneGenerateRule(rules []kyverno.Rule, client dclient.Interface, pName string, logger logr.Logger) bool {
generatePolicyWithClone := false
for _, rule := range rules {
if rule.Generation.Clone.Name == "" {
@ -155,7 +155,7 @@ func ProcessDeletePolicyForCloneGenerateRule(rules []kyverno.Rule, client *dclie
return generatePolicyWithClone
}
func updateSourceResource(pName string, rule kyverno.Rule, client *dclient.Client, log logr.Logger) error {
func updateSourceResource(pName string, rule kyverno.Rule, client dclient.Interface, log logr.Logger) error {
obj, err := client.GetResource("", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)
if err != nil {
return errors.Wrapf(err, "source resource %s/%s/%s not found", rule.Generation.Kind, rule.Generation.Clone.Namespace, rule.Generation.Clone.Name)

View file

@ -2,40 +2,65 @@ package client
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/go-logr/logr"
openapiv2 "github.com/googleapis/gnostic/openapiv2"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
patchTypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/discovery/cached/memory"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/dynamic/dynamicinformer"
"k8s.io/client-go/kubernetes"
csrtype "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
event "k8s.io/client-go/kubernetes/typed/core/v1"
certsv1beta1 "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
)
//Client enables interaction with k8 resource
type Client struct {
type Interface interface {
// NewDynamicSharedInformerFactory returns a new instance of DynamicSharedInformerFactory
NewDynamicSharedInformerFactory(time.Duration) dynamicinformer.DynamicSharedInformerFactory
// GetEventsInterface provides typed interface for events
GetEventsInterface() (corev1.EventInterface, error)
// GetCSRInterface provides type interface for CSR
GetCSRInterface() (certsv1beta1.CertificateSigningRequestInterface, error)
// GetDynamicInterface fetches underlying dynamic interface
GetDynamicInterface() dynamic.Interface
// Discovery return the discovery client implementation
Discovery() IDiscovery
// SetDiscovery sets the discovery client implementation
SetDiscovery(discoveryClient IDiscovery)
// GetResource returns the resource in unstructured/json format
GetResource(apiVersion string, kind string, namespace string, name string, subresources ...string) (*unstructured.Unstructured, error)
// PatchResource patches the resource
PatchResource(apiVersion string, kind string, namespace string, name string, patch []byte) (*unstructured.Unstructured, error)
// ListResource returns the list of resources in unstructured/json format
// Access items using []Items
ListResource(apiVersion string, kind string, namespace string, lselector *metav1.LabelSelector) (*unstructured.UnstructuredList, error)
// DeleteResource deletes the specified resource
DeleteResource(apiVersion string, kind string, namespace string, name string, dryRun bool) error
// CreateResource creates object for the specified resource/namespace
CreateResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error)
// UpdateResource updates object for the specified resource/namespace
UpdateResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error)
// UpdateStatusResource updates the resource "status" subresource
UpdateStatusResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error)
}
// Client enables interaction with k8 resource
type client struct {
client dynamic.Interface
discoveryClient IDiscovery
log logr.Logger
clientConfig *rest.Config
kclient kubernetes.Interface
DiscoveryClient IDiscovery
}
//NewClient creates new instance of client
func NewClient(config *rest.Config, resync time.Duration, stopCh <-chan struct{}, log logr.Logger) (*Client, error) {
// NewClient creates new instance of client
func NewClient(config *rest.Config, resync time.Duration, stopCh <-chan struct{}, log logr.Logger) (Interface, error) {
dclient, err := dynamic.NewForConfig(config)
if err != nil {
return nil, err
@ -44,50 +69,47 @@ func NewClient(config *rest.Config, resync time.Duration, stopCh <-chan struct{}
if err != nil {
return nil, err
}
client := Client{
client := client{
client: dclient,
clientConfig: config,
kclient: kclient,
log: log.WithName("dclient"),
}
// Set discovery client
discoveryClient := &ServerPreferredResources{
discoveryClient := &serverPreferredResources{
cachedClient: memory.NewMemCacheClient(kclient.Discovery()),
log: client.log,
}
// client will invalidate registered resources cache every x seconds,
// As there is no way to identify if the registered resource is available or not
// we will be invalidating the local cache, so the next request get a fresh cache
// If a resource is removed then and cache is not invalidate yet, we will not detect the removal
// but the re-sync shall re-evaluate
go discoveryClient.Poll(resync, stopCh)
client.SetDiscovery(discoveryClient)
return &client, nil
}
//NewDynamicSharedInformerFactory returns a new instance of DynamicSharedInformerFactory
func (c *Client) NewDynamicSharedInformerFactory(defaultResync time.Duration) dynamicinformer.DynamicSharedInformerFactory {
// NewDynamicSharedInformerFactory returns a new instance of DynamicSharedInformerFactory
func (c *client) NewDynamicSharedInformerFactory(defaultResync time.Duration) dynamicinformer.DynamicSharedInformerFactory {
return dynamicinformer.NewDynamicSharedInformerFactory(c.client, defaultResync)
}
//GetEventsInterface provides typed interface for events
func (c *Client) GetEventsInterface() (event.EventInterface, error) {
// GetEventsInterface provides typed interface for events
func (c *client) GetEventsInterface() (corev1.EventInterface, error) {
return c.kclient.CoreV1().Events(""), nil
}
//GetCSRInterface provides type interface for CSR
func (c *Client) GetCSRInterface() (csrtype.CertificateSigningRequestInterface, error) {
// GetCSRInterface provides type interface for CSR
func (c *client) GetCSRInterface() (certsv1beta1.CertificateSigningRequestInterface, error) {
return c.kclient.CertificatesV1beta1().CertificateSigningRequests(), nil
}
func (c *Client) getInterface(apiVersion string, kind string) dynamic.NamespaceableResourceInterface {
func (c *client) getInterface(apiVersion string, kind string) dynamic.NamespaceableResourceInterface {
return c.client.Resource(c.getGroupVersionMapper(apiVersion, kind))
}
func (c *Client) getResourceInterface(apiVersion string, 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(apiVersion, kind)
// Get the namespacable interface
@ -101,271 +123,96 @@ func (c *Client) getResourceInterface(apiVersion string, kind string, namespace
}
// Keep this a stateful as the resource list will be based on the kubernetes version we connect to
func (c *Client) getGroupVersionMapper(apiVersion string, kind string) schema.GroupVersionResource {
func (c *client) getGroupVersionMapper(apiVersion string, kind string) schema.GroupVersionResource {
if apiVersion == "" {
gvr, _ := c.DiscoveryClient.GetGVRFromKind(kind)
gvr, _ := c.discoveryClient.GetGVRFromKind(kind)
return gvr
}
return c.DiscoveryClient.GetGVRFromAPIVersionKind(apiVersion, kind)
return c.discoveryClient.GetGVRFromAPIVersionKind(apiVersion, kind)
}
// GetResource returns the resource in unstructured/json format
func (c *Client) GetResource(apiVersion string, kind string, namespace string, name string, subresources ...string) (*unstructured.Unstructured, error) {
return c.getResourceInterface(apiVersion, kind, namespace).Get(context.TODO(), 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(context.TODO(), name, metav1.GetOptions{}, subresources...)
}
//PatchResource patches the resource
func (c *Client) PatchResource(apiVersion string, kind string, namespace string, name string, patch []byte) (*unstructured.Unstructured, error) {
return c.getResourceInterface(apiVersion, kind, namespace).Patch(context.TODO(), name, patchTypes.JSONPatchType, patch, meta.PatchOptions{})
// PatchResource patches the resource
func (c *client) PatchResource(apiVersion string, kind string, namespace string, name string, patch []byte) (*unstructured.Unstructured, error) {
return c.getResourceInterface(apiVersion, kind, namespace).Patch(context.TODO(), name, types.JSONPatchType, patch, metav1.PatchOptions{})
}
// GetDynamicInterface fetches underlying dynamic interface
func (c *Client) GetDynamicInterface() dynamic.Interface {
func (c *client) GetDynamicInterface() dynamic.Interface {
return c.client
}
// ListResource returns the list of resources in unstructured/json format
// Access items using []Items
func (c *Client) ListResource(apiVersion string, kind string, namespace string, lselector *meta.LabelSelector) (*unstructured.UnstructuredList, error) {
options := meta.ListOptions{}
func (c *client) ListResource(apiVersion string, kind string, namespace string, lselector *metav1.LabelSelector) (*unstructured.UnstructuredList, error) {
options := metav1.ListOptions{}
if lselector != nil {
options = meta.ListOptions{LabelSelector: meta.FormatLabelSelector(lselector)}
options = metav1.ListOptions{LabelSelector: metav1.FormatLabelSelector(lselector)}
}
return c.getResourceInterface(apiVersion, kind, namespace).List(context.TODO(), options)
}
// DeleteResource deletes the specified resource
func (c *Client) DeleteResource(apiVersion string, kind string, namespace string, name string, dryRun bool) error {
options := meta.DeleteOptions{}
func (c *client) DeleteResource(apiVersion string, kind string, namespace string, name string, dryRun bool) error {
options := metav1.DeleteOptions{}
if dryRun {
options = meta.DeleteOptions{DryRun: []string{meta.DryRunAll}}
options = metav1.DeleteOptions{DryRun: []string{metav1.DryRunAll}}
}
return c.getResourceInterface(apiVersion, kind, namespace).Delete(context.TODO(), name, options)
}
// CreateResource creates object for the specified resource/namespace
func (c *Client) CreateResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
options := meta.CreateOptions{}
func (c *client) CreateResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
options := metav1.CreateOptions{}
if dryRun {
options = meta.CreateOptions{DryRun: []string{meta.DryRunAll}}
options = metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}}
}
// convert typed to unstructured obj
if unstructuredObj := convertToUnstructured(obj); unstructuredObj != nil {
if unstructuredObj, err := kubeutils.ConvertToUnstructured(obj); err == nil && unstructuredObj != nil {
return c.getResourceInterface(apiVersion, kind, namespace).Create(context.TODO(), unstructuredObj, options)
}
return nil, fmt.Errorf("unable to create resource ")
}
// UpdateResource updates object for the specified resource/namespace
func (c *Client) UpdateResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
options := meta.UpdateOptions{}
func (c *client) UpdateResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
options := metav1.UpdateOptions{}
if dryRun {
options = meta.UpdateOptions{DryRun: []string{meta.DryRunAll}}
options = metav1.UpdateOptions{DryRun: []string{metav1.DryRunAll}}
}
// convert typed to unstructured obj
if unstructuredObj := convertToUnstructured(obj); unstructuredObj != nil {
if unstructuredObj, err := kubeutils.ConvertToUnstructured(obj); err == nil && unstructuredObj != nil {
return c.getResourceInterface(apiVersion, kind, namespace).Update(context.TODO(), unstructuredObj, options)
}
return nil, fmt.Errorf("unable to update resource ")
}
// UpdateStatusResource updates the resource "status" subresource
func (c *Client) UpdateStatusResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
options := meta.UpdateOptions{}
func (c *client) UpdateStatusResource(apiVersion string, kind string, namespace string, obj interface{}, dryRun bool) (*unstructured.Unstructured, error) {
options := metav1.UpdateOptions{}
if dryRun {
options = meta.UpdateOptions{DryRun: []string{meta.DryRunAll}}
options = metav1.UpdateOptions{DryRun: []string{metav1.DryRunAll}}
}
// convert typed to unstructured obj
if unstructuredObj := convertToUnstructured(obj); unstructuredObj != nil {
if unstructuredObj, err := kubeutils.ConvertToUnstructured(obj); err == nil && unstructuredObj != nil {
return c.getResourceInterface(apiVersion, kind, namespace).UpdateStatus(context.TODO(), unstructuredObj, options)
}
return nil, fmt.Errorf("unable to update resource ")
}
func convertToUnstructured(obj interface{}) *unstructured.Unstructured {
unstrObj := map[string]interface{}{}
raw, err := json.Marshal(obj)
if err != nil {
return nil
}
err = json.Unmarshal(raw, &unstrObj)
if err != nil {
return nil
}
return &unstructured.Unstructured{Object: unstrObj}
}
//IDiscovery provides interface to mange Kind and GVR mapping
type IDiscovery interface {
FindResource(apiVersion string, kind string) (*meta.APIResource, schema.GroupVersionResource, error)
GetGVRFromKind(kind string) (schema.GroupVersionResource, error)
GetGVRFromAPIVersionKind(apiVersion string, kind string) schema.GroupVersionResource
GetServerVersion() (*version.Info, error)
OpenAPISchema() (*openapiv2.Document, error)
DiscoveryCache() discovery.CachedDiscoveryInterface
// Discovery return the discovery client implementation
func (c *client) Discovery() IDiscovery {
return c.discoveryClient
}
// SetDiscovery sets the discovery client implementation
func (c *Client) SetDiscovery(discoveryClient IDiscovery) {
c.DiscoveryClient = discoveryClient
}
//ServerPreferredResources stores the cachedClient instance for discovery client
type ServerPreferredResources struct {
cachedClient discovery.CachedDiscoveryInterface
log logr.Logger
}
// DiscoveryCache gets the discovery client cache
func (c ServerPreferredResources) DiscoveryCache() discovery.CachedDiscoveryInterface {
return c.cachedClient
}
//Poll will keep invalidate the local cache
func (c ServerPreferredResources) Poll(resync time.Duration, stopCh <-chan struct{}) {
logger := c.log.WithName("Poll")
// start a ticker
ticker := time.NewTicker(resync)
defer func() { ticker.Stop() }()
logger.V(4).Info("starting registered resources sync", "period", resync)
for {
select {
case <-stopCh:
logger.Info("stopping registered resources sync")
return
case <-ticker.C:
// set cache as stale
logger.V(6).Info("invalidating local client cache for registered resources")
c.cachedClient.Invalidate()
}
}
}
// OpenAPISchema returns the API server OpenAPI schema document
func (c ServerPreferredResources) OpenAPISchema() (*openapiv2.Document, error) {
return c.cachedClient.OpenAPISchema()
}
// GetGVRFromKind get the Group Version Resource from kind
func (c ServerPreferredResources) GetGVRFromKind(kind string) (schema.GroupVersionResource, error) {
if kind == "" {
return schema.GroupVersionResource{}, nil
}
_, gvr, err := c.FindResource("", kind)
if err != nil {
c.log.Info("schema not found", "kind", kind)
return schema.GroupVersionResource{}, err
}
return gvr, nil
}
// 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()
}
// 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(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(apiVersion, kind); err == nil {
return r, gvr, nil
}
}
return nil, schema.GroupVersionResource{}, err
}
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.ServerGroupsAndResources()
}
if err != nil {
if discovery.IsGroupDiscoveryFailedError(err) {
logDiscoveryErrors(err, c)
} else if isMetricsServerUnavailable(kind, err) {
c.log.V(3).Info("failed to find preferred resource version", "error", err.Error())
} else {
c.log.Error(err, "failed to find preferred resource version")
return nil, schema.GroupVersionResource{}, err
}
}
for _, serverResource := range serverResources {
if apiVersion != "" && serverResource.GroupVersion != apiVersion {
continue
}
for _, resource := range serverResource.APIResources {
if strings.Contains(resource.Name, "/") {
// skip the sub-resources like deployment/status
continue
}
// match kind or names (e.g. Namespace, namespaces, namespace)
// to allow matching API paths (e.g. /api/v1/namespaces).
if resource.Kind == kind || resource.Name == kind || resource.SingularName == kind {
gv, err := schema.ParseGroupVersion(serverResource.GroupVersion)
if err != nil {
c.log.Error(err, "failed to parse groupVersion", "groupVersion", serverResource.GroupVersion)
return nil, schema.GroupVersionResource{}, err
}
return &resource, gv.WithResource(resource.Name), nil
}
}
}
return nil, schema.GroupVersionResource{}, fmt.Errorf("kind '%s' not found in apiVersion '%s'", kind, apiVersion)
}
func logDiscoveryErrors(err error, c ServerPreferredResources) {
discoveryError := err.(*discovery.ErrGroupDiscoveryFailed)
for gv, e := range discoveryError.Groups {
if gv.Group == "custom.metrics.k8s.io" || gv.Group == "metrics.k8s.io" || gv.Group == "external.metrics.k8s.io" {
// These errors occur when Prometheus is installed as an external metrics server
// See: https://github.com/kyverno/kyverno/issues/1490
c.log.V(3).Info("failed to retrieve metrics API group", "gv", gv)
continue
}
c.log.Error(e, "failed to retrieve API group", "gv", gv)
}
}
func isMetricsServerUnavailable(kind string, err error) bool {
// error message is defined at:
// https://github.com/kubernetes/apimachinery/blob/2456ebdaba229616fab2161a615148884b46644b/pkg/api/errors/errors.go#L432
return strings.HasPrefix(kind, "metrics.k8s.io/") &&
strings.Contains(err.Error(), "the server is currently unable to handle the request")
func (c *client) SetDiscovery(discoveryClient IDiscovery) {
c.discoveryClient = discoveryClient
}

View file

@ -5,7 +5,8 @@ import (
"testing"
"github.com/kyverno/kyverno/pkg/config"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
@ -23,7 +24,7 @@ import (
type fixture struct {
t *testing.T
objects []runtime.Object
client *Client
client Interface
}
func newFixture(t *testing.T) *fixture {
@ -43,12 +44,12 @@ func newFixture(t *testing.T) *fixture {
}
objects := []runtime.Object{
newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"),
newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"),
newUnstructured("group2/version", "TheKind", "ns-foo", "name2-baz"),
newUnstructured("apps/v1", "Deployment", config.KyvernoNamespace, config.KyvernoDeploymentName),
kubeutils.NewUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
kubeutils.NewUnstructured("group2/version", "TheKind", "ns-foo", "name2-foo"),
kubeutils.NewUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
kubeutils.NewUnstructured("group/version", "TheKind", "ns-foo", "name-baz"),
kubeutils.NewUnstructured("group2/version", "TheKind", "ns-foo", "name2-baz"),
kubeutils.NewUnstructured("apps/v1", "Deployment", config.KyvernoNamespace, config.KyvernoDeploymentName),
}
scheme := runtime.NewScheme()
@ -88,17 +89,17 @@ func TestCRUDResource(t *testing.T) {
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", kubeutils.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", kubeutils.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", kubeutils.NewUnstructuredWithSpec("group/version", "TheKind", "ns-foo", "name-foo1", map[string]interface{}{"foo": "status"}), false)
if err != nil {
t.Errorf("UpdateStatusResource not working: %s", err)
}
@ -110,7 +111,7 @@ func TestEventInterface(t *testing.T) {
if err != nil {
t.Errorf("GetEventsInterface not working: %s", err)
}
_, err = iEvent.List(context.TODO(), meta.ListOptions{})
_, err = iEvent.List(context.TODO(), metav1.ListOptions{})
if err != nil {
t.Errorf("Testing Event interface not working: %s", err)
}
@ -121,7 +122,7 @@ func TestCSRInterface(t *testing.T) {
if err != nil {
t.Errorf("GetCSRInterface not working: %s", err)
}
_, err = iCSR.List(context.TODO(), meta.ListOptions{})
_, err = iCSR.List(context.TODO(), metav1.ListOptions{})
if err != nil {
t.Errorf("Testing CSR interface not working: %s", err)
}

157
pkg/dclient/discovery.go Normal file
View file

@ -0,0 +1,157 @@
package client
import (
"fmt"
"strings"
"time"
"github.com/go-logr/logr"
openapiv2 "github.com/googleapis/gnostic/openapiv2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
)
// IDiscovery provides interface to mange Kind and GVR mapping
type IDiscovery interface {
FindResource(apiVersion string, kind string) (*metav1.APIResource, schema.GroupVersionResource, error)
GetGVRFromKind(kind string) (schema.GroupVersionResource, error)
GetGVRFromAPIVersionKind(apiVersion string, kind string) schema.GroupVersionResource
GetServerVersion() (*version.Info, error)
OpenAPISchema() (*openapiv2.Document, error)
DiscoveryCache() discovery.CachedDiscoveryInterface
}
// serverPreferredResources stores the cachedClient instance for discovery client
type serverPreferredResources struct {
cachedClient discovery.CachedDiscoveryInterface
log logr.Logger
}
// DiscoveryCache gets the discovery client cache
func (c serverPreferredResources) DiscoveryCache() discovery.CachedDiscoveryInterface {
return c.cachedClient
}
// Poll will keep invalidate the local cache
func (c serverPreferredResources) Poll(resync time.Duration, stopCh <-chan struct{}) {
logger := c.log.WithName("Poll")
// start a ticker
ticker := time.NewTicker(resync)
defer func() { ticker.Stop() }()
logger.V(4).Info("starting registered resources sync", "period", resync)
for {
select {
case <-stopCh:
logger.Info("stopping registered resources sync")
return
case <-ticker.C:
// set cache as stale
logger.V(6).Info("invalidating local client cache for registered resources")
c.cachedClient.Invalidate()
}
}
}
// OpenAPISchema returns the API server OpenAPI schema document
func (c serverPreferredResources) OpenAPISchema() (*openapiv2.Document, error) {
return c.cachedClient.OpenAPISchema()
}
// GetGVRFromKind get the Group Version Resource from kind
func (c serverPreferredResources) GetGVRFromKind(kind string) (schema.GroupVersionResource, error) {
if kind == "" {
return schema.GroupVersionResource{}, nil
}
_, gvr, err := c.FindResource("", kind)
if err != nil {
c.log.Info("schema not found", "kind", kind)
return schema.GroupVersionResource{}, err
}
return gvr, nil
}
// 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()
}
// 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(apiVersion string, kind string) (*metav1.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(apiVersion, kind); err == nil {
return r, gvr, nil
}
}
return nil, schema.GroupVersionResource{}, err
}
func (c serverPreferredResources) findResource(apiVersion string, kind string) (*metav1.APIResource, schema.GroupVersionResource, error) {
var serverResources []*metav1.APIResourceList
var err error
if apiVersion == "" {
serverResources, err = c.cachedClient.ServerPreferredResources()
} else {
_, serverResources, err = c.cachedClient.ServerGroupsAndResources()
}
if err != nil {
if discovery.IsGroupDiscoveryFailedError(err) {
logDiscoveryErrors(err, c)
} else if isMetricsServerUnavailable(kind, err) {
c.log.V(3).Info("failed to find preferred resource version", "error", err.Error())
} else {
c.log.Error(err, "failed to find preferred resource version")
return nil, schema.GroupVersionResource{}, err
}
}
for _, serverResource := range serverResources {
if apiVersion != "" && serverResource.GroupVersion != apiVersion {
continue
}
for _, resource := range serverResource.APIResources {
if strings.Contains(resource.Name, "/") {
// skip the sub-resources like deployment/status
continue
}
// match kind or names (e.g. Namespace, namespaces, namespace)
// to allow matching API paths (e.g. /api/v1/namespaces).
if resource.Kind == kind || resource.Name == kind || resource.SingularName == kind {
gv, err := schema.ParseGroupVersion(serverResource.GroupVersion)
if err != nil {
c.log.Error(err, "failed to parse groupVersion", "groupVersion", serverResource.GroupVersion)
return nil, schema.GroupVersionResource{}, err
}
return &resource, gv.WithResource(resource.Name), nil
}
}
}
return nil, schema.GroupVersionResource{}, fmt.Errorf("kind '%s' not found in apiVersion '%s'", kind, apiVersion)
}

69
pkg/dclient/fake.go Normal file
View file

@ -0,0 +1,69 @@
package client
import (
"fmt"
"strings"
openapiv2 "github.com/googleapis/gnostic/openapiv2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
)
// NewFakeDiscoveryClient returns a fakediscovery client
func NewFakeDiscoveryClient(registeredResources []schema.GroupVersionResource) *fakeDiscoveryClient {
// Load some-preregistered resources
res := []schema.GroupVersionResource{
{Version: "v1", Resource: "configmaps"},
{Version: "v1", Resource: "endpoints"},
{Version: "v1", Resource: "namespaces"},
{Version: "v1", Resource: "resourcequotas"},
{Version: "v1", Resource: "secrets"},
{Version: "v1", Resource: "serviceaccounts"},
{Group: "apps", Version: "v1", Resource: "daemonsets"},
{Group: "apps", Version: "v1", Resource: "deployments"},
{Group: "apps", Version: "v1", Resource: "statefulsets"},
}
registeredResources = append(registeredResources, res...)
return &fakeDiscoveryClient{registeredResources: registeredResources}
}
type fakeDiscoveryClient struct {
registeredResources []schema.GroupVersionResource
}
func (c *fakeDiscoveryClient) getGVR(resource string) schema.GroupVersionResource {
for _, gvr := range c.registeredResources {
if gvr.Resource == resource {
return gvr
}
}
return schema.GroupVersionResource{}
}
func (c *fakeDiscoveryClient) GetServerVersion() (*version.Info, error) {
return nil, nil
}
func (c *fakeDiscoveryClient) GetGVRFromKind(kind string) (schema.GroupVersionResource, error) {
resource := strings.ToLower(kind) + "s"
return c.getGVR(resource), nil
}
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) (*metav1.APIResource, schema.GroupVersionResource, error) {
return nil, schema.GroupVersionResource{}, fmt.Errorf("not implemented")
}
func (c *fakeDiscoveryClient) OpenAPISchema() (*openapiv2.Document, error) {
return nil, nil
}
func (c *fakeDiscoveryClient) DiscoveryCache() discovery.CachedDiscoveryInterface {
return nil
}

19
pkg/dclient/mock.go Normal file
View file

@ -0,0 +1,19 @@
package client
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic/fake"
kubefake "k8s.io/client-go/kubernetes/fake"
)
//NewMockClient ---testing utilities
func NewMockClient(scheme *runtime.Scheme, gvrToListKind map[schema.GroupVersionResource]string, objects ...runtime.Object) (Interface, error) {
c := fake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind, objects...)
// the typed and dynamic client are initialized with similar resources
kclient := kubefake.NewSimpleClientset(objects...)
return &client{
client: c,
kclient: kclient,
}, nil
}

View file

@ -1,104 +1,28 @@
package client
import (
"fmt"
"strings"
openapiv2 "github.com/googleapis/gnostic/openapiv2"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic/fake"
kubernetesfake "k8s.io/client-go/kubernetes/fake"
)
//NewMockClient ---testing utilities
func NewMockClient(scheme *runtime.Scheme, gvrToListKind map[schema.GroupVersionResource]string, objects ...runtime.Object) (*Client, error) {
client := fake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind, objects...)
// the typed and dynamic client are initialized with similar resources
kclient := kubernetesfake.NewSimpleClientset(objects...)
return &Client{
client: client,
kclient: kclient,
}, nil
}
// NewFakeDiscoveryClient returns a fakediscovery client
func NewFakeDiscoveryClient(registeredResources []schema.GroupVersionResource) *fakeDiscoveryClient {
// Load some-preregistered resources
res := []schema.GroupVersionResource{
{Version: "v1", Resource: "configmaps"},
{Version: "v1", Resource: "endpoints"},
{Version: "v1", Resource: "namespaces"},
{Version: "v1", Resource: "resourcequotas"},
{Version: "v1", Resource: "secrets"},
{Version: "v1", Resource: "serviceaccounts"},
{Group: "apps", Version: "v1", Resource: "daemonsets"},
{Group: "apps", Version: "v1", Resource: "deployments"},
{Group: "apps", Version: "v1", Resource: "statefulsets"},
}
registeredResources = append(registeredResources, res...)
return &fakeDiscoveryClient{registeredResources: registeredResources}
}
type fakeDiscoveryClient struct {
registeredResources []schema.GroupVersionResource
}
func (c *fakeDiscoveryClient) getGVR(resource string) schema.GroupVersionResource {
for _, gvr := range c.registeredResources {
if gvr.Resource == resource {
return gvr
func logDiscoveryErrors(err error, c serverPreferredResources) {
discoveryError := err.(*discovery.ErrGroupDiscoveryFailed)
for gv, e := range discoveryError.Groups {
if gv.Group == "custom.metrics.k8s.io" || gv.Group == "metrics.k8s.io" || gv.Group == "external.metrics.k8s.io" {
// These errors occur when Prometheus is installed as an external metrics server
// See: https://github.com/kyverno/kyverno/issues/1490
c.log.V(3).Info("failed to retrieve metrics API group", "gv", gv)
continue
}
}
return schema.GroupVersionResource{}
}
func (c *fakeDiscoveryClient) GetServerVersion() (*version.Info, error) {
return nil, nil
}
func (c *fakeDiscoveryClient) GetGVRFromKind(kind string) (schema.GroupVersionResource, error) {
resource := strings.ToLower(kind) + "s"
return c.getGVR(resource), nil
}
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")
}
func (c *fakeDiscoveryClient) OpenAPISchema() (*openapiv2.Document, error) {
return nil, nil
}
func (c *fakeDiscoveryClient) DiscoveryCache() discovery.CachedDiscoveryInterface {
return nil
}
func newUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiVersion,
"kind": kind,
"metadata": map[string]interface{}{
"namespace": namespace,
"name": name,
},
},
c.log.Error(e, "failed to retrieve API group", "gv", gv)
}
}
func newUnstructuredWithSpec(apiVersion, kind, namespace, name string, spec map[string]interface{}) *unstructured.Unstructured {
u := newUnstructured(apiVersion, kind, namespace, name)
u.Object["spec"] = spec
return u
func isMetricsServerUnavailable(kind string, err error) bool {
// error message is defined at:
// https://github.com/kubernetes/apimachinery/blob/2456ebdaba229616fab2161a615148884b46644b/pkg/api/errors/errors.go#L432
return strings.HasPrefix(kind, "metrics.k8s.io/") &&
strings.Contains(err.Error(), "the server is currently unable to handle the request")
}

View file

@ -26,7 +26,7 @@ type PolicyContext struct {
AdmissionInfo urkyverno.RequestInfo
// Dynamic client - used for api lookups
Client *client.Client
Client client.Interface
// Config handler
ExcludeGroupRole []string

View file

@ -21,7 +21,7 @@ import (
//Generator generate events
type Generator struct {
client *client.Client
client client.Interface
// list/get cluster policy
cpLister kyvernolister.ClusterPolicyLister
// list/get policy
@ -46,7 +46,7 @@ type Interface interface {
}
//NewEventGenerator to generate a new event controller
func NewEventGenerator(client *client.Client, cpInformer kyvernoinformer.ClusterPolicyInformer, pInformer kyvernoinformer.PolicyInformer, log logr.Logger) *Generator {
func NewEventGenerator(client client.Interface, cpInformer kyvernoinformer.ClusterPolicyInformer, pInformer kyvernoinformer.PolicyInformer, log logr.Logger) *Generator {
gen := Generator{
client: client,
cpLister: cpInformer.Lister(),
@ -65,7 +65,7 @@ func rateLimiter() workqueue.RateLimiter {
return workqueue.DefaultItemBasedRateLimiter()
}
func initRecorder(client *client.Client, eventSource Source, log logr.Logger) record.EventRecorder {
func initRecorder(client client.Interface, eventSource Source, log logr.Logger) record.EventRecorder {
// Initialize Event Broadcaster
err := scheme.AddToScheme(scheme.Scheme)
if err != nil {

View file

@ -21,7 +21,7 @@ import (
)
type crdSync struct {
client *client.Client
client client.Interface
controller *Controller
}
@ -53,7 +53,7 @@ var crdDefinitionNew struct {
}
// NewCRDSync ...
func NewCRDSync(client *client.Client, controller *Controller) *crdSync {
func NewCRDSync(client client.Interface, controller *Controller) *crdSync {
if controller == nil {
panic(fmt.Errorf("nil controller sent into crd sync"))
}
@ -69,7 +69,7 @@ func (c *crdSync) Run(workers int, stopCh <-chan struct{}) {
log.Log.Error(err, "failed to update in-cluster api versions")
}
newDoc, err := c.client.DiscoveryClient.DiscoveryCache().OpenAPISchema()
newDoc, err := c.client.Discovery().DiscoveryCache().OpenAPISchema()
if err != nil {
log.Log.Error(err, "cannot get OpenAPI schema")
}
@ -87,7 +87,7 @@ func (c *crdSync) Run(workers int, stopCh <-chan struct{}) {
}
func (c *crdSync) sync() {
c.client.DiscoveryClient.DiscoveryCache().Invalidate()
c.client.Discovery().DiscoveryCache().Invalidate()
crds, err := c.client.GetDynamicInterface().Resource(runtimeSchema.GroupVersionResource{
Group: "apiextensions.k8s.io",
Version: "v1",
@ -108,7 +108,7 @@ func (c *crdSync) sync() {
log.Log.Error(err, "sync failed, unable to update in-cluster api versions")
}
newDoc, err := c.client.DiscoveryClient.DiscoveryCache().OpenAPISchema()
newDoc, err := c.client.Discovery().DiscoveryCache().OpenAPISchema()
if err != nil {
log.Log.Error(err, "cannot get OpenAPI schema")
}
@ -120,12 +120,12 @@ func (c *crdSync) sync() {
}
func (c *crdSync) updateInClusterKindToAPIVersions() error {
_, apiResourceLists, err := c.client.DiscoveryClient.DiscoveryCache().ServerGroupsAndResources()
_, apiResourceLists, err := c.client.Discovery().DiscoveryCache().ServerGroupsAndResources()
if err != nil {
return errors.Wrapf(err, "fetching API server groups and resources")
}
preferredAPIResourcesLists, err := c.client.DiscoveryClient.DiscoveryCache().ServerPreferredResources()
preferredAPIResourcesLists, err := c.client.Discovery().DiscoveryCache().ServerPreferredResources()
if err != nil {
return errors.Wrapf(err, "fetching API server preferreds resources")
}

View file

@ -22,7 +22,7 @@ type Validation interface {
// - Mutate
// - Validation
// - Generate
func validateActions(idx int, rule *kyverno.Rule, client *dclient.Client, mock bool) error {
func validateActions(idx int, rule *kyverno.Rule, client dclient.Interface, mock bool) error {
if rule == nil {
return nil
}

View file

@ -20,7 +20,7 @@ import (
// applyPolicy applies policy on a resource
func applyPolicy(policy kyverno.PolicyInterface, resource unstructured.Unstructured,
logger logr.Logger, excludeGroupRole []string,
client *client.Client, namespaceLabels map[string]string) (responses []*response.EngineResponse) {
client client.Interface, namespaceLabels map[string]string) (responses []*response.EngineResponse) {
startTime := time.Now()
defer func() {

View file

@ -209,7 +209,7 @@ func (pc *PolicyController) processExistingKinds(kinds []string, policy kyverno.
if err != nil {
gv, k := kubeutils.GetKindFromGVK(kind)
if !strings.Contains(k, "*") {
resourceSchema, _, err := pc.client.DiscoveryClient.FindResource(gv, k)
resourceSchema, _, err := pc.client.Discovery().FindResource(gv, k)
if err != nil {
logger.Error(err, "failed to find resource", "kind", k)
continue

View file

@ -20,12 +20,12 @@ type Operations interface {
//Auth provides implementation to check if caller/self/kyverno has access to perofrm operations
type Auth struct {
client *dclient.Client
client dclient.Interface
log logr.Logger
}
//NewAuth returns a new instance of Auth for operations
func NewAuth(client *dclient.Client, log logr.Logger) *Auth {
func NewAuth(client dclient.Interface, log logr.Logger) *Auth {
a := Auth{
client: client,
log: log,

View file

@ -24,7 +24,7 @@ type Generate struct {
}
//NewGenerateFactory returns a new instance of Generate validation checker
func NewGenerateFactory(client *dclient.Client, rule kyverno.Generation, log logr.Logger) *Generate {
func NewGenerateFactory(client dclient.Interface, rule kyverno.Generation, log logr.Logger) *Generate {
g := Generate{
rule: rule,
authCheck: NewAuth(client, log),

View file

@ -53,7 +53,7 @@ const (
// PolicyController is responsible for synchronizing Policy objects stored
// in the system with the corresponding policy violations
type PolicyController struct {
client *client.Client
client client.Interface
kyvernoClient kyvernoclient.Interface
pInformer kyvernoinformer.ClusterPolicyInformer
npInformer kyvernoinformer.PolicyInformer
@ -98,7 +98,7 @@ type PolicyController struct {
func NewPolicyController(
kubeClient kubernetes.Interface,
kyvernoClient kyvernoclient.Interface,
client *client.Client,
client client.Interface,
pInformer kyvernoinformer.ClusterPolicyInformer,
npInformer kyvernoinformer.PolicyInformer,
urInformer urkyvernoinformer.UpdateRequestInformer,

View file

@ -78,7 +78,7 @@ func validateJSONPatchPathForForwardSlash(patch string) error {
}
// Validate checks the policy and rules declarations for required configurations
func Validate(policy kyverno.PolicyInterface, client *dclient.Client, mock bool, openAPIController *openapi.Controller) (*admissionv1.AdmissionResponse, error) {
func Validate(policy kyverno.PolicyInterface, client dclient.Interface, mock bool, openAPIController *openapi.Controller) (*admissionv1.AdmissionResponse, error) {
namespaced := policy.IsNamespaced()
spec := policy.GetSpec()
background := spec.BackgroundProcessingEnabled()
@ -95,7 +95,7 @@ func Validate(policy kyverno.PolicyInterface, client *dclient.Client, mock bool,
clusterResources := sets.NewString()
if !mock && namespaced {
// Get all the cluster type kind supported by cluster
res, err := client.DiscoveryClient.DiscoveryCache().ServerPreferredResources()
res, err := client.Discovery().DiscoveryCache().ServerPreferredResources()
if err != nil {
return nil, err
}
@ -1017,7 +1017,7 @@ func podControllerAutoGenExclusion(policy kyverno.PolicyInterface) bool {
// validateKinds verifies if an API resource that matches 'kind' is valid kind
// and found in the cache, returns error if not found
func validateKinds(kinds []string, mock bool, client *dclient.Client, p kyverno.PolicyInterface) error {
func validateKinds(kinds []string, mock bool, client dclient.Interface, p kyverno.PolicyInterface) error {
for _, kind := range kinds {
gv, k := kubeutils.GetKindFromGVK(kind)
if k == p.GetKind() {
@ -1025,7 +1025,7 @@ func validateKinds(kinds []string, mock bool, client *dclient.Client, p kyverno.
}
if !mock && !kubeutils.SkipSubResources(k) && !strings.Contains(kind, "*") {
_, _, err := client.DiscoveryClient.FindResource(gv, k)
_, _, err := client.Discovery().FindResource(gv, k)
if err != nil {
return fmt.Errorf("unable to convert GVK to GVR, %s, err: %s", kinds, err)
}

View file

@ -51,7 +51,7 @@ var LabelSelector = &metav1.LabelSelector{
// ReportGenerator creates policy report
type ReportGenerator struct {
pclient kyvernoclient.Interface
dclient *dclient.Client
dclient dclient.Interface
clusterReportInformer policyreportinformer.ClusterPolicyReportInformer
reportInformer policyreportinformer.PolicyReportInformer
@ -76,7 +76,7 @@ type ReportGenerator struct {
// NewReportGenerator returns a new instance of policy report generator
func NewReportGenerator(
pclient kyvernoclient.Interface,
dclient *dclient.Client,
dclient dclient.Interface,
clusterReportInformer policyreportinformer.ClusterPolicyReportInformer,
reportInformer policyreportinformer.PolicyReportInformer,
reportReqInformer requestinformer.ReportChangeRequestInformer,

View file

@ -27,7 +27,7 @@ const workQueueRetryLimit = 10
// Generator creates report request
type Generator struct {
dclient *dclient.Client
dclient dclient.Interface
reportChangeRequestLister requestlister.ReportChangeRequestLister
@ -49,7 +49,7 @@ type Generator struct {
// NewReportChangeRequestGenerator returns a new instance of report request generator
func NewReportChangeRequestGenerator(client policyreportclient.Interface,
dclient *dclient.Client,
dclient dclient.Interface,
reportReqInformer requestinformer.ReportChangeRequestInformer,
clusterReportReqInformer requestinformer.ClusterReportChangeRequestInformer,
cpolInformer kyvernoinformer.ClusterPolicyInformer,

View file

@ -201,11 +201,11 @@ func runTestCase(t *testing.T, tc TestCase) bool {
return true
}
func createNamespace(client *client.Client, ns *unstructured.Unstructured) error {
func createNamespace(client client.Interface, ns *unstructured.Unstructured) error {
_, err := client.CreateResource("", "Namespace", "", ns, false)
return err
}
func validateGeneratedResources(t *testing.T, client *client.Client, policy kyverno.ClusterPolicy, namespace string, expected []kyverno.ResourceSpec) {
func validateGeneratedResources(t *testing.T, client client.Interface, policy kyverno.ClusterPolicy, namespace string, expected []kyverno.ResourceSpec) {
t.Log("--validate if resources are generated---")
// list of expected generated resources
for _, resource := range expected {
@ -347,13 +347,10 @@ func loadPolicyResource(t *testing.T, file string) *unstructured.Unstructured {
return resources[0]
}
func getClient(t *testing.T, files []string) *client.Client {
func getClient(t *testing.T, files []string) client.Interface {
var objects []k8sRuntime.Object
if files != nil {
for _, file := range files {
objects = loadObjects(t, file)
}
for _, file := range files {
objects = loadObjects(t, file)
}
// create mock client
scheme := k8sRuntime.NewScheme()

View file

@ -0,0 +1,39 @@
package kube
import (
"encoding/json"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func ConvertToUnstructured(obj interface{}) (*unstructured.Unstructured, error) {
raw, err := json.Marshal(obj)
if err != nil {
return nil, err
}
unstrObj := map[string]interface{}{}
err = json.Unmarshal(raw, &unstrObj)
if err != nil {
return nil, err
}
return &unstructured.Unstructured{Object: unstrObj}, nil
}
func NewUnstructured(apiVersion, kind, namespace, name string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": apiVersion,
"kind": kind,
"metadata": map[string]interface{}{
"namespace": namespace,
"name": name,
},
},
}
}
func NewUnstructuredWithSpec(apiVersion, kind, namespace, name string, spec map[string]interface{}) *unstructured.Unstructured {
u := NewUnstructured(apiVersion, kind, namespace, name)
u.Object["spec"] = spec
return u
}

View file

@ -39,7 +39,7 @@ var DefaultWebhookTimeout int64 = 10
// webhookConfigManager manges the webhook configuration dynamically
// it is NOT multi-thread safe
type webhookConfigManager struct {
client *client.Client
client client.Interface
kyvernoClient kyvernoclient.Interface
pInformer kyvernoinformer.ClusterPolicyInformer
@ -78,7 +78,7 @@ type manage interface {
}
func newWebhookConfigManager(
client *client.Client,
client client.Interface,
kyvernoClient kyvernoclient.Interface,
pInformer kyvernoinformer.ClusterPolicyInformer,
npInformer kyvernoinformer.PolicyInformer,
@ -783,7 +783,7 @@ func (m *webhookConfigManager) mergeWebhook(dst *webhook, policy kyverno.PolicyI
case "ServiceProxyOptions":
gvrList = append(gvrList, schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services/proxy"})
default:
_, gvr, err := m.client.DiscoveryClient.FindResource(gv, k)
_, gvr, err := m.client.Discovery().FindResource(gv, k)
if err != nil {
m.log.Error(err, "unable to convert GVK to GVR", "GVK", gvk)
continue

View file

@ -67,7 +67,7 @@ type Register struct {
// NewRegister creates new Register instance
func NewRegister(
clientConfig *rest.Config,
client *client.Client,
client client.Interface,
kubeClient kubernetes.Interface,
kyvernoClient kyvernoclient.Interface,
mwcInformer adminformers.MutatingWebhookConfigurationInformer,

View file

@ -241,7 +241,7 @@ func (ws *WebhookServer) handleUpdateGenerateTargetResource(request *admissionv1
}
}
func getGeneratedByResource(newRes *unstructured.Unstructured, resLabels map[string]string, client *client.Client, rule kyverno.Rule, logger logr.Logger) (kyverno.Rule, error) {
func getGeneratedByResource(newRes *unstructured.Unstructured, resLabels map[string]string, client client.Interface, rule kyverno.Rule, logger logr.Logger) (kyverno.Rule, error) {
var apiVersion, kind, name, namespace string
sourceRequest := &admissionv1.AdmissionRequest{}
kind = resLabels["kyverno.io/generated-by-kind"]

View file

@ -42,7 +42,7 @@ type WebhookServer struct {
server *http.Server
// clients
client *client.Client
client client.Interface
kyvernoClient kyvernoclient.Interface
// listers
@ -94,7 +94,7 @@ type WebhookServer struct {
// Policy Controller and Kubernetes Client should be initialized in configuration
func NewWebhookServer(
kyvernoClient kyvernoclient.Interface,
client *client.Client,
client client.Interface,
tlsPair *tlsutils.PemPair,
urInformer urinformer.UpdateRequestInformer,
pInformer kyvernoinformer.ClusterPolicyInformer,

View file

@ -42,7 +42,7 @@ type AuditHandler interface {
}
type auditHandler struct {
client *client.Client
client client.Interface
queue workqueue.RateLimitingInterface
pCache policycache.Interface
eventGen event.Interface
@ -66,7 +66,7 @@ func NewValidateAuditHandler(pCache policycache.Interface,
namespaces informers.NamespaceInformer,
log logr.Logger,
dynamicConfig config.Interface,
client *client.Client,
client client.Interface,
promConfig *metrics.PromConfig) AuditHandler {
return &auditHandler{