github.com/cdoern/storage@v1.12.13/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 expectedVFS string 47 }{ 48 // No options 49 {"tmpfs", "tmpfs", "", "", "", ""}, 50 // Default rw / ro test 51 {source, "", "bind", "", "", ""}, 52 {source, "", "bind,private", "", "", ""}, 53 {source, "", "bind,shared", "", "shared", ""}, 54 {source, "", "bind,slave", "", "master", ""}, 55 {source, "", "bind,unbindable", "", "unbindable", ""}, 56 // Read Write tests 57 {source, "", "bind,rw", "rw", "", ""}, 58 {source, "", "bind,rw,private", "rw", "", ""}, 59 {source, "", "bind,rw,shared", "rw", "shared", ""}, 60 {source, "", "bind,rw,slave", "rw", "master", ""}, 61 {source, "", "bind,rw,unbindable", "rw", "unbindable", ""}, 62 // Read Only tests 63 {source, "", "bind,ro", "ro", "", ""}, 64 {source, "", "bind,ro,private", "ro", "", ""}, 65 {source, "", "bind,ro,shared", "ro", "shared", ""}, 66 {source, "", "bind,ro,slave", "ro", "master", ""}, 67 {source, "", "bind,ro,unbindable", "ro", "unbindable", ""}, 68 // Remount tests to change per filesystem options 69 {"", "", "remount,size=128k", "rw", "", "rw,size=128k"}, 70 {"", "", "remount,ro,size=128k", "ro", "", "ro,size=128k"}, 71 } 72 73 for _, tc := range tests { 74 ftype, options := tc.ftype, tc.options 75 if tc.ftype == "" { 76 ftype = "none" 77 } 78 if tc.options == "" { 79 options = "none" 80 } 81 82 t.Run(fmt.Sprintf("%v-%v", ftype, options), func(t *testing.T) { 83 if strings.Contains(tc.options, "slave") { 84 // Slave requires a shared source 85 if err := MakeShared(source); err != nil { 86 t.Fatal(err) 87 } 88 defer func() { 89 if err := MakePrivate(source); err != nil { 90 t.Fatal(err) 91 } 92 }() 93 } 94 if strings.Contains(tc.options, "remount") { 95 // create a new mount to remount first 96 if err := Mount("tmpfs", target, "tmpfs", ""); err != nil { 97 t.Fatal(err) 98 } 99 } 100 if err := Mount(tc.source, target, tc.ftype, tc.options); err != nil { 101 t.Fatal(err) 102 } 103 defer ensureUnmount(t, target) 104 validateMount(t, target, tc.expectedOpts, tc.expectedOptional, tc.expectedVFS) 105 }) 106 } 107 } 108 109 // ensureUnmount umounts mnt checking for errors 110 func ensureUnmount(t *testing.T, mnt string) { 111 if err := Unmount(mnt); err != nil { 112 t.Error(err) 113 } 114 } 115 116 // validateMount checks that mnt has the given options 117 func validateMount(t *testing.T, mnt string, opts, optional, vfs string) { 118 info, err := GetMounts() 119 if err != nil { 120 t.Fatal(err) 121 } 122 123 wantedOpts := make(map[string]struct{}) 124 if opts != "" { 125 for _, opt := range strings.Split(opts, ",") { 126 wantedOpts[opt] = struct{}{} 127 } 128 } 129 130 wantedOptional := make(map[string]struct{}) 131 if optional != "" { 132 for _, opt := range strings.Split(optional, ",") { 133 wantedOptional[opt] = struct{}{} 134 } 135 } 136 137 wantedVFS := make(map[string]struct{}) 138 if vfs != "" { 139 for _, opt := range strings.Split(vfs, ",") { 140 wantedVFS[opt] = struct{}{} 141 } 142 } 143 volunteeredVFS := map[string]struct{}{"seclabel": {}} 144 volunteeredOPT := map[string]struct{}{"relatime": {}} 145 146 mnts := make(map[int]*Info, len(info)) 147 for _, mi := range info { 148 mnts[mi.ID] = mi 149 } 150 151 for _, mi := range info { 152 if mi.Mountpoint != mnt { 153 continue 154 } 155 156 // Use parent info as the defaults 157 p := mnts[mi.Parent] 158 pOpts := make(map[string]struct{}) 159 if p.Opts != "" { 160 for _, opt := range strings.Split(p.Opts, ",") { 161 pOpts[clean(opt)] = struct{}{} 162 } 163 } 164 pOptional := make(map[string]struct{}) 165 if p.Optional != "" { 166 for _, field := range strings.Split(p.Optional, ",") { 167 pOptional[clean(field)] = struct{}{} 168 } 169 } 170 171 // Validate Opts 172 if mi.Opts != "" { 173 for _, opt := range strings.Split(mi.Opts, ",") { 174 opt = clean(opt) 175 if !has(volunteeredOPT, opt) && !has(wantedOpts, opt) && !has(pOpts, opt) { 176 t.Errorf("unexpected mount option %q expected %q", opt, opts) 177 } 178 delete(wantedOpts, opt) 179 } 180 } 181 for opt := range wantedOpts { 182 t.Errorf("missing mount option %q found %q", opt, mi.Opts) 183 } 184 185 // Validate Optional 186 if mi.Optional != "" { 187 for _, field := range strings.Split(mi.Optional, ",") { 188 field = clean(field) 189 if !has(wantedOptional, field) && !has(pOptional, field) { 190 t.Errorf("unexpected optional failed %q expected %q", field, optional) 191 } 192 delete(wantedOptional, field) 193 } 194 } 195 for field := range wantedOptional { 196 t.Errorf("missing optional field %q found %q", field, mi.Optional) 197 } 198 199 // Validate VFS if set 200 if vfs != "" { 201 if mi.VfsOpts != "" { 202 for _, opt := range strings.Split(mi.VfsOpts, ",") { 203 opt = clean(opt) 204 if !has(wantedVFS, opt) && !has(volunteeredVFS, opt) { 205 t.Errorf("unexpected mount option %q expected %q", opt, vfs) 206 } 207 delete(wantedVFS, opt) 208 } 209 } 210 for opt := range wantedVFS { 211 t.Errorf("missing mount option %q found %q", opt, mi.VfsOpts) 212 } 213 } 214 215 return 216 } 217 218 t.Errorf("failed to find mount %q", mnt) 219 } 220 221 // clean strips off any value param after the colon 222 func clean(v string) string { 223 return strings.SplitN(v, ":", 2)[0] 224 } 225 226 // has returns true if key is a member of m 227 func has(m map[string]struct{}, key string) bool { 228 _, ok := m[key] 229 return ok 230 }