kubelet_volumes_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package kubelet
  14. import (
  15. "fmt"
  16. "testing"
  17. "github.com/stretchr/testify/assert"
  18. v1 "k8s.io/api/core/v1"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. "k8s.io/apimachinery/pkg/runtime"
  21. "k8s.io/apimachinery/pkg/types"
  22. core "k8s.io/client-go/testing"
  23. "k8s.io/kubernetes/pkg/volume"
  24. volumetest "k8s.io/kubernetes/pkg/volume/testing"
  25. "k8s.io/kubernetes/pkg/volume/util"
  26. )
  27. func TestListVolumesForPod(t *testing.T) {
  28. testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
  29. defer testKubelet.Cleanup()
  30. kubelet := testKubelet.kubelet
  31. pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
  32. Containers: []v1.Container{
  33. {
  34. Name: "container1",
  35. VolumeMounts: []v1.VolumeMount{
  36. {
  37. Name: "vol1",
  38. MountPath: "/mnt/vol1",
  39. },
  40. {
  41. Name: "vol2",
  42. MountPath: "/mnt/vol2",
  43. },
  44. },
  45. },
  46. },
  47. Volumes: []v1.Volume{
  48. {
  49. Name: "vol1",
  50. VolumeSource: v1.VolumeSource{
  51. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  52. PDName: "fake-device1",
  53. },
  54. },
  55. },
  56. {
  57. Name: "vol2",
  58. VolumeSource: v1.VolumeSource{
  59. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  60. PDName: "fake-device2",
  61. },
  62. },
  63. },
  64. },
  65. })
  66. stopCh := runVolumeManager(kubelet)
  67. defer close(stopCh)
  68. kubelet.podManager.SetPods([]*v1.Pod{pod})
  69. err := kubelet.volumeManager.WaitForAttachAndMount(pod)
  70. assert.NoError(t, err)
  71. podName := util.GetUniquePodName(pod)
  72. volumesToReturn, volumeExsit := kubelet.ListVolumesForPod(types.UID(podName))
  73. assert.True(t, volumeExsit, "expected to find volumes for pod %q", podName)
  74. outerVolumeSpecName1 := "vol1"
  75. assert.NotNil(t, volumesToReturn[outerVolumeSpecName1], "key %s", outerVolumeSpecName1)
  76. outerVolumeSpecName2 := "vol2"
  77. assert.NotNil(t, volumesToReturn[outerVolumeSpecName2], "key %s", outerVolumeSpecName2)
  78. }
  79. func TestPodVolumesExist(t *testing.T) {
  80. testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
  81. defer testKubelet.Cleanup()
  82. kubelet := testKubelet.kubelet
  83. pods := []*v1.Pod{
  84. {
  85. ObjectMeta: metav1.ObjectMeta{
  86. Name: "pod1",
  87. UID: "pod1uid",
  88. },
  89. Spec: v1.PodSpec{
  90. Containers: []v1.Container{
  91. {
  92. Name: "container1",
  93. VolumeMounts: []v1.VolumeMount{
  94. {
  95. Name: "vol1",
  96. MountPath: "/mnt/vol1",
  97. },
  98. },
  99. },
  100. },
  101. Volumes: []v1.Volume{
  102. {
  103. Name: "vol1",
  104. VolumeSource: v1.VolumeSource{
  105. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  106. PDName: "fake-device1",
  107. },
  108. },
  109. },
  110. },
  111. },
  112. },
  113. {
  114. ObjectMeta: metav1.ObjectMeta{
  115. Name: "pod2",
  116. UID: "pod2uid",
  117. },
  118. Spec: v1.PodSpec{
  119. Containers: []v1.Container{
  120. {
  121. Name: "container2",
  122. VolumeMounts: []v1.VolumeMount{
  123. {
  124. Name: "vol2",
  125. MountPath: "/mnt/vol2",
  126. },
  127. },
  128. },
  129. },
  130. Volumes: []v1.Volume{
  131. {
  132. Name: "vol2",
  133. VolumeSource: v1.VolumeSource{
  134. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  135. PDName: "fake-device2",
  136. },
  137. },
  138. },
  139. },
  140. },
  141. },
  142. {
  143. ObjectMeta: metav1.ObjectMeta{
  144. Name: "pod3",
  145. UID: "pod3uid",
  146. },
  147. Spec: v1.PodSpec{
  148. Containers: []v1.Container{
  149. {
  150. Name: "container3",
  151. VolumeMounts: []v1.VolumeMount{
  152. {
  153. Name: "vol3",
  154. MountPath: "/mnt/vol3",
  155. },
  156. },
  157. },
  158. },
  159. Volumes: []v1.Volume{
  160. {
  161. Name: "vol3",
  162. VolumeSource: v1.VolumeSource{
  163. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  164. PDName: "fake-device3",
  165. },
  166. },
  167. },
  168. },
  169. },
  170. },
  171. }
  172. stopCh := runVolumeManager(kubelet)
  173. defer close(stopCh)
  174. kubelet.podManager.SetPods(pods)
  175. for _, pod := range pods {
  176. err := kubelet.volumeManager.WaitForAttachAndMount(pod)
  177. assert.NoError(t, err)
  178. }
  179. for _, pod := range pods {
  180. podVolumesExist := kubelet.podVolumesExist(pod.UID)
  181. assert.True(t, podVolumesExist, "pod %q", pod.UID)
  182. }
  183. }
  184. func TestVolumeAttachAndMountControllerDisabled(t *testing.T) {
  185. testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
  186. defer testKubelet.Cleanup()
  187. kubelet := testKubelet.kubelet
  188. pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
  189. Containers: []v1.Container{
  190. {
  191. Name: "container1",
  192. VolumeMounts: []v1.VolumeMount{
  193. {
  194. Name: "vol1",
  195. MountPath: "/mnt/vol1",
  196. },
  197. },
  198. },
  199. },
  200. Volumes: []v1.Volume{
  201. {
  202. Name: "vol1",
  203. VolumeSource: v1.VolumeSource{
  204. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  205. PDName: "fake-device",
  206. },
  207. },
  208. },
  209. },
  210. })
  211. stopCh := runVolumeManager(kubelet)
  212. defer close(stopCh)
  213. kubelet.podManager.SetPods([]*v1.Pod{pod})
  214. err := kubelet.volumeManager.WaitForAttachAndMount(pod)
  215. assert.NoError(t, err)
  216. podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
  217. util.GetUniquePodName(pod))
  218. expectedPodVolumes := []string{"vol1"}
  219. assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
  220. for _, name := range expectedPodVolumes {
  221. assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
  222. }
  223. assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
  224. assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
  225. 1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
  226. assert.NoError(t, volumetest.VerifyAttachCallCount(
  227. 1 /* expectedAttachCallCount */, testKubelet.volumePlugin))
  228. assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
  229. 1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
  230. assert.NoError(t, volumetest.VerifySetUpCallCount(
  231. 1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
  232. }
  233. func TestVolumeUnmountAndDetachControllerDisabled(t *testing.T) {
  234. testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
  235. defer testKubelet.Cleanup()
  236. kubelet := testKubelet.kubelet
  237. pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
  238. Containers: []v1.Container{
  239. {
  240. Name: "container1",
  241. VolumeMounts: []v1.VolumeMount{
  242. {
  243. Name: "vol1",
  244. MountPath: "/mnt/vol1",
  245. },
  246. },
  247. },
  248. },
  249. Volumes: []v1.Volume{
  250. {
  251. Name: "vol1",
  252. VolumeSource: v1.VolumeSource{
  253. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  254. PDName: "fake-device",
  255. },
  256. },
  257. },
  258. },
  259. })
  260. stopCh := runVolumeManager(kubelet)
  261. defer close(stopCh)
  262. // Add pod
  263. kubelet.podManager.SetPods([]*v1.Pod{pod})
  264. // Verify volumes attached
  265. err := kubelet.volumeManager.WaitForAttachAndMount(pod)
  266. assert.NoError(t, err)
  267. podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
  268. util.GetUniquePodName(pod))
  269. expectedPodVolumes := []string{"vol1"}
  270. assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
  271. for _, name := range expectedPodVolumes {
  272. assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
  273. }
  274. assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
  275. assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
  276. 1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
  277. assert.NoError(t, volumetest.VerifyAttachCallCount(
  278. 1 /* expectedAttachCallCount */, testKubelet.volumePlugin))
  279. assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
  280. 1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
  281. assert.NoError(t, volumetest.VerifySetUpCallCount(
  282. 1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
  283. // Remove pod
  284. kubelet.podManager.SetPods([]*v1.Pod{})
  285. assert.NoError(t, waitForVolumeUnmount(kubelet.volumeManager, pod))
  286. // Verify volumes unmounted
  287. podVolumes = kubelet.volumeManager.GetMountedVolumesForPod(
  288. util.GetUniquePodName(pod))
  289. assert.Len(t, podVolumes, 0,
  290. "Expected volumes to be unmounted and detached. But some volumes are still mounted: %#v", podVolumes)
  291. assert.NoError(t, volumetest.VerifyTearDownCallCount(
  292. 1 /* expectedTearDownCallCount */, testKubelet.volumePlugin))
  293. // Verify volumes detached and no longer reported as in use
  294. assert.NoError(t, waitForVolumeDetach(v1.UniqueVolumeName("fake/fake-device"), kubelet.volumeManager))
  295. assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
  296. assert.NoError(t, volumetest.VerifyDetachCallCount(
  297. 1 /* expectedDetachCallCount */, testKubelet.volumePlugin))
  298. }
  299. func TestVolumeAttachAndMountControllerEnabled(t *testing.T) {
  300. testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
  301. defer testKubelet.Cleanup()
  302. kubelet := testKubelet.kubelet
  303. kubeClient := testKubelet.fakeKubeClient
  304. kubeClient.AddReactor("get", "nodes",
  305. func(action core.Action) (bool, runtime.Object, error) {
  306. return true, &v1.Node{
  307. ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname},
  308. Status: v1.NodeStatus{
  309. VolumesAttached: []v1.AttachedVolume{
  310. {
  311. Name: "fake/fake-device",
  312. DevicePath: "fake/path",
  313. },
  314. }},
  315. }, nil
  316. })
  317. kubeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
  318. return true, nil, fmt.Errorf("no reaction implemented for %s", action)
  319. })
  320. pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
  321. Containers: []v1.Container{
  322. {
  323. Name: "container1",
  324. VolumeMounts: []v1.VolumeMount{
  325. {
  326. Name: "vol1",
  327. MountPath: "/mnt/vol1",
  328. },
  329. },
  330. },
  331. },
  332. Volumes: []v1.Volume{
  333. {
  334. Name: "vol1",
  335. VolumeSource: v1.VolumeSource{
  336. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  337. PDName: "fake-device",
  338. },
  339. },
  340. },
  341. },
  342. })
  343. stopCh := runVolumeManager(kubelet)
  344. defer close(stopCh)
  345. kubelet.podManager.SetPods([]*v1.Pod{pod})
  346. // Fake node status update
  347. go simulateVolumeInUseUpdate(
  348. v1.UniqueVolumeName("fake/fake-device"),
  349. stopCh,
  350. kubelet.volumeManager)
  351. assert.NoError(t, kubelet.volumeManager.WaitForAttachAndMount(pod))
  352. podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
  353. util.GetUniquePodName(pod))
  354. expectedPodVolumes := []string{"vol1"}
  355. assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
  356. for _, name := range expectedPodVolumes {
  357. assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
  358. }
  359. assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
  360. assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
  361. 1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
  362. assert.NoError(t, volumetest.VerifyZeroAttachCalls(testKubelet.volumePlugin))
  363. assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
  364. 1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
  365. assert.NoError(t, volumetest.VerifySetUpCallCount(
  366. 1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
  367. }
  368. func TestVolumeUnmountAndDetachControllerEnabled(t *testing.T) {
  369. testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
  370. defer testKubelet.Cleanup()
  371. kubelet := testKubelet.kubelet
  372. kubeClient := testKubelet.fakeKubeClient
  373. kubeClient.AddReactor("get", "nodes",
  374. func(action core.Action) (bool, runtime.Object, error) {
  375. return true, &v1.Node{
  376. ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname},
  377. Status: v1.NodeStatus{
  378. VolumesAttached: []v1.AttachedVolume{
  379. {
  380. Name: "fake/fake-device",
  381. DevicePath: "fake/path",
  382. },
  383. }},
  384. }, nil
  385. })
  386. kubeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
  387. return true, nil, fmt.Errorf("no reaction implemented for %s", action)
  388. })
  389. pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
  390. Containers: []v1.Container{
  391. {
  392. Name: "container1",
  393. VolumeMounts: []v1.VolumeMount{
  394. {
  395. Name: "vol1",
  396. MountPath: "/mnt/vol1",
  397. },
  398. },
  399. },
  400. },
  401. Volumes: []v1.Volume{
  402. {
  403. Name: "vol1",
  404. VolumeSource: v1.VolumeSource{
  405. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  406. PDName: "fake-device",
  407. },
  408. },
  409. },
  410. },
  411. })
  412. stopCh := runVolumeManager(kubelet)
  413. defer close(stopCh)
  414. // Add pod
  415. kubelet.podManager.SetPods([]*v1.Pod{pod})
  416. // Fake node status update
  417. go simulateVolumeInUseUpdate(
  418. v1.UniqueVolumeName("fake/fake-device"),
  419. stopCh,
  420. kubelet.volumeManager)
  421. // Verify volumes attached
  422. assert.NoError(t, kubelet.volumeManager.WaitForAttachAndMount(pod))
  423. podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
  424. util.GetUniquePodName(pod))
  425. expectedPodVolumes := []string{"vol1"}
  426. assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
  427. for _, name := range expectedPodVolumes {
  428. assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
  429. }
  430. assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
  431. assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
  432. 1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
  433. assert.NoError(t, volumetest.VerifyZeroAttachCalls(testKubelet.volumePlugin))
  434. assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
  435. 1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
  436. assert.NoError(t, volumetest.VerifySetUpCallCount(
  437. 1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
  438. // Remove pod
  439. kubelet.podManager.SetPods([]*v1.Pod{})
  440. assert.NoError(t, waitForVolumeUnmount(kubelet.volumeManager, pod))
  441. // Verify volumes unmounted
  442. podVolumes = kubelet.volumeManager.GetMountedVolumesForPod(
  443. util.GetUniquePodName(pod))
  444. assert.Len(t, podVolumes, 0,
  445. "Expected volumes to be unmounted and detached. But some volumes are still mounted: %#v", podVolumes)
  446. assert.NoError(t, volumetest.VerifyTearDownCallCount(
  447. 1 /* expectedTearDownCallCount */, testKubelet.volumePlugin))
  448. // Verify volumes detached and no longer reported as in use
  449. assert.NoError(t, waitForVolumeDetach(v1.UniqueVolumeName("fake/fake-device"), kubelet.volumeManager))
  450. assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
  451. assert.NoError(t, volumetest.VerifyZeroDetachCallCount(testKubelet.volumePlugin))
  452. }
  453. type stubVolume struct {
  454. path string
  455. volume.MetricsNil
  456. }
  457. func (f *stubVolume) GetPath() string {
  458. return f.path
  459. }
  460. func (f *stubVolume) GetAttributes() volume.Attributes {
  461. return volume.Attributes{}
  462. }
  463. func (f *stubVolume) CanMount() error {
  464. return nil
  465. }
  466. func (f *stubVolume) SetUp(mounterArgs volume.MounterArgs) error {
  467. return nil
  468. }
  469. func (f *stubVolume) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
  470. return nil
  471. }
  472. type stubBlockVolume struct {
  473. dirPath string
  474. volName string
  475. }
  476. func (f *stubBlockVolume) GetGlobalMapPath(spec *volume.Spec) (string, error) {
  477. return "", nil
  478. }
  479. func (f *stubBlockVolume) GetPodDeviceMapPath() (string, string) {
  480. return f.dirPath, f.volName
  481. }
  482. func (f *stubBlockVolume) SetUpDevice() (string, error) {
  483. return "", nil
  484. }
  485. func (f stubBlockVolume) MapPodDevice() error {
  486. return nil
  487. }
  488. func (f *stubBlockVolume) TearDownDevice(mapPath string, devicePath string) error {
  489. return nil
  490. }
  491. func (f *stubBlockVolume) UnmapPodDevice() error {
  492. return nil
  493. }