Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build mountinfo on multiple platforms #3

Merged
merged 2 commits into from
Jan 6, 2023
Merged

Conversation

peterguy
Copy link
Contributor

@peterguy peterguy commented Jan 4, 2023

Build mountinfo on multiple platforms

It functions the same as before this commit on Linux OSs that use the sysfs pseudo filesystem, and will now work on macOS through the use of the OS tools stat and diskutil. Windows, Solaris, Plan9, etc... are currently not supported. go build should work on those operating systems, and this commit includes a shim for Windows: device_windows.go. I did notice that GOOS=plan9 GOARCH=amd64 go build failed because a transitive dependency does not support Plan9.

Changed files

info.go

move functions into new platform-specific files. The entrypoint for the platform-specific code is the discoverDeviceName function.

info_linux_test.go and info_test.go

move the Test_DeviceName_Snapshots function from info_test.go to info_linux_test.go so that the Linux test file will run a smoke test on CWD and also a test representing other mounted filesystems. Copy Test_DeviceName_SmokeTest from info_linux_test.go to info_test.go so that other operating systems will run the smoke test.

go.mod and go.sum

upgrade all dependencies and add a new dependency: github.com/moby/sys/mountinfo to handle reading mount info in order to find the sysfs mount point.

New files

device.go

discoverDeviceName implementation for non-Unix/Linux/Windows operating systems. Currently returns a "not implemented" error.

device_darwin.go

discoverDeviceName implementation for macOS. Uses stat and diskutil.

device_linux.go

discoverDeviceName implementation for Linux. Uses device identifier numbers and the sysfs pseudo filesystem to find block devices. Does not work for non-block devices (like Docker bind mounts).

device_unix.go

functions that work on both Unix and Linux (and derivative) operating systems, but not on others. This is where the golang.org/x/sys/unix package is used to stat files in order to find the device identifier numbers.

device_windows.go

discoverDeviceName implementation for Windows. Currently returns a "not implemented" error.

Test Plan

Run go test -v on various platforms.

macOS

go test -v will run a smoke test using the current working directory. The result should be a PASS, and if you provide the verbose option (-v), you'll see which device name was found from the current working directory.

Linux

If you don't have Linux readily available, you can use Docker. From the project directory, run this command to launch a Go container and go test the project:

