...

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

Documentation: k8s.io/kubernetes/pkg/kubelet

     1  /*
     2  Copyright 2016 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 kubelet
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  	v1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	core "k8s.io/client-go/testing"
    31  	"k8s.io/kubernetes/pkg/volume"
    32  	volumetest "k8s.io/kubernetes/pkg/volume/testing"
    33  	"k8s.io/kubernetes/pkg/volume/util"
    34  )
    35  
    36  func TestListVolumesForPod(t *testing.T) {
    37  	if testing.Short() {
    38  		t.Skip("skipping test in short mode.")
    39  	}
    40  
    41  	testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
    42  	defer testKubelet.Cleanup()
    43  	kubelet := testKubelet.kubelet
    44  
    45  	pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
    46  		Containers: []v1.Container{
    47  			{
    48  				Name: "container1",
    49  				VolumeMounts: []v1.VolumeMount{
    50  					{
    51  						Name:      "vol1",
    52  						MountPath: "/mnt/vol1",
    53  					},
    54  					{
    55  						Name:      "vol2",
    56  						MountPath: "/mnt/vol2",
    57  					},
    58  				},
    59  			},
    60  		},
    61  		Volumes: []v1.Volume{
    62  			{
    63  				Name: "vol1",
    64  				VolumeSource: v1.VolumeSource{
    65  					GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
    66  						PDName: "fake-device1",
    67  					},
    68  				},
    69  			},
    70  			{
    71  				Name: "vol2",
    72  				VolumeSource: v1.VolumeSource{
    73  					GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
    74  						PDName: "fake-device2",
    75  					},
    76  				},
    77  			},
    78  		},
    79  	})
    80  
    81  	stopCh := runVolumeManager(kubelet)
    82  	defer close(stopCh)
    83  
    84  	kubelet.podManager.SetPods([]*v1.Pod{pod})
    85  	err := kubelet.volumeManager.WaitForAttachAndMount(context.Background(), pod)
    86  	assert.NoError(t, err)
    87  
    88  	podName := util.GetUniquePodName(pod)
    89  
    90  	volumesToReturn, volumeExsit := kubelet.ListVolumesForPod(types.UID(podName))
    91  	assert.True(t, volumeExsit, "expected to find volumes for pod %q", podName)
    92  
    93  	outerVolumeSpecName1 := "vol1"
    94  	assert.NotNil(t, volumesToReturn[outerVolumeSpecName1], "key %s", outerVolumeSpecName1)
    95  
    96  	outerVolumeSpecName2 := "vol2"
    97  	assert.NotNil(t, volumesToReturn[outerVolumeSpecName2], "key %s", outerVolumeSpecName2)
    98  }
    99  
   100  func TestPodVolumesExist(t *testing.T) {
   101  	if testing.Short() {
   102  		t.Skip("skipping test in short mode.")
   103  	}
   104  
   105  	testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
   106  	defer testKubelet.Cleanup()
   107  	kubelet := testKubelet.kubelet
   108  
   109  	pods := []*v1.Pod{
   110  		{
   111  			ObjectMeta: metav1.ObjectMeta{
   112  				Name: "pod1",
   113  				UID:  "pod1uid",
   114  			},
   115  			Spec: v1.PodSpec{
   116  				Containers: []v1.Container{
   117  					{
   118  						Name: "container1",
   119  						VolumeMounts: []v1.VolumeMount{
   120  							{
   121  								Name:      "vol1",
   122  								MountPath: "/mnt/vol1",
   123  							},
   124  						},
   125  					},
   126  				},
   127  				Volumes: []v1.Volume{
   128  					{
   129  						Name: "vol1",
   130  						VolumeSource: v1.VolumeSource{
   131  							RBD: &v1.RBDVolumeSource{
   132  								RBDImage: "fake1",
   133  							},
   134  						},
   135  					},
   136  				},
   137  			},
   138  		},
   139  		{
   140  			ObjectMeta: metav1.ObjectMeta{
   141  				Name: "pod2",
   142  				UID:  "pod2uid",
   143  			},
   144  			Spec: v1.PodSpec{
   145  				Containers: []v1.Container{
   146  					{
   147  						Name: "container2",
   148  						VolumeMounts: []v1.VolumeMount{
   149  							{
   150  								Name:      "vol2",
   151  								MountPath: "/mnt/vol2",
   152  							},
   153  						},
   154  					},
   155  				},
   156  				Volumes: []v1.Volume{
   157  					{
   158  						Name: "vol2",
   159  						VolumeSource: v1.VolumeSource{
   160  							RBD: &v1.RBDVolumeSource{
   161  								RBDImage: "fake2",
   162  							},
   163  						},
   164  					},
   165  				},
   166  			},
   167  		},
   168  		{
   169  			ObjectMeta: metav1.ObjectMeta{
   170  				Name: "pod3",
   171  				UID:  "pod3uid",
   172  			},
   173  			Spec: v1.PodSpec{
   174  				Containers: []v1.Container{
   175  					{
   176  						Name: "container3",
   177  						VolumeMounts: []v1.VolumeMount{
   178  							{
   179  								Name:      "vol3",
   180  								MountPath: "/mnt/vol3",
   181  							},
   182  						},
   183  					},
   184  				},
   185  				Volumes: []v1.Volume{
   186  					{
   187  						Name: "vol3",
   188  						VolumeSource: v1.VolumeSource{
   189  							RBD: &v1.RBDVolumeSource{
   190  								RBDImage: "fake3",
   191  							},
   192  						},
   193  					},
   194  				},
   195  			},
   196  		},
   197  	}
   198  
   199  	stopCh := runVolumeManager(kubelet)
   200  	defer close(stopCh)
   201  
   202  	kubelet.podManager.SetPods(pods)
   203  	for _, pod := range pods {
   204  		err := kubelet.volumeManager.WaitForAttachAndMount(context.Background(), pod)
   205  		assert.NoError(t, err)
   206  	}
   207  
   208  	for _, pod := range pods {
   209  		podVolumesExist := kubelet.podVolumesExist(pod.UID)
   210  		assert.True(t, podVolumesExist, "pod %q", pod.UID)
   211  	}
   212  }
   213  
   214  func TestPodVolumeDeadlineAttachAndMount(t *testing.T) {
   215  	if testing.Short() {
   216  		t.Skip("skipping test in short mode.")
   217  	}
   218  
   219  	testKubelet := newTestKubeletWithImageList(t, nil /*imageList*/, false, /* controllerAttachDetachEnabled */
   220  		false /*initFakeVolumePlugin*/, true /*localStorageCapacityIsolation*/)
   221  
   222  	defer testKubelet.Cleanup()
   223  	kubelet := testKubelet.kubelet
   224  
   225  	// any test cases added here should have volumes that fail to mount
   226  	pods := []*v1.Pod{
   227  		{
   228  			ObjectMeta: metav1.ObjectMeta{
   229  				Name: "pod1",
   230  				UID:  "pod1uid",
   231  			},
   232  			Spec: v1.PodSpec{
   233  				Containers: []v1.Container{
   234  					{
   235  						Name: "container1",
   236  						VolumeMounts: []v1.VolumeMount{
   237  							{
   238  								Name:      "vol1",
   239  								MountPath: "/mnt/vol1",
   240  							},
   241  						},
   242  					},
   243  				},
   244  				Volumes: []v1.Volume{
   245  					{
   246  						Name: "vol1",
   247  						VolumeSource: v1.VolumeSource{
   248  							Secret: &v1.SecretVolumeSource{
   249  								SecretName: "non-existent",
   250  							},
   251  						},
   252  					},
   253  				},
   254  			},
   255  		},
   256  	}
   257  
   258  	stopCh := runVolumeManager(kubelet)
   259  	defer close(stopCh)
   260  
   261  	kubelet.podManager.SetPods(pods)
   262  	for _, pod := range pods {
   263  		start := time.Now()
   264  		// ensure our context times out quickly
   265  		ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second))
   266  		err := kubelet.volumeManager.WaitForAttachAndMount(ctx, pod)
   267  		delta := time.Since(start)
   268  		// the standard timeout is 2 minutes, so if it's just a few seconds we know that the context timeout was the cause
   269  		assert.Lessf(t, delta, 10*time.Second, "WaitForAttachAndMount should timeout when the context is cancelled")
   270  		assert.ErrorIs(t, err, context.DeadlineExceeded)
   271  		cancel()
   272  	}
   273  }
   274  
   275  func TestPodVolumeDeadlineUnmount(t *testing.T) {
   276  	if testing.Short() {
   277  		t.Skip("skipping test in short mode.")
   278  	}
   279  
   280  	testKubelet := newTestKubeletWithImageList(t, nil /*imageList*/, false, /* controllerAttachDetachEnabled */
   281  		true /*initFakeVolumePlugin*/, true /*localStorageCapacityIsolation*/)
   282  
   283  	defer testKubelet.Cleanup()
   284  	kubelet := testKubelet.kubelet
   285  
   286  	// any test cases added here should have volumes that succeed at mounting
   287  	pods := []*v1.Pod{
   288  		{
   289  			ObjectMeta: metav1.ObjectMeta{
   290  				Name: "pod1",
   291  				UID:  "pod1uid",
   292  			},
   293  			Spec: v1.PodSpec{
   294  				Containers: []v1.Container{
   295  					{
   296  						Name: "container1",
   297  						VolumeMounts: []v1.VolumeMount{
   298  							{
   299  								Name:      "vol1",
   300  								MountPath: "/mnt/vol1",
   301  							},
   302  						},
   303  					},
   304  				},
   305  				Volumes: []v1.Volume{
   306  					{
   307  						Name: "vol1",
   308  						VolumeSource: v1.VolumeSource{
   309  							RBD: &v1.RBDVolumeSource{
   310  								RBDImage: "fake-device",
   311  							},
   312  						},
   313  					},
   314  				},
   315  			},
   316  		},
   317  	}
   318  
   319  	stopCh := runVolumeManager(kubelet)
   320  	defer close(stopCh)
   321  
   322  	kubelet.podManager.SetPods(pods)
   323  	for i, pod := range pods {
   324  		if err := kubelet.volumeManager.WaitForAttachAndMount(context.Background(), pod); err != nil {
   325  			t.Fatalf("pod %d failed: %v", i, err)
   326  		}
   327  		start := time.Now()
   328  		// ensure our context times out quickly
   329  		ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second))
   330  		err := kubelet.volumeManager.WaitForUnmount(ctx, pod)
   331  		delta := time.Since(start)
   332  		// the standard timeout is 2 minutes, so if it's just a few seconds we know that the context timeout was the cause
   333  		assert.Lessf(t, delta, 10*time.Second, "WaitForUnmount should timeout when the context is cancelled")
   334  		assert.ErrorIs(t, err, context.DeadlineExceeded)
   335  		cancel()
   336  	}
   337  }
   338  
   339  func TestVolumeAttachAndMountControllerDisabled(t *testing.T) {
   340  	if testing.Short() {
   341  		t.Skip("skipping test in short mode.")
   342  	}
   343  
   344  	testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
   345  	defer testKubelet.Cleanup()
   346  	kubelet := testKubelet.kubelet
   347  
   348  	pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
   349  		Containers: []v1.Container{
   350  			{
   351  				Name: "container1",
   352  				VolumeMounts: []v1.VolumeMount{
   353  					{
   354  						Name:      "vol1",
   355  						MountPath: "/mnt/vol1",
   356  					},
   357  				},
   358  			},
   359  		},
   360  		Volumes: []v1.Volume{
   361  			{
   362  				Name: "vol1",
   363  				VolumeSource: v1.VolumeSource{
   364  					RBD: &v1.RBDVolumeSource{
   365  						RBDImage: "fake",
   366  					},
   367  				},
   368  			},
   369  		},
   370  	})
   371  
   372  	stopCh := runVolumeManager(kubelet)
   373  	defer close(stopCh)
   374  
   375  	kubelet.podManager.SetPods([]*v1.Pod{pod})
   376  	err := kubelet.volumeManager.WaitForAttachAndMount(context.Background(), pod)
   377  	assert.NoError(t, err)
   378  
   379  	podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
   380  		util.GetUniquePodName(pod))
   381  
   382  	expectedPodVolumes := []string{"vol1"}
   383  	assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
   384  	for _, name := range expectedPodVolumes {
   385  		assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
   386  	}
   387  	assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
   388  	assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
   389  		1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
   390  	assert.NoError(t, volumetest.VerifyAttachCallCount(
   391  		1 /* expectedAttachCallCount */, testKubelet.volumePlugin))
   392  	assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
   393  		1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
   394  	assert.NoError(t, volumetest.VerifySetUpCallCount(
   395  		1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
   396  }
   397  
   398  func TestVolumeUnmountAndDetachControllerDisabled(t *testing.T) {
   399  	if testing.Short() {
   400  		t.Skip("skipping test in short mode.")
   401  	}
   402  
   403  	testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
   404  	defer testKubelet.Cleanup()
   405  	kubelet := testKubelet.kubelet
   406  
   407  	pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
   408  		Containers: []v1.Container{
   409  			{
   410  				Name: "container1",
   411  				VolumeMounts: []v1.VolumeMount{
   412  					{
   413  						Name:      "vol1",
   414  						MountPath: "/mnt/vol1",
   415  					},
   416  				},
   417  			},
   418  		},
   419  		Volumes: []v1.Volume{
   420  			{
   421  				Name: "vol1",
   422  				VolumeSource: v1.VolumeSource{
   423  					RBD: &v1.RBDVolumeSource{
   424  						RBDImage: "fake-device",
   425  					},
   426  				},
   427  			},
   428  		},
   429  	})
   430  
   431  	stopCh := runVolumeManager(kubelet)
   432  	defer close(stopCh)
   433  
   434  	// Add pod
   435  	kubelet.podManager.SetPods([]*v1.Pod{pod})
   436  
   437  	// Verify volumes attached
   438  	err := kubelet.volumeManager.WaitForAttachAndMount(context.Background(), pod)
   439  	assert.NoError(t, err)
   440  
   441  	podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
   442  		util.GetUniquePodName(pod))
   443  
   444  	expectedPodVolumes := []string{"vol1"}
   445  	assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
   446  	for _, name := range expectedPodVolumes {
   447  		assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
   448  	}
   449  
   450  	assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
   451  	assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
   452  		1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
   453  	assert.NoError(t, volumetest.VerifyAttachCallCount(
   454  		1 /* expectedAttachCallCount */, testKubelet.volumePlugin))
   455  	assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
   456  		1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
   457  	assert.NoError(t, volumetest.VerifySetUpCallCount(
   458  		1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
   459  
   460  	// Remove pod
   461  	// TODO: technically waitForVolumeUnmount
   462  	kubelet.podWorkers.(*fakePodWorkers).setPodRuntimeBeRemoved(pod.UID)
   463  	kubelet.podManager.SetPods([]*v1.Pod{})
   464  
   465  	assert.NoError(t, kubelet.volumeManager.WaitForUnmount(context.Background(), pod))
   466  	if actual := kubelet.volumeManager.GetMountedVolumesForPod(util.GetUniquePodName(pod)); len(actual) > 0 {
   467  		t.Fatalf("expected volume unmount to wait for no volumes: %v", actual)
   468  	}
   469  
   470  	// Verify volumes unmounted
   471  	podVolumes = kubelet.volumeManager.GetMountedVolumesForPod(
   472  		util.GetUniquePodName(pod))
   473  
   474  	assert.Len(t, podVolumes, 0,
   475  		"Expected volumes to be unmounted and detached. But some volumes are still mounted: %#v", podVolumes)
   476  
   477  	assert.NoError(t, volumetest.VerifyTearDownCallCount(
   478  		1 /* expectedTearDownCallCount */, testKubelet.volumePlugin))
   479  
   480  	// Verify volumes detached and no longer reported as in use
   481  	assert.NoError(t, waitForVolumeDetach(v1.UniqueVolumeName("fake/fake-device"), kubelet.volumeManager))
   482  	assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
   483  	assert.NoError(t, volumetest.VerifyDetachCallCount(
   484  		1 /* expectedDetachCallCount */, testKubelet.volumePlugin))
   485  }
   486  
   487  func TestVolumeAttachAndMountControllerEnabled(t *testing.T) {
   488  	if testing.Short() {
   489  		t.Skip("skipping test in short mode.")
   490  	}
   491  
   492  	testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
   493  	defer testKubelet.Cleanup()
   494  	kubelet := testKubelet.kubelet
   495  	kubeClient := testKubelet.fakeKubeClient
   496  	kubeClient.AddReactor("get", "nodes",
   497  		func(action core.Action) (bool, runtime.Object, error) {
   498  			return true, &v1.Node{
   499  				ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname},
   500  				Status: v1.NodeStatus{
   501  					VolumesAttached: []v1.AttachedVolume{
   502  						{
   503  							Name:       "fake/fake-device",
   504  							DevicePath: "fake/path",
   505  						},
   506  					}},
   507  			}, nil
   508  		})
   509  	kubeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
   510  		return true, nil, fmt.Errorf("no reaction implemented for %s", action)
   511  	})
   512  
   513  	pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
   514  		Containers: []v1.Container{
   515  			{
   516  				Name: "container1",
   517  				VolumeMounts: []v1.VolumeMount{
   518  					{
   519  						Name:      "vol1",
   520  						MountPath: "/mnt/vol1",
   521  					},
   522  				},
   523  			},
   524  		},
   525  		Volumes: []v1.Volume{
   526  			{
   527  				Name: "vol1",
   528  				VolumeSource: v1.VolumeSource{
   529  					RBD: &v1.RBDVolumeSource{
   530  						RBDImage: "fake-device",
   531  					},
   532  				},
   533  			},
   534  		},
   535  	})
   536  
   537  	stopCh := runVolumeManager(kubelet)
   538  	defer close(stopCh)
   539  
   540  	kubelet.podManager.SetPods([]*v1.Pod{pod})
   541  
   542  	// Fake node status update
   543  	go simulateVolumeInUseUpdate(
   544  		v1.UniqueVolumeName("fake/fake-device"),
   545  		stopCh,
   546  		kubelet.volumeManager)
   547  
   548  	assert.NoError(t, kubelet.volumeManager.WaitForAttachAndMount(context.Background(), pod))
   549  
   550  	podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
   551  		util.GetUniquePodName(pod))
   552  	allPodVolumes := kubelet.volumeManager.GetPossiblyMountedVolumesForPod(
   553  		util.GetUniquePodName(pod))
   554  	assert.Equal(t, podVolumes, allPodVolumes, "GetMountedVolumesForPod and GetPossiblyMountedVolumesForPod should return the same volumes")
   555  
   556  	expectedPodVolumes := []string{"vol1"}
   557  	assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
   558  	for _, name := range expectedPodVolumes {
   559  		assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
   560  	}
   561  	assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
   562  	assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
   563  		1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
   564  	assert.NoError(t, volumetest.VerifyZeroAttachCalls(testKubelet.volumePlugin))
   565  	assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
   566  		1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
   567  	assert.NoError(t, volumetest.VerifySetUpCallCount(
   568  		1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
   569  }
   570  
   571  func TestVolumeUnmountAndDetachControllerEnabled(t *testing.T) {
   572  	if testing.Short() {
   573  		t.Skip("skipping test in short mode.")
   574  	}
   575  
   576  	testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
   577  	defer testKubelet.Cleanup()
   578  	kubelet := testKubelet.kubelet
   579  	kubeClient := testKubelet.fakeKubeClient
   580  	kubeClient.AddReactor("get", "nodes",
   581  		func(action core.Action) (bool, runtime.Object, error) {
   582  			return true, &v1.Node{
   583  				ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname},
   584  				Status: v1.NodeStatus{
   585  					VolumesAttached: []v1.AttachedVolume{
   586  						{
   587  							Name:       "fake/fake-device",
   588  							DevicePath: "fake/path",
   589  						},
   590  					}},
   591  			}, nil
   592  		})
   593  	kubeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
   594  		return true, nil, fmt.Errorf("no reaction implemented for %s", action)
   595  	})
   596  
   597  	pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
   598  		Containers: []v1.Container{
   599  			{
   600  				Name: "container1",
   601  				VolumeMounts: []v1.VolumeMount{
   602  					{
   603  						Name:      "vol1",
   604  						MountPath: "/mnt/vol1",
   605  					},
   606  				},
   607  			},
   608  		},
   609  		Volumes: []v1.Volume{
   610  			{
   611  				Name: "vol1",
   612  				VolumeSource: v1.VolumeSource{
   613  					RBD: &v1.RBDVolumeSource{
   614  						RBDImage: "fake-device",
   615  					},
   616  				},
   617  			},
   618  		},
   619  	})
   620  
   621  	stopCh := runVolumeManager(kubelet)
   622  	defer close(stopCh)
   623  
   624  	// Add pod
   625  	kubelet.podManager.SetPods([]*v1.Pod{pod})
   626  
   627  	// Fake node status update
   628  	go simulateVolumeInUseUpdate(
   629  		v1.UniqueVolumeName("fake/fake-device"),
   630  		stopCh,
   631  		kubelet.volumeManager)
   632  
   633  	// Verify volumes attached
   634  	assert.NoError(t, kubelet.volumeManager.WaitForAttachAndMount(context.Background(), pod))
   635  
   636  	podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
   637  		util.GetUniquePodName(pod))
   638  	allPodVolumes := kubelet.volumeManager.GetPossiblyMountedVolumesForPod(
   639  		util.GetUniquePodName(pod))
   640  	assert.Equal(t, podVolumes, allPodVolumes, "GetMountedVolumesForPod and GetPossiblyMountedVolumesForPod should return the same volumes")
   641  
   642  	expectedPodVolumes := []string{"vol1"}
   643  	assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
   644  	for _, name := range expectedPodVolumes {
   645  		assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
   646  	}
   647  
   648  	assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
   649  	assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
   650  		1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
   651  	assert.NoError(t, volumetest.VerifyZeroAttachCalls(testKubelet.volumePlugin))
   652  	assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
   653  		1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
   654  	assert.NoError(t, volumetest.VerifySetUpCallCount(
   655  		1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
   656  
   657  	// Remove pod
   658  	kubelet.podWorkers.(*fakePodWorkers).setPodRuntimeBeRemoved(pod.UID)
   659  	kubelet.podManager.SetPods([]*v1.Pod{})
   660  
   661  	assert.NoError(t, waitForVolumeUnmount(kubelet.volumeManager, pod))
   662  
   663  	// Verify volumes unmounted
   664  	podVolumes = kubelet.volumeManager.GetMountedVolumesForPod(
   665  		util.GetUniquePodName(pod))
   666  	allPodVolumes = kubelet.volumeManager.GetPossiblyMountedVolumesForPod(
   667  		util.GetUniquePodName(pod))
   668  	assert.Equal(t, podVolumes, allPodVolumes, "GetMountedVolumesForPod and GetPossiblyMountedVolumesForPod should return the same volumes")
   669  
   670  	assert.Len(t, podVolumes, 0,
   671  		"Expected volumes to be unmounted and detached. But some volumes are still mounted: %#v", podVolumes)
   672  
   673  	assert.NoError(t, volumetest.VerifyTearDownCallCount(
   674  		1 /* expectedTearDownCallCount */, testKubelet.volumePlugin))
   675  
   676  	// Verify volumes detached and no longer reported as in use
   677  	assert.NoError(t, waitForVolumeDetach(v1.UniqueVolumeName("fake/fake-device"), kubelet.volumeManager))
   678  	assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
   679  	assert.NoError(t, volumetest.VerifyZeroDetachCallCount(testKubelet.volumePlugin))
   680  }
   681  
   682  type stubVolume struct {
   683  	path string
   684  	volume.MetricsNil
   685  }
   686  
   687  func (f *stubVolume) GetPath() string {
   688  	return f.path
   689  }
   690  
   691  func (f *stubVolume) GetAttributes() volume.Attributes {
   692  	return volume.Attributes{}
   693  }
   694  
   695  func (f *stubVolume) SetUp(mounterArgs volume.MounterArgs) error {
   696  	return nil
   697  }
   698  
   699  func (f *stubVolume) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
   700  	return nil
   701  }
   702  
   703  type stubBlockVolume struct {
   704  	dirPath string
   705  	volName string
   706  }
   707  
   708  func (f *stubBlockVolume) GetGlobalMapPath(spec *volume.Spec) (string, error) {
   709  	return "", nil
   710  }
   711  
   712  func (f *stubBlockVolume) GetPodDeviceMapPath() (string, string) {
   713  	return f.dirPath, f.volName
   714  }
   715  
   716  func (f *stubBlockVolume) SetUpDevice() (string, error) {
   717  	return "", nil
   718  }
   719  
   720  func (f stubBlockVolume) MapPodDevice() error {
   721  	return nil
   722  }
   723  
   724  func (f *stubBlockVolume) TearDownDevice(mapPath string, devicePath string) error {
   725  	return nil
   726  }
   727  
   728  func (f *stubBlockVolume) UnmapPodDevice() error {
   729  	return nil
   730  }
   731  
   732  func (f *stubBlockVolume) SupportsMetrics() bool {
   733  	return false
   734  }
   735  
   736  func (f *stubBlockVolume) GetMetrics() (*volume.Metrics, error) {
   737  	return nil, nil
   738  }
   739  

View as plain text