@@ -22,6 +22,7 @@ import (
22
22
"crypto/x509"
23
23
"fmt"
24
24
"io"
25
+ "net"
25
26
"net/http"
26
27
"os"
27
28
"path"
@@ -30,9 +31,12 @@ import (
30
31
31
32
"k8s.io/apiserver/pkg/server"
32
33
"k8s.io/apiserver/pkg/server/options"
34
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
33
35
cloudprovider "k8s.io/cloud-provider"
34
36
cloudctrlmgrtesting "k8s.io/cloud-provider/app/testing"
35
37
"k8s.io/cloud-provider/fake"
38
+ featuregatetesting "k8s.io/component-base/featuregate/testing"
39
+ zpagesfeatures "k8s.io/component-base/zpages/features"
36
40
"k8s.io/klog/v2/ktesting"
37
41
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
38
42
kubectrlmgrtesting "k8s.io/kubernetes/cmd/kube-controller-manager/app/testing"
@@ -289,3 +293,145 @@ func fakeCloudProviderFactory(io.Reader) (cloudprovider.Interface, error) {
289
293
DisableRoutes : true , // disable routes for server tests, otherwise --cluster-cidr is required
290
294
}, nil
291
295
}
296
+
297
+ func TestKubeControllerManagerServingStatusz (t * testing.T ) {
298
+
299
+ // authenticate to apiserver via bearer token
300
+ token := "flwqkenfjasasdfmwerasd" // Fake token for testing.
301
+ tokenFile , err := os .CreateTemp ("" , "kubeconfig" )
302
+ if err != nil {
303
+ t .Fatal (err )
304
+ }
305
+ if _ , err = tokenFile .WriteString (fmt .Sprintf (`
306
+ %s,system:kube-controller-manager,system:kube-controller-manager,""
307
+ ` , token )); err != nil {
308
+ t .Fatal (err )
309
+ }
310
+ if err = tokenFile .Close (); err != nil {
311
+ t .Fatal (err )
312
+ }
313
+
314
+ // start apiserver
315
+ server := kubeapiservertesting .StartTestServerOrDie (t , nil , []string {
316
+ "--token-auth-file" , tokenFile .Name (),
317
+ "--authorization-mode" , "RBAC" ,
318
+ }, framework .SharedEtcd ())
319
+ defer server .TearDownFn ()
320
+
321
+ // create kubeconfig for the apiserver
322
+ apiserverConfig , err := os .CreateTemp ("" , "kubeconfig" )
323
+ if err != nil {
324
+ t .Fatal (err )
325
+ }
326
+ if _ , err = apiserverConfig .WriteString (fmt .Sprintf (`
327
+ apiVersion: v1
328
+ kind: Config
329
+ clusters:
330
+ - cluster:
331
+ server: %s
332
+ certificate-authority: %s
333
+ name: integration
334
+ contexts:
335
+ - context:
336
+ cluster: integration
337
+ user: controller-manager
338
+ name: default-context
339
+ current-context: default-context
340
+ users:
341
+ - name: controller-manager
342
+ user:
343
+ token: %s
344
+ ` , server .ClientConfig .Host , server .ServerOpts .SecureServing .ServerCert .CertKey .CertFile , token )); err != nil {
345
+ t .Fatal (err )
346
+ }
347
+ if err = apiserverConfig .Close (); err != nil {
348
+ t .Fatal (err )
349
+ }
350
+
351
+ tests := []struct {
352
+ name string
353
+ flags []string
354
+ path string
355
+ anonymous bool // to use the token or not
356
+ wantErr bool
357
+ wantSecureCode * int
358
+ }{
359
+ {"serving /statusz" , []string {
360
+ "--authentication-skip-lookup" , // to survive inaccessible extensions-apiserver-authentication configmap
361
+ "--authentication-kubeconfig" , apiserverConfig .Name (),
362
+ "--authorization-kubeconfig" , apiserverConfig .Name (),
363
+ "--authorization-always-allow-paths" , "/statusz" ,
364
+ "--kubeconfig" , apiserverConfig .Name (),
365
+ "--leader-elect=false" ,
366
+ }, "/statusz" , false , false , intPtr (http .StatusOK )},
367
+ }
368
+ for _ , tt := range tests {
369
+ t .Run (tt .name , func (t * testing.T ) {
370
+ featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , zpagesfeatures .ComponentStatusz , true )
371
+ _ , ctx := ktesting .NewTestContext (t )
372
+ secureOptions , secureInfo , tearDownFn , err := kubeControllerManagerTester {}.StartTestServer (ctx , append (append ([]string {}, tt .flags ... ), []string {}... ))
373
+ if tearDownFn != nil {
374
+ defer tearDownFn ()
375
+ }
376
+ if (err != nil ) != tt .wantErr {
377
+ t .Fatalf ("StartTestServer() error = %v, wantErr %v" , err , tt .wantErr )
378
+ }
379
+ if err != nil {
380
+ return
381
+ }
382
+
383
+ if want , got := tt .wantSecureCode != nil , secureInfo != nil ; want != got {
384
+ t .Errorf ("SecureServing enabled: expected=%v got=%v" , want , got )
385
+ } else if want {
386
+ // only interested on the port, because we are using always localhost
387
+ _ , port , err := net .SplitHostPort (secureInfo .Listener .Addr ().String ())
388
+ if err != nil {
389
+ t .Fatalf ("could not get host and port from %s : %v" , secureInfo .Listener .Addr ().String (), err )
390
+ }
391
+ // use IPv4 because the self-signed cert does not support [::]
392
+ url := fmt .Sprintf ("https://127.0.0.1:%s%s" , port , tt .path )
393
+
394
+ // read self-signed server cert disk
395
+ pool := x509 .NewCertPool ()
396
+ serverCertPath := path .Join (secureOptions .ServerCert .CertDirectory , secureOptions .ServerCert .PairName + ".crt" )
397
+ serverCert , err := os .ReadFile (serverCertPath )
398
+ if err != nil {
399
+ t .Fatalf ("Failed to read component server cert %q: %v" , serverCertPath , err )
400
+ }
401
+ pool .AppendCertsFromPEM (serverCert )
402
+ tr := & http.Transport {
403
+ TLSClientConfig : & tls.Config {
404
+ RootCAs : pool ,
405
+ },
406
+ }
407
+
408
+ client := & http.Client {Transport : tr }
409
+ req , err := http .NewRequest (http .MethodGet , url , nil )
410
+ req .Header .Set ("Accept" , "text/plain" )
411
+ if err != nil {
412
+ t .Fatal (err )
413
+ }
414
+ if ! tt .anonymous {
415
+ req .Header .Add ("Authorization" , fmt .Sprintf ("Token %s" , token ))
416
+ }
417
+ r , err := client .Do (req )
418
+ if err != nil {
419
+ t .Fatalf ("failed to GET %s from component: %v" , tt .path , err )
420
+ }
421
+
422
+ if _ , err = io .ReadAll (r .Body ); err != nil {
423
+ t .Fatalf ("failed to read response body: %v" , err )
424
+ }
425
+ defer func () {
426
+ if err := r .Body .Close (); err != nil {
427
+ t .Fatalf ("Error closing response body: %v" , err )
428
+ }
429
+ }()
430
+
431
+ if got , expected := r .StatusCode , * tt .wantSecureCode ; got != expected {
432
+ t .Fatalf ("expected http %d at %s of component, got: %d" , expected , tt .path , got )
433
+ }
434
+ }
435
+ })
436
+ }
437
+ }
0 commit comments