Tests are flaky: ClusterAddonReconciler [AfterEach] Basic test applies the helm chart
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Stale
- Tech stack
- go, kubernetes
- Domain
- testing-qa
Research direction
Start with internal/controller/clusterstack_controller_test.go, especially the test at line 616, and review the BeforeEach setup. Run make test-unit and compare the related tests in clusteraddon_controller_test.go and internal/test/integration/workloadcluster/cluster_addon_test.go. Done means the deletion test reflects envtest's garbage-collection behavior and the affected test runs no longer fail intermittently.
Written by the indexing model from the issue text.
Description
/kind bug
In CI a test failed:
[FAIL] ClusterAddonReconciler [AfterEach] Basic test applies the helm chart
/home/runner/work/cluster-stack-operator/cluster-stack-operator/internal/test/integration/workloadcluster/cluster_addon_test.go:109
After pressing "re-run test" in the Github web UI, the test was fine.
This means that the test is flaky since it fails and succeeds without any code change.
Here is the successful run: https://github.com/SovereignCloudStack/cluster-stack-operator/actions/runs/10717474962?pr=255
What did you expect to happen:
I expect tests to not be flaky.
Fixing flakyness
I used that script to run tests again and again:
#!/bin/bash
trap 'echo "ERROR: A command has failed. Exiting the script. Line was ($0:$LINENO): $(sed -n "${LINENO}p" "$0")"; exit 3' ERR
set -Eeuo pipefail
i=0
while make test-unit; do
i=$((i + 1))
echo "Test run $i"
date
sleep 1
echo "=========================================================================================
echo "=========================================================================================
echo
done
Works fine, the second run failed:
Summarizing 1 Failure:
[FAIL] ClusterStackReconciler Test with provider Tests with multiple versions [It] checks ProviderClusterstackrelease is deleted when version is removed from spec
/home/guettli/syself/cluster-stack-operator-public/internal/controller/clusterstack_controller_test.go:616
Ran 40 of 40 Specs in 10.203 seconds
FAIL! -- 39 Passed | 1 Failed | 0 Pending | 0 Skipped
From time to time this fails, too:
Summarizing 1 Failure:
[FAIL] ClusterStackReconciler Test with provider Basic test [It] creates the cluster stack release object with cluster stack auto subscribe false
/home/guettli/syself/cluster-stack-operator-public/internal/controller/clusterstack_controller_test.go:487
Ran 40 of 40 Specs in 8.498 seconds
FAIL! -- 39 Passed | 1 Failed | 0 Pending | 0 Skipped
sometimes this fails:
Summarizing 1 Failure:
[FAIL] ClusterAddonReconciler Basic test [It] creates the clusterAddon object
/home/guettli/syself/cluster-stack-operator-public/internal/controller/clusteraddon_controller_test.go:112
Ran 40 of 40 Specs in 9.054 seconds
FAIL! -- 39 Passed | 1 Failed | 0 Pending | 0 Skipped
How to make the test always fail
If the test waits until the clusterStackRelease v2 is created, then the test always fails.
/////////////////////////////////////////////////////////////////////
// Flaky test
FIt("checks ProviderClusterstackrelease is deleted when version is removed from spec", func() {
fmt.Println("itttttttttttttttttttttttttttttttttttttttttt")
ph, err := patch.NewHelper(clusterStack, testEnv)
Expect(err).ShouldNot(HaveOccurred())
// new
providerclusterStackReleaseRefV2 := &corev1.ObjectReference{
APIVersion: "infrastructure.clusterstack.x-k8s.io/v1alpha1",
Kind: "TestInfrastructureProviderClusterStackRelease",
Name: clusterStackReleaseTagV2,
Namespace: testNs.Name,
}
Eventually(func() bool {
obj, err := external.Get(ctx, testEnv.GetClient(), providerclusterStackReleaseRefV2, testNs.Name)
if err != nil {
fmt.Printf("foundProviderclusterStackReleaseRef is not found. %s\n", err.Error())
return false
}
fmt.Printf("foundProviderclusterStackReleaseRef is found. %s %+v %+v\n",
obj.GetName(), obj.GetFinalizers(), obj.GetOwnerReferences())
return true
}, timeout, interval).Should(BeTrue())
Instead of the Eventually block, you can use time.Sleep(time.Second * 1) it has the same effect. This will fail here:
Eventually(func() bool {
return apierrors.IsNotFound(testEnv.Get(ctx, clusterStackReleaseTagV2Key, &csov1alpha1.ClusterStackRelease{}))
}, timeout, interval).Should(BeTrue())
I think the real error is in BeforeEach(). It does not wait until the resources get created.
BeforeEach(func() {
clusterStack.Spec = csov1alpha1.ClusterStackSpec{
Provider: "docker",
Name: "ferrol",
KubernetesVersion: "1.27",
Versions: []string{"v1", "v2"},
AutoSubscribe: false,
ProviderRef: &corev1.ObjectReference{
APIVersion: "infrastructure.clusterstack.x-k8s.io/v1alpha1",
Kind: "TestInfrastructureProviderClusterStackReleaseTemplate",
Name: "provider-test1",
Namespace: testNs.Name,
},
}
Expect(testEnv.Create(ctx, clusterStack)).To(Succeed())
cs, err := clusterstack.New(clusterStack.Spec.Provider, clusterStack.Spec.Name, clusterStack.Spec.KubernetesVersion, "v1")
Expect(err).To(BeNil())
clusterStackReleaseTagV1 = cs.String()
cs, err = clusterstack.New(clusterStack.Spec.Provider, clusterStack.Spec.Name, clusterStack.Spec.KubernetesVersion, "v2")
Expect(err).To(BeNil())
clusterStackReleaseTagV2 = cs.String()
clusterStackReleaseTagV1Key = types.NamespacedName{Name: clusterStackReleaseTagV1, Namespace: testNs.Name}
clusterStackReleaseTagV2Key = types.NamespacedName{Name: clusterStackReleaseTagV2, Namespace: testNs.Name}
})
@janiskemper what do you think: Does it make sense to wait in BeforeEach, so that the tests get a stable environment?
The good news: The test fails reproducible with FIt() this means. The flakiness is inside this single test (because no other test gets called).
Current conclusion
The test (It("checks ProviderClusterstackrelease is deleted when version is removed from spec")) has always been wrong. In some edge cases (roughly every 8th run), it failed because the correct sequence was executed.
The deletionTimestamp does not get propagated from the parent-object to the child-object, because in envTest the GC is not running.
Related kubebuilder docs: https://book.kubebuilder.io/reference/envtest.html#testing-considerations
I asked at #controller-runtime how other developers handle it.
I will update the code, so that I check for the DeletionTimestamp of the parent-object, and then check the ownerRef at the child-object. I will remove the test that the child-object gets deleted.
- Dominant language
- Go
- Stars
- 18
- Forks
- 5
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from SovereignCloudStack/cluster-stack-operator
-
CI issues (1) Openbug Container
SovereignCloudStack/cluster-stack-operator#340 · 1 comment · 3 assignees ·
-
cso: crash .... Open
Difficulty 3/5 1-2 days Newbie friendliness 58/100
SovereignCloudStack/cluster-stack-operator#337 · 2 comments ·
-
Difficulty 4/5 3-5 days Newbie friendliness 50/100
SovereignCloudStack/cluster-stack-operator#336 · 3 comments ·
-
bug
SovereignCloudStack/cluster-stack-operator#327 · 1 assignee ·
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
SovereignCloudStack/cluster-stack-operator#272 · 32 comments ·
All issues in SovereignCloudStack/cluster-stack-operator
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
milvus-io/birdwatcher#545 ·
-
kind/bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
kubernetes-sigs/prow#953 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
caddyserver/caddy#8046 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
jaegertracing/jaeger#9588 ·