Migrate to Packer
This commit is contained in:
parent
b97775abd1
commit
e466a94d18
|
@ -5,7 +5,13 @@ jobs:
|
||||||
- image: alpine
|
- image: alpine
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: apk add --no-cache --no-progress bash openssh
|
- setup_remote_docker
|
||||||
|
- run: >-
|
||||||
|
apk add --no-cache --no-progress
|
||||||
|
bash docker make openssh
|
||||||
|
- run: >-
|
||||||
|
echo "${DOCKER_PASSWORD}" |
|
||||||
|
docker login --username "${DOCKER_USERNAME}" --password-stdin
|
||||||
- run: scripts/deploy.bash
|
- run: scripts/deploy.bash
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
*.log
|
*.log
|
||||||
.log
|
.log
|
||||||
.lsp-repl-history
|
.lsp-repl-history
|
||||||
|
do_digitalocean.pem
|
||||||
node_modules
|
node_modules
|
||||||
out
|
out
|
||||||
|
secrets.json
|
||||||
tests
|
tests
|
||||||
tests-*
|
tests-*
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -19,10 +19,10 @@ image-prod: ## Build Docker image for production
|
||||||
.PHONY: docker
|
.PHONY: docker
|
||||||
docker: image-dev docker-nobuild ## Run shell with source code and deps inside Docker
|
docker: image-dev docker-nobuild ## Run shell with source code and deps inside Docker
|
||||||
|
|
||||||
.PHONY: docker
|
.PHONY: docker-nobuild
|
||||||
docker-nobuild: ## Same as 'make docker', but don't rebuild image
|
docker-nobuild: ## Same as 'make docker', but don't rebuild image
|
||||||
scripts/docker.bash run -it --rm -v "$(PWD):/home/docker/src" -p 6119:6119 -p 6120:6120 -h riju riju bash
|
scripts/docker.bash run -it --rm -v "$(PWD):/home/docker/src" -p 6119:6119 -p 6120:6120 -h riju riju bash
|
||||||
|
|
||||||
.PHONY: deploy
|
.PHONY: deploy
|
||||||
deploy: ## Deploy current master from GitHub to production
|
deploy: image-prod ## Build, publish, and deploy production image
|
||||||
scripts/deploy.bash
|
scripts/deploy.bash
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"variables": {
|
||||||
|
"api_token": ""
|
||||||
|
},
|
||||||
|
"sensitive-variables": ["api_token"],
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"type": "digitalocean",
|
||||||
|
"api_token": "{{user `api_token`}}",
|
||||||
|
"image": "ubuntu-20-04-x64",
|
||||||
|
"region": "sfo3",
|
||||||
|
"size": "s-1vcpu-1gb",
|
||||||
|
"ssh_username": "root"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"provisioners": [
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"script": "provision-certbot.bash"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"variables": {
|
||||||
|
"api_token": "",
|
||||||
|
"admin_password": "",
|
||||||
|
"admin_ssh_public_key_file": "",
|
||||||
|
"deploy_ssh_public_key_file": "",
|
||||||
|
"docker_repo": "raxod502/riju"
|
||||||
|
},
|
||||||
|
"sensitive-variables": ["api_token", "admin_password"],
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"type": "digitalocean",
|
||||||
|
"api_token": "{{user `api_token`}}",
|
||||||
|
"image": "ubuntu-20-04-x64",
|
||||||
|
"region": "sfo3",
|
||||||
|
"size": "s-1vcpu-1gb",
|
||||||
|
"ssh_username": "root"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"provisioners": [
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"source": "{{user `admin_ssh_public_key_file`}}",
|
||||||
|
"destination": "/tmp/id_admin.pub"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"source": "{{user `deploy_ssh_public_key_file`}}",
|
||||||
|
"destination": "/tmp/id_deploy.pub"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"source": "resources/riju.bash",
|
||||||
|
"destination": "/tmp/riju.bash"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"source": "resources/riju.service",
|
||||||
|
"destination": "/tmp/riju.service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"source": "resources/rijuctl.bash",
|
||||||
|
"destination": "/tmp/rijuctl.bash"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"script": "provision.bash",
|
||||||
|
"environment_vars": [
|
||||||
|
"ADMIN_PASSWORD={{user `admin_password`}}",
|
||||||
|
"DOCKER_REPO={{user `docker_repo`}}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update
|
||||||
|
apt-get dist-upgrade -y
|
||||||
|
|
||||||
|
apt-get install -y certbot
|
||||||
|
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
if [[ -z "${ADMIN_PASSWORD}" ]]; then
|
||||||
|
echo "you need to set admin_password in secrets.json" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${DOCKER_REPO}" ]]; then
|
||||||
|
echo "internal error: somehow DOCKER_REPO was not set" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for user in admin deploy; do
|
||||||
|
if [[ ! -s "/tmp/id_${user}.pub" ]]; then
|
||||||
|
echo "you need to set ${user}_ssh_public_key_file in secrets.json" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -vq "PRIVATE KEY" "/tmp/id_${user}.pub"; then
|
||||||
|
echo "you accidentally set ${user}_ssh_public_key_file to a private key" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS=" " read contents < "/tmp/id_${user}.pub"
|
||||||
|
echo "${contents}" > "/tmp/id_${user}.pub"
|
||||||
|
done
|
||||||
|
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update
|
||||||
|
apt-get dist-upgrade -y
|
||||||
|
|
||||||
|
apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common
|
||||||
|
curl -sSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
|
||||||
|
add-apt-repository -n universe
|
||||||
|
add-apt-repository -n "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||||
|
|
||||||
|
packages="
|
||||||
|
|
||||||
|
bsdmainutils
|
||||||
|
certbot
|
||||||
|
containerd.io
|
||||||
|
docker-ce
|
||||||
|
docker-ce-cli
|
||||||
|
git
|
||||||
|
make
|
||||||
|
members
|
||||||
|
python3
|
||||||
|
tmux
|
||||||
|
vim
|
||||||
|
whois
|
||||||
|
|
||||||
|
"
|
||||||
|
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y ${packages}
|
||||||
|
|
||||||
|
sed -Ei 's/^#?PermitRootLogin .*/PermitRootLogin no/' /etc/ssh/sshd_config
|
||||||
|
sed -Ei 's/^#?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
||||||
|
sed -Ei 's/^#?PermitEmptyPasswords .*/PermitEmptyPasswords no/' /etc/ssh/sshd_config
|
||||||
|
|
||||||
|
passwd -l root
|
||||||
|
useradd admin -g admin -G sudo -s /usr/bin/bash -p "$(echo "${ADMIN_PASSWORD}" | mkpasswd -s)" -m
|
||||||
|
useradd deploy -s /usr/bin/bash -p "!"
|
||||||
|
|
||||||
|
for user in admin deploy; do
|
||||||
|
mkdir -p "/home/${user}/.ssh"
|
||||||
|
mv "/tmp/id_${user}.pub" "/home/${user}/.ssh/authorized_keys"
|
||||||
|
chown -R "${user}:${user}" "/home/${user}/.ssh"
|
||||||
|
chmod -R go-rwx "/home/${user}/.ssh"
|
||||||
|
done
|
||||||
|
|
||||||
|
sed -i 's/^/command="sudo rijuctl",restrict/' /home/deploy/.ssh/authorized_keys
|
||||||
|
|
||||||
|
cat <<"EOF" > /etc/sudoers.d/riju
|
||||||
|
deploy ALL=(root) NOPASSWD: /usr/local/bin/rijuctl
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sed -i "s#DOCKER_REPO_REPLACED_BY_PACKER#${DOCKER_REPO}#" /tmp/rijuctl.bash
|
||||||
|
|
||||||
|
mv /tmp/riju.bash /usr/local/bin/riju
|
||||||
|
mv /tmp/riju.service /etc/systemd/system/riju.service
|
||||||
|
mv /tmp/rijuctl.bash /usr/local/bin/rijuctl
|
||||||
|
|
||||||
|
chmod +x /usr/local/bin/riju
|
||||||
|
chmod +x /usr/local/bin/rijuctl
|
||||||
|
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
domain="$(ls /etc/letsencrypt/live | grep -v README | head -n1)"
|
||||||
|
|
||||||
|
if [[ -n "${domain}" ]]; then
|
||||||
|
echo "Detected cert for domain: ${domain}, enabling TLS" >&2
|
||||||
|
export TLS=1
|
||||||
|
TLS_PRIVATE_KEY="$(base64 "/etc/letsencrypt/live/${domain}/privkey.pem")"
|
||||||
|
TLS_CERTIFICATE="$(base64 "/etc/letsencrypt/live/${domain}/fullchain.pem")"
|
||||||
|
export TLS_PRIVATE_KEY TLS_CERTIFICATE
|
||||||
|
if [[ "${domain}" == riju.codes ]]; then
|
||||||
|
echo "Domain is riju.codes, enabling analytics" >&2
|
||||||
|
export ANALYTICS=1
|
||||||
|
else
|
||||||
|
echo "Domain is not riju.codes, disabling analytics" >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No certs installed in /etc/letsencrypt/live, disabling TLS" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -t 1 ]]; then
|
||||||
|
it=-it
|
||||||
|
else
|
||||||
|
it=
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker run ${it} -e TLS -e TLS_PRIVATE_KEY -e TLS_CERTIFICATE -e ANALYTICS \
|
||||||
|
--rm -p 0.0.0.0:80:6119 -p 0.0.0.0:443:6120 -h riju riju:live
|
|
@ -5,7 +5,7 @@ After=docker.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=exec
|
Type=exec
|
||||||
ExecStart=riju-serve
|
ExecStart=riju
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|
||||||
[Install]
|
[Install]
|
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
DOCKER_REPO="${DOCKER_REPO:-DOCKER_REPO_REPLACED_BY_PACKER}"
|
||||||
|
|
||||||
|
if [[ -n "${SSH_ORIGINAL_COMMAND}" ]]; then
|
||||||
|
set -- ${SSH_ORIGINAL_COMMAND}
|
||||||
|
fi
|
||||||
|
|
||||||
|
function usage {
|
||||||
|
echo "usage: rijuctl deploy TAG" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function main {
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
subcmd="$1"
|
||||||
|
shift
|
||||||
|
case "${subcmd}" in
|
||||||
|
deploy)
|
||||||
|
deploy "$@"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
function deploy {
|
||||||
|
if (( $# != 1 )); then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
tag="$1"
|
||||||
|
if [[ -z "${tag}" ]]; then
|
||||||
|
usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker pull "${DOCKER_REPO}:${tag}"
|
||||||
|
docker tag riju:live riju:prev
|
||||||
|
docker tag "${DOCKER_REPO}:${tag}" riju:live
|
||||||
|
docker system prune -f
|
||||||
|
systemctl restart riju
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
|
@ -1,41 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import errno
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import signal
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
result = subprocess.run(["pgrep", "-x", "riju-deploy"], stdout=subprocess.PIPE)
|
|
||||||
assert result.returncode in {0, 1}
|
|
||||||
for pid in result.stdout.decode().splitlines():
|
|
||||||
print(f"Found existing process {pid}, trying to kill ...", file=sys.stderr)
|
|
||||||
pid = int(pid)
|
|
||||||
os.kill(pid, signal.SIGTERM)
|
|
||||||
while True:
|
|
||||||
time.sleep(0.01)
|
|
||||||
try:
|
|
||||||
os.kill(pid, 0)
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno == errno.ESRCH:
|
|
||||||
break
|
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
|
||||||
os.chdir(tmpdir)
|
|
||||||
subprocess.run(
|
|
||||||
[
|
|
||||||
"git",
|
|
||||||
"clone",
|
|
||||||
"https://github.com/raxod502/riju.git",
|
|
||||||
"--single-branch",
|
|
||||||
"--depth=1",
|
|
||||||
"--no-tags",
|
|
||||||
],
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
os.chdir("riju")
|
|
||||||
subprocess.run(["scripts/deploy-phase2.py"], check=True)
|
|
|
@ -1,25 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import errno
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import signal
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
import time
|
|
||||||
|
|
||||||
subprocess.run(["docker", "pull", "ubuntu:rolling"], check=True)
|
|
||||||
subprocess.run(["docker", "system", "prune", "-f"], check=True)
|
|
||||||
subprocess.run(["make", "image-prod"], check=True)
|
|
||||||
existing_containers = subprocess.run(
|
|
||||||
["docker", "ps", "-q"], check=True, stdout=subprocess.PIPE
|
|
||||||
).stdout.splitlines()
|
|
||||||
subprocess.run(["scripts/install-scripts.bash"], check=True)
|
|
||||||
if existing_containers:
|
|
||||||
subprocess.run(["docker", "kill", *existing_containers], check=True)
|
|
||||||
subprocess.run(["systemctl", "enable", "riju"], check=True)
|
|
||||||
subprocess.run(["systemctl", "restart", "riju"], check=True)
|
|
||||||
|
|
||||||
print("==> Successfully deployed Riju! <==", file=sys.stderr)
|
|
|
@ -1,23 +1,50 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
set -e
|
set -euxo pipefail
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
tmpdir="$(mktemp -d)"
|
if [[ -z "${DOCKER_REPO}" ]]; then
|
||||||
keyfile="${tmpdir}/id"
|
echo "environment variable not set: DOCKER_REPO" >&2
|
||||||
|
|
||||||
if [[ -n "$DEPLOY_KEY" ]]; then
|
|
||||||
printf '%s\n' "$DEPLOY_KEY" | base64 -d > "$keyfile"
|
|
||||||
elif [[ -f "$HOME/.ssh/id_rsa_riju_deploy" ]]; then
|
|
||||||
cp "$HOME/.ssh/id_rsa_riju_deploy" "$keyfile"
|
|
||||||
else
|
|
||||||
echo 'deploy.bash: you must set $DEPLOY_KEY' >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chmod go-rw "$keyfile"
|
if [[ -z "${DOMAIN}" ]]; then
|
||||||
|
echo "environment variable not set: DOMAIN" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${DEPLOY_SSH_PRIVATE_KEY}" ]]; then
|
||||||
|
if [[ -f "$HOME/.ssh/id_rsa_riju_deploy" ]]; then
|
||||||
|
DEPLOY_SSH_PRIVATE_KEY="$(< "$HOME/.ssh/id_rsa_riju_deploy")"
|
||||||
|
else
|
||||||
|
echo "environment variable not set: DEPLOY_SSH_PRIVATE_KEY"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
DEPLOY_SSH_PRIVATE_KEY="$(printf '%s\n' "${DEPLOY_SSH_PRIVATE_KEY}" | base64 -d)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
tag="$(date +%s%3N)-$(git branch --show-current)-$(git rev-parse @)"
|
||||||
|
|
||||||
|
if [[ -n "$(git status --porcelain)" ]]; then
|
||||||
|
tag="${tag}-dirty"
|
||||||
|
fi
|
||||||
|
|
||||||
|
scripts/docker.bash tag riju:prod "${DOCKER_REPO}:${tag}"
|
||||||
|
scripts/docker.bash push "${DOCKER_REPO}:${tag}"
|
||||||
|
|
||||||
|
tmpdir="$(mktemp -d)"
|
||||||
|
|
||||||
|
function cleanup {
|
||||||
|
rm -rf "${tmpdir}"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
printf '%s' "${DEPLOY_SSH_PRIVATE_KEY}" > "${tmpdir}/id"
|
||||||
|
|
||||||
|
chmod go-rw "${tmpdir}/id"
|
||||||
ssh -o IdentitiesOnly=yes \
|
ssh -o IdentitiesOnly=yes \
|
||||||
-o StrictHostKeyChecking=no \
|
-o StrictHostKeyChecking=no \
|
||||||
-o UserKnownHostsFile=/dev/null \
|
-o UserKnownHostsFile=/dev/null \
|
||||||
-o LogLevel=QUIET \
|
-o LogLevel=QUIET \
|
||||||
-i "${keyfile}" deploy@riju.codes
|
-i "${tmpdir}/id" "deploy@${DOMAIN}" \
|
||||||
|
deploy "${tag}"
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
cp scripts/riju.service /etc/systemd/system/riju.service
|
|
||||||
cp scripts/riju-serve.bash /usr/local/bin/riju-serve
|
|
||||||
cp scripts/certbot-pre.bash /etc/letsencrypt/renewal-hooks/pre/riju
|
|
||||||
cp scripts/certbot-post.bash /etc/letsencrypt/renewal-hooks/post/riju
|
|
||||||
cp scripts/deploy-phase1.py /usr/local/bin/riju-deploy
|
|
||||||
|
|
||||||
systemctl daemon-reload
|
|
|
@ -1,22 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
TLS=1
|
|
||||||
TLS_PRIVATE_KEY="$(base64 /etc/letsencrypt/live/riju.codes/privkey.pem)"
|
|
||||||
TLS_CERTIFICATE="$(base64 /etc/letsencrypt/live/riju.codes/fullchain.pem)"
|
|
||||||
ANALYTICS=1
|
|
||||||
|
|
||||||
# Do this separately so that errors in command substitution will crash
|
|
||||||
# the script.
|
|
||||||
export TLS TLS_PRIVATE_KEY TLS_CERTIFICATE ANALYTICS
|
|
||||||
|
|
||||||
if [[ -t 1 ]]; then
|
|
||||||
it=-it
|
|
||||||
else
|
|
||||||
it=
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker run ${it} -e TLS -e TLS_PRIVATE_KEY -e TLS_CERTIFICATE -e ANALYTICS \
|
|
||||||
--rm -p 0.0.0.0:80:6119 -p 0.0.0.0:443:6120 -h riju riju:prod
|
|
Loading…
Reference in New Issue