{ pkgs ? (import (import ./nixpkgs-path.nix) {}), ... }: let vault_addr = "http://vault:8200"; in with pkgs; with lib; stdenv.mkDerivation { name = "commands-nix"; buildInputs = [ terraform vault gnused coreutils-full dig bash samba4Full morph xxd ]; shellHook = '' export NIX_PATH="nixpkgs=${import ./nixpkgs-path.nix}:morph-options=${morph.lib}/options.nix" export LD_LIBRARY_PATH="${libvirt}/lib:$LD_LIBRARY_PATH" export VAULT_ADDR=${escapeShellArg vault_addr} export DOMAIN="$(hostname -d)" PROXMOX_STORAGE=hdd-fs MEMORY=2048 NETWORK_PREFIX_LENGTH=12 rebuild-nixos-image() { echo "$(nix-build custom-image.nix)/nixos".img } mkvirt() { eval "$(env_cascade)" || return 3 eval "$(env_name "$1")" || return 4 if [ -f "./hosts/$name"".nix" ];then 1>&2 echo "there is already a file at ./hosts/$name"".nix. move it or remove it." return 1 fi [ -f "result/nixos.img" ] || ( 1>&2 echo "you do not seem to have a result/nixos.img file. building one now." rebuild-nixos-image || errcho "could not build result/nixos.img." || return $? ) [ -f "result/nixos.img" ] || errcho "there must be a file in result/nixos.img. make one with rebuild-nixos-image." || return $? IMG="$(realpath result/nixos.img)" [ -f "$IMG" ] || errcho "result/nixos.img must be resolveable to a file. rebuild the image link with rebuild-nixos-image." || return $? [ $? -eq 0 ] || return 5 VMID=$(sudo pvesh get /cluster/nextid) [ $? -eq 0 ] || return 6 sudo qm create $VMID \ --memory "$MEMORY" \ --net0 virtio,bridge=vmbr0 \ --ipconfig0 ip=dhcp \ --agent enabled=1,type=virtio \ --virtio0 "$PROXMOX_STORAGE":0,import-from="$IMG",discard=on,format=raw \ --boot c \ --bootdisk virtio0 \ --vga serial0 \ --serial0 socket \ --name "$name" \ --start 1 [ $? -eq 0 ] || return 7 IP="$(wait-for-vm-ipv4 $VMID)" ; [ $? -eq 0 ] || return 8 ssh-rescan "$IP" sed -e ' s#profiles/qemu-vm-install-media#profiles/qemu-vm#g s#networking[.]hostName = "nixos";#networking.hostName = "'"$name"'";#g ' "./hosts/_basic.nix" > "./hosts/""$name"".nix" 1>&2 echo "performing initial deployment to temporary IP: $IP" rsync -a ./ "$IP":/tmp/cascade/ guestbash $VMID 'nixos-rebuild -I nixos-config=/tmp/cascade/hosts/'"$name"'.nix switch && systemctl restart systemd-networkd' 1>&2 echo "retrieving permanent IP" IP="$(wait-for-vm-ipv4 $VMID)" ; [ $? -eq 0 ] || return 8 samba-create-ipv4-records "$IP" "$name" "$DOMAIN" ; [ $? -eq 0 ] || return 9 ssh-rescan "$name" "$name.$DOMAIN" "$IP" 1>&2 echo "morphing host" morph deploy network.nix switch --on="$name" } file-ends-with-newline() { [ x"$(xxd -ps "$1" | sed -E -e '$! d' -e 's/.*(..)$/\1/')" = x"0a" ] } ensure-file-ends-with-newline() { file-ends-with-newline "$1" || echo >> "$1" } ssh-rescan() { 1>&2 echo "ensuring known_hosts ends with a newline" ensure-file-ends-with-newline "$HOME/.ssh/known_hosts" while [ $# -gt 0 ];do 1>&2 echo "replacing any stale ssh keys that can be found" ssh-keygen -f "$HOME/.ssh/known_hosts" -R "$1" 2> /dev/null || true ssh-keyscan "$1" 2> /dev/null | sort | head -1 >> "$HOME/.ssh/known_hosts" shift done } samba-create-ipv4-records() { eval "$(env_cascade)" IP="$1" HOST="$2" :;:;:;:;:;:;:;:;: ;_IP_R="$IP" IP1="''${_IP_R%%.*}";_IP_R="''${_IP_R#*.}" IP2="''${_IP_R%%.*}";_IP_R="''${_IP_R#*.}" IP3="''${_IP_R%%.*}";_IP_R="''${_IP_R#*.}" IP4="''${_IP_R}" ;_IP_R="''${_IP_R#*.}" # at the end, the last fragment has no . so if we try to remove another dotted-quad/4, # we simply get no changes. the _desired end state_ is to have _IP_R = IP4. [ x"$IP4" = x"$_IP_R" ] || errcho "error parsing IPv4" samba-tool dns cleanup -k yes "$DC" "$HOST"."$DOMAIN" samba-tool dns add -k yes "$DC" "$DOMAIN" "$HOST" A "$IP" samba-tool dns zonecreate "$DC" "$IP3.$IP2.$IP1.in-addr.arpa." -k yes || true samba-tool dns add -k yes "$DC" "$IP3.$IP2.$IP1.in-addr.arpa." "$IP4.$IP3.$IP2.$IP1.in-addr.arpa." PTR "$HOST.$DOMAIN." } wait-for-vm-ipv4() { VMID="$1" while true;do IP="$( sudo qm guest cmd $VMID network-get-interfaces | \ jq -r ' .[] |select(.["hardware-address"] != "00:00:00:00:00:00") |.["ip-addresses"] | select(.) | .[] |select(.["ip-address-type"] == "ipv4") |.["ip-address"]' \ 2> /dev/null \ | grep -E '172|10|192' || true)" if [ x != x"$IP" ];then echo "$IP" break fi sleep 1 done } env_cascade() { DOMAIN="$(hostname -d)" #calculate some other variables REALM="$(echo "$DOMAIN" | tr a-z A-Z)" WORKGROUP="$(echo "$DOMAIN" | cut -d . -f 1)" #and recalculate others to fix caps DOMAIN="$(echo "$REALM" | tr A-Z a-z)" DC="$(dig +short -x "$(dig +short "$DOMAIN")")" DC="''${DC%.}" # just in case, let's drop the absolution dot workgroup="$(echo "$WORKGROUP" | tr A-Z a-z)" WORKGROUP="$(echo "$workgroup" | tr a-z A-Z)" printf '%q=%q\n' \ DOMAIN "$DOMAIN" \ REALM "$REALM" \ WORKGROUP "$WORKGROUP" \ DC "$DC" \ workgroup "$workgroup" } env_name() { # protect thy stdout ( exec 18>&1- exec 1>&2 NAME="$1" [ x"$NAME" != x ] || errcho "you need to provide a hostname" exec 1>&18 printf '%q=%q\n' name "$(echo "$NAME" | tr A-Z a-z)" printf '%q=%q\n' NAME "$(echo "$NAME" | tr a-z A-Z)" ) } errcho() { RC=$? if [ $RC -ne 0 ];then 1>&2 printf '%s\n' "$@" return $RC fi } guestbash() { VMID="$1" CMD="$2" shift;shift echo -n "$CMD" | sudo qm guest exec --pass-stdin=1 --timeout=300 "$VMID" -- /run/wrappers/bin/sudo -i bash -s "$@" } deploy() { nix-build network.nix && morph build network.nix && morph deploy network.nix "$@" } destroy-host() { eval "$(env_cascade)" while [ $# -gt 0 ];do eval "$(env_name "$1")" VMID="$(sudo qm list | awk -v N="$1" '$2 == N {print $1}')" echo "destroying hosts/$1.nix and VM #$VMID" 1>&2 sleep 1 sudo qm stop $VMID sudo qm destroy $VMID rm -f hosts/"$1".nix samba-tool dns cleanup -k yes "$DC" "$name"."$DOMAIN" shift done } cat << EOF Welcome! You've entered the cascade shell. You may make a virt on the local proxmox host using: mkvirt hostname-here You may also register an existing host in DNS using: samba-create-ipv4-records IP hostname-here Then morph may be used to deploy: # Create a hosts/hostname-here.nix file based on hosts/_basic.nix deploy test # this is an alias which runs morph on network.nix. deploy switch # the argument is the same as to morph deploy and nixos-rebuild EOF ''; }