Skip to content

Commit 0089f33

Browse files
aditigupta96k8s-publishing-bot
authored andcommitted
Fixes the flaky test (Issue #132953)
Kubernetes-commit: 870c561aeea99b6b98a9b57dc0c7114eea1ec7f8
1 parent b9eb912 commit 0089f33

File tree

1 file changed

+54
-5
lines changed

1 file changed

+54
-5
lines changed

test/integration/conversion/conversion_test.go

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,28 @@ func testWebhookConverter(t *testing.T, watchCache bool) {
271271
etcd3watcher.TestOnlySetFatalOnDecodeError(false)
272272
defer etcd3watcher.TestOnlySetFatalOnDecodeError(true)
273273

274+
// To avoid the high cost of restarting the API server for every test case, we start
275+
// the infrastructure (API Server + Webhook Server) ONCE at the beginning of the test.
276+
//
277+
// We use a 'dynamicWebhookHandler' to swap the conversion logic (the handler)
278+
// for each test case without restarting the actual HTTP server.
279+
//
280+
// This allows us to start the webhook server ONCE at the beginning of the test.
281+
// Crucially, this allows us to enforce teardown order: API Server stops -> Webhook Server stops.
282+
283+
// Create the mutable handler.
284+
proxyHandler := &dynamicWebhookHandler{}
285+
286+
// Start Webhook Server FIRST.
287+
// This ensures its deferred teardown runs LAST (after API server stop).
288+
webhookTearDown, webhookClientConfig, err := StartConversionWebhookServer(proxyHandler)
289+
if err != nil {
290+
t.Fatal(err)
291+
}
292+
defer webhookTearDown()
293+
294+
// Start API Server SECOND.
295+
// This ensures its deferred teardown runs FIRST.
274296
tearDown, config, options, err := fixtures.StartDefaultServer(t, fmt.Sprintf("--watch-cache=%v", watchCache))
275297
if err != nil {
276298
t.Fatal(err)
@@ -315,12 +337,12 @@ func testWebhookConverter(t *testing.T, watchCache bool) {
315337
for _, test := range tests {
316338
t.Run(test.group, func(t *testing.T) {
317339
upCh, handler := closeOnCall(test.handler)
318-
tearDown, webhookClientConfig, err := StartConversionWebhookServer(handler)
319-
if err != nil {
320-
t.Fatal(err)
321-
}
322-
defer tearDown()
323340

341+
// Inject the logic for this specific test case
342+
proxyHandler.set(handler)
343+
defer proxyHandler.set(nil)
344+
345+
// Configure the CRD to use the shared webhook server
324346
ctc.setConversionWebhook(t, webhookClientConfig, test.reviewVersions)
325347
defer ctc.removeConversionWebhook(t)
326348

@@ -1615,3 +1637,30 @@ func TestWebhookConversion_WhitespaceCABundleEtcdBypass(t *testing.T) {
16151637
verifyMultiVersionObject(t, "v1beta1", obj)
16161638

16171639
}
1640+
1641+
// dynamicWebhookHandler is a thread-safe http. Handler that allows swapping
1642+
// the underlying delegate handler at runtime. This is useful for sharing a single
1643+
// server instance across multiple test cases that require different behaviors.
1644+
type dynamicWebhookHandler struct {
1645+
mu sync.RWMutex
1646+
delegate http.Handler
1647+
}
1648+
1649+
// ServeHTTP implements http.Handler. It delegates the request to the currently
1650+
// configured handler. If no handler is set, it returns an internal server error.
1651+
func (h *dynamicWebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
1652+
h.mu.RLock()
1653+
defer h.mu.RUnlock()
1654+
if h.delegate != nil {
1655+
h.delegate.ServeHTTP(w, r)
1656+
} else {
1657+
http.Error(w, "unexpected call", http.StatusInternalServerError)
1658+
}
1659+
}
1660+
1661+
// set safely swaps the underlying delegate handler.
1662+
func (h *dynamicWebhookHandler) set(delegate http.Handler) {
1663+
h.mu.Lock()
1664+
defer h.mu.Unlock()
1665+
h.delegate = delegate
1666+
}

0 commit comments

Comments
 (0)