From 33418be0fc7d6bec11e17e81052e3b7ca5512e24 Mon Sep 17 00:00:00 2001 From: Jordan Williams Date: Mon, 1 Mar 2021 15:47:23 -0600 Subject: [PATCH] Inherit system storage driver in rootless configurations The storage driver set in `/etc/containers/storage.conf` was ignored for rootless configurations. Rootless configurations would use overlay or vfs by default. If the STORAGE_DRIVER environment was variable set, this value would be used instead. This commit changes this behavior to inherit the driver set in `/etc/containers/storage.conf`. To inherit the driver in rootless configurations, the driver must be valid in a rootless context. The valid rootless drivers are btrfs, overlay, and vfs at this time. To remain consistent with previous behavior, the STORAGE_DRIVER environment variable supersedes any driver set in `/etc/containers/storage.conf` and does not need to be a valid rootless driver. buildah's documentation for the `--storage-driver` command will need to be updated to reflect these changes. --- AUTHORS | 1 + docs/containers-storage.conf.5.md | 5 +- types/options.go | 19 +++++- types/options_test.go | 99 +++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 types/options_test.go diff --git a/AUTHORS b/AUTHORS index 11cd83d14..129dd3969 100644 --- a/AUTHORS +++ b/AUTHORS @@ -703,6 +703,7 @@ Joost Cassee Jordan Jordan Arentsen Jordan Sissel +Jordan Williams Jose Diaz-Gonzalez Joseph Anthony Pasquale Holsten Joseph Hager diff --git a/docs/containers-storage.conf.5.md b/docs/containers-storage.conf.5.md index ba6fcaae1..7f9e00348 100644 --- a/docs/containers-storage.conf.5.md +++ b/docs/containers-storage.conf.5.md @@ -29,7 +29,10 @@ The `storage` table supports the following options: **driver**="" container storage driver Default Copy On Write (COW) container storage driver. Valid drivers are "overlay", "vfs", "devmapper", "aufs", "btrfs", and "zfs". Some drivers (for example, "zfs", "btrfs", and "aufs") may not work if your kernel lacks support for the filesystem. - This field is requiered to guarantee proper operation. + This field is required to guarantee proper operation. + Valid rootless drivers are "btrfs", "overlay", and "vfs". + Rootless users default to the driver defined in the system configuration when possible. + When the system configuration uses an unsupported rootless driver, rootless users default to "overlay" if available, otherwise "vfs". **graphroot**="" container storage graph dir (default: "/var/lib/containers/storage") diff --git a/types/options.go b/types/options.go index 8cd89c1ab..38794120f 100644 --- a/types/options.go +++ b/types/options.go @@ -149,6 +149,17 @@ type StoreOptions struct { AutoNsMaxSize uint32 `json:"auto_userns_max_size,omitempty"` } +// isRootlessDriver returns true if the given storage driver is valid for containers running as non root +func isRootlessDriver(driver string) bool { + validDrivers := map[string]bool{ + "btrfs": true, + "overlay": true, + "overlay2": true, + "vfs": true, + } + return validDrivers[driver] +} + // getRootlessStorageOpts returns the storage opts for containers running as non root func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOptions, error) { var opts StoreOptions @@ -163,7 +174,13 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti } else { opts.GraphRoot = filepath.Join(dataDir, "containers", "storage") } - opts.GraphDriverName = os.Getenv("STORAGE_DRIVER") + + if driver := systemOpts.GraphDriverName; isRootlessDriver(driver) { + opts.GraphDriverName = driver + } + if driver := os.Getenv("STORAGE_DRIVER"); driver != "" { + opts.GraphDriverName = driver + } if opts.GraphDriverName == "" || opts.GraphDriverName == "overlay" { if path, err := exec.LookPath("fuse-overlayfs"); err == nil { opts.GraphDriverName = "overlay" diff --git a/types/options_test.go b/types/options_test.go new file mode 100644 index 000000000..2ee285671 --- /dev/null +++ b/types/options_test.go @@ -0,0 +1,99 @@ +package types + +import ( + "fmt" + "os" + "testing" + + "gotest.tools/assert" +) + +func TestGetRootlessStorageOpts(t *testing.T) { + envDriver, envDriverSet := os.LookupEnv("STORAGE_DRIVER") + os.Unsetenv("STORAGE_DRIVER") + + const vfsDriver = "vfs" + const overlayDriver = "overlay" + + t.Run("systemDriver=btrfs", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "btrfs" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, "btrfs") + }) + + t.Run("systemDriver=overlay", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = overlayDriver + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, overlayDriver) + }) + + t.Run("systemDriver=overlay2", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "overlay2" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, "overlay2") + }) + + t.Run("systemDriver=vfs", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = vfsDriver + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, vfsDriver) + }) + + t.Run("systemDriver=aufs", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "aufs" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Assert(t, storageOpts.GraphDriverName == overlayDriver || storageOpts.GraphDriverName == vfsDriver, fmt.Sprintf("The rootless driver should be set to 'overlay' or 'vfs' not '%v'", storageOpts.GraphDriverName)) + }) + + t.Run("systemDriver=devmapper", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "devmapper" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Assert(t, storageOpts.GraphDriverName == overlayDriver || storageOpts.GraphDriverName == vfsDriver, fmt.Sprintf("The rootless driver should be set to 'overlay' or 'vfs' not '%v'", storageOpts.GraphDriverName)) + }) + + t.Run("systemDriver=zfs", func(t *testing.T) { + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = "zfs" + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Assert(t, storageOpts.GraphDriverName == overlayDriver || storageOpts.GraphDriverName == vfsDriver, fmt.Sprintf("The rootless driver should be set to 'overlay' or 'vfs' not '%v'", storageOpts.GraphDriverName)) + }) + + t.Run("STORAGE_DRIVER=btrfs", func(t *testing.T) { + os.Setenv("STORAGE_DRIVER", "btrfs") + defer os.Unsetenv("STORAGE_DRIVER") + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = vfsDriver + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, "btrfs") + }) + + t.Run("STORAGE_DRIVER=zfs", func(t *testing.T) { + os.Setenv("STORAGE_DRIVER", "zfs") + defer os.Unsetenv("STORAGE_DRIVER") + systemOpts := StoreOptions{} + systemOpts.GraphDriverName = vfsDriver + storageOpts, err := getRootlessStorageOpts(1000, systemOpts) + assert.NilError(t, err) + assert.Equal(t, storageOpts.GraphDriverName, "zfs") + }) + + if envDriverSet { + os.Setenv("STORAGE_DRIVER", envDriver) + } else { + os.Unsetenv("STORAGE_DRIVER") + } +}