docker run --rm --volume $(pwd):/src --volume mountinfo_test:/test --volume ${GOPATH:-${HOME}/go}:/go --interactive --workdir /test golang bash <<EOF
cp -R /src/* .
go test -v
EOF

The result should be a PASS, and if you provide the verbose option (-v), you'll see which device names were found in the tests.

Afterward, you can clean up the mountinfo_test volume if you want

docker volume rm mountinfo_test

Windows

There is a stub for windows - device_windows.go - but it currently returns an error, which is by design, so go test on Windows will result in a FAIL with a "not implemented on windows" error message.

We may be able to make this work on windows using the DeviceIoControl API, which is exposed in the golang.org/x/sys/windows package, alng with STORAGE_PROPERTY_ID and STORAGE_DEVICE_ID_DESCRIPTOR to get the SCSI device IDs. These device IDs may not be exactly what we need - mountinfo provides metrics for Prometheus, so the SCSI device IDs may be too long to be useful.

Other Operating Systems

There is a catch-all stub for unsupported operating systems, such as Plan9 and Solaris - device.go. It will return a "not implemented on [GOOS]" error message, so go test will FAIL. This is by design.

It functions the same as before this commit on Linux OSs that use the `sysfs` pseudo filesystem, and will now work on macOS through the use of the OS tools `stat` and `diskutil`. Windows, Solaris, Plan9, etc... are currently not supported. `go build` should work on those operating systems, and  this commit includes a shim for Windows: `device_windows.go`. I did notice that `GOOS=plan9 GOARCH=amd64 go build` failed because a transitive dependency does not support Plan9.

move functions into new platform-specific files. The entrypoint for the platform-specific code is the `discoverDeviceName` function.

move the `Test_DeviceName_Snapshots` function from `info_test.go` to `info_linux_test.go` so that the Linux test file will run a smoke test on `CWD` and also a test representing other mounted filesystems. Copy `Test_DeviceName_SmokeTest` from `info_linux_test.go` to `info_test.go` so that other operating systems will run the smoke test.

upgrade all dependencies and add a new dependency: `github.com/moby/sys/mountinfo` to handle reading mount info in order to find the `sysfs` mount point.

`discoverDeviceName` implementation for non-Unix/Linux/Windows operating systems. Currently returns a "not implemented" error.

`discoverDeviceName` implementation for macOS. Uses `stat` and `diskutil`.

`discoverDeviceName` implementation for Linux. Uses device identifier numbers and the `sysfs` pseudo filesystem to find block devices. Does not work for non-block devices (like Docker bind mounts).

functions that work on both Unix and Linux (and derivative) operating systems, but not on others. This is where the `golang.org/x/sys/unix` package is used to stat files in order to find the device identifier numbers.

`discoverDeviceName` implementation for Windows. Currently returns a "not implemented" error.

Run `go test -v` on various platforms.

`go test -v` will run a smoke test using the current working directory. The result should be a PASS, and if you provide the verbose option (`-v`), you'll see which device name was found from the current working directory.

If you don't have Linux readily available, you can use Docker. From the project directory, run this command to launch a Go container and `go test` the project:
```
docker run --rm --volume $(pwd):/src --volume mountinfo_test:/test --volume ${GOPATH:-${HOME}/go}:/go --interactive --workdir /test golang bash <<EOF
cp -R /src/* .
go test -v
EOF
```
The result should be a PASS, and if you provide the verbose option (`-v`), you'll see which device names were found in the tests.

Afterward, you can clean up the `mountinfo_test` volume if you want
```
docker volume rm mountinfo_test
```

There is a stub for windows - `device_windows.go` - but it currently returns an error, which is by design, so `go test` on Windows will result in a FAIL with a "not implemented on windows" error message.

We may be able to make this work on windows using the `DeviceIoControl` API, which is exposed in the `golang.org/x/sys/windows` package, alng with `STORAGE_PROPERTY_ID` and `STORAGE_DEVICE_ID_DESCRIPTOR` to get the SCSI device IDs. These device IDs may not be exactly what we need - `mountinfo` provides metrics for Prometheus, so the SCSI device IDs may be too long to be useful.

There is a catch-all stub for unsupported operating systems, such as Plan9 and Solaris - `device.go`. It will return a "not implemented on [GOOS]" error message, so `go test` will FAIL. This is by design.
@peterguy peterguy requested a review from ggilmore January 4, 2023 18:59
@peterguy peterguy self-assigned this Jan 4, 2023
@peterguy peterguy linked an issue Jan 4, 2023 that may be closed by this pull request
@beyang
Copy link
Member

beyang commented Jan 6, 2023

macOS impl looks good to me.

Were there any changes to the Linux implementation, or was it just moving it to a separate file so it could be guarded by a build flag? I didn't review the Linux impl too closely.

Higher-level question: do we a macOS implementation at all? I think it's safe to assume that binaries running on macOS will all be local instances. If that's the case, should we run Prometheus at all or just avoid the monitoring stack altogether?

@peterguy peterguy merged commit 7026e28 into main Jan 6, 2023
@peterguy peterguy deleted the peterguy/multi-platform branch January 6, 2023 00:44
@peterguy
Copy link
Contributor Author

peterguy commented Jan 6, 2023

The Linux implementation is basically unchanged. Code was moved around so it could conform to build constraints, and I changed some things to enable mocking for testing instead of requiring a struct to be passed in all of the time.

I agree with your assumptions about macOS implementations, and we probably won't actually need the monitoring stack. I wrote it as more of a PoC to validate my multi-platform approach and figured I'd leave it in place because it'll be useful as a template, and in the off chance that it's useful at runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support multiple platforms for native binary
2 participants