mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
commit
86281d587e
5 changed files with 401 additions and 28 deletions
|
@ -1 +1,109 @@
|
|||
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/apimachinery/pkg/runtime/schema"
|
||||
"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 := []schema.GroupVersionResource{
|
||||
schema.GroupVersionResource{Group: "kyverno.io",
|
||||
Version: "v1alpha1",
|
||||
Resource: "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
|
||||
|
@ -172,24 +175,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{}
|
||||
|
@ -260,7 +249,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 {
|
||||
|
@ -284,7 +286,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,161 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
policytypes "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
||||
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// GetResource
|
||||
// ListResource
|
||||
// CreateResource
|
||||
// getGroupVersionMapper (valid and invalid resources)
|
||||
|
||||
//NewMockClient creates a mock client
|
||||
// - dynamic client
|
||||
// - kubernetes client
|
||||
// - objects to initialize the client
|
||||
|
||||
type fixture struct {
|
||||
t *testing.T
|
||||
objects []runtime.Object
|
||||
client *Client
|
||||
}
|
||||
|
||||
func newFixture(t *testing.T) *fixture {
|
||||
// init groupversion
|
||||
regResource := []schema.GroupVersionResource{
|
||||
schema.GroupVersionResource{Group: "group", Version: "version", Resource: "thekinds"},
|
||||
schema.GroupVersionResource{Group: "group2", Version: "version", Resource: "thekinds"},
|
||||
schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"},
|
||||
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||
}
|
||||
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"),
|
||||
}
|
||||
scheme := runtime.NewScheme()
|
||||
// Create mock client
|
||||
client, err := NewMockClient(scheme, objects...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// set discovery Client
|
||||
client.SetDiscovery(NewFakeDiscoveryClient(regResource))
|
||||
|
||||
f := fixture{
|
||||
t: t,
|
||||
objects: objects,
|
||||
client: client,
|
||||
}
|
||||
return &f
|
||||
|
||||
}
|
||||
|
||||
func TestCRUDResource(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
// Get Resource
|
||||
_, err := f.client.GetResource("thekinds", "ns-foo", "name-foo")
|
||||
if err != nil {
|
||||
t.Errorf("GetResource not working: %s", err)
|
||||
}
|
||||
// List Resources
|
||||
_, err = f.client.ListResource("thekinds", "ns-foo")
|
||||
if err != nil {
|
||||
t.Errorf("ListResource not working: %s", err)
|
||||
}
|
||||
// DeleteResouce
|
||||
err = f.client.DeleteResouce("thekinds", "ns-foo", "name-bar")
|
||||
if err != nil {
|
||||
t.Errorf("DeleteResouce not working: %s", err)
|
||||
}
|
||||
// CreateResource
|
||||
_, err = f.client.CreateResource("thekinds", "ns-foo", newUnstructured("group/version", "TheKind", "ns-foo", "name-foo1"))
|
||||
if err != nil {
|
||||
t.Errorf("CreateResource not working: %s", err)
|
||||
}
|
||||
// UpdateResource
|
||||
_, err = f.client.UpdateResource("thekinds", "ns-foo", newUnstructuredWithSpec("group/version", "TheKind", "ns-foo", "name-foo1", map[string]interface{}{"foo": "bar"}))
|
||||
if err != nil {
|
||||
t.Errorf("UpdateResource not working: %s", err)
|
||||
}
|
||||
// UpdateStatusResource
|
||||
_, err = f.client.UpdateStatusResource("thekinds", "ns-foo", newUnstructuredWithSpec("group/version", "TheKind", "ns-foo", "name-foo1", map[string]interface{}{"foo": "status"}))
|
||||
if err != nil {
|
||||
t.Errorf("UpdateStatusResource not working: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventInterface(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
iEvent, err := f.client.GetEventsInterface()
|
||||
if err != nil {
|
||||
t.Errorf("GetEventsInterface not working: %s", err)
|
||||
}
|
||||
_, err = iEvent.List(meta.ListOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Testing Event interface not working: %s", err)
|
||||
}
|
||||
}
|
||||
func TestCSRInterface(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
iCSR, err := f.client.GetCSRInterface()
|
||||
if err != nil {
|
||||
t.Errorf("GetCSRInterface not working: %s", err)
|
||||
}
|
||||
_, err = iCSR.List(meta.ListOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Testing CSR interface not working: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateResource(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
//GenerateResource -> copy From
|
||||
// 1 create namespace
|
||||
// 2 generate resource
|
||||
// create namespace
|
||||
ns, err := f.client.CreateResource("namespaces", "", newUnstructured("v1", "Namespace", "", "ns1"))
|
||||
if err != nil {
|
||||
t.Errorf("CreateResource not working: %s", err)
|
||||
}
|
||||
gen := policytypes.Generation{Kind: "TheKind",
|
||||
Name: "gen-kind",
|
||||
Clone: &policytypes.CloneFrom{Namespace: "ns-foo", Name: "name-foo"}}
|
||||
err = f.client.GenerateResource(gen, ns.GetName())
|
||||
if err != nil {
|
||||
t.Errorf("GenerateResource not working: %s", err)
|
||||
}
|
||||
_, err = f.client.GetResource("thekinds", "ns1", "gen-kind")
|
||||
if err != nil {
|
||||
t.Errorf("GetResource not working: %s", err)
|
||||
}
|
||||
// GenerateResource -> data
|
||||
gen = policytypes.Generation{Kind: "TheKind",
|
||||
Name: "name2-baz-new",
|
||||
Data: newUnstructured("group2/version", "TheKind", "ns1", "name2-baz-new")}
|
||||
err = f.client.GenerateResource(gen, ns.GetName())
|
||||
if err != nil {
|
||||
t.Errorf("GenerateResource not working: %s", err)
|
||||
}
|
||||
_, err = f.client.GetResource("thekinds", "ns1", "name2-baz-new")
|
||||
if err != nil {
|
||||
t.Errorf("GetResource not working: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKubePolicyDeployment(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
_, err := f.client.GetKubePolicyDeployment()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"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 +23,89 @@ 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(registeredResouces []schema.GroupVersionResource) *fakeDiscoveryClient {
|
||||
// Load some-preregistd resources
|
||||
res := []schema.GroupVersionResource{
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "configmaps"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "endpoints"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "namespaces"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "resourcequotas"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "secrets"},
|
||||
schema.GroupVersionResource{Version: "v1", Resource: "serviceaccounts"},
|
||||
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "daemonsets"},
|
||||
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
|
||||
schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"},
|
||||
}
|
||||
registeredResouces = append(registeredResouces, res...)
|
||||
return &fakeDiscoveryClient{registeredResouces: registeredResouces}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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 retry(attempts int, sleep time.Duration, fn func() error) error {
|
||||
if err := fn(); err != nil {
|
||||
if s, ok := err.(stop); ok {
|
||||
return s.error
|
||||
}
|
||||
if attempts--; attempts > 0 {
|
||||
time.Sleep(sleep)
|
||||
return retry(attempts, 2*sleep, fn)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Custom error
|
||||
type stop struct {
|
||||
error
|
||||
}
|
||||
|
|
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…
Add table
Reference in a new issue