Skip to content

Commit

Permalink
Configure eventually timeouts in e2e tests
Browse files Browse the repository at this point in the history
Instead of specifying cutom timeouts in `Eventually`, configure the
default eventually timeout and polling interval consistently across
e2e suites.

Also, cf/kubectl utilities wait for the `gexec.Session` to exit so that
tests can check the exit code directly without `Eventually` or explicit
waits.

Co-authored-by: Georgi Sabev <georgethebeatle@gmail.com>
  • Loading branch information
danail-branekov and georgethebeatle committed Oct 13, 2023
1 parent 5f0689c commit 9f24580
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 152 deletions.
20 changes: 4 additions & 16 deletions tests/crds/crds_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package crds_test

import (
"testing"
"time"

"code.cloudfoundry.org/korifi/tests/helpers"

"github.com/cloudfoundry/cf-test-helpers/cf"
"github.com/google/uuid"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand All @@ -15,7 +13,8 @@ import (

func TestCrds(t *testing.T) {
RegisterFailHandler(Fail)
SetDefaultEventuallyTimeout(10 * time.Second)
SetDefaultEventuallyTimeout(helpers.EventuallyTimeout())
SetDefaultEventuallyPollingInterval(helpers.EventuallyPollingInterval())
RunSpecs(t, "CRDs Suite")
}

Expand All @@ -29,9 +28,9 @@ var _ = BeforeSuite(func() {
rootNamespace = helpers.GetDefaultedEnvVar("ROOT_NAMESPACE", "cf")
serviceAccountFactory = helpers.NewServiceAccountFactory(rootNamespace)

Eventually(
Expect(
helpers.Kubectl("get", "namespace/"+rootNamespace),
).Should(Exit(0), "Could not find root namespace called %q", rootNamespace)
).To(Exit(0), "Could not find root namespace called %q", rootNamespace)

cfUser = uuid.NewString()
cfUserToken := serviceAccountFactory.CreateServiceAccount(cfUser)
Expand All @@ -42,14 +41,3 @@ var _ = AfterSuite(func() {
serviceAccountFactory.DeleteServiceAccount(cfUser)
helpers.RemoveUserFromKubeConfig(cfUser)
})

func loginAs(apiEndpoint string, user string) {
apiArguments := []string{
"api",
apiEndpoint,
"--skip-ssl-validation",
}
Eventually(cf.Cf(apiArguments...)).Should(Exit(0))

Eventually(cf.Cf("auth", user)).Should(Exit(0))
}
137 changes: 73 additions & 64 deletions tests/crds/crds_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package crds_test

import (
"fmt"

. "code.cloudfoundry.org/korifi/controllers/controllers/workloads/testutils"
"code.cloudfoundry.org/korifi/tests/helpers"

Expand Down Expand Up @@ -38,91 +40,89 @@ var _ = Describe("Using the k8s API directly", Ordered, func() {
})

AfterAll(func() {
deleteOrg := helpers.Kubectl("delete", "--ignore-not-found=true", "-n="+rootNamespace, "cforg", orgGUID)
deleteRoleBinding := helpers.Kubectl("delete", "--ignore-not-found=true", "-n="+rootNamespace, "rolebinding", bindingName)

Eventually(deleteOrg, "60s").Should(Exit(0), "deleteOrg")
Eventually(deleteRoleBinding, "20s").Should(Exit(0), "deleteRoleBinging")
Expect(
helpers.Kubectl("delete", "--ignore-not-found=true", "-n="+rootNamespace, "cforg", orgGUID),
).To(Exit(0))
Expect(
helpers.Kubectl("delete", "--ignore-not-found=true", "-n="+rootNamespace, "rolebinding", bindingName),
).To(Exit(0))
})

It("can create a CFOrg", func() {
applyCFOrg := helpers.KubectlApply(`---
Expect(helpers.KubectlApply(`---
apiVersion: korifi.cloudfoundry.org/v1alpha1
kind: CFOrg
metadata:
namespace: %s
name: %s
spec:
displayName: %s
`, rootNamespace, orgGUID, orgDisplayName)
Eventually(applyCFOrg).Should(Exit(0))
`, rootNamespace, orgGUID, orgDisplayName),
).To(Exit(0))

Eventually(
helpers.Kubectl("wait", "--for=condition=ready", "-n="+rootNamespace, "cforg/"+orgGUID),
"20s",
).Should(Exit(0))
Expect(
helpers.Kubectl("wait", "--for=condition=ready", "-n="+rootNamespace, "cforg/"+orgGUID, fmt.Sprintf("--timeout=%s", helpers.EventuallyTimeout())),
).To(Exit(0))

Eventually(
Expect(
helpers.Kubectl("get", "namespace/"+orgGUID),
).Should(Exit(0))
).To(Exit(0))
})

It("can create a CFSpace", func() {
applyCFSpace := helpers.KubectlApply(`---
Expect(helpers.KubectlApply(`---
apiVersion: korifi.cloudfoundry.org/v1alpha1
kind: CFSpace
metadata:
namespace: %s
name: %s
spec:
displayName: %s
`, orgGUID, spaceGUID, spaceDisplayName)
Eventually(applyCFSpace).Should(Exit(0))
`, orgGUID, spaceGUID, spaceDisplayName),
).To(Exit(0))

Eventually(
helpers.Kubectl("wait", "--for=condition=ready", "-n="+orgGUID, "cfspace/"+spaceGUID),
"20s",
).Should(Exit(0))
Expect(
helpers.Kubectl("wait", "--for=condition=ready", "-n="+orgGUID, "cfspace/"+spaceGUID, fmt.Sprintf("--timeout=%s", helpers.EventuallyTimeout())),
).To(Exit(0))

Eventually(
Expect(
helpers.Kubectl("get", "namespace/"+spaceGUID),
).Should(Exit(0))
).To(Exit(0))
})

It("can grant the necessary roles to push an app via the CLI", func() {
Eventually(
Expect(
helpers.Kubectl("create", "rolebinding", "-n="+rootNamespace, "--serviceaccount="+bindingUser, "--clusterrole=korifi-controllers-root-namespace-user", bindingName),
).Should(Exit(0))
Eventually(
).To(Exit(0))
Expect(
helpers.Kubectl("label", "rolebinding", bindingName, "-n="+rootNamespace, "cloudfoundry.org/role-guid="+GenerateGUID()),
).Should(Exit(0))
).To(Exit(0))

Eventually(
Expect(
helpers.Kubectl("create", "rolebinding", "-n="+orgGUID, "--serviceaccount="+bindingUser, "--clusterrole=korifi-controllers-organization-user", cfUser+"-org-user"),
).Should(Exit(0))
Eventually(
).To(Exit(0))
Expect(
helpers.Kubectl("label", "rolebinding", cfUser+"-org-user", "-n="+orgGUID, "cloudfoundry.org/role-guid="+GenerateGUID()),
).Should(Exit(0))
).To(Exit(0))

Eventually(
Expect(
helpers.Kubectl("create", "rolebinding", "-n="+spaceGUID, "--serviceaccount="+bindingUser, "--clusterrole=korifi-controllers-space-developer", cfUser+"-space-developer"),
).Should(Exit(0))
Eventually(
).To(Exit(0))
Expect(
helpers.Kubectl("label", "rolebinding", cfUser+"-space-developer", "-n="+spaceGUID, "cloudfoundry.org/role-guid="+GenerateGUID()),
).Should(Exit(0))
).To(Exit(0))

loginAs(korifiAPIEndpoint, cfUser)
Expect(cf.Cf("api", korifiAPIEndpoint, "--skip-ssl-validation")).To(Exit(0))
Expect(cf.Cf("auth", cfUser)).To(Exit(0))
Expect(cf.Cf("target", "-o", orgDisplayName, "-s", spaceDisplayName)).To(Exit(0))

Eventually(cf.Cf("target", "-o", orgDisplayName, "-s", spaceDisplayName)).Should(Exit(0))

Eventually(
Expect(
cf.Cf("push", PrefixedGUID("crds-test-app"), "-p", "../assets/dorifi", "--no-start"), // This could be any app
"20s",
).Should(Exit(0))
).To(Exit(0))
})

It("can create cf-admin rolebinding which propagates to child namespaces", func() {
applyCFAdminRoleBinding := helpers.KubectlApply(`---
Expect(helpers.KubectlApply(`---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
Expand All @@ -138,44 +138,53 @@ var _ = Describe("Using the k8s API directly", Ordered, func() {
- kind: ServiceAccount
name: %s
namespace: %s
`, rootNamespace, propagatedBindingName, cfUser, rootNamespace)
Eventually(applyCFAdminRoleBinding).Should(Exit(0))
`, rootNamespace, propagatedBindingName, cfUser, rootNamespace),
).To(Exit(0))

Eventually(func(g Gomega) {
g.Expect(helpers.Kubectl("get", "rolebinding/"+propagatedBindingName, "-n", rootNamespace)).To(Exit(0))
}).Should(Succeed())

Eventually(func() int {
return helpers.Kubectl("get", "rolebinding/"+propagatedBindingName, "-n", rootNamespace).Wait().ExitCode()
}, "20s").Should(BeNumerically("==", 0))
Eventually(func(g Gomega) {
g.Expect(helpers.Kubectl("get", "rolebinding/"+propagatedBindingName, "-n", orgGUID)).To(Exit(0))
}).Should(Succeed())

Eventually(func() int {
return helpers.Kubectl("get", "rolebinding/"+propagatedBindingName, "-n", orgGUID).Wait().ExitCode()
}, "20s").Should(BeNumerically("==", 0))
Eventually(func(g Gomega) {
g.Expect(helpers.Kubectl("get", "rolebinding/"+propagatedBindingName, "-n", orgGUID)).To(Exit(0))
}).Should(Succeed())

Eventually(func() int {
return helpers.Kubectl("get", "rolebinding/"+propagatedBindingName, "-n", spaceGUID).Wait().ExitCode()
}, "20s").Should(BeNumerically("==", 0))
Eventually(func(g Gomega) {
g.Expect(helpers.Kubectl("get", "rolebinding/"+propagatedBindingName, "-n", spaceGUID)).To(Exit(0))
}).Should(Succeed())
})

It("can delete the cf-admin rolebinding", func() {
Eventually(
Expect(
helpers.Kubectl("delete", "--ignore-not-found=true", "-n="+rootNamespace, "rolebinding/"+propagatedBindingName),
"20s",
).Should(Exit(0))
).To(Exit(0))

Eventually(helpers.Kubectl("wait", "--for=delete", "rolebinding/"+propagatedBindingName, "-n", rootNamespace, "--timeout=60s"), "60s").Should(Exit(0))
Expect(
helpers.Kubectl("wait", "--for=delete", "rolebinding/"+propagatedBindingName, "-n", rootNamespace, fmt.Sprintf("--timeout=%s", helpers.EventuallyTimeout())),
).To(Exit(0))

Eventually(helpers.Kubectl("wait", "--for=delete", "rolebinding/"+propagatedBindingName, "-n", orgGUID, "--timeout=60s"), "60s").Should(Exit(0))
Expect(
helpers.Kubectl("wait", "--for=delete", "rolebinding/"+propagatedBindingName, "-n", orgGUID, fmt.Sprintf("--timeout=%s", helpers.EventuallyTimeout())),
).To(Exit(0))

Eventually(helpers.Kubectl("wait", "--for=delete", "rolebinding/"+propagatedBindingName, "-n", spaceGUID, "--timeout=60s"), "60s").Should(Exit(0))
Expect(
helpers.Kubectl("wait", "--for=delete", "rolebinding/"+propagatedBindingName, "-n", spaceGUID, fmt.Sprintf("--timeout=%s", helpers.EventuallyTimeout())),
).To(Exit(0))
})

It("can delete the space", func() {
Eventually(helpers.Kubectl("delete", "--ignore-not-found=true", "-n="+orgGUID, "cfspace/"+spaceGUID), "120s").Should(Exit(0))
Eventually(helpers.Kubectl("wait", "--for=delete", "namespace/"+spaceGUID)).Should(Exit(0))
Expect(helpers.Kubectl("delete", "--ignore-not-found=true", "-n="+orgGUID, "cfspace/"+spaceGUID)).To(Exit(0))
Expect(helpers.Kubectl("wait", "--for=delete", "namespace/"+spaceGUID)).To(Exit(0))
})

It("can delete the org", func() {
Eventually(helpers.Kubectl("delete", "--ignore-not-found=true", "-n="+rootNamespace, "cforgs/"+orgGUID), "120s").Should(Exit(0))
Expect(helpers.Kubectl("delete", "--ignore-not-found=true", "-n="+rootNamespace, "cforgs/"+orgGUID)).To(Exit(0))

Eventually(helpers.Kubectl("wait", "--for=delete", "cforg/"+orgGUID, "-n", rootNamespace)).Should(Exit(0))
Eventually(helpers.Kubectl("wait", "--for=delete", "namespace/"+orgGUID)).Should(Exit(0))
Expect(helpers.Kubectl("wait", "--for=delete", "cforg/"+orgGUID, "-n", rootNamespace)).To(Exit(0))
Expect(helpers.Kubectl("wait", "--for=delete", "namespace/"+orgGUID)).To(Exit(0))
})
})
2 changes: 1 addition & 1 deletion tests/e2e/e2e_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ var _ = SynchronizedBeforeSuite(func() []byte {
adminClient = makeTokenClient(sharedSetup.AdminServiceAccountToken)

SetDefaultEventuallyTimeout(helpers.EventuallyTimeout())
SetDefaultEventuallyPollingInterval(2 * time.Second)
SetDefaultEventuallyPollingInterval(helpers.EventuallyPollingInterval())

logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
})
Expand Down
30 changes: 23 additions & 7 deletions tests/helpers/eventually.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,39 @@ import (
"strconv"
"time"

. "github.com/onsi/gomega" //lint:ignore ST1001 this is a test file
. "github.com/onsi/ginkgo/v2" //lint:ignore ST1001 this is a test file
. "github.com/onsi/gomega" //lint:ignore ST1001 this is a test file
)

func EventuallyShouldHold(condition func(g Gomega)) {
GinkgoHelper()

Eventually(condition).WithTimeout(EventuallyTimeout()).Should(Succeed())
Consistently(condition).Should(Succeed())
}

func EventuallyTimeout() time.Duration {
eventuallyTimeout := 4 * time.Minute
eventuallyTimeoutSecondsString := os.Getenv("E2E_EVENTUALLY_TIMEOUT_SECONDS")
GinkgoHelper()

return getDuration("E2E_EVENTUALLY_TIMEOUT_SECONDS", 4*60)
}

func EventuallyPollingInterval() time.Duration {
GinkgoHelper()

return getDuration("E2E_EVENTUALLY_POLLING_INTERVAL_SECONDS", 2)
}

func getDuration(envName string, defaultDurationSeconds int) time.Duration {
GinkgoHelper()

durationSecondsString := os.Getenv(envName)

if eventuallyTimeoutSecondsString != "" {
eventuallyTimeoutSeconds, err := strconv.Atoi(eventuallyTimeoutSecondsString)
if durationSecondsString != "" {
durationSeconds, err := strconv.Atoi(durationSecondsString)
Expect(err).NotTo(HaveOccurred())
eventuallyTimeout = time.Duration(eventuallyTimeoutSeconds) * time.Second
return time.Duration(durationSeconds) * time.Second
}

return eventuallyTimeout
return time.Duration(defaultDurationSeconds) * time.Second
}
8 changes: 4 additions & 4 deletions tests/helpers/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func RemoveUserFromKubeConfig(userName string) {

func Kubectl(args ...string) *Session {
cmdStarter := commandstarter.NewCommandStarter()
return KubectlWithCustomReporter(cmdStarter, commandreporter.NewCommandReporter(), args...)
return kubectlWithCustomReporter(cmdStarter, commandreporter.NewCommandReporter(), args...)
}

func KubectlApply(stdinText string, sprintfArgs ...any) *Session {
Expand All @@ -60,14 +60,14 @@ func KubectlApply(stdinText string, sprintfArgs ...any) *Session {
fmt.Sprintf(stdinText, sprintfArgs...),
),
)
return KubectlWithCustomReporter(cmdStarter, commandreporter.NewCommandReporter(), "apply", "-f=-")
return kubectlWithCustomReporter(cmdStarter, commandreporter.NewCommandReporter(), "apply", "-f=-")
}

func KubectlWithCustomReporter(cmdStarter *commandstarter.CommandStarter, reporter *commandreporter.CommandReporter, args ...string) *Session {
func kubectlWithCustomReporter(cmdStarter *commandstarter.CommandStarter, reporter *commandreporter.CommandReporter, args ...string) *Session {
request, err := cmdStarter.Start(reporter, "kubectl", args...)
if err != nil {
panic(err)
}

return request
return request.Wait()
}
Loading

0 comments on commit 9f24580

Please sign in to comment.