summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@shackspace.de>2015-06-24 14:36:59 +0200
committertv <tv@shackspace.de>2015-06-24 15:01:23 +0200
commit45a0cb01d37e64b4d4d56a7a6769aba61d0fd8f2 (patch)
treedb07acb0a8baa7ee67460e1743461a06a87c0adc
parentbb7343cd89115e0768baaa426c68e49d399f6c6f (diff)
rewrite infest-cac-CentOS-7-64bit
-rw-r--r--README.md32
-rwxr-xr-xbin/_cac_curl_api_v110
-rwxr-xr-xbin/_cac_exec8
-rwxr-xr-xbin/_cac_get_api_v13
-rwxr-xr-xbin/_cac_post_api_v13
-rwxr-xr-xbin/cac-cloudpro-build5
-rwxr-xr-xbin/cac-cloudpro-delete3
-rwxr-xr-xbin/cac-cloudpro-resources3
-rwxr-xr-xbin/cac-console3
-rwxr-xr-xbin/cac-get-server-by17
-rwxr-xr-xbin/cac-listservers12
-rwxr-xr-xbin/cac-listtasks3
-rwxr-xr-xbin/cac-listtemplates4
-rwxr-xr-xbin/cac-powerop3
-rwxr-xr-xbin/cac-rdns3
-rwxr-xr-xbin/cac-renameserver3
-rwxr-xr-xbin/cac-runmode3
-rwxr-xr-xbin/cac-ssh17
-rwxr-xr-xbin/cacnixos-networking28
-rwxr-xr-xbin/infest-CentOS-7-64bit150
-rwxr-xr-xbin/infest-cac21
-rwxr-xr-xcac337
-rwxr-xr-xinfest15
-rwxr-xr-xinfest-cac-CentOS-7-64bit.sh33
-rw-r--r--infest.d/cac-CentOS-7-64bit/finalize.sh66
-rw-r--r--infest.d/cac-CentOS-7-64bit/prepare.sh104
-rw-r--r--infest.d/nixos-install.sh8
27 files changed, 580 insertions, 317 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..8a72d2fee
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+
+
+# Turn a Cloud at Cost CentOS-7-64bit server into NixOS
+
+1. Configure the system (`$systemname`) you'd like to install (see Configuration below).
+2. Create new server instance (either Custom or cloudpro) using "CentOS-7-64bit".
+ Note the servername (something like c731445864-cloudpro-388922936).
+3. `cac_login=xxx cac_key=yyy ./infest-cac-CentOS-7-64bit.sh servername:$servername $systename`
+4. Enjoy. (`ssh root@$systename`)
+
+# Configuration
+
+Configure your system in modules/$systemname
+See modules/cd/default.nix as an example.
+
+Notice that modules/$systemname/networking will be autogenerated (but not committed).
+
+secrets/$systemname/nix/foo can be accessed as `<secrets/foo>` from within the configuration.
+
+You might want `secrets/$systemname/rsync/etc/tinc/retiolum/rsa_key.priv`.
+
+You might want `secrets/$systemname/nix/hashedPasswords.nix`, which looks like
+
+```nix
+_: { users.extraUsers.root.hashedPassword = "XXX"; }
+```
+
+`XXX` can be generated with e.g.
+
+```
+mkpasswd -m sha-512 -S $(openssl rand -base64 16 | tr -d '+=' | head -c 16)
+```
diff --git a/bin/_cac_curl_api_v1 b/bin/_cac_curl_api_v1
deleted file mode 100755
index 65acebd9a..000000000
--- a/bin/_cac_curl_api_v1
+++ /dev/null
@@ -1,10 +0,0 @@
-#! /bin/sh
-set -euf
-
-exec _cac_exec curl -fsS "$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
-)
diff --git a/bin/_cac_exec b/bin/_cac_exec
deleted file mode 100755
index c932454e2..000000000
--- a/bin/_cac_exec
+++ /dev/null
@@ -1,8 +0,0 @@
-#! /bin/sh
-set -euf
-
-if test -z "${cac_via-}"; then
- exec "$@"
-else
- exec ssh -q "$cac_via" -t "$@"
-fi
diff --git a/bin/_cac_get_api_v1 b/bin/_cac_get_api_v1
deleted file mode 100755
index 67aac8560..000000000
--- a/bin/_cac_get_api_v1
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_curl_api_v1 -G "$@"
diff --git a/bin/_cac_post_api_v1 b/bin/_cac_post_api_v1
deleted file mode 100755
index b946ed9fa..000000000
--- a/bin/_cac_post_api_v1
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_curl_api_v1 -XPOST "$@"
diff --git a/bin/cac-cloudpro-build b/bin/cac-cloudpro-build
deleted file mode 100755
index 782fa0d72..000000000
--- a/bin/cac-cloudpro-build
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /bin/sh
-set -euf
-
-# default os=26 is CentOS-7-64bit
-exec _cac_post_api_v1 cloudpro/build cpu="$1" ram="$2" storage="$3" os="${4-26}"
diff --git a/bin/cac-cloudpro-delete b/bin/cac-cloudpro-delete
deleted file mode 100755
index ee1dbbc7e..000000000
--- a/bin/cac-cloudpro-delete
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_post_api_v1 cloudpro/delete sid="$1"
diff --git a/bin/cac-cloudpro-resources b/bin/cac-cloudpro-resources
deleted file mode 100755
index 9ec5872e7..000000000
--- a/bin/cac-cloudpro-resources
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_get_api_v1 cloudpro/resources
diff --git a/bin/cac-console b/bin/cac-console
deleted file mode 100755
index ed1cbd5ff..000000000
--- a/bin/cac-console
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_post_api_v1 console sid="$1"
diff --git a/bin/cac-get-server-by b/bin/cac-get-server-by
deleted file mode 100755
index b6d6b4ba3..000000000
--- a/bin/cac-get-server-by
+++ /dev/null
@@ -1,17 +0,0 @@
-#! /bin/sh
-set -euf
-
-cac-listservers \
- | jq \
- --arg k "$1" \
- --arg v "$2" \
- '
- map(select(.[$k]==$v)) |
- if (. | length) == 0 then
- null
- elif (. | length) == 1 then
- .[0]
- else
- .
- end
- '
diff --git a/bin/cac-listservers b/bin/cac-listservers
deleted file mode 100755
index 1e815d2af..000000000
--- a/bin/cac-listservers
+++ /dev/null
@@ -1,12 +0,0 @@
-#! /bin/sh
-set -euf
-
-listservers=$(_cac_get_api_v1 listservers)
-status=$(echo "$listservers" | jq -r .status)
-
-if [ "$status" = ok ]; then
- echo "$listservers" | jq -r .data
-else
- echo "$0: bad listservers status: $status" >&2
- exit 1
-fi
diff --git a/bin/cac-listtasks b/bin/cac-listtasks
deleted file mode 100755
index 14be3948a..000000000
--- a/bin/cac-listtasks
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_get_api_v1 listtasks
diff --git a/bin/cac-listtemplates b/bin/cac-listtemplates
deleted file mode 100755
index c4414e019..000000000
--- a/bin/cac-listtemplates
+++ /dev/null
@@ -1,4 +0,0 @@
-#! /bin/sh
-set -euf
-
-exec _cac_get_api_v1 listtemplates
diff --git a/bin/cac-powerop b/bin/cac-powerop
deleted file mode 100755
index c897835f0..000000000
--- a/bin/cac-powerop
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_post_api_v1 powerop sid="$1" action="$2"
diff --git a/bin/cac-rdns b/bin/cac-rdns
deleted file mode 100755
index c2d9ecdab..000000000
--- a/bin/cac-rdns
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_post_api_v1 rdns sid="$1" hostname="$2"
diff --git a/bin/cac-renameserver b/bin/cac-renameserver
deleted file mode 100755
index f0eff9b3d..000000000
--- a/bin/cac-renameserver
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_post_api_v1 renameserver sid="$1" name="$2"
diff --git a/bin/cac-runmode b/bin/cac-runmode
deleted file mode 100755
index 200b9fb79..000000000
--- a/bin/cac-runmode
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /bin/sh
-set -euf
-exec _cac_post_api_v1 rdns sid="$1" mode="$2"
diff --git a/bin/cac-ssh b/bin/cac-ssh
deleted file mode 100755
index a0ec5dcf3..000000000
--- a/bin/cac-ssh
+++ /dev/null
@@ -1,17 +0,0 @@
-#! /bin/sh
-set -euf
-
-server=$1
-shift
-
-address=$(echo $server | jq -r .ip)
-target=root@$address
-
-SSHPASS=$(echo $server | jq -r .rootpass)
-export SSHPASS
-
-exec sshpass -e ssh \
- -o StrictHostKeyChecking=no \
- -o UserKnownHostsFile=/dev/null \
- "$target" \
- "$@"
diff --git a/bin/cacnixos-networking b/bin/cacnixos-networking
deleted file mode 100755
index 4b246ebf1..000000000
--- a/bin/cacnixos-networking
+++ /dev/null
@@ -1,28 +0,0 @@
-#! /bin/sh
-# cacnixos-networking : cac-server x hostname -> nixos-module
-# TODO use label for hostname
-set -euf
-
-server=$1
-hostname=$2
-
-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 '{...}:\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'
diff --git a/bin/infest-CentOS-7-64bit b/bin/infest-CentOS-7-64bit
deleted file mode 100755
index a8afea14b..000000000
--- a/bin/infest-CentOS-7-64bit
+++ /dev/null
@@ -1,150 +0,0 @@
-#! /bin/sh
-set -euf
-
-server=$1
-hostname=$2
-
-address=$(echo $server | jq -r .ip)
-RSYNC_RSH='sshpass -e ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
-SSHPASS=$(echo $server | jq -r .rootpass)
-export SSHPASS
-export RSYNC_RSH
-
-main="modules/$hostname/default.nix"
-target="root@$address"
-
-cacnixos-networking "$server" $hostname \
- > modules/$hostname/networking.nix
-
-echo '(
- set -xeuf
- type bzip2 || yum install -y bzip2
- type rsync || yum install -y rsync
-)' \
- | sshpass -e ssh \
- -o StrictHostKeyChecking=no \
- -o UserKnownHostsFile=/dev/null \
- "root@$address" \
- /bin/sh
-
-make-rsync-filter "$main" \
- | rsync -f '. -' -zvrlptD --delete-excluded ./ "$target":/etc/nixos/
-
-#
-#
-#
-echo '(
- set -xeuf
- groupadd -g 30000 nixbld || :
- for i in `seq 1 10`; do
- useradd -c "foolsgarden Nix build user $i" \
- -d /var/empty \
- -s /sbin/nologin \
- -g 30000 \
- -G 30000 \
- -l -u $(expr 30000 + $i) \
- nixbld$i || :
- rm -f /var/spool/mail/nixbld$i
- done
-
- #curl https://nixos.org/nix/install | sh
- nix_tar=$nix_basename.tar.bz2
- if ! echo $nix_sha256 $nix_tar | sha256sum -c; then
- curl -O -C - $nix_url || :
- if ! echo $nix_sha256 $nix_tar | sha256sum -c; then
- curl -O $nix_url || :
- if ! echo $nix_sha256 $nix_tar | sha256sum -c; then
- echo $0: cannot download $nix_url >&2
- exit 5
- fi
- fi
- fi
-
- if ! test -d $nix_basename; then
- tar jxf $nix_basename.tar.bz2
- fi
-
- nix_find=$nix_basename.find.txt
- if ! echo $nix_find_sha1sum $nix_find | sha1sum -c; then
- find $nix_basename | sort > $nix_find
- if ! echo $nix_find_sha1sum $nix_find | sha1sum -c; then
- echo $0: cannot unpack $nix_basename.tar.bz2 >&2
- # TODO we could retry
- exit 6
- fi
- fi
-
- mkdir -p bin
- PATH=$HOME/bin:$PATH
- export PATH
-
- # generate fake sudo because
- # sudo: sorry, you must have a tty to run sudo
- {
- echo "#! /bin/sh"
- echo "exec env \"\$@\""
- } > bin/sudo
- chmod +x bin/sudo
-
- ./$nix_basename/install
-
- . /root/.nix-profile/etc/profile.d/nix.sh
-
- nixpkgs_expr="import <nixpkgs> { system = builtins.currentSystem; }"
- nixpkgs_path=$(
- find /nix/store -mindepth 1 -maxdepth 1 -name *-nixpkgs-* -type d
- )
-
- for i in nixos-generate-config nixos-install; do
- nix-env \
- --arg config "{ nix.package = ($nixpkgs_expr).nix; }" \
- --arg pkgs "$nixpkgs_expr" \
- --arg modulesPath "throw \"no modulesPath\"" \
- -f $nixpkgs_path/nixpkgs/nixos/modules/installer/tools/tools.nix \
- -iA config.system.build.$i
- done
-
- # TODO following fail when aborted in-between
- if ! test -d /int; then
- mkdir -p /int
- mount --bind /int /mnt
- fi
- if ! test -d /mnt/boot; then
- mkdir -p /mnt/boot
- mount /dev/sda1 /mnt/boot
- fi
-
- mkdir -p /mnt/etc/nixos
- rsync -zvrlptD --delete-excluded /etc/nixos/ /mnt/etc/nixos/
-
- mkdir -m 0444 -p /mnt/var/empty
-
- ln -s $main /mnt/etc/nixos/configuration.nix
- nixos-install \
- -I secrets=/etc/nixos/secrets
-
- find / \
- 1> /root/pre-rsync-find.out \
- 2> /root/pre-rsync-find.err
-
- rsync -va --force /int/ /
-
- # find / -type f -mtime +1 -exec rm -v {} \; 2>&1 > rm.log
- # ^ too aggressive, kills journal which is bad
- # shutdown -r now
- # nix-channel --add https://nixos.org/channels/nixos-unstable nixos
- # nix-channel --remove nixpkgs
- # nix-channel --update
-
-)' \
- | sshpass -e ssh \
- -o StrictHostKeyChecking=no \
- -o UserKnownHostsFile=/dev/null \
- "root@$address" \
- -T /usr/bin/env \
- nix_url="$nix_url" \
- nix_basename="$(basename $nix_url .tar.bz2)" \
- nix_sha256="$nix_sha256" \
- nix_find_sha1sum="$nix_find_sha1sum" \
- main="$main" \
- /bin/sh
diff --git a/bin/infest-cac b/bin/infest-cac
deleted file mode 100755
index d7d7bb96c..000000000
--- a/bin/infest-cac
+++ /dev/null
@@ -1,21 +0,0 @@
-#! /bin/sh
-set -euf
-
-server=$(cac-get-server-by servername "$1")
-hostname=$2
-
-serverstatus=$(echo $server | jq -r .status)
-case $serverstatus in
- 'Powered On') : ;;
- *)
- echo $0: bad server status: $serverstatus >&2
- exit 2
-esac
-
-template=$(echo $server | jq -r .template)
-case $template in
- 'CentOS-7-64bit') infest-"$template" "$server" "$hostname";;
- *)
- echo $0: bad template: $template >&2
- exit 3
-esac
diff --git a/cac b/cac
new file mode 100755
index 000000000..7bec90956
--- /dev/null
+++ b/cac
@@ -0,0 +1,337 @@
+#! /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 . $prefix/etc/nixos/
+ pushgit hosts $prefix/etc/nixos/hosts/
+ pushgit tmp/nixpkgs/$hostname $prefix/etc/nixos/nixpkgs/
+ pushdir secrets/$hostname/nix $prefix/etc/nixos/secrets/
+ pushdir secrets/$hostname/rsync $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 remote-dir -> ? |> ?
+rsyncfiles() {(
+ set -x
+ rsync \
+ --rsync-path="mkdir -p \"$2\" && rsync" \
+ -vzrlptD \
+ --files-from=- \
+ "$1"/ \
+ "$target:$2"
+)}
+
+
+# gitfiles : git-work-tree -> lines filename
+gitfiles() {
+ git -C "$1" archive --format=tar HEAD | tar t | sed '/\/$/d'
+}
+
+# pushgit : git-work-tree x remote-dir -> ?
+pushgit() {
+ gitfiles "$1" | rsyncfiles "$1" "$2"
+}
+
+# dirfiles : local-dir -> lines filename
+dirfiles() {(
+ cd "$1"
+ find . -type f | sed 's/^\.\///'
+)}
+
+# pushgit : local-dir x remote-dir -> ?
+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
diff --git a/infest b/infest
deleted file mode 100755
index 8c891c428..000000000
--- a/infest
+++ /dev/null
@@ -1,15 +0,0 @@
-#! /bin/sh
-#
-# usage: ./infest cac-servername hostname
-#
-set -euf
-
-PATH="$PWD/bin${PATH+:$PATH}"
-export PATH
-
-nix_url=https://nixos.org/releases/nix/nix-1.8/nix-1.8-x86_64-linux.tar.bz2
-nix_sha256=52fab207b4ce4d098a12d85357d0353e972c492bab0aa9e08e1600363e76fefb
-nix_find_sha1sum=86f8775bd4f0841edd4c816df861cebf509d58c3
-export nix_url nix_sha256 nix_find_sha1sum
-
-exec infest-cac "$@"
diff --git a/infest-cac-CentOS-7-64bit.sh b/infest-cac-CentOS-7-64bit.sh
new file mode 100755
index 000000000..32090ae9e
--- /dev/null
+++ b/infest-cac-CentOS-7-64bit.sh
@@ -0,0 +1,33 @@
+#! /bin/sh
+set -xeuf
+
+serverspec=$1
+systemname=$2
+
+./cac poll 10s 2>/dev/null &
+pollpid=$!
+trap "kill $pollpid; trap - EXIT" EXIT
+
+./cac waitstatus $serverspec 'Powered On'
+
+# TODO don't set label/mode if they're already good
+./cac setlabel $serverspec $systemname
+./cac setmode $systemname normal
+./cac generatenetworking $systemname > modules/$systemname/networking.nix
+
+cat infest.d/cac-CentOS-7-64bit/prepare.sh | ./cac ssh $systemname \
+ nix_url=https://nixos.org/releases/nix/nix-1.9/nix-1.9-x86_64-linux.tar.bz2 \
+ nix_sha256=5c76611c631e79aef5faf3db2d253237998bbee0f61fa093f925fa32203ae32b \
+ /bin/sh
+
+./cac pushconfig $systemname /mnt
+
+# This needs to be run twice because (at least):
+# Initialized empty Git repository in /var/lib/git/$reponame
+# chown: invalid user: 'git:nogroup'
+cat infest.d/nixos-install.sh | ./cac ssh $systemname || :
+cat infest.d/nixos-install.sh | ./cac ssh $systemname
+
+cat infest.d/cac-CentOS-7-64bit/finalize.sh | ./cac ssh $systemname
+
+./cac powerop $systemname reset
diff --git a/infest.d/cac-CentOS-7-64bit/finalize.sh b/infest.d/cac-CentOS-7-64bit/finalize.sh
new file mode 100644
index 000000000..b70276b33
--- /dev/null
+++ b/infest.d/cac-CentOS-7-64bit/finalize.sh
@@ -0,0 +1,66 @@
+#! /bin/sh
+set -eu
+{
+ umount /mnt2
+ umount /mnt/nix
+ umount /mnt/boot
+ umount /mnt
+ umount /boot
+
+ PATH=$(for i in /nix/store/*coreutils*/bin; do :; done; echo $i)
+ export PATH
+
+ mkdir /oldshit
+
+ mv /bin /oldshit/
+ mv /newshit/bin /
+
+ # TODO ensure /boot is empty
+ rmdir /newshit/boot
+
+ # skip /dev
+ rmdir /newshit/dev
+
+ mv /etc /oldshit/
+ mv /newshit/etc /
+
+ # TODO ensure /home is empty
+ rmdir /newshit/home
+
+ # skip /nix (it's already there)
+ rmdir /newshit/nix
+
+ # skip /proc
+ rmdir /newshit/proc
+
+ # skip /run
+ rmdir /newshit/run
+
+ # skip /sys
+ rmdir /newshit/sys
+
+ # skip /tmp
+ # TODO rmdir /newshit/tmp
+
+ mv /usr /oldshit/
+ mv /newshit/usr /
+
+ mv /var /oldshit/
+ mv /newshit/var /
+
+ mv /root /oldshit/
+ mv /newshit/root /
+
+ mv /lib /oldshit/
+ mv /lib64 /oldshit/
+ mv /sbin /oldshit/
+ mv /mnt2 /oldshit/
+ mv /srv /oldshit/
+ mv /opt /oldshit/
+
+
+ mv /newshit /root/ # TODO this one shoult be empty
+ mv /oldshit /root/
+
+ sync
+}
diff --git a/infest.d/cac-CentOS-7-64bit/prepare.sh b/infest.d/cac-CentOS-7-64bit/prepare.sh
new file mode 100644
index 000000000..f932e9c30
--- /dev/null
+++ b/infest.d/cac-CentOS-7-64bit/prepare.sh
@@ -0,0 +1,104 @@
+#! /bin/sh
+set -euf
+
+: $nix_url
+: $nix_sha256
+
+{
+ #
+ # prepare host
+ #
+
+ type bzip2 2>/dev/null || yum install -y bzip2
+ type rsync 2>/dev/null || yum install -y rsync
+
+ if ! getent group nixbld >/dev/null; then
+ groupadd -g 30000 -r nixbld
+ fi
+ for i in `seq 1 10`; do
+ if ! getent passwd nixbld$i 2>/dev/null; then
+ useradd \
+ -c "CentOS Nix build user $i" \
+ -d /var/empty \
+ -g 30000 \
+ -G 30000 \
+ -l \
+ -M \
+ -s /sbin/nologin \
+ -u $(expr 30000 + $i) \
+ nixbld$i
+ rm -f /var/spool/mail/nixbld$i
+ fi
+ done
+
+ # generate fake sudo because
+ # sudo: sorry, you must have a tty to run sudo
+ mkdir -p bin
+ printf '#! /bin/sh\nexec env "$@"\n' > bin/sudo
+ chmod +x bin/sudo
+
+ PATH=$PWD/bin:$PATH
+ export PATH
+
+ # install nix on host (cf. https://nixos.org/nix/install)
+ if ! test -e /root/.nix-profile/etc/profile.d/nix.sh; then
+ (
+ verify() {
+ echo $nix_sha256 $(basename $nix_url) | sha256sum -c
+ }
+ if ! verify; then
+ curl -C - -O "$nix_url"
+ verify
+ fi
+ )
+ tar jxf $(basename $nix_url)
+ $(basename $nix_url .tar.bz2)/install
+ fi
+
+ MANPATH=/var/empty . /root/.nix-profile/etc/profile.d/nix.sh
+
+ if ! type nixos-install 2>/dev/null; then
+ nixpkgs_expr='import <nixpkgs> { system = builtins.currentSystem; }'
+ nixpkgs_path=$(find /nix/store -mindepth 1 -maxdepth 1 -name *-nixpkgs-* -type d)
+ nix-env \
+ --arg config "{ nix.package = ($nixpkgs_expr).nix; }" \
+ --arg pkgs "$nixpkgs_expr" \
+ --arg modulesPath 'throw "no modulesPath"' \
+ -f $nixpkgs_path/nixpkgs/nixos/modules/installer/tools/tools.nix \
+ -iA config.system.build.nixos-install
+ fi
+
+ #
+ # mount install directory
+ #
+
+ if ! mount | grep -Fq '/dev/mapper/centos-root on /mnt type xfs'; then
+ mkdir -p /newshit
+ mount --bind /newshit /mnt
+ fi
+
+ if ! mount | grep -Fq '/dev/sda1 on /mnt/boot type xfs'; then
+ mkdir -p /mnt/boot
+ mount /dev/sda1 /mnt/boot
+ fi
+
+ if ! mount | grep -Fq '/dev/mapper/centos-root on /mnt/n