338 lines
6.7 KiB
Bash
Executable File
338 lines
6.7 KiB
Bash
Executable File
#! /bin/sh
|
|
set -euf
|
|
|
|
PATH=$PWD/bin:$PATH
|
|
export PATH
|
|
|
|
cac_listservers_cache=$PWD/tmp/cac_listservers_cache.json
|
|
|
|
|
|
cac() {
|
|
__cac_cli__command=$1
|
|
shift
|
|
__cac_cli__"$__cac_cli__command" "$@"
|
|
}
|
|
|
|
# WIP
|
|
__cac_cli__help() {(
|
|
exec sed < "$0" -n '
|
|
s/^__cac_cli__\([^(]\+\)().*/\1/p
|
|
'
|
|
)}
|
|
|
|
# usage: console
|
|
__cac_cli__console() {(
|
|
server=$(__cac_cli__getserver "$1")
|
|
sid=$(echo $server | jq -r .sid)
|
|
# TODO check reply status == ok
|
|
_cac_post_api_v1 console sid="$sid" | jq -r .console
|
|
)}
|
|
|
|
__cac_cli__listservers() {
|
|
jq -r . $cac_listservers_cache
|
|
}
|
|
|
|
__cac_cli__update() {(
|
|
umask 0077
|
|
servers=$(_cac_listservers)
|
|
echo $servers > $cac_listservers_cache.tmp
|
|
mv $cac_listservers_cache.tmp $cac_listservers_cache
|
|
)}
|
|
|
|
__cac_cli__getserver() {(
|
|
|
|
case $1 in
|
|
*:*)
|
|
k=${1%%:*}
|
|
v=${1#*:}
|
|
;;
|
|
*)
|
|
k=label
|
|
v=${1#*:}
|
|
;;
|
|
esac
|
|
|
|
if result=$(jq \
|
|
-e \
|
|
--arg k "$k" \
|
|
--arg v "$v" \
|
|
'
|
|
map(select(.[$k]==$v)) |
|
|
if (. | length) == 1 then
|
|
.[0]
|
|
else
|
|
null
|
|
end
|
|
' \
|
|
$cac_listservers_cache); then
|
|
echo $result | jq -r .
|
|
else
|
|
echo "$0 getserver $k:$v => not unique server found" >&2
|
|
exit 23
|
|
fi
|
|
)}
|
|
|
|
__cac_cli__generatenetworking() {(
|
|
server=$(__cac_cli__getserver "$1")
|
|
|
|
hostname=$(echo $server | jq -r .label)
|
|
|
|
address=$(echo $server | jq -r .ip)
|
|
gateway=$(echo $server | jq -r .gateway)
|
|
nameserver=8.8.8.8
|
|
netmask=$(echo $server | jq -r .netmask)
|
|
prefix=$(netmask-to-prefix $netmask)
|
|
|
|
#printf '# Generated file: %s generatenetworking %s %s\n' "$0" "$1" "$2"
|
|
#printf '# on %s\n' "$(date -Is)"
|
|
#printf '\n'
|
|
printf '_:\n'
|
|
printf '\n'
|
|
printf '{\n'
|
|
printf ' networking.hostName = "%s";\n' $hostname
|
|
printf ' networking.interfaces.enp2s1.ip4 = [\n'
|
|
printf ' {\n'
|
|
printf ' address = "%s";\n' $address
|
|
printf ' prefixLength = %d;\n' $prefix
|
|
printf ' }\n'
|
|
printf ' ];\n'
|
|
printf ' networking.defaultGateway = "%s";\n' $gateway
|
|
printf ' networking.nameservers = [\n'
|
|
printf ' "%s"\n' $nameserver
|
|
printf ' ];\n'
|
|
printf '}\n'
|
|
)}
|
|
|
|
__cac_cli__powerop() {(
|
|
server=$(__cac_cli__getserver "$1")
|
|
action=$2
|
|
|
|
sid=$(echo $server | jq -r .sid)
|
|
|
|
reply=$(_cac_post_api_v1 powerop sid="$sid" action="$action")
|
|
|
|
case $(echo $reply | jq -r .status) in
|
|
ok)
|
|
echo $reply | jq -r . >&2
|
|
__cac_cli__update
|
|
;;
|
|
*)
|
|
echo bad reply: >&2
|
|
echo $reply | jq -r . >&2
|
|
exit 23
|
|
;;
|
|
esac
|
|
)}
|
|
__cac_cli__pushconfig() {(
|
|
server=$(__cac_cli__getserver "$1")
|
|
|
|
prefix=${2-/}
|
|
|
|
hostname=$(echo $server | jq -r .label)
|
|
|
|
address=$(echo $server | jq -r .ip)
|
|
target=root@$address
|
|
|
|
RSYNC_RSH='sshpass -e ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
|
|
SSHPASS=$(echo $server | jq -r .rootpass)
|
|
export RSYNC_RSH SSHPASS
|
|
|
|
pushgit . $target:$prefix/etc/nixos/
|
|
pushgit hosts $target:$prefix/etc/nixos/hosts/
|
|
pushgit tmp/nixpkgs/$hostname $target:$prefix/etc/nixos/nixpkgs/
|
|
pushdir secrets/$hostname/nix $target:$prefix/etc/nixos/secrets/
|
|
pushdir secrets/$hostname/rsync $target:$prefix/
|
|
echo "_:{imports=[./modules/$hostname];}" \
|
|
| $RSYNC_RSH "$target" tee "$prefix/etc/nixos/configuration.nix" \
|
|
> /dev/null
|
|
|
|
## TODO chmod and chown secrets
|
|
)}
|
|
|
|
__cac_cli__setlabel() {(
|
|
server=$(__cac_cli__getserver "$1")
|
|
label=$2
|
|
|
|
sid=$(echo $server | jq -r .sid)
|
|
|
|
reply=$(_cac_post_api_v1 renameserver sid="$sid" name="$label")
|
|
|
|
case $(echo $reply | jq -r .status) in
|
|
ok)
|
|
echo $reply | jq -r . >&2
|
|
__cac_cli__update
|
|
;;
|
|
*)
|
|
echo bad reply: >&2
|
|
echo $reply | jq -r . >&2
|
|
exit 23
|
|
;;
|
|
esac
|
|
)}
|
|
|
|
__cac_cli__setmode() {(
|
|
server=$(__cac_cli__getserver "$1")
|
|
mode=$2
|
|
|
|
sid=$(echo $server | jq -r .sid)
|
|
|
|
reply=$(_cac_post_api_v1 runmode sid="$sid" mode="$mode")
|
|
|
|
case $(echo $reply | jq -r .status) in
|
|
ok)
|
|
echo $reply | jq -r . >&2
|
|
__cac_cli__update
|
|
;;
|
|
*)
|
|
echo bad reply: >&2
|
|
echo $reply | jq -r .
|
|
exit 23
|
|
;;
|
|
esac
|
|
)}
|
|
|
|
__cac_cli__ssh() {(
|
|
server=$(__cac_cli__getserver "$1")
|
|
shift
|
|
|
|
address=$(echo $server | jq -r .ip)
|
|
target=root@$address
|
|
|
|
SSHPASS=$(echo $server | jq -r .rootpass)
|
|
export SSHPASS
|
|
|
|
exec sshpass -e ssh \
|
|
-S none \
|
|
-o StrictHostKeyChecking=no \
|
|
-o UserKnownHostsFile=/dev/null \
|
|
$target \
|
|
"$@"
|
|
)}
|
|
|
|
|
|
# usage: ./cac waitstatus mode:Safe 'Powered On'
|
|
# blocks until server has specfied state
|
|
__cac_cli__waitstatus() {
|
|
server=$(__cac_cli__getserver "$1")
|
|
status=$(echo $server | jq -r .status)
|
|
|
|
case $status in
|
|
$2)
|
|
return
|
|
;;
|
|
esac
|
|
|
|
echo "$(date -Is) Waiting for status: $2; current status: $status ..." >&2
|
|
|
|
__cac_cli__waitforcacheupdate __cac_cli__waitstatus "$@"
|
|
}
|
|
|
|
|
|
# XXX for __cac_cli__waitforcacheupdate and __cac_cli__poll cache means $cac_listservers_cache
|
|
|
|
# blocks until cache has been updated then executes "$@"
|
|
__cac_cli__waitforcacheupdate() {
|
|
case $(inotifywait --format %f -q -e moved_to $(dirname $cac_listservers_cache)) in
|
|
$(basename $cac_listservers_cache)) "$@";;
|
|
*) __cac_cli__waitforcacheupdate "$@";;
|
|
esac
|
|
}
|
|
|
|
# usage: with cac ./cac poll 60s
|
|
# continuously update cache, sleeping at least $1 between updates
|
|
__cac_cli__poll() {
|
|
__cac_cli__update
|
|
t=${1-1m}
|
|
echo "$(date -Is) cache updated; sleeping $t ..." >&2
|
|
sleep "$t"
|
|
__cac_cli__poll "$@"
|
|
}
|
|
|
|
|
|
_cac_listservers() {(
|
|
servers=$(_cac_get_api_v1 listservers)
|
|
status=$(echo $servers | jq -r .status)
|
|
|
|
if [ "$status" = ok ]; then
|
|
echo "$servers" | jq -r .data
|
|
else
|
|
echo "cac_listservers: bad listservers status: $status" >&2
|
|
exit 1
|
|
fi
|
|
)}
|
|
|
|
|
|
|
|
|
|
# rsyncfiles : lines filename |> local-dir x rsync-target -> ? |> ?
|
|
rsyncfiles() {(
|
|
set -x
|
|
rsync \
|
|
--rsync-path="mkdir -p \"$2\" && rsync" \
|
|
-vzrlptD \
|
|
--files-from=- \
|
|
"$1"/ \
|
|
"$2"
|
|
)}
|
|
|
|
|
|
# gitfiles : git-work-tree -> lines filename
|
|
gitfiles() {
|
|
git -C "$1" archive --format=tar HEAD | tar t | sed '/\/$/d'
|
|
}
|
|
|
|
# pushgit : git-work-tree x rsync-target -> ?
|
|
pushgit() {
|
|
gitfiles "$1" | rsyncfiles "$1" "$2"
|
|
}
|
|
|
|
# dirfiles : local-dir -> lines filename
|
|
dirfiles() {(
|
|
cd "$1"
|
|
find . -type f | sed 's/^\.\///'
|
|
)}
|
|
|
|
# pushdir : local-dir x rsync-target -> ?
|
|
pushdir() {
|
|
dirfiles "$1" | rsyncfiles "$1" "$2"
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_cac_get_api_v1() {
|
|
_cac_curl_api_v1 -G "$@"
|
|
}
|
|
|
|
_cac_post_api_v1() {
|
|
_cac_curl_api_v1 -XPOST "$@"
|
|
}
|
|
|
|
_cac_curl_api_v1() {
|
|
_cac_exec curl -sS "$1" "https://panel.cloudatcost.com/api/v1/$2.php" $(
|
|
shift 2
|
|
set -- "$@" login="$cac_login" key="$cac_key"
|
|
for arg; do
|
|
echo -d $(printf '%s' "$arg" | urlencode)
|
|
done
|
|
)
|
|
}
|
|
|
|
_cac_exec() {
|
|
if test -z "${cac_via-}"; then
|
|
env -- "$@"
|
|
else
|
|
ssh -q "$cac_via" -t "$@"
|
|
fi
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case ${run-true} in
|
|
true) cac "$@";;
|
|
esac
|