Merge pull request #59050 from cofyc/get_fstype

Automatic merge from submit-queue (batch tested with PRs 51323, 59306, 58991, 59050). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Proposal: Use `blkid` to detect fs type of device instead of `lsblk`.

**What this PR does / why we need it**:

Use `blkid` to detect fs type of device instead of `lsblk`. 

`lsblk` depends on `udev`, and device driver's udev rules. If udev rules are not installed properly, `lsblk` could not get fs type of disk. This will cause problems, e.g. expanding volume will fail because it could not detect fs type of disk.

Take `rbd`  as example, `lsblk -n -o FSTYPE /dev/rbd0` command actually read fs type from `/run/udev/data/b252:0` (may vary on different machines, see real file from `strace lsblk -n -o FSTYPE /dev/rbd0` ).

This file `/run/udev/data/b252:0` is generated by `udev` and device driver's udev files. If machine don't have `/lib/udev/rules.d/60-ceph-by-parttypeuuid.rules` udev rule file installed (this file is from `ceph-osd` package on ubuntu), it will not be properly generated, e.g:

```
# cat /run/udev/data/b251:0
S:rbd/<pool>/<image>
I:13234059587579
E:ID_FS_TYPE=
E:net.ifnames=0
G:systemd
```

`lsblk -n -o FSTYPE /dev/rbd0` will get empty fs type.

