diff --git a/manager/resource_test.go b/manager/resource_test.go index 579547c9e..5628413bd 100644 --- a/manager/resource_test.go +++ b/manager/resource_test.go @@ -1,21 +1,23 @@ -package manager +package manager_test import ( "testing" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" + + "github.com/virtual-kubelet/virtual-kubelet/manager" + testutil "github.com/virtual-kubelet/virtual-kubelet/test/util" ) // TestGetPods verifies that the resource manager acts as a passthrough to a pod lister. func TestGetPods(t *testing.T) { var ( lsPods = []*v1.Pod{ - makePod("namespace-0", "name-0", "image-0"), - makePod("namespace-1", "name-1", "image-1"), + testutil.FakePodWithSingleContainer("namespace-0", "name-0", "image-0"), + testutil.FakePodWithSingleContainer("namespace-1", "name-1", "image-1"), } ) @@ -27,7 +29,7 @@ func TestGetPods(t *testing.T) { podLister := corev1listers.NewPodLister(indexer) // Create a new instance of the resource manager based on the pod lister. - rm, err := NewResourceManager(podLister, nil, nil) + rm, err := manager.NewResourceManager(podLister, nil, nil) if err != nil { t.Fatal(err) } @@ -43,8 +45,8 @@ func TestGetPods(t *testing.T) { func TestGetSecret(t *testing.T) { var ( lsSecrets = []*v1.Secret{ - makeSecret("namespace-0", "name-0", "key-0", "val-0"), - makeSecret("namespace-1", "name-1", "key-1", "val-1"), + testutil.FakeSecret("namespace-0", "name-0", map[string]string{"key-0": "val-0"}), + testutil.FakeSecret("namespace-1", "name-1", map[string]string{"key-1": "val-1"}), } ) @@ -56,7 +58,7 @@ func TestGetSecret(t *testing.T) { secretLister := corev1listers.NewSecretLister(indexer) // Create a new instance of the resource manager based on the secret lister. - rm, err := NewResourceManager(nil, secretLister, nil) + rm, err := manager.NewResourceManager(nil, secretLister, nil) if err != nil { t.Fatal(err) } @@ -82,8 +84,8 @@ func TestGetSecret(t *testing.T) { func TestGetConfigMap(t *testing.T) { var ( lsConfigMaps = []*v1.ConfigMap{ - makeConfigMap("namespace-0", "name-0", "key-0", "val-0"), - makeConfigMap("namespace-1", "name-1", "key-1", "val-1"), + testutil.FakeConfigMap("namespace-0", "name-0", map[string]string{"key-0": "val-0"}), + testutil.FakeConfigMap("namespace-1", "name-1", map[string]string{"key-1": "val-1"}), } ) @@ -95,7 +97,7 @@ func TestGetConfigMap(t *testing.T) { configMapLister := corev1listers.NewConfigMapLister(indexer) // Create a new instance of the resource manager based on the config map lister. - rm, err := NewResourceManager(nil, nil, configMapLister) + rm, err := manager.NewResourceManager(nil, nil, configMapLister) if err != nil { t.Fatal(err) } @@ -116,43 +118,3 @@ func TestGetConfigMap(t *testing.T) { t.Fatalf("expected a 'not found' error, got %v", err) } } - -func makeConfigMap(namespace, name, key, value string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Data: map[string]string{ - key: value, - }, - } -} - -func makePod(namespace, name, image string) *v1.Pod { - return &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Image: image, - }, - }, - }, - } -} - -func makeSecret(namespace, name, key, value string) *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Data: map[string][]byte{ - key: []byte(value), - }, - } -} diff --git a/test/util/kubernetes.go b/test/util/kubernetes.go new file mode 100644 index 000000000..3fbeae877 --- /dev/null +++ b/test/util/kubernetes.go @@ -0,0 +1,56 @@ +package util + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" +) + +// FakeConfigMap returns a configmap with the specified namespace, name and data. +func FakeConfigMap(namespace, name string, data map[string]string) *corev1.ConfigMap { + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: data, + } +} + +// FakeEventRecorder returns an event recorder that can be used to capture events. +func FakeEventRecorder(bufferSize int) *record.FakeRecorder { + return record.NewFakeRecorder(bufferSize) +} + +// FakePodWithSingleContainer returns a pod with the specified namespace and name, and having a single container with the specified image. +func FakePodWithSingleContainer(namespace, name, image string) *corev1.Pod { + return &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: name, + Image: image, + }, + }, + }, + } +} + +// FakeSecret returns a secret with the specified namespace, name and data. +func FakeSecret(namespace, name string, data map[string]string) *corev1.Secret { + res := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Data: make(map[string][]byte), + } + for key, val := range data { + res.Data[key] = []byte(val) + } + return res +} diff --git a/test/util/resource_manager.go b/test/util/resource_manager.go new file mode 100644 index 000000000..65abe43dc --- /dev/null +++ b/test/util/resource_manager.go @@ -0,0 +1,40 @@ +package util + +import ( + "time" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/tools/cache" + + "github.com/virtual-kubelet/virtual-kubelet/manager" +) + +// FakeResourceManager returns an instance of the resource manager that will return the specified objects when its "GetX" methods are called. +// Objects can be any valid Kubernetes object (corev1.Pod, corev1.ConfigMap, corev1.Secret, ...). +func FakeResourceManager(objects ...runtime.Object) *manager.ResourceManager { + // Create a fake Kubernetes client that will list the specified objects. + kubeClient := fake.NewSimpleClientset(objects...) + // Create a shared informer factory from where we can grab informers and listers for pods, configmaps and secrets. + kubeInformerFactory := informers.NewSharedInformerFactory(kubeClient, 30*time.Second) + // Grab informers for pods, configmaps and secrets. + pInformer := kubeInformerFactory.Core().V1().Pods() + mInformer := kubeInformerFactory.Core().V1().ConfigMaps() + sInformer := kubeInformerFactory.Core().V1().Secrets() + // Start all the required informers. + go pInformer.Informer().Run(wait.NeverStop) + go mInformer.Informer().Run(wait.NeverStop) + go sInformer.Informer().Run(wait.NeverStop) + // Wait for the caches to be synced. + if !cache.WaitForCacheSync(wait.NeverStop, pInformer.Informer().HasSynced, mInformer.Informer().HasSynced, sInformer.Informer().HasSynced) { + panic("failed to wait for caches to be synced") + } + // Create a new instance of the resource manager using the listers for pods, configmaps and secrets. + r, err := manager.NewResourceManager(pInformer.Lister(), sInformer.Lister(), mInformer.Lister()) + if err != nil { + panic(err) + } + return r +} diff --git a/vkubelet/env_internal_test.go b/vkubelet/env_internal_test.go index dc44bf18d..3699558da 100644 --- a/vkubelet/env_internal_test.go +++ b/vkubelet/env_internal_test.go @@ -3,22 +3,17 @@ package vkubelet import ( "context" "testing" - "time" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "github.com/virtual-kubelet/virtual-kubelet/manager" + testutil "github.com/virtual-kubelet/virtual-kubelet/test/util" ) const ( + // defaultEventRecorderBufferSize is the default buffer size to use when creating fake event recorders. + defaultEventRecorderBufferSize = 5 // invalidKey1 is a key that cannot be used as the name of an environment variable (since it starts with a digit). invalidKey1 = "1INVALID" // invalidKey2 is a key that cannot be used as the name of an environment variable (since it starts with a digit). @@ -51,34 +46,34 @@ var ( // Used so we can take its address when a pointer to a bool is required. bTrue = true // configMap1 is a configmap containing a single key, valid as the name of an environment variable. - configMap1 = mockConfigMap(namespace, "configmap-1", map[string]string{ + configMap1 = testutil.FakeConfigMap(namespace, "configmap-1", map[string]string{ keyFoo: "__foo__", }) // configMap2 is a configmap containing a single key, valid as the name of an environment variable. - configMap2 = mockConfigMap(namespace, "configmap-2", map[string]string{ + configMap2 = testutil.FakeConfigMap(namespace, "configmap-2", map[string]string{ keyBar: "__bar__", }) // configMap3 is a configmap containing a single key, valid as the name of an environment variable. - configMap3 = mockConfigMap(namespace, "configmap-2", map[string]string{ + configMap3 = testutil.FakeConfigMap(namespace, "configmap-2", map[string]string{ keyFoo: "__foo__", keyBar: "__bar__", }) // invalidConfigMap1 is a configmap containing two keys, one of which is invalid as the name of an environment variable. - invalidConfigMap1 = mockConfigMap(namespace, "invalid-configmap-1", map[string]string{ + invalidConfigMap1 = testutil.FakeConfigMap(namespace, "invalid-configmap-1", map[string]string{ keyFoo: "__foo__", invalidKey1: "will-be-skipped", invalidKey2: "will-be-skipped", }) // secret1 is a secret containing a single key, valid as the name of an environment variable. - secret1 = mockSecret(namespace, "secret-1", map[string]string{ + secret1 = testutil.FakeSecret(namespace, "secret-1", map[string]string{ keyBaz: "__baz__", }) // secret2 is a secret containing a single key, valid as the name of an environment variable. - secret2 = mockSecret(namespace, "secret-2", map[string]string{ + secret2 = testutil.FakeSecret(namespace, "secret-2", map[string]string{ keyFoo: "__foo__", }) // invalidSecret1 is a secret containing two keys, one of which is invalid as the name for an environment variable. - invalidSecret1 = mockSecret(namespace, "invalid-secret-1", map[string]string{ + invalidSecret1 = testutil.FakeSecret(namespace, "invalid-secret-1", map[string]string{ invalidKey3: "will-be-skipped", keyBaz: "__baz__", }) @@ -87,8 +82,8 @@ var ( // TestPopulatePodWithInitContainers populates the environment of a pod with four containers (two init containers, two containers). // Then, it checks that the resulting environment for each container contains the expected environment variables. func TestPopulatePodWithInitContainers(t *testing.T) { - rm := fakeResourceManager(configMap1, configMap2, secret1, secret2) - er := fakeEventRecorder() + rm := testutil.FakeResourceManager(configMap1, configMap2, secret1, secret2) + er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize) // Create a pod object having two init containers and two containers. // The containers' environment is to be populated from two configmaps and two secrets. @@ -187,8 +182,8 @@ func TestPopulatePodWithInitContainers(t *testing.T) { // TestEnvFromTwoConfigMapsAndOneSecret populates the environment of a container from two configmaps and one secret. // Then, it checks that the resulting environment contains all the expected environment variables and values. func TestEnvFromTwoConfigMapsAndOneSecret(t *testing.T) { - rm := fakeResourceManager(configMap1, configMap2, secret1) - er := fakeEventRecorder() + rm := testutil.FakeResourceManager(configMap1, configMap2, secret1) + er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize) // Create a pod object having a single container. // The container's environment is to be populated from two configmaps and one secret. @@ -258,8 +253,8 @@ func TestEnvFromTwoConfigMapsAndOneSecret(t *testing.T) { // TestEnvFromConfigMapAndSecretWithInvalidKeys populates the environment of a container from a configmap and a secret containing invalid keys. // Then, it checks that the resulting environment contains all the expected environment variables and values, and that the invalid keys have been skipped. func TestEnvFromConfigMapAndSecretWithInvalidKeys(t *testing.T) { - rm := fakeResourceManager(invalidConfigMap1, invalidSecret1) - er := fakeEventRecorder() + rm := testutil.FakeResourceManager(invalidConfigMap1, invalidSecret1) + er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize) // Create a pod object having a single container. // The container's environment is to be populated from a configmap and a secret, both of which have some invalid keys. @@ -326,8 +321,8 @@ func TestEnvFromConfigMapAndSecretWithInvalidKeys(t *testing.T) { // TestEnvOverridesEnvFrom populates the environment of a container from a configmap, and from another configmap's key with a "conflicting" key. // Then, it checks that the value of the "conflicting" key has been correctly overriden. func TestEnvOverridesEnvFrom(t *testing.T) { - rm := fakeResourceManager(configMap3) - er := fakeEventRecorder() + rm := testutil.FakeResourceManager(configMap3) + er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize) // override will override the value of "keyFoo" from "configMap3". override := "__override__" @@ -385,8 +380,8 @@ func TestEnvOverridesEnvFrom(t *testing.T) { // TestEnvFromInexistentConfigMaps populates the environment of a container from two configmaps (one of them optional) that do not exist. // Then, it checks that the expected events have been recorded. func TestEnvFromInexistentConfigMaps(t *testing.T) { - rm := fakeResourceManager() - er := fakeEventRecorder() + rm := testutil.FakeResourceManager() + er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize) missingConfigMap1Name := "missing-config-map-1" missingConfigMap2Name := "missing-config-map-2" @@ -441,8 +436,8 @@ func TestEnvFromInexistentConfigMaps(t *testing.T) { } func TestEnvFromInexistentSecrets(t *testing.T) { - rm := fakeResourceManager() - er := fakeEventRecorder() + rm := testutil.FakeResourceManager() + er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize) missingSecret1Name := "missing-secret-1" missingSecret2Name := "missing-secret-2" @@ -495,60 +490,3 @@ func TestEnvFromInexistentSecrets(t *testing.T) { assert.Contains(t, event2, ReasonMandatorySecretNotFound) assert.Contains(t, event2, missingSecret2Name) } - -// mockConfigMap returns a configmap with the specified namespace, name and data. -func mockConfigMap(namespace, name string, data map[string]string) *corev1.ConfigMap { - return &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Data: data, - } -} - -// fakeEventRecorder returns an event recorder that can be used to capture events. -func fakeEventRecorder() *record.FakeRecorder { - return record.NewFakeRecorder(10) -} - -// fakeResourceManager returns an instance of the resource manager that will return the specified objects when its "GetX" functions are called. -// Objects can be any valid Kubernetes object (corev1.Pod, corev1.ConfigMap, corev1.Secret, ...). -func fakeResourceManager(objects ...runtime.Object) *manager.ResourceManager { - // Create a fake Kubernetes client that will list the specified objects. - kubeClient := fake.NewSimpleClientset(objects...) - // Create a shared informer factory from where we can grab informers and listers for pods, configmaps and secrets. - kubeInformerFactory := informers.NewSharedInformerFactory(kubeClient, 30*time.Second) - // Grab and start informers for pods, configmaps and secrets. - pInformer := kubeInformerFactory.Core().V1().Pods() - mInformer := kubeInformerFactory.Core().V1().ConfigMaps() - sInformer := kubeInformerFactory.Core().V1().Secrets() - go pInformer.Informer().Run(wait.NeverStop) - go mInformer.Informer().Run(wait.NeverStop) - go sInformer.Informer().Run(wait.NeverStop) - // Wait for the caches to be synced. - if !cache.WaitForCacheSync(wait.NeverStop, pInformer.Informer().HasSynced, mInformer.Informer().HasSynced, sInformer.Informer().HasSynced) { - panic("failed to wait for caches to be synced") - } - // Create a new instance of the resource manager using the listers for pods, configmaps and secrets. - r, err := manager.NewResourceManager(pInformer.Lister(), sInformer.Lister(), mInformer.Lister()) - if err != nil { - panic(err) - } - return r -} - -// mockSecret returns a secret with the specified namespace, name and data. -func mockSecret(namespace, name string, data map[string]string) *corev1.Secret { - res := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - Data: make(map[string][]byte), - } - for key, val := range data { - res.Data[key] = []byte(val) - } - return res -}