Merge pull request 'vars: eval finalScript lazy' (#2606) from lazy-finalscript into main
This commit is contained in:
@@ -41,7 +41,6 @@ in
|
|||||||
inherit (generator)
|
inherit (generator)
|
||||||
name
|
name
|
||||||
dependencies
|
dependencies
|
||||||
finalScript
|
|
||||||
validationHash
|
validationHash
|
||||||
migrateFact
|
migrateFact
|
||||||
prompts
|
prompts
|
||||||
|
|||||||
@@ -24,35 +24,37 @@ let
|
|||||||
filePromptNames = attrNames (filterAttrs (_name: prompt: prompt.createFile) config.prompts);
|
filePromptNames = attrNames (filterAttrs (_name: prompt: prompt.createFile) config.prompts);
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
finalScript = mkOptionDefault ''
|
finalScript = mkOptionDefault (
|
||||||
set -eu -o pipefail
|
pkgs.writeScript "generator-${config.name}" ''
|
||||||
|
set -eu -o pipefail
|
||||||
|
|
||||||
export PATH="${makeBinPath config.runtimeInputs}:${pkgs.coreutils}/bin"
|
export PATH="${makeBinPath config.runtimeInputs}:${pkgs.coreutils}/bin"
|
||||||
|
|
||||||
${optionalString (pkgs.stdenv.hostPlatform.isLinux) ''
|
${optionalString (pkgs.stdenv.hostPlatform.isLinux) ''
|
||||||
# prepare sandbox user on platforms where this is supported
|
# prepare sandbox user on platforms where this is supported
|
||||||
mkdir -p /etc
|
mkdir -p /etc
|
||||||
|
|
||||||
cat > /etc/group <<EOF
|
cat > /etc/group <<EOF
|
||||||
root:x:0:
|
root:x:0:
|
||||||
nixbld:!:$(id -g):
|
nixbld:!:$(id -g):
|
||||||
nogroup:x:65534:
|
nogroup:x:65534:
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat > /etc/passwd <<EOF
|
cat > /etc/passwd <<EOF
|
||||||
root:x:0:0:Nix build user:/build:/noshell
|
root:x:0:0:Nix build user:/build:/noshell
|
||||||
nixbld:x:$(id -u):$(id -g):Nix build user:/build:/noshell
|
nixbld:x:$(id -u):$(id -g):Nix build user:/build:/noshell
|
||||||
nobody:x:65534:65534:Nobody:/:/noshell
|
nobody:x:65534:65534:Nobody:/:/noshell
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat > /etc/hosts <<EOF
|
cat > /etc/hosts <<EOF
|
||||||
127.0.0.1 localhost
|
127.0.0.1 localhost
|
||||||
::1 localhost
|
::1 localhost
|
||||||
EOF
|
EOF
|
||||||
''}
|
''}
|
||||||
${promptsToFilesScript filePromptNames}
|
${promptsToFilesScript filePromptNames}
|
||||||
${config.script}
|
${config.script}
|
||||||
'';
|
''
|
||||||
|
);
|
||||||
|
|
||||||
files = genAttrs filePromptNames (_name: { });
|
files = genAttrs filePromptNames (_name: { });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ in
|
|||||||
- all required programs are in PATH
|
- all required programs are in PATH
|
||||||
- sandbox is set up correctly
|
- sandbox is set up correctly
|
||||||
'';
|
'';
|
||||||
type = lib.types.str;
|
type = lib.types.path;
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
internal = true;
|
internal = true;
|
||||||
};
|
};
|
||||||
|
|||||||
25
nixosModules/clanCore/vars/secret/on-machine.nix
Normal file
25
nixosModules/clanCore/vars/secret/on-machine.nix
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
sortedGenerators = lib.toposort (a: b: builtins.elem a.name b.dependencies) (
|
||||||
|
lib.attrValues config.clan.core.vars.generators
|
||||||
|
);
|
||||||
|
generateSecrets = ''
|
||||||
|
${lib.concatStringsSep "\n" (_gen: ''
|
||||||
|
v
|
||||||
|
'') sortedGenerators}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkIf (config.clan.core.vars.settings.secretStore == "on-machine") {
|
||||||
|
environment.systemPackages = [
|
||||||
|
(pkgs.writeShellApplication {
|
||||||
|
text = generateSecrets;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -166,7 +166,11 @@ class Machine:
|
|||||||
generators: dict[str, Any] = clan_vars.get("generators")
|
generators: dict[str, Any] = clan_vars.get("generators")
|
||||||
if generators is None:
|
if generators is None:
|
||||||
return []
|
return []
|
||||||
return [Generator.from_json(gen) for gen in generators.values()]
|
_generators = [Generator.from_json(gen) for gen in generators.values()]
|
||||||
|
for gen in _generators:
|
||||||
|
gen.machine(self)
|
||||||
|
|
||||||
|
return _generators
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def secrets_upload_directory(self) -> str:
|
def secrets_upload_directory(self) -> str:
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def nix_build(flags: list[str], gcroot: Path | None = None) -> list[str]:
|
|||||||
"--print-out-paths",
|
"--print-out-paths",
|
||||||
"--print-build-logs",
|
"--print-build-logs",
|
||||||
*(["--show-trace"] if log.isEnabledFor(logging.DEBUG) else []),
|
*(["--show-trace"] if log.isEnabledFor(logging.DEBUG) else []),
|
||||||
*(["--out-root", str(gcroot)] if gcroot is not None else []),
|
*(["--out-root", str(gcroot)] if gcroot is not None else ["--no-link"]),
|
||||||
*flags,
|
*flags,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from clan_cli.completions import (
|
|||||||
from clan_cli.errors import ClanError
|
from clan_cli.errors import ClanError
|
||||||
from clan_cli.git import commit_files
|
from clan_cli.git import commit_files
|
||||||
from clan_cli.machines.inventory import get_all_machines, get_selected_machines
|
from clan_cli.machines.inventory import get_all_machines, get_selected_machines
|
||||||
from clan_cli.nix import nix_shell
|
from clan_cli.nix import nix_shell, nix_test_store
|
||||||
from clan_cli.vars._types import StoreBase
|
from clan_cli.vars._types import StoreBase
|
||||||
|
|
||||||
from .check import check_vars
|
from .check import check_vars
|
||||||
@@ -40,7 +40,6 @@ class Generator:
|
|||||||
files: list[Var] = field(default_factory=list)
|
files: list[Var] = field(default_factory=list)
|
||||||
share: bool = False
|
share: bool = False
|
||||||
validation: str | None = None
|
validation: str | None = None
|
||||||
final_script: str = ""
|
|
||||||
prompts: list[Prompt] = field(default_factory=list)
|
prompts: list[Prompt] = field(default_factory=list)
|
||||||
dependencies: list[str] = field(default_factory=list)
|
dependencies: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
@@ -62,7 +61,6 @@ class Generator:
|
|||||||
return cls(
|
return cls(
|
||||||
name=data["name"],
|
name=data["name"],
|
||||||
share=data["share"],
|
share=data["share"],
|
||||||
final_script=data["finalScript"],
|
|
||||||
files=[Var.from_json(data["name"], f) for f in data["files"].values()],
|
files=[Var.from_json(data["name"], f) for f in data["files"].values()],
|
||||||
validation=data["validationHash"],
|
validation=data["validationHash"],
|
||||||
dependencies=data["dependencies"],
|
dependencies=data["dependencies"],
|
||||||
@@ -70,8 +68,18 @@ class Generator:
|
|||||||
prompts=[Prompt.from_json(p) for p in data["prompts"].values()],
|
prompts=[Prompt.from_json(p) for p in data["prompts"].values()],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def final_script(self) -> Path:
|
||||||
|
assert self._machine is not None
|
||||||
|
final_script = self._machine.build_nix(
|
||||||
|
f"config.clan.core.vars.generators.{self.name}.finalScript"
|
||||||
|
)
|
||||||
|
return final_script
|
||||||
|
|
||||||
|
|
||||||
def bubblewrap_cmd(generator: str, tmpdir: Path) -> list[str]:
|
def bubblewrap_cmd(generator: str, tmpdir: Path) -> list[str]:
|
||||||
|
test_store = nix_test_store()
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
return nix_shell(
|
return nix_shell(
|
||||||
[
|
[
|
||||||
@@ -81,6 +89,7 @@ def bubblewrap_cmd(generator: str, tmpdir: Path) -> list[str]:
|
|||||||
[
|
[
|
||||||
"bwrap",
|
"bwrap",
|
||||||
"--ro-bind", "/nix/store", "/nix/store",
|
"--ro-bind", "/nix/store", "/nix/store",
|
||||||
|
*(["--ro-bind", str(test_store), str(test_store)] if test_store else []),
|
||||||
"--tmpfs", "/usr/lib/systemd",
|
"--tmpfs", "/usr/lib/systemd",
|
||||||
"--dev", "/dev",
|
"--dev", "/dev",
|
||||||
"--bind", str(tmpdir), str(tmpdir),
|
"--bind", str(tmpdir), str(tmpdir),
|
||||||
@@ -188,7 +197,7 @@ def execute_generator(
|
|||||||
prompt_file.write_text(value)
|
prompt_file.write_text(value)
|
||||||
|
|
||||||
if sys.platform == "linux":
|
if sys.platform == "linux":
|
||||||
cmd = bubblewrap_cmd(generator.final_script, tmpdir)
|
cmd = bubblewrap_cmd(str(generator.final_script), tmpdir)
|
||||||
else:
|
else:
|
||||||
cmd = ["bash", "-c", generator.final_script]
|
cmd = ["bash", "-c", generator.final_script]
|
||||||
run(cmd, RunOpts(env=env))
|
run(cmd, RunOpts(env=env))
|
||||||
@@ -201,7 +210,7 @@ def execute_generator(
|
|||||||
secret_file = tmpdir_out / file.name
|
secret_file = tmpdir_out / file.name
|
||||||
if not secret_file.is_file():
|
if not secret_file.is_file():
|
||||||
msg = f"did not generate a file for '{file.name}' when running the following command:\n"
|
msg = f"did not generate a file for '{file.name}' when running the following command:\n"
|
||||||
msg += generator.final_script
|
msg += str(generator.final_script)
|
||||||
raise ClanError(msg)
|
raise ClanError(msg)
|
||||||
if file.secret:
|
if file.secret:
|
||||||
file_path = secret_vars_store.set(
|
file_path = secret_vars_store.set(
|
||||||
|
|||||||
@@ -858,7 +858,7 @@ def test_stdout_of_generate(
|
|||||||
"my_generator",
|
"my_generator",
|
||||||
regenerate=True,
|
regenerate=True,
|
||||||
)
|
)
|
||||||
assert "Updated" not in caplog.text
|
assert "Updated var" not in caplog.text
|
||||||
assert "hello" in caplog.text
|
assert "hello" in caplog.text
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
with caplog.at_level(logging.INFO):
|
with caplog.at_level(logging.INFO):
|
||||||
|
|||||||
Reference in New Issue
Block a user