1// +build !windows 2 3/* 4Copyright 2019 The Kubernetes Authors. 5 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17*/ 18 19package mount 20 21import ( 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "reflect" 26 "testing" 27) 28 29func writeFile(content string) (string, string, error) { 30 tempDir, err := ioutil.TempDir("", "mounter_shared_test") 31 if err != nil { 32 return "", "", err 33 } 34 filename := filepath.Join(tempDir, "mountinfo") 35 err = ioutil.WriteFile(filename, []byte(content), 0600) 36 if err != nil { 37 os.RemoveAll(tempDir) 38 return "", "", err 39 } 40 return tempDir, filename, nil 41} 42 43func TestParseMountInfo(t *testing.T) { 44 info := 45 `62 0 253:0 / / rw,relatime shared:1 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered 4678 62 0:41 / /tmp rw,nosuid,nodev shared:30 - tmpfs tmpfs rw,seclabel 4780 62 0:42 / /var/lib/nfs/rpc_pipefs rw,relatime shared:31 - rpc_pipefs sunrpc rw 4882 62 0:43 / /var/lib/foo rw,relatime shared:32 - tmpfs tmpfs rw 4983 63 0:44 / /var/lib/bar rw,relatime - tmpfs tmpfs rw 50227 62 253:0 /var/lib/docker/devicemapper /var/lib/docker/devicemapper rw,relatime - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered 51224 62 253:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered 5276 17 8:1 / /mnt/stateful_partition rw,nosuid,nodev,noexec,relatime - ext4 /dev/sda1 rw,commit=30,data=ordered 5380 17 8:1 /var /var rw,nosuid,nodev,noexec,relatime shared:30 - ext4 /dev/sda1 rw,commit=30,data=ordered 54189 80 8:1 /var/lib/kubelet /var/lib/kubelet rw,relatime shared:30 - ext4 /dev/sda1 rw,commit=30,data=ordered 55818 77 8:40 / /var/lib/kubelet/pods/c25464af-e52e-11e7-ab4d-42010a800002/volumes/kubernetes.io~gce-pd/vol1 rw,relatime shared:290 - ext4 /dev/sdc rw,data=ordered 56819 78 8:48 / /var/lib/kubelet/pods/c25464af-e52e-11e7-ab4d-42010a800002/volumes/kubernetes.io~gce-pd/vol1 rw,relatime shared:290 - ext4 /dev/sdd rw,data=ordered 57900 100 8:48 /dir1 /var/lib/kubelet/pods/c25464af-e52e-11e7-ab4d-42010a800002/volume-subpaths/vol1/subpath1/0 rw,relatime shared:290 - ext4 /dev/sdd rw,data=ordered 58901 101 8:1 /dir1 /var/lib/kubelet/pods/c25464af-e52e-11e7-ab4d-42010a800002/volume-subpaths/vol1/subpath1/1 rw,relatime shared:290 - ext4 /dev/sdd rw,data=ordered 59902 102 8:1 /var/lib/kubelet/pods/d4076f24-e53a-11e7-ba15-42010a800002/volumes/kubernetes.io~empty-dir/vol1/dir1 /var/lib/kubelet/pods/d4076f24-e53a-11e7-ba15-42010a800002/volume-subpaths/vol1/subpath1/0 rw,relatime shared:30 - ext4 /dev/sda1 rw,commit=30,data=ordered 60903 103 8:1 /var/lib/kubelet/pods/d4076f24-e53a-11e7-ba15-42010a800002/volumes/kubernetes.io~empty-dir/vol2/dir1 /var/lib/kubelet/pods/d4076f24-e53a-11e7-ba15-42010a800002/volume-subpaths/vol1/subpath1/1 rw,relatime shared:30 - ext4 /dev/sda1 rw,commit=30,data=ordered 61178 25 253:0 /etc/bar /var/lib/kubelet/pods/12345/volume-subpaths/vol1/subpath1/0 rw,relatime shared:1 - ext4 /dev/sdb2 rw,errors=remount-ro,data=ordered 62698 186 0:41 /tmp1/dir1 /var/lib/kubelet/pods/41135147-e697-11e7-9342-42010a800002/volume-subpaths/vol1/subpath1/0 rw shared:26 - tmpfs tmpfs rw 63918 77 8:50 / /var/lib/kubelet/pods/2345/volumes/kubernetes.io~gce-pd/vol1 rw,relatime shared:290 - ext4 /dev/sdc rw,data=ordered 64919 78 8:58 / /var/lib/kubelet/pods/2345/volumes/kubernetes.io~gce-pd/vol1 rw,relatime shared:290 - ext4 /dev/sdd rw,data=ordered 65920 100 8:50 /dir1 /var/lib/kubelet/pods/2345/volume-subpaths/vol1/subpath1/0 rw,relatime shared:290 - ext4 /dev/sdc rw,data=ordered 66150 23 1:58 / /media/nfs_vol rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223 67151 24 1:58 / /media/nfs_bindmount rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs/foo rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223 68134 23 0:58 / /var/lib/kubelet/pods/43219158-e5e1-11e7-a392-0e858b8eaf40/volumes/kubernetes.io~nfs/nfs1 rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223 69187 23 0:58 / /var/lib/kubelet/pods/1fc5ea21-eff4-11e7-ac80-0e858b8eaf40/volumes/kubernetes.io~nfs/nfs2 rw,relatime shared:96 - nfs4 172.18.4.223:/srv/nfs2 rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223 70188 24 0:58 / /var/lib/kubelet/pods/43219158-e5e1-11e7-a392-0e858b8eaf40/volume-subpaths/nfs1/subpath1/0 rw,relatime shared:89 - nfs4 172.18.4.223:/srv/nfs/foo rw,vers=4.0,rsize=524288,wsize=524288,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=172.18.4.223,local_lock=none,addr=172.18.4.223 71347 60 0:71 / /var/lib/kubelet/pods/13195d46-f9fa-11e7-bbf1-5254007a695a/volumes/kubernetes.io~nfs/vol2 rw,relatime shared:170 - nfs 172.17.0.3:/exports/2 rw,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=172.17.0.3,mountvers=3,mountport=20048,mountproto=udp,local_lock=none,addr=172.17.0.3 72222 24 253:0 /tmp/src /mnt/dst rw,relatime shared:1 - ext4 /dev/mapper/vagrant--vg-root rw,errors=remount-ro,data=ordered 7328 18 0:24 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755 7429 28 0:25 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd 7531 28 0:27 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,cpuset 7632 28 0:28 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,cpu,cpuacct 7733 28 0:29 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,freezer 7834 28 0:30 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,net_cls,net_prio 7935 28 0:31 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,pids 8036 28 0:32 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,devices 8137 28 0:33 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,hugetlb 8238 28 0:34 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,blkio 8339 28 0:35 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,memory 8440 28 0:36 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,perf_event 85` 86 tempDir, filename, err := writeFile(info) 87 if err != nil { 88 t.Fatalf("cannot create temporary file: %v", err) 89 } 90 defer os.RemoveAll(tempDir) 91 92 tests := []struct { 93 name string 94 id int 95 expectedInfo MountInfo 96 }{ 97 { 98 "simple bind mount", 99 189, 100 MountInfo{ 101 ID: 189, 102 ParentID: 80, 103 Major: 8, 104 Minor: 1, 105 Root: "/var/lib/kubelet", 106 Source: "/dev/sda1", 107 MountPoint: "/var/lib/kubelet", 108 OptionalFields: []string{"shared:30"}, 109 FsType: "ext4", 110 MountOptions: []string{"rw", "relatime"}, 111 SuperOptions: []string{"rw", "commit=30", "data=ordered"}, 112 }, 113 }, 114 { 115 "bind mount a directory", 116 222, 117 MountInfo{ 118 ID: 222, 119 ParentID: 24, 120 Major: 253, 121 Minor: 0, 122 Root: "/tmp/src", 123 Source: "/dev/mapper/vagrant--vg-root", 124 MountPoint: "/mnt/dst", 125 OptionalFields: []string{"shared:1"}, 126 FsType: "ext4", 127 MountOptions: []string{"rw", "relatime"}, 128 SuperOptions: []string{"rw", "errors=remount-ro", "data=ordered"}, 129 }, 130 }, 131 { 132 "more than one optional fields", 133 224, 134 MountInfo{ 135 ID: 224, 136 ParentID: 62, 137 Major: 253, 138 Minor: 0, 139 Root: "/var/lib/docker/devicemapper/test/shared", 140 Source: "/dev/mapper/ssd-root", 141 MountPoint: "/var/lib/docker/devicemapper/test/shared", 142 OptionalFields: []string{"master:1", "shared:44"}, 143 FsType: "ext4", 144 MountOptions: []string{"rw", "relatime"}, 145 SuperOptions: []string{"rw", "seclabel", "data=ordered"}, 146 }, 147 }, 148 { 149 "cgroup-mountpoint", 150 28, 151 MountInfo{ 152 ID: 28, 153 ParentID: 18, 154 Major: 0, 155 Minor: 24, 156 Root: "/", 157 Source: "tmpfs", 158 MountPoint: "/sys/fs/cgroup", 159 OptionalFields: []string{"shared:9"}, 160 FsType: "tmpfs", 161 MountOptions: []string{"ro", "nosuid", "nodev", "noexec"}, 162 SuperOptions: []string{"ro", "mode=755"}, 163 }, 164 }, 165 { 166 "cgroup-subsystem-systemd-mountpoint", 167 29, 168 MountInfo{ 169 ID: 29, 170 ParentID: 28, 171 Major: 0, 172 Minor: 25, 173 Root: "/", 174 Source: "cgroup", 175 MountPoint: "/sys/fs/cgroup/systemd", 176 OptionalFields: []string{"shared:10"}, 177 FsType: "cgroup", 178 MountOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"}, 179 SuperOptions: []string{"rw", "xattr", "release_agent=/lib/systemd/systemd-cgroups-agent", "name=systemd"}, 180 }, 181 }, 182 { 183 "cgroup-subsystem-cpuset-mountpoint", 184 31, 185 MountInfo{ 186 ID: 31, 187 ParentID: 28, 188 Major: 0, 189 Minor: 27, 190 Root: "/", 191 Source: "cgroup", 192 MountPoint: "/sys/fs/cgroup/cpuset", 193 OptionalFields: []string{"shared:13"}, 194 FsType: "cgroup", 195 MountOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"}, 196 SuperOptions: []string{"rw", "cpuset"}, 197 }, 198 }, 199 } 200 201 infos, err := ParseMountInfo(filename) 202 if err != nil { 203 t.Fatalf("Cannot parse %s: %s", filename, err) 204 } 205 206 for _, test := range tests { 207 found := false 208 for _, info := range infos { 209 if info.ID == test.id { 210 found = true 211 if !reflect.DeepEqual(info, test.expectedInfo) { 212 t.Errorf("Test case %q:\n expected: %+v\n got: %+v", test.name, test.expectedInfo, info) 213 } 214 break 215 } 216 } 217 if !found { 218 t.Errorf("Test case %q: mountPoint %d not found", test.name, test.id) 219 } 220 } 221} 222 223func TestBadParseMountInfo(t *testing.T) { 224 tests := []struct { 225 info string 226 name string 227 id int 228 expectedInfo *MountInfo 229 error string 230 }{ 231 { 232 `224 62 253:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`, 233 "good major:minor field", 234 224, 235 &MountInfo{ 236 ID: 224, 237 ParentID: 62, 238 Major: 253, 239 Minor: 0, 240 Root: "/var/lib/docker/devicemapper/test/shared", 241 Source: "/dev/mapper/ssd-root", 242 MountPoint: "/var/lib/docker/devicemapper/test/shared", 243 OptionalFields: []string{"master:1", "shared:44"}, 244 FsType: "ext4", 245 MountOptions: []string{"rw", "relatime"}, 246 SuperOptions: []string{"rw", "seclabel", "data=ordered"}, 247 }, 248 "", 249 }, 250 { 251 `224 62 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`, 252 "missing major:minor field", 253 224, 254 nil, 255 `parsing '224 62 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered' failed: unexpected minor:major pair [/var/lib/docker/devicemapper/test/shared]`, 256 }, 257 { 258 `224 62 :0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`, 259 "empty major field", 260 224, 261 nil, 262 `parsing '' failed: unable to parse major device id, err:strconv.Atoi: parsing "": invalid syntax`, 263 }, 264 { 265 `224 62 253: /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`, 266 "empty minor field", 267 224, 268 nil, 269 `parsing '' failed: unable to parse minor device id, err:strconv.Atoi: parsing "": invalid syntax`, 270 }, 271 { 272 `224 62 foo:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`, 273 "alphabet in major field", 274 224, 275 nil, 276 `parsing 'foo' failed: unable to parse major device id, err:strconv.Atoi: parsing "foo": invalid syntax`, 277 }, 278 { 279 `224 62 253:bar /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`, 280 "alphabet in minor field", 281 224, 282 nil, 283 `parsing 'bar' failed: unable to parse minor device id, err:strconv.Atoi: parsing "bar": invalid syntax`, 284 }, 285 } 286 287 for _, test := range tests { 288 tempDir, filename, err := writeFile(test.info) 289 if err != nil { 290 t.Fatalf("cannot create temporary file: %v", err) 291 } 292 defer os.RemoveAll(tempDir) 293 294 infos, err := ParseMountInfo(filename) 295 if err != nil { 296 if err.Error() != test.error { 297 t.Errorf("Test case %q:\n expected error: %+v\n got: %+v", test.name, test.error, err.Error()) 298 } 299 continue 300 } 301 302 found := false 303 for _, info := range infos { 304 if info.ID == test.id { 305 found = true 306 if !reflect.DeepEqual(info, *test.expectedInfo) { 307 t.Errorf("Test case %q:\n expected: %+v\n got: %+v", test.name, test.expectedInfo, info) 308 } 309 break 310 } 311 } 312 if !found { 313 t.Errorf("Test case %q: mountPoint %d not found", test.name, test.id) 314 } 315 } 316} 317