volumes_unix_test.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. // +build !windows
  2. package daemon
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "reflect"
  7. "testing"
  8. containertypes "github.com/docker/docker/api/types/container"
  9. mounttypes "github.com/docker/docker/api/types/mount"
  10. "github.com/docker/docker/container"
  11. "github.com/docker/docker/volume"
  12. )
  13. func TestBackportMountSpec(t *testing.T) {
  14. d := Daemon{containers: container.NewMemoryStore()}
  15. c := &container.Container{
  16. CommonContainer: container.CommonContainer{
  17. State: &container.State{},
  18. MountPoints: map[string]*volume.MountPoint{
  19. "/apple": {Destination: "/apple", Source: "/var/lib/docker/volumes/12345678", Name: "12345678", RW: true, CopyData: true}, // anonymous volume
  20. "/banana": {Destination: "/banana", Source: "/var/lib/docker/volumes/data", Name: "data", RW: true, CopyData: true}, // named volume
  21. "/cherry": {Destination: "/cherry", Source: "/var/lib/docker/volumes/data", Name: "data", CopyData: true}, // RO named volume
  22. "/dates": {Destination: "/dates", Source: "/var/lib/docker/volumes/data", Name: "data"}, // named volume nocopy
  23. "/elderberry": {Destination: "/elderberry", Source: "/var/lib/docker/volumes/data", Name: "data"}, // masks anon vol
  24. "/fig": {Destination: "/fig", Source: "/data", RW: true}, // RW bind
  25. "/guava": {Destination: "/guava", Source: "/data", RW: false, Propagation: "shared"}, // RO bind + propagation
  26. "/kumquat": {Destination: "/kumquat", Name: "data", RW: false, CopyData: true}, // volumes-from
  27. // partially configured mountpoint due to #32613
  28. // specifically, `mp.Spec.Source` is not set
  29. "/honeydew": {
  30. Type: mounttypes.TypeVolume,
  31. Destination: "/honeydew",
  32. Name: "data",
  33. Source: "/var/lib/docker/volumes/data",
  34. Spec: mounttypes.Mount{Type: mounttypes.TypeVolume, Target: "/honeydew", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  35. },
  36. // from hostconfig.Mounts
  37. "/jambolan": {
  38. Type: mounttypes.TypeVolume,
  39. Destination: "/jambolan",
  40. Source: "/var/lib/docker/volumes/data",
  41. RW: true,
  42. Name: "data",
  43. Spec: mounttypes.Mount{Type: mounttypes.TypeVolume, Target: "/jambolan", Source: "data"},
  44. },
  45. },
  46. HostConfig: &containertypes.HostConfig{
  47. Binds: []string{
  48. "data:/banana",
  49. "data:/cherry:ro",
  50. "data:/dates:ro,nocopy",
  51. "data:/elderberry:ro,nocopy",
  52. "/data:/fig",
  53. "/data:/guava:ro,shared",
  54. "data:/honeydew:nocopy",
  55. },
  56. VolumesFrom: []string{"1:ro"},
  57. Mounts: []mounttypes.Mount{
  58. {Type: mounttypes.TypeVolume, Target: "/jambolan"},
  59. },
  60. },
  61. Config: &containertypes.Config{Volumes: map[string]struct{}{
  62. "/apple": {},
  63. "/elderberry": {},
  64. }},
  65. }}
  66. d.containers.Add("1", &container.Container{
  67. CommonContainer: container.CommonContainer{
  68. State: &container.State{},
  69. ID: "1",
  70. MountPoints: map[string]*volume.MountPoint{
  71. "/kumquat": {Destination: "/kumquat", Name: "data", RW: false, CopyData: true},
  72. },
  73. HostConfig: &containertypes.HostConfig{
  74. Binds: []string{
  75. "data:/kumquat:ro",
  76. },
  77. },
  78. },
  79. })
  80. type expected struct {
  81. mp *volume.MountPoint
  82. comment string
  83. }
  84. pretty := func(mp *volume.MountPoint) string {
  85. b, err := json.MarshalIndent(mp, "\t", " ")
  86. if err != nil {
  87. return fmt.Sprintf("%#v", mp)
  88. }
  89. return string(b)
  90. }
  91. for _, x := range []expected{
  92. {
  93. mp: &volume.MountPoint{
  94. Type: mounttypes.TypeVolume,
  95. Destination: "/apple",
  96. RW: true,
  97. Name: "12345678",
  98. Source: "/var/lib/docker/volumes/12345678",
  99. CopyData: true,
  100. Spec: mounttypes.Mount{
  101. Type: mounttypes.TypeVolume,
  102. Source: "",
  103. Target: "/apple",
  104. },
  105. },
  106. comment: "anonymous volume",
  107. },
  108. {
  109. mp: &volume.MountPoint{
  110. Type: mounttypes.TypeVolume,
  111. Destination: "/banana",
  112. RW: true,
  113. Name: "data",
  114. Source: "/var/lib/docker/volumes/data",
  115. CopyData: true,
  116. Spec: mounttypes.Mount{
  117. Type: mounttypes.TypeVolume,
  118. Source: "data",
  119. Target: "/banana",
  120. },
  121. },
  122. comment: "named volume",
  123. },
  124. {
  125. mp: &volume.MountPoint{
  126. Type: mounttypes.TypeVolume,
  127. Destination: "/cherry",
  128. Name: "data",
  129. Source: "/var/lib/docker/volumes/data",
  130. CopyData: true,
  131. Spec: mounttypes.Mount{
  132. Type: mounttypes.TypeVolume,
  133. Source: "data",
  134. Target: "/cherry",
  135. ReadOnly: true,
  136. },
  137. },
  138. comment: "read-only named volume",
  139. },
  140. {
  141. mp: &volume.MountPoint{
  142. Type: mounttypes.TypeVolume,
  143. Destination: "/dates",
  144. Name: "data",
  145. Source: "/var/lib/docker/volumes/data",
  146. Spec: mounttypes.Mount{
  147. Type: mounttypes.TypeVolume,
  148. Source: "data",
  149. Target: "/dates",
  150. ReadOnly: true,
  151. VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true},
  152. },
  153. },
  154. comment: "named volume with nocopy",
  155. },
  156. {
  157. mp: &volume.MountPoint{
  158. Type: mounttypes.TypeVolume,
  159. Destination: "/elderberry",
  160. Name: "data",
  161. Source: "/var/lib/docker/volumes/data",
  162. Spec: mounttypes.Mount{
  163. Type: mounttypes.TypeVolume,
  164. Source: "data",
  165. Target: "/elderberry",
  166. ReadOnly: true,
  167. VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true},
  168. },
  169. },
  170. comment: "masks an anonymous volume",
  171. },
  172. {
  173. mp: &volume.MountPoint{
  174. Type: mounttypes.TypeBind,
  175. Destination: "/fig",
  176. Source: "/data",
  177. RW: true,
  178. Spec: mounttypes.Mount{
  179. Type: mounttypes.TypeBind,
  180. Source: "/data",
  181. Target: "/fig",
  182. },
  183. },
  184. comment: "bind mount with read/write",
  185. },
  186. {
  187. mp: &volume.MountPoint{
  188. Type: mounttypes.TypeBind,
  189. Destination: "/guava",
  190. Source: "/data",
  191. RW: false,
  192. Propagation: "shared",
  193. Spec: mounttypes.Mount{
  194. Type: mounttypes.TypeBind,
  195. Source: "/data",
  196. Target: "/guava",
  197. ReadOnly: true,
  198. BindOptions: &mounttypes.BindOptions{Propagation: "shared"},
  199. },
  200. },
  201. comment: "bind mount with read/write + shared propgation",
  202. },
  203. {
  204. mp: &volume.MountPoint{
  205. Type: mounttypes.TypeVolume,
  206. Destination: "/honeydew",
  207. Source: "/var/lib/docker/volumes/data",
  208. RW: true,
  209. Propagation: "shared",
  210. Spec: mounttypes.Mount{
  211. Type: mounttypes.TypeVolume,
  212. Source: "data",
  213. Target: "/honeydew",
  214. VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true},
  215. },
  216. },
  217. comment: "partially configured named volume caused by #32613",
  218. },
  219. {
  220. mp: &(*c.MountPoints["/jambolan"]), // copy the mountpoint, expect no changes
  221. comment: "volume defined in mounts API",
  222. },
  223. {
  224. mp: &volume.MountPoint{
  225. Type: mounttypes.TypeVolume,
  226. Destination: "/kumquat",
  227. Source: "/var/lib/docker/volumes/data",
  228. RW: false,
  229. Name: "data",
  230. Spec: mounttypes.Mount{
  231. Type: mounttypes.TypeVolume,
  232. Source: "data",
  233. Target: "/kumquat",
  234. ReadOnly: true,
  235. },
  236. },
  237. comment: "partially configured named volume caused by #32613",
  238. },
  239. } {
  240. mp := c.MountPoints[x.mp.Destination]
  241. d.backportMountSpec(c)
  242. if !reflect.DeepEqual(mp.Spec, x.mp.Spec) {
  243. t.Fatalf("%s\nexpected:\n\t%s\n\ngot:\n\t%s", x.comment, pretty(x.mp), pretty(mp))
  244. }
  245. }
  246. }