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.
This commit is contained in:
Sotiris Salloumis 2026-04-27 14:49:02 +02:00
parent 036205cc2e
commit 5486715fbf
10 changed files with 49 additions and 91 deletions

View file

@ -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
}

View file

@ -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).

View file

@ -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"))
})
})

View file

@ -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))

View file

@ -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")

View file

@ -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
}

View file

@ -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")

View file

@ -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"))

View file

@ -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()

View file

@ -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"))
}
}()