attacher_test.go 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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 photon_pd
  14. import (
  15. "context"
  16. "errors"
  17. "testing"
  18. "k8s.io/api/core/v1"
  19. "k8s.io/kubernetes/pkg/cloudprovider/providers/photon"
  20. "k8s.io/kubernetes/pkg/volume"
  21. volumetest "k8s.io/kubernetes/pkg/volume/testing"
  22. "k8s.io/apimachinery/pkg/types"
  23. "k8s.io/klog"
  24. )
  25. func TestGetDeviceName_Volume(t *testing.T) {
  26. plugin := newPlugin()
  27. name := "my-photon-volume"
  28. spec := createVolSpec(name, false)
  29. deviceName, err := plugin.GetVolumeName(spec)
  30. if err != nil {
  31. t.Errorf("GetDeviceName error: %v", err)
  32. }
  33. if deviceName != name {
  34. t.Errorf("GetDeviceName error: expected %s, got %s", name, deviceName)
  35. }
  36. }
  37. func TestGetDeviceName_PersistentVolume(t *testing.T) {
  38. plugin := newPlugin()
  39. name := "my-photon-pv"
  40. spec := createPVSpec(name, true)
  41. deviceName, err := plugin.GetVolumeName(spec)
  42. if err != nil {
  43. t.Errorf("GetDeviceName error: %v", err)
  44. }
  45. if deviceName != name {
  46. t.Errorf("GetDeviceName error: expected %s, got %s", name, deviceName)
  47. }
  48. }
  49. // One testcase for TestAttachDetach table test below
  50. type testcase struct {
  51. name string
  52. // For fake Photon cloud provider:
  53. attach attachCall
  54. detach detachCall
  55. diskIsAttached diskIsAttachedCall
  56. t *testing.T
  57. // Actual test to run
  58. test func(test *testcase) (string, error)
  59. // Expected return of the test
  60. expectedDevice string
  61. expectedError error
  62. }
  63. func TestAttachDetach(t *testing.T) {
  64. diskName := "000-000-000"
  65. nodeName := types.NodeName("instance")
  66. readOnly := false
  67. spec := createVolSpec(diskName, readOnly)
  68. detachError := errors.New("Fake detach error")
  69. diskCheckError := errors.New("Fake DiskIsAttached error")
  70. tests := []testcase{
  71. // Successful Attach call
  72. {
  73. name: "Attach_Positive",
  74. diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
  75. attach: attachCall{diskName, nodeName, nil},
  76. test: func(testcase *testcase) (string, error) {
  77. attacher := newAttacher(testcase)
  78. return attacher.Attach(spec, nodeName)
  79. },
  80. expectedDevice: "/dev/disk/by-id/wwn-0x000000000",
  81. },
  82. // Disk is already attached
  83. {
  84. name: "Attach_Positive_AlreadyAttached",
  85. diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
  86. attach: attachCall{diskName, nodeName, nil},
  87. test: func(testcase *testcase) (string, error) {
  88. attacher := newAttacher(testcase)
  89. return attacher.Attach(spec, nodeName)
  90. },
  91. expectedDevice: "/dev/disk/by-id/wwn-0x000000000",
  92. },
  93. // Detach succeeds
  94. {
  95. name: "Detach_Positive",
  96. diskIsAttached: diskIsAttachedCall{diskName, nodeName, true, nil},
  97. detach: detachCall{diskName, nodeName, nil},
  98. test: func(testcase *testcase) (string, error) {
  99. detacher := newDetacher(testcase)
  100. return "", detacher.Detach(diskName, nodeName)
  101. },
  102. },
  103. // Disk is already detached
  104. {
  105. name: "Detach_Positive_AlreadyDetached",
  106. diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, nil},
  107. test: func(testcase *testcase) (string, error) {
  108. detacher := newDetacher(testcase)
  109. return "", detacher.Detach(diskName, nodeName)
  110. },
  111. },
  112. // Detach succeeds when DiskIsAttached fails
  113. {
  114. name: "Detach_Positive_CheckFails",
  115. diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
  116. detach: detachCall{diskName, nodeName, nil},
  117. test: func(testcase *testcase) (string, error) {
  118. detacher := newDetacher(testcase)
  119. return "", detacher.Detach(diskName, nodeName)
  120. },
  121. },
  122. // Detach fails
  123. {
  124. name: "Detach_Negative",
  125. diskIsAttached: diskIsAttachedCall{diskName, nodeName, false, diskCheckError},
  126. detach: detachCall{diskName, nodeName, detachError},
  127. test: func(testcase *testcase) (string, error) {
  128. detacher := newDetacher(testcase)
  129. return "", detacher.Detach(diskName, nodeName)
  130. },
  131. expectedError: detachError,
  132. },
  133. }
  134. for _, testcase := range tests {
  135. testcase.t = t
  136. device, err := testcase.test(&testcase)
  137. if err != testcase.expectedError {
  138. t.Errorf("%s failed: expected err=%q, got %q", testcase.name, testcase.expectedError.Error(), err.Error())
  139. }
  140. if device != testcase.expectedDevice {
  141. t.Errorf("%s failed: expected device=%q, got %q", testcase.name, testcase.expectedDevice, device)
  142. }
  143. t.Logf("Test %q succeeded", testcase.name)
  144. }
  145. }
  146. // newPlugin creates a new gcePersistentDiskPlugin with fake cloud, NewAttacher
  147. // and NewDetacher won't work.
  148. func newPlugin() *photonPersistentDiskPlugin {
  149. host := volumetest.NewFakeVolumeHost("/tmp", nil, nil)
  150. plugins := ProbeVolumePlugins()
  151. plugin := plugins[0]
  152. plugin.Init(host)
  153. return plugin.(*photonPersistentDiskPlugin)
  154. }
  155. func newAttacher(testcase *testcase) *photonPersistentDiskAttacher {
  156. return &photonPersistentDiskAttacher{
  157. host: nil,
  158. photonDisks: testcase,
  159. }
  160. }
  161. func newDetacher(testcase *testcase) *photonPersistentDiskDetacher {
  162. return &photonPersistentDiskDetacher{
  163. photonDisks: testcase,
  164. }
  165. }
  166. func createVolSpec(name string, readOnly bool) *volume.Spec {
  167. return &volume.Spec{
  168. Volume: &v1.Volume{
  169. VolumeSource: v1.VolumeSource{
  170. PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
  171. PdID: name,
  172. },
  173. },
  174. },
  175. }
  176. }
  177. func createPVSpec(name string, readOnly bool) *volume.Spec {
  178. return &volume.Spec{
  179. PersistentVolume: &v1.PersistentVolume{
  180. Spec: v1.PersistentVolumeSpec{
  181. PersistentVolumeSource: v1.PersistentVolumeSource{
  182. PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
  183. PdID: name,
  184. },
  185. },
  186. },
  187. },
  188. }
  189. }
  190. // Fake PhotonPD implementation
  191. type attachCall struct {
  192. diskName string
  193. nodeName types.NodeName
  194. ret error
  195. }
  196. type detachCall struct {
  197. diskName string
  198. nodeName types.NodeName
  199. ret error
  200. }
  201. type diskIsAttachedCall struct {
  202. diskName string
  203. nodeName types.NodeName
  204. isAttached bool
  205. ret error
  206. }
  207. func (testcase *testcase) AttachDisk(ctx context.Context, diskName string, nodeName types.NodeName) error {
  208. expected := &testcase.attach
  209. if expected.diskName == "" && expected.nodeName == "" {
  210. // testcase.attach looks uninitialized, test did not expect to call
  211. // AttachDisk
  212. testcase.t.Errorf("Unexpected AttachDisk call!")
  213. return errors.New("Unexpected AttachDisk call!")
  214. }
  215. if expected.diskName != diskName {
  216. testcase.t.Errorf("Unexpected AttachDisk call: expected diskName %s, got %s", expected.diskName, diskName)
  217. return errors.New("Unexpected AttachDisk call: wrong diskName")
  218. }
  219. if expected.nodeName != nodeName {
  220. testcase.t.Errorf("Unexpected AttachDisk call: expected nodeName %s, got %s", expected.nodeName, nodeName)
  221. return errors.New("Unexpected AttachDisk call: wrong nodeName")
  222. }
  223. klog.V(4).Infof("AttachDisk call: %s, %s, returning %v", diskName, nodeName, expected.ret)
  224. return expected.ret
  225. }
  226. func (testcase *testcase) DetachDisk(ctx context.Context, diskName string, nodeName types.NodeName) error {
  227. expected := &testcase.detach
  228. if expected.diskName == "" && expected.nodeName == "" {
  229. // testcase.detach looks uninitialized, test did not expect to call
  230. // DetachDisk
  231. testcase.t.Errorf("Unexpected DetachDisk call!")
  232. return errors.New("Unexpected DetachDisk call!")
  233. }
  234. if expected.diskName != diskName {
  235. testcase.t.Errorf("Unexpected DetachDisk call: expected diskName %s, got %s", expected.diskName, diskName)
  236. return errors.New("Unexpected DetachDisk call: wrong diskName")
  237. }
  238. if expected.nodeName != nodeName {
  239. testcase.t.Errorf("Unexpected DetachDisk call: expected nodeName %s, got %s", expected.nodeName, nodeName)
  240. return errors.New("Unexpected DetachDisk call: wrong nodeName")
  241. }
  242. klog.V(4).Infof("DetachDisk call: %s, %s, returning %v", diskName, nodeName, expected.ret)
  243. return expected.ret
  244. }
  245. func (testcase *testcase) DiskIsAttached(ctx context.Context, diskName string, nodeName types.NodeName) (bool, error) {
  246. expected := &testcase.diskIsAttached
  247. if expected.diskName == "" && expected.nodeName == "" {
  248. // testcase.diskIsAttached looks uninitialized, test did not expect to
  249. // call DiskIsAttached
  250. testcase.t.Errorf("Unexpected DiskIsAttached call!")
  251. return false, errors.New("Unexpected DiskIsAttached call!")
  252. }
  253. if expected.diskName != diskName {
  254. testcase.t.Errorf("Unexpected DiskIsAttached call: expected diskName %s, got %s", expected.diskName, diskName)
  255. return false, errors.New("Unexpected DiskIsAttached call: wrong diskName")
  256. }
  257. if expected.nodeName != nodeName {
  258. testcase.t.Errorf("Unexpected DiskIsAttached call: expected nodeName %s, got %s", expected.nodeName, nodeName)
  259. return false, errors.New("Unexpected DiskIsAttached call: wrong nodeName")
  260. }
  261. klog.V(4).Infof("DiskIsAttached call: %s, %s, returning %v, %v", diskName, nodeName, expected.isAttached, expected.ret)
  262. return expected.isAttached, expected.ret
  263. }
  264. func (testcase *testcase) DisksAreAttached(ctx context.Context, diskNames []string, nodeName types.NodeName) (map[string]bool, error) {
  265. return nil, errors.New("Not implemented")
  266. }
  267. func (testcase *testcase) CreateDisk(volumeOptions *photon.VolumeOptions) (volumeName string, err error) {
  268. return "", errors.New("Not implemented")
  269. }
  270. func (testcase *testcase) DeleteDisk(volumeName string) error {
  271. return errors.New("Not implemented")
  272. }
  273. func (testcase *testcase) GetVolumeLabels(volumeName string) (map[string]string, error) {
  274. return map[string]string{}, errors.New("Not implemented")
  275. }
  276. func (testcase *testcase) GetDiskPath(volumeName string) (string, error) {
  277. return "", errors.New("Not implemented")
  278. }