Migrate to Packer
This commit is contained in:
parent
b97775abd1
commit
e466a94d18
|
@ -5,7 +5,13 @@ jobs:
|
|||
- image: alpine
|
||||
steps:
|
||||
- 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
|
||||
workflows:
|
||||
version: 2
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
*.log
|
||||
.log
|
||||
.lsp-repl-history
|
||||
do_digitalocean.pem
|
||||
node_modules
|
||||
out
|
||||
secrets.json
|
||||
tests
|
||||
tests-*
|
||||
|
|
4
Makefile
4
Makefile
|
@ -19,10 +19,10 @@ image-prod: ## Build Docker image for production
|
|||
.PHONY: 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
|
||||
scripts/docker.bash run -it --rm -v "$(PWD):/home/docker/src" -p 6119:6119 -p 6120:6120 -h riju riju bash
|
||||
|
||||
.PHONY: deploy
|
||||
deploy: ## Deploy current master from GitHub to production
|
||||
deploy: image-prod ## Build, publish, and deploy production image
|
||||
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]
|
||||
Type=exec
|
||||
ExecStart=riju-serve
|
||||
ExecStart=riju
|
||||
Restart=always
|
||||
|
||||
[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
|
||||
|
||||
set -e
|
||||
set -o pipefail
|
||||
set -euxo pipefail
|
||||
|
||||
tmpdir="$(mktemp -d)"
|
||||
keyfile="${tmpdir}/id"
|
||||
|
||||
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
|
||||
if [[ -z "${DOCKER_REPO}" ]]; then
|
||||
echo "environment variable not set: DOCKER_REPO" >&2
|
||||
exit 1
|
||||
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 \
|
||||
-o StrictHostKeyChecking=no \
|
||||
-o UserKnownHostsFile=/dev/null \
|
||||
-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