From 80cc27ae50f4eabb7728f731f738f1c2ca275167 Mon Sep 17 00:00:00 2001 From: Krzysztof Ostrowski Date: Wed, 4 Mar 2026 20:03:17 +0100 Subject: [PATCH] kep-3926: fix 60s shutdown timeout in transformation tests Cancel informer watch connections before tearing down the test apiserver to avoid hitting the 60s http.Server.Shutdown drain timeout. Each subtest was blocking on open watch connections, adding ~300s to the total suite runtime. Decouple the informer lifecycle from the apiserver by using a separate cancellable context for factory.Start(). Go's defer LIFO ordering ensures the informer stops before test.cleanUp() shuts down the server. Also reduce EncryptionConfigFileChangePollDuration to 1s in TestMain. --- .../bitflip_transformation_test.go | 4 +++- .../transformation/cr_transformation_test.go | 4 +++- .../controlplane/transformation/main_test.go | 4 ++++ .../secrets_transformation_test.go | 17 +++++++++-------- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/test/integration/controlplane/transformation/bitflip_transformation_test.go b/test/integration/controlplane/transformation/bitflip_transformation_test.go index 158c1537d9d..9813a3251ad 100644 --- a/test/integration/controlplane/transformation/bitflip_transformation_test.go +++ b/test/integration/controlplane/transformation/bitflip_transformation_test.go @@ -171,7 +171,9 @@ func TestBitFlipCorruptObjectDeletion(t *testing.T) { t.Fatalf("unexpected error from AddEventHandler: %v", err) } - factory.Start(test.Done()) + informerCtx, informerCancel := context.WithCancel(test) + defer informerCancel() + factory.Start(informerCtx.Done()) waitForSyncCtx, waitForSyncCancel := context.WithTimeout(context.Background(), wait.ForeverTestTimeout) defer waitForSyncCancel() if !cache.WaitForCacheSync(waitForSyncCtx.Done(), secretInformer.Informer().HasSynced) { diff --git a/test/integration/controlplane/transformation/cr_transformation_test.go b/test/integration/controlplane/transformation/cr_transformation_test.go index c6bb3218f9e..5f859e14a83 100644 --- a/test/integration/controlplane/transformation/cr_transformation_test.go +++ b/test/integration/controlplane/transformation/cr_transformation_test.go @@ -236,7 +236,9 @@ func TestCRAllowUnsafeMalformedObjectDeletionFeature(t *testing.T) { } lister := informer.Lister() - factory.Start(test.Done()) + informerCtx, informerCancel := context.WithCancel(test) + defer informerCancel() + factory.Start(informerCtx.Done()) waitForSyncCtx, waitForSyncCancel := context.WithTimeout(context.Background(), wait.ForeverTestTimeout) defer waitForSyncCancel() if !cache.WaitForCacheSync(waitForSyncCtx.Done(), informer.Informer().HasSynced) { diff --git a/test/integration/controlplane/transformation/main_test.go b/test/integration/controlplane/transformation/main_test.go index 31a3bbbb6fe..34c6f7c2394 100644 --- a/test/integration/controlplane/transformation/main_test.go +++ b/test/integration/controlplane/transformation/main_test.go @@ -21,10 +21,14 @@ import ( "testing" "time" + encryptionconfigcontroller "k8s.io/apiserver/pkg/server/options/encryptionconfig/controller" "k8s.io/kubernetes/test/integration/framework" ) func TestMain(m *testing.M) { + // Speed up encryption config reload from the default 1 minute to 1 second. + // This variable is exported specifically for integration tests. + encryptionconfigcontroller.EncryptionConfigFileChangePollDuration = time.Second framework.EtcdMain(m.Run) } diff --git a/test/integration/controlplane/transformation/secrets_transformation_test.go b/test/integration/controlplane/transformation/secrets_transformation_test.go index 48267c8c8ac..aef549fa151 100644 --- a/test/integration/controlplane/transformation/secrets_transformation_test.go +++ b/test/integration/controlplane/transformation/secrets_transformation_test.go @@ -97,7 +97,7 @@ resources: // 2. Secrets are decrypted on read // when EncryptionConfiguration is passed to KubeAPI server. func TestSecretsShouldBeTransformed(t *testing.T) { - var testCases = []struct { + testCases := []struct { transformerConfigContent string transformerPrefix string unSealFunc unSealSecret @@ -267,7 +267,9 @@ func TestAllowUnsafeMalformedObjectDeletionFeature(t *testing.T) { } lister := informer.Lister() - factory.Start(test.Done()) + informerCtx, informerCancel := context.WithCancel(test) + defer informerCancel() + factory.Start(informerCtx.Done()) waitForSyncCtx, waitForSyncCancel := context.WithTimeout(context.Background(), wait.ForeverTestTimeout) defer waitForSyncCancel() if !cache.WaitForCacheSync(waitForSyncCtx.Done(), informer.Informer().HasSynced) { @@ -295,8 +297,7 @@ func TestAllowUnsafeMalformedObjectDeletionFeature(t *testing.T) { // i) wait for the breaking changes to take effect testCtx, cancel := context.WithCancel(context.Background()) defer cancel() - // TODO: dynamic encryption config reload takes about 1m, so can't use - // wait.ForeverTestTimeout just yet, investigate and reduce the reload time. + // dynamic encryption config reload takes about 1m, so can't use wait.ForeverTestTimeout err = wait.PollUntilContextTimeout(testCtx, 1*time.Second, 2*time.Minute, true, func(ctx context.Context) (done bool, err error) { _, err = test.restClient.CoreV1().Secrets(testNamespace).Get(ctx, secretCorrupt, metav1.GetOptions{}) var got apierrors.APIStatus @@ -665,8 +666,8 @@ func runBenchmark(b *testing.B, transformerConfig string) { } func unSealWithGCMTransformer(ctx context.Context, cipherText []byte, dataCtx value.Context, - transformerConfig apiserverv1.ProviderConfiguration) ([]byte, error) { - + transformerConfig apiserverv1.ProviderConfiguration, +) ([]byte, error) { block, err := newAESCipher(transformerConfig.AESGCM.Keys[0].Secret) if err != nil { return nil, fmt.Errorf("failed to create block cipher: %v", err) @@ -686,8 +687,8 @@ func unSealWithGCMTransformer(ctx context.Context, cipherText []byte, dataCtx va } func unSealWithCBCTransformer(ctx context.Context, cipherText []byte, dataCtx value.Context, - transformerConfig apiserverv1.ProviderConfiguration) ([]byte, error) { - + transformerConfig apiserverv1.ProviderConfiguration, +) ([]byte, error) { block, err := newAESCipher(transformerConfig.AESCBC.Keys[0].Secret) if err != nil { return nil, err