mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	Merge pull request #128818 from yongruilin/flagz-kube-scheduler
feat: Add flagz endpoint for kube-scheduler
This commit is contained in:
		| @@ -26,11 +26,15 @@ import ( | |||||||
| 	restclient "k8s.io/client-go/rest" | 	restclient "k8s.io/client-go/rest" | ||||||
| 	"k8s.io/client-go/tools/events" | 	"k8s.io/client-go/tools/events" | ||||||
| 	"k8s.io/client-go/tools/leaderelection" | 	"k8s.io/client-go/tools/leaderelection" | ||||||
|  | 	"k8s.io/component-base/zpages/flagz" | ||||||
| 	kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" | 	kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Config has all the context to run a Scheduler | // Config has all the context to run a Scheduler | ||||||
| type Config struct { | 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 is the scheduler server's configuration object. | ||||||
| 	ComponentConfig kubeschedulerconfig.KubeSchedulerConfiguration | 	ComponentConfig kubeschedulerconfig.KubeSchedulerConfiguration | ||||||
|  |  | ||||||
|   | |||||||
| @@ -46,6 +46,8 @@ import ( | |||||||
| 	logsapi "k8s.io/component-base/logs/api/v1" | 	logsapi "k8s.io/component-base/logs/api/v1" | ||||||
| 	"k8s.io/component-base/metrics" | 	"k8s.io/component-base/metrics" | ||||||
| 	utilversion "k8s.io/component-base/version" | 	utilversion "k8s.io/component-base/version" | ||||||
|  | 	zpagesfeatures "k8s.io/component-base/zpages/features" | ||||||
|  | 	"k8s.io/component-base/zpages/flagz" | ||||||
| 	"k8s.io/klog/v2" | 	"k8s.io/klog/v2" | ||||||
| 	schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" | 	schedulerappconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" | ||||||
| 	"k8s.io/kubernetes/pkg/scheduler" | 	"k8s.io/kubernetes/pkg/scheduler" | ||||||
| @@ -260,6 +262,11 @@ func (o *Options) ApplyTo(logger klog.Logger, c *schedulerappconfig.Config) erro | |||||||
| 	if o.Deprecated != nil { | 	if o.Deprecated != nil { | ||||||
| 		c.PodMaxInUnschedulablePodsDuration = o.Deprecated.PodMaxInUnschedulablePodsDuration | 		c.PodMaxInUnschedulablePodsDuration = o.Deprecated.PodMaxInUnschedulablePodsDuration | ||||||
| 	} | 	} | ||||||
|  | 	if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentFlagz) { | ||||||
|  | 		if o.Flags != nil { | ||||||
|  | 			c.Flagz = flagz.NamedFlagSetsReader{FlagSets: *o.Flags} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|   | |||||||
| @@ -56,6 +56,8 @@ import ( | |||||||
| 	"k8s.io/component-base/term" | 	"k8s.io/component-base/term" | ||||||
| 	utilversion "k8s.io/component-base/version" | 	utilversion "k8s.io/component-base/version" | ||||||
| 	"k8s.io/component-base/version/verflag" | 	"k8s.io/component-base/version/verflag" | ||||||
|  | 	zpagesfeatures "k8s.io/component-base/zpages/features" | ||||||
|  | 	"k8s.io/component-base/zpages/flagz" | ||||||
| 	"k8s.io/klog/v2" | 	"k8s.io/klog/v2" | ||||||
| 	schedulerserverconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" | 	schedulerserverconfig "k8s.io/kubernetes/cmd/kube-scheduler/app/config" | ||||||
| 	"k8s.io/kubernetes/cmd/kube-scheduler/app/options" | 	"k8s.io/kubernetes/cmd/kube-scheduler/app/options" | ||||||
| @@ -68,6 +70,10 @@ import ( | |||||||
| 	"k8s.io/kubernetes/pkg/scheduler/profile" | 	"k8s.io/kubernetes/pkg/scheduler/profile" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	kubeScheduler = "kube-scheduler" | ||||||
|  | ) | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) | 	utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) | ||||||
| 	utilruntime.Must(features.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) | 	utilruntime.Must(features.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) | ||||||
| @@ -224,7 +230,7 @@ func Run(ctx context.Context, cc *schedulerserverconfig.CompletedConfig, sched * | |||||||
| 			cc.Client, | 			cc.Client, | ||||||
| 			metav1.NamespaceSystem, | 			metav1.NamespaceSystem, | ||||||
| 			cc.LeaderElection.Lock.Identity(), | 			cc.LeaderElection.Lock.Identity(), | ||||||
| 			"kube-scheduler", | 			kubeScheduler, | ||||||
| 			binaryVersion.FinalizeVersion(), | 			binaryVersion.FinalizeVersion(), | ||||||
| 			emulationVersion.FinalizeVersion(), | 			emulationVersion.FinalizeVersion(), | ||||||
| 			coordinationv1.OldestEmulationVersion, | 			coordinationv1.OldestEmulationVersion, | ||||||
| @@ -236,9 +242,9 @@ func Run(ctx context.Context, cc *schedulerserverconfig.CompletedConfig, sched * | |||||||
| 		go leaseCandidate.Run(ctx) | 		go leaseCandidate.Run(ctx) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Start up the healthz server. | 	// Start up the server for endpoints. | ||||||
| 	if cc.SecureServing != nil { | 	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 | 		// TODO: handle stoppedCh and listenerStoppedCh returned by c.SecureServing.Serve | ||||||
| 		if _, _, err := cc.SecureServing.Serve(handler, 0, ctx.Done()); err != nil { | 		if _, _, err := cc.SecureServing.Serve(handler, 0, ctx.Done()); err != nil { | ||||||
| 			// fail early for secure handlers, removing the old error loop from above | 			// 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 | // newEndpointsHandler creates an API health server from the config, and will also | ||||||
| // embed the metrics handler. | // 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. | // 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 { | func newEndpointsHandler(config *kubeschedulerconfig.KubeSchedulerConfiguration, informers informers.SharedInformerFactory, isLeader func() bool, healthzChecks, readyzChecks []healthz.HealthChecker, flagReader flagz.Reader) http.Handler { | ||||||
| 	pathRecorderMux := mux.NewPathRecorderMux("kube-scheduler") | 	pathRecorderMux := mux.NewPathRecorderMux(kubeScheduler) | ||||||
| 	healthz.InstallHandler(pathRecorderMux, healthzChecks...) | 	healthz.InstallHandler(pathRecorderMux, healthzChecks...) | ||||||
| 	healthz.InstallLivezHandler(pathRecorderMux) | 	healthz.InstallLivezHandler(pathRecorderMux) | ||||||
| 	healthz.InstallReadyzHandler(pathRecorderMux, readyzChecks...) | 	healthz.InstallReadyzHandler(pathRecorderMux, readyzChecks...) | ||||||
| @@ -362,6 +368,12 @@ func newHealthEndpointsAndMetricsHandler(config *kubeschedulerconfig.KubeSchedul | |||||||
| 		} | 		} | ||||||
| 		routes.DebugFlags{}.Install(pathRecorderMux, "v", routes.StringFlagPutHandler(logs.GlogSetter)) | 		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 | 	return pathRecorderMux | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ type Reader interface { | |||||||
| 	GetFlagz() map[string]string | 	GetFlagz() map[string]string | ||||||
| } | } | ||||||
|  |  | ||||||
| // NamedFlagSetsGetter implements Reader for cliflag.NamedFlagSets | // NamedFlagSetsReader implements Reader for cliflag.NamedFlagSets | ||||||
| type NamedFlagSetsReader struct { | type NamedFlagSetsReader struct { | ||||||
| 	FlagSets cliflag.NamedFlagSets | 	FlagSets cliflag.NamedFlagSets | ||||||
| } | } | ||||||
|   | |||||||
| @@ -24,16 +24,21 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path" | 	"path" | ||||||
|  | 	"regexp" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"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" | 	"k8s.io/klog/v2/ktesting" | ||||||
| 	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" | 	kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" | ||||||
| 	kubeschedulertesting "k8s.io/kubernetes/cmd/kube-scheduler/app/testing" | 	kubeschedulertesting "k8s.io/kubernetes/cmd/kube-scheduler/app/testing" | ||||||
| 	"k8s.io/kubernetes/test/integration/framework" | 	"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) | 	server, configStr, _, err := startTestAPIServer(t) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("Failed to start kube-apiserver server: %v", err) | 		t.Fatalf("Failed to start kube-apiserver server: %v", err) | ||||||
| @@ -67,49 +72,61 @@ func TestHealthEndpoints(t *testing.T) { | |||||||
| 		name                 string | 		name                 string | ||||||
| 		path                 string | 		path                 string | ||||||
| 		useBrokenConfig      bool | 		useBrokenConfig      bool | ||||||
|  | 		requestHeader        map[string]string | ||||||
| 		wantResponseCode     int | 		wantResponseCode     int | ||||||
|  | 		wantResponseBodyRegx string | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			"/healthz", | 			name:             "/healthz", | ||||||
| 			"/healthz", | 			path:             "/healthz", | ||||||
| 			false, | 			useBrokenConfig:  false, | ||||||
| 			http.StatusOK, | 			wantResponseCode: http.StatusOK, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"/livez", | 			name:             "/livez", | ||||||
| 			"/livez", | 			path:             "/livez", | ||||||
| 			false, | 			useBrokenConfig:  false, | ||||||
| 			http.StatusOK, | 			wantResponseCode: http.StatusOK, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"/livez with ping check", | 			name:             "/livez with ping check", | ||||||
| 			"/livez/ping", | 			path:             "/livez/ping", | ||||||
| 			false, | 			useBrokenConfig:  false, | ||||||
| 			http.StatusOK, | 			wantResponseCode: http.StatusOK, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"/readyz", | 			name:             "/readyz", | ||||||
| 			"/readyz", | 			path:             "/readyz", | ||||||
| 			false, | 			useBrokenConfig:  false, | ||||||
| 			http.StatusOK, | 			wantResponseCode: http.StatusOK, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"/readyz with sched-handler-sync", | 			name:             "/readyz with sched-handler-sync", | ||||||
| 			"/readyz/sched-handler-sync", | 			path:             "/readyz/sched-handler-sync", | ||||||
| 			false, | 			useBrokenConfig:  false, | ||||||
| 			http.StatusOK, | 			wantResponseCode: http.StatusOK, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"/readyz with shutdown", | 			name:             "/readyz with shutdown", | ||||||
| 			"/readyz/shutdown", | 			path:             "/readyz/shutdown", | ||||||
| 			false, | 			useBrokenConfig:  false, | ||||||
| 			http.StatusOK, | 			wantResponseCode: http.StatusOK, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"/readyz with broken apiserver", | 			name:             "/readyz with broken apiserver", | ||||||
| 			"/readyz", | 			path:             "/readyz", | ||||||
| 			true, | 			useBrokenConfig:  true, | ||||||
| 			http.StatusInternalServerError, | 			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 { | 			if err != nil { | ||||||
| 				t.Fatalf("failed to request: %v", err) | 				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) | 			r, err := client.Do(req) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				t.Fatalf("failed to GET %s from component: %v", tt.path, err) | 				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 { | 			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)) | 				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
	 Kubernetes Prow Robot
					Kubernetes Prow Robot