mounter_linux_test.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // +build linux
  2. package mount // import "github.com/docker/docker/pkg/mount"
  3. import (
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "strings"
  8. "testing"
  9. )
  10. func TestMount(t *testing.T) {
  11. if os.Getuid() != 0 {
  12. t.Skip("root required")
  13. }
  14. source, err := ioutil.TempDir("", "mount-test-source-")
  15. if err != nil {
  16. t.Fatal(err)
  17. }
  18. defer os.RemoveAll(source)
  19. // Ensure we have a known start point by mounting tmpfs with given options
  20. if err := Mount("tmpfs", source, "tmpfs", "private"); err != nil {
  21. t.Fatal(err)
  22. }
  23. defer ensureUnmount(t, source)
  24. validateMount(t, source, "", "", "")
  25. if t.Failed() {
  26. t.FailNow()
  27. }
  28. target, err := ioutil.TempDir("", "mount-test-target-")
  29. if err != nil {
  30. t.Fatal(err)
  31. }
  32. defer os.RemoveAll(target)
  33. tests := []struct {
  34. source string
  35. ftype string
  36. options string
  37. expectedOpts string
  38. expectedOptional string
  39. expectedVFS string
  40. }{
  41. // No options
  42. {"tmpfs", "tmpfs", "", "", "", ""},
  43. // Default rw / ro test
  44. {source, "", "bind", "", "", ""},
  45. {source, "", "bind,private", "", "", ""},
  46. {source, "", "bind,shared", "", "shared", ""},
  47. {source, "", "bind,slave", "", "master", ""},
  48. {source, "", "bind,unbindable", "", "unbindable", ""},
  49. // Read Write tests
  50. {source, "", "bind,rw", "rw", "", ""},
  51. {source, "", "bind,rw,private", "rw", "", ""},
  52. {source, "", "bind,rw,shared", "rw", "shared", ""},
  53. {source, "", "bind,rw,slave", "rw", "master", ""},
  54. {source, "", "bind,rw,unbindable", "rw", "unbindable", ""},
  55. // Read Only tests
  56. {source, "", "bind,ro", "ro", "", ""},
  57. {source, "", "bind,ro,private", "ro", "", ""},
  58. {source, "", "bind,ro,shared", "ro", "shared", ""},
  59. {source, "", "bind,ro,slave", "ro", "master", ""},
  60. {source, "", "bind,ro,unbindable", "ro", "unbindable", ""},
  61. // Remount tests to change per filesystem options
  62. {"", "", "remount,size=128k", "rw", "", "rw,size=128k"},
  63. {"", "", "remount,ro,size=128k", "ro", "", "ro,size=128k"},
  64. }
  65. for _, tc := range tests {
  66. ftype, options := tc.ftype, tc.options
  67. if tc.ftype == "" {
  68. ftype = "none"
  69. }
  70. if tc.options == "" {
  71. options = "none"
  72. }
  73. t.Run(fmt.Sprintf("%v-%v", ftype, options), func(t *testing.T) {
  74. if strings.Contains(tc.options, "slave") {
  75. // Slave requires a shared source
  76. if err := MakeShared(source); err != nil {
  77. t.Fatal(err)
  78. }
  79. defer func() {
  80. if err := MakePrivate(source); err != nil {
  81. t.Fatal(err)
  82. }
  83. }()
  84. }
  85. if strings.Contains(tc.options, "remount") {
  86. // create a new mount to remount first
  87. if err := Mount("tmpfs", target, "tmpfs", ""); err != nil {
  88. t.Fatal(err)
  89. }
  90. }
  91. if err := Mount(tc.source, target, tc.ftype, tc.options); err != nil {
  92. t.Fatal(err)
  93. }
  94. defer ensureUnmount(t, target)
  95. validateMount(t, target, tc.expectedOpts, tc.expectedOptional, tc.expectedVFS)
  96. })
  97. }
  98. }
  99. // ensureUnmount umounts mnt checking for errors
  100. func ensureUnmount(t *testing.T, mnt string) {
  101. if err := Unmount(mnt); err != nil {
  102. t.Error(err)
  103. }
  104. }
  105. // validateMount checks that mnt has the given options
  106. func validateMount(t *testing.T, mnt string, opts, optional, vfs string) {
  107. info, err := GetMounts(nil)
  108. if err != nil {
  109. t.Fatal(err)
  110. }
  111. wantedOpts := make(map[string]struct{})
  112. if opts != "" {
  113. for _, opt := range strings.Split(opts, ",") {
  114. wantedOpts[opt] = struct{}{}
  115. }
  116. }
  117. wantedOptional := make(map[string]struct{})
  118. if optional != "" {
  119. for _, opt := range strings.Split(optional, ",") {
  120. wantedOptional[opt] = struct{}{}
  121. }
  122. }
  123. wantedVFS := make(map[string]struct{})
  124. if vfs != "" {
  125. for _, opt := range strings.Split(vfs, ",") {
  126. wantedVFS[opt] = struct{}{}
  127. }
  128. }
  129. mnts := make(map[int]*Info, len(info))
  130. for _, mi := range info {
  131. mnts[mi.ID] = mi
  132. }
  133. for _, mi := range info {
  134. if mi.Mountpoint != mnt {
  135. continue
  136. }
  137. // Use parent info as the defaults
  138. p := mnts[mi.Parent]
  139. pOpts := make(map[string]struct{})
  140. if p.Opts != "" {
  141. for _, opt := range strings.Split(p.Opts, ",") {
  142. pOpts[clean(opt)] = struct{}{}
  143. }
  144. }
  145. pOptional := make(map[string]struct{})
  146. if p.Optional != "" {
  147. for _, field := range strings.Split(p.Optional, ",") {
  148. pOptional[clean(field)] = struct{}{}
  149. }
  150. }
  151. // Validate Opts
  152. if mi.Opts != "" {
  153. for _, opt := range strings.Split(mi.Opts, ",") {
  154. opt = clean(opt)
  155. if !has(wantedOpts, opt) && !has(pOpts, opt) {
  156. t.Errorf("unexpected mount option %q, expected %q", opt, opts)
  157. }
  158. delete(wantedOpts, opt)
  159. }
  160. }
  161. for opt := range wantedOpts {
  162. t.Errorf("missing mount option %q, found %q", opt, mi.Opts)
  163. }
  164. // Validate Optional
  165. if mi.Optional != "" {
  166. for _, field := range strings.Split(mi.Optional, ",") {
  167. field = clean(field)
  168. if !has(wantedOptional, field) && !has(pOptional, field) {
  169. t.Errorf("unexpected optional field %q, expected %q", field, optional)
  170. }
  171. delete(wantedOptional, field)
  172. }
  173. }
  174. for field := range wantedOptional {
  175. t.Errorf("missing optional field %q, found %q", field, mi.Optional)
  176. }
  177. // Validate VFS if set
  178. if vfs != "" {
  179. if mi.VfsOpts != "" {
  180. for _, opt := range strings.Split(mi.VfsOpts, ",") {
  181. opt = clean(opt)
  182. if !has(wantedVFS, opt) && opt != "seclabel" { // can be added by selinux
  183. t.Errorf("unexpected vfs option %q, expected %q", opt, vfs)
  184. }
  185. delete(wantedVFS, opt)
  186. }
  187. }
  188. for opt := range wantedVFS {
  189. t.Errorf("missing vfs option %q, found %q", opt, mi.VfsOpts)
  190. }
  191. }
  192. return
  193. }
  194. t.Errorf("failed to find mount %q", mnt)
  195. }
  196. // clean strips off any value param after the colon
  197. func clean(v string) string {
  198. return strings.SplitN(v, ":", 2)[0]
  199. }
  200. // has returns true if key is a member of m
  201. func has(m map[string]struct{}, key string) bool {
  202. _, ok := m[key]
  203. return ok
  204. }