mirror of
https://cgit.krebsco.de/krops
synced 2024-11-01 00:39:48 +01:00
Compare commits
9 commits
Author | SHA1 | Date | |
---|---|---|---|
|
a6c7ecd8ba | ||
|
cbc475bdf4 | ||
|
1c524b6727 | ||
|
9c0d53cf44 | ||
|
59aa5d0e41 | ||
|
3ebbfc6261 | ||
|
e5c13343a6 | ||
|
6ee1d00b92 | ||
|
625bd446dd |
6 changed files with 223 additions and 11 deletions
49
README.md
49
README.md
|
@ -6,6 +6,7 @@ krops is a lightweight toolkit to deploy NixOS systems, remotely or locally.
|
||||||
## Some Features
|
## Some Features
|
||||||
|
|
||||||
- store your secrets in [password store](https://www.passwordstore.org/)
|
- store your secrets in [password store](https://www.passwordstore.org/)
|
||||||
|
or [passage](https://github.com/FiloSottile/passage)
|
||||||
- build your systems remotely
|
- build your systems remotely
|
||||||
- minimal overhead (it's basically just `nixos-rebuild switch`!)
|
- minimal overhead (it's basically just `nixos-rebuild switch`!)
|
||||||
- run from custom nixpkgs branch/checkout/fork
|
- run from custom nixpkgs branch/checkout/fork
|
||||||
|
@ -96,7 +97,7 @@ pkgs.krops.writeDeploy "deploy" {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
For more details about the `target` attribute, please check the `mkTarget`
|
For more details about the `target` attribute, please check the `mkTarget`
|
||||||
function in [lib/default.nix](lib/defaults.nix).
|
function in [lib/default.nix](lib/default.nix).
|
||||||
|
|
||||||
### `backup` (optional, defaults to false)
|
### `backup` (optional, defaults to false)
|
||||||
|
|
||||||
|
@ -136,6 +137,29 @@ Create the sentinel file (`/var/src/.populate`) before syncing the new source.
|
||||||
|
|
||||||
Specifies which `nixos-rebuild` operation to perform.
|
Specifies which `nixos-rebuild` operation to perform.
|
||||||
|
|
||||||
|
### `useNixOutputMonitor` (optional, defaults to `"opportunistic"`)
|
||||||
|
|
||||||
|
Specifies when to pipe `nixos-rebuild`'s output to
|
||||||
|
[nom](https://github.com/maralorn/nix-output-monitor).
|
||||||
|
|
||||||
|
Supported values:
|
||||||
|
|
||||||
|
* `"opportunistic"` (default) -
|
||||||
|
Use `nom` only if it is present on the target machine.
|
||||||
|
|
||||||
|
* `"optimistic"` -
|
||||||
|
Use `nom`, assuming it is present on the target machine.
|
||||||
|
|
||||||
|
* `"pessimistic"` -
|
||||||
|
Use `nom` via `nix-shell` on the target machine.
|
||||||
|
|
||||||
|
* `true` -
|
||||||
|
Use `nom`.
|
||||||
|
If it is not present on the target machine, then use it via `nix-shell`.
|
||||||
|
|
||||||
|
* `false` -
|
||||||
|
Don't use `nom`
|
||||||
|
|
||||||
## writeTest
|
## writeTest
|
||||||
|
|
||||||
Very similiar to writeDeploy, but just builds the system on the target without
|
Very similiar to writeDeploy, but just builds the system on the target without
|
||||||
|
@ -275,6 +299,29 @@ Supported attributes:
|
||||||
sub-directory in the password store.
|
sub-directory in the password store.
|
||||||
|
|
||||||
|
|
||||||
|
### `passage`
|
||||||
|
|
||||||
|
The passage source type decrypts files from a local
|
||||||
|
[passage store](https://github.com/FiloSottile/passage)
|
||||||
|
and transfers them to the target using
|
||||||
|
[`rsync`](https://rsync.samba.org/).
|
||||||
|
|
||||||
|
Supported attributes:
|
||||||
|
|
||||||
|
* `dir` -
|
||||||
|
Path to the passage store.
|
||||||
|
For a partial transfer, this may point to a subdirectory.
|
||||||
|
Example: `~/.passage/store/hosts/MYHOSTNAME`
|
||||||
|
|
||||||
|
* `identities_file` (optional) -
|
||||||
|
Path to the identities file.
|
||||||
|
Defaults to `~/.passage/identities`.
|
||||||
|
|
||||||
|
* `age` (optional) -
|
||||||
|
Path of the age binary.
|
||||||
|
Defaults to `age` (absolute path gets resolved using `passage`'s search path.)
|
||||||
|
|
||||||
|
|
||||||
### `pipe`
|
### `pipe`
|
||||||
|
|
||||||
Executes a local command, capture its stdout, and send that as a file to the
|
Executes a local command, capture its stdout, and send that as a file to the
|
||||||
|
|
27
flake.lock
Normal file
27
flake.lock
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1689940971,
|
||||||
|
"narHash": "sha256-397xShPnFqPC59Bmpo3lS+/Aw0yoDRMACGo1+h2VJMo=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "9ca785644d067445a4aa749902b29ccef61f7476",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
29
flake.nix
Normal file
29
flake.nix
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
description = "krops - krebs operations";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, ... }:
|
||||||
|
let
|
||||||
|
supportedSystems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"i686-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"riscv64-linux"
|
||||||
|
];
|
||||||
|
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
lib = forAllSystems (system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
krops = pkgs.callPackage ./pkgs/krops {};
|
||||||
|
populate = pkgs.callPackage ./pkgs/populate {};
|
||||||
|
in {
|
||||||
|
inherit populate;
|
||||||
|
inherit (krops) rebuild runShell withNixOutputMonitor writeCommand writeDeploy writeTest;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
|
@ -39,6 +39,17 @@
|
||||||
default = null;
|
default = null;
|
||||||
type = lib.types.nullOr source-types.pass;
|
type = lib.types.nullOr source-types.pass;
|
||||||
};
|
};
|
||||||
|
passage = lib.mkOption {
|
||||||
|
apply = x:
|
||||||
|
if lib.types.pathname.check x
|
||||||
|
then { dir = x; }
|
||||||
|
else x;
|
||||||
|
default = null;
|
||||||
|
type = lib.types.nullOr (lib.types.oneOf [
|
||||||
|
lib.types.pathname
|
||||||
|
source-types.passage
|
||||||
|
]);
|
||||||
|
};
|
||||||
pipe = lib.mkOption {
|
pipe = lib.mkOption {
|
||||||
apply = x:
|
apply = x:
|
||||||
if lib.types.absolute-pathname.check x
|
if lib.types.absolute-pathname.check x
|
||||||
|
@ -160,6 +171,21 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
passage = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
age = lib.mkOption {
|
||||||
|
default = "age";
|
||||||
|
type = lib.types.pathname;
|
||||||
|
};
|
||||||
|
dir = lib.mkOption {
|
||||||
|
type = lib.types.pathname;
|
||||||
|
};
|
||||||
|
identities_file = lib.mkOption {
|
||||||
|
default = toString ~/.passage/identities;
|
||||||
|
type = lib.types.pathname;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
pipe = lib.types.submodule {
|
pipe = lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
command = lib.mkOption {
|
command = lib.mkOption {
|
||||||
|
|
|
@ -4,16 +4,24 @@ in
|
||||||
|
|
||||||
{ nix, openssh, populate, writers }: rec {
|
{ nix, openssh, populate, writers }: rec {
|
||||||
|
|
||||||
rebuild = args: target:
|
rebuild = {
|
||||||
runShell target {} "nixos-rebuild -I ${lib.escapeShellArg target.path} ${
|
useNixOutputMonitor
|
||||||
lib.concatMapStringsSep " " lib.escapeShellArg args
|
}:
|
||||||
}";
|
args: target:
|
||||||
|
runShell target {}
|
||||||
|
(withNixOutputMonitor target useNixOutputMonitor /* sh */ ''
|
||||||
|
NIX_PATH=${lib.escapeShellArg target.path} \
|
||||||
|
nixos-rebuild ${lib.escapeShellArgs args}
|
||||||
|
'');
|
||||||
|
|
||||||
runShell = target: {
|
runShell = target: {
|
||||||
allocateTTY ? false
|
allocateTTY ? false
|
||||||
}: command:
|
}: command:
|
||||||
let
|
let
|
||||||
command' = if target.sudo then "sudo ${command}" else command;
|
command' = /* sh */ ''
|
||||||
|
${lib.optionalString target.sudo "sudo"} \
|
||||||
|
/bin/sh -c ${lib.escapeShellArg command}
|
||||||
|
'';
|
||||||
in
|
in
|
||||||
if lib.isLocalTarget target
|
if lib.isLocalTarget target
|
||||||
then command'
|
then command'
|
||||||
|
@ -24,7 +32,42 @@ in
|
||||||
(if allocateTTY then "-t" else "-T")
|
(if allocateTTY then "-t" else "-T")
|
||||||
target.extraOptions
|
target.extraOptions
|
||||||
target.host
|
target.host
|
||||||
command'])}
|
command'
|
||||||
|
])}
|
||||||
|
'';
|
||||||
|
|
||||||
|
withNixOutputMonitor = target: mode_: command: let
|
||||||
|
mode =
|
||||||
|
lib.getAttr (lib.typeOf mode_) {
|
||||||
|
bool = lib.toJSON mode_;
|
||||||
|
string = mode_;
|
||||||
|
};
|
||||||
|
in /* sh */ ''
|
||||||
|
printf '# use nix-output-monitor: %s\n' ${lib.escapeShellArg mode} >&2
|
||||||
|
${lib.getAttr mode rec {
|
||||||
|
opportunistic = /* sh */ ''
|
||||||
|
if command -v nom >/dev/null; then
|
||||||
|
${optimistic}
|
||||||
|
else
|
||||||
|
${false}
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
optimistic = /* sh */ ''
|
||||||
|
(${command}) 2>&1 | nom
|
||||||
|
'';
|
||||||
|
pessimistic = /* sh */ ''
|
||||||
|
NIX_PATH=${lib.escapeShellArg target.path} \
|
||||||
|
nix-shell -p nix-output-monitor --run ${lib.escapeShellArg optimistic}
|
||||||
|
'';
|
||||||
|
true = /* sh */ ''
|
||||||
|
if command -v nom >/dev/null; then
|
||||||
|
${optimistic}
|
||||||
|
else
|
||||||
|
${pessimistic}
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
false = command;
|
||||||
|
}}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
writeCommand = name: {
|
writeCommand = name: {
|
||||||
|
@ -51,7 +94,8 @@ in
|
||||||
force ? false,
|
force ? false,
|
||||||
operation ? "switch",
|
operation ? "switch",
|
||||||
source,
|
source,
|
||||||
target
|
target,
|
||||||
|
useNixOutputMonitor ? "opportunistic"
|
||||||
}: let
|
}: let
|
||||||
buildTarget' =
|
buildTarget' =
|
||||||
if buildTarget == null
|
if buildTarget == null
|
||||||
|
@ -65,7 +109,7 @@ in
|
||||||
${lib.optionalString (buildTarget' != target')
|
${lib.optionalString (buildTarget' != target')
|
||||||
(populate { inherit backup force source; target = buildTarget'; })}
|
(populate { inherit backup force source; target = buildTarget'; })}
|
||||||
${populate { inherit backup force source; target = target'; }}
|
${populate { inherit backup force source; target = target'; }}
|
||||||
${rebuild ([
|
${rebuild { inherit useNixOutputMonitor; } ([
|
||||||
operation
|
operation
|
||||||
] ++ lib.optionals crossDeploy [
|
] ++ lib.optionals crossDeploy [
|
||||||
"--no-build-nix"
|
"--no-build-nix"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
with import ../../lib;
|
with import ../../lib;
|
||||||
with shell;
|
with shell;
|
||||||
|
|
||||||
{ coreutils, dash, findutils, git, jq, openssh, pass, rsync, writers }:
|
{ coreutils, dash, findutils, git, jq, openssh, pass, passage, rsync, writers }:
|
||||||
|
|
||||||
let
|
let
|
||||||
check = { force, target }: let
|
check = { force, target }: let
|
||||||
|
@ -119,7 +119,15 @@ let
|
||||||
umask 0077
|
umask 0077
|
||||||
|
|
||||||
if test -e ${quote source.dir}/.git; then
|
if test -e ${quote source.dir}/.git; then
|
||||||
local_pass_info=${quote source.name}\ $(${git}/bin/git -C ${quote source.dir} log -1 --format=%H ${quote source.name})
|
local_pass_info=${quote source.name}\ $(
|
||||||
|
${git}/bin/git -C ${quote source.dir} log -1 --format=%H ${quote source.name}
|
||||||
|
# we append a hash for every symlink, otherwise we would miss updates on
|
||||||
|
# files where the symlink points to
|
||||||
|
${findutils}/bin/find ${quote source.dir}/${quote source.name} -type l \
|
||||||
|
-exec ${coreutils}/bin/realpath {} + |
|
||||||
|
${coreutils}/bin/sort |
|
||||||
|
${findutils}/bin/xargs -r -n 1 ${git}/bin/git -C ${quote source.dir} log -1 --format=%H
|
||||||
|
)
|
||||||
remote_pass_info=$(${runShell target /* sh */ ''
|
remote_pass_info=$(${runShell target /* sh */ ''
|
||||||
cat ${quote target.path}/.pass_info || :
|
cat ${quote target.path}/.pass_info || :
|
||||||
''})
|
''})
|
||||||
|
@ -163,6 +171,37 @@ let
|
||||||
${rsync' target rsyncDefaultConfig /* sh */ "$tmp_dir"}
|
${rsync' target rsyncDefaultConfig /* sh */ "$tmp_dir"}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
pop.passage = target: source: /* sh */ ''
|
||||||
|
set -efu
|
||||||
|
|
||||||
|
export PASSAGE_AGE=${quote source.age}
|
||||||
|
export PASSAGE_DIR=${quote source.dir}
|
||||||
|
export PASSAGE_IDENTITIES_FILE=${quote source.identities_file}
|
||||||
|
|
||||||
|
umask 0077
|
||||||
|
|
||||||
|
tmp_dir=$(${coreutils}/bin/mktemp -dt populate-passage.XXXXXXXX)
|
||||||
|
trap cleanup EXIT
|
||||||
|
cleanup() {
|
||||||
|
rm -fR "$tmp_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
${findutils}/bin/find "$PASSAGE_DIR" -type f -name \*.age -follow |
|
||||||
|
while read -r age_path; do
|
||||||
|
|
||||||
|
rel_name=''${age_path#$PASSAGE_DIR}
|
||||||
|
rel_name=''${rel_name%.age}
|
||||||
|
|
||||||
|
tmp_path=$tmp_dir/$rel_name
|
||||||
|
|
||||||
|
${coreutils}/bin/mkdir -p "$(${coreutils}/bin/dirname "$tmp_path")"
|
||||||
|
${passage}/bin/passage show "$rel_name" > "$tmp_path"
|
||||||
|
${coreutils}/bin/touch -r "$age_path" "$tmp_path"
|
||||||
|
done
|
||||||
|
|
||||||
|
${rsync' target rsyncDefaultConfig /* sh */ "$tmp_dir"}
|
||||||
|
'';
|
||||||
|
|
||||||
pop.pipe = target: source: /* sh */ ''
|
pop.pipe = target: source: /* sh */ ''
|
||||||
${quote source.command} | {
|
${quote source.command} | {
|
||||||
${runShell target /* sh */ "cat > ${quote target.path}"}
|
${runShell target /* sh */ "cat > ${quote target.path}"}
|
||||||
|
|
Loading…
Reference in a new issue