[60-ceph-by-parttypeuuid.rules](https://github.com/ceph/ceph/blob/v13.0.0/udev/60-ceph-by-parttypeuuid.rules) is udev rule, which underlyingly runs `blkid` commands to get infos of device, then store them in udev file.

If we use `blkid` to get fs type, kubelet volume manager will not depend on `udev` stuffs. Currently, if kubelet node does not have `60-ceph-by-parttypeuuid.rules` installed (from `ceph-osd` package), it will fail to get fs type of rbd image. Even administrator install `ceph-osd` later, it can not get fs type of previous mapped images (udev data files not be to updated automatically).

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #

**Special notes for your reviewer**:

`udevadm test` logs:

- Ubuntu 16.04 (ceph-common installed), without ceph-osd: [without-ceph-osd.log](https://github.com/kubernetes/kubernetes/files/1678512/without-ceph-osd.log)
- Ubuntu 16.04 (ceph-common installed), with ceph-osd: [with-ceph-osd.log](https://github.com/kubernetes/kubernetes/files/1678509/with-ceph-osd.log)

**Release note**:

```release-note
NONE
```
pull/6/head
Kubernetes Submit Queue 2018-02-06 10:40:40 -08:00 committed by GitHub
commit b13092554c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 35 deletions

View File

@ -536,36 +536,57 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string,
return mountErr
}
// GetDiskFormat uses 'lsblk' to see if the given disk is unformated
// GetDiskFormat uses 'blkid' to see if the given disk is unformated
func (mounter *SafeFormatAndMount) GetDiskFormat(disk string) (string, error) {
args := []string{"-n", "-o", "FSTYPE", disk}
glog.V(4).Infof("Attempting to determine if disk %q is formatted using lsblk with args: (%v)", disk, args)
dataOut, err := mounter.Exec.Run("lsblk", args...)
args := []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", disk}
glog.V(4).Infof("Attempting to determine if disk %q is formatted using blkid with args: (%v)", disk, args)
dataOut, err := mounter.Exec.Run("blkid", args...)
output := string(dataOut)
glog.V(4).Infof("Output: %q", output)
glog.V(4).Infof("Output: %q, err: %v", output, err)
if err != nil {
if exit, ok := err.(utilexec.ExitError); ok {
if exit.ExitStatus() == 2 {
// Disk device is unformatted.
// For `blkid`, if the specified token (TYPE/PTTYPE, etc) was
// not found, or no (specified) devices could be identified, an
// exit code of 2 is returned.
return "", nil
}
}
glog.Errorf("Could not determine if disk %q is formatted (%v)", disk, err)
return "", err
}
// Split lsblk output into lines. Unformatted devices should contain only
// "\n". Beware of "\n\n", that's a device with one empty partition.
output = strings.TrimSuffix(output, "\n") // Avoid last empty line
var fstype, pttype string
lines := strings.Split(output, "\n")
if lines[0] != "" {
// The device is formatted
return lines[0], nil
for _, l := range lines {
if len(l) <= 0 {
// Ignore empty line.
continue
}
cs := strings.Split(l, "=")
if len(cs) != 2 {
return "", fmt.Errorf("blkid returns invalid output: %s", output)
}
// TYPE is filesystem type, and PTTYPE is partition table type, according
// to https://www.kernel.org/pub/linux/utils/util-linux/v2.21/libblkid-docs/.
if cs[0] == "TYPE" {
fstype = cs[1]
} else if cs[0] == "PTTYPE" {
pttype = cs[1]
}
}
if len(lines) == 1 {
// The device is unformatted and has no dependent devices
return "", nil
if len(pttype) > 0 {
glog.V(4).Infof("Disk %s detected partition table type: %s", pttype)
// Returns a special non-empty string as filesystem type, then kubelet
// will not format it.
return "unknown data, probably partitions", nil
}
// The device has dependent devices, most probably partitions (LVM, LUKS
// and MD RAID are reported as FSTYPE and caught above).
return "unknown data, probably partitions", nil
return fstype, nil
}
// isShared returns true, if given path is on a mount point that has shared

View File

@ -100,55 +100,55 @@ func TestSafeFormatAndMount(t *testing.T) {
},
},
{
description: "Test that 'lsblk' is called and fails",
description: "Test that 'blkid' is called and fails",
fstype: "ext4",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"lsblk", []string{"-n", "-o", "FSTYPE", "/dev/foo"}, "ext4\n", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "DEVNAME=/dev/foo\nTYPE=ext4\n", nil},
},
expectedError: fmt.Errorf("unknown filesystem type '(null)'"),
},
{
description: "Test that 'lsblk' is called and confirms unformatted disk, format fails",
description: "Test that 'blkid' is called and confirms unformatted disk, format fails",
fstype: "ext4",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"lsblk", []string{"-n", "-o", "FSTYPE", "/dev/foo"}, "\n", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", fmt.Errorf("formatting failed")},
},
expectedError: fmt.Errorf("formatting failed"),
},
{
description: "Test that 'lsblk' is called and confirms unformatted disk, format passes, second mount fails",
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount fails",
fstype: "ext4",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), fmt.Errorf("Still cannot mount")},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"lsblk", []string{"-n", "-o", "FSTYPE", "/dev/foo"}, "\n", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", nil},
},
expectedError: fmt.Errorf("Still cannot mount"),
},
{
description: "Test that 'lsblk' is called and confirms unformatted disk, format passes, second mount passes",
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount passes",
fstype: "ext4",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"lsblk", []string{"-n", "-o", "FSTYPE", "/dev/foo"}, "\n", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", nil},
},
expectedError: nil,
},
{
description: "Test that 'lsblk' is called and confirms unformatted disk, format passes, second mount passes with ext3",
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount passes with ext3",
fstype: "ext3",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"lsblk", []string{"-n", "-o", "FSTYPE", "/dev/foo"}, "\n", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.ext3", []string{"-F", "/dev/foo"}, "", nil},
},
expectedError: nil,
@ -159,30 +159,31 @@ func TestSafeFormatAndMount(t *testing.T) {
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"lsblk", []string{"-n", "-o", "FSTYPE", "/dev/foo"}, "\n", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
{"mkfs.xfs", []string{"/dev/foo"}, "", nil},
},
expectedError: nil,
},
{
description: "Test that 'lsblk' is called and reports ext4 partition",
description: "Test that 'blkid' is called and reports ext4 partition",
fstype: "ext3",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"lsblk", []string{"-n", "-o", "FSTYPE", "/dev/foo"}, "\next4\n", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "DEVNAME=/dev/foo\nPTTYPE=dos\n", nil},
},
expectedError: fmt.Errorf("failed to mount the volume as \"ext3\", it already contains unknown data, probably partitions. Mount error: unknown filesystem type '(null)'"),
},
{
description: "Test that 'lsblk' is called and reports empty partition",
fstype: "ext3",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
description: "Test that 'blkid' is called but has some usage or other errors (an exit code of 4 is returned)",
fstype: "xfs",
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
execScripts: []ExecArgs{
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
{"lsblk", []string{"-n", "-o", "FSTYPE", "/dev/foo"}, "\n\n", nil},
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 4}},
{"mkfs.xfs", []string{"/dev/foo"}, "", nil},
},
expectedError: fmt.Errorf("failed to mount the volume as \"ext3\", it already contains unknown data, probably partitions. Mount error: unknown filesystem type '(null)'"),
expectedError: fmt.Errorf("exit 4"),
},
}