Skip to content

Commit e401771

Browse files
committed
commands/.../test: create new test subcommands
1 parent afffc38 commit e401771

File tree

3 files changed

+221
-63
lines changed

3 files changed

+221
-63
lines changed

commands/operator-sdk/cmd/test.go

Lines changed: 7 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -15,76 +15,20 @@
1515
package cmd
1616

1717
import (
18-
"io/ioutil"
19-
"log"
20-
"os"
21-
"strings"
22-
23-
"github.com/operator-framework/operator-sdk/pkg/test"
18+
"github.com/operator-framework/operator-sdk/commands/operator-sdk/cmd/test"
2419

2520
"github.com/spf13/cobra"
2621
)
2722

28-
var (
29-
testLocation string
30-
kubeconfig string
31-
globalManifestPath string
32-
namespacedManifestPath string
33-
goTestFlags string
34-
)
35-
3623
func NewTestCmd() *cobra.Command {
3724
testCmd := &cobra.Command{
38-
Use: "test --test-location <path to tests directory> [flags]",
39-
Short: "Run End-To-End tests",
40-
Run: testFunc,
41-
}
42-
defaultKubeConfig := ""
43-
homedir, ok := os.LookupEnv("HOME")
44-
if ok {
45-
defaultKubeConfig = homedir + "/.kube/config"
25+
Use: "test",
26+
Short: "Tests the operator",
27+
Long: `The test command has subcommands that can test the operator locally or from within a cluster.
28+
`,
4629
}
47-
testCmd.Flags().StringVarP(&testLocation, "test-location", "t", "", "Location of test files (e.g. ./test/e2e/)")
48-
testCmd.MarkFlagRequired("test-location")
49-
testCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", defaultKubeConfig, "Kubeconfig path")
50-
testCmd.Flags().StringVarP(&globalManifestPath, "global-init", "g", "deploy/crd.yaml", "Path to manifest for Global resources (e.g. CRD manifest)")
51-
testCmd.Flags().StringVarP(&namespacedManifestPath, "namespaced-init", "n", "", "Path to manifest for per-test, namespaced resources (e.g. RBAC and Operator manifest)")
52-
testCmd.Flags().StringVarP(&goTestFlags, "go-test-flags", "f", "", "Additional flags to pass to go test")
5330

31+
testCmd.AddCommand(cmdtest.NewTestLocalCmd())
32+
testCmd.AddCommand(cmdtest.NewTestClusterCmd())
5433
return testCmd
5534
}
56-
57-
func testFunc(cmd *cobra.Command, args []string) {
58-
// if no namespaced manifest path is given, combine deploy/rbac.yaml and deploy/operator.yaml
59-
if namespacedManifestPath == "" {
60-
os.Mkdir("deploy/test", os.FileMode(int(0775)))
61-
namespacedManifestPath = "deploy/test/namespace-manifests.yaml"
62-
rbac, err := ioutil.ReadFile("deploy/rbac.yaml")
63-
if err != nil {
64-
log.Fatalf("could not find rbac manifest: %v", err)
65-
}
66-
operator, err := ioutil.ReadFile("deploy/operator.yaml")
67-
if err != nil {
68-
log.Fatalf("could not find operator manifest: %v", err)
69-
}
70-
combined := append(rbac, []byte("\n---\n")...)
71-
combined = append(combined, operator...)
72-
err = ioutil.WriteFile(namespacedManifestPath, combined, os.FileMode(int(0664)))
73-
if err != nil {
74-
log.Fatalf("could not create temporary namespaced manifest file: %v", err)
75-
}
76-
defer func() {
77-
err := os.Remove(namespacedManifestPath)
78-
if err != nil {
79-
log.Fatalf("could not delete temporary namespace manifest file")
80-
}
81-
}()
82-
}
83-
testArgs := []string{"test", testLocation + "/..."}
84-
testArgs = append(testArgs, "-"+test.KubeConfigFlag, kubeconfig)
85-
testArgs = append(testArgs, "-"+test.NamespacedManPathFlag, namespacedManifestPath)
86-
testArgs = append(testArgs, "-"+test.GlobalManPathFlag, globalManifestPath)
87-
testArgs = append(testArgs, "-"+test.ProjRootFlag, mustGetwd())
88-
testArgs = append(testArgs, strings.Split(goTestFlags, " ")...)
89-
execCmd(os.Stdout, "go", testArgs...)
90-
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright 2018 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cmdtest
16+
17+
import (
18+
"fmt"
19+
"os"
20+
"os/exec"
21+
"time"
22+
23+
cmdError "github.com/operator-framework/operator-sdk/commands/operator-sdk/error"
24+
25+
"github.com/spf13/cobra"
26+
v1 "k8s.io/api/core/v1"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/client-go/kubernetes"
29+
"k8s.io/client-go/tools/clientcmd"
30+
)
31+
32+
var (
33+
testNamespace string
34+
kubeconfigCluster string
35+
globalManifestPathCluster string
36+
)
37+
38+
func NewTestClusterCmd() *cobra.Command {
39+
testCmd := &cobra.Command{
40+
Use: "test --test-location <path to tests directory> [flags]",
41+
Short: "Run End-To-End tests",
42+
Run: testClusterFunc,
43+
}
44+
defaultKubeConfig := ""
45+
homedir, ok := os.LookupEnv("HOME")
46+
if ok {
47+
defaultKubeConfig = homedir + "/.kube/config"
48+
}
49+
testCmd.Flags().StringVarP(&testNamespace, "namespace", "n", "default", "Namespace to run tests in")
50+
testCmd.Flags().StringVarP(&kubeconfigCluster, "kubeconfig", "k", defaultKubeConfig, "Kubeconfig path")
51+
testCmd.Flags().StringVarP(&globalManifestPathCluster, "global-init", "g", "", "Path to manifest for Global resources (e.g. CRD manifest)")
52+
53+
return testCmd
54+
}
55+
56+
func testClusterFunc(cmd *cobra.Command, args []string) {
57+
if globalManifestPathCluster != "" {
58+
globalCmd := exec.Command("kubectl", "create", "-f", globalManifestPathCluster)
59+
cmdOut, err := globalCmd.CombinedOutput()
60+
if err != nil {
61+
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("Could not create global resources: %v\nKubectl Output: %v", err, cmdOut))
62+
}
63+
}
64+
testPod := &v1.Pod{
65+
ObjectMeta: metav1.ObjectMeta{
66+
Name: "operator-test",
67+
},
68+
Spec: v1.PodSpec{
69+
RestartPolicy: v1.RestartPolicyNever,
70+
Containers: []v1.Container{{
71+
Name: "operator-test",
72+
Image: args[0],
73+
ImagePullPolicy: v1.PullAlways,
74+
Command: []string{"/go-test.sh"},
75+
Env: []v1.EnvVar{{
76+
Name: "TEST_NAMESPACE",
77+
ValueFrom: &v1.EnvVarSource{FieldRef: &v1.ObjectFieldSelector{FieldPath: "metadata.namespace"}},
78+
}},
79+
}},
80+
},
81+
}
82+
kubeconfig, err := clientcmd.BuildConfigFromFlags("", kubeconfigCluster)
83+
if err != nil {
84+
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to get kubeconfig: %v", err))
85+
}
86+
kubeclient, err := kubernetes.NewForConfig(kubeconfig)
87+
if err != nil {
88+
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to create kubeclient: %v", err))
89+
}
90+
testPod, err = kubeclient.CoreV1().Pods(testNamespace).Create(testPod)
91+
if err != nil {
92+
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to create test pod: %v", err))
93+
}
94+
for {
95+
testPod, err = kubeclient.CoreV1().Pods(testNamespace).Get("operator-test", metav1.GetOptions{})
96+
if err != nil {
97+
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to get test pod: %v", err))
98+
}
99+
if testPod.Status.Phase != v1.PodSucceeded && testPod.Status.Phase != v1.PodFailed {
100+
time.Sleep(time.Second * 5)
101+
continue
102+
} else if testPod.Status.Phase == v1.PodSucceeded {
103+
fmt.Printf("Test Successfully Completed")
104+
return
105+
} else if testPod.Status.Phase == v1.PodFailed {
106+
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("Test Failed: %+v", kubeclient.CoreV1().Pods(testNamespace).GetLogs("operator-test", &v1.PodLogOptions{})))
107+
}
108+
}
109+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2018 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cmdtest
16+
17+
import (
18+
"fmt"
19+
"io/ioutil"
20+
"log"
21+
"os"
22+
"os/exec"
23+
"strings"
24+
25+
cmdError "github.com/operator-framework/operator-sdk/commands/operator-sdk/error"
26+
"github.com/operator-framework/operator-sdk/pkg/test"
27+
28+
"github.com/spf13/cobra"
29+
)
30+
31+
var (
32+
kubeconfig string
33+
globalManifestPath string
34+
namespacedManifestPath string
35+
goTestFlags string
36+
)
37+
38+
func NewTestLocalCmd() *cobra.Command {
39+
testCmd := &cobra.Command{
40+
Use: "test <path to tests directory> [flags]",
41+
Short: "Run End-To-End tests",
42+
Run: testLocalFunc,
43+
}
44+
defaultKubeConfig := ""
45+
homedir, ok := os.LookupEnv("HOME")
46+
if ok {
47+
defaultKubeConfig = homedir + "/.kube/config"
48+
}
49+
testCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", defaultKubeConfig, "Kubeconfig path")
50+
testCmd.Flags().StringVarP(&globalManifestPath, "global-init", "g", "deploy/crd.yaml", "Path to manifest for Global resources (e.g. CRD manifest)")
51+
testCmd.Flags().StringVarP(&namespacedManifestPath, "namespaced-init", "n", "", "Path to manifest for per-test, namespaced resources (e.g. RBAC and Operator manifest)")
52+
testCmd.Flags().StringVarP(&goTestFlags, "go-test-flags", "f", "", "Additional flags to pass to go test")
53+
54+
return testCmd
55+
}
56+
57+
func testLocalFunc(cmd *cobra.Command, args []string) {
58+
// if no namespaced manifest path is given, combine deploy/rbac.yaml and deploy/operator.yaml
59+
if namespacedManifestPath == "" {
60+
os.Mkdir("deploy/test", os.FileMode(int(0775)))
61+
namespacedManifestPath = "deploy/test/namespace-manifests.yaml"
62+
rbac, err := ioutil.ReadFile("deploy/rbac.yaml")
63+
if err != nil {
64+
log.Fatalf("could not find rbac manifest: %v", err)
65+
}
66+
operator, err := ioutil.ReadFile("deploy/operator.yaml")
67+
if err != nil {
68+
log.Fatalf("could not find operator manifest: %v", err)
69+
}
70+
combined := append(rbac, []byte("\n---\n")...)
71+
combined = append(combined, operator...)
72+
err = ioutil.WriteFile(namespacedManifestPath, combined, os.FileMode(int(0664)))
73+
if err != nil {
74+
log.Fatalf("could not create temporary namespaced manifest file: %v", err)
75+
}
76+
defer func() {
77+
err := os.Remove(namespacedManifestPath)
78+
if err != nil {
79+
log.Fatalf("could not delete temporary namespace manifest file")
80+
}
81+
}()
82+
}
83+
testArgs := []string{"test", args[0] + "/..."}
84+
testArgs = append(testArgs, "-"+test.KubeConfigFlag, kubeconfig)
85+
testArgs = append(testArgs, "-"+test.NamespacedManPathFlag, namespacedManifestPath)
86+
testArgs = append(testArgs, "-"+test.GlobalManPathFlag, globalManifestPath)
87+
testArgs = append(testArgs, "-"+test.ProjRootFlag, mustGetwd())
88+
testArgs = append(testArgs, strings.Split(goTestFlags, " ")...)
89+
dc := exec.Command("go", testArgs...)
90+
dc.Dir = mustGetwd()
91+
dc.Stdout = os.Stdout
92+
dc.Stderr = os.Stderr
93+
err := dc.Run()
94+
if err != nil {
95+
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to exec %s %#v: %v", cmd, args, err))
96+
}
97+
}
98+
99+
func mustGetwd() string {
100+
wd, err := os.Getwd()
101+
if err != nil {
102+
cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to determine the full path of the current directory: %v", err))
103+
}
104+
return wd
105+
}

0 commit comments

Comments
 (0)