Skip to content

Commit 953b40b

Browse files
authored
Merge pull request kubernetes#128857 from zhifei92/flagz-kubelet
feat: Add flagz endpoint for kubelet
2 parents be32ca6 + 2db729c commit 953b40b

File tree

8 files changed

+48
-8
lines changed

8 files changed

+48
-8
lines changed

cmd/kubelet/app/server.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ import (
8686
"k8s.io/component-base/tracing"
8787
"k8s.io/component-base/version"
8888
"k8s.io/component-base/version/verflag"
89+
zpagesfeatures "k8s.io/component-base/zpages/features"
90+
"k8s.io/component-base/zpages/flagz"
8991
nodeutil "k8s.io/component-helpers/node/util"
9092
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
9193
kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
@@ -267,6 +269,13 @@ is checked every 20 seconds (also configurable with a flag).`,
267269
return fmt.Errorf("failed to construct kubelet dependencies: %w", err)
268270
}
269271

272+
if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentFlagz) {
273+
if cleanFlagSet != nil {
274+
namedFlagSet := map[string]*pflag.FlagSet{server.ComponentKubelet: cleanFlagSet}
275+
kubeletDeps.Flagz = flagz.NamedFlagSetsReader{FlagSets: cliflag.NamedFlagSets{FlagSets: namedFlagSet}}
276+
}
277+
}
278+
270279
if err := checkPermissions(); err != nil {
271280
klog.ErrorS(err, "kubelet running with insufficient permissions")
272281
}

pkg/kubelet/kubelet.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ import (
6868
"k8s.io/client-go/util/certificate"
6969
"k8s.io/client-go/util/flowcontrol"
7070
cloudprovider "k8s.io/cloud-provider"
71+
"k8s.io/component-base/zpages/flagz"
7172
"k8s.io/component-helpers/apimachinery/lease"
7273
internalapi "k8s.io/cri-api/pkg/apis"
7374
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
@@ -292,6 +293,7 @@ type Dependencies struct {
292293
Options []Option
293294

294295
// Injected Dependencies
296+
Flagz flagz.Reader
295297
Auth server.AuthInterface
296298
CAdvisorInterface cadvisor.Interface
297299
Cloud cloudprovider.Interface
@@ -616,6 +618,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
616618
nodeStatusMaxImages: nodeStatusMaxImages,
617619
tracer: tracer,
618620
nodeStartupLatencyTracker: kubeDeps.NodeStartupLatencyTracker,
621+
flagz: kubeDeps.Flagz,
619622
}
620623

621624
if klet.cloud != nil {
@@ -1433,6 +1436,9 @@ type Kubelet struct {
14331436

14341437
// Health check kubelet
14351438
healthChecker watchdog.HealthChecker
1439+
1440+
// flagz is the Reader interface to get flags for flagz page.
1441+
flagz flagz.Reader
14361442
}
14371443

14381444
// ListPodStats is delegated to StatsProvider, which implements stats.Provider interface
@@ -3084,12 +3090,12 @@ func (kl *Kubelet) BirthCry() {
30843090
// ListenAndServe runs the kubelet HTTP server.
30853091
func (kl *Kubelet) ListenAndServe(kubeCfg *kubeletconfiginternal.KubeletConfiguration, tlsOptions *server.TLSOptions,
30863092
auth server.AuthInterface, tp trace.TracerProvider) {
3087-
server.ListenAndServeKubeletServer(kl, kl.resourceAnalyzer, kl.containerManager.GetHealthCheckers(), kubeCfg, tlsOptions, auth, tp)
3093+
server.ListenAndServeKubeletServer(kl, kl.resourceAnalyzer, kl.containerManager.GetHealthCheckers(), kl.flagz, kubeCfg, tlsOptions, auth, tp)
30883094
}
30893095

30903096
// ListenAndServeReadOnly runs the kubelet HTTP server in read-only mode.
30913097
func (kl *Kubelet) ListenAndServeReadOnly(address net.IP, port uint, tp trace.TracerProvider) {
3092-
server.ListenAndServeKubeletReadOnlyServer(kl, kl.resourceAnalyzer, kl.containerManager.GetHealthCheckers(), address, port, tp)
3098+
server.ListenAndServeKubeletReadOnlyServer(kl, kl.resourceAnalyzer, kl.containerManager.GetHealthCheckers(), kl.flagz, address, port, tp)
30933099
}
30943100

30953101
// ListenAndServePodResources runs the kubelet podresources grpc service

pkg/kubelet/server/auth.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"k8s.io/apiserver/pkg/server/healthz"
2828
utilfeature "k8s.io/apiserver/pkg/util/feature"
2929
"k8s.io/component-base/configz"
30+
"k8s.io/component-base/zpages/flagz"
3031
"k8s.io/component-base/zpages/statusz"
3132
"k8s.io/klog/v2"
3233
"k8s.io/kubernetes/pkg/features"
@@ -74,6 +75,7 @@ func isSubpath(subpath, path string) bool {
7475
// /runningPods/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=pods,proxy
7576
// /healthz/* => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=healthz,proxy
7677
// /configz => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=configz,proxy
78+
// /flagz => verb=<api verb from request>, resource=nodes, name=<node name>, subresource(s)=configz,proxy
7779
func (n nodeAuthorizerAttributesGetter) GetRequestAttributes(u user.Info, r *http.Request) []authorizer.Attributes {
7880

7981
apiVerb := ""
@@ -120,6 +122,8 @@ func (n nodeAuthorizerAttributesGetter) GetRequestAttributes(u user.Info, r *htt
120122
subresources = append(subresources, "checkpoint")
121123
case isSubpath(requestPath, statusz.DefaultStatuszPath):
122124
subresources = append(subresources, "statusz")
125+
case isSubpath(requestPath, flagz.DefaultFlagzPath):
126+
subresources = append(subresources, "configz")
123127
default:
124128
subresources = append(subresources, "proxy")
125129
}

pkg/kubelet/server/auth_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ func AuthzTestCases(fineGrained bool) []AuthzTestCase {
125125
"/attach/{podNamespace}/{podID}/{uid}/{containerName}": {"proxy"},
126126
"/checkpoint/{podNamespace}/{podID}/{containerName}": {"checkpoint"},
127127
"/configz": {"proxy"},
128+
"/flagz": {"configz"},
128129
"/statusz": {"statusz"},
129130
"/containerLogs/{podNamespace}/{podID}/{containerName}": {"proxy"},
130131
"/debug/flags/v": {"proxy"},

pkg/kubelet/server/server.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import (
6969
"k8s.io/component-base/metrics/legacyregistry"
7070
"k8s.io/component-base/metrics/prometheus/slis"
7171
zpagesfeatures "k8s.io/component-base/zpages/features"
72+
"k8s.io/component-base/zpages/flagz"
7273
"k8s.io/component-base/zpages/statusz"
7374
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
7475
"k8s.io/cri-client/pkg/util"
@@ -117,6 +118,7 @@ const (
117118

118119
// Server is a http.Handler which exposes kubelet functionality over HTTP.
119120
type Server struct {
121+
flagz flagz.Reader
120122
auth AuthInterface
121123
host HostInterface
122124
restfulCont containerInterface
@@ -167,6 +169,7 @@ func ListenAndServeKubeletServer(
167169
host HostInterface,
168170
resourceAnalyzer stats.ResourceAnalyzer,
169171
checkers []healthz.HealthChecker,
172+
flagz flagz.Reader,
170173
kubeCfg *kubeletconfiginternal.KubeletConfiguration,
171174
tlsOptions *TLSOptions,
172175
auth AuthInterface,
@@ -175,7 +178,7 @@ func ListenAndServeKubeletServer(
175178
address := netutils.ParseIPSloppy(kubeCfg.Address)
176179
port := uint(kubeCfg.Port)
177180
klog.InfoS("Starting to listen", "address", address, "port", port)
178-
handler := NewServer(host, resourceAnalyzer, checkers, auth, kubeCfg)
181+
handler := NewServer(host, resourceAnalyzer, checkers, flagz, auth, kubeCfg)
179182

180183
if utilfeature.DefaultFeatureGate.Enabled(features.KubeletTracing) {
181184
handler.InstallTracingFilter(tp)
@@ -210,11 +213,12 @@ func ListenAndServeKubeletReadOnlyServer(
210213
host HostInterface,
211214
resourceAnalyzer stats.ResourceAnalyzer,
212215
checkers []healthz.HealthChecker,
216+
flagz flagz.Reader,
213217
address net.IP,
214218
port uint,
215219
tp oteltrace.TracerProvider) {
216220
klog.InfoS("Starting to listen read-only", "address", address, "port", port)
217-
s := NewServer(host, resourceAnalyzer, checkers, nil, nil)
221+
s := NewServer(host, resourceAnalyzer, checkers, nil, nil, nil)
218222

219223
if utilfeature.DefaultFeatureGate.Enabled(features.KubeletTracing) {
220224
s.InstallTracingFilter(tp, otelrestful.WithPublicEndpoint())
@@ -291,10 +295,12 @@ func NewServer(
291295
host HostInterface,
292296
resourceAnalyzer stats.ResourceAnalyzer,
293297
checkers []healthz.HealthChecker,
298+
flagz flagz.Reader,
294299
auth AuthInterface,
295300
kubeCfg *kubeletconfiginternal.KubeletConfiguration) Server {
296301

297302
server := Server{
303+
flagz: flagz,
298304
host: host,
299305
resourceAnalyzer: resourceAnalyzer,
300306
auth: auth,
@@ -575,6 +581,13 @@ func (s *Server) InstallAuthRequiredHandlers() {
575581
statusz.Install(s.restfulCont, ComponentKubelet, statusz.NewRegistry(compatibility.DefaultBuildEffectiveVersion()))
576582
}
577583

584+
if utilfeature.DefaultFeatureGate.Enabled(zpagesfeatures.ComponentFlagz) {
585+
if s.flagz != nil {
586+
s.addMetricsBucketMatcher("flagz")
587+
flagz.Install(s.restfulCont, ComponentKubelet, s.flagz)
588+
}
589+
}
590+
578591
// The /runningpods endpoint is used for testing only.
579592
s.addMetricsBucketMatcher("runningpods")
580593
ws = new(restful.WebService)

pkg/kubelet/server/server_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import (
5959
utilfeature "k8s.io/apiserver/pkg/util/feature"
6060
featuregatetesting "k8s.io/component-base/featuregate/testing"
6161
zpagesfeatures "k8s.io/component-base/zpages/features"
62+
"k8s.io/component-base/zpages/flagz"
6263
"k8s.io/kubelet/pkg/cri/streaming"
6364
"k8s.io/kubelet/pkg/cri/streaming/portforward"
6465
remotecommandserver "k8s.io/kubelet/pkg/cri/streaming/remotecommand"
@@ -373,6 +374,7 @@ func newServerTestWithDebuggingHandlers(kubeCfg *kubeletconfiginternal.KubeletCo
373374
fw.fakeKubelet,
374375
stats.NewResourceAnalyzer(fw.fakeKubelet, time.Minute, &record.FakeRecorder{}),
375376
[]healthz.HealthChecker{},
377+
flagz.NamedFlagSetsReader{},
376378
fw.fakeAuth,
377379
kubeCfg,
378380
)
@@ -646,6 +648,7 @@ func TestAuthFilters(t *testing.T) {
646648
// Enable features.ContainerCheckpoint during test
647649
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ContainerCheckpoint, true)
648650
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, zpagesfeatures.ComponentStatusz, true)
651+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, zpagesfeatures.ComponentFlagz, true)
649652

650653
fw := newServerTest()
651654
defer fw.testHTTPServer.Close()
@@ -1729,9 +1732,11 @@ func TestMetricBuckets(t *testing.T) {
17291732
"stats": {url: "/stats/", bucket: "stats"},
17301733
"stats summary sub": {url: "/stats/summary", bucket: "stats"},
17311734
"statusz": {url: "/statusz", bucket: "statusz"},
1735+
"/flagz": {url: "/flagz", bucket: "flagz"},
17321736
"invalid path": {url: "/junk", bucket: "other"},
17331737
"invalid path starting with good": {url: "/healthzjunk", bucket: "other"},
17341738
}
1739+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, zpagesfeatures.ComponentFlagz, true)
17351740
fw := newServerTest()
17361741
defer fw.testHTTPServer.Close()
17371742

@@ -1960,8 +1965,8 @@ func TestNewServerRegistersMetricsSLIsEndpointTwice(t *testing.T) {
19601965
}
19611966
resourceAnalyzer := stats.NewResourceAnalyzer(nil, time.Minute, &record.FakeRecorder{})
19621967

1963-
server1 := NewServer(host, resourceAnalyzer, []healthz.HealthChecker{}, nil, nil)
1964-
server2 := NewServer(host, resourceAnalyzer, []healthz.HealthChecker{}, nil, nil)
1968+
server1 := NewServer(host, resourceAnalyzer, []healthz.HealthChecker{}, flagz.NamedFlagSetsReader{}, nil, nil)
1969+
server2 := NewServer(host, resourceAnalyzer, []healthz.HealthChecker{}, flagz.NamedFlagSetsReader{}, nil, nil)
19651970

19661971
// Check if both servers registered the /metrics/slis endpoint
19671972
assert.Contains(t, server1.restfulCont.RegisteredHandlePaths(), "/metrics/slis", "First server should register /metrics/slis")

staging/src/k8s.io/component-base/zpages/flagz/flagz.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030
)
3131

3232
const (
33+
DefaultFlagzPath = "/flagz"
34+
3335
flagzHeaderFmt = `
3436
%s flags
3537
Warning: This endpoint is not meant to be machine parseable, has no formatting compatibility guarantees and is for debugging purposes only.
@@ -56,7 +58,7 @@ func Install(m mux, componentName string, flagReader Reader) {
5658
}
5759

5860
func (reg *registry) installHandler(m mux, componentName string, flagReader Reader) {
59-
m.Handle("/flagz", reg.handleFlags(componentName, flagReader))
61+
m.Handle(DefaultFlagzPath, reg.handleFlags(componentName, flagReader))
6062
}
6163

6264
func (reg *registry) handleFlags(componentName string, flagReader Reader) http.HandlerFunc {

staging/src/k8s.io/component-base/zpages/flagz/flagz_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func TestFlagz(t *testing.T) {
8585
mux := http.NewServeMux()
8686
Install(mux, componentName, test.flagzReader)
8787

88-
req, err := http.NewRequest(http.MethodGet, "http://example.com/flagz", nil)
88+
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://example.com%s", DefaultFlagzPath), nil)
8989
if err != nil {
9090
t.Fatalf("case[%d] Unexpected error: %v", i, err)
9191
}

0 commit comments

Comments
 (0)