CASCADE/shell.nix

245 lines
6.9 KiB
Nix

{ 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
# free-ips.txt contains fre IPs, one per line
# this is currently not being used in favor of DHCP towards the end
#IPALLOC="$(head -1 free-ips.txt)"
#sed -i -e '1d' free-ips.txt
[ -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
'';
}