Merge pull request 'vm_secrets' (#401) from vm_secrets into main
All checks were successful
checks-impure / test (push) Successful in 21s
checks / test (push) Successful in 31s
assets1 / test (push) Successful in 6s

This commit is contained in:
clan-bot 2023-10-04 16:15:54 +00:00
commit d015e3ff60
5 changed files with 62 additions and 9 deletions

View File

@ -3,6 +3,14 @@ let
vmConfig = extendModules {
modules = [
(modulesPath + "/virtualisation/qemu-vm.nix")
{
virtualisation.fileSystems.${config.clanCore.secretsUploadDirectory} = lib.mkForce {
device = "secrets";
fsType = "9p";
neededForBoot = true;
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
};
}
];
};
in
@ -52,6 +60,8 @@ in
toplevel = vmConfig.config.system.build.toplevel;
regInfo = (pkgs.closureInfo { rootPaths = vmConfig.config.virtualisation.additionalPaths; });
inherit (config.clan.virtualisation) memorySize cores graphics;
generateSecrets = config.system.clan.generateSecrets;
uploadSecrets = config.system.clan.uploadSecrets;
});
};

View File

@ -8,7 +8,7 @@ import sys
import threading
import traceback
from enum import Enum
from typing import Any, Iterator, Type, TypeVar
from typing import Any, Iterator, Optional, Type, TypeVar
from uuid import UUID, uuid4
from .errors import ClanError
@ -30,7 +30,7 @@ class Command:
self._output.put(None)
self.done = True
def run(self, cmd: list[str]) -> None:
def run(self, cmd: list[str], env: Optional[dict[str, str]] = None) -> None:
self.running = True
self.log.debug(f"Running command: {shlex.join(cmd)}")
self.p = subprocess.Popen(
@ -38,6 +38,7 @@ class Command:
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
env=env,
)
assert self.p.stdout is not None and self.p.stderr is not None
os.set_blocking(self.p.stdout.fileno(), False)

View File

@ -1,32 +1,36 @@
import argparse
import asyncio
import json
import os
import shlex
import sys
import tempfile
from pathlib import Path
from typing import Iterator
from uuid import UUID
from ..dirs import get_clan_flake_toplevel
from ..nix import nix_build, nix_shell
from ..nix import nix_build, nix_config, nix_shell
from ..task_manager import BaseTask, Command, create_task
from .inspect import VmConfig, inspect_vm
class BuildVmTask(BaseTask):
def __init__(self, uuid: UUID, vm: VmConfig) -> None:
super().__init__(uuid, num_cmds=4)
super().__init__(uuid, num_cmds=6)
self.vm = vm
def get_vm_create_info(self, cmds: Iterator[Command]) -> dict:
config = nix_config()
system = config["system"]
clan_dir = self.vm.flake_url
machine = self.vm.flake_attr
cmd = next(cmds)
cmd.run(
nix_build(
[
# f'{clan_dir}#clanInternals.machines."{system}"."{machine}".config.clan.virtualisation.createJSON' # TODO use this
f'{clan_dir}#nixosConfigurations."{machine}".config.system.clan.vm.create'
f'{clan_dir}#clanInternals.machines."{system}"."{machine}".config.system.clan.vm.create'
]
)
)
@ -48,8 +52,29 @@ class BuildVmTask(BaseTask):
tmpdir = Path(tmpdir_)
xchg_dir = tmpdir / "xchg"
xchg_dir.mkdir()
secrets_dir = tmpdir / "secrets"
secrets_dir.mkdir()
disk_img = f"{tmpdir_}/disk.img"
env = os.environ.copy()
env["CLAN_DIR"] = str(self.vm.flake_url)
env["PYTHONPATH"] = str(
":".join(sys.path)
) # TODO do this in the clanCore module
env["SECRETS_DIR"] = str(secrets_dir)
cmd = next(cmds)
cmd.run(
[vm_config["generateSecrets"]],
env=env,
)
cmd = next(cmds)
cmd.run(
[vm_config["uploadSecrets"]],
env=env,
)
cmd = next(cmds)
cmd.run(
nix_shell(
@ -97,6 +122,7 @@ class BuildVmTask(BaseTask):
"-virtfs", "local,path=/nix/store,security_model=none,mount_tag=nix-store",
"-virtfs", f"local,path={xchg_dir},security_model=none,mount_tag=shared",
"-virtfs", f"local,path={xchg_dir},security_model=none,mount_tag=xchg",
"-virtfs", f"local,path={secrets_dir},security_model=none,mount_tag=secrets",
"-drive", f'cache=writeback,file={disk_img},format=raw,id=drive1,if=none,index=1,werror=report',
"-device", "virtio-blk-pci,bootindex=1,drive=drive1,serial=root",
"-device", "virtio-keyboard",

View File

@ -6,7 +6,7 @@ from pydantic import BaseModel
from ..async_cmd import run
from ..dirs import get_clan_flake_toplevel
from ..nix import nix_eval
from ..nix import nix_config, nix_eval
class VmConfig(BaseModel):
@ -19,9 +19,11 @@ class VmConfig(BaseModel):
async def inspect_vm(flake_url: str, flake_attr: str) -> VmConfig:
config = nix_config()
system = config["system"]
cmd = nix_eval(
[
f"{flake_url}#nixosConfigurations.{json.dumps(flake_attr)}.config.system.clan.vm.config"
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.system.clan.vm.config'
]
)
stdout = await run(cmd)

View File

@ -1,10 +1,15 @@
import os
from pathlib import Path
from typing import TYPE_CHECKING
import pytest
from api import TestClient
from cli import Cli
from httpx import SyncByteStream
if TYPE_CHECKING:
from age_keys import KeyPair
@pytest.mark.impure
def test_inspect(api: TestClient, test_flake_with_core: Path) -> None:
@ -33,7 +38,16 @@ def test_incorrect_uuid(api: TestClient) -> None:
@pytest.mark.skipif(not os.path.exists("/dev/kvm"), reason="Requires KVM")
@pytest.mark.impure
def test_create(api: TestClient, test_flake_with_core: Path) -> None:
def test_create(
api: TestClient,
monkeypatch: pytest.MonkeyPatch,
test_flake_with_core: Path,
age_keys: list["KeyPair"],
) -> None:
monkeypatch.chdir(test_flake_with_core)
monkeypatch.setenv("SOPS_AGE_KEY", age_keys[0].privkey)
cli = Cli()
cli.run(["secrets", "users", "add", "user1", age_keys[0].pubkey])
print(f"flake_url: {test_flake_with_core} ")
response = api.post(
"/api/vms/create",