From b41e43abff9f149f0b2de3e141f0fde4beef586a Mon Sep 17 00:00:00 2001 From: Chris Montgomery Date: Wed, 6 Sep 2023 18:32:21 -0400 Subject: [PATCH 1/4] fix(shell:aliases|nu): workaround to support exa aliases option --- .../homeProfiles/shells/nushell/default.nix | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/profiles/homeProfiles/shells/nushell/default.nix b/profiles/homeProfiles/shells/nushell/default.nix index f9303893b..9aeca4b1b 100644 --- a/profiles/homeProfiles/shells/nushell/default.nix +++ b/profiles/homeProfiles/shells/nushell/default.nix @@ -8,10 +8,13 @@ inherit (config.home) username sessionVariables; l = flake.inputs.nixpkgs.lib // builtins; - # FIXME: hardcoded + exaCfg = config.programs.exa; + + # FIXME: hardcoded -- note also that we can't rely on variable expansion to resolve paths in env.nu userConfigDir = "~/.config/dotfield/users/${username}/config/nushell"; /* + @partial replaceVars :: [String] -> (String -> String) */ replaceVars = names: @@ -19,6 +22,11 @@ (l.map (v: "$" + v) names) (l.map (v: "$env.${v}") names); + /* + replaceVars' :: String -> String + */ + replaceVars' = replaceVars commonNames; + commonNames = [ "HOME" "EDITOR" @@ -37,7 +45,7 @@ lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: let - value' = replaceVars commonNames value; + value' = replaceVars' value; in "$env.${name} = `${value'}`") attrs); in { @@ -63,4 +71,14 @@ in { xdg.configFile."nushell/home.nu".source = pkgs.writeText "home.nu" '' ${attrsToEnvDecls sessionVariables} ''; + + # FIXME: needs PR to upstream `exa` module for fix to use + # `home.shellAliases`, not shell-specific options + home.shellAliases = lib.mkIf (exaCfg.enable && exaCfg.shellAliases.enable) { + ls = "exa"; + ll = "exa -l"; + la = "exa -a"; + lt = "exa --tree"; + lla = "exa -la"; + }; } From 7ccc444c771eee517b141ba4506bff9859975139 Mon Sep 17 00:00:00 2001 From: Chris Montgomery Date: Mon, 4 Sep 2023 13:29:47 -0400 Subject: [PATCH 2/4] docs(moraine): filesystem layout update --- machines/moraine/README.md | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/machines/moraine/README.md b/machines/moraine/README.md index 650d6b32c..5eecd5878 100644 --- a/machines/moraine/README.md +++ b/machines/moraine/README.md @@ -2,7 +2,7 @@ ## Layout -Loosely based on TRaSH's advice [link](https://trash-guides.info/Hardlinks/How-to-setup-for/ +Loosely based on TRaSH's advice [link](https://trash-guides.info/Hardlinks/How-to-setup-for/) ```shell-session sudo chown -R $USER:$USER /data @@ -11,22 +11,25 @@ sudo chmod -R a=,a+rX,u+w,g+w /data ```txt . -├── mnt # │ │ │ │ │ │ -│ └── silo # │ │ │ │ │ [5] rclone mount of storage box -└── srv # │ │ │ │ │ │ - └── data # │ │ │ │ │ │ - ├── media # │ │ │ │ │ │ - │ ├── incoming # │ │ │ │ [4] queued for starr processing - │ └── library # │ │ │ │ │ [5] symlink: /mnt/silo/data (tbd: unionfs) - │ ├── movies # │ │ │ │ │ [5] target for radarr - │ ├── music # │ │ │ │ │ [5] target for lidarr - │ ├── other # │ │ │ │ │ [5] tbd (archival, oddities) - │ └── tv # │ │ │ │ │ [5] target for sonarr - └── torrents # │ │ │ │ │ │ - ├── complete # │ │ │ [3] │ │ - ├── incoming # │ [1] │ │ │ │ - ├── metadata # │ │ [2] meta-info files for active torrents - └── watch # [0] torrent meta-info file intake +├── mnt +│ └── silo # │ │ │ │ │ [5] │ +└── srv # │ │ │ │ │ │ │ + └── data # │ │ │ │ │ │ │ + ├── media # │ │ │ │ │ │ │ + │ ├── incoming # │ │ │ [3] │ │ │ + │ ├── library # │ │ │ │ │ [5] │ + │ │ ├── movies # │ │ │ │ │ [5] │ + │ │ ├── music # │ │ │ │ │ [5] │ + │ │ ├── other # │ │ │ │ │ [5] │ + │ │ └── tv # │ │ │ │ │ [5] │ + │ └── outgoing # │ │ │ │ [4] │ │ + └── torrents # │ │ │ │ │ │ │ + ├── complete # │ │ [2] │ │ │ │ + ├── incoming # │ [1] │ │ │ │ │ + ├── metadata # │ [1] │ │ │ │ │ + └── watch # [0] │ │ │ │ │ │ + ├── load # [0] │ │ │ │ │ │ + └── start # [0] │ │ │ │ │ │ ``` ### `/mnt` From 3844528cd1953adf23533183e5467245bacfcb89 Mon Sep 17 00:00:00 2001 From: Chris Montgomery Date: Mon, 4 Sep 2023 13:50:58 -0400 Subject: [PATCH 3/4] fix(moraine): implement datastore filesystem layout --- machines/moraine/bin/provision.sh | 41 ++++++++++++++++++++----------- machines/moraine/filesystems.nix | 34 +++++++++++++++++++------ 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/machines/moraine/bin/provision.sh b/machines/moraine/bin/provision.sh index 00a58f19f..22218a6e3 100644 --- a/machines/moraine/bin/provision.sh +++ b/machines/moraine/bin/provision.sh @@ -24,9 +24,7 @@ export NEW_HOSTNAME=moraine export FSOPTS="defaults,x-mount.mkdir,noatime" export BTRFSOPTS="${FSOPTS},compress=zstd" -# Preserve the Cloudbox/Saltbox `/mnt/local/` structure for easier reference. -# Mounting a non-root fs under `/mnt` also seems like best practice. -export LOCAL_PREFIX="/mnt/mnt/local" +export LOCAL_PREFIX="/mnt/srv/" # boot/root export NVME01="/dev/nvme0n1" @@ -220,20 +218,35 @@ btrfs device scan mkdir -p "${LOCAL_PREFIX}" mount -t btrfs LABEL=local "${LOCAL_PREFIX}" btrfs subvolume create "${LOCAL_PREFIX}/@backups" -btrfs subvolume create "${LOCAL_PREFIX}/@completed" -btrfs subvolume create "${LOCAL_PREFIX}/@torrents" -btrfs subvolume create "${LOCAL_PREFIX}/@media" +btrfs subvolume create "${LOCAL_PREFIX}/@bt-completed" +btrfs subvolume create "${LOCAL_PREFIX}/@bt-incoming" +btrfs subvolume create "${LOCAL_PREFIX}/@bt-metadata" +btrfs subvolume create "${LOCAL_PREFIX}/@bt-watch" +btrfs subvolume create "${LOCAL_PREFIX}/@media-incoming" +btrfs subvolume create "${LOCAL_PREFIX}/@media-outgoing" btrfs subvolume list -a "${LOCAL_PREFIX}" umount "${LOCAL_PREFIX}" -mount -t btrfs -o "subvol=@backups,${FSOPTS}" LABEL="local" \ - "${LOCAL_PREFIX}/backups" -mount -t btrfs -o "subvol=@completed,${FSOPTS}" LABEL="local" \ - "${LOCAL_PREFIX}/downloads/completed" -mount -t btrfs -o "subvol=@torrents,${FSOPTS}" LABEL="local" \ - "${LOCAL_PREFIX}/downloads/torrents" -mount -t btrfs -o "subvol=@media,${FSOPTS}" LABEL="local" \ - "${LOCAL_PREFIX}/Media" +mount -t btrfs -o "subvol=@backups,${BTRFSOPTS}" LABEL="local" \ + "${LOCAL_PREFIX}/backups" + +mount -t btrfs -o "subvol=@bt-completed,${BTRFSOPTS}" LABEL="local" \ + "${LOCAL_PREFIX}/data/torrents/completed" + +mount -t btrfs -o "subvol=@bt-incoming,${BTRFSOPTS}" LABEL="local" \ + "${LOCAL_PREFIX}/data/torrents/incoming" + +mount -t btrfs -o "subvol=@bt-metadata,${BTRFSOPTS}" LABEL="local" \ + "${LOCAL_PREFIX}/data/torrents/metadata" + +mount -t btrfs -o "subvol=@bt-watch,${BTRFSOPTS}" LABEL="local" \ + "${LOCAL_PREFIX}/data/torrents/watch" + +mount -t btrfs -o "subvol=@media-incoming,${BTRFSOPTS}" LABEL="local" \ + "${LOCAL_PREFIX}/media/incoming" + +mount -t btrfs -o "subvol=@media-outgoing,${BTRFSOPTS}" LABEL="local" \ + "${LOCAL_PREFIX}/media/outgoing" # Repeat mkdir since `/mnt` has since been umounted. mkdir -p /mnt/boot diff --git a/machines/moraine/filesystems.nix b/machines/moraine/filesystems.nix index dcad71a9c..5b96895a0 100644 --- a/machines/moraine/filesystems.nix +++ b/machines/moraine/filesystems.nix @@ -45,31 +45,49 @@ in { fileSystems."/var/lib/postgres" = { device = "/dev/disk-by-label/nixos"; fsType = "btrfs"; - options = commonOpts ++ ["subvol=@postgres" "ssd" "nofail"]; + options = commonOpts ++ ["subvol=@postgres" "ssd"]; }; - fileSystems."/mnt/local/backups" = { + fileSystems."/srv/backups" = { device = "/dev/disk/by-label/local"; fsType = "btrfs"; options = commonOpts ++ ["subvol=@backups" "nofail"]; }; - fileSystems."/mnt/local/downloads/completed" = { + fileSystems."/srv/data/torrents/completed" = { device = "/dev/disk/by-label/local"; fsType = "btrfs"; - options = commonOpts ++ ["subvol=@completed" "nofail"]; + options = commonOpts ++ ["subvol=@bt-completed"]; }; - fileSystems."/mnt/local/downloads/torrents" = { + fileSystems."/srv/data/torrents/incoming" = { device = "/dev/disk/by-label/local"; fsType = "btrfs"; - options = commonOpts ++ ["subvol=@torrents" "nofail"]; + options = commonOpts ++ ["subvol=@bt-incoming"]; }; - fileSystems."/mnt/local/Media" = { + fileSystems."/srv/data/torrents/metadata" = { device = "/dev/disk/by-label/local"; fsType = "btrfs"; - options = commonOpts ++ ["subvol=@media" "nofail"]; + options = commonOpts ++ ["subvol=@bt-metadata"]; + }; + + fileSystems."/srv/data/torrents/watch" = { + device = "/dev/disk/by-label/local"; + fsType = "btrfs"; + options = commonOpts ++ ["subvol=@bt-watch"]; + }; + + fileSystems."/srv/media/incoming" = { + device = "/dev/disk/by-label/local"; + fsType = "btrfs"; + options = commonOpts ++ ["subvol=@media-incoming"]; + }; + + fileSystems."/srv/media/outgoing" = { + device = "/dev/disk/by-label/local"; + fsType = "btrfs"; + options = commonOpts ++ ["subvol=@media-outgoing"]; }; fileSystems."/boot" = { From 1b82083bc5ae0397e325d54b256fa8c4e7a64d68 Mon Sep 17 00:00:00 2001 From: Chris Montgomery Date: Mon, 4 Sep 2023 16:01:33 -0400 Subject: [PATCH 4/4] feat(moraine|fs|rclone): rclone mount silo with unix mount --- machines/moraine/rclone-mount.nix | 221 ++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 machines/moraine/rclone-mount.nix diff --git a/machines/moraine/rclone-mount.nix b/machines/moraine/rclone-mount.nix new file mode 100644 index 000000000..21960e1b7 --- /dev/null +++ b/machines/moraine/rclone-mount.nix @@ -0,0 +1,221 @@ +# FIXME: set up rclone remote declaratively (somehow) +# TODO: sshfs or samba/cifs? +### Sources: +# +# +# +# +# +# +# +# +# +# +# +# TODO: why not use `rclone union` remote type? saltbox does not seem to use this (why?) +# TODO: +# TODO: +# TODO: +# TODO: +# +# NOTE(rclone): Option --rc does not work together with --daemon or in mount.rclone mode. We are aware of this limitation and work on fixing it. It is tracked by tickets: +# https://github.com/rclone/rclone/issues/2618 +# https://github.com/rclone/rclone/issues/5664 +{pkgs, ...}: let + rcloneRemote = "silo"; + runDir = "/srv/data/run"; + mediaDir = "srv/data/media/"; + # FIXME: should be unionfs + mountPath = "${mediaDir}/library"; + + # TODO: btrfs subvolume for cache? + # -- yea actually... what happens when it's NOT a subvol? + # i bet it falls back to the nvme `/` root vol, not the hdd vol... + cachePath = "${mediaDir}/cache"; +in { + systemd.tmpfiles.rules = [ + # FIXME: adjust to needs + "d ${cachePath} 0755 root root -" + ]; + + system.fsPackages = [ + # + (pkgs.runCommand "mount.rclone" {} '' + mkdir -p $out/bin + ln -s ${pkgs.rclone}/bin/rclone $out/bin/mount.rclone + '') + ]; + + ################################ + # Feeder + ################################ + + # TODO: + # feeder_mount_start_command: |- + # /usr/bin/rclone mount \ + # --user-agent='{{ user_agent }}' \ + # --config={{ rclone_config_path }} \ + # --allow-other \ + # --copy-links \ + # --dir-cache-time=1m \ + # --max-read-ahead=200M \ + # --umask=002 \ + # --syslog \ + # -v \ + # feeder:/mnt/local /mnt/feeder + + # NOTE: vfs mount + # rclone_vfs_mount_start_command: |- + # /usr/bin/rclone mount \ + # --user-agent='{{ user_agent }}' \ + # --config={{ rclone_config_path }} \ + # --allow-other \ + # --rc \ + # --rc-no-auth \ + # --rc-addr=localhost:5572 \ + # --drive-skip-gdocs \ + # --vfs-read-chunk-size=64M \ + # --vfs-read-chunk-size-limit=2048M \ + # --buffer-size=64M \ + # --poll-interval=15s \ + # --dir-cache-time=1000h \ + # --timeout=10m \ + # --drive-chunk-size=64M \ + # --drive-pacer-min-sleep=10ms \ + # --drive-pacer-burst=1000 \ + # --umask=002 \ + # --syslog \ + # {{ '--bind=' + ansible_default_ipv4.address + ' ' if mounts.ipv4_only else '' }}-v \ + # {{ rclone.remote }}: /mnt/remote + + # NOTE(rclone): > The umount operation can fail, for example when the + # > mountpoint is busy. When that happens, it is the user's + # > responsibility to stop the mount manually." + # rclone_vfs_mount_stop_command: /bin/fusermount -uz /mnt/remote + + # rclone_vfs_mount_start_post_command: |- + # /usr/bin/rclone rc vfs/refresh recursive=true --url http://localhost:5572 _async=true + + # NOTE: vfs cache + fileSystems."/mnt/silo" = { + fsType = "rclone"; + # via + options = [ + "config=${rcloneConf}" + "allow-other" + "async-read=true" + "dir-cache-time=1000h" + "buffer-size=32M" + "poll-interval=15s" + "rc" + "rc-no-auth" + "rc-addr=localhost:5572" + "use-mmap" + "vfs-read-ahead=128M" + "vfs-read-chunk-size=32M" + "vfs-read-chunk-size-limit=2G" + # FIXME:"vfs-cache-max-age=???????????????" + "vfs-cache-mode=full" + "vfs-cache-poll-interval=30s" + # FIXME: "vfs-cache-max-size=???????????" + "timeout=10m" + # FIXME: verify umask + "umask=002" + "cache-dir=${vfsCacheDir}" + # FIXME: maybe? "syslog" + # FIXME: what? "bind=${ipv4} ..... if mounts.ipv4only else '' -v" + # TODO: remove probably: "user-agent=????????"; + "${rcloneSiloRemote}: " + # FIXME: prob unnecessary "mountpoint" + ]; + # FIXME: translate above: + # /usr/bin/rclone mount \ + # --user-agent='{{ user_agent }}' \ + # --config={{ rclone_config_path }} \ + # --allow-other \ + # --async-read=true \ + # --dir-cache-time=1000h \ + # --buffer-size=32M \ + # --poll-interval=15s \ + # --rc \ + # --rc-no-auth \ + # --rc-addr=localhost:5572 \ + # --use-mmap \ + # --vfs-read-ahead=128M \ + # --vfs-read-chunk-size=32M \ + # --vfs-read-chunk-size-limit=2G \ + # --vfs-cache-max-age={{ rclone_vfs_cache_max_age }} \ + # --vfs-cache-mode=full \ + # --vfs-cache-poll-interval=30s \ + # --vfs-cache-max-size={{ rclone_vfs_cache_max_size }} \ + # --timeout=10m \ + # --drive-skip-gdocs \ + # --drive-pacer-min-sleep=10ms \ + # --drive-pacer-burst=1000 \ + # --umask=002 \ + # {{ '--cache-dir=' + rclone_vfs_cache_dir + ' ' if (rclone_vfs_cache_dir | length > 0) else "" }}--syslog \ + # {{ '--bind=' + ansible_default_ipv4.address + ' ' if mounts.ipv4_only else "" }}-v \ + # {{ rclone.remote }}: /mnt/remote + }; + + # TODO: + # rclone_vfs_cache_mount_stop_command: /bin/fusermount -uz /mnt/remote + + # TODO: + # rclone_vfs_cache_mount_start_post_command: |- + # /usr/bin/rclone rc vfs/refresh recursive=true --url http://localhost:5572 _async=true + + # TODO: + # rclone_vfs_refresh_interval: 86400 + # rclone_vfs_refresh_command: |- + # /usr/bin/rclone rc vfs/refresh recursive=true --url http://localhost:5572 _async=true + + ############################################################################# + # TODO: + + # local_mount_branch: "{{ '/mnt/feeder' if (feeder_remote_is_active is defined and feeder_remote_is_active) else '/mnt/local' }}" + + # mount_type: "mergerfs" + + # mergerfs_mount_branches: "{{ local_mount_branch }}=RW:/mnt/remote=NC" + + # mergerfs_mount_service_after: "network-online.target" + + # mergerfs_mount_start_command: |- + # /usr/bin/mergerfs \ + # -o category.create=ff,async_read=true,cache.files=partial \ + # -o dropcacheonclose=true,minfreespace=0,fsname=mergerfs \ + # -o xattr=nosys,statfs=base,statfs_ignore=nc,umask=002,noatime \ + # {{ mergerfs_mount_branches }} /mnt/unionfs + + # mergerfs_mount_stop_command: /bin/fusermount -u /mnt/unionfs + + ################################################################### + + # FIXME: set mode/group + # + fileSystems.${mountPath} = { + # NOTE: Ends with `:`. + # + device = "${rcloneRemote}:"; + fsType = "rclone"; + options = [ + "user" + "noauto" + "_netdev" + "rw" + "allow_other" + "args2env" + "vfs-cache-mode=writes" + "vfs-cache-max-size=1G" + "config=/root/.config/rclone/rclone.conf" + "cache-dir=${cachePath}" + "log-file=${runDir}/rclone.log" + "x-systemd.automount" + "x-systemd.mount-timeout=5" + "x-systemd.idle-timeout=30" + ]; + }; + + environment.systemPackages = [pkgs.rclone]; +}