From 5486715fbf67dad54b4a3bcc74eb71bbb04bf7ea Mon Sep 17 00:00:00 2001 From: Sotiris Salloumis Date: Mon, 27 Apr 2026 14:49:02 +0200 Subject: [PATCH] Move kubeletHealthCheck from e2enode to node To reduce duplication of code and overcome import cycle not allowed error during compile time, when used in non e2e_node packages. --- test/e2e/framework/node/helper.go | 23 +++++++++++++++ test/e2e_node/device_plugin_test.go | 14 ++++----- test/e2e_node/lock_contention_linux_test.go | 5 ++-- test/e2e_node/node_container_manager_test.go | 5 ++-- test/e2e_node/pod_status_test.go | 3 +- test/e2e_node/services/util.go | 29 ++----------------- test/e2e_node/standalone_test.go | 5 ++-- .../testdeviceplugin/device-plugin.go | 27 ++--------------- test/e2e_node/util.go | 26 ++--------------- test/e2e_node/util_kubeletconfig.go | 3 +- 10 files changed, 49 insertions(+), 91 deletions(-) diff --git a/test/e2e/framework/node/helper.go b/test/e2e/framework/node/helper.go index 012d9712775..1e694ea57c6 100644 --- a/test/e2e/framework/node/helper.go +++ b/test/e2e/framework/node/helper.go @@ -18,8 +18,10 @@ package node import ( "context" + "crypto/tls" "encoding/json" "fmt" + "net/http" "time" "github.com/onsi/ginkgo/v2" @@ -222,3 +224,24 @@ func RemoveExtendedResource(ctx context.Context, clientSet clientset.Interface, }) framework.ExpectNoError(err) } + +func HealthCheck(url string) bool { + insecureTransport := http.DefaultTransport.(*http.Transport).Clone() + insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + insecureHTTPClient := &http.Client{ + Transport: insecureTransport, + } + + req, err := http.NewRequest(http.MethodHead, url, nil) + if err != nil { + return false + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", framework.TestContext.BearerToken)) + resp, err := insecureHTTPClient.Do(req) + if err != nil { + framework.Logf("Health check on %q failed, error=%v", url, err) + } else if resp.StatusCode != http.StatusOK { + framework.Logf("Health check on %q failed, status=%d", url, resp.StatusCode) + } + return err == nil && resp.StatusCode == http.StatusOK +} diff --git a/test/e2e_node/device_plugin_test.go b/test/e2e_node/device_plugin_test.go index d268ea05efe..4e5b1724818 100644 --- a/test/e2e_node/device_plugin_test.go +++ b/test/e2e_node/device_plugin_test.go @@ -370,7 +370,7 @@ func testDevicePlugin(f *framework.Framework, pluginSockDir string) { restartKubelet(ctx, true) ginkgo.By("Wait for node to be ready again") - e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute) + framework.ExpectNoError(e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute)) ginkgo.By("Waiting for resource to become available on the local node after restart") gomega.Eventually(ctx, func() bool { @@ -421,7 +421,7 @@ func testDevicePlugin(f *framework.Framework, pluginSockDir string) { framework.ExpectNoError(err) ginkgo.By("Wait for node to be ready again") - e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute) + framework.ExpectNoError(e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute)) ginkgo.By("Waiting for container to restart") ensurePodContainerRestart(ctx, f, pod1.Name, pod1.Name) @@ -435,7 +435,7 @@ func testDevicePlugin(f *framework.Framework, pluginSockDir string) { restartKubelet(ctx, true) ginkgo.By("Wait for node to be ready again") - e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute) + framework.ExpectNoError(e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute)) ginkgo.By("Checking an instance of the pod is running") gomega.Eventually(ctx, getPodByName). @@ -535,7 +535,7 @@ func testDevicePlugin(f *framework.Framework, pluginSockDir string) { framework.ExpectNoError(err) ginkgo.By("Wait for node to be ready again") - e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute) + framework.ExpectNoError(e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute)) ginkgo.By("Re-Register resources and delete the plugin pod") gp := int64(0) @@ -592,7 +592,7 @@ func testDevicePlugin(f *framework.Framework, pluginSockDir string) { restartKubelet(ctx, true) ginkgo.By("Wait for node to be ready again") - e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute) + framework.ExpectNoError(e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute)) ginkgo.By("Checking the same instance of the pod is still running after kubelet restart") gomega.Eventually(ctx, getPodByName). @@ -658,7 +658,7 @@ func testDevicePlugin(f *framework.Framework, pluginSockDir string) { // wait until the kubelet health check will fail gomega.Eventually(ctx, func() bool { - ok := kubeletHealthCheck(kubeletHealthCheckURL) + ok := e2enode.HealthCheck(kubeletHealthCheckURL) framework.Logf("kubelet health check at %q value=%v", kubeletHealthCheckURL, ok) return ok }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeFalseBecause("expected kubelet health check to be failed")) @@ -1007,7 +1007,7 @@ func testDevicePluginNodeReboot(f *framework.Framework, pluginSockDir string) { restartKubelet(ctx) ginkgo.By("Wait for node to be ready again") - e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute) + framework.ExpectNoError(e2enode.WaitForAllNodesSchedulable(ctx, f.ClientSet, 5*time.Minute)) ginkgo.By("Waiting for the pod to fail with admission error as device plugin hasn't re-registered yet") gomega.Eventually(ctx, getPod). diff --git a/test/e2e_node/lock_contention_linux_test.go b/test/e2e_node/lock_contention_linux_test.go index 605434ad7be..b89fc6c7c16 100644 --- a/test/e2e_node/lock_contention_linux_test.go +++ b/test/e2e_node/lock_contention_linux_test.go @@ -28,6 +28,7 @@ import ( "github.com/onsi/gomega" "k8s.io/kubernetes/test/e2e/feature" "k8s.io/kubernetes/test/e2e/framework" + e2enode "k8s.io/kubernetes/test/e2e/framework/node" ) const contentionLockFile = "/var/run/kubelet.lock" @@ -43,7 +44,7 @@ var _ = SIGDescribe("Lock contention", framework.WithSlow(), framework.WithDisru ginkgo.By("perform kubelet health check to check if kubelet is healthy and running.") // Precautionary check that kubelet is healthy before running the test. - gomega.Expect(kubeletHealthCheck(kubeletHealthCheckURL)).To(gomega.BeTrueBecause("expected kubelet to be in healthy state")) + gomega.Expect(e2enode.HealthCheck(kubeletHealthCheckURL)).To(gomega.BeTrueBecause("expected kubelet to be in healthy state")) ginkgo.By("acquiring the lock on lock file i.e /var/run/kubelet.lock") // Open the file with the intention to acquire the lock, this would imitate the behaviour @@ -70,7 +71,7 @@ var _ = SIGDescribe("Lock contention", framework.WithSlow(), framework.WithDisru // Once the lock is acquired, check if the kubelet is in healthy state or not. // It should not be as the lock contention forces the kubelet to stop. gomega.Eventually(ctx, func() bool { - return kubeletHealthCheck(kubeletHealthCheckURL) + return e2enode.HealthCheck(kubeletHealthCheckURL) }, 10*time.Second, time.Second).Should(gomega.BeFalseBecause("expected kubelet to not be in healthy state")) }) }) diff --git a/test/e2e_node/node_container_manager_test.go b/test/e2e_node/node_container_manager_test.go index edad5e3afbc..2bdfd626f07 100644 --- a/test/e2e_node/node_container_manager_test.go +++ b/test/e2e_node/node_container_manager_test.go @@ -38,6 +38,7 @@ import ( "k8s.io/kubernetes/test/e2e/feature" "k8s.io/kubernetes/test/e2e/framework" + e2enode "k8s.io/kubernetes/test/e2e/framework/node" e2enodekubelet "k8s.io/kubernetes/test/e2e_node/kubeletconfig" "github.com/onsi/ginkgo/v2" @@ -127,7 +128,7 @@ var _ = SIGDescribe("Node Container Manager", framework.WithSerial(), func() { ginkgo.By("Started the kubelet") gomega.Consistently(ctx, func(ctx context.Context) bool { - return getNodeReadyStatus(ctx, f) && kubeletHealthCheck(kubeletHealthCheckURL) + return getNodeReadyStatus(ctx, f) && e2enode.HealthCheck(kubeletHealthCheckURL) }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(gomega.BeTrueBecause("node keeps reporting ready status")) }) }) @@ -244,7 +245,7 @@ func runTest(ctx context.Context, f *framework.Framework) error { // wait until the kubelet health check will fail gomega.Eventually(ctx, func() bool { - return kubeletHealthCheck(kubeletHealthCheckURL) + return e2enode.HealthCheck(kubeletHealthCheckURL) }, time.Minute, time.Second).Should(gomega.BeFalseBecause("expected kubelet health check to be failed")) framework.ExpectNoError(e2enodekubelet.WriteKubeletConfigFile(oldCfg)) diff --git a/test/e2e_node/pod_status_test.go b/test/e2e_node/pod_status_test.go index e2151b82645..2789b2d9f7c 100644 --- a/test/e2e_node/pod_status_test.go +++ b/test/e2e_node/pod_status_test.go @@ -26,6 +26,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" "k8s.io/kubernetes/test/e2e/framework" + e2enode "k8s.io/kubernetes/test/e2e/framework/node" e2epod "k8s.io/kubernetes/test/e2e/framework/pod" admissionapi "k8s.io/pod-security-admission/api" ) @@ -97,7 +98,7 @@ var _ = SIGDescribe(framework.WithSerial(), "Pods status phase", func() { ginkgo.By("Stopping the kubelet") startKubelet := mustStopKubelet(ctx, f) gomega.Eventually(ctx, func() bool { - return kubeletHealthCheck(kubeletHealthCheckURL) + return e2enode.HealthCheck(kubeletHealthCheckURL) }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeFalseBecause("kubelet should be stopped")) ginkgo.By("Stopping the pod sandbox") diff --git a/test/e2e_node/services/util.go b/test/e2e_node/services/util.go index cbc48264dfa..6ac56043048 100644 --- a/test/e2e_node/services/util.go +++ b/test/e2e_node/services/util.go @@ -17,9 +17,7 @@ limitations under the License. package services import ( - "crypto/tls" "fmt" - "net/http" "os" "os/signal" "syscall" @@ -27,7 +25,7 @@ import ( "k8s.io/klog/v2" - "k8s.io/kubernetes/test/e2e/framework" + e2enode "k8s.io/kubernetes/test/e2e/framework/node" ) // terminationSignals are signals that cause the program to exit in the @@ -47,12 +45,6 @@ func waitForTerminationSignal() { func readinessCheck(name string, urls []string, errCh <-chan error) error { klog.Infof("Running readiness check for service %q", name) - insecureTransport := http.DefaultTransport.(*http.Transport).Clone() - insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - insecureHTTPClient := &http.Client{ - Transport: insecureTransport, - } - endTime := time.Now().Add(*serverStartTimeout) blockCh := make(chan error) defer close(blockCh) @@ -78,7 +70,7 @@ func readinessCheck(name string, urls []string, errCh <-chan error) error { case <-time.After(time.Second): ready := true for _, url := range urls { - if !healthCheck(insecureHTTPClient, url) { + if !e2enode.HealthCheck(url) { ready = false break } @@ -90,20 +82,3 @@ func readinessCheck(name string, urls []string, errCh <-chan error) error { } return fmt.Errorf("e2e service %q readiness check timeout %v", name, *serverStartTimeout) } - -// Perform a health check. Anything other than a 200-response is treated as a failure. -// Only returns non-recoverable errors. -func healthCheck(client *http.Client, url string) bool { - req, err := http.NewRequest("HEAD", url, nil) - if err != nil { - return false - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", framework.TestContext.BearerToken)) - resp, err := client.Do(req) - if err != nil { - klog.Warningf("Health check on %q failed, error=%v", url, err) - } else if resp.StatusCode != http.StatusOK { - klog.Warningf("Health check on %q failed, status=%d", url, resp.StatusCode) - } - return err == nil && resp.StatusCode == http.StatusOK -} diff --git a/test/e2e_node/standalone_test.go b/test/e2e_node/standalone_test.go index 94f47c08ea1..c097c4c37cf 100644 --- a/test/e2e_node/standalone_test.go +++ b/test/e2e_node/standalone_test.go @@ -42,6 +42,7 @@ import ( "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/test/e2e/feature" "k8s.io/kubernetes/test/e2e/framework" + e2enode "k8s.io/kubernetes/test/e2e/framework/node" testutils "k8s.io/kubernetes/test/utils" imageutils "k8s.io/kubernetes/test/utils/image" admissionapi "k8s.io/pod-security-admission/api" @@ -388,7 +389,7 @@ var _ = SIGDescribe(feature.StandaloneMode, func() { ginkgo.By("restart kubelet") restartKubelet(ctx, true) gomega.Eventually(ctx, func() bool { - return kubeletHealthCheck(kubeletHealthCheckURL) + return e2enode.HealthCheck(kubeletHealthCheckURL) }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeTrueBecause("kubelet should be started")) ginkgo.By("wait for the mirror pod to be updated") @@ -711,7 +712,7 @@ var _ = SIGDescribe(feature.StandaloneMode, framework.WithSerial(), func() { restartKubelet(ctx, true) gomega.Eventually(ctx, func() bool { - return kubeletHealthCheck(kubeletHealthCheckURL) + return e2enode.HealthCheck(kubeletHealthCheckURL) }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeTrueBecause("kubelet should be started")) ginkgo.By("ensuring that pod is running") diff --git a/test/e2e_node/testdeviceplugin/device-plugin.go b/test/e2e_node/testdeviceplugin/device-plugin.go index 9a5e9982f28..4e692cf5ce7 100644 --- a/test/e2e_node/testdeviceplugin/device-plugin.go +++ b/test/e2e_node/testdeviceplugin/device-plugin.go @@ -18,10 +18,8 @@ package testdeviceplugin import ( "context" - "crypto/tls" "fmt" "net" - "net/http" "os" "sync" "time" @@ -30,10 +28,10 @@ import ( "github.com/onsi/gomega" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "k8s.io/klog/v2" kubeletdevicepluginv1beta1 "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" "k8s.io/kubernetes/pkg/cluster/ports" "k8s.io/kubernetes/test/e2e/framework" + e2enode "k8s.io/kubernetes/test/e2e/framework/node" ) var ( @@ -157,27 +155,6 @@ func (dp *DevicePlugin) GetPreferredAllocation(ctx context.Context, request *kub return nil, nil } -func kubeletHealthCheck(url string) bool { - insecureTransport := http.DefaultTransport.(*http.Transport).Clone() - insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - insecureHTTPClient := &http.Client{ - Transport: insecureTransport, - } - - req, err := http.NewRequest(http.MethodHead, url, nil) - if err != nil { - return false - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", framework.TestContext.BearerToken)) - resp, err := insecureHTTPClient.Do(req) - if err != nil { - klog.Warningf("Health check on %q failed, error=%v", url, err) - } else if resp.StatusCode != http.StatusOK { - klog.Warningf("Health check on %q failed, status=%d", url, resp.StatusCode) - } - return err == nil && resp.StatusCode == http.StatusOK -} - func (dp *DevicePlugin) RegisterDevicePlugin(ctx context.Context, uniqueName, resourceName string, devices []*kubeletdevicepluginv1beta1.Device) error { ginkgo.GinkgoHelper() @@ -190,7 +167,7 @@ func (dp *DevicePlugin) RegisterDevicePlugin(ctx context.Context, uniqueName, re ginkgo.By("Ensuring kubelet is healthy") gomega.Eventually(ctx, func() bool { - ok := kubeletHealthCheck(kubeletHealthCheckURL) + ok := e2enode.HealthCheck(kubeletHealthCheckURL) framework.Logf("kubelet health check at %q value=%v", kubeletHealthCheckURL, ok) return ok }, framework.PodStartTimeout, framework.Poll).Should(gomega.BeTrueBecause("expected kubelet health check to be successful")) diff --git a/test/e2e_node/util.go b/test/e2e_node/util.go index bb97ae69050..a74c8edbfb4 100644 --- a/test/e2e_node/util.go +++ b/test/e2e_node/util.go @@ -18,7 +18,6 @@ package e2enode import ( "context" - "crypto/tls" "encoding/json" "flag" "fmt" @@ -197,7 +196,7 @@ func addBeforeEachForCleaningUpPods(f *framework.Framework) { func waitForKubeletToStart(ctx context.Context, f *framework.Framework) { // wait until the kubelet health check will succeed gomega.Eventually(ctx, func() bool { - return kubeletHealthCheck(kubeletHealthCheckURL) + return e2enode.HealthCheck(kubeletHealthCheckURL) }, 2*time.Minute, 5*time.Second).Should(gomega.BeTrueBecause("expected kubelet to be in healthy state")) // Wait for the Kubelet to be ready. @@ -434,7 +433,7 @@ func mustStopKubelet(ctx context.Context, f *framework.Framework) func(ctx conte // wait until the kubelet health check fail gomega.Eventually(ctx, func() bool { - return kubeletHealthCheck(kubeletHealthCheckURL) + return e2enode.HealthCheck(kubeletHealthCheckURL) }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeFalseBecause("kubelet was expected to be stopped but it is still running")) return func(ctx context.Context) { @@ -445,27 +444,6 @@ func mustStopKubelet(ctx context.Context, f *framework.Framework) func(ctx conte } } -func kubeletHealthCheck(url string) bool { - insecureTransport := http.DefaultTransport.(*http.Transport).Clone() - insecureTransport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - insecureHTTPClient := &http.Client{ - Transport: insecureTransport, - } - - req, err := http.NewRequest("HEAD", url, nil) - if err != nil { - return false - } - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", framework.TestContext.BearerToken)) - resp, err := insecureHTTPClient.Do(req) - if err != nil { - klog.Warningf("Health check on %q failed, error=%v", url, err) - } else if resp.StatusCode != http.StatusOK { - klog.Warningf("Health check on %q failed, status=%d", url, resp.StatusCode) - } - return err == nil && resp.StatusCode == http.StatusOK -} - func toCgroupFsName(cgroupName cm.CgroupName) string { if kubeletCfg.CgroupDriver == "systemd" { return cgroupName.ToSystemd() diff --git a/test/e2e_node/util_kubeletconfig.go b/test/e2e_node/util_kubeletconfig.go index 0a6d1a622f0..cbae76e6091 100644 --- a/test/e2e_node/util_kubeletconfig.go +++ b/test/e2e_node/util_kubeletconfig.go @@ -31,6 +31,7 @@ import ( kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" "k8s.io/kubernetes/test/e2e/framework" + e2enode "k8s.io/kubernetes/test/e2e/framework/node" e2enodekubelet "k8s.io/kubernetes/test/e2e_node/kubeletconfig" "github.com/onsi/ginkgo/v2" @@ -154,7 +155,7 @@ func withStoppedKubelet(ctx context.Context, f *framework.Framework, ensureConsi kubeletStart(ctx) if ensureConsistentReadyNode { gomega.Consistently(ctx, func(ctx context.Context) bool { - return getNodeReadyStatus(ctx, f) && kubeletHealthCheck(kubeletHealthCheckURL) + return getNodeReadyStatus(ctx, f) && e2enode.HealthCheck(kubeletHealthCheckURL) }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(gomega.BeTrueBecause("node keeps reporting ready status")) } }()