mirror of
https://github.com/kyverno/kyverno.git
synced 2024-12-14 11:57:48 +00:00
initial client & controller test
This commit is contained in:
parent
4ce925ccf0
commit
a8d3dd79ee
5 changed files with 352 additions and 28 deletions
|
@ -1 +1,105 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golang/glog"
|
||||
types "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||
"github.com/nirmata/kyverno/pkg/sharedinformer"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/sample-controller/pkg/signals"
|
||||
)
|
||||
|
||||
func TestCreatePolicy(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
// new policy is added to policy lister and explictly passed to sync-handler
|
||||
// to process the existing
|
||||
policy := newPolicy("test-policy")
|
||||
f.policyLister = append(f.policyLister, policy)
|
||||
f.objects = append(f.objects, policy)
|
||||
// run controller
|
||||
f.runControler("test-policy")
|
||||
}
|
||||
|
||||
func (f *fixture) runControler(policyName string) {
|
||||
policyInformerFactory, err := sharedinformer.NewFakeSharedInformerFactory()
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
// new controller
|
||||
policyController := NewPolicyController(
|
||||
f.Client,
|
||||
policyInformerFactory,
|
||||
nil,
|
||||
nil)
|
||||
|
||||
stopCh := signals.SetupSignalHandler()
|
||||
// start informer & controller
|
||||
policyInformerFactory.Run(stopCh)
|
||||
if err = policyController.Run(stopCh); err != nil {
|
||||
glog.Fatalf("Error running PolicyController: %v\n", err)
|
||||
}
|
||||
// add policy to the informer
|
||||
for _, p := range f.policyLister {
|
||||
policyInformerFactory.GetInfomer().GetIndexer().Add(p)
|
||||
}
|
||||
|
||||
// sync handler
|
||||
// reads the policy from the policy lister and processes them
|
||||
err = policyController.syncHandler(policyName)
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
policyController.Stop()
|
||||
|
||||
}
|
||||
|
||||
type fixture struct {
|
||||
t *testing.T
|
||||
Client *client.Client
|
||||
policyLister []*types.Policy
|
||||
objects []runtime.Object
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
f := &fixture{}
|
||||
f.t = t
|
||||
return f
|
||||
}
|
||||
|
||||
// create mock client with initial resouces
|
||||
// set registered resources for gvr
|
||||
func (f *fixture) setupFixture() {
|
||||
scheme := runtime.NewScheme()
|
||||
fclient, err := client.NewMockClient(scheme, f.objects...)
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
regresource := map[string]string{"kyverno.io/v1alpha1": "policys"}
|
||||
fclient.SetDiscovery(client.NewFakeDiscoveryClient(regresource))
|
||||
}
|
||||
|
||||
func newPolicy(name string) *types.Policy {
|
||||
return &types.Policy{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: types.SchemeGroupVersion.String()},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,15 +27,16 @@ import (
|
|||
|
||||
//Client enables interaction with k8 resource
|
||||
type Client struct {
|
||||
client dynamic.Interface
|
||||
cachedClient discovery.CachedDiscoveryInterface
|
||||
clientConfig *rest.Config
|
||||
kclient *kubernetes.Clientset
|
||||
client dynamic.Interface
|
||||
cachedClient discovery.CachedDiscoveryInterface
|
||||
clientConfig *rest.Config
|
||||
kclient kubernetes.Interface
|
||||
discoveryClient IDiscovery
|
||||
}
|
||||
|
||||
//NewClient creates new instance of client
|
||||
func NewClient(config *rest.Config) (*Client, error) {
|
||||
client, err := dynamic.NewForConfig(config)
|
||||
dclient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -44,13 +45,15 @@ func NewClient(config *rest.Config) (*Client, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{
|
||||
client: client,
|
||||
client := Client{
|
||||
client: dclient,
|
||||
clientConfig: config,
|
||||
kclient: kclient,
|
||||
cachedClient: memory.NewMemCacheClient(kclient.Discovery()),
|
||||
}, nil
|
||||
}
|
||||
// Set discovery client
|
||||
discoveryClient := ServerPreferredResources{memory.NewMemCacheClient(kclient.Discovery())}
|
||||
client.SetDiscovery(discoveryClient)
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
//GetKubePolicyDeployment returns kube policy depoyment value
|
||||
|
@ -100,7 +103,7 @@ func (c *Client) getGroupVersionMapper(resource string) schema.GroupVersionResou
|
|||
//TODO: add checks to see if the resource is supported
|
||||
//TODO: build the resource list dynamically( by querying the registered resources)
|
||||
//TODO: the error scenarios
|
||||
return c.getGVR(resource)
|
||||
return c.discoveryClient.getGVR(resource)
|
||||
}
|
||||
|
||||
// GetResource returns the resource in unstructured/json format
|
||||
|
@ -156,24 +159,10 @@ func convertToUnstructured(obj interface{}) *unstructured.Unstructured {
|
|||
return &unstructured.Unstructured{Object: unstructuredObj}
|
||||
}
|
||||
|
||||
//ConvertToRuntimeObject converts unstructed to runtime.Object runtime instance
|
||||
func ConvertToRuntimeObject(obj *unstructured.Unstructured) (*runtime.Object, error) {
|
||||
scheme := runtime.NewScheme()
|
||||
gvk := obj.GroupVersionKind()
|
||||
runtimeObj, err := scheme.New(gvk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &runtimeObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &runtimeObj, nil
|
||||
}
|
||||
|
||||
// GenerateResource creates resource of the specified kind(supports 'clone' & 'data')
|
||||
func (c *Client) GenerateResource(generator types.Generation, namespace string) error {
|
||||
var err error
|
||||
rGVR := c.getGVRFromKind(generator.Kind)
|
||||
rGVR := c.discoveryClient.getGVRFromKind(generator.Kind)
|
||||
resource := &unstructured.Unstructured{}
|
||||
|
||||
var rdata map[string]interface{}
|
||||
|
@ -244,7 +233,20 @@ func (c *Client) waitUntilNamespaceIsCreated(name string) error {
|
|||
return lastError
|
||||
}
|
||||
|
||||
func (c *Client) getGVR(resource string) schema.GroupVersionResource {
|
||||
type IDiscovery interface {
|
||||
getGVR(resource string) schema.GroupVersionResource
|
||||
getGVRFromKind(kind string) schema.GroupVersionResource
|
||||
}
|
||||
|
||||
func (c *Client) SetDiscovery(discoveryClient IDiscovery) {
|
||||
c.discoveryClient = discoveryClient
|
||||
}
|
||||
|
||||
type ServerPreferredResources struct {
|
||||
cachedClient discovery.CachedDiscoveryInterface
|
||||
}
|
||||
|
||||
func (c ServerPreferredResources) getGVR(resource string) schema.GroupVersionResource {
|
||||
emptyGVR := schema.GroupVersionResource{}
|
||||
serverresources, err := c.cachedClient.ServerPreferredResources()
|
||||
if err != nil {
|
||||
|
@ -268,7 +270,7 @@ func (c *Client) getGVR(resource string) schema.GroupVersionResource {
|
|||
|
||||
//To-do: measure performance
|
||||
//To-do: evaluate DefaultRESTMapper to fetch kind->resource mapping
|
||||
func (c *Client) getGVRFromKind(kind string) schema.GroupVersionResource {
|
||||
func (c ServerPreferredResources) getGVRFromKind(kind string) schema.GroupVersionResource {
|
||||
emptyGVR := schema.GroupVersionResource{}
|
||||
serverresources, err := c.cachedClient.ServerPreferredResources()
|
||||
if err != nil {
|
||||
|
|
|
@ -1,6 +1,160 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
policytypes "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// GetResource
|
||||
// ListResource
|
||||
// CreateResource
|
||||
// getGroupVersionMapper (valid and invalid resources)
|
||||
|
||||
//NewMockClient creates a mock client
|
||||
// - dynamic client
|
||||
// - kubernetes client
|
||||
// - objects to initialize the client
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func TestClient(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
// init groupversion
|
||||
regresource := map[string]string{"group/version": "thekinds",
|
||||
"group2/version": "thekinds",
|
||||
"v1": "namespaces",
|
||||
"apps/v1": "deployments"}
|
||||
// init resources
|
||||
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", "kyverno", "kyverno-deployment"),
|
||||
}
|
||||
|
||||
// Mock Client
|
||||
client, err := NewMockClient(scheme, objects...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// set discovery Client
|
||||
client.SetDiscovery(NewFakeDiscoveryClient(regresource))
|
||||
// Get Resource
|
||||
res, err := client.GetResource("thekinds", "ns-foo", "name-foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(res)
|
||||
// List Resources
|
||||
list, err := client.ListResource("thekinds", "ns-foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(len(list.Items))
|
||||
// DeleteResouce
|
||||
err = client.DeleteResouce("thekinds", "ns-foo", "name-bar")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// CreateResource
|
||||
res, err = client.CreateResource("thekinds", "ns-foo", newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// UpdateResource
|
||||
res, err = client.UpdateResource("thekinds", "ns-foo", newUnstructuredWithSpec("group/version", "TheKind", "ns-foo", "name-foo1", map[string]interface{}{"foo": "bar"}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// UpdateStatusResource
|
||||
res, err = client.UpdateStatusResource("thekinds", "ns-foo", newUnstructuredWithSpec("group/version", "TheKind", "ns-foo", "name-foo1", map[string]interface{}{"foo": "status"}))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
iEvent, err := client.GetEventsInterface()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eventList, err := iEvent.List(meta.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(eventList.Items)
|
||||
|
||||
iCSR, err := client.GetCSRInterface()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
csrList, err := iCSR.List(meta.ListOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(csrList.Items)
|
||||
|
||||
//GenerateResource -> copy From
|
||||
// 1 create namespace
|
||||
// 2 generate resource
|
||||
|
||||
// create namespace
|
||||
ns, err := client.CreateResource("namespaces", "", newUnstructured("v1", "Namespace", "", "ns1"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gen := policytypes.Generation{Kind: "TheKind",
|
||||
Name: "gen-kind",
|
||||
Clone: &policytypes.CloneFrom{Namespace: "ns-foo", Name: "name-foo"}}
|
||||
err = client.GenerateResource(gen, ns.GetName())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, err = client.GetResource("thekinds", "ns1", "gen-kind")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// GenerateResource -> data
|
||||
gen = policytypes.Generation{Kind: "TheKind",
|
||||
Name: "name2-baz-new",
|
||||
Data: newUnstructured("group2/version", "TheKind", "ns1", "name2-baz-new")}
|
||||
err = client.GenerateResource(gen, ns.GetName())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, err = client.GetResource("thekinds", "ns1", "name2-baz-new")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Get Kube Policy Deployment
|
||||
deploy, err := client.GetKubePolicyDeployment()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(deploy.GetName())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic/fake"
|
||||
kubernetesfake "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -16,3 +22,46 @@ const (
|
|||
)
|
||||
const namespaceCreationMaxWaitTime time.Duration = 30 * time.Second
|
||||
const namespaceCreationWaitInterval time.Duration = 100 * time.Millisecond
|
||||
|
||||
//---testing utilities
|
||||
func NewMockClient(scheme *runtime.Scheme, objects ...runtime.Object) (*Client, error) {
|
||||
client := fake.NewSimpleDynamicClient(scheme, objects...)
|
||||
// the typed and dynamic client are initalized with similar resources
|
||||
kclient := kubernetesfake.NewSimpleClientset(objects...)
|
||||
return &Client{
|
||||
client: client,
|
||||
kclient: kclient,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
// NewFakeDiscoveryClient returns a fakediscovery client
|
||||
func NewFakeDiscoveryClient(regResources map[string]string) *fakeDiscoveryClient {
|
||||
registeredResources := make([]schema.GroupVersionResource, len(regResources))
|
||||
for groupVersion, resource := range regResources {
|
||||
gv, err := schema.ParseGroupVersion(groupVersion)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
registeredResources = append(registeredResources, gv.WithResource(resource))
|
||||
}
|
||||
return &fakeDiscoveryClient{registeredResouces: registeredResources}
|
||||
}
|
||||
|
||||
type fakeDiscoveryClient struct {
|
||||
registeredResouces []schema.GroupVersionResource
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) getGVR(resource string) schema.GroupVersionResource {
|
||||
for _, gvr := range c.registeredResouces {
|
||||
if gvr.Resource == resource {
|
||||
return gvr
|
||||
}
|
||||
}
|
||||
return schema.GroupVersionResource{}
|
||||
}
|
||||
|
||||
func (c *fakeDiscoveryClient) getGVRFromKind(kind string) schema.GroupVersionResource {
|
||||
resource := strings.ToLower(kind) + "s"
|
||||
return c.getGVR(resource)
|
||||
}
|
||||
|
|
15
pkg/sharedinformer/utils.go
Normal file
15
pkg/sharedinformer/utils.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package sharedinformer
|
||||
|
||||
import (
|
||||
"github.com/nirmata/kyverno/pkg/client/clientset/versioned/fake"
|
||||
informers "github.com/nirmata/kyverno/pkg/client/informers/externalversions"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func NewFakeSharedInformerFactory(objects ...runtime.Object) (SharedInfomer, error) {
|
||||
fakePolicyClient := fake.NewSimpleClientset(objects...)
|
||||
policyInformerFactory := informers.NewSharedInformerFactory(fakePolicyClient, 0)
|
||||
return &sharedInfomer{
|
||||
policyInformerFactory: policyInformerFactory,
|
||||
}, nil
|
||||
}
|
Loading…
Reference in a new issue