rename deployment address to target address
All checks were successful
checks-impure / test (pull_request) Successful in 1m35s
checks / test (pull_request) Successful in 2m43s

This is a prepares having a build server for deployment
This commit is contained in:
Jörg Thalheim 2024-02-02 11:32:48 +07:00
parent 7daca31db7
commit 3538cf2e46
16 changed files with 69 additions and 43 deletions

View File

@ -5,6 +5,7 @@ let
directory = ../..;
machines = {
test_backup_client = {
clan.networking.targetHost = "client";
imports = [ self.nixosModules.test_backup_client ];
fileSystems."/".device = "/dev/null";
boot.loader.grub.device = "/dev/null";

View File

@ -5,6 +5,7 @@ let
directory = ../..;
machines = {
test_install_machine = {
clan.networking.targetHost = "test_install_machine";
imports = [ self.nixosModules.test_install_machine ];
};
};

View File

@ -88,17 +88,18 @@ $ clan machines install my-machine <target_host>
## Update Your Machines
Clan CLI enables you to remotely update your machines over SSH. This requires setting up a deployment address for each target machine.
Clan CLI enables you to remotely update your machines over SSH. This requires setting up a target address for each target machine.
### Setting the Deployment Address
### Setting the Target Host
Replace `host_or_ip` with the actual hostname or IP address of your target machine:
```shellSession
$ clan config --machine my-machine clan.networking.deploymentAddress root@host_or_ip
$ clan config --machine my-machine clan.networking.targetHost root@host_or_ip
```
_Note: The use of `root@` in the deployment address implies SSH access as the root user. Ensure that the root login is secured and only used when necessary._
_Note: The use of `root@` in the target address implies SSH access as the root user.
Ensure that the root login is secured and only used when necessary._
### Updating Machine Configurations

View File

@ -1,7 +1,7 @@
{ config, lib, ... }:
{
options.clan.networking = {
deploymentAddress = lib.mkOption {
targetHost = lib.mkOption {
description = ''
The target SSH node for deployment.
@ -14,10 +14,27 @@
- user@machine2.example.com
- root@example.com:2222&IdentityFile=/path/to/private/key
'';
type = lib.types.str;
};
buildHost = lib.mkOption {
description = ''
The build SSH node where nixos-rebuild will be executed.
If set to null, the targetHost will be used.
format: user@host:port&SSH_OPTION=SSH_VALUE
examples:
- machine.example.com
- user@machine2.example.com
- root@example.com:2222&IdentityFile=/path/to/private/key
'';
type = lib.types.nullOr lib.types.str;
default = "root@${config.networking.hostName}";
default = null;
};
};
imports = [
(lib.mkRenamedOptionModule [ "clan" "networking" "deploymentAddress" ] [ "clan" "networking" "buildHost" ])
];
config = {
# conflicts with systemd-resolved
networking.useHostResolvConf = false;

View File

@ -19,10 +19,16 @@
the location of the deployment.json file
'';
};
deploymentAddress = lib.mkOption {
deployment.buildHost = lib.mkOption {
type = lib.types.str;
description = ''
the address of the deployment server
the hostname of the build host where nixos-rebuild is run
'';
};
deployment.targetHost = lib.mkOption {
type = lib.types.str;
description = ''
the hostname of the target host to be deployed to
'';
};
secretsUploadDirectory = lib.mkOption {
@ -66,10 +72,10 @@
config = {
system.clan.deployment.data = {
inherit (config.system.clan) secretsModule secretsData;
inherit (config.clan.networking) deploymentAddress;
inherit (config.clan.networking) targetHost buildHost;
inherit (config.clanCore) secretsUploadDirectory;
};
system.clan.deployment.file = pkgs.writeText "deployment.json" (builtins.toJSON config.system.clan.deployment.data);
};
};
}

View File

@ -14,14 +14,12 @@ log = logging.getLogger(__name__)
def install_nixos(machine: Machine, kexec: str | None = None) -> None:
log.info(f"deployment address1: {machine.deployment_info['deploymentAddress']}")
secrets_module = importlib.import_module(machine.secrets_module)
log.info(f"installing {machine.name}")
log.info(f"using secret store: {secrets_module.SecretStore}")
secret_store = secrets_module.SecretStore(machine=machine)
h = machine.host
log.info(f"deployment address2: {machine.deployment_info['deploymentAddress']}")
target_host = f"{h.user or 'root'}@{h.host}"
log.info(f"target host: {target_host}")
@ -77,10 +75,7 @@ def install_command(args: argparse.Namespace) -> None:
kexec=args.kexec,
)
machine = Machine(opts.machine, flake=opts.flake)
machine.get_deployment_info()
machine.deployment_info["deploymentAddress"] = opts.target_host
log.info(f"target host: {opts.target_host}")
log.info(f"deployment address: {machine.deployment_info['deploymentAddress']}")
machine.target_host = opts.target_host
install_nixos(machine, kexec=opts.kexec)

View File

@ -28,8 +28,7 @@ class Machine:
self.eval_cache: dict[str, str] = {}
self.build_cache: dict[str, Path] = {}
if deployment_info is not None:
self.deployment_info = deployment_info
self._deployment_info: None | dict[str, str] = deployment_info
def __str__(self) -> str:
return f"Machine(name={self.name}, flake={self.flake})"
@ -37,29 +36,34 @@ class Machine:
def __repr__(self) -> str:
return str(self)
def get_deployment_info(self) -> None:
self.deployment_info = json.loads(
@property
def deployment_info(self) -> dict[str, str]:
if self._deployment_info is not None:
return self._deployment_info
self._deployment_info = json.loads(
self.build_nix("config.system.clan.deployment.file").read_text()
)
print(f"self_deployment_info: {self.deployment_info}")
return self._deployment_info
@property
def deployment_address(self) -> str:
if not hasattr(self, "deployment_info"):
self.get_deployment_info()
return self.deployment_info["deploymentAddress"]
def target_host(self) -> str:
# deploymentAddress is deprecated.
return (
self.deployment_info.get("targetHost")
or self.deployment_info["deploymentAddress"]
)
@target_host.setter
def target_host(self, value: str) -> None:
self.deployment_info["targetHost"] = value
@property
def secrets_module(self) -> str:
if not hasattr(self, "deployment_info"):
self.get_deployment_info()
print(f"self_deployment_info2: {self.deployment_info}")
return self.deployment_info["secretsModule"]
@property
def secrets_data(self) -> dict:
if not hasattr(self, "deployment_info"):
self.get_deployment_info()
if self.deployment_info["secretsData"]:
try:
return json.loads(Path(self.deployment_info["secretsData"]).read_text())
@ -72,8 +76,6 @@ class Machine:
@property
def secrets_upload_directory(self) -> str:
if not hasattr(self, "deployment_info"):
self.get_deployment_info()
return self.deployment_info["secretsUploadDirectory"]
@property
@ -90,7 +92,7 @@ class Machine:
@property
def host(self) -> Host:
return parse_deployment_address(
self.name, self.deployment_address, meta={"machine": self}
self.name, self.target_host, meta={"machine": self}
)
def eval_nix(self, attr: str, refresh: bool = False) -> str:

View File

@ -91,7 +91,7 @@ def get_all_machines(clan_dir: Path) -> HostGroup:
# very hacky. would be better to do a MachinesGroup instead
host = parse_deployment_address(
name,
machine_data["deploymentAddress"],
machine_data.get("targetHost") or machine_data.get("deploymentAddress"),
meta={
"machine": Machine(
name=name, flake=clan_dir, deployment_info=machine_data
@ -116,7 +116,7 @@ def update(args: argparse.Namespace) -> None:
raise ClanError("Could not find clan flake toplevel directory")
if len(args.machines) == 1 and args.target_host is not None:
machine = Machine(name=args.machines[0], flake=args.flake)
machine.deployment_info["deploymentAddress"] = args.target_host
machine.target_host = args.target_host
host = parse_deployment_address(
args.machines[0],
args.target_host,

View File

@ -1,5 +1,5 @@
{ lib, ... }: {
clan.networking.deploymentAddress = "__CLAN_DEPLOYMENT_ADDRESS__";
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version;
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
clanCore.secretsUploadDirectory = "__CLAN_SOPS_KEY_DIR__";

View File

@ -1,5 +1,5 @@
{ lib, ... }: {
clan.networking.deploymentAddress = "__CLAN_DEPLOYMENT_ADDRESS__";
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version;
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
clanCore.secretsUploadDirectory = "__CLAN_SOPS_KEY_DIR__";

View File

@ -1,5 +1,5 @@
{ lib, ... }: {
clan.networking.deploymentAddress = "__CLAN_DEPLOYMENT_ADDRESS__";
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version;
clan.virtualisation.graphics = false;

View File

@ -12,7 +12,7 @@
clanName = "test_flake_with_core";
machines = {
vm1 = { lib, ... }: {
clan.networking.deploymentAddress = "__CLAN_DEPLOYMENT_ADDRESS__";
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version;
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
clanCore.secretsUploadDirectory = "__CLAN_SOPS_KEY_DIR__";
@ -32,7 +32,7 @@
};
};
vm2 = { lib, ... }: {
clan.networking.deploymentAddress = "__CLAN_DEPLOYMENT_ADDRESS__";
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version;
sops.age.keyFile = "__CLAN_SOPS_KEY_PATH__";
clanCore.secretsUploadDirectory = "__CLAN_SOPS_KEY_DIR__";

View File

@ -12,7 +12,7 @@
clanName = "test_flake_with_core_and_pass";
machines = {
vm1 = { lib, ... }: {
clan.networking.deploymentAddress = "__CLAN_DEPLOYMENT_ADDRESS__";
clan.networking.targetHost = "__CLAN_TARGET_ADDRESS__";
system.stateVersion = lib.version;
clanCore.secretStore = "password-store";
clanCore.secretsUploadDirectory = lib.mkForce "__CLAN_SOPS_KEY_DIR__/secrets";

View File

@ -60,7 +60,7 @@ def test_upload_secret(
flake = test_flake_with_core_and_pass.path.joinpath("flake.nix")
host = host_group.hosts[0]
addr = f"{host.user}@{host.host}:{host.port}?StrictHostKeyChecking=no&UserKnownHostsFile=/dev/null&IdentityFile={host.key}"
new_text = flake.read_text().replace("__CLAN_DEPLOYMENT_ADDRESS__", addr)
new_text = flake.read_text().replace("__CLAN_TARGET_ADDRESS__", addr)
flake.write_text(new_text)
cli.run(["secrets", "upload", "vm1"])
zerotier_identity_secret = (

View File

@ -52,7 +52,7 @@ def test_secrets_upload(
flake = test_flake_with_core.path.joinpath("flake.nix")
host = host_group.hosts[0]
addr = f"{host.user}@{host.host}:{host.port}?StrictHostKeyChecking=no&UserKnownHostsFile=/dev/null&IdentityFile={host.key}"
new_text = flake.read_text().replace("__CLAN_DEPLOYMENT_ADDRESS__", addr)
new_text = flake.read_text().replace("__CLAN_TARGET_ADDRESS__", addr)
flake.write_text(new_text)
cli.run(["--flake", str(test_flake_with_core.path), "secrets", "upload", "vm1"])

View File

@ -154,7 +154,10 @@ def test_vm_persistence(
),
)
),
clan=dict(virtualisation=dict(graphics=False)),
clan=dict(
virtualisation=dict(graphics=False),
networking=dict(targetHost="client"),
),
)
),
)