Skip to content

Commit f7627c6

Browse files
authored
test/e2e: move all resource creation to resource_creator (#370)
* test/e2e: move all resource creation to resource_creator This moves all the resource creation (which is currently the CreateFromYAML and namespace functions) into resource_creator.go. This allows these functions to be called under TestCtx, which now stores the namespace and a CRClient that can be retrieved by these functions when needed instead of being regenerated. The functions also now have access the Global framework variable and thus don't need to have a kubeclient and kubeconfig be passed as arguments when called. * test/e2e/framework/resource_creator.go: add default case in createCRDFromYAML * test/e2e/framework/resource_creator.go: ignore creation error if IsAlreadyExists
1 parent 1c3780f commit f7627c6

File tree

5 files changed

+203
-161
lines changed

5 files changed

+203
-161
lines changed

test/e2e/e2eutil/create_util.go

Lines changed: 0 additions & 111 deletions
This file was deleted.

test/e2e/framework/context.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ import (
55
"strings"
66
"testing"
77
"time"
8+
9+
"k8s.io/client-go/rest"
810
)
911

1012
type TestCtx struct {
1113
ID string
12-
cleanUpFns []finalizerFn
14+
CleanUpFns []finalizerFn
15+
Namespace string
16+
CRClient *rest.RESTClient
1317
}
1418

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

39+
func (ctx *TestCtx) GetID() string {
40+
return ctx.ID
41+
}
42+
3543
// GetObjID returns an ascending ID based on the length of cleanUpFns. It is
3644
// based on the premise that every new object also appends a new finalizerFn on
37-
// cleanUpFns. This can e.g. be used to create multiple namespaces in the same
38-
// test context.
45+
// cleanUpFns.
3946
func (ctx *TestCtx) GetObjID() string {
40-
return ctx.ID + "-" + strconv.Itoa(len(ctx.cleanUpFns))
47+
return ctx.ID + "-" + strconv.Itoa(len(ctx.CleanUpFns))
4148
}
4249

4350
func (ctx *TestCtx) Cleanup(t *testing.T) {
44-
for i := len(ctx.cleanUpFns) - 1; i >= 0; i-- {
45-
err := ctx.cleanUpFns[i]()
51+
for i := len(ctx.CleanUpFns) - 1; i >= 0; i-- {
52+
err := ctx.CleanUpFns[i]()
4653
if err != nil {
4754
t.Errorf("A cleanup function failed with error: %v\n", err)
4855
}
4956
}
5057
}
5158

5259
func (ctx *TestCtx) AddFinalizerFn(fn finalizerFn) {
53-
ctx.cleanUpFns = append(ctx.cleanUpFns, fn)
60+
ctx.CleanUpFns = append(ctx.CleanUpFns, fn)
5461
}

test/e2e/framework/framework.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log"
66
"os"
77

8+
extensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
89
"k8s.io/client-go/kubernetes"
910
"k8s.io/client-go/rest"
1011
"k8s.io/client-go/tools/clientcmd"
@@ -13,9 +14,10 @@ import (
1314
var Global *Framework
1415

1516
type Framework struct {
16-
KubeConfig *rest.Config
17-
KubeClient kubernetes.Interface
18-
ImageName *string
17+
KubeConfig *rest.Config
18+
KubeClient kubernetes.Interface
19+
ExtensionsClient *extensions.Clientset
20+
ImageName *string
1921
}
2022

2123
func setup() error {
@@ -38,10 +40,15 @@ func setup() error {
3840
if err != nil {
3941
return err
4042
}
43+
extensionsClient, err := extensions.NewForConfig(kubeconfig)
44+
if err != nil {
45+
return err
46+
}
4147
Global = &Framework{
42-
KubeConfig: kubeconfig,
43-
KubeClient: kubeclient,
44-
ImageName: imageName,
48+
KubeConfig: kubeconfig,
49+
KubeClient: kubeclient,
50+
ExtensionsClient: extensionsClient,
51+
ImageName: imageName,
4552
}
4653
return nil
4754
}
Lines changed: 156 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,169 @@
11
package framework
22

33
import (
4-
"testing"
4+
"errors"
5+
"strings"
56

7+
y2j "github.com/ghodss/yaml"
8+
yaml "gopkg.in/yaml.v2"
9+
apps "k8s.io/api/apps/v1"
610
core "k8s.io/api/core/v1"
11+
"k8s.io/api/rbac/v1beta1"
12+
crd "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
13+
extensions_scheme "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
14+
apierrors "k8s.io/apimachinery/pkg/api/errors"
715
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
16+
"k8s.io/apimachinery/pkg/runtime/schema"
17+
"k8s.io/apimachinery/pkg/runtime/serializer"
18+
"k8s.io/client-go/kubernetes/scheme"
19+
"k8s.io/client-go/rest"
820
)
921

10-
func (ctx *TestCtx) CreateNamespace(f *Framework, t *testing.T) (string, error) {
22+
func (ctx *TestCtx) GetNamespace() (string, error) {
23+
if ctx.Namespace != "" {
24+
return ctx.Namespace, nil
25+
}
1126
// create namespace
12-
namespace := ctx.GetObjID()
13-
namespaceObj := &core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
14-
_, err := f.KubeClient.CoreV1().Namespaces().Create(namespaceObj)
27+
ctx.Namespace = ctx.GetID()
28+
namespaceObj := &core.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ctx.Namespace}}
29+
_, err := Global.KubeClient.CoreV1().Namespaces().Create(namespaceObj)
1530
if err != nil {
1631
return "", err
1732
}
18-
t.Logf("Created namespace: %s", namespace)
19-
ctx.AddFinalizerFn(func() error { return f.KubeClient.CoreV1().Namespaces().Delete(namespace, metav1.NewDeleteOptions(0)) })
20-
return namespace, nil
33+
ctx.AddFinalizerFn(func() error {
34+
return Global.KubeClient.CoreV1().Namespaces().Delete(ctx.Namespace, metav1.NewDeleteOptions(0))
35+
})
36+
return ctx.Namespace, nil
37+
}
38+
39+
func (ctx *TestCtx) GetCRClient(yamlCR []byte) (*rest.RESTClient, error) {
40+
if ctx.CRClient != nil {
41+
return ctx.CRClient, nil
42+
}
43+
// get new RESTClient for custom resources
44+
crConfig := Global.KubeConfig
45+
yamlMap := make(map[interface{}]interface{})
46+
err := yaml.Unmarshal(yamlCR, &yamlMap)
47+
if err != nil {
48+
return nil, err
49+
}
50+
groupVersion := strings.Split(yamlMap["apiVersion"].(string), "/")
51+
crGV := schema.GroupVersion{Group: groupVersion[0], Version: groupVersion[1]}
52+
crConfig.GroupVersion = &crGV
53+
crConfig.APIPath = "/apis"
54+
crConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
55+
56+
if crConfig.UserAgent == "" {
57+
crConfig.UserAgent = rest.DefaultKubernetesUserAgent()
58+
}
59+
ctx.CRClient, err = rest.RESTClientFor(crConfig)
60+
if err != nil {
61+
return nil, err
62+
}
63+
return ctx.CRClient, nil
64+
}
65+
66+
func (ctx *TestCtx) createCRFromYAML(yamlFile []byte, resourceName string) error {
67+
client, err := ctx.GetCRClient(yamlFile)
68+
if err != nil {
69+
return err
70+
}
71+
namespace, err := ctx.GetNamespace()
72+
if err != nil {
73+
return err
74+
}
75+
jsonDat, err := y2j.YAMLToJSON(yamlFile)
76+
err = client.Post().
77+
Namespace(namespace).
78+
Resource(resourceName).
79+
Body(jsonDat).
80+
Do().
81+
Error()
82+
ctx.AddFinalizerFn(func() error {
83+
return client.Delete().
84+
Namespace(namespace).
85+
Resource(resourceName).
86+
Body(metav1.NewDeleteOptions(0)).
87+
Do().
88+
Error()
89+
})
90+
return err
91+
}
92+
93+
func (ctx *TestCtx) createCRDFromYAML(yamlFile []byte) error {
94+
decode := extensions_scheme.Codecs.UniversalDeserializer().Decode
95+
obj, _, err := decode(yamlFile, nil, nil)
96+
if err != nil {
97+
return err
98+
}
99+
switch o := obj.(type) {
100+
case *crd.CustomResourceDefinition:
101+
_, err = Global.ExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(o)
102+
ctx.AddFinalizerFn(func() error {
103+
err = Global.ExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Delete(o.Name, metav1.NewDeleteOptions(0))
104+
if err != nil && !apierrors.IsNotFound(err) {
105+
return err
106+
}
107+
return nil
108+
})
109+
if apierrors.IsAlreadyExists(err) {
110+
return nil
111+
}
112+
return err
113+
default:
114+
return errors.New("Non-CRD resource in createCRDFromYAML function")
115+
}
116+
}
117+
118+
func (ctx *TestCtx) CreateFromYAML(yamlFile []byte) error {
119+
yamlMap := make(map[interface{}]interface{})
120+
err := yaml.Unmarshal(yamlFile, &yamlMap)
121+
if err != nil {
122+
return err
123+
}
124+
kind := yamlMap["kind"].(string)
125+
switch kind {
126+
case "Role":
127+
case "RoleBinding":
128+
case "Deployment":
129+
case "CustomResourceDefinition":
130+
return ctx.createCRDFromYAML(yamlFile)
131+
// we assume that all custom resources are from operator-sdk and thus follow
132+
// a common naming convention
133+
default:
134+
return ctx.createCRFromYAML(yamlFile, strings.ToLower(kind)+"s")
135+
}
136+
137+
decode := scheme.Codecs.UniversalDeserializer().Decode
138+
obj, _, err := decode(yamlFile, nil, nil)
139+
if err != nil {
140+
return err
141+
}
142+
143+
namespace, err := ctx.GetNamespace()
144+
if err != nil {
145+
return err
146+
}
147+
switch o := obj.(type) {
148+
case *v1beta1.Role:
149+
_, err = Global.KubeClient.RbacV1beta1().Roles(namespace).Create(o)
150+
ctx.AddFinalizerFn(func() error {
151+
return Global.KubeClient.RbacV1beta1().Roles(namespace).Delete(o.Name, metav1.NewDeleteOptions(0))
152+
})
153+
return err
154+
case *v1beta1.RoleBinding:
155+
_, err = Global.KubeClient.RbacV1beta1().RoleBindings(namespace).Create(o)
156+
ctx.AddFinalizerFn(func() error {
157+
return Global.KubeClient.RbacV1beta1().RoleBindings(namespace).Delete(o.Name, metav1.NewDeleteOptions(0))
158+
})
159+
return err
160+
case *apps.Deployment:
161+
_, err = Global.KubeClient.AppsV1().Deployments(namespace).Create(o)
162+
ctx.AddFinalizerFn(func() error {
163+
return Global.KubeClient.AppsV1().Deployments(namespace).Delete(o.Name, metav1.NewDeleteOptions(0))
164+
})
165+
return err
166+
default:
167+
return errors.New("Unhandled resource type")
168+
}
21169
}

0 commit comments

Comments
 (0)