mirror of
https://github.com/kyverno/kyverno.git
synced 2025-04-18 02:06:52 +00:00
refactor: dclient package (#3775)
* 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:
parent
6e07acdd87
commit
c79223393b
41 changed files with 473 additions and 481 deletions
cmd
cli/kubectl-kyverno
initContainer
kyverno
pkg
auth
background
common
dclient
engine
event
openapi
policy
policyreport
testrunner
utils/kube
webhookconfig
webhooks
|
@ -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 {
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
157
pkg/dclient/discovery.go
Normal 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
69
pkg/dclient/fake.go
Normal 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
19
pkg/dclient/mock.go
Normal 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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
39
pkg/utils/kube/unstructured.go
Normal file
39
pkg/utils/kube/unstructured.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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{
|
||||
|
|
Loading…
Add table
Reference in a new issue