mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	feat: Add flagz endpoint for kube-scheduler
This commit is contained in:
		| @@ -26,11 +26,15 @@ import ( | ||||
| 	restclient "k8s.io/client-go/rest" | ||||
| 	"k8s.io/client-go/tools/events" | ||||
| 	"k8s.io/client-go/tools/leaderelection" | ||||
| 	"k8s.io/component-base/zpages/flagz" | ||||
| 	kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" | ||||
| ) | ||||
|  | ||||
| // Config has all the context to run a Scheduler | ||||
| type Config struct { | ||||
| 	// Flagz is the Reader interface to get flags for flagz page. | ||||
| 	Flagz flagz.Reader | ||||
|  | ||||
| 	// ComponentConfig is the scheduler server's configuration object. | ||||
| 	ComponentConfig kubeschedulerconfig.KubeSchedulerConfiguration | ||||
|  | ||||
|   | ||||
| @@ -46,6 +46,8 @@ import ( | ||||
| 	logsapi "k8s.io/component-base/logs/api/v1" | ||||
| 	"k8s.io/component-base/metrics" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	zpagesfeatures "k8s.io/component-base/zpages/features" | ||||
| 	"k8s.io/component-base/zpages/flagz" | ||||
| 	"k8s.io/klog/v2" | ||||
| 	schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" | ||||
| 	"k8s.io/kubernetes/pkg/scheduler" | ||||
| @@ -260,6 +262,11 @@ func (o *Options) ApplyTo(logger klog.Logger, c *schedulerappconfig.Config) erro | ||||
| 	if o.Deprecated != nil { | ||||
| 		c.PodMaxInUnschedulablePodsDuration = o.Deprecated.PodMaxInUnschedulablePodsDuration | ||||
| 	} | ||||
| 	if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentFlagz) { | ||||
| 		if o.Flags != nil { | ||||
| 			c.Flagz = flagz.NamedFlagSetsReader{FlagSets: *o.Flags} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|   | ||||
| @@ -56,6 +56,8 @@ import ( | ||||
| 	"k8s.io/component-base/term" | ||||
| 	utilversion "k8s.io/component-base/version" | ||||
| 	"k8s.io/component-base/version/verflag" | ||||
| 	zpagesfeatures "k8s.io/component-base/zpages/features" | ||||
| 	"k8s.io/component-base/zpages/flagz" | ||||
| 	"k8s.io/klog/v2" | ||||
| 	schedulerserverconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" | ||||
| 	"k8s.io/kubernetes/cmd/kube-scheduler/app/options" | ||||
| @@ -68,6 +70,10 @@ import ( | ||||
| 	"k8s.io/kubernetes/pkg/scheduler/profile" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	kubeScheduler = "kube-scheduler" | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) | ||||
| 	utilruntime.Must(features.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) | ||||
| @@ -224,7 +230,7 @@ func Run(ctx context.Context, cc *schedulerserverconfig.CompletedConfig, sched * | ||||
| 			cc.Client, | ||||
| 			metav1.NamespaceSystem, | ||||
| 			cc.LeaderElection.Lock.Identity(), | ||||
| 			"kube-scheduler", | ||||
| 			kubeScheduler, | ||||
| 			binaryVersion.FinalizeVersion(), | ||||
| 			emulationVersion.FinalizeVersion(), | ||||
| 			coordinationv1.OldestEmulationVersion, | ||||
| @@ -236,9 +242,9 @@ func Run(ctx context.Context, cc *schedulerserverconfig.CompletedConfig, sched * | ||||
| 		go leaseCandidate.Run(ctx) | ||||
| 	} | ||||
|  | ||||
| 	// Start up the healthz server. | ||||
| 	// Start up the server for endpoints. | ||||
| 	if cc.SecureServing != nil { | ||||
| 		handler := buildHandlerChain(newHealthEndpointsAndMetricsHandler(&cc.ComponentConfig, cc.InformerFactory, isLeader, checks, readyzChecks), cc.Authentication.Authenticator, cc.Authorization.Authorizer) | ||||
| 		handler := buildHandlerChain(newEndpointsHandler(&cc.ComponentConfig, cc.InformerFactory, isLeader, checks, readyzChecks, cc.Flagz), cc.Authentication.Authenticator, cc.Authorization.Authorizer) | ||||
| 		// TODO: handle stoppedCh and listenerStoppedCh returned by c.SecureServing.Serve | ||||
| 		if _, _, err := cc.SecureServing.Serve(handler, 0, ctx.Done()); err != nil { | ||||
| 			// fail early for secure handlers, removing the old error loop from above | ||||
| @@ -344,11 +350,11 @@ func installMetricHandler(pathRecorderMux *mux.PathRecorderMux, informers inform | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // newHealthEndpointsAndMetricsHandler creates an API health server from the config, and will also | ||||
| // embed the metrics handler. | ||||
| // newEndpointsHandler creates an API health server from the config, and will also | ||||
| // embed the metrics handler and z-pages handler. | ||||
| // TODO: healthz check is deprecated, please use livez and readyz instead. Will be removed in the future. | ||||
| func newHealthEndpointsAndMetricsHandler(config *kubeschedulerconfig.KubeSchedulerConfiguration, informers informers.SharedInformerFactory, isLeader func() bool, healthzChecks, readyzChecks []healthz.HealthChecker) http.Handler { | ||||
| 	pathRecorderMux := mux.NewPathRecorderMux("kube-scheduler") | ||||
| func newEndpointsHandler(config *kubeschedulerconfig.KubeSchedulerConfiguration, informers informers.SharedInformerFactory, isLeader func() bool, healthzChecks, readyzChecks []healthz.HealthChecker, flagReader flagz.Reader) http.Handler { | ||||
| 	pathRecorderMux := mux.NewPathRecorderMux(kubeScheduler) | ||||
| 	healthz.InstallHandler(pathRecorderMux, healthzChecks...) | ||||
| 	healthz.InstallLivezHandler(pathRecorderMux) | ||||
| 	healthz.InstallReadyzHandler(pathRecorderMux, readyzChecks...) | ||||
| @@ -362,6 +368,12 @@ func newHealthEndpointsAndMetricsHandler(config *kubeschedulerconfig.KubeSchedul | ||||
| 		} | ||||
| 		routes.DebugFlags{}.Install(pathRecorderMux, "v", routes.StringFlagPutHandler(logs.GlogSetter)) | ||||
| 	} | ||||
|  | ||||
| 	if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentFlagz) { | ||||
| 		if flagReader != nil { | ||||
| 			flagz.Install(pathRecorderMux, kubeScheduler, flagReader) | ||||
| 		} | ||||
| 	} | ||||
| 	return pathRecorderMux | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -24,16 +24,21 @@ import ( | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	utilfeature "k8s.io/apiserver/pkg/util/feature" | ||||
| 	featuregatetesting "k8s.io/component-base/featuregate/testing" | ||||
| 	"k8s.io/component-base/zpages/features" | ||||
| 	"k8s.io/klog/v2/ktesting" | ||||
| 	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" | ||||
| 	kubeschedulertesting "k8s.io/kubernetes/cmd/kube-scheduler/app/testing" | ||||
| 	"k8s.io/kubernetes/test/integration/framework" | ||||
| ) | ||||
| 
 | ||||
| func TestHealthEndpoints(t *testing.T) { | ||||
| func TestEndpointHandlers(t *testing.T) { | ||||
| 	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ComponentFlagz, true) | ||||
| 	server, configStr, _, err := startTestAPIServer(t) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to start kube-apiserver server: %v", err) | ||||
| @@ -64,52 +69,64 @@ func TestHealthEndpoints(t *testing.T) { | ||||
| 	}() | ||||
| 
 | ||||
| 	tests := []struct { | ||||
| 		name             string | ||||
| 		path             string | ||||
| 		useBrokenConfig  bool | ||||
| 		wantResponseCode int | ||||
| 		name                 string | ||||
| 		path                 string | ||||
| 		useBrokenConfig      bool | ||||
| 		requestHeader        map[string]string | ||||
| 		wantResponseCode     int | ||||
| 		wantResponseBodyRegx string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			"/healthz", | ||||
| 			"/healthz", | ||||
| 			false, | ||||
| 			http.StatusOK, | ||||
| 			name:             "/healthz", | ||||
| 			path:             "/healthz", | ||||
| 			useBrokenConfig:  false, | ||||
| 			wantResponseCode: http.StatusOK, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/livez", | ||||
| 			"/livez", | ||||
| 			false, | ||||
| 			http.StatusOK, | ||||
| 			name:             "/livez", | ||||
| 			path:             "/livez", | ||||
| 			useBrokenConfig:  false, | ||||
| 			wantResponseCode: http.StatusOK, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/livez with ping check", | ||||
| 			"/livez/ping", | ||||
| 			false, | ||||
| 			http.StatusOK, | ||||
| 			name:             "/livez with ping check", | ||||
| 			path:             "/livez/ping", | ||||
| 			useBrokenConfig:  false, | ||||
| 			wantResponseCode: http.StatusOK, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/readyz", | ||||
| 			"/readyz", | ||||
| 			false, | ||||
| 			http.StatusOK, | ||||
| 			name:             "/readyz", | ||||
| 			path:             "/readyz", | ||||
| 			useBrokenConfig:  false, | ||||
| 			wantResponseCode: http.StatusOK, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/readyz with sched-handler-sync", | ||||
| 			"/readyz/sched-handler-sync", | ||||
| 			false, | ||||
| 			http.StatusOK, | ||||
| 			name:             "/readyz with sched-handler-sync", | ||||
| 			path:             "/readyz/sched-handler-sync", | ||||
| 			useBrokenConfig:  false, | ||||
| 			wantResponseCode: http.StatusOK, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/readyz with shutdown", | ||||
| 			"/readyz/shutdown", | ||||
| 			false, | ||||
| 			http.StatusOK, | ||||
| 			name:             "/readyz with shutdown", | ||||
| 			path:             "/readyz/shutdown", | ||||
| 			useBrokenConfig:  false, | ||||
| 			wantResponseCode: http.StatusOK, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/readyz with broken apiserver", | ||||
| 			"/readyz", | ||||
| 			true, | ||||
| 			http.StatusInternalServerError, | ||||
| 			name:             "/readyz with broken apiserver", | ||||
| 			path:             "/readyz", | ||||
| 			useBrokenConfig:  true, | ||||
| 			wantResponseCode: http.StatusInternalServerError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:             "/flagz", | ||||
| 			path:             "/flagz", | ||||
| 			requestHeader:    map[string]string{"Accept": "text/plain"}, | ||||
| 			wantResponseCode: http.StatusOK, | ||||
| 			wantResponseBodyRegx: `^\n` + | ||||
| 				`kube-scheduler flags\n` + | ||||
| 				`Warning: This endpoint is not meant to be machine parseable, ` + | ||||
| 				`has no formatting compatibility guarantees and is for debugging purposes only.`, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| @@ -141,6 +158,11 @@ func TestHealthEndpoints(t *testing.T) { | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("failed to request: %v", err) | ||||
| 			} | ||||
| 			if tt.requestHeader != nil { | ||||
| 				for k, v := range tt.requestHeader { | ||||
| 					req.Header.Set(k, v) | ||||
| 				} | ||||
| 			} | ||||
| 			r, err := client.Do(req) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("failed to GET %s from component: %v", tt.path, err) | ||||
| @@ -156,6 +178,15 @@ func TestHealthEndpoints(t *testing.T) { | ||||
| 			if got, expected := r.StatusCode, tt.wantResponseCode; got != expected { | ||||
| 				t.Fatalf("expected http %d at %s of component, got: %d %q", expected, tt.path, got, string(body)) | ||||
| 			} | ||||
| 			if tt.wantResponseBodyRegx != "" { | ||||
| 				matched, err := regexp.MatchString(tt.wantResponseBodyRegx, string(body)) | ||||
| 				if err != nil { | ||||
| 					t.Fatalf("failed to compile regex: %v", err) | ||||
| 				} | ||||
| 				if !matched { | ||||
| 					t.Fatalf("response body does not match regex.\nExpected:\n%s\n\nGot:\n%s", tt.wantResponseBodyRegx, string(body)) | ||||
| 				} | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 yongruilin
					yongruilin