...

Source file src/k8s.io/kubernetes/pkg/kubelet/kubelet_volumes_linux_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5  Copyright 2018 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package kubelet
    21  
    22  import (
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  	"testing"
    27  
    28  	v1 "k8s.io/api/core/v1"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  
    31  	"k8s.io/apimachinery/pkg/types"
    32  	_ "k8s.io/kubernetes/pkg/apis/core/install"
    33  	"k8s.io/mount-utils"
    34  )
    35  
    36  func validateDirExists(dir string) error {
    37  	_, err := os.ReadDir(dir)
    38  	if err != nil {
    39  		return err
    40  	}
    41  	return nil
    42  }
    43  
    44  func validateDirNotExists(dir string) error {
    45  	_, err := os.ReadDir(dir)
    46  	if os.IsNotExist(err) {
    47  		return nil
    48  	}
    49  	if err != nil {
    50  		return err
    51  	}
    52  	return fmt.Errorf("dir %q still exists", dir)
    53  }
    54  
    55  func TestCleanupOrphanedPodDirs(t *testing.T) {
    56  	if testing.Short() {
    57  		t.Skip("skipping test in short mode.")
    58  	}
    59  
    60  	testCases := map[string]struct {
    61  		pods         []*v1.Pod
    62  		prepareFunc  func(kubelet *Kubelet) error
    63  		validateFunc func(kubelet *Kubelet) error
    64  		expectErr    bool
    65  	}{
    66  		"nothing-to-do": {},
    67  		"pods-dir-not-found": {
    68  			prepareFunc: func(kubelet *Kubelet) error {
    69  				return os.Remove(kubelet.getPodsDir())
    70  			},
    71  			expectErr: true,
    72  		},
    73  		"pod-doesnot-exist-novolume": {
    74  			prepareFunc: func(kubelet *Kubelet) error {
    75  				podDir := kubelet.getPodDir("pod1uid")
    76  				return os.MkdirAll(filepath.Join(podDir, "not/a/volume"), 0750)
    77  			},
    78  			validateFunc: func(kubelet *Kubelet) error {
    79  				podDir := kubelet.getPodDir("pod1uid")
    80  				return validateDirNotExists(filepath.Join(podDir, "not"))
    81  			},
    82  		},
    83  		"pod-exists-with-volume": {
    84  			pods: []*v1.Pod{
    85  				{
    86  					ObjectMeta: metav1.ObjectMeta{
    87  						Name: "pod1",
    88  						UID:  "pod1uid",
    89  					},
    90  				},
    91  			},
    92  			prepareFunc: func(kubelet *Kubelet) error {
    93  				podDir := kubelet.getPodDir("pod1uid")
    94  				return os.MkdirAll(filepath.Join(podDir, "volumes/plugin/name"), 0750)
    95  			},
    96  			validateFunc: func(kubelet *Kubelet) error {
    97  				podDir := kubelet.getPodDir("pod1uid")
    98  				return validateDirExists(filepath.Join(podDir, "volumes/plugin/name"))
    99  			},
   100  		},
   101  		"pod-doesnot-exist-with-volume": {
   102  			prepareFunc: func(kubelet *Kubelet) error {
   103  				podDir := kubelet.getPodDir("pod1uid")
   104  				return os.MkdirAll(filepath.Join(podDir, "volumes/plugin/name"), 0750)
   105  			},
   106  			validateFunc: func(kubelet *Kubelet) error {
   107  				podDir := kubelet.getPodDir("pod1uid")
   108  				return validateDirNotExists(podDir)
   109  			},
   110  		},
   111  		"pod-doesnot-exist-with-volume-subdir": {
   112  			prepareFunc: func(kubelet *Kubelet) error {
   113  				podDir := kubelet.getPodDir("pod1uid")
   114  				return os.MkdirAll(filepath.Join(podDir, "volumes/plugin/name/subdir"), 0750)
   115  			},
   116  			validateFunc: func(kubelet *Kubelet) error {
   117  				podDir := kubelet.getPodDir("pod1uid")
   118  				return validateDirNotExists(filepath.Join(podDir, "volumes"))
   119  			},
   120  		},
   121  		"pod-doesnot-exist-with-subpath": {
   122  			prepareFunc: func(kubelet *Kubelet) error {
   123  				podDir := kubelet.getPodDir("pod1uid")
   124  				return os.MkdirAll(filepath.Join(podDir, "volume-subpaths/volume/container/index"), 0750)
   125  			},
   126  			validateFunc: func(kubelet *Kubelet) error {
   127  				podDir := kubelet.getPodDir("pod1uid")
   128  				return validateDirNotExists(podDir)
   129  			},
   130  		},
   131  		"pod-doesnot-exist-with-subpath-top": {
   132  			prepareFunc: func(kubelet *Kubelet) error {
   133  				podDir := kubelet.getPodDir("pod1uid")
   134  				return os.MkdirAll(filepath.Join(podDir, "volume-subpaths"), 0750)
   135  			},
   136  			validateFunc: func(kubelet *Kubelet) error {
   137  				podDir := kubelet.getPodDir("pod1uid")
   138  				return validateDirNotExists(podDir)
   139  			},
   140  		},
   141  		"pod-doesnot-exists-with-populated-volume": {
   142  			prepareFunc: func(kubelet *Kubelet) error {
   143  				podDir := kubelet.getPodDir("pod1uid")
   144  				volumePath := filepath.Join(podDir, "volumes/plugin/name")
   145  				if err := os.MkdirAll(volumePath, 0750); err != nil {
   146  					return err
   147  				}
   148  				return os.WriteFile(filepath.Join(volumePath, "test.txt"), []byte("test1"), 0640)
   149  			},
   150  			validateFunc: func(kubelet *Kubelet) error {
   151  				podDir := kubelet.getPodDir("pod1uid")
   152  				return validateDirExists(filepath.Join(podDir, "volumes/plugin/name"))
   153  			},
   154  		},
   155  		"pod-doesnot-exists-with-populated-subpath": {
   156  			prepareFunc: func(kubelet *Kubelet) error {
   157  				podDir := kubelet.getPodDir("pod1uid")
   158  				subPath := filepath.Join(podDir, "volume-subpaths/volume/container/index")
   159  				if err := os.MkdirAll(subPath, 0750); err != nil {
   160  					return err
   161  				}
   162  				return os.WriteFile(filepath.Join(subPath, "test.txt"), []byte("test1"), 0640)
   163  			},
   164  			validateFunc: func(kubelet *Kubelet) error {
   165  				podDir := kubelet.getPodDir("pod1uid")
   166  				return validateDirExists(filepath.Join(podDir, "volume-subpaths/volume/container/index"))
   167  			},
   168  		},
   169  		// TODO: test volume in volume-manager
   170  	}
   171  
   172  	for name, tc := range testCases {
   173  		t.Run(name, func(t *testing.T) {
   174  			testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
   175  			defer testKubelet.Cleanup()
   176  			kubelet := testKubelet.kubelet
   177  
   178  			if tc.prepareFunc != nil {
   179  				if err := tc.prepareFunc(kubelet); err != nil {
   180  					t.Fatalf("%s failed preparation: %v", name, err)
   181  				}
   182  			}
   183  
   184  			err := kubelet.cleanupOrphanedPodDirs(tc.pods, nil)
   185  			if tc.expectErr && err == nil {
   186  				t.Errorf("%s failed: expected error, got success", name)
   187  			}
   188  			if !tc.expectErr && err != nil {
   189  				t.Errorf("%s failed: got error %v", name, err)
   190  			}
   191  
   192  			if tc.validateFunc != nil {
   193  				if err := tc.validateFunc(kubelet); err != nil {
   194  					t.Errorf("%s failed validation: %v", name, err)
   195  				}
   196  			}
   197  
   198  		})
   199  	}
   200  }
   201  
   202  func TestPodVolumesExistWithMount(t *testing.T) {
   203  	poduid := types.UID("poduid")
   204  	testCases := map[string]struct {
   205  		prepareFunc func(kubelet *Kubelet) error
   206  		expected    bool
   207  	}{
   208  		"noncsivolume-dir-not-exist": {
   209  			prepareFunc: func(kubelet *Kubelet) error {
   210  				return nil
   211  			},
   212  			expected: false,
   213  		},
   214  		"noncsivolume-dir-exist-noplugins": {
   215  			prepareFunc: func(kubelet *Kubelet) error {
   216  				podDir := kubelet.getPodDir(poduid)
   217  				return os.MkdirAll(filepath.Join(podDir, "volumes/"), 0750)
   218  			},
   219  			expected: false,
   220  		},
   221  		"noncsivolume-dir-exist-nomount": {
   222  			prepareFunc: func(kubelet *Kubelet) error {
   223  				podDir := kubelet.getPodDir(poduid)
   224  				return os.MkdirAll(filepath.Join(podDir, "volumes/plugin/name"), 0750)
   225  			},
   226  			expected: false,
   227  		},
   228  		"noncsivolume-dir-exist-with-mount": {
   229  			prepareFunc: func(kubelet *Kubelet) error {
   230  				podDir := kubelet.getPodDir(poduid)
   231  				volumePath := filepath.Join(podDir, "volumes/plugin/name")
   232  				if err := os.MkdirAll(volumePath, 0750); err != nil {
   233  					return err
   234  				}
   235  				fm := mount.NewFakeMounter(
   236  					[]mount.MountPoint{
   237  						{Device: "/dev/sdb", Path: volumePath},
   238  					})
   239  				kubelet.mounter = fm
   240  				return nil
   241  			},
   242  			expected: true,
   243  		},
   244  		"noncsivolume-dir-exist-nomount-withcsimountpath": {
   245  			prepareFunc: func(kubelet *Kubelet) error {
   246  				podDir := kubelet.getPodDir(poduid)
   247  				volumePath := filepath.Join(podDir, "volumes/plugin/name/mount")
   248  				if err := os.MkdirAll(volumePath, 0750); err != nil {
   249  					return err
   250  				}
   251  				fm := mount.NewFakeMounter(
   252  					[]mount.MountPoint{
   253  						{Device: "/dev/sdb", Path: volumePath},
   254  					})
   255  				kubelet.mounter = fm
   256  				return nil
   257  			},
   258  			expected: false,
   259  		},
   260  		"csivolume-dir-exist-nomount": {
   261  			prepareFunc: func(kubelet *Kubelet) error {
   262  				podDir := kubelet.getPodDir(poduid)
   263  				volumePath := filepath.Join(podDir, "volumes/kubernetes.io~csi/name")
   264  				return os.MkdirAll(volumePath, 0750)
   265  			},
   266  			expected: false,
   267  		},
   268  		"csivolume-dir-exist-mount-nocsimountpath": {
   269  			prepareFunc: func(kubelet *Kubelet) error {
   270  				podDir := kubelet.getPodDir(poduid)
   271  				volumePath := filepath.Join(podDir, "volumes/kubernetes.io~csi/name/mount")
   272  				return os.MkdirAll(volumePath, 0750)
   273  			},
   274  			expected: false,
   275  		},
   276  		"csivolume-dir-exist-withcsimountpath": {
   277  			prepareFunc: func(kubelet *Kubelet) error {
   278  				podDir := kubelet.getPodDir(poduid)
   279  				volumePath := filepath.Join(podDir, "volumes/kubernetes.io~csi/name/mount")
   280  				if err := os.MkdirAll(volumePath, 0750); err != nil {
   281  					return err
   282  				}
   283  				fm := mount.NewFakeMounter(
   284  					[]mount.MountPoint{
   285  						{Device: "/dev/sdb", Path: volumePath},
   286  					})
   287  				kubelet.mounter = fm
   288  				return nil
   289  			},
   290  			expected: true,
   291  		},
   292  	}
   293  
   294  	for name, tc := range testCases {
   295  		t.Run(name, func(t *testing.T) {
   296  			testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
   297  			defer testKubelet.Cleanup()
   298  			kubelet := testKubelet.kubelet
   299  
   300  			if tc.prepareFunc != nil {
   301  				if err := tc.prepareFunc(kubelet); err != nil {
   302  					t.Fatalf("%s failed preparation: %v", name, err)
   303  				}
   304  			}
   305  
   306  			exist := kubelet.podVolumesExist(poduid)
   307  			if tc.expected != exist {
   308  				t.Errorf("%s failed: expected %t, got %t", name, tc.expected, exist)
   309  			}
   310  		})
   311  	}
   312  }
   313  

View as plain text