...

Source file src/k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles/configfiles_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles

     1  /*
     2  Copyright 2017 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 configfiles
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"path/filepath"
    23  	goruntime "runtime"
    24  	"testing"
    25  
    26  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
    29  	kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
    30  	kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/config/scheme"
    31  	utilfiles "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/files"
    32  	utiltest "k8s.io/kubernetes/pkg/kubelet/kubeletconfig/util/test"
    33  	utilfs "k8s.io/kubernetes/pkg/util/filesystem"
    34  )
    35  
    36  const configDir = "/test-config-dir"
    37  const relativePath = "relative/path/test"
    38  const kubeletFile = "kubelet"
    39  
    40  func TestLoad(t *testing.T) {
    41  	cases := []struct {
    42  		desc          string
    43  		file          *string
    44  		expect        *kubeletconfig.KubeletConfiguration
    45  		err           string
    46  		strictErr     bool
    47  		skipOnWindows bool
    48  	}{
    49  		// missing file
    50  		{
    51  			desc: "missing file",
    52  			err:  "failed to read",
    53  		},
    54  		// empty file
    55  		{
    56  			desc: "empty file",
    57  			file: newString(``),
    58  			err:  "was empty",
    59  		},
    60  		// invalid format
    61  		{
    62  			desc: "invalid yaml",
    63  			file: newString(`*`),
    64  			err:  "failed to decode",
    65  		},
    66  		{
    67  			desc: "invalid json",
    68  			file: newString(`{*`),
    69  			err:  "failed to decode",
    70  		},
    71  		// invalid object
    72  		{
    73  			desc: "missing kind",
    74  			file: newString(`{"apiVersion":"kubelet.config.k8s.io/v1beta1"}`),
    75  			err:  "failed to decode",
    76  		},
    77  		{
    78  			desc: "missing version",
    79  			file: newString(`{"kind":"KubeletConfiguration"}`),
    80  			err:  "failed to decode",
    81  		},
    82  		{
    83  			desc: "unregistered kind",
    84  			file: newString(`{"kind":"BogusKind","apiVersion":"kubelet.config.k8s.io/v1beta1"}`),
    85  			err:  "failed to decode",
    86  		},
    87  		{
    88  			desc: "unregistered version",
    89  			file: newString(`{"kind":"KubeletConfiguration","apiVersion":"bogusversion"}`),
    90  			err:  "failed to decode",
    91  		},
    92  
    93  		// empty object with correct kind and version should result in the defaults for that kind and version
    94  		{
    95  			desc: "default from yaml",
    96  			file: newString(`kind: KubeletConfiguration
    97  apiVersion: kubelet.config.k8s.io/v1beta1`),
    98  			expect:        newConfig(t),
    99  			skipOnWindows: true,
   100  		},
   101  		{
   102  			desc:          "default from json",
   103  			file:          newString(`{"kind":"KubeletConfiguration","apiVersion":"kubelet.config.k8s.io/v1beta1"}`),
   104  			expect:        newConfig(t),
   105  			skipOnWindows: true,
   106  		},
   107  
   108  		// relative path
   109  		{
   110  			desc: "yaml, relative path is resolved",
   111  			file: newString(fmt.Sprintf(`kind: KubeletConfiguration
   112  apiVersion: kubelet.config.k8s.io/v1beta1
   113  staticPodPath: %s`, relativePath)),
   114  			expect: func() *kubeletconfig.KubeletConfiguration {
   115  				kc := newConfig(t)
   116  				kc.StaticPodPath = filepath.Join(configDir, relativePath)
   117  				return kc
   118  			}(),
   119  			skipOnWindows: true,
   120  		},
   121  		{
   122  			desc: "json, relative path is resolved",
   123  			file: newString(fmt.Sprintf(`{"kind":"KubeletConfiguration","apiVersion":"kubelet.config.k8s.io/v1beta1","staticPodPath":"%s"}`, relativePath)),
   124  			expect: func() *kubeletconfig.KubeletConfiguration {
   125  				kc := newConfig(t)
   126  				kc.StaticPodPath = filepath.Join(configDir, relativePath)
   127  				return kc
   128  			}(),
   129  			skipOnWindows: true,
   130  		},
   131  		{
   132  			// This should fail from v1beta2+
   133  			desc: "duplicate field",
   134  			file: newString(fmt.Sprintf(`kind: KubeletConfiguration
   135  apiVersion: kubelet.config.k8s.io/v1beta1
   136  staticPodPath: %s
   137  staticPodPath: %s/foo`, relativePath, relativePath)),
   138  			// err:       `key "staticPodPath" already set`,
   139  			// strictErr: true,
   140  			expect: func() *kubeletconfig.KubeletConfiguration {
   141  				kc := newConfig(t)
   142  				kc.StaticPodPath = filepath.Join(configDir, relativePath, "foo")
   143  				return kc
   144  			}(),
   145  			skipOnWindows: true,
   146  		},
   147  		{
   148  			// This should fail from v1beta2+
   149  			desc: "unknown field",
   150  			file: newString(`kind: KubeletConfiguration
   151  apiVersion: kubelet.config.k8s.io/v1beta1
   152  foo: bar`),
   153  			// err:       "found unknown field: foo",
   154  			// strictErr: true,
   155  			expect:        newConfig(t),
   156  			skipOnWindows: true,
   157  		},
   158  	}
   159  
   160  	for _, c := range cases {
   161  		t.Run(c.desc, func(t *testing.T) {
   162  			// Skip tests that fail on Windows, as discussed during the SIG Testing meeting from January 10, 2023
   163  			if c.skipOnWindows && goruntime.GOOS == "windows" {
   164  				t.Skip("Skipping test that fails on Windows")
   165  			}
   166  
   167  			fs := utilfs.NewTempFs()
   168  			fs.MkdirAll(configDir, 0777)
   169  			path := filepath.Join(configDir, kubeletFile)
   170  			if c.file != nil {
   171  				if err := addFile(fs, path, *c.file); err != nil {
   172  					t.Fatalf("unexpected error: %v", err)
   173  				}
   174  			}
   175  			loader, err := NewFsLoader(fs, path)
   176  			if err != nil {
   177  				t.Fatalf("unexpected error: %v", err)
   178  			}
   179  			kc, err := loader.Load()
   180  
   181  			if c.strictErr && !runtime.IsStrictDecodingError(errors.Unwrap(err)) {
   182  				t.Fatalf("got error: %v, want strict decoding error", err)
   183  			}
   184  			if utiltest.SkipRest(t, c.desc, err, c.err) {
   185  				return
   186  			}
   187  			if !apiequality.Semantic.DeepEqual(c.expect, kc) {
   188  				t.Fatalf("expect %#v but got %#v", *c.expect, *kc)
   189  			}
   190  		})
   191  	}
   192  }
   193  
   194  func TestResolveRelativePaths(t *testing.T) {
   195  	absolutePath := filepath.Join(configDir, "absolute")
   196  	cases := []struct {
   197  		desc          string
   198  		path          string
   199  		expect        string
   200  		skipOnWindows bool
   201  	}{
   202  		{"empty path", "", "", false},
   203  		{"absolute path", absolutePath, absolutePath, true},
   204  		{"relative path", relativePath, filepath.Join(configDir, relativePath), false},
   205  	}
   206  
   207  	paths := kubeletconfig.KubeletConfigurationPathRefs(newConfig(t))
   208  	if len(paths) == 0 {
   209  		t.Fatalf("requires at least one path field to exist in the KubeletConfiguration type")
   210  	}
   211  	for _, c := range cases {
   212  		t.Run(c.desc, func(t *testing.T) {
   213  			// Skip tests that fail on Windows, as discussed during the SIG Testing meeting from January 10, 2023
   214  			if c.skipOnWindows && goruntime.GOOS == "windows" {
   215  				t.Skip("Skipping test that fails on Windows")
   216  			}
   217  
   218  			// set the path, resolve it, and check if it resolved as we would expect
   219  			*(paths[0]) = c.path
   220  			resolveRelativePaths(paths, configDir)
   221  			if *(paths[0]) != c.expect {
   222  				t.Fatalf("expect %s but got %s", c.expect, *(paths[0]))
   223  			}
   224  		})
   225  	}
   226  }
   227  
   228  func newString(s string) *string {
   229  	return &s
   230  }
   231  
   232  func addFile(fs utilfs.Filesystem, path string, file string) error {
   233  	if err := utilfiles.EnsureDir(fs, filepath.Dir(path)); err != nil {
   234  		return err
   235  	}
   236  	return utilfiles.ReplaceFile(fs, path, []byte(file))
   237  }
   238  
   239  func newConfig(t *testing.T) *kubeletconfig.KubeletConfiguration {
   240  	kubeletScheme, _, err := kubeletscheme.NewSchemeAndCodecs()
   241  	if err != nil {
   242  		t.Fatalf("unexpected error: %v", err)
   243  	}
   244  	// get the built-in default configuration
   245  	external := &kubeletconfigv1beta1.KubeletConfiguration{}
   246  	kubeletScheme.Default(external)
   247  	kc := &kubeletconfig.KubeletConfiguration{}
   248  	err = kubeletScheme.Convert(external, kc, nil)
   249  	if err != nil {
   250  		t.Fatalf("unexpected error: %v", err)
   251  	}
   252  	return kc
   253  }
   254  

View as plain text