feat: Integrate device plugin registration gRPC server health checks.

This commit is contained in:
zhangzhifei16 2024-10-30 13:26:17 +08:00 committed by zhifei92
parent e5a31e8bbc
commit 1381e41f28
13 changed files with 104 additions and 12 deletions

View File

@ -28,6 +28,7 @@ import (
// TODO: Migrate kubelet to either use its own internal objects or client library. // TODO: Migrate kubelet to either use its own internal objects or client library.
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apiserver/pkg/server/healthz"
internalapi "k8s.io/cri-api/pkg/apis" internalapi "k8s.io/cri-api/pkg/apis"
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
@ -122,6 +123,10 @@ type ContainerManager interface {
// registration. // registration.
GetPluginRegistrationHandlers() map[string]cache.PluginHandler GetPluginRegistrationHandlers() map[string]cache.PluginHandler
// GetHealthCheckers returns a set of health checkers for all plugins.
// These checkers are integrated into the systemd watchdog to monitor the service's health.
GetHealthCheckers() []healthz.HealthChecker
// ShouldResetExtendedResourceCapacity returns whether or not the extended resources should be zeroed, // ShouldResetExtendedResourceCapacity returns whether or not the extended resources should be zeroed,
// due to node recreation. // due to node recreation.
ShouldResetExtendedResourceCapacity() bool ShouldResetExtendedResourceCapacity() bool

View File

@ -41,6 +41,7 @@ import (
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/server/healthz"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
@ -661,6 +662,10 @@ func (cm *containerManagerImpl) GetPluginRegistrationHandlers() map[string]cache
return res return res
} }
func (cm *containerManagerImpl) GetHealthCheckers() []healthz.HealthChecker {
return []healthz.HealthChecker{cm.deviceManager.GetHealthChecker()}
}
// TODO: move the GetResources logic to PodContainerManager. // TODO: move the GetResources logic to PodContainerManager.
func (cm *containerManagerImpl) GetResources(ctx context.Context, pod *v1.Pod, container *v1.Container) (*kubecontainer.RunContainerOptions, error) { func (cm *containerManagerImpl) GetResources(ctx context.Context, pod *v1.Pod, container *v1.Container) (*kubecontainer.RunContainerOptions, error) {
logger := klog.FromContext(ctx) logger := klog.FromContext(ctx)

View File

@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/server/healthz"
internalapi "k8s.io/cri-api/pkg/apis" internalapi "k8s.io/cri-api/pkg/apis"
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager" "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager"
@ -95,6 +96,10 @@ func (cm *containerManagerStub) GetPluginRegistrationHandlers() map[string]cache
return nil return nil
} }
func (cm *containerManagerStub) GetHealthCheckers() []healthz.HealthChecker {
return []healthz.HealthChecker{}
}
func (cm *containerManagerStub) GetDevicePluginResourceCapacity() (v1.ResourceList, v1.ResourceList, []string) { func (cm *containerManagerStub) GetDevicePluginResourceCapacity() (v1.ResourceList, v1.ResourceList, []string) {
return cm.extendedPluginResources, cm.extendedPluginResources, []string{} return cm.extendedPluginResources, cm.extendedPluginResources, []string{}
} }

View File

@ -36,6 +36,7 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/server/healthz"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record" "k8s.io/client-go/tools/record"
internalapi "k8s.io/cri-api/pkg/apis" internalapi "k8s.io/cri-api/pkg/apis"
@ -224,6 +225,10 @@ func (cm *containerManagerImpl) GetPluginRegistrationHandlers() map[string]cache
return map[string]cache.PluginHandler{pluginwatcherapi.DevicePlugin: cm.deviceManager.GetWatcherHandler()} return map[string]cache.PluginHandler{pluginwatcherapi.DevicePlugin: cm.deviceManager.GetWatcherHandler()}
} }
func (cm *containerManagerImpl) GetHealthCheckers() []healthz.HealthChecker {
return []healthz.HealthChecker{cm.deviceManager.GetHealthChecker()}
}
func (cm *containerManagerImpl) GetDevicePluginResourceCapacity() (v1.ResourceList, v1.ResourceList, []string) { func (cm *containerManagerImpl) GetDevicePluginResourceCapacity() (v1.ResourceList, v1.ResourceList, []string) {
return cm.deviceManager.GetCapacity() return cm.deviceManager.GetCapacity()
} }

View File

