k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubeadm/app/componentconfigs/kubelet_test.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package componentconfigs
    18  
    19  import (
    20  	"fmt"
    21  	"path/filepath"
    22  	"reflect"
    23  	"testing"
    24  
    25  	"github.com/lithammer/dedent"
    26  
    27  	v1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/runtime/schema"
    30  	clientsetfake "k8s.io/client-go/kubernetes/fake"
    31  	kubeletconfig "k8s.io/kubelet/config/v1beta1"
    32  	"k8s.io/utils/ptr"
    33  
    34  	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
    35  	kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
    36  	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
    37  )
    38  
    39  func testKubeletConfigMap(contents string) *v1.ConfigMap {
    40  	return &v1.ConfigMap{
    41  		ObjectMeta: metav1.ObjectMeta{
    42  			Name:      constants.KubeletBaseConfigurationConfigMap,
    43  			Namespace: metav1.NamespaceSystem,
    44  		},
    45  		Data: map[string]string{
    46  			constants.KubeletBaseConfigurationConfigMapKey: dedent.Dedent(contents),
    47  		},
    48  	}
    49  }
    50  
    51  func TestKubeletDefault(t *testing.T) {
    52  	tests := []struct {
    53  		name       string
    54  		clusterCfg kubeadmapi.ClusterConfiguration
    55  		expected   kubeletConfig
    56  	}{
    57  		{
    58  			name:       "No specific defaulting works",
    59  			clusterCfg: kubeadmapi.ClusterConfiguration{},
    60  			expected: kubeletConfig{
    61  				config: kubeletconfig.KubeletConfiguration{
    62  					FeatureGates:  map[string]bool{},
    63  					StaticPodPath: kubeadmapiv1.DefaultManifestsDir,
    64  					ClusterDNS:    []string{kubeadmapiv1.DefaultClusterDNSIP},
    65  					Authentication: kubeletconfig.KubeletAuthentication{
    66  						X509: kubeletconfig.KubeletX509Authentication{
    67  							ClientCAFile: constants.CACertName,
    68  						},
    69  						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
    70  							Enabled: ptr.To(kubeletAuthenticationAnonymousEnabled),
    71  						},
    72  						Webhook: kubeletconfig.KubeletWebhookAuthentication{
    73  							Enabled: ptr.To(kubeletAuthenticationWebhookEnabled),
    74  						},
    75  					},
    76  					Authorization: kubeletconfig.KubeletAuthorization{
    77  						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
    78  					},
    79  					HealthzBindAddress: kubeletHealthzBindAddress,
    80  					HealthzPort:        ptr.To[int32](constants.KubeletHealthzPort),
    81  					RotateCertificates: kubeletRotateCertificates,
    82  					CgroupDriver:       constants.CgroupDriverSystemd,
    83  				},
    84  			},
    85  		},
    86  		{
    87  			name: "Service subnet, no dual stack defaulting works",
    88  			clusterCfg: kubeadmapi.ClusterConfiguration{
    89  				Networking: kubeadmapi.Networking{
    90  					ServiceSubnet: "192.168.0.0/16",
    91  				},
    92  			},
    93  			expected: kubeletConfig{
    94  				config: kubeletconfig.KubeletConfiguration{
    95  					FeatureGates:  map[string]bool{},
    96  					StaticPodPath: kubeadmapiv1.DefaultManifestsDir,
    97  					ClusterDNS:    []string{"192.168.0.10"},
    98  					Authentication: kubeletconfig.KubeletAuthentication{
    99  						X509: kubeletconfig.KubeletX509Authentication{
   100  							ClientCAFile: constants.CACertName,
   101  						},
   102  						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
   103  							Enabled: ptr.To(kubeletAuthenticationAnonymousEnabled),
   104  						},
   105  						Webhook: kubeletconfig.KubeletWebhookAuthentication{
   106  							Enabled: ptr.To(kubeletAuthenticationWebhookEnabled),
   107  						},
   108  					},
   109  					Authorization: kubeletconfig.KubeletAuthorization{
   110  						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
   111  					},
   112  					HealthzBindAddress: kubeletHealthzBindAddress,
   113  					HealthzPort:        ptr.To[int32](constants.KubeletHealthzPort),
   114  					RotateCertificates: kubeletRotateCertificates,
   115  					CgroupDriver:       constants.CgroupDriverSystemd,
   116  				},
   117  			},
   118  		},
   119  		{
   120  			name: "Service subnet, enabled dual stack defaulting works",
   121  			clusterCfg: kubeadmapi.ClusterConfiguration{
   122  				Networking: kubeadmapi.Networking{
   123  					ServiceSubnet: "192.168.0.0/16",
   124  				},
   125  			},
   126  			expected: kubeletConfig{
   127  				config: kubeletconfig.KubeletConfiguration{
   128  					FeatureGates:  map[string]bool{},
   129  					StaticPodPath: kubeadmapiv1.DefaultManifestsDir,
   130  					ClusterDNS:    []string{"192.168.0.10"},
   131  					Authentication: kubeletconfig.KubeletAuthentication{
   132  						X509: kubeletconfig.KubeletX509Authentication{
   133  							ClientCAFile: constants.CACertName,
   134  						},
   135  						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
   136  							Enabled: ptr.To(kubeletAuthenticationAnonymousEnabled),
   137  						},
   138  						Webhook: kubeletconfig.KubeletWebhookAuthentication{
   139  							Enabled: ptr.To(kubeletAuthenticationWebhookEnabled),
   140  						},
   141  					},
   142  					Authorization: kubeletconfig.KubeletAuthorization{
   143  						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
   144  					},
   145  					HealthzBindAddress: kubeletHealthzBindAddress,
   146  					HealthzPort:        ptr.To[int32](constants.KubeletHealthzPort),
   147  					RotateCertificates: kubeletRotateCertificates,
   148  					CgroupDriver:       constants.CgroupDriverSystemd,
   149  				},
   150  			},
   151  		},
   152  		{
   153  			name: "DNS domain defaulting works",
   154  			clusterCfg: kubeadmapi.ClusterConfiguration{
   155  				Networking: kubeadmapi.Networking{
   156  					DNSDomain: "example.com",
   157  				},
   158  			},
   159  			expected: kubeletConfig{
   160  				config: kubeletconfig.KubeletConfiguration{
   161  					FeatureGates:  map[string]bool{},
   162  					StaticPodPath: kubeadmapiv1.DefaultManifestsDir,
   163  					ClusterDNS:    []string{kubeadmapiv1.DefaultClusterDNSIP},
   164  					ClusterDomain: "example.com",
   165  					Authentication: kubeletconfig.KubeletAuthentication{
   166  						X509: kubeletconfig.KubeletX509Authentication{
   167  							ClientCAFile: constants.CACertName,
   168  						},
   169  						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
   170  							Enabled: ptr.To(kubeletAuthenticationAnonymousEnabled),
   171  						},
   172  						Webhook: kubeletconfig.KubeletWebhookAuthentication{
   173  							Enabled: ptr.To(kubeletAuthenticationWebhookEnabled),
   174  						},
   175  					},
   176  					Authorization: kubeletconfig.KubeletAuthorization{
   177  						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
   178  					},
   179  					HealthzBindAddress: kubeletHealthzBindAddress,
   180  					HealthzPort:        ptr.To[int32](constants.KubeletHealthzPort),
   181  					RotateCertificates: kubeletRotateCertificates,
   182  					CgroupDriver:       constants.CgroupDriverSystemd,
   183  				},
   184  			},
   185  		},
   186  		{
   187  			name: "CertificatesDir defaulting works",
   188  			clusterCfg: kubeadmapi.ClusterConfiguration{
   189  				CertificatesDir: "/path/to/certs",
   190  			},
   191  			expected: kubeletConfig{
   192  				config: kubeletconfig.KubeletConfiguration{
   193  					FeatureGates:  map[string]bool{},
   194  					StaticPodPath: kubeadmapiv1.DefaultManifestsDir,
   195  					ClusterDNS:    []string{kubeadmapiv1.DefaultClusterDNSIP},
   196  					Authentication: kubeletconfig.KubeletAuthentication{
   197  						X509: kubeletconfig.KubeletX509Authentication{
   198  							ClientCAFile: filepath.Join("/path/to/certs", constants.CACertName),
   199  						},
   200  						Anonymous: kubeletconfig.KubeletAnonymousAuthentication{
   201  							Enabled: ptr.To(kubeletAuthenticationAnonymousEnabled),
   202  						},
   203  						Webhook: kubeletconfig.KubeletWebhookAuthentication{
   204  							Enabled: ptr.To(kubeletAuthenticationWebhookEnabled),
   205  						},
   206  					},
   207  					Authorization: kubeletconfig.KubeletAuthorization{
   208  						Mode: kubeletconfig.KubeletAuthorizationModeWebhook,
   209  					},
   210  					HealthzBindAddress: kubeletHealthzBindAddress,
   211  					HealthzPort:        ptr.To[int32](constants.KubeletHealthzPort),
   212  					RotateCertificates: kubeletRotateCertificates,
   213  					CgroupDriver:       constants.CgroupDriverSystemd,
   214  				},
   215  			},
   216  		},
   217  	}
   218  
   219  	for _, test := range tests {
   220  		t.Run(test.name, func(t *testing.T) {
   221  			// This is the same for all test cases so we set it here
   222  			expected := test.expected
   223  			expected.configBase.GroupVersion = kubeletconfig.SchemeGroupVersion
   224  
   225  			got := &kubeletConfig{
   226  				configBase: configBase{
   227  					GroupVersion: kubeletconfig.SchemeGroupVersion,
   228  				},
   229  			}
   230  			got.Default(&test.clusterCfg, &kubeadmapi.APIEndpoint{}, &kubeadmapi.NodeRegistrationOptions{})
   231  
   232  			if !reflect.DeepEqual(got, &expected) {
   233  				t.Fatalf("Missmatch between expected and got:\nExpected:\n%v\n---\nGot:\n%v", expected, *got)
   234  			}
   235  		})
   236  	}
   237  }
   238  
   239  // runKubeletFromTest holds common test case data and evaluation code for kubeletHandler.From* functions
   240  func runKubeletFromTest(t *testing.T, perform func(gvk schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error)) {
   241  	const (
   242  		kind          = "KubeletConfiguration"
   243  		clusterDomain = "foo.bar"
   244  	)
   245  
   246  	gvk := kubeletHandler.GroupVersion.WithKind(kind)
   247  	yaml := fmt.Sprintf("apiVersion: %s\nkind: %s\nclusterDomain: %s", kubeletHandler.GroupVersion, kind, clusterDomain)
   248  
   249  	cfg, err := perform(gvk, yaml)
   250  
   251  	if err != nil {
   252  		t.Fatalf("unexpected failure: %v", err)
   253  	}
   254  	if cfg == nil {
   255  		t.Fatal("no config loaded where it should have been")
   256  	}
   257  	if kubeletCfg, ok := cfg.(*kubeletConfig); !ok {
   258  		t.Fatalf("found different object type than expected: %s", reflect.TypeOf(cfg))
   259  	} else if kubeletCfg.config.ClusterDomain != clusterDomain {
   260  		t.Fatalf("unexpected control value (clusterDomain):\n\tgot: %q\n\texpected: %q", kubeletCfg.config.ClusterDomain, clusterDomain)
   261  	}
   262  }
   263  
   264  func TestKubeletFromDocumentMap(t *testing.T) {
   265  	runKubeletFromTest(t, func(gvk schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error) {
   266  		return kubeletHandler.FromDocumentMap(kubeadmapi.DocumentMap{
   267  			gvk: []byte(yaml),
   268  		})
   269  	})
   270  }
   271  
   272  func TestKubeletFromCluster(t *testing.T) {
   273  	runKubeletFromTest(t, func(_ schema.GroupVersionKind, yaml string) (kubeadmapi.ComponentConfig, error) {
   274  		client := clientsetfake.NewSimpleClientset(
   275  			testKubeletConfigMap(yaml),
   276  		)
   277  		return kubeletHandler.FromCluster(client, testClusterCfg())
   278  	})
   279  }