From 2acec2c1eb7a95621aac30be790a3a0ce8e61499 Mon Sep 17 00:00:00 2001 From: Sean Buckley Date: Thu, 9 Sep 2021 23:46:13 -0400 Subject: [PATCH] add morph library --- flake.nix | 1 + lib/morph.nix | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 lib/morph.nix diff --git a/flake.nix b/flake.nix index 38df402..6344f0e 100644 --- a/flake.nix +++ b/flake.nix @@ -55,6 +55,7 @@ lib = { getHosts = import lib/hosts.nix; + morphHosts = import lib/morph.nix; forAllSystems = f: builtins.listToAttrs (map (name: { inherit name; value = f name; }) (builtins.attrNames nixpkgs.legacyPackages) diff --git a/lib/morph.nix b/lib/morph.nix new file mode 100644 index 0000000..553da63 --- /dev/null +++ b/lib/morph.nix @@ -0,0 +1,111 @@ +{ nixpkgs +, nixosConfigurations +, path +, flake +, extraMorphModules ? [ ] +}: + +let + + allHostsMeta = builtins.mapAttrs + (n: v: import (path + "/${n}")) + nixosConfigurations; + +in +{ + morph-entrypoint = system: + let + globalHealthChecks.cmd = [ + { + cmd = [ "nixos-check-reboot" ]; + description = "Check for pending reboot"; + } + { + cmd = [ "systemctl is-system-running" ]; + description = "Check services are running"; + } + ]; + + getConfig = name: value: { ... }: { + imports = extraMorphModules ++ nixosConfigurations.${name}.extraArgs.modules; + config = nixpkgs.lib.mkMerge [ + { inherit (allHostsMeta.${name}) deployment; } + { deployment.healthChecks = globalHealthChecks; } + ]; + }; + + in + { network.pkgs = nixpkgs.legacyPackages.${system}; } // + builtins.mapAttrs getConfig nixosConfigurations; + + + + packages = system: + let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (nixpkgs.lib) concatMapStrings; + + sh = (scriptBody: pkgs.writeShellScriptBin "run" '' + set -e + die() { echo "$*" 1>&2 ; exit 1; } + ${scriptBody} + ''); + + sshKnownHostsTxt = pkgs.writeText "known_hosts" (concatMapStrings + (hostName: + let m = allHostsMeta.${hostName}; + in concatMapStrings (key: "${m.deployment.targetHost} ${key}\n") m.sshPublicKeys + ) + (builtins.attrNames nixosConfigurations) + ); + + sshConfig = pkgs.writeText "ssh_config" '' + StrictHostKeyChecking yes + GlobalKnownHostsFile ${sshKnownHostsTxt} + ''; + + in + rec { + + morph-config = nixpkgs.legacyPackages.${system}.writeText "morph.nix" '' + let flake = builtins.getFlake "${flake}"; + in flake.morph-entrypoint builtins.currentSystem + ''; + + morph = pkgs.runCommand "morph" { } '' + mkdir -p $out/bin + . ${pkgs.makeWrapper}/nix-support/setup-hook + makeWrapper ${pkgs.morph}/bin/morph $out/bin/morph \ + --set SSH_USER root \ + --set SSH_CONFIG_FILE ${sshConfig} + ''; + + deploy = sh '' + exec "${morph}/bin/morph" deploy ${morph-config} "$@" + ''; + + push = sh '' + exec "${morph}/bin/morph" push ${morph-config} "$@" + ''; + + ssh = sh '' + [ -n "$1" ] || die "Specify a host" + [ -d "${path}/$1" ] || die "Invalid host" + ip="$(nix eval --raw --file "${path}/$1" deployment.targetHost)" + shift + exec ssh -F"${sshConfig}" "$ip" "$@" + ''; + + check = sh '' + res="$( ${morph}/bin/morph build ${morph-config} )" + ${morph}/bin/morph check-health ${morph-config} + echo -e "\nUpdate checks:" + for hostname in $( ls "$res" ); do + echo " $hostname" + diff \ + <('${ssh}/bin/run' "$hostname" readlink /run/current-system) \ + <(readlink "$res/$hostname") || true + done + ''; + }; +}