You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Enables Btrfs fallback for other Linux distributions.
ZFS is the preferred storage driver, but it isn't available everywhere: WSL, SUSE, Arch, Fedora, and other distributions may ship without it. Keying the fallback off the distribution name is too coarse; it assumes every non-Ubuntu host lacks ZFS and every Ubuntu-derived one has it, neither of which holds for Debian and its derivatives.
Instead, detect whether the host provides a usable ZFS kernel module, already loaded (present at /dev/zfsAND/sys/module/zfs ) or loadable for the running kernel per modules.dep(5) (/lib/modules/$(uname -r)/modules.dep) or modules.builtin (/lib/modules/$(uname -r)/modules.builtin) and keep ZFS only then, falling back to btrfs otherwise. Only the kernel module is checked: LXD ships as a snap that bundles the zpool/zfs userspace tools, so a loaded or loadable module is what actually determines usability from the host side, tested on Arch Linux and openSUSE tumbleweed.
I think we also need to be more careful about where we use the storagePoolDriver variable. Currently we can assume it's accurate, since whether the host is WSL or not is unlikely to change. The availability of the ZFS module can easily change, though; we need a separate mechanism to indicate "what's the current driver of the workshop pool."
I think we also need to be more careful about where we use the storagePoolDriver variable. Currently we can assume it's accurate, since whether the host is WSL or not is unlikely to change. The availability of the ZFS module can easily change, though; we need a separate mechanism to indicate "what's the current driver of the workshop pool."
So, considering the case where ZFS was available when the pool was created but later stops being present, most likely because the user installed a different kernel that doesn't ship the ZFS module. What should we do with the now-unusable ZFS pool?
I can think of those options:
Put the daemon into a degraded state and add an actionable error message like "restore the ZFS-capable kernel, or remove the pool to recreate it as Btrfs"
Warn the user with an actionable message like "Old ZFS pool detected on system without support for ZFS on path <path-to-zfs-pool>" but create a new Btrfs pool under a different name.
I agree, but we should not suggest removing the pool, it will probably break the workshops using it (if it's even possible to remove it). Instead maybe it's best to reinstall the Workshop snap.
We just need to test what actually happens in that scenario, maybe LXD will fail to start and we probably handle that already.
@jonathan-conder I've implemented the requested change in 54bf6dd, which stops assuming a fixed state for ZFS availability. I then tested the degraded-state behavior for the scenario where ZFS is present at first but later vanishes.
I ran this on an Ubuntu Resolute LXD VM, reproducing a workshop pool created on ZFS whose kernel module later becomes unavailable after a kernel change.
Reproduction
Started with ZFS available, so the workshop pool was created on the zfs driver.
Built and installed a .deb for a newer kernel (7.2.0-rc7) that doesn't ship the ZFS module.
Removed the previous kernel and its ZFS module package (linux-main-modules-zfs-7.0.0-29-generic).
Rebooted into the new kernel and ran workshop list.
Result
Instead of surfacing a raw LXD error, the daemon goes into degraded mode with an actionable message:
$ workshop listerror: system is not healthy: cannot use the "workshop" storage pool, its storage driver may be unavailable (for example a ZFS pool after booting a kernel without the ZFS module): Error loading "zfs" module: Failed running: modprobe -b zfs: exit status 1 (modprobe: FATAL: Module zfs not found in directory /lib/modules/7.2.0-rc7)Boot into a kernel that provides the pool's storage driver, or reinstall Workshop to recreate the pool on an available driver
Recovery
The ZFS pool can't be deleted through LXD while the module is missing (lxc storage delete loads the driver, which fails), so the recovery is to reinstall Workshop: this recreates the workshop pool on an available driver (Btrfs) for a host that no longer provides ZFS.
On adding a Spread test
Let me know if you think we should add a Spread test covering this case. Two caveats:
For my manual test I had to build and install an upstream kernel, since simply uninstalling linux-main-modules-zfs-7.0.0-xx-generic isn't enough to reproduce a realistic scenario. For an automated test we could instead force the broken state, making it reproducible by just forcefully removing a package.
The bigger drawback is that this would require Spread to run on LXD VMs instead of containers. That would be a significant change to the current testing infrastructure.
The reason will be displayed to describe this comment to others. Learn more.
this adds an additional API call to these operations, do we have any info on the impact it has on latency? we could alternatively cache the storage pool driver at launch
The ZFS pool can't be deleted through LXD while the module is missing (lxc storage delete loads the driver, which fails), so the recovery is to reinstall Workshop: this recreates the workshop pool on an available driver (Btrfs) for a host that no longer provides ZFS.
I don't see how reinstalling Workshop will help. If LXD is unable to delete the pool then it will still be there after the reinstall.
For my manual test I had to build and install an upstream kernel, since simply uninstalling linux-main-modules-zfs-7.0.0-xx-generic isn't enough to reproduce a realistic scenario. For an automated test we could instead force the broken state, making it reproducible by just forcefully removing a package.
It's not reasonable to do that in a test. It's a rare enough case that I think some manual recovery steps are OK.
The bigger drawback is that this would require Spread to run on LXD VMs instead of containers. That would be a significant change to the current testing infrastructure.
I don't see how reinstalling Workshop will help. If LXD is unable to delete the pool then it will still be there after the reinstall.
Yeah, I think in this case you would need to reinstall LXD and Workshop, but this seems too drastic. Anyway, I've tested, and when the ZFS-capable kernel is restored, the workshop will be working again.
The reason will be displayed to describe this comment to others. Learn more.
Ok I think we're nearly there, just a couple of minor comments below, and I still think we should check for storagePoolDriver != existingPool.Driver in ensureBackendReady()
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Approval is blocked by an unresolved critical data race during LXD recovery, along with documentation inconsistencies about Btrfs capitalization and ZFS-only behavior.
This prose uses lowercase btrfs, whereas the tutorial and the new code comment use Btrfs. Keep the filesystem name consistently capitalized in user-facing documentation; reserve lowercase for the LXD driver identifier.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Approval is blocked by an unresolved race that can expose a newly created ZFS pool before its driver is published, along with unsupported pre-existing pool acceptance and inconsistent backend-specific documentation.
Publish the pool driver before concurrent backend operations
internal/workshop/lxd/lxd_backend.go:374
The atomic load/store removes the data race, but it does not close the publication window: after CreateStoragePool completes and before setPoolDriver runs, a concurrently executing task can enter mergeDevices or copyInstance and observe poolDriver() == "". On a newly created ZFS pool that omits initial.zfs.promote, so the first snapshot/copy can use the Btrfs path and fail or lose the required ZFS promotion. Publish the driver before the pool becomes usable, or serialize pool creation/recovery with backend operations.
Reject existing pools with unsupported drivers
internal/workshop/lxd/lxd_backend.go:408
When a workshop pool already exists and poolDriver() is still empty, this branch adopts whatever LXD driver it finds. That allows a pre-existing dir, lvm, or other pool to be silently accepted even though preferredDriver and the documented behavior only support ZFS and Btrfs; the snapshot behavior then no longer matches the backend's assumptions. Validate existingPool.Driver against the supported drivers before recording it (or return an actionable error).
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Active critical issues remain, including an unused import that prevents compilation and insufficient validation of existing or unknown storage drivers, which can cause incorrect snapshot behavior.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The change leaves two active documentation inconsistencies: an unconditional ZFS/LZ4 capability claim on the Btrfs path and a contradictory developer prerequisite that still requires ZFS.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Active documentation inconsistency and stale storage-driver caching remain unresolved, including a moderate correctness risk when recreating a deleted pool.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The documentation remains internally contradictory because several sections still present ZFS as universal despite newly documented Btrfs support, leaving an active correctness issue to resolve.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Active documentation issues remain: the driver name uses inconsistent btrfs capitalization, and the architecture diagram still describes the storage subsystem exclusively as ZFS despite documenting a Btrfs fallback.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The documentation still presents ZFS behavior as universal even though storage is now driver-dependent, leaving inaccurate explanations for the newly supported btrfs path.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The change still has an unresolved critical backend issue where an existing unsupported storage pool can be accepted and cause later operations to fail, along with documentation that contradicts fallback behavior.
ZFS is preferred, but WSL, SUSE, Arch, Fedora and others may ship
without it. Instead of keying the fallback off WSL, pick the first
driver LXD reports as supported (ZFS, then Btrfs), and fail the system
check with a clear error if the pool is missing and neither is
available.
Record the pool's driver atomically, since it may be set after the
daemon starts serving. Adopt an existing pool's driver if it is ZFS or
Btrfs and reject anything else, recreate a missing pool on the
preferred supported driver, and serialize ensureBackendReady so
concurrent callers don't race to create the pool.
Update the docs to describe the detection-based fallback.
Closes: canonical#742
Signed-off-by: Lincoln Wallace <[email protected]>
wip
Signed-off-by: Lincoln Wallace <[email protected]>
Workshop no longer limits btrfs to WSL. It uses ZFS where LXD
supports it and falls back to btrfs otherwise. Update the tutorial,
reference, architecture and contributing pages to match.
Signed-off-by: Lincoln Wallace <[email protected]>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Approval is blocked by an unaddressed compatibility failure in the snapshot integration suite for Btrfs, along with incorrect handling of pending or errored storage pools and contradictory documentation.
For storage, it uses ZFS where |ws_markup| detects that ZFS is available,
and otherwise falls back to btrfs.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Enables Btrfs fallback for other Linux distributions.
ZFS is the preferred storage driver, but it isn't available everywhere: WSL, SUSE, Arch, Fedora, and other distributions may ship without it. Keying the fallback off the distribution name is too coarse; it assumes every non-Ubuntu host lacks ZFS and every Ubuntu-derived one has it, neither of which holds for Debian and its derivatives.
Instead, detect whether the host provides a usable ZFS kernel module, already loaded (present at
/dev/zfsAND/sys/module/zfs) or loadable for the running kernel per modules.dep(5) (/lib/modules/$(uname -r)/modules.dep) or modules.builtin (/lib/modules/$(uname -r)/modules.builtin) and keep ZFS only then, falling back to btrfs otherwise. Only the kernel module is checked: LXD ships as a snap that bundles the zpool/zfs userspace tools, so a loaded or loadable module is what actually determines usability from the host side, tested on Arch Linux and openSUSE tumbleweed.Self-review quick check
isWSL()Docs
Procedure:
Content:
tutorial/andhow-to/sections).docs/.coverage.yamlupdated, coverage tags added (.. artefact).Or: