245 lines
6.9 KiB
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
|
|
|
|
'';
|
|
}
|
|
|