Add 'deployment.keys.<key>.keyCommand' support

Fixes #3.
This commit is contained in:
Zhaofeng Li 2021-02-10 18:08:47 -08:00
parent ce9f639a53
commit 52622ecd27
2 changed files with 36 additions and 8 deletions

View file

@ -103,7 +103,7 @@ let
text = lib.mkOption { text = lib.mkOption {
description = '' description = ''
Content of the key. Content of the key.
Either `keyFile` or `text` must be set. One of `text`, `keyCommand` and `keyFile` must be set.
''; '';
default = null; default = null;
type = types.nullOr types.str; type = types.nullOr types.str;
@ -111,12 +111,22 @@ let
keyFile = lib.mkOption { keyFile = lib.mkOption {
description = '' description = ''
Path of the local file to read the key from. Path of the local file to read the key from.
Either `keyFile` or `text` must be set. One of `text`, `keyCommand` and `keyFile` must be set.
''; '';
default = null; default = null;
apply = value: if value == null then null else toString value; apply = value: if value == null then null else toString value;
type = types.nullOr types.path; type = types.nullOr types.path;
}; };
keyCommand = lib.mkOption {
description = ''
Command to run to generate the key.
One of `text`, `keyCommand` and `keyFile` must be set.
'';
default = null;
type = let
nonEmptyList = types.addCheck (types.listOf types.str) (l: length l > 0);
in types.nullOr nonEmptyList;
};
destDir = lib.mkOption { destDir = lib.mkOption {
description = '' description = ''
Destination directory on the host. Destination directory on the host.
@ -190,11 +200,13 @@ let
else pkgs; else pkgs;
evalConfig = import (npkgs.path + "/nixos/lib/eval-config.nix"); evalConfig = import (npkgs.path + "/nixos/lib/eval-config.nix");
assertionModule = { config, ... }: { assertionModule = { config, ... }: {
assertions = lib.mapAttrsToList (key: opts: { assertions = lib.mapAttrsToList (key: opts: let
assertion = (opts.text == null) != (opts.keyFile == null); nonNulls = l: filter (x: x != null) l;
in {
assertion = length (nonNulls [opts.text opts.keyCommand opts.keyFile]) == 1;
message = message =
let prefix = "${name}.deployment.keys.${key}"; let prefix = "${name}.deployment.keys.${key}";
in "Exactly one of `${prefix}.text` and `${prefix}.keyFile` must be set."; in "Exactly one of `${prefix}.text`, `${prefix}.keyCommand` and `${prefix}.keyFile` must be set.";
}) config.deployment.keys; }) config.deployment.keys;
}; };
in evalConfig { in evalConfig {

View file

@ -1,11 +1,16 @@
use std::{ use std::{
io::{self, Cursor}, io::{self, Cursor},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Stdio,
}; };
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::{fs::File, io::AsyncRead}; use tokio::{
fs::File,
io::AsyncRead,
process::Command,
};
use validator::{Validate, ValidationError}; use validator::{Validate, ValidationError};
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -44,8 +49,19 @@ impl Key {
KeySource::Text(content) => { KeySource::Text(content) => {
Ok(Box::new(Cursor::new(content))) Ok(Box::new(Cursor::new(content)))
} }
KeySource::Command(_command) => { KeySource::Command(command) => {
todo!("Implement keyCommand support") let pathname = &command[0];
let argv = &command[1..];
let stdout = Command::new(pathname)
.args(argv)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()?
.stdout.take().unwrap();
Ok(Box::new(stdout))
} }
KeySource::File(path) => { KeySource::File(path) => {
Ok(Box::new(File::open(path).await?)) Ok(Box::new(File::open(path).await?))