Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Configurable nix options and substitute-on-destination #83

Merged
merged 3 commits into from
Oct 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,38 @@ Health checks will be repeated until success, and the interval can be configured

It is currently possible to have expressions like `"test \"$(systemctl list-units --failed --no-legend --no-pager |wc -l)\" -eq 0"` (count number of failed systemd units, fail if non-zero) as the first argument in a cmd-healthcheck. This works, but is discouraged, and might break at any time.

### Advanced configuration

**nix.conf-options:** The "network"-attrset supports a sub-attrset named "nixConfig". Options configured here will pass `--option <name> <value>` to all nix commands.
Note: these options apply to an entire deployment and are *not* configurable on per-host basis.
The default is an empty set, meaning that the nix configuration is inherited from the build environment. See `man nix.conf`.

**special deployment options:**

(per-host granularity)

`buildOnly` makes morph skip the "push" and "switch" steps for the given host, even if "morph deploy" or "morph push" is executed. (default: false)

`substituteOnDestination` Sets the `--substitute-on-destination` flag on nix copy, allowing for the deployment target to use substitutes. See `nix copy --help`. (default: false)


Example usage of `nixConfig` and deployment module options:
```
network = {
nixConfig = {
"extra-sandbox-paths" = "/foo/bar";
};
};

machine1 = { ... }: {
deployment.buildOnly = true;
};

machine2 = { ... }: {
deployment.substituteOnDestination = true;
};
```


## Hacking morph

Expand Down
5 changes: 4 additions & 1 deletion data/eval-machines.nix
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,12 @@ rec {

machines =
flip mapAttrs nodes (n: v': let v = scrubOptionValue v'; in
{ inherit (v.config.deployment) targetHost secrets healthChecks buildOnly;
{ inherit (v.config.deployment) targetHost secrets healthChecks buildOnly substituteOnDestination;
name = n;
nixosRelease = v.config.system.nixos.release or (removeSuffix v.config.system.nixos.version.suffix v.config.system.nixos.version);
nixConfig = mapAttrs
(n: v: if builtins.isString v then v else throw "nix option '${n}' must have a string typed value")
(network'.network.nixConfig or {});
}
);

Expand Down
10 changes: 10 additions & 0 deletions data/options.nix
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ in
'';
};

substituteOnDestination = mkOption {
type = bool;
default = false;
description = ''
Sets the `--substitute-on-destination` flag on nix copy,
allowing for the deployment target to use substitutes.
See `nix copy --help`.
'';
};

secrets = mkOption {
default = {};
example = {
Expand Down
73 changes: 48 additions & 25 deletions nix/nix.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@ import (
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"syscall"
"time"
"path"
)

type Host struct {
HealthChecks healthchecks.HealthChecks
Name string
NixosRelease string
TargetHost string
Secrets map[string]secrets.Secret
BuildOnly bool
HealthChecks healthchecks.HealthChecks
Name string
NixosRelease string
TargetHost string
Secrets map[string]secrets.Secret
BuildOnly bool
SubstituteOnDestination bool
NixConfig map[string]string
}

type NixContext struct {
Expand Down Expand Up @@ -142,26 +144,28 @@ func (ctx *NixContext) BuildMachines(deploymentPath string, hosts []Host, nixArg

resultLinkPath := filepath.Join(path.Dir(deploymentPath), ".gcroots", path.Base(deploymentPath))
if ctx.KeepGCRoot {
if err = os.MkdirAll(path.Dir(resultLinkPath), 0755) ; err != nil {
ctx.KeepGCRoot = false;
fmt.Fprintf(os.Stderr, "Unable to create GC root, skipping: %s", err)
}
if err = os.MkdirAll(path.Dir(resultLinkPath), 0755); err != nil {
ctx.KeepGCRoot = false
fmt.Fprintf(os.Stderr, "Unable to create GC root, skipping: %s", err)
}
}
if ! ctx.KeepGCRoot {
// create tmp dir for result link
tmpdir, err := ioutil.TempDir("", "morph-")
if err != nil {
return "", err
}
defer os.Remove(tmpdir)
resultLinkPath = filepath.Join(tmpdir, "result")
if !ctx.KeepGCRoot {
// create tmp dir for result link
tmpdir, err := ioutil.TempDir("", "morph-")
if err != nil {
return "", err
}
defer os.Remove(tmpdir)
resultLinkPath = filepath.Join(tmpdir, "result")
}
args := []string{ctx.EvalMachines,
"-A", "machines",
"--arg", "networkExpr", deploymentPath,
"--arg", "names", hostsArg,
"--out-link", resultLinkPath}

args = append(args, mkOptions(hosts[0])...)

if len(nixArgs) > 0 {
args = append(args, nixArgs...)
}
Expand All @@ -176,8 +180,8 @@ func (ctx *NixContext) BuildMachines(deploymentPath string, hosts []Host, nixArg
}

cmd := exec.Command("nix-build", args...)
if ! ctx.KeepGCRoot {
defer os.Remove(resultLinkPath)
if !ctx.KeepGCRoot {
defer os.Remove(resultLinkPath)
}

// show process output on attached stdout/stderr
Expand All @@ -200,6 +204,16 @@ func (ctx *NixContext) BuildMachines(deploymentPath string, hosts []Host, nixArg
return
}

func mkOptions(host Host) []string {
var options = make([]string, 0)
for k, v := range host.NixConfig {
options = append(options, "--option")
options = append(options, k)
options = append(options, v)
}
return options
}

func GetNixSystemPath(host Host, resultPath string) (string, error) {
return os.Readlink(filepath.Join(resultPath, host.Name))
}
Expand Down Expand Up @@ -235,14 +249,23 @@ func Push(ctx *ssh.SSHContext, host Host, paths ...string) (err error) {
keyArg = "?ssh-key=" + ctx.IdentityFile
}
if ctx.SkipHostKeyCheck {
env = append(env, fmt.Sprintf("NIX_SSHOPTS=%s","-o StrictHostkeyChecking=No -o UserKnownHostsFile=/dev/null"))
env = append(env, fmt.Sprintf("NIX_SSHOPTS=%s", "-o StrictHostkeyChecking=No -o UserKnownHostsFile=/dev/null"))
}

options := mkOptions(host)
for _, path := range paths {
cmd := exec.Command(
"nix", "copy",
args := []string{
"copy",
path,
"--to", "ssh://"+userArg+host.TargetHost+keyArg,
"--to", "ssh://" + userArg + host.TargetHost + keyArg,
}
args = append(args, options...)
if host.SubstituteOnDestination {
args = append(args, "--substitute-on-destination")
}

cmd := exec.Command(
"nix", args...,
)
cmd.Env = env

Expand Down