Skip to content

Commit

Permalink
🧱 Add Ansible playbook to manage cloud resources (#1701)
Browse files Browse the repository at this point in the history
  • Loading branch information
SPGoding authored Jan 4, 2025
1 parent 2ffd1de commit 52b4eca
Show file tree
Hide file tree
Showing 60 changed files with 3,591 additions and 13,237 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ module.exports = {
"parser": "@typescript-eslint/parser",
"parserOptions": {
"tsconfigRootDir": __dirname,
"project": "./packages/**/tsconfig.json"
// Avoid Out of Memory error
// https://github.com/typescript-eslint/typescript-eslint/issues/1192
"project": "./packages/tsconfig-eslint.json"
},
"plugins": [
"@typescript-eslint",
Expand Down
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/ansible/ [email protected]
3 changes: 2 additions & 1 deletion .packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,5 +124,6 @@
"commit": "f222cd48f53a3dfb73506bba8ce259cf6ad3fc5f",
"version": "4.4.4"
}
}
},
"web-api-server": {}
}
3 changes: 3 additions & 0 deletions ansible/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
secrets.yml
inventory-*.yml
!inventory-prod.yml
11 changes: 11 additions & 0 deletions ansible/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
An Ansible playbook for managing cloud infrastructure used by Spyglass's various services.

Current managed services:

* API: https://api.spyglassmc.com
* Discord bot
* Weblate: https://weblate.spyglassmc.com

Ansible variables:

* See each role's default variables.
5 changes: 5 additions & 0 deletions ansible/inventory-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ungrouped:
hosts:
api.spyglassmc.com:
vars:
ansible_ssh_pipelining: true
6 changes: 6 additions & 0 deletions ansible/playbook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- name: Install spyglassmc.ai Cloud (TM)
hosts: all
roles:
- api-server
- discord-bot
- weblate
4 changes: 4 additions & 0 deletions ansible/roles/acmesh/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# (str) Dynamic DNS key for TXT record on _acme-challenge.spyglassmc.com on Hurricane Electric DNS
acmesh_dynamic_dns_key: null
# (str) Email used for generating certificates with Let's Encrypt
acmesh_email: null
23 changes: 23 additions & 0 deletions ansible/roles/acmesh/files/dns_hedyn.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/bash

# https://github.com/acmesh-official/acme.sh/issues/3512

dns_hedyn_add() {
fulldomain=$1
txtvalue=$2

HEdyn_key="${HEdyn_key:-$(_readdomainconf HEdyn_key)}"
if [ "$HEdyn_key" ]; then
_savedomainconf HEdyn_key "$HEdyn_key"
elif [ -z "$HEdyn_key" ]; then
_err "You didn't specify HEdyn_key environment variable."
return 1
fi

hostname_encoded="$(printf "%s" "${fulldomain}" | _url_encode)"
password_encoded="$(printf "%s" "${HEdyn_key}" | _url_encode)"
txt_encoded="$(printf "%s" "${txtvalue}" | _url_encode)"
body="hostname=${hostname_encoded}&password=${password_encoded}&txt=${txt_encoded}"
response=$(_post "$body" "https://dyn.dns.he.net/nic/update")
test $response == 'good'
}
40 changes: 40 additions & 0 deletions ansible/roles/acmesh/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
- name: Install acme.sh
become: true
ansible.builtin.shell:
cmd: |
set -o pipefail
curl https://get.acme.sh | sh -s email={{ acmesh_email }}
executable: /bin/bash
creates: /root/.acme.sh/acme.sh

- name: Create certificate directory
become: true
ansible.builtin.file:
path: /etc/nginx/ssl/spyglassmc.com/
state: directory
mode: '755'

- name: Add Hurricane Electric dynamic DNS integration
become: true
ansible.builtin.copy:
src: dns_hedyn.sh
dest: /root/.acme.sh/dnsapi/dns_hedyn.sh
mode: '755'

- name: Issue certificate
become: true
ansible.builtin.shell:
cmd: |
set -e
set -o pipefail
(umask 066; touch /etc/nginx/ssl/spyglassmc.com/key.pem)
(umask 022; touch /etc/nginx/ssl/spyglassmc.com/cert.pem)
export HEdyn_key={{ acmesh_dynamic_dns_key }}
/root/.acme.sh/acme.sh --issue --server letsencrypt \
-d *.spyglassmc.com --dns dns_hedyn
/root/.acme.sh/acme.sh --install-cert -d *.spyglassmc.com \
--key-file /etc/nginx/ssl/spyglassmc.com/key.pem \
--fullchain-file /etc/nginx/ssl/spyglassmc.com/cert.pem \
--reloadcmd "service nginx reload"
executable: /bin/bash
creates: /etc/nginx/ssl/spyglassmc.com/key.pem
2 changes: 2 additions & 0 deletions ansible/roles/api-server/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# (str)
api_server_webhook_secret: null
14 changes: 14 additions & 0 deletions ansible/roles/api-server/files/spyglassmc-api-server.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[Unit]
Description=SpyglassMC API server service
After=network.target

[Service]
User=api-server
Group=api-server
WorkingDirectory=/var/lib/api-server
ExecStart=/var/lib/api-server/start.sh
Restart=always

[Install]
WantedBy=multi-user.target

4 changes: 4 additions & 0 deletions ansible/roles/api-server/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- name: Reload systemctl daemon
become: true
ansible.builtin.systemd:
daemon_reload: true
3 changes: 3 additions & 0 deletions ansible/roles/api-server/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies:
- common
- nodejs
54 changes: 54 additions & 0 deletions ansible/roles/api-server/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
- name: Install API server
become: true
community.general.npm:
name: '@spyglassmc/web-api-server'
global: true
state: latest

