Skip to content
This repository was archived by the owner on Mar 25, 2024. It is now read-only.

Commit b14c391

Browse files
authored
Merge pull request #53 from luthermonson/e2e-testing
Adding e2e Testing
2 parents e094f73 + 0954d5e commit b14c391

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+14660
-43
lines changed

Dockerfile.dapper

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ RUN if [ "${ARCH}" == "amd64" ]; then \
1717
curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.15.0; \
1818
fi
1919

20+
ENV K3S_VERSION v0.6.1
21+
#integration tests only support amd64
22+
RUN if [ "${ARCH}" == "amd64" ]; then \
23+
curl -sL https://github.com/rancher/k3s/releases/download/${K3S_VERSION}/k3s > /usr/bin/k3s \
24+
&& chmod +x /usr/bin/k3s; \
25+
fi
26+
27+
ENV DAPPER_RUN_ARGS --privileged
28+
VOLUME /var/lib/rancher/k3s
29+
VOLUME /var/lib/cni
30+
VOLUME /var/log
31+
2032
ENV DAPPER_ENV REPO TAG DRONE_TAG
2133
ENV DAPPER_SOURCE /go/src/github.com/rancher/terraform-controller/
2234
ENV DAPPER_OUTPUT ./bin ./dist

e2e/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# End to End Testing
2+
3+
This folder contains all End to End (e2e) testing code
4+
5+
## Prerequisites
6+
7+
- The e2e tests require you to run `make build-controller` at least once because the binary `./bin/terraform-controller` needs to be available to the e2e tests.
8+
- These tests are expecting a new clean k8s cluster and does not clean up after itself so it is expected that the cluster will be thrown away after e2e tests are complete. You will want to look at k3s/k3d to make this process smoother. Using the `make` scripts are the best way to run these and you are using them at your own risk on an existing cluster.
9+
10+
## Usage
11+
12+
To build/run evertything, use:
13+
14+
```
15+
make
16+
```
17+
18+
To target only the e2e tests, use:
19+
20+
```
21+
make build-controller #created ./bin/terraform-controller
22+
make e2e
23+
```
24+
25+
## Running Tests Locally with k3d
26+
27+
If you'd like to run against a local k3s, it is recommended that you use [k3d](https://github.com/rancher/k3d).
28+
29+
To boot a cluster, run:
30+
31+
```
32+
k3d create --name e2e
33+
```
34+
35+
Then use the provided config to boot the controller. You can use a command like this:
36+
37+
```
38+
KUBECONFIG="$(k3d get-kubeconfig --name='e2e')" ./terraform-controller --threads 1
39+
```
40+
41+
After the controller is running locally, you can run the tests in the same manner:
42+
43+
```
44+
KUBECONFIG="$(k3d get-kubeconfig --name='e2e')" go test -json -count=1 ./e2e/...
45+
```
46+
47+
The `count=1` option is needed because tests will be cached if the code doesn't change, even though you are running the tests against a new cluster with no data. In this case, you would want to re-run the tests for a new cluster.
48+
49+
# Initializing
50+
51+
There is an initilization process which mimics `kubectl create -f ./manifests`. This process assumes that you are running a new k3s server, and tests could fail if it tries to make something that already exists. Therefore, if you are doing local development of the e2e tests, it is recommended to delete and recreate your k3s cluster before performing another run.
52+
53+
To automate the process of deleting your k3s cluster before you recreate it, you can use a Run/Debug configuration in Goland or use a one-line shell tool.
54+
55+
# Terraform Module
56+
57+
We use a [test module](https://github.com/luthermonson/terraform-controller-test-module) to test executions. It uses the k8s Terraform provider and creates a ConfigMap. The e2e tests validate that they were created.
58+
59+
Please note that the e2e test module uses the default k3s setting to boot the API server on https://10.43.0.1 as seen [here](https://github.com/luthermonson/terraform-controller-test-module/blob/master/main.tf#L5). If you change the `--api-port` setting, you will need to change this line to match.

e2e/e2e.go

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
package e2e
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
tfv1 "github.com/rancher/terraform-controller/pkg/apis/terraformcontroller.cattle.io/v1"
8+
tf "github.com/rancher/terraform-controller/pkg/generated/clientset/versioned/typed/terraformcontroller.cattle.io/v1"
9+
"github.com/rancher/wrangler/pkg/crd"
10+
"github.com/rancher/wrangler/pkg/signals"
11+
"github.com/sirupsen/logrus"
12+
v1 "k8s.io/api/core/v1"
13+
"k8s.io/apimachinery/pkg/api/errors"
14+
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/apimachinery/pkg/util/wait"
16+
"k8s.io/client-go/kubernetes"
17+
"k8s.io/client-go/rest"
18+
"k8s.io/client-go/tools/clientcmd"
19+
)
20+
21+
type E2E struct {
22+
ctx context.Context
23+
cs *kubernetes.Clientset
24+
cfg *rest.Config
25+
kubeconfig string
26+
namespace string
27+
module_url string
28+
crds []crd.CRD
29+
}
30+
31+
func NewE2E(namespace, kubeconfig, module string, crdsNames []string) *E2E {
32+
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
33+
if err != nil {
34+
logrus.Fatalf("Error building kubeconfig: %s", err.Error())
35+
}
36+
37+
cs, err := kubernetes.NewForConfig(cfg)
38+
if err != nil {
39+
logrus.Fatalf("Error building kubeconfig: %s", err.Error())
40+
}
41+
42+
var crds = make([]crd.CRD, len(crdsNames))
43+
for k, v := range crdsNames {
44+
crds[k] = crd.FromGV(tfv1.SchemeGroupVersion, v)
45+
}
46+
47+
return &E2E{
48+
ctx: signals.SetupSignalHandler(context.Background()),
49+
cs: cs,
50+
cfg: cfg,
51+
kubeconfig: kubeconfig,
52+
namespace: namespace,
53+
module_url: module,
54+
crds: crds,
55+
}
56+
}
57+
58+
func (e *E2E) createState() error {
59+
cs, err := tf.NewForConfig(e.cfg)
60+
if err != nil {
61+
return err
62+
}
63+
64+
_, err = cs.States(e.namespace).Create(e.getState())
65+
if err != nil {
66+
return err
67+
}
68+
69+
return nil
70+
}
71+
72+
func (e *E2E) createModule() error {
73+
cs, err := tf.NewForConfig(e.cfg)
74+
if err != nil {
75+
return err
76+
}
77+
78+
_, err = cs.Modules(e.namespace).Create(e.getModule())
79+
if err != nil {
80+
return err
81+
}
82+
83+
err = wait.Poll(time.Second, 15*time.Second, func() (bool, error) {
84+
module, err := cs.Modules(e.namespace).Get(e.generateModuleName(), v12.GetOptions{})
85+
if err == nil && module.Status.ContentHash != "" {
86+
return true, nil
87+
}
88+
89+
if errors.IsNotFound(err) {
90+
return false, nil
91+
}
92+
93+
logrus.Printf("Waiting for Module to be processed by terraform-controller: %+v\n", err)
94+
return false, err
95+
})
96+
97+
if err != nil {
98+
return err
99+
}
100+
101+
return nil
102+
}
103+
104+
func (e *E2E) createVariables() error {
105+
_, err := e.cs.CoreV1().Secrets(e.namespace).Create(e.getSecret())
106+
if err != nil {
107+
return err
108+
}
109+
110+
_, err = e.cs.CoreV1().Secrets(e.namespace).Create(e.getSecretEnv())
111+
if err != nil {
112+
return err
113+
}
114+
115+
_, err = e.cs.CoreV1().ConfigMaps(e.namespace).Create(e.getConfigMap())
116+
if err != nil {
117+
return err
118+
}
119+
120+
_, err = e.cs.CoreV1().ConfigMaps(e.namespace).Create(e.getConfigMapEnv())
121+
if err != nil {
122+
return err
123+
}
124+
125+
return nil
126+
}
127+
128+
func (e *E2E) getState() *tfv1.State {
129+
return &tfv1.State{
130+
ObjectMeta: v12.ObjectMeta{
131+
Name: e.generateStateName(),
132+
Namespace: e.namespace,
133+
},
134+
Spec: tfv1.StateSpec{
135+
ModuleName: e.generateModuleName(),
136+
AutoConfirm: true,
137+
DestroyOnDelete: true,
138+
Image: "rancher/terraform-controller-executor:v0.0.10-alpha1",
139+
Variables: tfv1.Variables{
140+
ConfigNames: []string{e.generateConfigMapName()},
141+
EnvConfigName: []string{e.generateConfigMapEnvName()},
142+
SecretNames: []string{e.generateSecretName()},
143+
EnvSecretNames: []string{e.generateSecretEnvName()},
144+
},
145+
},
146+
}
147+
}
148+
149+
func (e *E2E) generateStateName() string {
150+
return e.namespace + "-state"
151+
}
152+
153+
func (e *E2E) generateModuleName() string {
154+
return e.namespace + "-module"
155+
}
156+
157+
func (e *E2E) generateSecretName() string {
158+
return e.namespace + "-secret"
159+
}
160+
161+
func (e *E2E) generateSecretEnvName() string {
162+
return e.namespace + "-secret-env"
163+
}
164+
165+
func (e *E2E) getSecret() *v1.Secret {
166+
return &v1.Secret{
167+
ObjectMeta: v12.ObjectMeta{
168+
Name: e.generateSecretName(),
169+
Namespace: e.namespace,
170+
},
171+
Type: "opaque",
172+
StringData: map[string]string{
173+
"test_secret": e.namespace,
174+
},
175+
}
176+
}
177+
178+
func (e *E2E) getSecretEnv() *v1.Secret {
179+
return &v1.Secret{
180+
ObjectMeta: v12.ObjectMeta{
181+
Name: e.generateSecretEnvName(),
182+
Namespace: e.namespace,
183+
},
184+
Type: "opaque",
185+
StringData: map[string]string{
186+
"test_secret_env": e.namespace,
187+
},
188+
}
189+
}
190+
191+
func (e *E2E) generateConfigMapName() string {
192+
return e.namespace + "-config-map"
193+
}
194+
195+
func (e *E2E) generateConfigMapEnvName() string {
196+
return e.namespace + "-config-map-env"
197+
}
198+
199+
func (e *E2E) getConfigMap() *v1.ConfigMap {
200+
return &v1.ConfigMap{
201+
ObjectMeta: v12.ObjectMeta{
202+
Name: e.generateConfigMapName(),
203+
Namespace: e.namespace,
204+
},
205+
Data: map[string]string{
206+
"test_config_map": e.namespace,
207+
},
208+
}
209+
}
210+
211+
func (e *E2E) getConfigMapEnv() *v1.ConfigMap {
212+
return &v1.ConfigMap{
213+
ObjectMeta: v12.ObjectMeta{
214+
Name: e.generateConfigMapEnvName(),
215+
Namespace: e.namespace,
216+
},
217+
Data: map[string]string{
218+
"test_config_map_env": e.namespace,
219+
},
220+
}
221+
}
222+
223+
func (e *E2E) getModule() *tfv1.Module {
224+
return &tfv1.Module{
225+
ObjectMeta: v12.ObjectMeta{
226+
Name: e.generateModuleName(),
227+
Namespace: e.namespace,
228+
},
229+
Spec: tfv1.ModuleSpec{
230+
ModuleContent: tfv1.ModuleContent{
231+
Git: tfv1.GitLocation{
232+
URL: e.module_url,
233+
},
234+
},
235+
},
236+
}
237+
}

0 commit comments

Comments
 (0)