# Ignore SSH key validation when connecting to these remote hosts. # (Otherwise, deployment scripts break when a VM IP address reuse.) SSHOPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR" HELP="" _cmd() { HELP="$(printf "%s\n%-20s %s\n" "$HELP" "$1" "$2")" } _cmd help "Show available commands" _cmd_help() { printf "$(basename $0) - the container training swiss army knife\n" printf "Commands:" printf "%s" "$HELP" | sort } _cmd cards "Generate ready-to-print cards for a group of VMs" _cmd_cards() { TAG=$1 need_tag OPTIONS_FILE=$2 [ -f "$OPTIONS_FILE" ] || die "Please specify a YAML options file as 2nd argument." OPTIONS_FILE_PATH="$(readlink -f "$OPTIONS_FILE")" # This will process logins.jsonl to generate two files: cards.pdf and cards.html ( cd tags/$TAG ../../../lib/make-login-cards.py "$OPTIONS_FILE_PATH" ) ln -sf ../tags/$TAG/cards.html www/$TAG.html ln -sf ../tags/$TAG/cards.pdf www/$TAG.pdf info "Cards created. You can view them with:" info "xdg-open tags/$TAG/cards.html tags/$TAG/cards.pdf (on Linux)" info "open tags/$TAG/cards.html (on macOS)" info "Or you can start a web server with:" info "$0 www" } _cmd clean "Remove information about destroyed clusters" _cmd_clean() { for TAG in tags/*; do if grep -q ^destroyed$ "$TAG/status"; then info "Removing $TAG..." rm -rf "$TAG" fi done } _cmd codeserver "Install code-server on the clusters" _cmd_codeserver() { TAG=$1 need_tag ARCH=${ARCHITECTURE-amd64} CODESERVER_VERSION=4.96.4 CODESERVER_URL=https://github.com/coder/code-server/releases/download/v${CODESERVER_VERSION}/code-server-${CODESERVER_VERSION}-linux-${ARCH}.tar.gz pssh " set -e i_am_first_node || exit 0 if ! [ -x /usr/local/bin/code-server ]; then curl -fsSL $CODESERVER_URL | sudo tar zx -C /opt sudo ln -s /opt/code-server-${CODESERVER_VERSION}-linux-${ARCH}/bin/code-server /usr/local/bin/code-server sudo -u $USER_LOGIN -H code-server --install-extension ms-azuretools.vscode-docker sudo -u $USER_LOGIN -H code-server --install-extension ms-kubernetes-tools.vscode-kubernetes-tools sudo -u $USER_LOGIN -H mkdir -p /home/$USER_LOGIN/.local/share/code-server/User echo '{\"workbench.startupEditor\": \"terminal\"}' | sudo -u $USER_LOGIN tee /home/$USER_LOGIN/.local/share/code-server/User/settings.json sudo -u $USER_LOGIN mkdir -p /home/$USER_LOGIN/.config/systemd/user sudo -u $USER_LOGIN tee /home/$USER_LOGIN/.config/systemd/user/code-server.service < tags/$TAG/status } _cmd create "Create lab environments" _cmd_create() { while [ ! -z "$*" ]; do case "$1" in --mode) MODE=$2; shift 2;; --provider) PROVIDER=$2; shift 2;; --settings) SETTINGS=$2; shift 2;; --students) STUDENTS=$2; shift 2;; --tag) TAG=$2; shift 2;; *) die "Unrecognized parameter: $1." esac done if [ -z "$MODE" ]; then info "Using default mode (pssh)." MODE=pssh fi if [ -z "$PROVIDER" ]; then die "Please add --provider flag to specify which provider to use." fi if [ -z "$SETTINGS" ]; then die "Please add --settings flag to specify which settings file to use." fi if [ -z "$STUDENTS" ]; then info "Defaulting to 1 student since --students flag wasn't specified." STUDENTS=1 fi case "$MODE" in mk8s) PROVIDER_BASE=terraform/one-kubernetes ;; pssh) PROVIDER_BASE=terraform/virtual-machines ;; *) die "Invalid mode: $MODE (supported modes: mk8s, pssh)." ;; esac if ! [ -f "$SETTINGS" ]; then die "Settings file ($SETTINGS) not found." fi # Check that the provider is valid. if [ -d $PROVIDER_BASE/$PROVIDER ]; then if [ -f $PROVIDER_BASE/$PROVIDER/requires_tfvars ]; then die "Provider $PROVIDER cannot be used directly, because it requires a tfvars file." fi PROVIDER_DIRECTORY=$PROVIDER_BASE/$PROVIDER TFVARS="" elif [ -f $PROVIDER_BASE/$PROVIDER.tfvars ]; then TFVARS=$PROVIDER_BASE/$PROVIDER.tfvars PROVIDER_DIRECTORY=$(dirname $PROVIDER_BASE/$PROVIDER) else error "Provider $PROVIDER not found." info "Available providers for mode $MODE:" ( cd $PROVIDER_BASE for P in *; do if [ -d "$P" ]; then [ -f "$P/requires_tfvars" ] || info "$P" for V in $P/*.tfvars; do [ -f "$V" ] && info "${V%.tfvars}" done fi done ) die "Please specify a valid provider." fi if [ -z "$TAG" ]; then TAG=$(_cmd_maketag) fi mkdir -p tags/$TAG echo creating > tags/$TAG/status ln -s ../../$SETTINGS tags/$TAG/settings.env.orig cp $SETTINGS tags/$TAG/settings.env . $SETTINGS echo $MODE > tags/$TAG/mode echo $PROVIDER > tags/$TAG/provider case "$MODE" in mk8s) cp -d terraform/many-kubernetes/*.* tags/$TAG mkdir tags/$TAG/one-kubernetes-module cp $PROVIDER_DIRECTORY/*.tf tags/$TAG/one-kubernetes-module mkdir tags/$TAG/one-kubernetes-config mv tags/$TAG/one-kubernetes-module/config.tf tags/$TAG/one-kubernetes-config ;; pssh) cp $PROVIDER_DIRECTORY/*.tf tags/$TAG if [ "$TFVARS" ]; then cp "$TFVARS" "tags/$TAG/$(basename $TFVARS).auto.tfvars" fi ;; esac ( cd tags/$TAG terraform init echo tag = \"$TAG\" >> terraform.tfvars echo how_many_clusters = $STUDENTS >> terraform.tfvars if [ "$CLUSTERSIZE" ]; then echo nodes_per_cluster = $CLUSTERSIZE >> terraform.tfvars fi ) sep # If the settings.env file has a "STEPS" field, # automatically execute all the actions listed in that field. # If an action fails, retry it up to 10 times. for STEP in $(echo $STEPS); do sep "$TAG -> $STEP" TRY=1 MAXTRY=10 while ! $0 $STEP $TAG ; do TRY=$(($TRY+1)) if [ $TRY -gt $MAXTRY ]; then error "This step ($STEP) failed after $MAXTRY attempts." info "You can troubleshoot the situation manually, or terminate these instances with:" info "$0 destroy $TAG" die "Giving up." else sep info "Step '$STEP' failed for '$TAG'. Let's wait 10 seconds and try again." info "(Attempt $TRY out of $MAXTRY.)" sleep 10 fi done done sep info "Deployment successful." info "To log into the first machine of that batch, you can run:" info "$0 ssh $TAG" info "To terminate these instances, you can run:" info "$0 destroy $TAG" } _cmd destroy "Destroy lab environments" _cmd_destroy() { TAG=$1 need_tag cd tags/$TAG echo destroying > status terraform destroy -auto-approve echo destroyed > status } _cmd clusterize "Group VMs in clusters" _cmd_clusterize() { TAG=$1 need_tag pssh " set -e grep PSSH_ /etc/ssh/sshd_config || echo 'AcceptEnv PSSH_*' | sudo tee -a /etc/ssh/sshd_config grep KUBECOLOR_ /etc/ssh/sshd_config || echo 'AcceptEnv KUBECOLOR_*' | sudo tee -a /etc/ssh/sshd_config sudo systemctl restart ssh.service" pssh -I < tags/$TAG/clusters.tsv " grep -w \$PSSH_HOST | tr '\t' '\n' > /tmp/cluster" pssh " echo \$PSSH_HOST > /tmp/ipv4 head -n 1 /tmp/cluster | sudo tee /etc/ipv4_of_first_node echo ${CLUSTERPREFIX}1 | sudo tee /etc/name_of_first_node echo HOSTIP=\$PSSH_HOST | sudo tee -a /etc/environment NODEINDEX=\$((\$PSSH_NODENUM%$CLUSTERSIZE+1)) if [ \$NODEINDEX = 1 ]; then sudo ln -sf /bin/true /usr/local/bin/i_am_first_node else sudo ln -sf /bin/false /usr/local/bin/i_am_first_node fi echo $CLUSTERPREFIX\$NODEINDEX | sudo tee /etc/hostname sudo hostname $CLUSTERPREFIX\$NODEINDEX N=1 while read ip; do grep -w \$ip /etc/hosts || echo \$ip $CLUSTERPREFIX\$N | sudo tee -a /etc/hosts N=\$((\$N+1)) done < /tmp/cluster " jq --raw-input --compact-output \ --arg USER_LOGIN "$USER_LOGIN" --arg USER_PASSWORD "$USER_PASSWORD" ' { "login": $USER_LOGIN, "password": $USER_PASSWORD, "ipaddrs": . }' < tags/$TAG/clusters.tsv > tags/$TAG/logins.jsonl echo cluster_ok > tags/$TAG/status } _cmd disabledocker "Stop Docker Engine and don't restart it automatically" _cmd_disabledocker() { TAG=$1 need_tag pssh " sudo systemctl disable docker.socket --now sudo systemctl disable docker.service --now sudo systemctl disable containerd.service --now " } _cmd docker "Install and start Docker" _cmd_docker() { TAG=$1 need_tag pssh " set -e # On EC2, the ephemeral disk might be mounted on /mnt. # If /mnt is a mountpoint, place Docker workspace on it. if mountpoint -q /mnt; then sudo mkdir -p /mnt/docker sudo ln -sfn /mnt/docker /var/lib/docker fi # This will install the latest Docker. sudo apt-get -qy install apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository 'deb https://download.docker.com/linux/ubuntu jammy stable' sudo apt-get -q update sudo apt-get -qy install docker-ce # Add registry mirror configuration. if ! [ -f /etc/docker/daemon.json ]; then sudo mkdir -p /etc/docker echo '{\"registry-mirrors\": [\"https://mirror.gcr.io\"]}' | sudo tee /etc/docker/daemon.json sudo systemctl restart docker fi " ##VERSION## https://github.com/docker/compose/releases COMPOSE_VERSION=v2.11.1 COMPOSE_PLATFORM='linux-$(uname -m)' # Just in case you need Compose 1.X, you can use the following lines. # (But it will probably only work for x86_64 machines.) #COMPOSE_VERSION=1.29.2 #COMPOSE_PLATFORM='Linux-$(uname -m)' pssh " set -e ### Install docker-compose. sudo curl -fsSL -o /usr/local/bin/docker-compose \ https://github.com/docker/compose/releases/download/$COMPOSE_VERSION/docker-compose-$COMPOSE_PLATFORM sudo chmod +x /usr/local/bin/docker-compose docker-compose version ### Install docker-machine. ##VERSION## https://github.com/docker/machine/releases MACHINE_VERSION=v0.16.2 sudo curl -fsSL -o /usr/local/bin/docker-machine \ https://github.com/docker/machine/releases/download/\$MACHINE_VERSION/docker-machine-\$(uname -s)-\$(uname -m) sudo chmod +x /usr/local/bin/docker-machine docker-machine version " } _cmd kubebins "Install Kubernetes and CNI binaries but don't start anything" _cmd_kubebins() { TAG=$1 need_tag if [ "$KUBEVERSION" = "" ]; then KUBEVERSION="$(curl -fsSL https://cdn.dl.k8s.io/release/stable.txt | sed s/^v//)" fi ##VERSION## case "$KUBEVERSION" in 1.19.*) ETCD_VERSION=v3.4.13 CNI_VERSION=v0.8.7 ;; *) ETCD_VERSION=v3.5.10 CNI_VERSION=v1.3.0 ;; esac K8SBIN_VERSION="v$KUBEVERSION" ARCH=${ARCHITECTURE-amd64} pssh --timeout 300 " set -e cd /usr/local/bin if ! [ -x etcd ]; then curl -L https://github.com/etcd-io/etcd/releases/download/$ETCD_VERSION/etcd-$ETCD_VERSION-linux-$ARCH.tar.gz \ | sudo tar --strip-components=1 --wildcards -zx '*/etcd' '*/etcdctl' fi if ! [ -x hyperkube ]; then ##VERSION## curl -L https://dl.k8s.io/$K8SBIN_VERSION/kubernetes-server-linux-$ARCH.tar.gz \ | sudo tar --strip-components=3 -zx \ kubernetes/server/bin/kube{ctl,let,-proxy,-apiserver,-scheduler,-controller-manager} fi sudo mkdir -p /opt/cni/bin cd /opt/cni/bin if ! [ -x bridge ]; then curl -L https://github.com/containernetworking/plugins/releases/download/$CNI_VERSION/cni-plugins-linux-$ARCH-$CNI_VERSION.tgz \ | sudo tar -zx fi " } _cmd kubepkgs "Install Kubernetes packages (kubectl, kubeadm, kubelet)" _cmd_kubepkgs() { TAG=$1 need_tag # Prior September 2023, there was a single Kubernetes package repo that # contained packages for all versions, so we could just add that repo # and install whatever was the latest version available there. # Things have changed (versions after September 2023, e.g. 1.28.3 are # not in the old repo) and now there is a different repo for each # minor version, so we need to figure out what minor version we are # installing to add the corresponding repo. if [ "$KUBEVERSION" = "" ]; then KUBEVERSION="$(curl -fsSL https://cdn.dl.k8s.io/release/stable.txt | sed s/^v//)" fi KUBEREPOVERSION="$(echo $KUBEVERSION | cut -d. -f1-2)" # Since the new repo doesn't have older versions, add a safety check here. MINORVERSION="$(echo $KUBEVERSION | cut -d. -f2)" if [ "$MINORVERSION" -lt 24 ]; then die "Cannot install kubepkgs for versions before 1.24." fi pssh " sudo tee /etc/apt/preferences.d/kubernetes < /tmp/token && cat >/tmp/kubeadm-config.yaml < /tmp/kubeadm-config.yaml && sudo kubeadm join --config /tmp/kubeadm-config.yaml fi" if [ -f "tags/$TAG/id_rsa" ]; then ssh-add -d tags/$TAG/id_rsa fi # Install metrics server pssh " if i_am_first_node; then kubectl apply -f https://raw.githubusercontent.com/jpetazzo/container.training/master/k8s/metrics-server.yaml #helm upgrade --install metrics-server \ # --repo https://kubernetes-sigs.github.io/metrics-server/ metrics-server \ # --namespace kube-system --set args={--kubelet-insecure-tls} fi" } _cmd kubetools "Install a bunch of CLI tools for Kubernetes" _cmd_kubetools() { TAG=$1 need_tag ARCH=${ARCHITECTURE-amd64} # Folks, please, be consistent! # Either pick "uname -m" (on Linux, that's x86_64, aarch64, etc.) # Or GOARCH (amd64, arm64, etc.) # But don't mix both! Thank you ♥ case $ARCH in amd64) HERP_DERP_ARCH=x86_64 TILT_ARCH=x86_64 ;; *) HERP_DERP_ARCH=$ARCH TILT_ARCH=${ARCH}_ALPHA ;; esac # Install ArgoCD CLI ##VERSION## https://github.com/argoproj/argo-cd/releases/latest URL=https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-${ARCH} pssh " if [ ! -x /usr/local/bin/argocd ]; then sudo curl -o /usr/local/bin/argocd -fsSL $URL sudo chmod +x /usr/local/bin/argocd argocd completion bash | sudo tee /etc/bash_completion.d/argocd argocd version --client fi" # Install Flux CLI ##VERSION## https://github.com/fluxcd/flux2/releases FLUX_VERSION=2.3.0 FILENAME=flux_${FLUX_VERSION}_linux_${ARCH} URL=https://github.com/fluxcd/flux2/releases/download/v$FLUX_VERSION/$FILENAME.tar.gz pssh " if [ ! -x /usr/local/bin/flux ]; then curl -fsSL $URL | sudo tar -C /usr/local/bin -zx flux sudo chmod +x /usr/local/bin/flux flux completion bash | sudo tee /etc/bash_completion.d/flux flux --version fi" # Install kubectx and kubens pssh " set -e if ! [ -x /usr/local/bin/kctx ]; then cd /tmp git clone https://github.com/ahmetb/kubectx sudo cp kubectx/kubectx /usr/local/bin/kctx sudo cp kubectx/kubens /usr/local/bin/kns sudo cp kubectx/completion/*.bash /etc/bash_completion.d fi" # Install kube-ps1 pssh " set -e if ! [ -d /opt/kube-ps1 ]; then cd /tmp git clone https://github.com/jonmosco/kube-ps1 sudo mv kube-ps1 /opt/kube-ps1 sudo -u $USER_LOGIN sed -i s/docker-prompt/kube_ps1/ /home/$USER_LOGIN/.bashrc && sudo -u $USER_LOGIN tee -a /home/$USER_LOGIN/.bashrc < tags/$TAG/status } _cmd ips "Show the IP addresses for a given tag" _cmd_ips() { TAG=$1 need_tag $TAG while true; do for I in $(seq $CLUSTERSIZE); do read ip || return 0 printf "%s\t" "$ip" done printf "\n" done < tags/$TAG/ips.txt } _cmd inventory "List all VMs on a given provider (or across all providers if no arg given)" _cmd_inventory() { FIXME } _cmd logins "Show login information for a group of instances" _cmd_logins() { TAG=$1 need_tag $TAG cat tags/$TAG/logins.jsonl \ | jq -r '"\(if .codeServerPort then "\(.codeServerPort)\t" else "" end )\(.password)\tssh -l \(.login)\(if .port then " -p \(.port)" else "" end)\t\(.ipaddrs)"' } _cmd maketag "Generate a quasi-unique tag for a group of instances" _cmd_maketag() { if [ -z $USER ]; then export USER=anonymous fi MS=$(($(date +%N | tr -d 0)/1000000)) date +%Y-%m-%d-%H-%M-$MS-$USER } _cmd netfix "Disable GRO and run a pinger job on the VMs" _cmd_netfix () { TAG=$1 need_tag pssh " sudo ethtool -K ens3 gro off sudo tee /root/pinger.service < ../logins.jsonl terraform output -raw ips_txt > ../ips.txt echo "stage2_ok" > status } _cmd standardize "Deal with non-standard Ubuntu cloud images" _cmd_standardize() { TAG=$1 need_tag # Try to log in as root. # If successful, make sure than we have: # - sudo # - ubuntu user # Note that on Scaleway, the keys of the root account get copied # a little bit later after boot; so the first time we run "standardize" # we might end up copying an incomplete authorized_keys file. # That's why we copy it inconditionally here, rather than checking # for existence and skipping if it already exists. pssh -l root -t 5 true 2>&1 >/dev/null && { pssh -l root " grep DEBIAN_FRONTEND /etc/environment || echo DEBIAN_FRONTEND=noninteractive >> /etc/environment #grep cloud-init /etc/sudoers && rm /etc/sudoers apt-get update && apt-get install sudo -y getent passwd ubuntu || { useradd ubuntu -m -s /bin/bash echo 'ubuntu ALL=(ALL:ALL) NOPASSWD:ALL' > /etc/sudoers.d/ubuntu } install --owner=ubuntu --mode=700 --directory /home/ubuntu/.ssh install --owner=ubuntu --mode=600 /root/.ssh/authorized_keys --target-directory /home/ubuntu/.ssh " } # Now make sure that we have an ubuntu user pssh true # Disable unattended upgrades so that they don't mess up with the subsequent steps pssh sudo rm -f /etc/apt/apt.conf.d/50unattended-upgrades # Some cloud providers think that it's smart to disable password authentication. # We need to re-neable it, though. # Digital Ocecan pssh " if [ -f /etc/ssh/sshd_config.d/50-cloud-init.conf ]; then sudo rm /etc/ssh/sshd_config.d/50-cloud-init.conf sudo systemctl restart ssh.service fi" # AWS pssh "if [ -f /etc/ssh/sshd_config.d/60-cloudimg-settings.conf ]; then sudo rm /etc/ssh/sshd_config.d/60-cloudimg-settings.conf sudo systemctl restart ssh.service fi" # Special case for oracle since their iptables blocks everything but SSH pssh " if [ -f /etc/iptables/rules.v4 ]; then sudo sed -i 's/-A INPUT -j REJECT --reject-with icmp-host-prohibited//' /etc/iptables/rules.v4 sudo netfilter-persistent flush sudo netfilter-persistent start fi" # oracle-cloud-agent upgrades pacakges in the background. # This breaks our deployment scripts, because when we invoke apt-get, it complains # that the lock already exists (symptom: random "Exited with error code 100"). # Workaround: if we detect oracle-cloud-agent, remove it. # But this agent seems to also take care of installing/upgrading # the unified-monitoring-agent package, so when we stop the snap, # it can leave dpkg in a broken state. We "fix" it with the 2nd command. pssh " if [ -d /snap/oracle-cloud-agent ]; then sudo snap remove oracle-cloud-agent sudo dpkg --remove --force-remove-reinstreq unified-monitoring-agent fi" } _cmd tailhist "Install history viewer on port 1088" _cmd_tailhist () { TAG=$1 need_tag ARCH=${ARCHITECTURE-amd64} [ "$ARCH" = "aarch64" ] && ARCH=arm64 # We use "wget -c" here in case the download was aborted # halfway through and we're actually trying to download it again. pssh " set -e sudo apt-get install unzip -y wget -c https://github.com/joewalnes/websocketd/releases/download/v0.3.0/websocketd-0.3.0-linux_$ARCH.zip unzip -o websocketd-0.3.0-linux_$ARCH.zip websocketd sudo mv websocketd /usr/local/bin/websocketd sudo mkdir -p /opt/tailhist sudo tee /opt/tailhist.service < tags/$TAG/status ( cd tags/$TAG terraform apply -auto-approve # The Terraform provider for Proxmox has a bug; sometimes it fails # to obtain VM address from the QEMU agent. In that case, we put # ERROR in the ips.txt file (instead of the VM IP address). Detect # that so that we run Terraform again (this typically solves the issue). if grep -q ERROR ips.txt; then die "Couldn't obtain IP address of some machines. Try to re-run terraform." fi ) echo terraformed > tags/$TAG/status } _cmd tools "Install a bunch of useful tools (editors, git, jq...)" _cmd_tools() { TAG=$1 need_tag pssh " set -e sudo apt-get -q update sudo apt-get -qy install apache2-utils argon2 emacs-nox git httping htop jid joe jq mosh tree unzip # This is for VMs with broken PRNG (symptom: running docker-compose randomly hangs) sudo apt-get -qy install haveged " } _cmd pssh "Run an arbitrary command on all nodes" _cmd_pssh() { TAG=$1 need_tag shift pssh "$@" } _cmd pull_images "Pre-pull a bunch of Docker images" _cmd_pull_images() { TAG=$1 need_tag pull_tag } _cmd remap_nodeports "Remap NodePort range to 10000-10999" _cmd_remap_nodeports() { TAG=$1 need_tag FIND_LINE=" - --service-cluster-ip-range=10.96.0.0\/12" ADD_LINE=" - --service-node-port-range=10000-10999" MANIFEST_FILE=/etc/kubernetes/manifests/kube-apiserver.yaml pssh " if i_am_first_node && ! grep -q '$ADD_LINE' $MANIFEST_FILE; then sudo sed -i 's/\($FIND_LINE\)\$/\1\n$ADD_LINE/' $MANIFEST_FILE fi" info "If you have manifests hard-coding nodePort values," info "you might want to patch them with a command like:" info " if i_am_first_node; then kubectl -n kube-system patch svc prometheus-server \\ -p 'spec: { ports: [ {port: 80, nodePort: 10101} ]}' fi " } _cmd ssh "Open an SSH session to the first node of a tag" _cmd_ssh() { TAG=$1 need_tag IP=$(head -1 tags/$TAG/ips.txt) info "Logging into $IP (default password: $USER_PASSWORD)" ssh $SSHOPTS $USER_LOGIN@$IP } _cmd tags "List groups of VMs known locally" _cmd_tags() { ( cd tags echo "[#] [Status] [Tag] [Mode] [Provider]" for tag in *; do if [ -f $tag/logins.jsonl ]; then count="$(wc -l < $tag/logins.jsonl)" else count="?" fi if [ -f $tag/status ]; then status="$(cat $tag/status)" else status="?" fi if [ -f $tag/mode ]; then mode="$(cat $tag/mode)" else mode="?" fi if [ -f $tag/provider ]; then provider="$(cat $tag/provider)" else provider="?" fi echo "$count $status $tag $mode $provider" done ) | column -t } _cmd test "Run tests (pre-flight checks) on a group of VMs" _cmd_test() { TAG=$1 need_tag test_tag } _cmd tmux "Log into the first node and start a tmux server" _cmd_tmux() { TAG=$1 need_tag IP=$(head -1 tags/$TAG/ips.txt) info "Opening ssh+tmux with $IP" rm -f /tmp/tmux-$UID/default ssh $SSHOPTS -t -L /tmp/tmux-$UID/default:/tmp/tmux-1001/default docker@$IP tmux new-session -As 0 } _cmd helmprom "Install Prometheus with Helm" _cmd_helmprom() { TAG=$1 need_tag pssh " if i_am_first_node; then sudo -u $USER_LOGIN -H helm upgrade --install prometheus prometheus \ --repo https://prometheus-community.github.io/helm-charts/ \ --namespace prometheus --create-namespace \ --set server.service.type=NodePort \ --set server.service.nodePort=30090 \ --set server.persistentVolume.enabled=false \ --set alertmanager.enabled=false fi" } _cmd passwords "Set individual passwords for each cluster" _cmd_passwords() { TAG=$1 need_tag PASSWORDS_FILE="tags/$TAG/passwords" if ! [ -f "$PASSWORDS_FILE" ]; then error "File $PASSWORDS_FILE not found. Please create it first." error "It should contain one password per line." error "It should have as many lines as there are clusters." die "Aborting." fi N_CLUSTERS=$($0 ips "$TAG" | wc -l) N_PASSWORDS=$(wc -l < "$PASSWORDS_FILE") if [ "$N_CLUSTERS" != "$N_PASSWORDS" ]; then die "Found $N_CLUSTERS clusters and $N_PASSWORDS passwords. Aborting." fi $0 ips "$TAG" | paste "$PASSWORDS_FILE" - | while read password nodes; do info "Setting password for $nodes..." for node in $nodes; do echo $USER_LOGIN $password | ssh $SSHOPTS -i tags/$TAG/id_rsa ubuntu@$node ' read login password echo $login:$password | sudo chpasswd hashedpassword=$(echo -n $password | argon2 saltysalt$RANDOM -e) sudo -u $login mkdir -p /home/$login/.config/code-server echo "hashed-password: \"$hashedpassword\"" | sudo -u $login tee /home/$login/.config/code-server/config.yaml >/dev/null ' done done info "Done." } _cmd wait "Wait until VMs are ready (reachable, cloud init is done, ubuntu user is up)" _cmd_wait() { TAG=$1 need_tag # Wait until all hosts are reachable. info "Trying to reach $TAG instances..." while >/dev/stderr echo -n "."; do pssh -t 5 true 2>&1 >/dev/null && { SSH_USER=ubuntu break } pssh -l root -t 5 true 2>&1 >/dev/null && { SSH_USER=root break } sleep 2 done >/dev/stderr echo "" # If this VM image is using cloud-init, # wait for cloud-init to be done info "Waiting for cloud-init to be done on $TAG instances..." pssh -l $SSH_USER " if [ -d /var/lib/cloud ]; then cloud-init status --wait case $? in 0) exit 0;; # all is good 2) exit 0;; # recoverable error (happens with proxmox deprecated cloud-init payloads) *) exit 1;; # all other problems esac fi" } # Sometimes, weave fails to come up on some nodes. # Symptom: the pods on a node are unreachable (they don't even ping). # Remedy: wipe out Weave state and delete weave pod on that node. # Specifically, identify the weave pod that is defective, then: # kubectl -n kube-system exec weave-net-XXXXX -c weave rm /weavedb/weave-netdata.db # kubectl -n kube-system delete pod weave-net-XXXXX _cmd weavetest "Check that weave seems properly setup" _cmd_weavetest() { TAG=$1 need_tag pssh " kubectl -n kube-system get pods -o name | grep weave | cut -d/ -f2 | xargs -I POD kubectl -n kube-system exec POD -c weave -- \ sh -c \"./weave --local status | grep Connections | grep -q ' 1 failed' || ! echo POD \"" } _cmd webssh "Install a WEB SSH server on the machines (port 1080)" _cmd_webssh() { TAG=$1 need_tag pssh " sudo apt-get update && sudo apt-get install python3-tornado python3-paramiko -y" pssh " cd /opt [ -d webssh ] || sudo git clone https://github.com/jpetazzo/webssh" pssh " for KEYFILE in /etc/ssh/*.pub; do read a b c < \$KEYFILE; echo localhost \$a \$b done | sudo tee /opt/webssh/known_hosts" pssh "cat >webssh.service <