diff --git a/.gitignore b/.gitignore index 0e35890..23440b9 100644 --- a/.gitignore +++ b/.gitignore @@ -36,5 +36,5 @@ override.tf.json # Ignore CLI configuration files .terraformrc terraform.rc - +.env # End of https://www.toptal.com/developers/gitignore/api/terraform diff --git a/README.md b/README.md index 1f22212..d44823d 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,7 @@ opentofu.tofu init opentofu.tofu plan opentofu.tofu apply opentofu.tofu destroy -``` - -### SOPS for .env and secret management -```sh -sops -e modules/apps//.env > modules/apps//.env.enc +tofu apply -target module. ``` ### On WSL @@ -52,4 +48,191 @@ start and configure ssh agent ```sh eval $(ssh-agent) ssh-add ~/.ssh/id_ed25519 +``` + +## Add new service +### Create base module +```sh +mkdir modules/apps/ +``` +Example +```sh +modules/apps/bookshelf/ +├── cloud-init +│ └── service.yaml +├── lib +│ ├── scripts +│ │ ├── env.sh +│ └── services +├── main.tf +├── output.tf +├── variables.tf +├── .env.example +└── .env +``` + +#### main.tf + +```hcl +module "vm" { + source = "../../vm" + name = var.name + hostname = var.hostname + domain = var.domain + vm_id = var.vm_id + node_name = var.node_name + vm_ip_address = var.vm_ip_address + + template_id = var.template_id + + cores = var.cores + memory = var.memory + disk_size = var.disk_size + + ssh_public_key = var.ssh_public_key + proxmox_host_ip = var.proxmox_host_ip + cloudinit_config = templatefile( + "${path.module}/cloud-init/service.yaml", + { + hostname = var.hostname + domain = var.domain + ssh_key = var.ssh_public_key + proxmox_host_ip = var.proxmox_host_ip + environment-setup-script = indent(6, file("${path.module}/lib/scripts/env.sh")) + env-file-content = indent(6, file("${path.module}/.env")) + } + ) +} +``` + +Add inside templatefile object scripts content to upload with `cloud-init`. + +#### variables.tf + +```hcl +variable "name" { + type = string +} + +variable "vm_id" { + type = number +} + +variable "node_name" { + type = string + default = "mop" +} + +variable "cores" { + type = number + default = 2 +} + +variable "memory" { + type = number + default = 2048 +} + +variable "balloon" { + description = "Minimum vm memory, using ballooning devide to reach Proxmox node memory target." + type = number + default = 1024 +} + +variable "template_id" { + type = number +} + +variable "ssh_public_key" { + type = string + description = "Public SSH key for cloud-init user" +} + +variable "hostname" { + description = "VM hostname" + type = string + default = "test" +} + +variable "domain" { + description = "VM domain" + type = string + default = "" +} + +variable "disk_size" { + type = number + default = 10 +} + +variable "proxmox_host_ip" { + type = string +} + +variable "vm_ip_address" { + type = string +} +``` + +#### output.tf + +```hcl +output "traefik_service" { + value = [{ + domain = var.domain + name = var.name + host = "${var.hostname}" + ip = var.vm_ip_address + port = 80 + }] +} +``` + +This output supports multiple service for one vm. + +#### cloud-init/service.yaml + +```hcl +#cloud-config +hostname: ${hostname} +local-hostname: ${hostname} +fqdn: ${hostname}.${domain} +manage_etc_hosts: true + +users: + - default + - name: ${hostname} + groups: sudo + shell: /bin/bash + sudo: ALL=(ALL) NOPASSWD:ALL + ssh_authorized_keys: + - ${ssh_key} + +disable_root: true + +package_update: true +package_upgrade: false + +packages: + - git + - nfs-common + +mounts: + - [ "192.168.1.12:/main/backups", "/backups", "nfs", "defaults,_netdev,x-systemd.requires=network-online.target", "0", "0" ] + +write_files: + - path: /opt/bookshelf/env.sh + permissions: "0644" + content: | + ${environment-setup-script} + - path: /opt/bookshelf/bookshelf.env + permissions: "0644" + content: | + ${env-file-content} + +runcmd: + - ls / + +final_message: | + Base system ready for ${hostname} ``` \ No newline at end of file diff --git a/main.tf b/main.tf index d753def..553bb0c 100644 --- a/main.tf +++ b/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { proxmox = { source = "bpg/proxmox" - version = "0.42.0" + version = "0.93.0" } } } @@ -18,10 +18,69 @@ provider "proxmox" { } } +locals { + traefik_services = { + bookshelf = module.bookshelf.traefik_service + gitea = module.gitea.traefik_service + fefan = module.fefan.traefik_service + } +} + +resource "local_file" "traefik_config" { + filename = "${path.module}/${var.gateway_repository}/services.yml" + + content = templatefile("${path.module}/templates/traefik.services.tpl", { + services = local.traefik_services + }) +} + +resource "null_resource" "commit_traefik" { + depends_on = [local_file.traefik_config] + triggers = { + config_sha = sha256(local_file.traefik_config.content) + } + + provisioner "local-exec" { + working_dir = "${path.module}/${var.gateway_repository}" + command = "git add services.yml && git commit -m 'Update Traefik services' && git push" + } +} + +resource "null_resource" "notify_gateway" { + depends_on = [null_resource.commit_traefik] + triggers = { + config_sha = sha256(local_file.traefik_config.content) + } + provisioner "local-exec" { + command = "curl -X POST -H 'X-Webhook-Token: ${var.gateway_token}' http://192.168.1.89:5555/reload" + } +} + +module "gateway" { + source = "./modules/apps/gateway" + providers = {} + vm_ip_address = "192.168.1.89" + name = "gateway" + hostname = "gateway" + domain = "aldon.fr" + vm_id = 200 + node_name = "mop" + + template_id = 103 + + cores = 2 + memory = 2048 + balloon = 1024 + disk_size = 16 + + ssh_public_key = var.ssh_public_key + proxmox_host_ip = var.proxmox_host_ip +} + module "gitea" { source = "./modules/apps/gitea" providers = {} - + vm_ip_address = "192.168.1.90" name = "gitea" hostname = "gitea" domain = "aldon.fr" @@ -32,16 +91,17 @@ module "gitea" { cores = 2 memory = 2048 + balloon = 1024 disk_size = 16 ssh_public_key = var.ssh_public_key proxmox_host_ip = var.proxmox_host_ip } - module "bookshelf" { source = "./modules/apps/bookshelf" providers = {} + vm_ip_address = "192.168.1.91" name = "bookshelf" hostname = "bookshelf" @@ -53,6 +113,29 @@ module "bookshelf" { cores = 1 memory = 1024 + balloon = 1024 + disk_size = 16 + + ssh_public_key = var.ssh_public_key + proxmox_host_ip = var.proxmox_host_ip +} + +module "fefan" { + source = "./modules/apps/fefan" + providers = {} + vm_ip_address = "192.168.1.92" + + name = "fefan" + hostname = "fefan" + domain = "fefan.fr" + vm_id = 213 + node_name = "mop" + + template_id = 103 + + cores = 1 + memory = 1024 + balloon = 1024 disk_size = 16 ssh_public_key = var.ssh_public_key diff --git a/modules/apps/bookshelf/.env.example b/modules/apps/bookshelf/.env.example index 4aa9763..83b1c29 100644 --- a/modules/apps/bookshelf/.env.example +++ b/modules/apps/bookshelf/.env.example @@ -1,7 +1,7 @@ ACT_RUNNER_VERSION=0.2.13 ACT_RUNNER_LOCATION=/usr/local/bin ACT_RUNNER_USER=act_runner -ENV_FILE_LOCATION=/opt/bookshelf/secrets/bookshelf.env +ENV_FILE_LOCATION=/opt/bookshelf/bookshelf.env GITEA_INSTANCE_URL=https://gitea.aldon.fr GITEA_RUNNER_REGISTRATION_TOKEN= GITEA_BOOKSHELF_APPLICATION_TOKEN= diff --git a/modules/apps/bookshelf/cloud-init/service.yaml b/modules/apps/bookshelf/cloud-init/service.yaml index 811737b..2e9addb 100644 --- a/modules/apps/bookshelf/cloud-init/service.yaml +++ b/modules/apps/bookshelf/cloud-init/service.yaml @@ -26,16 +26,15 @@ packages: - curl - jq +mounts: + - [ "192.168.1.12:/main/backups", "/backups", "nfs", "defaults,_netdev,x-systemd.requires=network-online.target", "0", "0" ] + write_files: - - path: /etc/fstab - permissions: "0644" - content: | - ${proxmox_host_ip}:/main/backups /backups nfs defaults,_netdev,x-systemd.requires=network-online.target 0 0 - path: /opt/bookshelf/env.sh permissions: "0644" content: | ${environment-setup-script} - - path: /opt/bookshelf/secrets/bookshelf.env + - path: /opt/bookshelf/bookshelf.env permissions: "0644" content: | ${env-file-content} @@ -51,11 +50,11 @@ write_files: permissions: "0755" content: | ${create-backup-script} - - path: /etc/systemd/system/weekly-backup.timer + - path: /etc/systemd/system/create-backup.timer permissions: "0644" content: | ${create-backup-timer} - - path: /etc/systemd/system/weekly-backup.service + - path: /etc/systemd/system/create-backup.service permissions: "0644" content: | ${create-backup-service} @@ -76,7 +75,7 @@ runcmd: # Backup setup - mkdir -p /backups - mount -t nfs ${proxmox_host_ip}:/main/backups /backups - - systemctl enable --now weekly-backup.timer + - systemctl enable --now create-backup.timer # Docker setup - systemctl enable docker - systemctl start docker diff --git a/modules/apps/bookshelf/lib/scripts/create-backup.sh b/modules/apps/bookshelf/lib/scripts/create-backup.sh index e55d86b..994ce67 100644 --- a/modules/apps/bookshelf/lib/scripts/create-backup.sh +++ b/modules/apps/bookshelf/lib/scripts/create-backup.sh @@ -3,6 +3,6 @@ source /opt/bookshelf/env.sh TIMESTAMP=$(date +'%Y-%m-%d_%H%M%S') -docker exec bookshelf-database-1 mariadb-dump --all-database -u root -p"$MARIADB_ROOT_PASSWORD" > $BOOKSHELF_BACKUPS_DIR/bookshelf-dump-$TIMESTAMP.sql +docker exec bookshelf-database-1 mariadb-dump --all-databases -u root -p"$MARIADB_ROOT_PASSWORD" > $BOOKSHELF_BACKUPS_DIR/bookshelf-dump-$TIMESTAMP.sql -ls -1dt $BOOKSHELF_BACKUPS_DIR/$BOOKSHELF_BACKUP_PREFIX-*.zip | tail -n +5 | xargs -r rm -f +ls -1dt $BOOKSHELF_BACKUPS_DIR/$BOOKSHELF_BACKUP_PREFIX-*.sql | tail -n +5 | xargs -r rm -f diff --git a/modules/apps/bookshelf/lib/scripts/env.sh b/modules/apps/bookshelf/lib/scripts/env.sh index 3fa8dbb..1bd9f0e 100644 --- a/modules/apps/bookshelf/lib/scripts/env.sh +++ b/modules/apps/bookshelf/lib/scripts/env.sh @@ -1,4 +1,4 @@ #!/bin/bash set -a -[ -f /opt/bookshelf/secrets/bookshelf.env ] && source /opt/bookshelf/secrets/bookshelf.env +[ -f /opt/bookshelf/bookshelf.env ] && source /opt/bookshelf/bookshelf.env set +a \ No newline at end of file diff --git a/modules/apps/bookshelf/lib/scripts/install-bookshelf.sh b/modules/apps/bookshelf/lib/scripts/install-bookshelf.sh index 1f9ef39..307d48b 100644 --- a/modules/apps/bookshelf/lib/scripts/install-bookshelf.sh +++ b/modules/apps/bookshelf/lib/scripts/install-bookshelf.sh @@ -31,6 +31,17 @@ done if [ "$CONCLUSION" = "success" ]; then echo "Launching command..." + + while [ "$(docker inspect -f '{{.State.Running}}' bookshelf-database-1 2>/dev/null)" != "true" ]; do + echo "Waiting database container status" + sleep 5 + done + + until docker exec bookshelf-database-1 sh -c "mariadb -u root -p$MARIADB_ROOT_PASSWORD -e 'SELECT 1;' >/dev/null 2>&1"; do + echo "Waitin mariadb to accept connections" + sleep 5 + done + echo "Restoring backup" systemctl start restore-backup.service else echo "Workflow failed or was cancelled, aborting." diff --git a/modules/apps/bookshelf/lib/scripts/install-runner.sh b/modules/apps/bookshelf/lib/scripts/install-runner.sh index a70cc15..265a45d 100644 --- a/modules/apps/bookshelf/lib/scripts/install-runner.sh +++ b/modules/apps/bookshelf/lib/scripts/install-runner.sh @@ -2,6 +2,16 @@ source /opt/bookshelf/env.sh +## .env should define +# ACT_RUNNER_USER: act_runner username (act_runner) +# ACT_RUNNER_LOCATION: act_runner binary location (/usr/local/bin) +# ACT_RUNNER_VERSION: act_runner version (0.2.13) +# ENV_FILE_LOCATION: .env file location on vm (/opt/bookshelf/bookshelf.env) +# GITEA_INSTANCE_URL: url of the gitea instance (https://gitea.aldon.fr) +# GITEA_RUNNER_REGISTRATION_TOKEN: registration token for gitea runner (repository scope) +# USERNAME: username of the vm (bookshelf) +# REPOSITORY: repository on which service code is hosted (mop/bookshelf) + if ! id -u $ACT_RUNNER_USER >/dev/null 2>&1; then adduser \ --system \ @@ -30,9 +40,9 @@ runner: fetch_interval: 2s github_mirror: '' labels: - - "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest" - - "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04" - - "ubuntu-20.04:docker://docker.gitea.com/runner-images:ubuntu-20.04" + - 'ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest' + - 'ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04' + - 'ubuntu-20.04:docker://docker.gitea.com/runner-images:ubuntu-20.04' cache: enabled: true dir: "" @@ -55,5 +65,5 @@ host: EOF cd /home/act_runner -sudo -u $ACT_RUNNER_USER act_runner register --no-interactive --instance $GITEA_INSTANCE_URL --token $GITEA_RUNNER_REGISTRATION_TOKEN --name $USERNAME --labels $USERNAME $REPOSITORY +sudo -u $ACT_RUNNER_USER act_runner register --no-interactive --instance $GITEA_INSTANCE_URL --token $GITEA_RUNNER_REGISTRATION_TOKEN --name $USERNAME --labels $USERNAME chown -R $ACT_RUNNER_USER:docker /home/$ACT_RUNNER_USER \ No newline at end of file diff --git a/modules/apps/bookshelf/lib/services/create-backup.service b/modules/apps/bookshelf/lib/services/create-backup.service new file mode 100644 index 0000000..c0ace1f --- /dev/null +++ b/modules/apps/bookshelf/lib/services/create-backup.service @@ -0,0 +1,9 @@ +[Unit] +Description=Backup Service +Wants=network.target +After=network.target docker.service + +[Service] +Type=oneshot +User=root +ExecStart=/usr/local/bin/backup.sh \ No newline at end of file diff --git a/modules/apps/gitea/lib/services/weekly-backup.timer b/modules/apps/bookshelf/lib/services/create-backup.timer similarity index 52% rename from modules/apps/gitea/lib/services/weekly-backup.timer rename to modules/apps/bookshelf/lib/services/create-backup.timer index abf3e34..0e9d735 100644 --- a/modules/apps/gitea/lib/services/weekly-backup.timer +++ b/modules/apps/bookshelf/lib/services/create-backup.timer @@ -1,5 +1,5 @@ [Unit] -Description=Run Gitea backup weekly +Description=Run backup weekly [Timer] OnCalendar=Sun *-*-* 01:00:00 diff --git a/modules/apps/bookshelf/lib/services/restore-backup.service b/modules/apps/bookshelf/lib/services/restore-backup.service index 6d4f6b5..9bfcd33 100644 --- a/modules/apps/bookshelf/lib/services/restore-backup.service +++ b/modules/apps/bookshelf/lib/services/restore-backup.service @@ -1,11 +1,9 @@ [Unit] -Description=Restore latest Bookshelf backup +Description=Restore latest backup After=network.target Requires=docker.service [Service] Type=oneshot User=root -ExecStart=/usr/local/bin/restore-backup.sh -WorkingDirectory=/home/bookshelf -TimeoutStartSec=600 \ No newline at end of file +ExecStart=/usr/local/bin/restore-backup.sh \ No newline at end of file diff --git a/modules/apps/bookshelf/lib/services/weekly-backup.service b/modules/apps/bookshelf/lib/services/weekly-backup.service deleted file mode 100644 index 03c3989..0000000 --- a/modules/apps/bookshelf/lib/services/weekly-backup.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Weekly Bookshelf Backup -Wants=network.target -After=network.target docker.service -Before=shutdown.target reboot.target halt.target - -[Service] -Type=oneshot -User=root -ExecStart=/usr/local/bin/backup.sh \ No newline at end of file diff --git a/modules/apps/bookshelf/main.tf b/modules/apps/bookshelf/main.tf index 383067d..0c66569 100644 --- a/modules/apps/bookshelf/main.tf +++ b/modules/apps/bookshelf/main.tf @@ -5,7 +5,7 @@ module "vm" { domain = var.domain vm_id = var.vm_id node_name = var.node_name - vm_ip_address = "192.168.1.91" + vm_ip_address = var.vm_ip_address template_id = var.template_id @@ -26,8 +26,8 @@ module "vm" { restore-backup-script = indent(6, file("${path.module}/lib/scripts/restore-backup.sh")) restore-backup-service = indent(6, file("${path.module}/lib/services/restore-backup.service")) create-backup-script = indent(6, file("${path.module}/lib/scripts/create-backup.sh")) - create-backup-service = indent(6, file("${path.module}/lib/services/weekly-backup.service")) - create-backup-timer = indent(6, file("${path.module}/lib/services/weekly-backup.timer")) + create-backup-service = indent(6, file("${path.module}/lib/services/create-backup.service")) + create-backup-timer = indent(6, file("${path.module}/lib/services/create-backup.timer")) act_runner-service = indent(6, file("${path.module}/lib/services/act_runner.service")) act_runner-install-script = indent(6, file("${path.module}/lib/scripts/install-runner.sh")) bookshelf-install-script = indent(6, file("${path.module}/lib/scripts/install-bookshelf.sh")) diff --git a/modules/apps/bookshelf/output.tf b/modules/apps/bookshelf/output.tf new file mode 100644 index 0000000..f7ad914 --- /dev/null +++ b/modules/apps/bookshelf/output.tf @@ -0,0 +1,9 @@ +output "traefik_service" { + value = [{ + domain = var.domain + name = var.name + host = "${var.hostname}" + ip = var.vm_ip_address + port = 80 + }] +} \ No newline at end of file diff --git a/modules/apps/bookshelf/variables.tf b/modules/apps/bookshelf/variables.tf index c837103..cfc12a3 100644 --- a/modules/apps/bookshelf/variables.tf +++ b/modules/apps/bookshelf/variables.tf @@ -21,6 +21,12 @@ variable "memory" { default = 2048 } +variable "balloon" { + description = "Minimum vm memory, using ballooning devide to reach Proxmox node memory target." + type = number + default = 1024 +} + variable "template_id" { type = number } @@ -49,4 +55,8 @@ variable "disk_size" { variable "proxmox_host_ip" { type = string +} + +variable "vm_ip_address" { + type = string } \ No newline at end of file diff --git a/modules/apps/fefan/.env.example b/modules/apps/fefan/.env.example new file mode 100644 index 0000000..4adca9a --- /dev/null +++ b/modules/apps/fefan/.env.example @@ -0,0 +1,24 @@ +ACT_RUNNER_VERSION=0.2.13 +ACT_RUNNER_LOCATION=/usr/local/bin +ACT_RUNNER_USER=act_runner +ENV_FILE_LOCATION=/opt/fefan/fefan.env +GITEA_INSTANCE_URL=https://gitea.aldon.fr +GITEA_RUNNER_REGISTRATION_TOKEN= +GITEA_FEFAN_APPLICATION_TOKEN= +GITEA_FEFAN_REPOSITORY=Mop/fefan + +NEXT_PUBLIC_CONTENT_URI=https://content.fefan.fr/api +NEXT_PUBLIC_IMG_URI=https://content.fefan.fr +NEXT_PUBLIC_ORIGIN=https://fefan.fr +POSTGRES_USER=strapi +POSTGRES_PASSWORD=password +STRAPI_APP_KEYS= +STRAPI_TOKEN_SALT= +STRAPI_ADMIN_JWT_SECRET= +STRAPI_TRANSFER_TOKEN_SALT= +STRAPI_JWT_SECRET= + + +USERNAME=fefan +FEFAN_BACKUPS_DIR=/backups/fefan +FEFAN_BACKUP_PREFIX=fefan-dump \ No newline at end of file diff --git a/modules/apps/fefan/cloud-init/service.yaml b/modules/apps/fefan/cloud-init/service.yaml new file mode 100644 index 0000000..42130ea --- /dev/null +++ b/modules/apps/fefan/cloud-init/service.yaml @@ -0,0 +1,92 @@ +#cloud-config +hostname: ${hostname} +local-hostname: ${hostname} +fqdn: ${hostname}.${domain} +manage_etc_hosts: true + +users: + - default + - name: ${hostname} + groups: sudo + shell: /bin/bash + sudo: ALL=(ALL) NOPASSWD:ALL + ssh_authorized_keys: + - ${ssh_key} + +disable_root: true + +package_update: true +package_upgrade: false + +packages: + - git + - nfs-common + - docker.io + - docker-compose + - curl + - jq + +mounts: + - [ "192.168.1.12:/main/backups", "/backups", "nfs", "defaults,_netdev,x-systemd.requires=network-online.target", "0", "0" ] + +write_files: + - path: /opt/fefan/env.sh + permissions: "0644" + content: | + ${environment-setup-script} + - path: /opt/fefan/fefan.env + permissions: "0644" + content: | + ${env-file-content} + - path: /usr/local/bin/restore-backup.sh + permissions: "0755" + content: | + ${restore-backup-script} + - path: /etc/systemd/system/restore-backup.service + permissions: "0644" + content: | + ${restore-backup-service} + - path: /usr/local/bin/backup.sh + permissions: "0755" + content: | + ${create-backup-script} + - path: /etc/systemd/system/create-backup.timer + permissions: "0644" + content: | + ${create-backup-timer} + - path: /etc/systemd/system/create-backup.service + permissions: "0644" + content: | + ${create-backup-service} + - path: /etc/systemd/system/act_runner.service + permissions: "0644" + content: | + ${act_runner-service} + - path: /opt/fefan/install-runner.sh + permissions: "0755" + content: | + ${act_runner-install-script} + - path: /opt/fefan/install-fefan.sh + permissions: "0755" + content: | + ${fefan-install-script} + +runcmd: + # Backup setup + - mkdir -p /backups + - mount -t nfs ${proxmox_host_ip}:/main/backups /backups + - systemctl enable --now create-backup.timer + # Docker setup + - systemctl enable docker + - systemctl start docker + - usermod -aG docker ${hostname} + # Act_runner install + - /opt/fefan/install-runner.sh + - systemctl daemon-reload + - systemctl enable act_runner.service + - systemctl start act_runner.service + # Install fefan + - /opt/fefan/install-fefan.sh + +final_message: | + Base system ready for ${hostname} \ No newline at end of file diff --git a/modules/apps/fefan/lib/scripts/create-backup.sh b/modules/apps/fefan/lib/scripts/create-backup.sh new file mode 100644 index 0000000..3a8a19c --- /dev/null +++ b/modules/apps/fefan/lib/scripts/create-backup.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +source /opt/fefan/env.sh + +# FEFAN_BACKUPS_DIR=/backups/fefan +# FEFAN_BACKUP_PREFIX=fefan-dump + +TIMESTAMP=$(date +'%Y-%m-%d_%H%M%S') + +docker exec fefan-strapi-1 yarn strapi export -f export --no-encrypt +docker cp fefan-strapi-1:/app/export.tar.gz $FEFAN_BACKUPS_DIR/$FEFAN_BACKUP_PREFIX-$TIMESTAMP.tar.gz +docker exec fefan-strapi-1 rm /app/export.tar.gz + +ls -1dt $FEFAN_BACKUPS_DIR/$FEFAN_BACKUP_PREFIX-*.tar.gz | tail -n +5 | xargs -r rm -f \ No newline at end of file diff --git a/modules/apps/fefan/lib/scripts/env.sh b/modules/apps/fefan/lib/scripts/env.sh new file mode 100644 index 0000000..dbd8273 --- /dev/null +++ b/modules/apps/fefan/lib/scripts/env.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -a +[ -f /opt/fefan/fefan.env ] && source /opt/fefan/fefan.env +set +a \ No newline at end of file diff --git a/modules/apps/fefan/lib/scripts/install-fefan.sh b/modules/apps/fefan/lib/scripts/install-fefan.sh new file mode 100644 index 0000000..c789664 --- /dev/null +++ b/modules/apps/fefan/lib/scripts/install-fefan.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +source /opt/fefan/env.sh + +# trigger manually a CI/CD pipeline +curl -X POST -H "Authorization: token $GITEA_FEFAN_APPLICATION_TOKEN" \ + -H "Content-Type: application/json" \ + $GITEA_INSTANCE_URL/api/v1/repos/$GITEA_FEFAN_REPOSITORY/actions/workflows/deploy.yaml/dispatches \ + -d '{"ref": "main", "inputs": {"ref": "main"}}' + +RUN_ID=$(curl -s -H "Authorization: token $GITEA_FEFAN_APPLICATION_TOKEN" \ + $GITEA_INSTANCE_URL/api/v1/repos/$GITEA_FEFAN_REPOSITORY/actions/runs \ + | jq -r '.workflow_runs | sort_by(.created_at) | .[0].id') + +while true; do + STATUS=$(curl -s -H "Authorization: token $GITEA_FEFAN_APPLICATION_TOKEN" \ + $GITEA_INSTANCE_URL/api/v1/repos/$GITEA_FEFAN_REPOSITORY/actions/runs/$RUN_ID \ + | jq -r '.status') + + if [ "$STATUS" = "completed" ]; then + CONCLUSION=$(curl -s -H "Authorization: token $GITEA_FEFAN_APPLICATION_TOKEN" \ + $GITEA_INSTANCE_URL/api/v1/repos/$GITEA_FEFAN_REPOSITORY/actions/runs/$RUN_ID \ + | jq -r '.conclusion') + echo "Workflow finished with status: $CONCLUSION" + break + fi + + echo "Waiting 10 seconds..." + sleep 10 +done + +if [ "$CONCLUSION" = "success" ]; then + echo "Launching command..." + + while [ "$(docker inspect -f '{{.State.Running}}' fefan-db-1 2>/dev/null)" != "true" ]; do + echo "Waiting database container status" + sleep 5 + done + + systemctl start restore-backup.service +else + echo "Workflow failed or was cancelled, aborting." + exit 1 +fi \ No newline at end of file diff --git a/modules/apps/fefan/lib/scripts/install-runner.sh b/modules/apps/fefan/lib/scripts/install-runner.sh new file mode 100644 index 0000000..f55641f --- /dev/null +++ b/modules/apps/fefan/lib/scripts/install-runner.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +source /opt/fefan/env.sh + +## .env should define +# ACT_RUNNER_USER: act_runner username (act_runner) +# ACT_RUNNER_LOCATION: act_runner binary location (/usr/local/bin) +# ACT_RUNNER_VERSION: act_runner version (0.2.13) +# ENV_FILE_LOCATION: .env file location on vm (/opt/fefan/fefan.env) +# GITEA_INSTANCE_URL: url of the gitea instance (https://gitea.aldon.fr) +# GITEA_RUNNER_REGISTRATION_TOKEN: registration token for gitea runner (repository scope) +# USERNAME: username of the vm (fefan) +# REPOSITORY: repository on which service code is hosted (mop/fefan) + +if ! id -u $ACT_RUNNER_USER >/dev/null 2>&1; then + adduser \ + --system \ + --shell /bin/bash \ + --gecos 'Action runner user' \ + --ingroup docker\ + --disabled-password \ + --home /home/$ACT_RUNNER_USER \ + $ACT_RUNNER_USER +fi + +wget -O $ACT_RUNNER_LOCATION/act_runner https://dl.gitea.com/act_runner/$ACT_RUNNER_VERSION/act_runner-$ACT_RUNNER_VERSION-linux-amd64 +chmod +x $ACT_RUNNER_LOCATION/act_runner + +cat < /home/$ACT_RUNNER_USER/config.yaml +log: + level: info +runner: + file: .runner + capacity: 1 + timeout: 3h + shutdown_timeout: 0s + insecure: false + fetch_timeout: 5s + env_file: $ENV_FILE_LOCATION + fetch_interval: 2s + github_mirror: '' + labels: + - 'ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest' + - 'ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04' + - 'ubuntu-20.04:docker://docker.gitea.com/runner-images:ubuntu-20.04' +cache: + enabled: true + dir: "" + host: "" + port: 0 + external_server: "" +container: + network: "" + privileged: false + options: + workdir_parent: + valid_volumes: [] + docker_host: "" + force_pull: true + force_rebuild: false + require_docker: false + docker_timeout: 0s +host: + workdir_parent: +EOF + +cd /home/act_runner +sudo -u $ACT_RUNNER_USER act_runner register --no-interactive --instance $GITEA_INSTANCE_URL --token $GITEA_RUNNER_REGISTRATION_TOKEN --name $USERNAME --labels $USERNAME +chown -R $ACT_RUNNER_USER:docker /home/$ACT_RUNNER_USER \ No newline at end of file diff --git a/modules/apps/fefan/lib/scripts/restore-backup.sh b/modules/apps/fefan/lib/scripts/restore-backup.sh new file mode 100644 index 0000000..0cb9b02 --- /dev/null +++ b/modules/apps/fefan/lib/scripts/restore-backup.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +source /opt/fefan/env.sh + +# FEFAN_BACKUPS_DIR=/backups/fefan +# FEFAN_BACKUP_PREFIX=fefan-dump + +LATEST_BACKUP=$(ls -1 $FEFAN_BACKUPS_DIR/$FEFAN_BACKUP_PREFIX-*.tar.gz 2>/dev/null | sort | tail -n1) + +if [ -n "$LATEST_BACKUP" ] && [ -f "$LATEST_BACKUP" ]; then + docker cp $LATEST_BACKUP fefan-strapi-1:/app/${LATEST_BACKUP#"$FEFAN_BACKUPS_DIR"/} + docker exec fefan-strapi-1 yarn strapi import -f /app/${LATEST_BACKUP#"$FEFAN_BACKUPS_DIR"/} --force + docker exec fefan-strapi-1 rm /app/${LATEST_BACKUP#"$FEFAN_BACKUPS_DIR"/} +fi \ No newline at end of file diff --git a/modules/apps/fefan/lib/services/act_runner.service b/modules/apps/fefan/lib/services/act_runner.service new file mode 100644 index 0000000..72043da --- /dev/null +++ b/modules/apps/fefan/lib/services/act_runner.service @@ -0,0 +1,16 @@ +[Unit] +Description=Gitea Actions runner +Documentation=https://gitea.com/gitea/act_runner +After=docker.service + +[Service] +ExecStart=/usr/local/bin/act_runner daemon --config /home/act_runner/config.yaml +ExecReload=/bin/kill -s HUP $MAINPID +WorkingDirectory=/home/act_runner +TimeoutSec=0 +RestartSec=10 +Restart=always +User=act_runner + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/modules/apps/fefan/lib/services/create-backup.service b/modules/apps/fefan/lib/services/create-backup.service new file mode 100644 index 0000000..c0ace1f --- /dev/null +++ b/modules/apps/fefan/lib/services/create-backup.service @@ -0,0 +1,9 @@ +[Unit] +Description=Backup Service +Wants=network.target +After=network.target docker.service + +[Service] +Type=oneshot +User=root +ExecStart=/usr/local/bin/backup.sh \ No newline at end of file diff --git a/modules/apps/bookshelf/lib/services/weekly-backup.timer b/modules/apps/fefan/lib/services/create-backup.timer similarity index 51% rename from modules/apps/bookshelf/lib/services/weekly-backup.timer rename to modules/apps/fefan/lib/services/create-backup.timer index bd996f4..0e9d735 100644 --- a/modules/apps/bookshelf/lib/services/weekly-backup.timer +++ b/modules/apps/fefan/lib/services/create-backup.timer @@ -1,5 +1,5 @@ [Unit] -Description=Run Bookshelf backup weekly +Description=Run backup weekly [Timer] OnCalendar=Sun *-*-* 01:00:00 diff --git a/modules/apps/fefan/lib/services/restore-backup.service b/modules/apps/fefan/lib/services/restore-backup.service new file mode 100644 index 0000000..9bfcd33 --- /dev/null +++ b/modules/apps/fefan/lib/services/restore-backup.service @@ -0,0 +1,9 @@ +[Unit] +Description=Restore latest backup +After=network.target +Requires=docker.service + +[Service] +Type=oneshot +User=root +ExecStart=/usr/local/bin/restore-backup.sh \ No newline at end of file diff --git a/modules/apps/fefan/main.tf b/modules/apps/fefan/main.tf new file mode 100644 index 0000000..268ca3a --- /dev/null +++ b/modules/apps/fefan/main.tf @@ -0,0 +1,37 @@ +module "vm" { + source = "../../vm" + name = var.name + hostname = var.hostname + domain = var.domain + vm_id = var.vm_id + node_name = var.node_name + vm_ip_address = var.vm_ip_address + + template_id = var.template_id + + cores = var.cores + memory = var.memory + disk_size = var.disk_size + + ssh_public_key = var.ssh_public_key + proxmox_host_ip = var.proxmox_host_ip + cloudinit_config = templatefile( + "${path.module}/cloud-init/service.yaml", + { + hostname = var.hostname + domain = var.domain + ssh_key = var.ssh_public_key + proxmox_host_ip = var.proxmox_host_ip + environment-setup-script = indent(6, file("${path.module}/lib/scripts/env.sh")) + restore-backup-script = indent(6, file("${path.module}/lib/scripts/restore-backup.sh")) + restore-backup-service = indent(6, file("${path.module}/lib/services/restore-backup.service")) + create-backup-script = indent(6, file("${path.module}/lib/scripts/create-backup.sh")) + create-backup-service = indent(6, file("${path.module}/lib/services/create-backup.service")) + create-backup-timer = indent(6, file("${path.module}/lib/services/create-backup.timer")) + act_runner-service = indent(6, file("${path.module}/lib/services/act_runner.service")) + act_runner-install-script = indent(6, file("${path.module}/lib/scripts/install-runner.sh")) + fefan-install-script = indent(6, file("${path.module}/lib/scripts/install-fefan.sh")) + env-file-content = indent(6, file("${path.module}/.env")) + } + ) +} \ No newline at end of file diff --git a/modules/apps/fefan/output.tf b/modules/apps/fefan/output.tf new file mode 100644 index 0000000..d931994 --- /dev/null +++ b/modules/apps/fefan/output.tf @@ -0,0 +1,18 @@ +output "traefik_service" { + value = [ + { + domain = "fefan.fr" + name = "fefan" + host = "" + ip = var.vm_ip_address + port = 3000 + }, + { + domain = "fefan.fr" + name = "content-fefan" + host = "content" + ip = var.vm_ip_address + port = 1337 + } + ] +} \ No newline at end of file diff --git a/modules/apps/fefan/variables.tf b/modules/apps/fefan/variables.tf new file mode 100644 index 0000000..cfc12a3 --- /dev/null +++ b/modules/apps/fefan/variables.tf @@ -0,0 +1,62 @@ +variable "name" { + type = string +} + +variable "vm_id" { + type = number +} + +variable "node_name" { + type = string + default = "mop" +} + +variable "cores" { + type = number + default = 2 +} + +variable "memory" { + type = number + default = 2048 +} + +variable "balloon" { + description = "Minimum vm memory, using ballooning devide to reach Proxmox node memory target." + type = number + default = 1024 +} + +variable "template_id" { + type = number +} + +variable "ssh_public_key" { + type = string + description = "Public SSH key for cloud-init user" +} + +variable "hostname" { + description = "VM hostname" + type = string + default = "test" +} + +variable "domain" { + description = "VM domain" + type = string + default = "" +} + +variable "disk_size" { + type = number + default = 10 +} + +variable "proxmox_host_ip" { + type = string +} + +variable "vm_ip_address" { + type = string +} \ No newline at end of file diff --git a/modules/apps/gateway/.env.example b/modules/apps/gateway/.env.example new file mode 100644 index 0000000..8eb1ad1 --- /dev/null +++ b/modules/apps/gateway/.env.example @@ -0,0 +1,8 @@ +#openssl rand -hex 20 +WEBHOOK_SECRET=xxx +TRAEFIK_VERSION=v3.6.7 +TRAEFIK_BINARY=/usr/local/bin/traefik +TRAEFIK_USER=traefik +TRAEFIK_CONF=/home/traefik/traefik.yml +GATEWAY_REPOSITORY=/Mop/gateway +GATEWAY_REPOSITORY_LOCATION=/home/traefik/gateway \ No newline at end of file diff --git a/modules/apps/gateway/cloud-init/service.yaml b/modules/apps/gateway/cloud-init/service.yaml new file mode 100644 index 0000000..a976e3e --- /dev/null +++ b/modules/apps/gateway/cloud-init/service.yaml @@ -0,0 +1,60 @@ +#cloud-config +hostname: ${hostname} +local-hostname: ${hostname} +fqdn: ${hostname}.${domain} +manage_etc_hosts: true + +users: + - default + - name: ${hostname} + groups: sudo + shell: /bin/bash + sudo: ALL=(ALL) NOPASSWD:ALL + ssh_authorized_keys: + - ${ssh_key} + +disable_root: true + +package_update: true +package_upgrade: false + +packages: + - git + - nfs-common + - curl + - python3-flask + - gunicorn + +write_files: + - path: /opt/gateway/env.sh + permissions: "0644" + content: | + ${environment-setup-script} + - path: /opt/gateway/gateway.env + permissions: "0644" + content: | + ${env-file-content} + - path: /opt/gateway/install-traefik.sh + permissions: "0755" + content: | + ${install-traefik-script} + - path: /usr/local/bin/pull-webhook.py + permissions: "0755" + content: | + ${pull-webhook-script} + - path: /etc/systemd/system/pull-webhook.service + permissions: "0755" + content: | + ${pull-webhook-service} + - path: /etc/systemd/system/traefik.service + permissions: "0755" + content: | + ${traefik-service} + +runcmd: + - /opt/gateway/install-traefik.sh + - systemctl enable pull-webhook.service + - systemctl start pull-webhook.service + +final_message: | + Base system ready for ${hostname} \ No newline at end of file diff --git a/modules/apps/gateway/lib/scripts/env.sh b/modules/apps/gateway/lib/scripts/env.sh new file mode 100644 index 0000000..36d57eb --- /dev/null +++ b/modules/apps/gateway/lib/scripts/env.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -a +[ -f /opt/gateway/gateway.env ] && source /opt/gateway/gateway.env +set +a \ No newline at end of file diff --git a/modules/apps/gateway/lib/scripts/install-traefik.sh b/modules/apps/gateway/lib/scripts/install-traefik.sh new file mode 100644 index 0000000..a1f43ac --- /dev/null +++ b/modules/apps/gateway/lib/scripts/install-traefik.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# GATEWAY_REPOSITORY_LOCATION (path on vm) +# GATEWAY_REPOSITORY (path on gitea) +# TRAEFIK_USER +# TRAEFIK_BINARY +# TRAEFIK_VERSION +# TRAEFIK_CONF + +source /opt/gateway/env.sh + +if ! id -u $TRAEFIK_USER >/dev/null 2>&1; then + adduser \ + --system \ + --shell /bin/bash \ + --gecos 'Traefik reverse proxy user' \ + --group \ + --disabled-password \ + --home /home/$TRAEFIK_USER \ + $TRAEFIK_USER +fi + +if [ ! -f $TRAEFIK_BINARY ]; then + wget -O /tmp/traefik.tar.gz "https://github.com/traefik/traefik/releases/download/$TRAEFIK_VERSION/traefik_${TRAEFIK_VERSION}_linux_amd64.tar.gz" + tar -zxvf /tmp/traefik.tar.gz -C /usr/local/bin traefik + chmod +x $TRAEFIK_BINARY +fi + +mkdir -p /etc/traefik/certs +touch /etc/traefik/acme.json +chown $TRAEFIK_USER:$TRAEFIK_USER /etc/traefik/acme.json +chmod 600 /etc/traefik/acme.json +setcap 'cap_net_bind_service=+ep' /usr/local/bin/traefik + +git clone https://gitea.aldon.fr/$GATEWAY_REPOSITORY.git $GATEWAY_REPOSITORY_LOCATION + +cat > "$TRAEFIK_CONF" <