testvolumespec.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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 testing
  14. import (
  15. "fmt"
  16. "sync"
  17. "time"
  18. "k8s.io/api/core/v1"
  19. storagev1 "k8s.io/api/storage/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/runtime"
  22. "k8s.io/apimachinery/pkg/types"
  23. "k8s.io/apimachinery/pkg/watch"
  24. "k8s.io/client-go/kubernetes/fake"
  25. core "k8s.io/client-go/testing"
  26. "k8s.io/klog"
  27. "k8s.io/kubernetes/pkg/volume"
  28. "k8s.io/kubernetes/pkg/volume/util"
  29. )
  30. const TestPluginName = "kubernetes.io/testPlugin"
  31. // GetTestVolumeSpec returns a test volume spec
  32. func GetTestVolumeSpec(volumeName string, diskName v1.UniqueVolumeName) *volume.Spec {
  33. return &volume.Spec{
  34. Volume: &v1.Volume{
  35. Name: volumeName,
  36. VolumeSource: v1.VolumeSource{
  37. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  38. PDName: string(diskName),
  39. FSType: "fake",
  40. ReadOnly: false,
  41. },
  42. },
  43. },
  44. PersistentVolume: &v1.PersistentVolume{
  45. Spec: v1.PersistentVolumeSpec{
  46. AccessModes: []v1.PersistentVolumeAccessMode{
  47. v1.ReadWriteOnce,
  48. },
  49. },
  50. },
  51. }
  52. }
  53. var extraPods *v1.PodList
  54. func CreateTestClient() *fake.Clientset {
  55. fakeClient := &fake.Clientset{}
  56. extraPods = &v1.PodList{}
  57. fakeClient.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
  58. obj := &v1.PodList{}
  59. podNamePrefix := "mypod"
  60. namespace := "mynamespace"
  61. for i := 0; i < 5; i++ {
  62. podName := fmt.Sprintf("%s-%d", podNamePrefix, i)
  63. pod := v1.Pod{
  64. Status: v1.PodStatus{
  65. Phase: v1.PodRunning,
  66. },
  67. ObjectMeta: metav1.ObjectMeta{
  68. Name: podName,
  69. UID: types.UID(podName),
  70. Namespace: namespace,
  71. Labels: map[string]string{
  72. "name": podName,
  73. },
  74. },
  75. Spec: v1.PodSpec{
  76. Containers: []v1.Container{
  77. {
  78. Name: "containerName",
  79. Image: "containerImage",
  80. VolumeMounts: []v1.VolumeMount{
  81. {
  82. Name: "volumeMountName",
  83. ReadOnly: false,
  84. MountPath: "/mnt",
  85. },
  86. },
  87. },
  88. },
  89. Volumes: []v1.Volume{
  90. {
  91. Name: "volumeName",
  92. VolumeSource: v1.VolumeSource{
  93. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  94. PDName: "pdName",
  95. FSType: "ext4",
  96. ReadOnly: false,
  97. },
  98. },
  99. },
  100. },
  101. NodeName: "mynode",
  102. },
  103. }
  104. obj.Items = append(obj.Items, pod)
  105. }
  106. obj.Items = append(obj.Items, extraPods.Items...)
  107. return true, obj, nil
  108. })
  109. fakeClient.AddReactor("create", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) {
  110. createAction := action.(core.CreateAction)
  111. pod := createAction.GetObject().(*v1.Pod)
  112. extraPods.Items = append(extraPods.Items, *pod)
  113. return true, createAction.GetObject(), nil
  114. })
  115. fakeClient.AddReactor("list", "csinodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
  116. obj := &storagev1.CSINodeList{}
  117. nodeNamePrefix := "mynode"
  118. for i := 0; i < 5; i++ {
  119. var nodeName string
  120. if i != 0 {
  121. nodeName = fmt.Sprintf("%s-%d", nodeNamePrefix, i)
  122. } else {
  123. // We want also the "mynode" node since all the testing pods live there
  124. nodeName = nodeNamePrefix
  125. }
  126. csiNode := storagev1.CSINode{
  127. ObjectMeta: metav1.ObjectMeta{
  128. Name: nodeName,
  129. },
  130. }
  131. obj.Items = append(obj.Items, csiNode)
  132. }
  133. return true, obj, nil
  134. })
  135. fakeClient.AddReactor("list", "nodes", func(action core.Action) (handled bool, ret runtime.Object, err error) {
  136. obj := &v1.NodeList{}
  137. nodeNamePrefix := "mynode"
  138. for i := 0; i < 5; i++ {
  139. var nodeName string
  140. if i != 0 {
  141. nodeName = fmt.Sprintf("%s-%d", nodeNamePrefix, i)
  142. } else {
  143. // We want also the "mynode" node since all the testing pods live there
  144. nodeName = nodeNamePrefix
  145. }
  146. node := v1.Node{
  147. ObjectMeta: metav1.ObjectMeta{
  148. Name: nodeName,
  149. Labels: map[string]string{
  150. "name": nodeName,
  151. },
  152. Annotations: map[string]string{
  153. util.ControllerManagedAttachAnnotation: "true",
  154. },
  155. },
  156. Status: v1.NodeStatus{
  157. VolumesAttached: []v1.AttachedVolume{
  158. {
  159. Name: TestPluginName + "/lostVolumeName",
  160. DevicePath: "fake/path",
  161. },
  162. },
  163. },
  164. }
  165. obj.Items = append(obj.Items, node)
  166. }
  167. return true, obj, nil
  168. })
  169. fakeWatch := watch.NewFake()
  170. fakeClient.AddWatchReactor("*", core.DefaultWatchReactor(fakeWatch, nil))
  171. return fakeClient
  172. }
  173. // NewPod returns a test pod object
  174. func NewPod(uid, name string) *v1.Pod {
  175. return &v1.Pod{
  176. ObjectMeta: metav1.ObjectMeta{
  177. UID: types.UID(uid),
  178. Name: name,
  179. Namespace: name,
  180. },
  181. }
  182. }
  183. // NewPod returns a test pod object
  184. func NewPodWithVolume(podName, volumeName, nodeName string) *v1.Pod {
  185. return &v1.Pod{
  186. ObjectMeta: metav1.ObjectMeta{
  187. UID: types.UID(podName),
  188. Name: podName,
  189. Namespace: "mynamespace",
  190. Labels: map[string]string{
  191. "name": podName,
  192. },
  193. },
  194. Spec: v1.PodSpec{
  195. Containers: []v1.Container{
  196. {
  197. Name: "containerName",
  198. Image: "containerImage",
  199. VolumeMounts: []v1.VolumeMount{
  200. {
  201. Name: "volumeMountName",
  202. ReadOnly: false,
  203. MountPath: "/mnt",
  204. },
  205. },
  206. },
  207. },
  208. Volumes: []v1.Volume{
  209. {
  210. Name: volumeName,
  211. VolumeSource: v1.VolumeSource{
  212. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  213. PDName: "pdName",
  214. FSType: "ext4",
  215. ReadOnly: false,
  216. },
  217. },
  218. },
  219. },
  220. NodeName: nodeName,
  221. },
  222. }
  223. }
  224. type TestPlugin struct {
  225. ErrorEncountered bool
  226. attachedVolumeMap map[string][]string
  227. detachedVolumeMap map[string][]string
  228. pluginLock *sync.RWMutex
  229. }
  230. func (plugin *TestPlugin) Init(host volume.VolumeHost) error {
  231. return nil
  232. }
  233. func (plugin *TestPlugin) GetPluginName() string {
  234. return TestPluginName
  235. }
  236. func (plugin *TestPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
  237. plugin.pluginLock.Lock()
  238. defer plugin.pluginLock.Unlock()
  239. if spec == nil {
  240. klog.Errorf("GetVolumeName called with nil volume spec")
  241. plugin.ErrorEncountered = true
  242. }
  243. return spec.Name(), nil
  244. }
  245. func (plugin *TestPlugin) CanSupport(spec *volume.Spec) bool {
  246. plugin.pluginLock.Lock()
  247. defer plugin.pluginLock.Unlock()
  248. if spec == nil {
  249. klog.Errorf("CanSupport called with nil volume spec")
  250. plugin.ErrorEncountered = true
  251. }
  252. return true
  253. }
  254. func (plugin *TestPlugin) RequiresRemount() bool {
  255. return false
  256. }
  257. func (plugin *TestPlugin) NewMounter(spec *volume.Spec, podRef *v1.Pod, opts volume.VolumeOptions) (volume.Mounter, error) {
  258. plugin.pluginLock.Lock()
  259. defer plugin.pluginLock.Unlock()
  260. if spec == nil {
  261. klog.Errorf("NewMounter called with nil volume spec")
  262. plugin.ErrorEncountered = true
  263. }
  264. return nil, nil
  265. }
  266. func (plugin *TestPlugin) NewUnmounter(name string, podUID types.UID) (volume.Unmounter, error) {
  267. return nil, nil
  268. }
  269. func (plugin *TestPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
  270. fakeVolume := &v1.Volume{
  271. Name: volumeName,
  272. VolumeSource: v1.VolumeSource{
  273. GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
  274. PDName: "pdName",
  275. FSType: "ext4",
  276. ReadOnly: false,
  277. },
  278. },
  279. }
  280. return volume.NewSpecFromVolume(fakeVolume), nil
  281. }
  282. func (plugin *TestPlugin) NewAttacher() (volume.Attacher, error) {
  283. attacher := testPluginAttacher{
  284. ErrorEncountered: &plugin.ErrorEncountered,
  285. attachedVolumeMap: plugin.attachedVolumeMap,
  286. pluginLock: plugin.pluginLock,
  287. }
  288. return &attacher, nil
  289. }
  290. func (plugin *TestPlugin) NewDeviceMounter() (volume.DeviceMounter, error) {
  291. return plugin.NewAttacher()
  292. }
  293. func (plugin *TestPlugin) NewDetacher() (volume.Detacher, error) {
  294. detacher := testPluginDetacher{
  295. detachedVolumeMap: plugin.detachedVolumeMap,
  296. pluginLock: plugin.pluginLock,
  297. }
  298. return &detacher, nil
  299. }
  300. func (plugin *TestPlugin) CanAttach(spec *volume.Spec) (bool, error) {
  301. return true, nil
  302. }
  303. func (plugin *TestPlugin) CanDeviceMount(spec *volume.Spec) (bool, error) {
  304. return true, nil
  305. }
  306. func (plugin *TestPlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) {
  307. return plugin.NewDetacher()
  308. }
  309. func (plugin *TestPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
  310. return []string{}, nil
  311. }
  312. func (plugin *TestPlugin) SupportsMountOption() bool {
  313. return false
  314. }
  315. func (plugin *TestPlugin) SupportsBulkVolumeVerification() bool {
  316. return false
  317. }
  318. func (plugin *TestPlugin) GetErrorEncountered() bool {
  319. plugin.pluginLock.RLock()
  320. defer plugin.pluginLock.RUnlock()
  321. return plugin.ErrorEncountered
  322. }
  323. func (plugin *TestPlugin) GetAttachedVolumes() map[string][]string {
  324. plugin.pluginLock.RLock()
  325. defer plugin.pluginLock.RUnlock()
  326. ret := make(map[string][]string)
  327. for nodeName, volumeList := range plugin.attachedVolumeMap {
  328. ret[nodeName] = make([]string, len(volumeList))
  329. copy(ret[nodeName], volumeList)
  330. }
  331. return ret
  332. }
  333. func (plugin *TestPlugin) GetDetachedVolumes() map[string][]string {
  334. plugin.pluginLock.RLock()
  335. defer plugin.pluginLock.RUnlock()
  336. ret := make(map[string][]string)
  337. for nodeName, volumeList := range plugin.detachedVolumeMap {
  338. ret[nodeName] = make([]string, len(volumeList))
  339. copy(ret[nodeName], volumeList)
  340. }
  341. return ret
  342. }
  343. func CreateTestPlugin() []volume.VolumePlugin {
  344. attachedVolumes := make(map[string][]string)
  345. detachedVolumes := make(map[string][]string)
  346. return []volume.VolumePlugin{&TestPlugin{
  347. ErrorEncountered: false,
  348. attachedVolumeMap: attachedVolumes,
  349. detachedVolumeMap: detachedVolumes,
  350. pluginLock: &sync.RWMutex{},
  351. }}
  352. }
  353. // Attacher
  354. type testPluginAttacher struct {
  355. ErrorEncountered *bool
  356. attachedVolumeMap map[string][]string
  357. pluginLock *sync.RWMutex
  358. }
  359. func (attacher *testPluginAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string, error) {
  360. attacher.pluginLock.Lock()
  361. defer attacher.pluginLock.Unlock()
  362. if spec == nil {
  363. *attacher.ErrorEncountered = true
  364. klog.Errorf("Attach called with nil volume spec")
  365. return "", fmt.Errorf("Attach called with nil volume spec")
  366. }
  367. attacher.attachedVolumeMap[string(nodeName)] = append(attacher.attachedVolumeMap[string(nodeName)], spec.Name())
  368. return spec.Name(), nil
  369. }
  370. func (attacher *testPluginAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) {
  371. return nil, nil
  372. }
  373. func (attacher *testPluginAttacher) WaitForAttach(spec *volume.Spec, devicePath string, pod *v1.Pod, timeout time.Duration) (string, error) {
  374. attacher.pluginLock.Lock()
  375. defer attacher.pluginLock.Unlock()
  376. if spec == nil {
  377. *attacher.ErrorEncountered = true
  378. klog.Errorf("WaitForAttach called with nil volume spec")
  379. return "", fmt.Errorf("WaitForAttach called with nil volume spec")
  380. }
  381. fakePath := fmt.Sprintf("%s/%s", devicePath, spec.Name())
  382. return fakePath, nil
  383. }
  384. func (attacher *testPluginAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) {
  385. attacher.pluginLock.Lock()
  386. defer attacher.pluginLock.Unlock()
  387. if spec == nil {
  388. *attacher.ErrorEncountered = true
  389. klog.Errorf("GetDeviceMountPath called with nil volume spec")
  390. return "", fmt.Errorf("GetDeviceMountPath called with nil volume spec")
  391. }
  392. return "", nil
  393. }
  394. func (attacher *testPluginAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {
  395. attacher.pluginLock.Lock()
  396. defer attacher.pluginLock.Unlock()
  397. if spec == nil {
  398. *attacher.ErrorEncountered = true
  399. klog.Errorf("MountDevice called with nil volume spec")
  400. return fmt.Errorf("MountDevice called with nil volume spec")
  401. }
  402. return nil
  403. }
  404. // Detacher
  405. type testPluginDetacher struct {
  406. detachedVolumeMap map[string][]string
  407. pluginLock *sync.RWMutex
  408. }
  409. func (detacher *testPluginDetacher) Detach(volumeName string, nodeName types.NodeName) error {
  410. detacher.pluginLock.Lock()
  411. defer detacher.pluginLock.Unlock()
  412. detacher.detachedVolumeMap[string(nodeName)] = append(detacher.detachedVolumeMap[string(nodeName)], volumeName)
  413. return nil
  414. }
  415. func (detacher *testPluginDetacher) UnmountDevice(deviceMountPath string) error {
  416. return nil
  417. }