testvolumespec.go 12 KB

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