Skip to content

test/e2e: move all resource creation to resource_creator #370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 0 additions & 111 deletions test/e2e/e2eutil/create_util.go

This file was deleted.

21 changes: 14 additions & 7 deletions test/e2e/framework/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import (
"strings"
"testing"
"time"

"k8s.io/client-go/rest"
)

type TestCtx struct {
ID string
cleanUpFns []finalizerFn
CleanUpFns []finalizerFn
Namespace string
CRClient *rest.RESTClient
}

type finalizerFn func() error
Expand All @@ -32,23 +36,26 @@ func (f *Framework) NewTestCtx(t *testing.T) TestCtx {
}
}

func (ctx *TestCtx) GetID() string {
return ctx.ID
}

// GetObjID returns an ascending ID based on the length of cleanUpFns. It is
// based on the premise that every new object also appends a new finalizerFn on
// cleanUpFns. This can e.g. be used to create multiple namespaces in the same
// test context.
// cleanUpFns.
func (ctx *TestCtx) GetObjID() string {
return ctx.ID + "-" + strconv.Itoa(len(ctx.cleanUpFns))
return ctx.ID + "-" + strconv.Itoa(len(ctx.CleanUpFns))
}

func (ctx *TestCtx) Cleanup(t *testing.T) {
for i := len(ctx.cleanUpFns) - 1; i >= 0; i-- {
err := ctx.cleanUpFns[i]()
for i := len(ctx.CleanUpFns) - 1; i >= 0; i-- {
err := ctx.CleanUpFns[i]()
if err != nil {
t.Errorf("A cleanup function failed with error: %v\n", err)
}
}
}

func (ctx *TestCtx) AddFinalizerFn(fn finalizerFn) {
ctx.cleanUpFns = append(ctx.cleanUpFns, fn)
ctx.CleanUpFns = append(ctx.CleanUpFns, fn)
}
19 changes: 13 additions & 6 deletions test/e2e/framework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"os"

extensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
Expand All @@ -13,9 +14,10 @@ import (
var Global *Framework

type Framework struct {
KubeConfig *rest.Config
KubeClient kubernetes.Interface
ImageName *string
KubeConfig *rest.Config
KubeClient kubernetes.Interface
ExtensionsClient *extensions.Clientset
ImageName *string
}

func setup() error {
Expand All @@ -38,10 +40,15 @@ func setup() error {
if err != nil {
return err
}
extensionsClient, err := extensions.NewForConfig(kubeconfig)
if err != nil {
return err
}
Global = &Framework{
KubeConfig: kubeconfig,
KubeClient: kubeclient,
ImageName: imageName,
KubeConfig: kubeconfig,
KubeClient: kubeclient,
ExtensionsClient: extensionsClient,
ImageName: imageName,
}
return nil
}
160 changes: 152 additions & 8 deletions test/e2e/framework/resource_creator.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,165 @@
package framework

import (
"testing"
"errors"
"strings"

y2j "github.com/ghodss/yaml"
yaml "gopkg.in/yaml.v2"
apps "k8s.io/api/apps/v1"
core "k8s.io/api/core/v1"
"k8s.io/api/rbac/v1beta1"
crd "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
extensions_scheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
)

func (ctx *TestCtx) CreateNamespace(f *Framework, t *testing.T) (string, error) {
func (ctx *TestCtx) GetNamespace() (string, error) {
if ctx.Namespace != "" {
return ctx.Namespace, nil
}
// create namespace
namespace := ctx.GetObjID()
namespaceObj := &core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
_, err := f.KubeClient.CoreV1().Namespaces().Create(namespaceObj)
ctx.Namespace = ctx.GetID()
namespaceObj := &core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ctx.Namespace}}
_, err := Global.KubeClient.CoreV1().Namespaces().Create(namespaceObj)
if err != nil {
return "", err
}
t.Logf("Created namespace: %s", namespace)
ctx.AddFinalizerFn(func() error { return f.KubeClient.CoreV1().Namespaces().Delete(namespace, metav1.NewDeleteOptions(0)) })
return namespace, nil
ctx.AddFinalizerFn(func() error {
return Global.KubeClient.CoreV1().Namespaces().Delete(ctx.Namespace, metav1.NewDeleteOptions(0))
})
return ctx.Namespace, nil
}

func (ctx *TestCtx) GetCRClient(yamlCR []byte) (*rest.RESTClient, error) {
if ctx.CRClient != nil {
return ctx.CRClient, nil
}
// get new RESTClient for custom resources
crConfig := Global.KubeConfig
yamlMap := make(map[interface{}]interface{})
err := yaml.Unmarshal(yamlCR, &yamlMap)
if err != nil {
return nil, err
}
groupVersion := strings.Split(yamlMap["apiVersion"].(string), "/")
crGV := schema.GroupVersion{Group: groupVersion[0], Version: groupVersion[1]}
crConfig.GroupVersion = &crGV
crConfig.APIPath = "/apis"
crConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}

if crConfig.UserAgent == "" {
crConfig.UserAgent = rest.DefaultKubernetesUserAgent()
}
ctx.CRClient, err = rest.RESTClientFor(crConfig)
if err != nil {
return nil, err
}
return ctx.CRClient, nil
}

func (ctx *TestCtx) createCRFromYAML(yamlFile []byte, resourceName string) error {
client, err := ctx.GetCRClient(yamlFile)
if err != nil {
return err
}
namespace, err := ctx.GetNamespace()
if err != nil {
return err
}
jsonDat, err := y2j.YAMLToJSON(yamlFile)
err = client.Post().
Namespace(namespace).
Resource(resourceName).
Body(jsonDat).
Do().
Error()
ctx.AddFinalizerFn(func() error {
return client.Delete().
Namespace(namespace).
Resource(resourceName).
Body(metav1.NewDeleteOptions(0)).
Do().
Error()
})
return err
}

func (ctx *TestCtx) createCRDFromYAML(yamlFile []byte) error {
decode := extensions_scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode(yamlFile, nil, nil)
if err != nil {
return err
}
switch o := obj.(type) {
case *crd.CustomResourceDefinition:
_, err = Global.ExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(o)
ctx.AddFinalizerFn(func() error {
err = Global.ExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(o.Name, metav1.NewDeleteOptions(0))
if err != nil && !apierrors.IsNotFound(err) {
return err
}
return nil
})
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should ignore the error if the CRD already exists:

if apierrors.IsAlreadyExists(err) {
    return nil
}

}
return nil
}

func (ctx *TestCtx) CreateFromYAML(yamlFile []byte) error {
yamlMap := make(map[interface{}]interface{})
err := yaml.Unmarshal(yamlFile, &yamlMap)
if err != nil {
return err
}
kind := yamlMap["kind"].(string)
switch kind {
case "Role":
case "RoleBinding":
case "Deployment":
case "CustomResourceDefinition":
return ctx.createCRDFromYAML(yamlFile)
// we assume that all custom resources are from operator-sdk and thus follow
// a common naming convention
default:
return ctx.createCRFromYAML(yamlFile, strings.ToLower(kind)+"s")
}

decode := scheme.Codecs.UniversalDeserializer().Decode
obj, _, err := decode(yamlFile, nil, nil)
if err != nil {
return err
}

namespace, err := ctx.GetNamespace()
if err != nil {
return err
}
switch o := obj.(type) {
case *v1beta1.Role:
_, err = Global.KubeClient.RbacV1beta1().Roles(namespace).Create(o)
ctx.AddFinalizerFn(func() error {
return Global.KubeClient.RbacV1beta1().Roles(namespace).Delete(o.Name, metav1.NewDeleteOptions(0))
})
return err
case *v1beta1.RoleBinding:
_, err = Global.KubeClient.RbacV1beta1().RoleBindings(namespace).Create(o)
ctx.AddFinalizerFn(func() error {
return Global.KubeClient.RbacV1beta1().RoleBindings(namespace).Delete(o.Name, metav1.NewDeleteOptions(0))
})
return err
case *apps.Deployment:
_, err = Global.KubeClient.AppsV1().Deployments(namespace).Create(o)
ctx.AddFinalizerFn(func() error {
return Global.KubeClient.AppsV1().Deployments(namespace).Delete(o.Name, metav1.NewDeleteOptions(0))
})
return err
default:
return errors.New("Unhandled resource type")
}
}
Loading