github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/pkg/mount/mounter_linux_test.go (about) 1 // +build linux 2 3 package mount 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "os" 9 "strings" 10 "testing" 11 ) 12 13 func TestMount(t *testing.T) { 14 if os.Getuid() != 0 { 15 t.Skip("not root tests would fail") 16 } 17 18 source, err := ioutil.TempDir("", "mount-test-source-") 19 if err != nil { 20 t.Fatal(err) 21 } 22 defer os.RemoveAll(source) 23 24 // Ensure we have a known start point by mounting tmpfs with given options 25 if err := Mount("tmpfs", source, "tmpfs", "private"); err != nil { 26 t.Fatal(err) 27 } 28 defer ensureUnmount(t, source) 29 validateMount(t, source, "", "") 30 if t.Failed() { 31 t.FailNow() 32 } 33 34 target, err := ioutil.TempDir("", "mount-test-target-") 35 if err != nil { 36 t.Fatal(err) 37 } 38 defer os.RemoveAll(target) 39 40 tests := []struct { 41 source string 42 ftype string 43 options string 44 expectedOpts string 45 expectedOptional string 46 }{ 47 // No options 48 {"tmpfs", "tmpfs", "", "", ""}, 49 // Default rw / ro test 50 {source, "", "bind", "", ""}, 51 {source, "", "bind,private", "", ""}, 52 {source, "", "bind,shared", "", "shared"}, 53 {source, "", "bind,slave", "", "master"}, 54 {source, "", "bind,unbindable", "", "unbindable"}, 55 // Read Write tests 56 {source, "", "bind,rw", "rw", ""}, 57 {source, "", "bind,rw,private", "rw", ""}, 58 {source, "", "bind,rw,shared", "rw", "shared"}, 59 {source, "", "bind,rw,slave", "rw", "master"}, 60 {source, "", "bind,rw,unbindable", "rw", "unbindable"}, 61 // Read Only tests 62 {source, "", "bind,ro", "ro", ""}, 63 {source, "", "bind,ro,private", "ro", ""}, 64 {source, "", "bind,ro,shared", "ro", "shared"}, 65 {source, "", "bind,ro,slave", "ro", "master"}, 66 {source, "", "bind,ro,unbindable", "ro", "unbindable"}, 67 } 68 69 for _, tc := range tests { 70 ftype, options := tc.ftype, tc.options 71 if tc.ftype == "" { 72 ftype = "none" 73 } 74 if tc.options == "" { 75 options = "none" 76 } 77 78 t.Run(fmt.Sprintf("%v-%v", ftype, options), func(t *testing.T) { 79 if strings.Contains(tc.options, "slave") { 80 // Slave requires a shared source 81 if err := MakeShared(source); err != nil { 82 t.Fatal(err) 83 } 84 defer func() { 85 if err := MakePrivate(source); err != nil { 86 t.Fatal(err) 87 } 88 }() 89 } 90 if err := Mount(tc.source, target, tc.ftype, tc.options); err != nil { 91 t.Fatal(err) 92 } 93 defer ensureUnmount(t, target) 94 validateMount(t, target, tc.expectedOpts, tc.expectedOptional) 95 }) 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 106 // validateMount checks that mnt has the given options 107 func validateMount(t *testing.T, mnt string, opts, optional string) { 108 info, err := GetMounts() 109 if err != nil { 110 t.Fatal(err) 111 } 112 113 wantedOpts := make(map[string]struct{}) 114 if opts != "" { 115 for _, opt := range strings.Split(opts, ",") { 116 wantedOpts[opt] = struct{}{} 117 } 118 } 119 120 wantedOptional := make(map[string]struct{}) 121 if optional != "" { 122 for _, opt := range strings.Split(optional, ",") { 123 wantedOptional[opt] = struct{}{} 124 } 125 } 126 127 mnts := make(map[int]*Info, len(info)) 128 for _, mi := range info { 129 mnts[mi.ID] = mi 130 } 131 132 for _, mi := range info { 133 if mi.Mountpoint != mnt { 134 continue 135 } 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 152 // Validate Opts 153 if mi.Opts != "" { 154 for _, opt := range strings.Split(mi.Opts, ",") { 155 opt = clean(opt) 156 if !has(wantedOpts, opt) && !has(pOpts, opt) { 157 t.Errorf("unexpected mount option %q expected %q", opt, opts) 158 } 159 delete(wantedOpts, opt) 160 } 161 } 162 for opt := range wantedOpts { 163 t.Errorf("missing mount option %q found %q", opt, mi.Opts) 164 } 165 166 // Validate Optional 167 if mi.Optional != "" { 168 for _, field := range strings.Split(mi.Optional, ",") { 169 field = clean(field) 170 if !has(wantedOptional, field) && !has(pOptional, field) { 171 t.Errorf("unexpected optional failed %q expected %q", field, optional) 172 } 173 delete(wantedOptional, field) 174 } 175 } 176 for field := range wantedOptional { 177 t.Errorf("missing optional field %q found %q", field, mi.Optional) 178 } 179 180 return 181 } 182 183 t.Errorf("failed to find mount %q", mnt) 184 } 185 186 // clean strips off any value param after the colon 187 func clean(v string) string { 188 return strings.SplitN(v, ":", 2)[0] 189 } 190 191 // has returns true if key is a member of m 192 func has(m map[string]struct{}, key string) bool { 193 _, ok := m[key] 194 return ok 195 }