- name: Create user
become: true
block:
- name: Create group
ansible.builtin.group:
name: api-server
system: true

- name: Create user
ansible.builtin.user:
name: api-server
group: api-server
create_home: true
home: /var/lib/api-server
system: true

- name: Create launcher script
become: true
ansible.builtin.template:
src: start.sh
dest: /var/lib/api-server/start.sh
owner: api-server
group: api-server
mode: '700'

- name: Create service
become: true
ansible.builtin.copy:
src: spyglassmc-api-server.service
dest: /lib/systemd/system/spyglassmc-api-server.service
mode: '644'
notify: Reload systemctl daemon

- name: Start service
become: true
ansible.builtin.service:
name: spyglassmc-api-server
enabled: true
state: started

- name: Create nginx reverse proxy
become: true
ansible.builtin.template:
src: weblate.conf
dest: /etc/nginx/conf.d/api-server.conf
mode: '644'
notify: Reload nginx
21 changes: 21 additions & 0 deletions ansible/roles/api-server/templates/api-server.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
server {
listen 80;
listen [::]:80;

server_name api.spyglassmc.com;

return 308 https://$host$request_uri;
}

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;

server_name api.spyglassmc.com;

location / {
include proxy_params;
proxy_pass http://127.0.0.1:{{ api_server_port }};
}
}

7 changes: 7 additions & 0 deletions ansible/roles/api-server/templates/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash

export SPYGLASSMC_API_SERVER_DIR="/var/lib/api-server"
export SPYGLASSMC_API_SERVER_WEBHOOK_SECRET="{{ api_server_webhook_secret }}"
export SPYGLASSMC_API_SERVER_PORT="{{ api_server_port }}"

/usr/local/bin/spyglassmc-web-api-server
1 change: 1 addition & 0 deletions ansible/roles/api-server/vars/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
api_server_port: 3003
5 changes: 5 additions & 0 deletions ansible/roles/common/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- name: Update apt cache
become: true
ansible.builtin.apt:
update_cache: true
cache_valid_time: 86400 # 24 hours
6 changes: 6 additions & 0 deletions ansible/roles/discord-bot/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# (str)
discord_bot_client_id: null
# (str)
discord_bot_guild_id: null
# (str)
discord_bot_token: null
14 changes: 14 additions & 0 deletions ansible/roles/discord-bot/files/spyglassmc-discord-bot.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[Unit]
Description=SpyglassMC Discord bot service
After=network.target

[Service]
User=discord-bot
Group=discord-bot
WorkingDirectory=/var/lib/discord-bot
Environment="SPYGLASSMC_DISCORD_BOT_DIR=/var/lib/discord-bot"
ExecStart=/usr/local/bin/spyglassmc-discord-bot
Restart=always

[Install]
WantedBy=multi-user.target
4 changes: 4 additions & 0 deletions ansible/roles/discord-bot/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- name: Reload systemctl daemon
become: true
ansible.builtin.systemd:
daemon_reload: true
3 changes: 3 additions & 0 deletions ansible/roles/discord-bot/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies:
- common
- nodejs
45 changes: 45 additions & 0 deletions ansible/roles/discord-bot/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
- name: Install discord bot
become: true
community.general.npm:
name: '@spyglassmc/discord-bot'
global: true
state: latest

- name: Create user
become: true
block:
- name: Create group
ansible.builtin.group:
name: discord-bot
system: true

- name: Create user
ansible.builtin.user:
name: discord-bot
group: discord-bot
create_home: true
home: /var/lib/discord-bot
system: true

- name: Create config
become: true
become_user: discord-bot
ansible.builtin.template:
src: config.json
dest: /var/lib/discord-bot/config.json
mode: '600'

- name: Create service
become: true
ansible.builtin.copy:
src: spyglassmc-discord-bot.service
dest: /lib/systemd/system/spyglassmc-discord-bot.service
mode: '644'
notify: Reload systemctl daemon

- name: Start service
become: true
ansible.builtin.service:
name: spyglassmc-discord-bot
enabled: true
state: started
6 changes: 6 additions & 0 deletions ansible/roles/discord-bot/templates/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"clientId": "{{ discord_bot_client_id }}",
"guildId": "{{ discord_bot_guild_id }}",
"token": "{{ discord_bot_token }}"
}

2 changes: 2 additions & 0 deletions ansible/roles/docker/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dependencies:
- common
26 changes: 26 additions & 0 deletions ansible/roles/docker/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
- name: Add Docker apt repository
become: true
block:
- name: Add signing key
ansible.builtin.uri:
url: https://download.docker.com/linux/debian/gpg
dest: /etc/apt/keyrings/docker.asc
mode: '644'
status_code:
- 200 # OK
- 304 # Not Modified

- name: Add repository
ansible.builtin.apt_repository:
repo: 'deb [arch=arm64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian {{ ansible_distribution_release }} stable'
filename: docker

- name: Install Docker Engine
become: true
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
11 changes: 11 additions & 0 deletions ansible/roles/nginx/files/common.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ssl_certificate /etc/nginx/ssl/spyglassmc.com/cert.pem;
ssl_certificate_key /etc/nginx/ssl/spyglassmc.com/key.pem;

server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl default_server;
listen [::]:443 ssl default_server;

return 444;
}
Loading

0 comments on commit 52b4eca

Please sign in to comment.