From 4161e8909fa3412e3e5a3e3b2aaf90731a459e5a Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sat, 26 Oct 2024 22:11:49 +0000 Subject: [PATCH 1/6] Add umami backend --- .gitignore | 16 +++- infra/.terraform.lock.hcl | 63 +++++++++++++++ infra/bootstrap/data-volume.service | 20 +++++ infra/bootstrap/data-volume.sh | 5 ++ infra/bootstrap/docker-compose.sh | 7 ++ infra/bootstrap/docker-daemon.json | 8 ++ infra/bootstrap/umami.service | 19 +++++ infra/bootstrap/user_data.yml | 77 ++++++++++++++++++ infra/cloud_init.tf | 75 ++++++++++++++++++ infra/docker-compose.yml | 45 +++++++++++ infra/main.tf | 117 ++++++++++++++++++++++++++++ infra/outputs.tf | 15 ++++ infra/providers.tf | 20 +++++ infra/ssh.sh | 12 +++ infra/variables.tf | 23 ++++++ 15 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 infra/.terraform.lock.hcl create mode 100644 infra/bootstrap/data-volume.service create mode 100755 infra/bootstrap/data-volume.sh create mode 100644 infra/bootstrap/docker-compose.sh create mode 100644 infra/bootstrap/docker-daemon.json create mode 100644 infra/bootstrap/umami.service create mode 100644 infra/bootstrap/user_data.yml create mode 100644 infra/cloud_init.tf create mode 100644 infra/docker-compose.yml create mode 100644 infra/main.tf create mode 100644 infra/outputs.tf create mode 100644 infra/providers.tf create mode 100755 infra/ssh.sh create mode 100644 infra/variables.tf diff --git a/.gitignore b/.gitignore index 91128673..48323c98 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,20 @@ **/node_modules/ **/.vitepress/cache/ **/.vitepress/dist/ -articles.json *.dbg.s .idea/ + +.env + +# Terraform state files +terraform.tfstate* + +# Terraform plugins dir +.terraform + +# Temporary terraform fs lock file +.terraform.tfstate.lock.info + +# Terraform variables usually contain sensitive information +terraform.tfvars +terraform.tfvars.json diff --git a/infra/.terraform.lock.hcl b/infra/.terraform.lock.hcl new file mode 100644 index 00000000..50ec017a --- /dev/null +++ b/infra/.terraform.lock.hcl @@ -0,0 +1,63 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/cloudinit" { + version = "2.3.5" + constraints = "~> 2.3.5" + hashes = [ + "h1:HCoabXm6NQwCivl1q24+l9VUufc2mFqNeulsQBA9iFg=", + "zh:17c20574de8eb925b0091c9b6a4d859e9d6e399cd890b44cfbc028f4f312ac7a", + "zh:348664d9a900f7baf7b091cf94d657e4c968b240d31d9e162086724e6afc19d5", + "zh:5a876a468ffabff0299f8348e719cb704daf81a4867f8c6892f3c3c4add2c755", + "zh:6ef97ee4c8c6a69a3d36746ba5c857cf4f4d78f32aa3d0e1ce68f2ece6a5dba5", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:8283e5a785e3c518a440f6ac6e7cc4fc07fe266bf34974246f4e2ef05762feda", + "zh:a44eb5077950168b571b7eb65491246c00f45409110f0f172cc3a7605f19dba9", + "zh:aa0806cbff72b49c1b389c0b8e6904586e5259c08dabb7cb5040418568146530", + "zh:bec4613c3beaad9a7be7ca99cdb2852073f782355b272892e6ee97a22856aec1", + "zh:d7fe368577b6c8d1ae44c751ed42246754c10305c7f001cc0109833e95aa107d", + "zh:df2409fc6a364b1f0a0f8a9cd8a86e61e80307996979ce3790243c4ce88f2915", + "zh:ed3c263396ff1f4d29639cc43339b655235acf4d06296a7c120a80e4e0fd6409", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.3" + hashes = [ + "h1:+AnORRgFbRO6qqcfaQyeX80W0eX3VmjadjnUFUJTiXo=", + "zh:22d062e5278d872fe7aed834f5577ba0a5afe34a3bdac2b81f828d8d3e6706d2", + "zh:23dead00493ad863729495dc212fd6c29b8293e707b055ce5ba21ee453ce552d", + "zh:28299accf21763ca1ca144d8f660688d7c2ad0b105b7202554ca60b02a3856d3", + "zh:55c9e8a9ac25a7652df8c51a8a9a422bd67d784061b1de2dc9fe6c3cb4e77f2f", + "zh:756586535d11698a216291c06b9ed8a5cc6a4ec43eee1ee09ecd5c6a9e297ac1", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:9d5eea62fdb587eeb96a8c4d782459f4e6b73baeece4d04b4a40e44faaee9301", + "zh:a6355f596a3fb8fc85c2fb054ab14e722991533f87f928e7169a486462c74670", + "zh:b5a65a789cff4ada58a5baffc76cb9767dc26ec6b45c00d2ec8b1b027f6db4ed", + "zh:db5ab669cf11d0e9f81dc380a6fdfcac437aea3d69109c7aef1a5426639d2d65", + "zh:de655d251c470197bcbb5ac45d289595295acb8f829f6c781d4a75c8c8b7c7dd", + "zh:f5c68199f2e6076bce92a12230434782bf768103a427e9bb9abee99b116af7b5", + ] +} + +provider "registry.terraform.io/hetznercloud/hcloud" { + version = "1.48.1" + constraints = "~> 1.48.1" + hashes = [ + "h1:fa9fxdSV9DG+HDcXyRbcGfb6Dk94SBP3TamHb1yOYiI=", + "zh:086cce10cb005f25f85183c59e639d6675e91e919934c80f660ca1cc4b9bc09b", + "zh:111d185707168b90c7ed3d245b522b2bd508f0bd4275496a1acdc9c0adaa85f2", + "zh:1acba3f30150282d283c46cd7ce25e9afb8b027fd2f594d41de9131d25a42b27", + "zh:1f8858aa81f93d52550502a11c7ea4e9370316ab098f6b75a09ffe75da6129ee", + "zh:20e01e6e6f99f57b3c1ef2a9de5d617c0139d3f3934eeb5e6c5976ae8b831a48", + "zh:2a8489a586a7bdadc42bbc9e3cb7b9deaefdf8020e3f2caba2678877d5d64d52", + "zh:31d8017529b0429bc9e873ec5d358ab9b75af2ba0ae24f21abcd4d09f36b7ee9", + "zh:407b4d7f1407e7e4a51b6f4dcdb0c7fbf81f2f1e25a7275f34054009419125a2", + "zh:42cf7cf867d199054713d4e6060e4b578eff16f0f537e9aaa5fd990c3eab8bc6", + "zh:460ac856ff952c5d41525949b93cfb7ee642f900594eff965494f11999d7496b", + "zh:d09e527d23f62564c82bc24e286cf2cb8cb0ed6cdc6f4c66adf2145cfa62adac", + "zh:d465356710444ac70dea4883252efc429b73e79fc6dc94f075662b838476680e", + "zh:d476c8eca307e30a20eed54c0735b062a6f3066b4ac63eebecd38ab8f40c16f4", + "zh:e0e9b2f6d5e28dbd01fa1ec3147aa88062d6223c5146532a3dcd1d3bb827e1e9", + ] +} diff --git a/infra/bootstrap/data-volume.service b/infra/bootstrap/data-volume.service new file mode 100644 index 00000000..7f2fee46 --- /dev/null +++ b/infra/bootstrap/data-volume.service @@ -0,0 +1,20 @@ +[Unit] +Description=Data volume initialization + +# This unit is generated automatically by `systemd` using the builtin generator +# that reads the configurations from `/etc/fstab` +BindsTo=mnt-master.mount +After=mnt-master.mount + +[Service] +Type=oneshot +User=${server_os_user} +RemainAfterExit=yes +WorkingDirectory=/var/app +EnvironmentFile=${env_file_path} + +ExecStart=/var/app/data-volume.sh + +[Install] +RequiredBy=docker.service +Before=docker.service diff --git a/infra/bootstrap/data-volume.sh b/infra/bootstrap/data-volume.sh new file mode 100755 index 00000000..1e60f1e9 --- /dev/null +++ b/infra/bootstrap/data-volume.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +sudo mkdir -p $DATA_VOLUME_PATH/docker diff --git a/infra/bootstrap/docker-compose.sh b/infra/bootstrap/docker-compose.sh new file mode 100644 index 00000000..7c4139a0 --- /dev/null +++ b/infra/bootstrap/docker-compose.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euo pipefail + +echo "Running: docker compose $@" + +CURRENT_UID=$(id -u):$(id -g) docker compose $@ diff --git a/infra/bootstrap/docker-daemon.json b/infra/bootstrap/docker-daemon.json new file mode 100644 index 00000000..fed5ce5a --- /dev/null +++ b/infra/bootstrap/docker-daemon.json @@ -0,0 +1,8 @@ +{ + "data-root": "${data_volume_path}/docker", + "log-driver": "local", + "log-opts": { + "mode": "non-blocking", + "max-buffer-size": "5m" + } +} diff --git a/infra/bootstrap/umami.service b/infra/bootstrap/umami.service new file mode 100644 index 00000000..d7f979b8 --- /dev/null +++ b/infra/bootstrap/umami.service @@ -0,0 +1,19 @@ +[Unit] +Description=Umami Service (docker compose) + +BindsTo=docker.service +After=docker.service + +[Service] +Type=oneshot +User=${server_os_user} +RemainAfterExit=yes +WorkingDirectory=/var/app +EnvironmentFile=${env_file_path} + +ExecStartPre=-/var/app/docker-compose.sh rm +ExecStart=/var/app/docker-compose.sh up --detach --no-build --wait +ExecStop=/var/app/docker-compose.sh stop --timeout 60 + +[Install] +WantedBy=multi-user.target diff --git a/infra/bootstrap/user_data.yml b/infra/bootstrap/user_data.yml new file mode 100644 index 00000000..be2b254d --- /dev/null +++ b/infra/bootstrap/user_data.yml @@ -0,0 +1,77 @@ +#cloud-config + +disk_setup: + ${data_volume_device}: + table_type: gpt + layout: true + overwrite: false + +mounts: + - - ${data_volume_device} + - ${data_volume_path} + - ${data_volume_fs} + - defaults,noauto,x-systemd.growfs,x-systemd.makefs,x-systemd.device-timeout=10min + - "0" + - "0" + +groups: [docker] + +users: + - default + - name: ${server_os_user} + lock_passwd: true + shell: /bin/bash + ssh_authorized_keys: ["${ssh_public_key}"] + groups: docker + sudo: ALL=(ALL) NOPASSWD:ALL + +# package_update: true +# package_upgrade: true +# package_reboot_if_required: true +# packages: +# - apt-transport-https +# - ca-certificates +# - curl +# - gnupg + +write_files: + %{~ for path, file in files ~} + - encoding: gzip+base64 + content: ${file.content} + owner: '${server_os_user}:${server_os_user}' + path: ${path} + permissions: '${file.perms}' + defer: true + %{~ endfor ~} + +runcmd: + - | + log_content() { + echo "Contents of $1:" + cat $1 || echo "The file is absent at path $1" + } + log_content /var/run/reboot-required + log_content /var/run/reboot-required.pkgs + + - ip addr add ${server_ip} dev eth0 + + - systemctl enable --now data-volume.service + + - echo 'Installing docker...' + + # Installs docker and docker-compose on the server + # Based on instructions from https://docs.docker.com/engine/install/ubuntu/ + # and several github gists from here and there + + - export DOCKER_GPG=/etc/apt/keyrings/docker.gpg + - export DOCKER_URL=https://download.docker.com/linux/ubuntu + - mkdir -p /etc/apt/keyrings + - curl --retry 5 --retry-connrefused -fsSL $DOCKER_URL/gpg | gpg --dearmor -o $DOCKER_GPG + - 'echo "deb [arch=$(dpkg --print-architecture) signed-by=$DOCKER_GPG] $DOCKER_URL $(lsb_release -cs) stable" + | tee /etc/apt/sources.list.d/docker.list > /dev/null' + - apt-get update -y + - apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + - systemctl enable --now docker.service + + # Enable the systemd service responsible for managing Docker Compose services + - systemctl enable --now umami.service diff --git a/infra/cloud_init.tf b/infra/cloud_init.tf new file mode 100644 index 00000000..05a628b1 --- /dev/null +++ b/infra/cloud_init.tf @@ -0,0 +1,75 @@ +locals { + data_volume_path = "/mnt/master" + data_volume_fs = "ext4" + pg_data = "${local.data_volume_path}/data/postgres" + env_file_path = "/var/app/.env" + bootstrap = "${path.module}/bootstrap" + + template_files = { + "umami.service" = "/etc/systemd/system/umami.service" + "data-volume.service" = "/etc/systemd/system/data-volume.service" + "docker-daemon.json" = "/etc/docker/daemon.json" + } + data_files = merge( + { + "/var/app/docker-compose.yml" = file("${path.module}/docker-compose.yml") + (local.env_file_path) = join("\n", [for k, v in local.env_vars : "${k}=${v}"]) + }, + { + for source, target in local.template_files : + target => templatefile("${local.bootstrap}/${source}", local.template_vars) + } + ) + + exec_files = { + for file in fileset(local.bootstrap, "*.sh") : + "/var/app/${file}" => file("${local.bootstrap}/${file}") + } + + files_by_perms = { + "0444" = local.data_files + "0555" = local.exec_files + } + + template_vars = { + env_file_path = local.env_file_path + server_os_user = local.server_os_user + server_ip = hcloud_floating_ip.master.ip_address + + ssh_public_key = chomp(file("~/.ssh/id_rsa.pub")) + + data_volume_device = hcloud_volume.master.linux_device + data_volume_path = local.data_volume_path + data_volume_fs = local.data_volume_fs + } + + env_vars = { + PG_PASSWORD = var.pg_password + PG_DATA = local.pg_data + DATA_VOLUME_PATH = local.data_volume_path + UMAMI_APP_SECRET = var.umami_app_secret + } +} + +data "cloudinit_config" "master" { + part { + content = templatefile( + "${path.module}/bootstrap/user_data.yml", + merge( + local.template_vars, + { + files = merge( + flatten([ + for perms, files in local.files_by_perms : [ + for path, content in files : { + (path) = { content = base64gzip(content), perms = perms } + } + ] + ]) + ... + ) + } + ) + ) + } +} diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml new file mode 100644 index 00000000..88d64604 --- /dev/null +++ b/infra/docker-compose.yml @@ -0,0 +1,45 @@ +services: + umami: + image: docker.umami.is/umami-software/umami:postgresql-latest + ports: + - 3000:3000 + + env_file: .env + environment: + DATABASE_URL: postgresql://umami:${PG_PASSWORD}@postgres:5432/umami + DATABASE_TYPE: postgresql + APP_SECRET: ${UMAMI_APP_SECRET} + + networks: [postgres] + depends_on: + postgres: + condition: service_healthy + + restart: always + healthcheck: + test: [CMD, curl, http://localhost:3000/api/heartbeat] + interval: 10s + timeout: 10s + retries: 5 + + postgres: + image: postgres:17 + environment: + POSTGRES_USER: umami + POSTGRES_DB: umami + POSTGRES_PASSWORD: ${PG_PASSWORD} + + volumes: [postgres:/var/lib/postgresql/data] + networks: [postgres] + + healthcheck: + test: [CMD-SHELL, "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 10s + timeout: 5s + retries: 5 + +networks: + postgres: + +volumes: + postgres: diff --git a/infra/main.tf b/infra/main.tf new file mode 100644 index 00000000..7b14fc81 --- /dev/null +++ b/infra/main.tf @@ -0,0 +1,117 @@ +locals { + location = "fsn1" + + hostname = "hetzner-master" + + # XXX: using the name `admin` for the user is a bad idea. It does seem to work + # fine on Hetzner. However, when using Oracle Cloud, it was found that `admin` + # user name causes the server to be inaccessible via SSH. The supposition is + # that there is a conflict with the `admin` group name already present in + # the used Oracle Ubuntu AMI. + server_os_user = "mane" +} + +resource "hcloud_floating_ip" "master" { + type = "ipv4" + home_location = local.location +} + +resource "hcloud_floating_ip_assignment" "master" { + floating_ip_id = hcloud_floating_ip.master.id + server_id = hcloud_server.master.id +} + +resource "hcloud_server" "master" { + name = local.hostname + image = "ubuntu-24.04" + server_type = "cax21" + location = local.location + user_data = data.cloudinit_config.master.rendered + firewall_ids = [hcloud_firewall.this.id] + + public_net { + # Not having IPv4 enabled reduces the cost, but we need it because we are + # downloading some stuff from the public internet during the provisioning. + ipv4_enabled = true + ipv6_enabled = true + } +} + +resource "hcloud_volume" "master" { + name = "master" + size = 50 + location = local.location +} + +resource "hcloud_volume_attachment" "master" { + server_id = hcloud_server.master.id + volume_id = hcloud_volume.master.id + + # Automount doesn't work if server's cloud-init script contains `runcmd` module + # + # instead we use systemd mount unit via fstab + automount = false +} + +# HACK: we need to gracefully shutdown our systemd service with the database +# docker container before the data volume is detached. This null resource +# depends on the volume attachment resource, so the remote-exec provisioner +# teardown script will be run before the attachment is destroyed. +# +# Unfortunately, it's not possible to do this with `systemd`. The volume detach +# sequence is undocumented in Hetzner docs. One would expect that all `systemd` +# services dependent upon the volume's mount are stopped before the volume +# is detached but this isn't true. +# +# The reality is cruel. It was experimentally found that the volume is +# detached abruptly. Therefore the database doesn't have time to +# flush its data to the disk, which means potential data loss. +resource "null_resource" "teardown" { + triggers = { + data_volume_attachment_id = hcloud_volume_attachment.master.id + + # The data volume attachment ID is enough for the trigger, but these + # triggers are needed to workaround the problem that it's impossible + # to reference symbols other than `self` variable in the provisioner block. + # + # Issue in terraform: https://github.com/hashicorp/terraform/issues/23679 + server_ip = hcloud_server.master.ipv4_address + server_os_user = local.server_os_user + } + + provisioner "remote-exec" { + when = destroy + + inline = [ + <<-SCRIPT + #!/usr/bin/env bash + set -euo pipefail + sudo systemctl stop umami.service + SCRIPT + ] + + connection { + host = self.triggers.server_ip + user = self.triggers.server_os_user + } + } +} + +resource "hcloud_firewall" "this" { + name = "allow-inbound" + rule { + direction = "in" + protocol = "tcp" + port = "22" + source_ips = var.allowed_ssh_ips + } + rule { + direction = "in" + protocol = "tcp" + port = "3000" + source_ips = [ + "0.0.0.0/0", + "::/0" + ] + } +} diff --git a/infra/outputs.tf b/infra/outputs.tf new file mode 100644 index 00000000..8943d7ad --- /dev/null +++ b/infra/outputs.tf @@ -0,0 +1,15 @@ +output "server_ip" { + value = hcloud_floating_ip.master.ip_address +} + +output "server_status" { + value = hcloud_server.master.status +} + +output "data_volume_path" { + value = local.data_volume_path +} + +output "server_os_user" { + value = local.server_os_user +} diff --git a/infra/providers.tf b/infra/providers.tf new file mode 100644 index 00000000..67f91754 --- /dev/null +++ b/infra/providers.tf @@ -0,0 +1,20 @@ +provider "hcloud" { + token = var.hcloud_token +} + +terraform { + # Make sure to keep it in sync with the version requirement on CI + required_version = ">= 1.9" + + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + version = "~> 1.48.1" + } + + cloudinit = { + source = "hashicorp/cloudinit" + version = "~> 2.3.5" + } + } +} diff --git a/infra/ssh.sh b/infra/ssh.sh new file mode 100755 index 00000000..47cf8c43 --- /dev/null +++ b/infra/ssh.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -euo pipefail + +tf_output=$(terraform output -json) + +os_user=$(echo "$tf_output" | jq -r '.server_os_user.value') +ip=$(echo "$tf_output" | jq -r '.server_ip.value') + +echo "> ssh $os_user@$ip" + +ssh -t "$os_user@$ip" diff --git a/infra/variables.tf b/infra/variables.tf new file mode 100644 index 00000000..e2ac9a35 --- /dev/null +++ b/infra/variables.tf @@ -0,0 +1,23 @@ +variable "allowed_ssh_ips" { + nullable = false + type = list(string) + sensitive = true +} + +variable "pg_password" { + nullable = false + type = string + sensitive = true +} + +variable "umami_app_secret" { + nullable = false + type = string + sensitive = true +} + +variable "hcloud_token" { + nullable = false + type = string + sensitive = true +} From 0aae64e954ec58d0a38edb445980d0089e477f6c Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sat, 26 Oct 2024 22:59:18 +0000 Subject: [PATCH 2/6] Nest under the website folder --- {infra => website/infra}/.terraform.lock.hcl | 0 website/infra/README.md | 5 +++ .../infra}/bootstrap/data-volume.service | 0 .../infra}/bootstrap/data-volume.sh | 0 .../infra}/bootstrap/docker-compose.sh | 0 .../infra}/bootstrap/docker-daemon.json | 0 .../infra}/bootstrap/umami.service | 0 .../infra}/bootstrap/user_data.yml | 0 {infra => website/infra}/cloud_init.tf | 0 {infra => website/infra}/docker-compose.yml | 0 {infra => website/infra}/main.tf | 32 ++++++++----------- {infra => website/infra}/outputs.tf | 0 {infra => website/infra}/providers.tf | 0 {infra => website/infra}/ssh.sh | 0 {infra => website/infra}/variables.tf | 0 15 files changed, 19 insertions(+), 18 deletions(-) rename {infra => website/infra}/.terraform.lock.hcl (100%) create mode 100644 website/infra/README.md rename {infra => website/infra}/bootstrap/data-volume.service (100%) rename {infra => website/infra}/bootstrap/data-volume.sh (100%) rename {infra => website/infra}/bootstrap/docker-compose.sh (100%) rename {infra => website/infra}/bootstrap/docker-daemon.json (100%) rename {infra => website/infra}/bootstrap/umami.service (100%) rename {infra => website/infra}/bootstrap/user_data.yml (100%) rename {infra => website/infra}/cloud_init.tf (100%) rename {infra => website/infra}/docker-compose.yml (100%) rename {infra => website/infra}/main.tf (76%) rename {infra => website/infra}/outputs.tf (100%) rename {infra => website/infra}/providers.tf (100%) rename {infra => website/infra}/ssh.sh (100%) rename {infra => website/infra}/variables.tf (100%) diff --git a/infra/.terraform.lock.hcl b/website/infra/.terraform.lock.hcl similarity index 100% rename from infra/.terraform.lock.hcl rename to website/infra/.terraform.lock.hcl diff --git a/website/infra/README.md b/website/infra/README.md new file mode 100644 index 00000000..e919ebd6 --- /dev/null +++ b/website/infra/README.md @@ -0,0 +1,5 @@ +# umami backend + +This directory contains the deployment code for our [umami](https://umami.is/) backend used for collecting anonymous statics about the usage of our documentation website. The code for this lives here in the open for the sake of transparency and sharing (in case if you want to self-host your own umami instance on Hetzner). + +It is a simple Hetzner VPS that runs a docker-compose cluster with the umami service and Postgres. The data is stored on a separate volume. The server is allocated a static IPv4. diff --git a/infra/bootstrap/data-volume.service b/website/infra/bootstrap/data-volume.service similarity index 100% rename from infra/bootstrap/data-volume.service rename to website/infra/bootstrap/data-volume.service diff --git a/infra/bootstrap/data-volume.sh b/website/infra/bootstrap/data-volume.sh similarity index 100% rename from infra/bootstrap/data-volume.sh rename to website/infra/bootstrap/data-volume.sh diff --git a/infra/bootstrap/docker-compose.sh b/website/infra/bootstrap/docker-compose.sh similarity index 100% rename from infra/bootstrap/docker-compose.sh rename to website/infra/bootstrap/docker-compose.sh diff --git a/infra/bootstrap/docker-daemon.json b/website/infra/bootstrap/docker-daemon.json similarity index 100% rename from infra/bootstrap/docker-daemon.json rename to website/infra/bootstrap/docker-daemon.json diff --git a/infra/bootstrap/umami.service b/website/infra/bootstrap/umami.service similarity index 100% rename from infra/bootstrap/umami.service rename to website/infra/bootstrap/umami.service diff --git a/infra/bootstrap/user_data.yml b/website/infra/bootstrap/user_data.yml similarity index 100% rename from infra/bootstrap/user_data.yml rename to website/infra/bootstrap/user_data.yml diff --git a/infra/cloud_init.tf b/website/infra/cloud_init.tf similarity index 100% rename from infra/cloud_init.tf rename to website/infra/cloud_init.tf diff --git a/infra/docker-compose.yml b/website/infra/docker-compose.yml similarity index 100% rename from infra/docker-compose.yml rename to website/infra/docker-compose.yml diff --git a/infra/main.tf b/website/infra/main.tf similarity index 76% rename from infra/main.tf rename to website/infra/main.tf index 7b14fc81..239d3eab 100644 --- a/infra/main.tf +++ b/website/infra/main.tf @@ -8,7 +8,7 @@ locals { # user name causes the server to be inaccessible via SSH. The supposition is # that there is a conflict with the `admin` group name already present in # the used Oracle Ubuntu AMI. - server_os_user = "mane" + server_os_user = "master" } resource "hcloud_floating_ip" "master" { @@ -27,11 +27,9 @@ resource "hcloud_server" "master" { server_type = "cax21" location = local.location user_data = data.cloudinit_config.master.rendered - firewall_ids = [hcloud_firewall.this.id] + firewall_ids = [hcloud_firewall.master.id] public_net { - # Not having IPv4 enabled reduces the cost, but we need it because we are - # downloading some stuff from the public internet during the provisioning. ipv4_enabled = true ipv6_enabled = true } @@ -64,21 +62,19 @@ resource "hcloud_volume_attachment" "master" { # is detached but this isn't true. # # The reality is cruel. It was experimentally found that the volume is -# detached abruptly. Therefore the database doesn't have time to -# flush its data to the disk, which means potential data loss. -resource "null_resource" "teardown" { - triggers = { - data_volume_attachment_id = hcloud_volume_attachment.master.id - - # The data volume attachment ID is enough for the trigger, but these - # triggers are needed to workaround the problem that it's impossible - # to reference symbols other than `self` variable in the provisioner block. - # - # Issue in terraform: https://github.com/hashicorp/terraform/issues/23679 +# detached abruptly. Therefore the database doesn't have time to flush its data +# to disk, which means potential corruption or even data loss. +resource "terraform_data" "teardown" { + triggers_replace = [ + hcloud_volume_attachment.master.id + ] + + input = { server_ip = hcloud_server.master.ipv4_address server_os_user = local.server_os_user } + provisioner "remote-exec" { when = destroy @@ -91,13 +87,13 @@ resource "null_resource" "teardown" { ] connection { - host = self.triggers.server_ip - user = self.triggers.server_os_user + host = self.input.server_ip + user = self.input.server_os_user } } } -resource "hcloud_firewall" "this" { +resource "hcloud_firewall" "master" { name = "allow-inbound" rule { direction = "in" diff --git a/infra/outputs.tf b/website/infra/outputs.tf similarity index 100% rename from infra/outputs.tf rename to website/infra/outputs.tf diff --git a/infra/providers.tf b/website/infra/providers.tf similarity index 100% rename from infra/providers.tf rename to website/infra/providers.tf diff --git a/infra/ssh.sh b/website/infra/ssh.sh similarity index 100% rename from infra/ssh.sh rename to website/infra/ssh.sh diff --git a/infra/variables.tf b/website/infra/variables.tf similarity index 100% rename from infra/variables.tf rename to website/infra/variables.tf From 07b0ebe36bc500ad7876c5cb7017daccf5789c6c Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sat, 26 Oct 2024 23:37:45 +0000 Subject: [PATCH 3/6] Add cname --- website/public/CNAME | 1 + 1 file changed, 1 insertion(+) create mode 100644 website/public/CNAME diff --git a/website/public/CNAME b/website/public/CNAME new file mode 100644 index 00000000..f465878b --- /dev/null +++ b/website/public/CNAME @@ -0,0 +1 @@ +bon.rust \ No newline at end of file From c24a6b92b2bdc987dff942ffd46141c4c77ea8a7 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 27 Oct 2024 14:05:48 +0000 Subject: [PATCH 4/6] Switch to port 80 and cloudflare --- website/infra/docker-compose.yml | 2 +- website/infra/main.tf | 2 +- website/public/CNAME | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/website/infra/docker-compose.yml b/website/infra/docker-compose.yml index 88d64604..ae2e3b35 100644 --- a/website/infra/docker-compose.yml +++ b/website/infra/docker-compose.yml @@ -2,7 +2,7 @@ services: umami: image: docker.umami.is/umami-software/umami:postgresql-latest ports: - - 3000:3000 + - 80:3000 env_file: .env environment: diff --git a/website/infra/main.tf b/website/infra/main.tf index 239d3eab..70f1e4c8 100644 --- a/website/infra/main.tf +++ b/website/infra/main.tf @@ -104,7 +104,7 @@ resource "hcloud_firewall" "master" { rule { direction = "in" protocol = "tcp" - port = "3000" + port = "80" source_ips = [ "0.0.0.0/0", "::/0" diff --git a/website/public/CNAME b/website/public/CNAME index f465878b..c5dc03e1 100644 --- a/website/public/CNAME +++ b/website/public/CNAME @@ -1 +1 @@ -bon.rust \ No newline at end of file +bon-rs.com From aa98e3f5623af394e64dd05a8556a53a7f51c9ce Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 27 Oct 2024 14:19:40 +0000 Subject: [PATCH 5/6] Link umami --- CHANGELOG.md | 2 +- CONTRIBUTING.md | 2 +- README.md | 8 ++-- bon-cli/Cargo.toml | 2 +- bon-macros/Cargo.toml | 2 +- bon-macros/src/builder/builder_gen/mod.rs | 2 +- bon-macros/src/builder/builder_gen/models.rs | 2 +- bon-macros/src/lib.rs | 10 ++-- bon/Cargo.toml | 2 +- bon/src/__/ide.rs | 50 ++++++++++---------- bon/src/lib.rs | 4 +- website/.vitepress/config.mts | 17 +++---- website/README.md | 2 +- website/changelog.md | 23 ++++----- 14 files changed, 65 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bdba88e9..08726e3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1 @@ -See the [changelog](https://elastio.github.io/bon/changelog) page on the website for details. +See the [changelog](https://bon-rs.com/changelog) page on the website for details. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf8ad5f3..b717a91c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1 @@ -See the [contributing](https://elastio.github.io/bon/guide/internal/contributing) page on the website for details. +See the [contributing](https://bon-rs.com/guide/internal/contributing) page on the website for details. diff --git a/README.md b/README.md index 31395e86..799b6108 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ - + bon home @@ -34,7 +34,7 @@ `bon` is a Rust crate for generating compile-time-checked builders for functions and structs. It also provides idiomatic partial application with optional and named parameters for functions and methods. -Visit the [guide for a complete overview of the crate](https://elastio.github.io/bon/guide/overview). +Visit the [guide for a complete overview of the crate](https://bon-rs.com/guide/overview). ## Quick examples @@ -133,7 +133,7 @@ assert_eq!(user.level, Some(24)); assert!(user.is_admin); ``` -See [the guide](https://elastio.github.io/bon/guide/overview) for the rest. +See [the guide](https://bon-rs.com/guide/overview) for the rest. --- diff --git a/bon-cli/Cargo.toml b/bon-cli/Cargo.toml index 49f43c38..8b757f33 100644 --- a/bon-cli/Cargo.toml +++ b/bon-cli/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" description = "Dev tool for working with the `bon` crate" edition = "2021" -homepage = "https://elastio.github.io/bon/" +homepage = "https://bon-rs.com/" license = "MIT OR Apache-2.0" repository = "https://github.com/elastio/bon" diff --git a/bon-macros/Cargo.toml b/bon-macros/Cargo.toml index 6f9b6c50..5f8e88da 100644 --- a/bon-macros/Cargo.toml +++ b/bon-macros/Cargo.toml @@ -8,7 +8,7 @@ detail of the `bon` crate """ edition = "2021" -homepage = "https://elastio.github.io/bon/" +homepage = "https://bon-rs.com/" license = "MIT OR Apache-2.0" repository = "https://github.com/elastio/bon" diff --git a/bon-macros/src/builder/builder_gen/mod.rs b/bon-macros/src/builder/builder_gen/mod.rs index b2e7fdf1..db1b5b11 100644 --- a/bon-macros/src/builder/builder_gen/mod.rs +++ b/bon-macros/src/builder/builder_gen/mod.rs @@ -353,7 +353,7 @@ impl BuilderGenCtx { // The fields can't be hidden using Rust's privacy syntax. // The details about this are described in the blog post: - // https://elastio.github.io/bon/blog/the-weird-of-function-local-types-in-rust. + // https://bon-rs.com/blog/the-weird-of-function-local-types-in-rust. // // We could use `#[cfg(not(rust_analyzer))]` to hide the private fields in IDE. // However, RA would then not be able to type-check the generated code, which diff --git a/bon-macros/src/builder/builder_gen/models.rs b/bon-macros/src/builder/builder_gen/models.rs index 0626a7a9..d2d1f82e 100644 --- a/bon-macros/src/builder/builder_gen/models.rs +++ b/bon-macros/src/builder/builder_gen/models.rs @@ -168,7 +168,7 @@ pub(crate) struct BuilderGenCtx { /// /// This is an unfortunate workaround due to the limitations of defining the /// builder type inside of a nested module. See more details on this problem in -/// +/// pub(super) struct PrivateIdentsPool { pub(super) phantom: syn::Ident, pub(super) receiver: syn::Ident, diff --git a/bon-macros/src/lib.rs b/bon-macros/src/lib.rs index fa92e4ab..21bdfbc9 100644 --- a/bon-macros/src/lib.rs +++ b/bon-macros/src/lib.rs @@ -99,8 +99,8 @@ mod tests; /// or setting the same field twice will be reported as compile-time errors. /// /// See the full documentation for more details: -/// - [Guide](https://elastio.github.io/bon/guide/overview) -/// - [Attributes reference](https://elastio.github.io/bon/reference/builder) +/// - [Guide](https://bon-rs.com/guide/overview) +/// - [Attributes reference](https://bon-rs.com/reference/builder) #[proc_macro_attribute] pub fn builder( params: proc_macro::TokenStream, @@ -142,8 +142,8 @@ pub fn builder( /// or setting the same field twice will be reported as compile-time errors. /// /// See the full documentation for more details: -/// - [Guide](https://elastio.github.io/bon/guide/overview) -/// - [Attributes reference](https://elastio.github.io/bon/reference/builder) +/// - [Guide](https://bon-rs.com/guide/overview) +/// - [Attributes reference](https://bon-rs.com/reference/builder) #[proc_macro_derive(Builder, attributes(builder))] pub fn derive_builder(item: proc_macro::TokenStream) -> proc_macro::TokenStream { builder::generate_from_derive(item.into()).into() @@ -203,7 +203,7 @@ pub fn derive_builder(item: proc_macro::TokenStream) -> proc_macro::TokenStream /// or setting the same field twice will be reported as compile-time errors. /// /// For details on this macro including the reason why it's needed see -/// [this paragraph in the overview](https://elastio.github.io/bon/guide/overview#builder-for-an-associated-method). +/// [this paragraph in the overview](https://bon-rs.com/guide/overview#builder-for-an-associated-method). /// /// [`builder`]: macro@builder #[proc_macro_attribute] diff --git a/bon/Cargo.toml b/bon/Cargo.toml index d3c73d9a..e124004a 100644 --- a/bon/Cargo.toml +++ b/bon/Cargo.toml @@ -14,7 +14,7 @@ categories = [ keywords = ["builder", "macro", "derive", "constructor", "setter"] edition = "2021" -homepage = "https://elastio.github.io/bon/" +homepage = "https://bon-rs.com/" license = "MIT OR Apache-2.0" repository = "https://github.com/elastio/bon" diff --git a/bon/src/__/ide.rs b/bon/src/__/ide.rs index 7d06a6f1..8babac5e 100644 --- a/bon/src/__/ide.rs +++ b/bon/src/__/ide.rs @@ -8,87 +8,87 @@ pub mod builder_top_level { use super::*; - /// See the docs at + /// See the docs at pub const builder_type: Option = None; pub mod builder_type { use super::*; - /// See the docs at + /// See the docs at pub const name: Identifier = Identifier; - /// See the docs at + /// See the docs at pub const vis: VisibilityString = VisibilityString; - /// See the docs at + /// See the docs at pub const doc: DocComments = DocComments; } - /// See the docs at + /// See the docs at pub const finish_fn: Option = None; - /// See the docs at + /// See the docs at pub mod finish_fn { use super::*; - /// See the docs at + /// See the docs at pub const name: Identifier = Identifier; - /// See the docs at + /// See the docs at pub const vis: VisibilityString = VisibilityString; - /// See the docs at + /// See the docs at pub const doc: DocComments = DocComments; } - /// See the docs at + /// See the docs at pub const start_fn: Option = None; - /// See the docs at + /// See the docs at pub mod start_fn { use super::*; - /// See the docs at + /// See the docs at pub const name: Identifier = Identifier; - /// See the docs at + /// See the docs at pub const vis: VisibilityString = VisibilityString; - /// See the docs at + /// See the docs at pub const doc: DocComments = DocComments; } - /// See the docs at + /// See the docs at pub const state_mod: Option = None; - /// See the docs at + /// See the docs at pub mod state_mod { use super::*; - /// See the docs at + /// See the docs at pub const name: Identifier = Identifier; - /// See the docs at + /// See the docs at pub const vis: VisibilityString = VisibilityString; - /// See the docs at + /// See the docs at pub const doc: DocComments = DocComments; } - /// See the docs at + /// See the docs at pub mod on { use super::*; - /// See the docs at + /// See the docs at pub const into: Flag = Flag; } - /// See the docs at + /// See the docs at pub mod derive { - /// See the docs at + /// See the docs at pub use core::fmt::Debug; - /// See the docs at + /// See the docs at pub use core::clone::Clone; } @@ -96,7 +96,7 @@ pub mod builder_top_level { /// It's hinted with an underscore due to the limitations of the current /// completions limitation. This will be fixed in the future. /// - /// See the docs at + /// See the docs at pub const crate_: Option = None; } diff --git a/bon/src/lib.rs b/bon/src/lib.rs index bfc2a9ed..36f6a57c 100644 --- a/bon/src/lib.rs +++ b/bon/src/lib.rs @@ -1,6 +1,6 @@ #![doc( - html_logo_url = "https://elastio.github.io/bon/bon-logo-thumb.png", - html_favicon_url = "https://elastio.github.io/bon/bon-logo-medium.png" + html_logo_url = "https://bon-rs.com/bon-logo-thumb.png", + html_favicon_url = "https://bon-rs.com/bon-logo-medium.png" )] #![doc = include_str!("../README.md")] #![cfg_attr(not(feature = "std"), no_std)] diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts index 21790290..0f28f111 100644 --- a/website/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -2,8 +2,6 @@ import { defineConfig } from "vitepress"; import { abbr } from "@mdit/plugin-abbr"; import * as v1 from "../v1/config.mjs"; -const base = "/bon/"; - // https://vitepress.dev/reference/site-config export default defineConfig({ title: "Bon", @@ -24,13 +22,16 @@ export default defineConfig({ }, }, - base, - head: [ - ["link", { rel: "icon", href: `${base}bon-logo-thumb.png` }], + ["link", { rel: "icon", href: `bon-logo-thumb.png` }], + ["meta", { property: "og:image", content: `bon-logo-thumb.png` }], [ - "meta", - { property: "og:image", content: `${base}bon-logo-thumb.png` }, + "script", + { + defer: "", + src: "https://umami.bon-rs.com/script.js", + "data-website-id": "10c1ad05-7a6e-49ee-8633-5f8f75de4ab9", + }, ], ], @@ -134,7 +135,7 @@ export default defineConfig({ { text: "Shared Configuration", link: "/guide/patterns/shared-configuration", - } + }, ], }, { diff --git a/website/README.md b/website/README.md index cdebba6b..98ebadd3 100644 --- a/website/README.md +++ b/website/README.md @@ -1,6 +1,6 @@ # bon website -This folder contains the source code and markdown files that comprise the [bon website](https://elastio.github.io/bon/). +This folder contains the source code and markdown files that comprise the [bon website](https://bon-rs.com/). The website is built using [VitePress](https://vitepress.dev/). It's a simple and elegant framework for static websites that hides a lot of complexity from you. You don't need to be a TypeScript expert let alone a Vue expert to get around this directory. diff --git a/website/changelog.md b/website/changelog.md index 03d829e8..27894415 100644 --- a/website/changelog.md +++ b/website/changelog.md @@ -38,16 +38,17 @@ All the breaking changes are very unlikely to actually break your code that was ### Fixed -- Fixed `#[cfg/cfg_attr()]` not being expanded when used on function arguments with doc comments or other attributes. +- Fix `#[cfg/cfg_attr()]` not being expanded when used on function arguments with doc comments or other attributes. ### Other -- Added graceful internal panic handling. If some `bon` macro panics due to an internal bug, the macro will try to still generate a fallback for IDEs to still provide intellisense ([#145](https://github.com/elastio/bon/pull/145)) +- Add graceful internal panic handling. If some `bon` macro panics due to an internal bug, the macro will try to still generate a fallback for IDEs to still provide intellisense ([#145](https://github.com/elastio/bon/pull/145)) +- Switch from `elastio.github.io/bon` to a custom domain `bon-rs.com` ([#158](https://github.com/elastio/bon/pull/158)) ## [2.3.0](https://github.com/elastio/bon/compare/v2.2.1...v2.3.0) - 2024-09-14 -See the [blog post for this release](https://elastio.github.io/bon/blog/bon-builder-v2-3-release) that describes some of the most notable changes in detail. +See the [blog post for this release](https://bon-rs.com/blog/bon-builder-v2-3-release) that describes some of the most notable changes in detail. ### Added @@ -67,13 +68,13 @@ See the [blog post for this release](https://elastio.github.io/bon/blog/bon-buil ## [2.2.0](https://github.com/elastio/bon/compare/v2.1.1...v2.2.0) - 2024-09-08 -See the [blog post for this release](https://elastio.github.io/bon/blog/bon-builder-v2-2-release) that describes some of the most notable changes in detail. +See the [blog post for this release](https://bon-rs.com/blog/bon-builder-v2-2-release) that describes some of the most notable changes in detail. ### Changed -- The `#[bon::builder]` attribute was deprecated on structs. The new [`#[derive(bon::Builder)]`](https://elastio.github.io/bon/reference/builder) should be used to derive a builder from a struct. Starting with `bon` 2.3 (next minor release) all usages of `#[bon::builder]` on structs will generate deprecation warnings. ([#99](https://github.com/elastio/bon/pull/99)). +- The `#[bon::builder]` attribute was deprecated on structs. The new [`#[derive(bon::Builder)]`](https://bon-rs.com/reference/builder) should be used to derive a builder from a struct. Starting with `bon` 2.3 (next minor release) all usages of `#[bon::builder]` on structs will generate deprecation warnings. ([#99](https://github.com/elastio/bon/pull/99)). - There is a CLI to assist in migrating to the new syntax. See the [release blog post](https://elastio.github.io/bon/blog/bon-builder-v2-2-release#derive-builder-syntax-for-structs) for details about that. + There is a CLI to assist in migrating to the new syntax. See the [release blog post](https://bon-rs.com/blog/bon-builder-v2-2-release#derive-builder-syntax-for-structs) for details about that. ### Added @@ -100,7 +101,7 @@ See the [blog post for this release](https://elastio.github.io/bon/blog/bon-buil ## [2.1.0](https://github.com/elastio/bon/compare/v2.0.1...v2.1.0) - 2024-09-01 -See the [blog post for this release](https://elastio.github.io/bon/blog/bon-builder-v2-1-release) that describes some of the most notable changes in detail. +See the [blog post for this release](https://bon-rs.com/blog/bon-builder-v2-1-release) that describes some of the most notable changes in detail. ### Added @@ -125,8 +126,8 @@ See the [blog post for this release](https://elastio.github.io/bon/blog/bon-buil ### Docs -- Add a new section ["`None` literals inference"](https://elastio.github.io/bon/guide/patterns/into-conversions-in-depth#none-literals-inference) to docs for "Into Conversions In-Depth" -- Fix the docs about the comparison of Into conversions on the ["Alternatives"](http://elastio.github.io/bon/guide/alternatives) page that were not updated during the v2 release +- Add a new section ["`None` literals inference"](https://bon-rs.com/guide/patterns/into-conversions-in-depth#none-literals-inference) to docs for "Into Conversions In-Depth" +- Fix the docs about the comparison of Into conversions on the ["Alternatives"](http://bon-rs.com/guide/alternatives) page that were not updated during the v2 release ### Fixed @@ -139,7 +140,7 @@ See the [blog post for this release](https://elastio.github.io/bon/blog/bon-buil ## [2.0.0](https://github.com/elastio/bon/compare/v1.2.1...v2.0.0) - 2024-08-26 -See the [blog post](https://elastio.github.io/bon/blog/bon-builder-generator-v2-release) for details. +See the [blog post](https://bon-rs.com/blog/bon-builder-generator-v2-release) for details. ## [1.2.1](https://github.com/elastio/bon/compare/v1.2.0...v1.2.1) - 2024-08-12 @@ -216,4 +217,4 @@ See the [blog post](https://elastio.github.io/bon/blog/bon-builder-generator-v2- ### Added -- Initial release 🎉. See the [`bon` crate overview for details](https://elastio.github.io/bon/guide/overview). +- Initial release 🎉. See the [`bon` crate overview for details](https://bon-rs.com/guide/overview). From f8bd2c8dd9e5568ce868dcfdb9d3be67a468f4f3 Mon Sep 17 00:00:00 2001 From: Veetaha Date: Sun, 27 Oct 2024 14:27:22 +0000 Subject: [PATCH 6/6] Remove comments --- website/infra/bootstrap/user_data.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/website/infra/bootstrap/user_data.yml b/website/infra/bootstrap/user_data.yml index be2b254d..c961b8a2 100644 --- a/website/infra/bootstrap/user_data.yml +++ b/website/infra/bootstrap/user_data.yml @@ -25,14 +25,14 @@ users: groups: docker sudo: ALL=(ALL) NOPASSWD:ALL -# package_update: true -# package_upgrade: true -# package_reboot_if_required: true -# packages: -# - apt-transport-https -# - ca-certificates -# - curl -# - gnupg +package_update: true +package_upgrade: true +package_reboot_if_required: true +packages: + - apt-transport-https + - ca-certificates + - curl + - gnupg write_files: %{~ for path, file in files ~}