@ -34,6 +34,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
errorsutil "k8s.io/apimachinery/pkg/util/errors" errorsutil "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/server/healthz"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" pluginapi "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
@ -326,6 +327,11 @@ func (m *ManagerImpl) GetWatcherHandler() cache.PluginHandler {
return m.server return m.server
} }
// GetHealthChecker returns the plugin handler
func (m *ManagerImpl) GetHealthChecker() healthz.HealthChecker {
return m.server
}
// checkpointFile returns device plugin checkpoint file path. // checkpointFile returns device plugin checkpoint file path.
func (m *ManagerImpl) checkpointFile() string { func (m *ManagerImpl) checkpointFile() string {
return filepath.Join(m.checkpointdir, kubeletDeviceManagerCheckpoint) return filepath.Join(m.checkpointdir, kubeletDeviceManagerCheckpoint)

View File

@ -20,6 +20,7 @@ import (
"context" "context"
"fmt" "fmt"
"net" "net"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"sync" "sync"
@ -28,6 +29,7 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
core "k8s.io/api/core/v1" core "k8s.io/api/core/v1"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/klog/v2" "k8s.io/klog/v2"
api "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1" api "k8s.io/kubelet/pkg/apis/deviceplugin/v1beta1"
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper" v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
@ -39,6 +41,7 @@ import (
// Server interface provides methods for Device plugin registration server. // Server interface provides methods for Device plugin registration server.
type Server interface { type Server interface {
cache.PluginHandler cache.PluginHandler
healthz.HealthChecker
Start() error Start() error
Stop() error Stop() error
SocketPath() string SocketPath() string
@ -53,6 +56,9 @@ type server struct {
rhandler RegistrationHandler rhandler RegistrationHandler
chandler ClientHandler chandler ClientHandler
clients map[string]Client clients map[string]Client
// isStarted indicates whether the service has started successfully.
isStarted bool
} }
// NewServer returns an initialized device plugin registration server. // NewServer returns an initialized device plugin registration server.
@ -109,7 +115,9 @@ func (s *server) Start() error {
api.RegisterRegistrationServer(s.grpc, s) api.RegisterRegistrationServer(s.grpc, s)
go func() { go func() {
defer s.wg.Done() defer s.wg.Done()
s.setHealthy()
if err = s.grpc.Serve(ln); err != nil { if err = s.grpc.Serve(ln); err != nil {
s.setUnhealthy()
klog.ErrorS(err, "Error while serving device plugin registration grpc server") klog.ErrorS(err, "Error while serving device plugin registration grpc server")
} }
}() }()
@ -134,6 +142,9 @@ func (s *server) Stop() error {
s.grpc.Stop() s.grpc.Stop()
s.wg.Wait() s.wg.Wait()
s.grpc = nil s.grpc = nil
// During kubelet termination, we do not need the registration server,
// and we consider the kubelet to be healthy even when it is down.
s.setHealthy()
return nil return nil
} }
@ -190,3 +201,24 @@ func (s *server) visitClients(visit func(r string, c Client)) {
} }
s.mutex.Unlock() s.mutex.Unlock()
} }
func (s *server) Name() string {
return "device-plugin"
}
func (s *server) Check(_ *http.Request) error {
if s.isStarted {
return nil
}
return fmt.Errorf("device plugin registration gRPC server failed and no device plugins can register")
}
// setHealthy sets the health status of the gRPC server.
func (s *server) setHealthy() {
s.isStarted = true
}
// setUnhealthy sets the health status of the gRPC server to unhealthy.
func (s *server) setUnhealthy() {
s.isStarted = false
}

View File

@ -21,6 +21,7 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/server/healthz"
"k8s.io/kubernetes/pkg/kubelet/cm/containermap" "k8s.io/kubernetes/pkg/kubelet/cm/containermap"
"k8s.io/kubernetes/pkg/kubelet/cm/resourceupdates" "k8s.io/kubernetes/pkg/kubelet/cm/resourceupdates"
"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager" "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
@ -62,6 +63,7 @@ type Manager interface {
// GetWatcherHandler returns the plugin handler for the device manager. // GetWatcherHandler returns the plugin handler for the device manager.
GetWatcherHandler() cache.PluginHandler GetWatcherHandler() cache.PluginHandler
GetHealthChecker() healthz.HealthChecker
// GetDevices returns information about the devices assigned to pods and containers // GetDevices returns information about the devices assigned to pods and containers
GetDevices(podUID, containerName string) ResourceDeviceInstances GetDevices(podUID, containerName string) ResourceDeviceInstances

View File

@ -24,6 +24,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/server/healthz"
internalapi "k8s.io/cri-api/pkg/apis" internalapi "k8s.io/cri-api/pkg/apis"
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
"k8s.io/kubernetes/pkg/kubelet/cm/cpumanager" "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager"
@ -131,6 +132,13 @@ func (cm *FakeContainerManager) GetPluginRegistrationHandlers() map[string]cache
return nil return nil
} }
func (cm *FakeContainerManager) GetHealthCheckers() []healthz.HealthChecker {
cm.Lock()
defer cm.Unlock()
cm.CalledFunctions = append(cm.CalledFunctions, "GetPluginRegistrationServerChecker")
return []healthz.HealthChecker{}
}
func (cm *FakeContainerManager) GetDevicePluginResourceCapacity() (v1.ResourceList, v1.ResourceList, []string) { func (cm *FakeContainerManager) GetDevicePluginResourceCapacity() (v1.ResourceList, v1.ResourceList, []string) {
cm.Lock() cm.Lock()
defer cm.Unlock() defer cm.Unlock()

View File

@ -34,6 +34,7 @@ import (
"time" "time"
cadvisorapi "github.com/google/cadvisor/info/v1" cadvisorapi "github.com/google/cadvisor/info/v1"
inuserns "github.com/moby/sys/userns"
"github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux"
"go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/codes"
@ -46,7 +47,6 @@ import (
utilfs "k8s.io/kubernetes/pkg/util/filesystem" utilfs "k8s.io/kubernetes/pkg/util/filesystem"
netutils "k8s.io/utils/net" netutils "k8s.io/utils/net"
inuserns "github.com/moby/sys/userns"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
@ -961,7 +961,8 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
if utilfeature.DefaultFeatureGate.Enabled(features.SystemdWatchdog) { if utilfeature.DefaultFeatureGate.Enabled(features.SystemdWatchdog) {
// NewHealthChecker returns an error indicating that the watchdog is configured but the configuration is incorrect, // NewHealthChecker returns an error indicating that the watchdog is configured but the configuration is incorrect,
// the kubelet will not be started. // the kubelet will not be started.
klet.healthChecker, err = watchdog.NewHealthChecker(klet) checkers := klet.containerManager.GetHealthCheckers()
klet.healthChecker, err = watchdog.NewHealthChecker(klet, watchdog.WithExtendedCheckers(checkers))
if err != nil { if err != nil {
return nil, fmt.Errorf("create health checker: %w", err) return nil, fmt.Errorf("create health checker: %w", err)
} }
@ -2919,12 +2920,12 @@ func (kl *Kubelet) BirthCry() {
// ListenAndServe runs the kubelet HTTP server. // ListenAndServe runs the kubelet HTTP server.
func (kl *Kubelet) ListenAndServe(kubeCfg *kubeletconfiginternal.KubeletConfiguration, tlsOptions *server.TLSOptions, func (kl *Kubelet) ListenAndServe(kubeCfg *kubeletconfiginternal.KubeletConfiguration, tlsOptions *server.TLSOptions,
auth server.AuthInterface, tp trace.TracerProvider) { auth server.AuthInterface, tp trace.TracerProvider) {
server.ListenAndServeKubeletServer(kl, kl.resourceAnalyzer, kubeCfg, tlsOptions, auth, tp) server.ListenAndServeKubeletServer(kl, kl.resourceAnalyzer, kl.containerManager.GetHealthCheckers(), kubeCfg, tlsOptions, auth, tp)
} }
// ListenAndServeReadOnly runs the kubelet HTTP server in read-only mode. // ListenAndServeReadOnly runs the kubelet HTTP server in read-only mode.
func (kl *Kubelet) ListenAndServeReadOnly(address net.IP, port uint, tp trace.TracerProvider) { func (kl *Kubelet) ListenAndServeReadOnly(address net.IP, port uint, tp trace.TracerProvider) {
server.ListenAndServeKubeletReadOnlyServer(kl, kl.resourceAnalyzer, address, port, tp) server.ListenAndServeKubeletReadOnlyServer(kl, kl.resourceAnalyzer, kl.containerManager.GetHealthCheckers(), address, port, tp)
} }
// ListenAndServePodResources runs the kubelet podresources grpc service // ListenAndServePodResources runs the kubelet podresources grpc service

View File

@ -114,6 +114,7 @@ type Server struct {
metricsBuckets sets.Set[string] metricsBuckets sets.Set[string]
metricsMethodBuckets sets.Set[string] metricsMethodBuckets sets.Set[string]
resourceAnalyzer stats.ResourceAnalyzer resourceAnalyzer stats.ResourceAnalyzer
extendedCheckers []healthz.HealthChecker
} }
// TLSOptions holds the TLS options. // TLSOptions holds the TLS options.
@ -156,6 +157,7 @@ func (a *filteringContainer) RegisteredHandlePaths() []string {
func ListenAndServeKubeletServer( func ListenAndServeKubeletServer(
host HostInterface, host HostInterface,
resourceAnalyzer stats.ResourceAnalyzer, resourceAnalyzer stats.ResourceAnalyzer,
checkers []healthz.HealthChecker,
kubeCfg *kubeletconfiginternal.KubeletConfiguration, kubeCfg *kubeletconfiginternal.KubeletConfiguration,
tlsOptions *TLSOptions, tlsOptions *TLSOptions,
auth AuthInterface, auth AuthInterface,
@ -164,7 +166,7 @@ func ListenAndServeKubeletServer(
address := netutils.ParseIPSloppy(kubeCfg.Address) address := netutils.ParseIPSloppy(kubeCfg.Address)
port := uint(kubeCfg.Port) port := uint(kubeCfg.Port)
klog.InfoS("Starting to listen", "address", address, "port", port) klog.InfoS("Starting to listen", "address", address, "port", port)
handler := NewServer(host, resourceAnalyzer, auth, kubeCfg) handler := NewServer(host, resourceAnalyzer, checkers, auth, kubeCfg)
if utilfeature.DefaultFeatureGate.Enabled(features.KubeletTracing) { if utilfeature.DefaultFeatureGate.Enabled(features.KubeletTracing) {
handler.InstallTracingFilter(tp) handler.InstallTracingFilter(tp)
@ -198,11 +200,12 @@ func ListenAndServeKubeletServer(
func ListenAndServeKubeletReadOnlyServer( func ListenAndServeKubeletReadOnlyServer(
host HostInterface, host HostInterface,
resourceAnalyzer stats.ResourceAnalyzer, resourceAnalyzer stats.ResourceAnalyzer,
checkers []healthz.HealthChecker,
address net.IP, address net.IP,
port uint, port uint,
tp oteltrace.TracerProvider) { tp oteltrace.TracerProvider) {
klog.InfoS("Starting to listen read-only", "address", address, "port", port) klog.InfoS("Starting to listen read-only", "address", address, "port", port)
s := NewServer(host, resourceAnalyzer, nil, nil) s := NewServer(host, resourceAnalyzer, checkers, nil, nil)
if utilfeature.DefaultFeatureGate.Enabled(features.KubeletTracing) { if utilfeature.DefaultFeatureGate.Enabled(features.KubeletTracing) {
s.InstallTracingFilter(tp, otelrestful.WithPublicEndpoint()) s.InstallTracingFilter(tp, otelrestful.WithPublicEndpoint())
@ -278,6 +281,7 @@ type HostInterface interface {
func NewServer( func NewServer(
host HostInterface, host HostInterface,
resourceAnalyzer stats.ResourceAnalyzer, resourceAnalyzer stats.ResourceAnalyzer,
checkers []healthz.HealthChecker,
auth AuthInterface, auth AuthInterface,
kubeCfg *kubeletconfiginternal.KubeletConfiguration) Server { kubeCfg *kubeletconfiginternal.KubeletConfiguration) Server {
@ -288,6 +292,7 @@ func NewServer(
restfulCont: &filteringContainer{Container: restful.NewContainer()}, restfulCont: &filteringContainer{Container: restful.NewContainer()},
metricsBuckets: sets.New[string](), metricsBuckets: sets.New[string](),
metricsMethodBuckets: sets.New[string]("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"), metricsMethodBuckets: sets.New[string]("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"),
extendedCheckers: checkers,
} }
if auth != nil { if auth != nil {
server.InstallAuthFilter() server.InstallAuthFilter()
@ -392,11 +397,13 @@ func (s *Server) getMetricMethodBucket(method string) string {
// patterns with the restful Container. // patterns with the restful Container.
func (s *Server) InstallDefaultHandlers() { func (s *Server) InstallDefaultHandlers() {
s.addMetricsBucketMatcher("healthz") s.addMetricsBucketMatcher("healthz")
healthz.InstallHandler(s.restfulCont, checkers := []healthz.HealthChecker{
healthz.PingHealthz, healthz.PingHealthz,
healthz.LogHealthz, healthz.LogHealthz,
healthz.NamedCheck("syncloop", s.host.SyncLoopHealthCheck), healthz.NamedCheck("syncloop", s.host.SyncLoopHealthCheck),
) }
checkers = append(checkers, s.extendedCheckers...)
healthz.InstallHandler(s.restfulCont, checkers...)
slis.SLIMetricsWithReset{}.Install(s.restfulCont) slis.SLIMetricsWithReset{}.Install(s.restfulCont)

View File

@ -53,6 +53,7 @@ import (
"k8s.io/utils/ptr" "k8s.io/utils/ptr"
// Do some initialization to decode the query parameters correctly. // Do some initialization to decode the query parameters correctly.
"k8s.io/apiserver/pkg/server/healthz"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing" featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubelet/pkg/cri/streaming" "k8s.io/kubelet/pkg/cri/streaming"
@ -368,6 +369,7 @@ func newServerTestWithDebuggingHandlers(kubeCfg *kubeletconfiginternal.KubeletCo
server := NewServer( server := NewServer(
fw.fakeKubelet, fw.fakeKubelet,
stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute, &record.FakeRecorder{}), stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute, &record.FakeRecorder{}),
[]healthz.HealthChecker{},
fw.fakeAuth, fw.fakeAuth,
kubeCfg, kubeCfg,
) )
@ -1650,8 +1652,8 @@ func TestNewServerRegistersMetricsSLIsEndpointTwice(t *testing.T) {
} }
resourceAnalyzer := stats.NewResourceAnalyzer(nil, time.Minute, &record.FakeRecorder{}) resourceAnalyzer := stats.NewResourceAnalyzer(nil, time.Minute, &record.FakeRecorder{})
server1 := NewServer(host, resourceAnalyzer, nil, nil) server1 := NewServer(host, resourceAnalyzer, []healthz.HealthChecker{}, nil, nil)
server2 := NewServer(host, resourceAnalyzer, nil, nil) server2 := NewServer(host, resourceAnalyzer, []healthz.HealthChecker{}, nil, nil)
// Check if both servers registered the /metrics/slis endpoint // Check if both servers registered the /metrics/slis endpoint
assert.Contains(t, server1.restfulCont.RegisteredHandlePaths(), "/metrics/slis", "First server should register /metrics/slis") assert.Contains(t, server1.restfulCont.RegisteredHandlePaths(), "/metrics/slis", "First server should register /metrics/slis")

View File

@ -59,6 +59,12 @@ func WithWatchdogClient(watchdog WatchdogClient) Option {
} }
} }
func WithExtendedCheckers(checkers []healthz.HealthChecker) Option {
return func(hc *healthChecker) {
hc.checkers = append(hc.checkers, checkers...)
}
}
type healthChecker struct { type healthChecker struct {
checkers []healthz.HealthChecker checkers []healthz.HealthChecker
retryBackoff wait.Backoff retryBackoff wait.Backoff
@ -109,7 +115,7 @@ func NewHealthChecker(syncLoop syncLoopHealthChecker, opts ...Option) (HealthChe
Jitter: 0.1, Jitter: 0.1,
Steps: 2, Steps: 2,
} }
hc.checkers = checkers hc.checkers = append(hc.checkers, checkers...)
hc.retryBackoff = retryBackoff hc.retryBackoff = retryBackoff
hc.interval = watchdogVal / 2 hc.interval = watchdogVal / 2

View File

@ -19,12 +19,20 @@ limitations under the License.
package watchdog package watchdog
import "k8s.io/apiserver/pkg/server/healthz"
type healthCheckerUnsupported struct{} type healthCheckerUnsupported struct{}
var _ HealthChecker = &healthCheckerUnsupported{} var _ HealthChecker = &healthCheckerUnsupported{}
type Option func(*healthCheckerUnsupported)
func WithExtendedCheckers(checkers []healthz.HealthChecker) Option {
return nil
}
// NewHealthChecker creates a fake one here // NewHealthChecker creates a fake one here
func NewHealthChecker(_ syncLoopHealthChecker) (HealthChecker, error) { func NewHealthChecker(_ syncLoopHealthChecker, _ ...Option) (HealthChecker, error) {
return &healthCheckerUnsupported{}, nil return &healthCheckerUnsupported{}, nil
} }