clan-cli: add simple flash command
This commit is contained in:
@@ -69,6 +69,12 @@
|
|||||||
json metadata about the vm
|
json metadata about the vm
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
iso = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
A generated iso of the machine for the flash command
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
description = ''
|
description = ''
|
||||||
@@ -84,6 +90,5 @@
|
|||||||
inherit (config.clanCore) secretsUploadDirectory;
|
inherit (config.clanCore) secretsUploadDirectory;
|
||||||
};
|
};
|
||||||
system.clan.deployment.file = pkgs.writeText "deployment.json" (builtins.toJSON config.system.clan.deployment.data);
|
system.clan.deployment.file = pkgs.writeText "deployment.json" (builtins.toJSON config.system.clan.deployment.data);
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
clanCore.imports = [
|
clanCore.imports = [
|
||||||
inputs.sops-nix.nixosModules.sops
|
inputs.sops-nix.nixosModules.sops
|
||||||
./clanCore
|
./clanCore
|
||||||
|
./iso
|
||||||
({ pkgs, lib, ... }: {
|
({ pkgs, lib, ... }: {
|
||||||
clanCore.clanPkgs = lib.mkDefault self.packages.${pkgs.hostPlatform.system};
|
clanCore.clanPkgs = lib.mkDefault self.packages.${pkgs.hostPlatform.system};
|
||||||
})
|
})
|
||||||
|
|||||||
84
nixosModules/iso/default.nix
Normal file
84
nixosModules/iso/default.nix
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
{ config, extendModules, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
# Generates a fileSystems entry for bind mounting a given state folder path
|
||||||
|
# It binds directories from /var/clanstate/{some-path} to /{some-path}.
|
||||||
|
# As a result, all state paths will be persisted across reboots, because
|
||||||
|
# the state folder is mounted from the host system.
|
||||||
|
mkBindMount = path: {
|
||||||
|
name = path;
|
||||||
|
value = {
|
||||||
|
device = "/var/clanstate/${path}";
|
||||||
|
options = [ "bind" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Flatten the list of state folders into a single list
|
||||||
|
stateFolders = lib.flatten (
|
||||||
|
lib.mapAttrsToList
|
||||||
|
(_item: attrs: attrs.folders)
|
||||||
|
config.clanCore.state
|
||||||
|
);
|
||||||
|
|
||||||
|
# A module setting up bind mounts for all state folders
|
||||||
|
stateMounts = {
|
||||||
|
virtualisation.fileSystems =
|
||||||
|
lib.listToAttrs
|
||||||
|
(map mkBindMount stateFolders);
|
||||||
|
};
|
||||||
|
|
||||||
|
isoModule = { config, ... }: {
|
||||||
|
imports = [
|
||||||
|
stateMounts
|
||||||
|
];
|
||||||
|
options.clan.iso.disko = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
freeformType = (pkgs.formats.json { }).type;
|
||||||
|
};
|
||||||
|
default = {
|
||||||
|
disk = {
|
||||||
|
iso = {
|
||||||
|
type = "disk";
|
||||||
|
imageSize = "10G"; # TODO add auto image size in disko
|
||||||
|
content = {
|
||||||
|
type = "gpt";
|
||||||
|
partitions = {
|
||||||
|
boot = {
|
||||||
|
size = "1M";
|
||||||
|
type = "EF02"; # for grub MBR
|
||||||
|
};
|
||||||
|
ESP = {
|
||||||
|
size = "100M";
|
||||||
|
type = "EF00";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "vfat";
|
||||||
|
mountpoint = "/boot";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
root = {
|
||||||
|
size = "100%";
|
||||||
|
content = {
|
||||||
|
type = "filesystem";
|
||||||
|
format = "ext4";
|
||||||
|
mountpoint = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config.disko.devices = lib.mkOverride 51 config.clan.iso.disko;
|
||||||
|
};
|
||||||
|
|
||||||
|
isoConfig = extendModules {
|
||||||
|
modules = [ isoModule ];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = {
|
||||||
|
# for clan vm create
|
||||||
|
system.clan.iso = isoConfig.config.system.build.diskoImages;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ from pathlib import Path
|
|||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from . import backups, config, flakes, history, machines, secrets, vms
|
from . import backups, config, flakes, flash, history, machines, secrets, vms
|
||||||
from .custom_logger import setup_logging
|
from .custom_logger import setup_logging
|
||||||
from .dirs import get_clan_flake_toplevel
|
from .dirs import get_clan_flake_toplevel
|
||||||
from .errors import ClanCmdError, ClanError
|
from .errors import ClanCmdError, ClanError
|
||||||
@@ -102,6 +102,11 @@ def create_parser(prog: str | None = None) -> argparse.ArgumentParser:
|
|||||||
parser_history = subparsers.add_parser("history", help="manage history")
|
parser_history = subparsers.add_parser("history", help="manage history")
|
||||||
history.register_parser(parser_history)
|
history.register_parser(parser_history)
|
||||||
|
|
||||||
|
parser_flash = subparsers.add_parser(
|
||||||
|
"flash", help="flash machines to usb sticks or into isos"
|
||||||
|
)
|
||||||
|
flash.register_parser(parser_flash)
|
||||||
|
|
||||||
if argcomplete:
|
if argcomplete:
|
||||||
argcomplete.autocomplete(parser)
|
argcomplete.autocomplete(parser)
|
||||||
|
|
||||||
|
|||||||
62
pkgs/clan-cli/clan_cli/flash.py
Normal file
62
pkgs/clan-cli/clan_cli/flash.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import argparse
|
||||||
|
import importlib
|
||||||
|
import logging
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
|
from .machines.machines import Machine
|
||||||
|
from .secrets.generate import generate_secrets
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def flash_machine(machine: Machine, device: str | None = None) -> None:
|
||||||
|
secrets_module = importlib.import_module(machine.secrets_module)
|
||||||
|
secret_store = secrets_module.SecretStore(machine=machine)
|
||||||
|
|
||||||
|
generate_secrets(machine)
|
||||||
|
|
||||||
|
with TemporaryDirectory() as tmpdir_:
|
||||||
|
tmpdir = Path(tmpdir_)
|
||||||
|
upload_dir_ = machine.secrets_upload_directory
|
||||||
|
|
||||||
|
if upload_dir_.startswith("/"):
|
||||||
|
upload_dir_ = upload_dir_[1:]
|
||||||
|
upload_dir = tmpdir / upload_dir_
|
||||||
|
upload_dir.mkdir(parents=True)
|
||||||
|
secret_store.upload(upload_dir)
|
||||||
|
|
||||||
|
fs_image = machine.build_nix("config.system.clan.iso")
|
||||||
|
print(fs_image)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FlashOptions:
|
||||||
|
flake: Path
|
||||||
|
machine: str
|
||||||
|
device: str | None
|
||||||
|
|
||||||
|
|
||||||
|
def flash_command(args: argparse.Namespace) -> None:
|
||||||
|
opts = FlashOptions(
|
||||||
|
flake=args.flake,
|
||||||
|
machine=args.machine,
|
||||||
|
device=args.device,
|
||||||
|
)
|
||||||
|
machine = Machine(opts.machine, flake=opts.flake)
|
||||||
|
flash_machine(machine, device=opts.device)
|
||||||
|
|
||||||
|
|
||||||
|
def register_parser(parser: argparse.ArgumentParser) -> None:
|
||||||
|
parser.add_argument(
|
||||||
|
"machine",
|
||||||
|
type=str,
|
||||||
|
help="machine to install",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--device",
|
||||||
|
type=str,
|
||||||
|
help="device to flash the system to",
|
||||||
|
)
|
||||||
|
parser.set_defaults(func=flash_command)
|
||||||
Reference in New Issue
Block a user