Skip to content

Commit 68b7e16

Browse files
author
Amit Kumar Das
authored
feat(test): testing is never easy (#58)
This commit is a step to make testing simple. - This is all about simplified testing - This caters to integration as well as e2e testing - Tests are implemented as custom resources - No shell or bash scripting - No grep, awk or sed - No timing, retries, teardown - Knowledge of kubectl will help mage testprep cd test go run suite.go - Build the binary of your code - Copy the binary to test/bin - Implement test cases as YAMLs inside test/experiments - Create test manifests i.e. kubernetes yamls required to start testing - Put them under test/manifests - Which kind of kubernetes setup do you need for these tests to run? - Specify that in test/setup/controlplane - controlplane setup is supported currently - k3s, kind, k3d, etc setups will be supported in near future Q/ What is a control plane setup? A/ This is a kubernetes setup with kube apiserver, etcd & kubectl binaries that talk to each other. Following is an example, where I want to test my binary against a kubernetes control plane setup. apiVersion: v1.0.0 kind: TestOnControlPlane metadata: name: test-dope-on-ctrl-plane spec: target: binary: path: bin name: dope args: - --logtostderr - --alsologtostderr - --run-as-local - --v=2 - --discovery-interval=40s - --cache-flush-interval=240s - --metac-config-path=../config/ kubeAPIServerURLFlag: --kube-apiserver-url deploy: path: "../manifests" test: deploy: path: manifests experiments: path: experiments inference: experimentName: inference experimentNamespace: d-testing displaySelector: matchLabels: d-testing.metacontroller.app/enabled: "true" Signed-off-by: AmitKumarDas <[email protected]>
1 parent db6f82f commit 68b7e16

20 files changed

+1078
-992
lines changed

pkg/job/job.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ type Runner struct {
4545
// NewRunner returns a new instance of Runner
4646
func NewRunner(config RunnerConfig) *Runner {
4747
var isTearDown bool
48-
if config.Job.JobSpec.Teardown != nil {
49-
isTearDown = *config.Job.JobSpec.Teardown
48+
if config.Job.Spec.Teardown != nil {
49+
isTearDown = *config.Job.Spec.Teardown
5050
}
5151
return &Runner{
5252
isTearDown: isTearDown,
@@ -58,10 +58,10 @@ func NewRunner(config RunnerConfig) *Runner {
5858
}
5959

6060
func (r *Runner) init() {
61-
if r.Job.JobSpec.Enabled == nil {
61+
if r.Job.Spec.Enabled == nil {
6262
r.when = types.Once
6363
} else {
64-
r.when = r.Job.JobSpec.Enabled.When
64+
r.when = r.Job.Spec.Enabled.When
6565
}
6666
}
6767

@@ -151,13 +151,13 @@ func (r *Runner) buildLockRunner() *LockRunner {
151151
Task: lock,
152152
LockForever: isLockForever,
153153
Retry: NewRetry(RetryConfig{}),
154-
ProtectedTaskCount: len(r.Job.JobSpec.Tasks),
154+
ProtectedTaskCount: len(r.Job.Spec.Tasks),
155155
}
156156
}
157157

158158
// evalAll evaluates all tasks
159159
func (r *Runner) evalAll() error {
160-
for _, task := range r.Job.JobSpec.Tasks {
160+
for _, task := range r.Job.Spec.Tasks {
161161
err := r.eval(task)
162162
if err != nil {
163163
return err
@@ -197,7 +197,7 @@ func (r *Runner) runAll() (status *types.JobStatus, err error) {
197197
r.fixture.TearDown()
198198
}()
199199
var failedTasks int
200-
for idx, task := range r.Job.JobSpec.Tasks {
200+
for idx, task := range r.Job.Spec.Tasks {
201201
tr := &TaskRunner{
202202
Fixture: r.fixture,
203203
TaskIndex: idx + 1,
@@ -225,7 +225,7 @@ func (r *Runner) runAll() (status *types.JobStatus, err error) {
225225
} else {
226226
r.JobStatus.Phase = r.mayBePassedOrCompletedStatus()
227227
}
228-
r.JobStatus.TaskCount = len(r.Job.JobSpec.Tasks)
228+
r.JobStatus.TaskCount = len(r.Job.Spec.Tasks)
229229
return r.JobStatus, nil
230230
}
231231

File renamed without changes.

test/manifests/namespace.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: d-testing

test/pkg/common/common.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package common
2+
3+
import (
4+
"github.com/pkg/errors"
5+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
6+
"k8s.io/apimachinery/pkg/runtime"
7+
)
8+
9+
// CMDArgs defines a command along with its arguments
10+
type CMDArgs struct {
11+
CMD string
12+
Args []string
13+
}
14+
15+
// UnstructList defines an array of unstructured instances
16+
type UnstructList []unstructured.Unstructured
17+
18+
// ToTyped transforms the provided unstruct instance
19+
// to target type
20+
func ToTyped(src *unstructured.Unstructured, target interface{}) error {
21+
if src == nil || src.Object == nil {
22+
return errors.Errorf(
23+
"Can't transform unstruct to typed: Nil unstruct content",
24+
)
25+
}
26+
if target == nil {
27+
return errors.Errorf(
28+
"Can't transform unstruct to typed: Nil target",
29+
)
30+
}
31+
return runtime.DefaultUnstructuredConverter.FromUnstructured(
32+
src.UnstructuredContent(),
33+
target,
34+
)
35+
}

test/pkg/common/setup_loader.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package common
2+
3+
import (
4+
"io/ioutil"
5+
"path/filepath"
6+
"strings"
7+
8+
"github.com/pkg/errors"
9+
"k8s.io/klog/v2"
10+
)
11+
12+
// SetupLoader loads platform setup files
13+
type SetupLoader struct {
14+
Path string
15+
}
16+
17+
// Load loads all platform setup files and uses populator
18+
// as a callback to operate against these loaded files.
19+
//
20+
// NOTE:
21+
// Argument 'populator' is invoked for every valid file
22+
// found in the configured path
23+
func (l *SetupLoader) Load(populator func([]byte) error) error {
24+
klog.V(3).Infof(
25+
"Will load & populate setup file(s) at path %q", l.Path,
26+
)
27+
28+
files, err := ioutil.ReadDir(l.Path)
29+
if err != nil {
30+
return err
31+
}
32+
33+
if len(files) == 0 {
34+
return errors.Errorf(
35+
"No setup files(s) were found at %q", l.Path,
36+
)
37+
}
38+
39+
// there can be multiple config files
40+
for _, file := range files {
41+
fileName := file.Name()
42+
if file.IsDir() || file.Mode().IsDir() {
43+
klog.V(4).Infof(
44+
"Will skip setup file %q at path %q: Not a file",
45+
fileName,
46+
l.Path,
47+
)
48+
// we don't want to load directory
49+
continue
50+
}
51+
if !strings.HasSuffix(fileName, ".yaml") &&
52+
!strings.HasSuffix(fileName, ".json") {
53+
klog.V(4).Infof(
54+
"Will skip setup file %q at path %q: Not yaml or json",
55+
fileName,
56+
l.Path,
57+
)
58+
continue
59+
}
60+
// load the file
61+
fileNameWithPath := filepath.Join(l.Path, fileName)
62+
content, err := ioutil.ReadFile(fileNameWithPath)
63+
if err != nil {
64+
return errors.Wrapf(
65+
err,
66+
"Failed to load setup file %q",
67+
fileNameWithPath,
68+
)
69+
}
70+
// poluate the loaded content using populator callback
71+
err = populator(content)
72+
if err != nil {
73+
return errors.Wrapf(
74+
err,
75+
"Failed to populate from setup file %q",
76+
fileNameWithPath,
77+
)
78+
}
79+
klog.V(3).Infof(
80+
"Setup file %q was loaded & populated successfully", fileNameWithPath,
81+
)
82+
}
83+
84+
klog.V(3).Infof(
85+
"Setup files(s) at path %q were loaded & populated successfully",
86+
l.Path,
87+
)
88+
return nil
89+
}

test/pkg/runner/loader.go

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

0 commit comments

Comments
 (0)