Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

commands/.../test,pkg/test: add --up-local flag to test local #781

Merged
merged 13 commits into from
Nov 30, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
### Added

- 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))
- Add [`up-local`](https://github.com/operator-framework/operator-sdk/blob/master/doc/sdk-cli-reference.md#flags-9) flag to `test local` subcommand ([#781](https://github.com/operator-framework/operator-sdk/pull/781))
- 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))

### Bug fixes
Expand Down
49 changes: 49 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 31 additions & 21 deletions commands/operator-sdk/cmd/test/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type testLocalConfig struct {
namespacedManPath string
goTestFlags string
namespace string
upLocal bool
image string
}

Expand All @@ -61,6 +62,7 @@ func NewTestLocalCmd() *cobra.Command {
testCmd.Flags().StringVar(&tlConfig.namespacedManPath, "namespaced-manifest", "", "Path to manifest for per-test, namespaced resources (e.g. RBAC and Operator manifest)")
testCmd.Flags().StringVar(&tlConfig.goTestFlags, "go-test-flags", "", "Additional flags to pass to go test")
testCmd.Flags().StringVar(&tlConfig.namespace, "namespace", "", "If non-empty, single namespace to run tests in")
testCmd.Flags().BoolVar(&tlConfig.upLocal, "up-local", false, "Enable running operator locally with go run instead of as an image in the cluster")
testCmd.Flags().StringVar(&tlConfig.image, "image", "", "Use a different operator image from the one specified in the namespaced manifest")

return testCmd
Expand All @@ -71,6 +73,10 @@ func testLocalFunc(cmd *cobra.Command, args []string) {
log.Fatal("operator-sdk test local requires exactly 1 argument")
}

if tlConfig.upLocal && tlConfig.namespace == "" {
log.Fatal("must specify a namespace to run in when -up-local flag is set")
}

log.Info("Testing operator locally.")

// if no namespaced manifest path is given, combine deploy/service_account.yaml, deploy/role.yaml, deploy/role_binding.yaml and deploy/operator.yaml
Expand All @@ -80,28 +86,29 @@ func testLocalFunc(cmd *cobra.Command, args []string) {
log.Fatalf("could not create %s: (%v)", deployTestDir, err)
}
tlConfig.namespacedManPath = filepath.Join(deployTestDir, "namespace-manifests.yaml")

sa, err := ioutil.ReadFile(filepath.Join(scaffold.DeployDir, scaffold.ServiceAccountYamlFile))
if err != nil {
log.Warnf("could not find the serviceaccount manifest: (%v)", err)
}
role, err := ioutil.ReadFile(filepath.Join(scaffold.DeployDir, scaffold.RoleYamlFile))
if err != nil {
log.Warnf("could not find role manifest: (%v)", err)
}
roleBinding, err := ioutil.ReadFile(filepath.Join(scaffold.DeployDir, scaffold.RoleBindingYamlFile))
if err != nil {
log.Warnf("could not find role_binding manifest: (%v)", err)
}
operator, err := ioutil.ReadFile(filepath.Join(scaffold.DeployDir, scaffold.OperatorYamlFile))
if err != nil {
log.Fatalf("could not find operator manifest: (%v)", err)
}
combined := []byte{}
combined = combineManifests(combined, sa)
combined = combineManifests(combined, role)
combined = combineManifests(combined, roleBinding)
combined = append(combined, operator...)
if !tlConfig.upLocal {
sa, err := ioutil.ReadFile(filepath.Join(scaffold.DeployDir, scaffold.ServiceAccountYamlFile))
if err != nil {
log.Warnf("could not find the serviceaccount manifest: (%v)", err)
}
role, err := ioutil.ReadFile(filepath.Join(scaffold.DeployDir, scaffold.RoleYamlFile))
if err != nil {
log.Warnf("could not find role manifest: (%v)", err)
}
roleBinding, err := ioutil.ReadFile(filepath.Join(scaffold.DeployDir, scaffold.RoleBindingYamlFile))
if err != nil {
log.Warnf("could not find role_binding manifest: (%v)", err)
}
operator, err := ioutil.ReadFile(filepath.Join(scaffold.DeployDir, scaffold.OperatorYamlFile))
if err != nil {
log.Fatalf("could not find operator manifest: (%v)", err)
}
combined = combineManifests(combined, sa)
combined = combineManifests(combined, role)
combined = combineManifests(combined, roleBinding)
combined = append(combined, operator...)
}
err = ioutil.WriteFile(tlConfig.namespacedManPath, combined, os.FileMode(fileutil.DefaultFileMode))
if err != nil {
log.Fatalf("could not create temporary namespaced manifest file: (%v)", err)
Expand Down Expand Up @@ -167,6 +174,9 @@ func testLocalFunc(cmd *cobra.Command, args []string) {
if tlConfig.namespace != "" {
testArgs = append(testArgs, "-"+test.SingleNamespaceFlag, "-parallel=1")
}
if tlConfig.upLocal {
testArgs = append(testArgs, "-"+test.LocalOperatorFlag)
}
dc := exec.Command("go", testArgs...)
dc.Env = append(os.Environ(), fmt.Sprintf("%v=%v", test.TestNamespaceEnv, tlConfig.namespace))
dc.Dir = projutil.MustGetwd()
Expand Down
1 change: 1 addition & 0 deletions doc/sdk-cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ Runs the tests locally
* `--namespaced-manifest` string - path to manifest for per-test, namespaced resources (default: combines deploy/service_account.yaml, deploy/rbac.yaml, and deploy/operator.yaml)
* `--namespace` string - if non-empty, single namespace to run tests in (e.g. "operator-test") (default: "")
* `--go-test-flags` string - extra arguments to pass to `go test` (e.g. -f "-v -parallel=2")
* `--up-local` - Enable running operator locally with go run instead of as an image in the cluster
* `--image` string - use a different operator image from the one specified in the namespaced manifest
* `-h, --help` - help for local

Expand Down
20 changes: 16 additions & 4 deletions doc/test-framework/writing-e2e-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,9 @@ in your namespaced manifest. The custom `Create` function use the controller-run
creates a cleanup function that is called by `ctx.Cleanup` which deletes the resource and then waits for the resource to be
fully deleted before returning. This is configurable with `CleanupOptions`. For info on how to use `CleanupOptions` see
[this section](#how-to-use-cleanup).


If you want to make sure the operator's deployment is fully ready before moving onto the next part of the
test, the `WaitForDeployment` function from [e2eutil][e2eutil-link] (in the sdk under `pkg/test/e2eutil`) can be used:
test, the `WaitForOperatorDeployment` function from [e2eutil][e2eutil-link] (in the sdk under `pkg/test/e2eutil`) can be used:

```go
// get namespace
Expand All @@ -135,7 +134,7 @@ if err != nil {
// get global framework variables
f := framework.Global
// wait for memcached-operator to be ready
err = e2eutil.WaitForDeployment(t, f.KubeClient, namespace, "memcached-operator", 1, time.Second*5, time.Second*30)
err = e2eutil.WaitForOperatorDeployment(t, f.KubeClient, namespace, "memcached-operator", 1, time.Second*5, time.Second*30)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -188,7 +187,10 @@ if err != nil {
```

Now we can check if the operator successfully worked. In the case of the memcached operator, it should have
created a deployment called "example-memcached" with 3 replicas:
created a deployment called "example-memcached" with 3 replicas. To check, we use the `WaitForDeployment` function, which
is the same as `WaitForOperatorDeployment` with the exception that `WaitForOperatorDeployment` will skip waiting
for the deployment if the test is run locally and the `--up-local` flag is set; the `WaitForDeployment` function always
waits for the deployment:

```go
// wait for example-memcached to reach 3 replicas
Expand Down Expand Up @@ -250,6 +252,16 @@ $ kubectl create namespace operator-test
$ operator-sdk test local ./test/e2e --namespace operator-test
```

To run the operator itself locally during the tests instead of starting a deployment in the cluster, you can use the
hasbro17 marked this conversation as resolved.
Show resolved Hide resolved
`--up-local` flag. This mode will still create global resources, but by default will not create any in-cluster namespaced
resources unless the user specifies one through the `--namespaced-manifest` flag. (Note: the `--up-local` flag requires
the `--namespace` flag):

```shell
$ kubectl create namespace operator-test
$ operator-sdk test local ./test/e2e --namespace operator-test --up-local
```

For more documentation on the `operator-sdk test local` command, see the [SDK CLI Reference][sdk-cli-ref] doc.

For advanced use cases, it is possible to run the tests via `go test` directly. As long as all flags defined
Expand Down
10 changes: 7 additions & 3 deletions hack/tests/test-subcommand.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ set -ex

cd test/test-framework
# test framework with defaults
operator-sdk test local .
operator-sdk test local ./test/e2e
# test operator-sdk test flags
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
operator-sdk test local ./test/e2e --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
# test operator-sdk test local single namespace mode
kubectl create namespace test-memcached
operator-sdk test local . --namespace=test-memcached
operator-sdk test local ./test/e2e --namespace=test-memcached
kubectl delete namespace test-memcached
# test operator in up local mode
kubectl create namespace test-memcached
operator-sdk test local ./test/e2e --up-local --namespace=test-memcached
kubectl delete namespace test-memcached
3 changes: 1 addition & 2 deletions pkg/scaffold/test_pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@ import (
"testing"

"github.com/operator-framework/operator-sdk/pkg/scaffold/internal/testutil"
"github.com/operator-framework/operator-sdk/pkg/test"
)

func TestPodTest(t *testing.T) {
s, buf := setupScaffoldAndWriter()
err := s.Execute(appConfig,
&TestPod{
Image: "quay.io/app/operator:v1.0.0",
TestNamespaceEnv: test.TestNamespaceEnv,
TestNamespaceEnv: "TEST_NAMESPACE",
})
if err != nil {
t.Fatalf("failed to execute the scaffold: (%v)", err)
Expand Down
16 changes: 16 additions & 0 deletions pkg/test/e2eutil/wait_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"testing"
"time"

"github.com/operator-framework/operator-sdk/pkg/test"

apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
Expand All @@ -29,6 +31,20 @@ import (
// This can be used in multiple ways, like verifying that a required resource is ready before trying to use it, or to test
// failure handling, like simulated in SimulatePodFail.
func WaitForDeployment(t *testing.T, kubeclient kubernetes.Interface, namespace, name string, replicas int, retryInterval, timeout time.Duration) error {
return waitForDeployment(t, kubeclient, namespace, name, replicas, retryInterval, timeout, false)
}

// WaitForOperatorDeployment has the same functionality as WaitForDeployment but will no wait for the deployment if the
// test was run with a locally run operator (--up-local flag)
func WaitForOperatorDeployment(t *testing.T, kubeclient kubernetes.Interface, namespace, name string, replicas int, retryInterval, timeout time.Duration) error {
return waitForDeployment(t, kubeclient, namespace, name, replicas, retryInterval, timeout, true)
}

func waitForDeployment(t *testing.T, kubeclient kubernetes.Interface, namespace, name string, replicas int, retryInterval, timeout time.Duration, isOperator bool) error {
if isOperator && test.Global.LocalOperator {
t.Logf("Operator is running locally; skip waitForDeployment\n")
AlexNPavel marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
err := wait.Poll(retryInterval, timeout, func() (done bool, err error) {
deployment, err := kubeclient.AppsV1().Deployments(namespace).Get(name, metav1.GetOptions{IncludeUninitialized: true})
if err != nil {
Expand Down
4 changes: 3 additions & 1 deletion pkg/test/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ type Framework struct {
Scheme *runtime.Scheme
NamespacedManPath *string
Namespace string
LocalOperator bool
}

func setup(kubeconfigPath, namespacedManPath *string) error {
func setup(kubeconfigPath, namespacedManPath *string, localOperator bool) error {
var err error
var kubeconfig *rest.Config
if *kubeconfigPath == "incluster" {
Expand Down Expand Up @@ -105,6 +106,7 @@ func setup(kubeconfigPath, namespacedManPath *string) error {
Scheme: scheme,
NamespacedManPath: namespacedManPath,
Namespace: namespace,
LocalOperator: localOperator,
}
return nil
}
Expand Down
Loading