Skip to content

Commit 4565af3

Browse files
authored
commands/.../test/local,pkg/test: add no-setup flag (#770)
* commands/.../test/local,pkg/test: add `no-setup` flag
1 parent 1b1ebc0 commit 4565af3

File tree

8 files changed

+104
-42
lines changed

8 files changed

+104
-42
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
### Added
99

1010
- A new command [`operator-sdk print-deps`](https://github.com/operator-framework/operator-sdk/blob/master/doc/sdk-cli-reference.md#print-deps) which prints Golang packages and versions expected by the current Operator SDK version. Supplying `--as-file` prints packages and versions in Gopkg.toml format. ([#772](https://github.com/operator-framework/operator-sdk/pull/772))
11+
- Add [`no-setup`](https://github.com/operator-framework/operator-sdk/blob/master/doc/sdk-cli-reference.md#flags-9) flag to `test local` subcommand ([#770](https://github.com/operator-framework/operator-sdk/pull/770))
1112
- Add [`image`](https://github.com/operator-framework/operator-sdk/blob/master/doc/sdk-cli-reference.md#flags-9) flag to `test local` subcommand ([#768](https://github.com/operator-framework/operator-sdk/pull/768))
1213
- Ansible Operator log output includes much more information for troubleshooting ansible errors. ([#713](https://github.com/operator-framework/operator-sdk/pull/713))
1314
- Ansible Operator periodic reconciliation can be disabled ([#739](https://github.com/operator-framework/operator-sdk/pull/739))

commands/operator-sdk/cmd/test/local.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ type testLocalConfig struct {
4545
namespacedManPath string
4646
goTestFlags string
4747
namespace string
48+
noSetup bool
4849
image string
4950
}
5051

@@ -61,6 +62,7 @@ func NewTestLocalCmd() *cobra.Command {
6162
testCmd.Flags().StringVar(&tlConfig.namespacedManPath, "namespaced-manifest", "", "Path to manifest for per-test, namespaced resources (e.g. RBAC and Operator manifest)")
6263
testCmd.Flags().StringVar(&tlConfig.goTestFlags, "go-test-flags", "", "Additional flags to pass to go test")
6364
testCmd.Flags().StringVar(&tlConfig.namespace, "namespace", "", "If non-empty, single namespace to run tests in")
65+
testCmd.Flags().BoolVar(&tlConfig.noSetup, "no-setup", false, "Disable test resource creation")
6466
testCmd.Flags().StringVar(&tlConfig.image, "image", "", "Use a different operator image from the one specified in the namespaced manifest")
6567

6668
return testCmd
@@ -70,11 +72,14 @@ func testLocalFunc(cmd *cobra.Command, args []string) {
7072
if len(args) != 1 {
7173
log.Fatal("operator-sdk test local requires exactly 1 argument")
7274
}
75+
if (tlConfig.noSetup && tlConfig.globalManPath != "") || (tlConfig.noSetup && tlConfig.namespacedManPath != "") {
76+
log.Fatal("the global-manifest and namespaced-manifest flags cannot be enabled at the same time as the no-setup flag")
77+
}
7378

7479
log.Info("Testing operator locally.")
7580

7681
// if no namespaced manifest path is given, combine deploy/service_account.yaml, deploy/role.yaml, deploy/role_binding.yaml and deploy/operator.yaml
77-
if tlConfig.namespacedManPath == "" {
82+
if tlConfig.namespacedManPath == "" && !tlConfig.noSetup {
7883
err := os.MkdirAll(deployTestDir, os.FileMode(fileutil.DefaultDirFileMode))
7984
if err != nil {
8085
log.Fatalf("could not create %s: (%v)", deployTestDir, err)
@@ -113,7 +118,7 @@ func testLocalFunc(cmd *cobra.Command, args []string) {
113118
}
114119
}()
115120
}
116-
if tlConfig.globalManPath == "" {
121+
if tlConfig.globalManPath == "" && !tlConfig.noSetup {
117122
err := os.MkdirAll(deployTestDir, os.FileMode(fileutil.DefaultDirFileMode))
118123
if err != nil {
119124
log.Fatalf("could not create %s: (%v)", deployTestDir, err)
@@ -149,6 +154,25 @@ func testLocalFunc(cmd *cobra.Command, args []string) {
149154
}
150155
}()
151156
}
157+
if tlConfig.noSetup {
158+
err := os.MkdirAll(deployTestDir, os.FileMode(fileutil.DefaultDirFileMode))
159+
if err != nil {
160+
log.Fatalf("could not create %s: (%v)", deployTestDir, err)
161+
}
162+
tlConfig.namespacedManPath = filepath.Join(deployTestDir, "empty.yaml")
163+
tlConfig.globalManPath = filepath.Join(deployTestDir, "empty.yaml")
164+
emptyBytes := []byte{}
165+
err = ioutil.WriteFile(tlConfig.globalManPath, emptyBytes, os.FileMode(fileutil.DefaultFileMode))
166+
if err != nil {
167+
log.Fatalf("could not create empty manifest file: (%v)", err)
168+
}
169+
defer func() {
170+
err := os.Remove(tlConfig.globalManPath)
171+
if err != nil {
172+
log.Fatalf("could not delete empty manifest file: (%v)", err)
173+
}
174+
}()
175+
}
152176
if tlConfig.image != "" {
153177
if err := replaceImage(tlConfig.namespacedManPath, tlConfig.image); err != nil {
154178
log.Fatalf("failed to overwrite operator image in the namespaced manifest: %v", err)
@@ -164,7 +188,7 @@ func testLocalFunc(cmd *cobra.Command, args []string) {
164188
if tlConfig.goTestFlags != "" {
165189
testArgs = append(testArgs, strings.Split(tlConfig.goTestFlags, " ")...)
166190
}
167-
if tlConfig.namespace != "" {
191+
if tlConfig.namespace != "" || tlConfig.noSetup {
168192
testArgs = append(testArgs, "-"+test.SingleNamespaceFlag, "-parallel=1")
169193
}
170194
dc := exec.Command("go", testArgs...)

doc/sdk-cli-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ Runs the tests locally
257257
* `--namespaced-manifest` string - path to manifest for per-test, namespaced resources (default: combines deploy/service_account.yaml, deploy/rbac.yaml, and deploy/operator.yaml)
258258
* `--namespace` string - if non-empty, single namespace to run tests in (e.g. "operator-test") (default: "")
259259
* `--go-test-flags` string - extra arguments to pass to `go test` (e.g. -f "-v -parallel=2")
260+
* `--no-setup` bool - disable test resource creation
260261
* `--image` string - use a different operator image from the one specified in the namespaced manifest
261262
* `-h, --help` - help for local
262263

doc/test-framework/writing-e2e-tests.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,17 @@ $ kubectl create namespace operator-test
250250
$ operator-sdk test local ./test/e2e --namespace operator-test
251251
```
252252

253+
If you would prefer to create the resources yourself and skip resource creation, you can use the `--no-setup` flag:
254+
```shell
255+
$ kubectl create namespace operator-test
256+
$ kubectl create -f deploy/crds/cache_v1alpha1_memcached_crd.yaml
257+
$ kubectl create -f deploy/service_account.yaml --namespace operator-test
258+
$ kubectl create -f deploy/role.yaml --namespace operator-test
259+
$ kubectl create -f deploy/role_binding.yaml --namespace operator-test
260+
$ kubectl create -f deploy/operator.yaml --namespace operator-test
261+
$ operator-sdk test local ./test/e2e --namespace operator-test --no-setup
262+
```
263+
253264
For more documentation on the `operator-sdk test local` command, see the [SDK CLI Reference][sdk-cli-ref] doc.
254265

255266
For advanced use cases, it is possible to run the tests via `go test` directly. As long as all flags defined

hack/lib/test_lib.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#!/usr/bin/env bash
2+
13
function listPkgs() {
24
go list ./commands/... ./pkg/... ./test/... | grep -v generated
35
}
@@ -13,3 +15,34 @@ function listFiles() {
1315
# pipeline is much faster than for loop
1416
listPkgs | xargs -I {} find "${GOPATH}/src/{}" -name '*.go' | grep -v generated
1517
}
18+
19+
#===================================================================
20+
# FUNCTION trap_add ()
21+
#
22+
# Purpose: prepends a command to a trap
23+
#
24+
# - 1st arg: code to add
25+
# - remaining args: names of traps to modify
26+
#
27+
# Example: trap_add 'echo "in trap DEBUG"' DEBUG
28+
#
29+
# See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal
30+
#===================================================================
31+
function trap_add() {
32+
trap_add_cmd=$1; shift || fatal "${FUNCNAME} usage error"
33+
new_cmd=
34+
for trap_add_name in "$@"; do
35+
# Grab the currently defined trap commands for this trap
36+
existing_cmd=`trap -p "${trap_add_name}" | awk -F"'" '{print $2}'`
37+
38+
# Define default command
39+
[ -z "${existing_cmd}" ] && existing_cmd="echo exiting @ `date`"
40+
41+
# Generate the new command
42+
new_cmd="${trap_add_cmd};${existing_cmd}"
43+
44+
# Assign the test
45+
trap "${new_cmd}" "${trap_add_name}" || \
46+
fatal "unable to add to trap ${trap_add_name}"
47+
done
48+
}

hack/tests/e2e-ansible.sh

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,6 @@
11
#!/usr/bin/env bash
22

3-
#===================================================================
4-
# FUNCTION trap_add ()
5-
#
6-
# Purpose: prepends a command to a trap
7-
#
8-
# - 1st arg: code to add
9-
# - remaining args: names of traps to modify
10-
#
11-
# Example: trap_add 'echo "in trap DEBUG"' DEBUG
12-
#
13-
# See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal
14-
#===================================================================
15-
trap_add() {
16-
trap_add_cmd=$1; shift || fatal "${FUNCNAME} usage error"
17-
new_cmd=
18-
for trap_add_name in "$@"; do
19-
# Grab the currently defined trap commands for this trap
20-
existing_cmd=`trap -p "${trap_add_name}" | awk -F"'" '{print $2}'`
21-
22-
# Define default command
23-
[ -z "${existing_cmd}" ] && existing_cmd="echo exiting @ `date`"
24-
25-
# Generate the new command
26-
new_cmd="${trap_add_cmd};${existing_cmd}"
27-
28-
# Assign the test
29-
trap "${new_cmd}" "${trap_add_name}" || \
30-
fatal "unable to add to trap ${trap_add_name}"
31-
done
32-
}
3+
source hack/lib/test_lib.sh
334

345
DEST_IMAGE="quay.io/example/memcached-operator:v0.0.2"
356

hack/tests/test-subcommand.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
11
#!/usr/bin/env bash
2+
source hack/lib/test_lib.sh
3+
24
set -ex
35

4-
cd test/test-framework
6+
pushd test/test-framework
57
# test framework with defaults
68
operator-sdk test local .
79
# test operator-sdk test flags
810
operator-sdk test local . --global-manifest deploy/crds/cache_v1alpha1_memcached_crd.yaml --namespaced-manifest deploy/namespace-init.yaml --go-test-flags "-parallel 1" --kubeconfig $HOME/.kube/config --image=quay.io/coreos/operator-sdk-dev:test-framework-operator-runtime
911
# test operator-sdk test local single namespace mode
1012
kubectl create namespace test-memcached
13+
# we use the test-memcached namespace for all future tests, so we only need to set this trap once
14+
trap_add 'kubectl delete namespace test-memcached || true' EXIT
1115
operator-sdk test local . --namespace=test-memcached
1216
kubectl delete namespace test-memcached
17+
# test operator in no-setup mode
18+
kubectl create namespace test-memcached
19+
kubectl create -f deploy/crds/cache_v1alpha1_memcached_crd.yaml
20+
# this runs after the popd at the end, so it needs the path from the project root
21+
trap_add 'kubectl delete -f test/test-framework/deploy/crds/cache_v1alpha1_memcached_crd.yaml' EXIT
22+
kubectl create -f deploy/service_account.yaml --namespace test-memcached
23+
kubectl create -f deploy/role.yaml --namespace test-memcached
24+
kubectl create -f deploy/role_binding.yaml --namespace test-memcached
25+
kubectl create -f deploy/operator.yaml --namespace test-memcached
26+
operator-sdk test local . --namespace=test-memcached --no-setup
27+
kubectl delete namespace test-memcached
28+
popd

pkg/test/framework.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ type Framework struct {
5757
}
5858

5959
func setup(kubeconfigPath, namespacedManPath *string) error {
60+
namespace := ""
61+
if *singleNamespace {
62+
namespace = os.Getenv(TestNamespaceEnv)
63+
}
6064
var err error
6165
var kubeconfig *rest.Config
6266
if *kubeconfigPath == "incluster" {
@@ -73,8 +77,16 @@ func setup(kubeconfigPath, namespacedManPath *string) error {
7377
}
7478
kubeconfig, err = rest.InClusterConfig()
7579
*singleNamespace = true
80+
namespace = os.Getenv(TestNamespaceEnv)
81+
if len(namespace) == 0 {
82+
return fmt.Errorf("test namespace env not set")
83+
}
7684
} else {
77-
kubeconfig, _, err = k8sInternal.GetKubeconfigAndNamespace(*kubeconfigPath)
85+
var kcNamespace string
86+
kubeconfig, kcNamespace, err = k8sInternal.GetKubeconfigAndNamespace(*kubeconfigPath)
87+
if *singleNamespace && namespace == "" {
88+
namespace = kcNamespace
89+
}
7890
}
7991
if err != nil {
8092
return fmt.Errorf("failed to build the kubeconfig: %v", err)
@@ -91,13 +103,6 @@ func setup(kubeconfigPath, namespacedManPath *string) error {
91103
return fmt.Errorf("failed to build the dynamic client: %v", err)
92104
}
93105
dynamicDecoder = serializer.NewCodecFactory(scheme).UniversalDeserializer()
94-
namespace := ""
95-
if *singleNamespace {
96-
namespace = os.Getenv(TestNamespaceEnv)
97-
if len(namespace) == 0 {
98-
return fmt.Errorf("namespace set in %s cannot be empty", TestNamespaceEnv)
99-
}
100-
}
101106
Global = &Framework{
102107
Client: &frameworkClient{Client: dynClient},
103108
KubeConfig: kubeconfig,

0 commit comments

Comments
 (0)