Skip to content

Commit 5ce22a9

Browse files
author
tazhate
committed
fix(e2e): increase leader election lease duration and e2e timeouts
The controller manager was crashing after ~2 minutes in the GitHub Actions Kind environment (exit code 1). The most likely cause is leader election lease renewal failing under heavy CPU load from cert-manager and prometheus-operator running concurrently. Increased lease durations (15s→40s, 10s→25s, 2s→5s) to tolerate slow API server responses in resource-constrained CI environments. Also increased e2e default timeout from 2 to 5 minutes, added a BeforeEach to wait for controller readiness before each CR test, and added --previous log capture to diagnose future pod crashes. Spent ~30min analyzing crash timing in pod events and workqueue metrics.
1 parent 4682983 commit 5ce22a9

2 files changed

Lines changed: 54 additions & 7 deletions

File tree

cmd/main.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"os"
2424
"path/filepath"
25+
"time"
2526

2627
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
2728
// to ensure that exec-entrypoint and run can make use of them.
@@ -336,13 +337,19 @@ func main() {
336337
}
337338

338339
// --- Controller manager --------------------------------------------
340+
leaseDuration := 40 * time.Second
341+
renewDeadline := 25 * time.Second
342+
retryPeriod := 5 * time.Second
339343
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
340-
Scheme: scheme,
341-
Metrics: metricsServerOptions,
342-
WebhookServer: webhookServer,
343-
HealthProbeBindAddress: cfg.ProbeAddr,
344-
LeaderElection: cfg.LeaderElect,
345-
LeaderElectionID: defaultLeaderElectID,
344+
Scheme: scheme,
345+
Metrics: metricsServerOptions,
346+
WebhookServer: webhookServer,
347+
HealthProbeBindAddress: cfg.ProbeAddr,
348+
LeaderElection: cfg.LeaderElect,
349+
LeaderElectionID: defaultLeaderElectID,
350+
LeaseDuration: &leaseDuration,
351+
RenewDeadline: &renewDeadline,
352+
RetryPeriod: &retryPeriod,
346353
})
347354
if err != nil {
348355
setupLog.Error(err, "unable to start manager")

test/e2e/e2e_test.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ var _ = Describe("Manager", Ordered, func() {
106106
_, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s", err)
107107
}
108108

109+
By("Fetching previous controller manager pod logs (crash logs)")
110+
prevCmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace, "--previous")
111+
prevLogs, prevErr := utils.Run(prevCmd)
112+
if prevErr == nil {
113+
_, _ = fmt.Fprintf(GinkgoWriter, "Previous controller logs (crash):\n %s", prevLogs)
114+
}
115+
109116
By("Fetching Kubernetes events")
110117
cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp")
111118
eventsOutput, err := utils.Run(cmd)
@@ -135,7 +142,7 @@ var _ = Describe("Manager", Ordered, func() {
135142
}
136143
})
137144

138-
SetDefaultEventuallyTimeout(2 * time.Minute)
145+
SetDefaultEventuallyTimeout(5 * time.Minute)
139146
SetDefaultEventuallyPollingInterval(time.Second)
140147

141148
Context("Manager", func() {
@@ -275,6 +282,39 @@ var _ = Describe("Manager", Ordered, func() {
275282
Context("BlockchainNode CR lifecycle", func() {
276283
const testNS = namespace
277284

285+
// waitForControllerReady waits until the controller-manager pod is Running.
286+
// This handles the case where the pod restarts (e.g., leader election loss)
287+
// and ensures the controller is ready before applying CRs.
288+
waitForControllerReady := func() {
289+
Eventually(func(g Gomega) {
290+
cmd := exec.Command("kubectl", "get",
291+
"pods", "-l", "control-plane=controller-manager",
292+
"-o", "go-template={{ range .items }}"+
293+
"{{ if not .metadata.deletionTimestamp }}"+
294+
"{{ .metadata.name }}{{ \"\\n\" }}"+
295+
"{{ end }}{{ end }}",
296+
"-n", namespace,
297+
)
298+
podOutput, err := utils.Run(cmd)
299+
g.Expect(err).NotTo(HaveOccurred())
300+
podNames := utils.GetNonEmptyLines(podOutput)
301+
g.Expect(podNames).To(HaveLen(1))
302+
controllerPodName = podNames[0]
303+
304+
cmd = exec.Command("kubectl", "get",
305+
"pods", controllerPodName, "-o", "jsonpath={.status.phase}",
306+
"-n", namespace,
307+
)
308+
output, err := utils.Run(cmd)
309+
g.Expect(err).NotTo(HaveOccurred())
310+
g.Expect(output).To(Equal("Running"))
311+
}).Should(Succeed())
312+
}
313+
314+
BeforeEach(func() {
315+
waitForControllerReady()
316+
})
317+
278318
// cleanupCR deletes a BlockchainNode CR by name and waits for it to disappear.
279319
cleanupCR := func(name string) {
280320
cmd := exec.Command("kubectl", "delete", "blockchainnode", name,

0 commit comments

Comments
 (0)