pkg/test: add in-cluster/in-image testing support#469
Conversation
749d6e0 to
0cbc804
Compare
| `, | ||
| Run: buildFunc, | ||
| } | ||
| buildCmd.Flags().BoolVarP(&enableTests, "enable-tests", "e", false, "Enable in-cluster testing by adding test binary to the image") |
There was a problem hiding this comment.
Are keeping the default false for now and plan on changing it? I think eventually this should be a default of true? Thoughts?
There was a problem hiding this comment.
For now, we will keep it as default false. The SDK does not create any default tests for operators and the build would fail in that case. This is why we need a user to confirm that they have tests that they want to include. The test binary is also 34 MB, which may be undesirable to include by default.
| dbcmd.Env = append(os.Environ(), fmt.Sprintf("IMAGE=%v", image)) | ||
| intermediateImageName := image | ||
| if enableTests { | ||
| if namespacedManBuild == "" { |
There was a problem hiding this comment.
I wonder if pulling this code block into its own function may make this more readable?
0cbc804 to
7cfeb77
Compare
6a1309b to
aa34ac0
Compare
The cluster test has defers that need to run on error, which would not run if cmdError is used due to its use of os.Exit(). Instead, we can just return an error, which allows the defer functions to run.
This adds tests for the new incluster image testing mode as well as fixes some bugs that this test exposed
We don't need to do any extra operations anymore if a user does not supply a namespaced manifest path, so we can just use a default string
389d16a to
4f303c7
Compare
c152125 to
50eb65c
Compare
| command: ["/go-test.sh"] | ||
| resources: | ||
| requests: | ||
| cpu: 1 |
There was a problem hiding this comment.
I don't think we should specify resource requests by default. This could prevent the pod from being scheduled onto a node if there isn't enough cpu.
By not setting a default, the pod will either take on the default request and limit enforced by the namespace, or use as much as is available.
| func renderTestManifest(image string) error { | ||
| namespacedBytes, err := ioutil.ReadFile(namespacedManBuild) | ||
| if err != nil { | ||
| log.Fatalf("could not read rbac manifest: %v", err) |
There was a problem hiding this comment.
It's not rbac manifest(at least not in the test cluster case).
log.Fatalf("could not read namespaced manifest: %v", err)
| log.Fatalf("could not read rbac manifest: %v", err) | ||
| } | ||
| if err = generator.RenderTestYaml(cmdutil.GetConfig(), image); err != nil { | ||
| cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to generate deploy/test-pod.yaml: (%v)", err)) |
There was a problem hiding this comment.
You can use log.Fatalf() instead of cmdError.ExitWithError(). It's the exact same thing except it returns a specific error code.
We're going to remove cmdError.ExitWithError since returning error codes isn't as useful as we thought initially, and we never specified more than two codes.
| if err = generator.RenderTestYaml(cmdutil.GetConfig(), image); err != nil { | ||
| cmdError.ExitWithError(cmdError.ExitError, fmt.Errorf("failed to generate deploy/test-pod.yaml: (%v)", err)) | ||
| } | ||
| return verifyDeploymentImage(namespacedBytes, image) |
There was a problem hiding this comment.
The only error renderTestManifest() returns is actually just treated as a warning that's just printed out.
genWarning := renderTestManifest(image)
if genWarning != nil {
fmt.Printf("%s\n", genWarning)
}For the other two errors it just fails fast with log.Fatal.
So change renderTestManifest() to not return an error.
And print the warning message from verifyDeploymentImage() either here or outside this function by just checking the error returned directly from it.
|
|
||
| echo "building container ${IMAGE}..." | ||
| docker build -t "${IMAGE}" -f tmp/build/Dockerfile . | ||
| docker build -t "${IMAGE}" -f tmp/build/Dockerfile . --build-arg NAMESPACEDMAN=$NAMESPACEDMAN |
There was a problem hiding this comment.
We're no longer using this docker-build.sh script anymore right? We're just calling docker build directly in operator-sdk build.
So we should probably not generate this anymore.
| return err | ||
| } | ||
| return renderWriteFile(filepath.Join(buildDir, dockerfile), "tmp/build/Dockerfile", dockerFileTmpl, dTd) | ||
| return writeFileAndPrint(filepath.Join(buildDir, goTest), buf.Bytes(), os.FileMode(int(0755))) |
There was a problem hiding this comment.
Use the constant defaultExecFileMode .
There was a problem hiding this comment.
defaultExecMode is currently set to 0744, which doesn't work for this setup. Should I change defaultExecMode to 0755?
| inCluster := false | ||
| if *kubeconfigPath == "incluster" { | ||
| // Work around https://github.com/kubernetes/kubernetes/issues/40973 | ||
| // See https://github.com/coreos/etcd-operator/issues/731#issuecomment-283804819 |
There was a problem hiding this comment.
nit: We can remove the etcd-operator issue link since we're linking to the upstream issue in the line above.
| ImagePullPolicy: pullPolicy, | ||
| Command: []string{"/go-test.sh"}, | ||
| Env: []v1.EnvVar{{ | ||
| Name: "TEST_NAMESPACE", |
There was a problem hiding this comment.
Define this as a constant somewhere. In pkg/test probably.
| err = wait.PollImmediate(time.Second, time.Second*10, func() (done bool, err error) { | ||
| err = Global.DynamicClient.List(goctx.TODO(), &dynclient.ListOptions{Namespace: "default"}, obj) | ||
| if Global.InCluster { | ||
| err = Global.DynamicClient.List(goctx.TODO(), &dynclient.ListOptions{Namespace: os.Getenv("TEST_NAMESPACE")}, obj) |
There was a problem hiding this comment.
We should verify that TEST_NAMESPACE is not empty in the incluster case above, before using it here and in ctx.GetNamespace().
| } | ||
| testCmd.Flags().StringVarP(&testNamespace, "namespace", "n", "default", "Namespace to run tests in") | ||
| testCmd.Flags().StringVarP(&kubeconfigCluster, "kubeconfig", "k", defaultKubeConfig, "Kubeconfig path") | ||
| testCmd.Flags().StringVarP(&imagePullPolicy, "imagePullPolicy", "i", "Always", "Set test pod image pull policy. Allowed values: Always, Never") |
There was a problem hiding this comment.
One more nit here: Is there a reason to ask people to capitalize the words "always" and "never"? It's somewhat unusual for a CLI.
There was a problem hiding this comment.
The reason was that they are capitalized in the config file. I could change it, or maybe use strings.ToLower(...) and support both.
There was a problem hiding this comment.
We've decided to support both upper and lowercase
| log.Fatalf("failed to generate deploy/test-pod.yaml: (%v)", err) | ||
| } | ||
| // the error from verifyDeploymentImage is just a warning, not fatal error | ||
| fmt.Printf("%v", verifyDeploymentImage(namespacedBytes, image)) |
There was a problem hiding this comment.
Does this print <nil> every time if there's no error returned by verifyDeploymentImage()?
There was a problem hiding this comment.
Yes it does; I somehow missed that. I'll put a nil check there
| f := framework.Global | ||
| ctx := f.NewTestCtx(t) | ||
| defer ctx.Cleanup(t) | ||
| operatorYAML, err := ioutil.ReadFile("deploy/operator.yaml") |
There was a problem hiding this comment.
Check the returned error.
|
LGTM after nit. Great work. |
Issue #435