diff --git a/.gitea/actions/setup-ansible/action.yaml b/.gitea/actions/setup-ansible/action.yaml new file mode 100644 index 0000000..aa1747f --- /dev/null +++ b/.gitea/actions/setup-ansible/action.yaml @@ -0,0 +1,19 @@ +name: "Set up Ansible" +description: "Downloads and installs Ansible" +inputs: + version: + description: "Ansible version to install" + required: false + default: "10.2.0" +runs: + using: "composite" + steps: + - name: "Install Ansible" + shell: "bash" + env: + PIP_INDEX_URL: "https://nexus.bthlabs.pl/repository/pypi/simple/" + run: | + set -x + python3 -m venv /opt/ansible + + /opt/ansible/bin/pip install ansible==${{ inputs.version }} diff --git a/.gitea/workflows/build-deployment-images.yaml b/.gitea/workflows/build-deployment-images.yaml new file mode 100644 index 0000000..746f228 --- /dev/null +++ b/.gitea/workflows/build-deployment-images.yaml @@ -0,0 +1,81 @@ +name: "Build deployment images" + +on: + workflow_call: + inputs: + target: + required: true + type: "string" + registry: + required: false + type: "string" + default: "docker-hosted.nexus.bthlabs.pl" + platform: + required: false + type: "string" + default: "linux/amd64,linux/arm64" + secrets: + VAULT_ROLE_ID: + required: true + VAULT_SECRET_ID: + required: true + +jobs: + build-deployment-images: + name: "Build deployment images" + runs-on: "ubuntu-latest" + steps: + - name: "Checkout the code" + uses: "actions/checkout@v2" + - name: "Get build options" + id: "get-build-options" + uses: "./.gitea/actions/get-build-options" + - name: "Get `backend` version" + id: "get-backend-version" + uses: "./.gitea/actions/get-service-version" + with: + service: "backend" + - name: "Import Secrets" + id: "import-secrets" + uses: "hashicorp/vault-action@v2" + with: + url: "https://vault.bthlabs.pl/" + method: "approle" + roleId: "${{ secrets.VAULT_ROLE_ID }}" + secretId: "${{ secrets.VAULT_SECRET_ID }}" + secrets: | + gitea/data/${{ inputs.registry }} username | DOCKER_USERNAME ; + gitea/data/${{ inputs.registry }} password | DOCKER_PASSWORD + - name: "Set up Docker Buildx" + id: "setup-docker-buildx" + uses: "docker/setup-buildx-action@v3" + with: + driver: "remote" + endpoint: "tcp://builder-01.bthlab:2375" + platforms: "linux/amd64" + append: | + - endpoint: "tcp://builder-mac-01.bthlab:2375" + platforms: "linux/arm64" + - name: "Login to Docker Registry" + uses: "docker/login-action@v3" + with: + registry: "${{ inputs.registry }}" + username: "${{ steps.import-secrets.outputs.DOCKER_USERNAME }}" + password: "${{ steps.import-secrets.outputs.DOCKER_PASSWORD }}" + - name: "Build `backend-aio` image" + env: + SHORT_SHA: "${{ steps.get-build-options.outputs.short-sha }}" + VERSION: "${{ steps.get-backend-version.outputs.version }}" + BUILD: "${{ steps.get-backend-version.outputs.build-number }}" + run: | + set -x + docker buildx build \ + --cache-from "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket" \ + --cache-to "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,target=max" \ + --push \ + --platform "${{ inputs.platform }}" \ + --build-arg IMAGE_ID="${{ inputs.target }}.${SHORT_SHA}" \ + -f services/backend/Dockerfile \ + --target "${{ inputs.target }}" \ + -t "${{ inputs.registry }}/hotpocket/backend:${{ inputs.target }}-${VERSION}-${BUILD}" \ + services/ diff --git a/.gitea/workflows/development.yaml b/.gitea/workflows/development.yaml index a9f8a12..2f9a221 100644 --- a/.gitea/workflows/development.yaml +++ b/.gitea/workflows/development.yaml @@ -1,4 +1,4 @@ -name: "Deploy to development" +name: "Development deployment" on: push: @@ -6,81 +6,28 @@ on: - "development" jobs: - build-deployment-images: - name: "Build deployment images" - runs-on: "ubuntu-latest" - steps: - - name: "Checkout the code" - uses: "actions/checkout@v2" - - name: "Get build options" - id: "get-build-options" - uses: "./.gitea/actions/get-build-options" - - name: "Get `backend` version" - id: "get-backend-version" - uses: "./.gitea/actions/get-service-version" - with: - service: "backend" - - name: "Import Secrets" - id: "import-secrets" - uses: "hashicorp/vault-action@v2" - with: - url: "https://vault.bthlabs.pl/" - method: "approle" - roleId: "${{ secrets.VAULT_ROLE_ID }}" - secretId: "${{ secrets.VAULT_SECRET_ID }}" - secrets: | - gitea/data/docker-hosted.nexus.bthlab.bthlabs.net username | DOCKER_USERNAME ; - gitea/data/docker-hosted.nexus.bthlab.bthlabs.net password | DOCKER_PASSWORD - - name: "Set up Docker Buildx" - id: "setup-docker-buildx" - uses: "docker/setup-buildx-action@v3" - with: - driver: "remote" - endpoint: "tcp://builder-01.bthlab:2375" - platforms: "linux/amd64" - append: | - - endpoint: "tcp://builder-mac-01.bthlab:2375" - platforms: "linux/arm64" - - name: "Login to Docket Registry" - uses: "docker/login-action@v3" - with: - registry: "nexus.bthlab.bthlabs.net:8002" - username: "${{ steps.import-secrets.outputs.DOCKER_USERNAME }}" - password: "${{ steps.import-secrets.outputs.DOCKER_PASSWORD }}" - - name: "Build `backend-deployment` image" - env: - SHORT_SHA: "${{ steps.get-build-options.outputs.short-sha }}" - VERSION: "${{ steps.get-backend-version.outputs.version }}" - BUILD: "${{ steps.get-backend-version.outputs.build-number }}" - run: | - set -x - docker buildx build \ - --cache-from "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket" \ - --cache-to "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max" \ - --push \ - --platform linux/amd64 \ - --build-arg IMAGE_ID="deployment.${SHORT_SHA}" \ - -f services/backend/Dockerfile \ - --target deployment \ - -t "nexus.bthlab.bthlabs.net:8002/hotpocket/backend:deployment-${VERSION}-${BUILD}" \ - services/ + build-for-development: + name: "Build" + uses: "./.gitea/workflows/build-deployment-images.yaml" + with: + target: "deployment" + platform: "linux/amd64" + registry: "nexus.bthlab.bthlabs.net:8002" + secrets: + VAULT_ROLE_ID: "${{ secrets.VAULT_ROLE_ID }}" + VAULT_SECRET_ID: "${{ secrets.VAULT_SECRET_ID }}" - deploy: + deploy-to-deployment: name: "Deploy" runs-on: "ubuntu-latest" needs: - - "build-deployment-images" + - "build-for-development" env: KUBERNETES_NAMESPACE: "hotpocket-development" KUBERNETES_CLUSTER: "k8s.bthlab" steps: - name: "Checkout the code" uses: "actions/checkout@v2" - - name: "Get run info" - id: "get-run-info" - uses: "./.gitea/actions/get-run-info" - with: - compose-project-base: "${{ vars.COMPOSE_PROJECT_BASE }}" - name: "Get build options" id: "get-build-options" uses: "./.gitea/actions/get-build-options" @@ -105,7 +52,6 @@ jobs: gitea/data/k8s.bthlab config | KUBECONFIG_PAYLOAD - name: "Set up kubeconfig" env: - COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}" KUBECONFIG_PAYLOAD: "${{ steps.import-secrets.outputs.KUBECONFIG_PAYLOAD }}" run: | set -x @@ -117,7 +63,6 @@ jobs: /opt/k8s/bin/kubectl get node - name: "Run `backend` Django migrations" env: - COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}" BACKEND_TAG: "deployment-${{ steps.get-backend-version.outputs.version }}-${{ steps.get-backend-version.outputs.build-number }}" run: | set -x @@ -126,6 +71,7 @@ jobs: cd deployment/hotpocket.bthlab ; export KUBECONFIG="/opt/k8s/etc/kubeconfig" ; /opt/k8s/bin/kubectl config use-context ${KUBERNETES_CLUSTER} ; + /opt/k8s/bin/kubectl -n ${KUBERNETES_NAMESPACE} apply -f resources/backend/config-map-local-deps.yaml ; /opt/k8s/bin/kubectl -n ${KUBERNETES_NAMESPACE} set image cronjobs/backend-job-migrations migrations=nexus.bthlab.bthlabs.net:8002/hotpocket/backend:${BACKEND_TAG} ; /opt/k8s/bin/kubectl -n ${KUBERNETES_NAMESPACE} delete jobs --ignore-not-found=true backend-job-migrations ; /opt/k8s/bin/kubectl -n ${KUBERNETES_NAMESPACE} create job backend-job-migrations --from=cronjob/backend-job-migrations ; @@ -133,7 +79,6 @@ jobs: ) - name: "Deploy" env: - COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.compose-project }}" BACKEND_TAG: "deployment-${{ steps.get-backend-version.outputs.version }}-${{ steps.get-backend-version.outputs.build-number }}" run: | set -x diff --git a/.gitea/workflows/production.yaml b/.gitea/workflows/production.yaml new file mode 100644 index 0000000..46ab89a --- /dev/null +++ b/.gitea/workflows/production.yaml @@ -0,0 +1,76 @@ +name: "Production deployment" + +on: + release: + types: ["published"] + +jobs: + build-for-production: + name: "Build" + uses: "./.gitea/workflows/build-deployment-images.yaml" + with: + target: "deployment" + platform: "linux/amd64" + secrets: + VAULT_ROLE_ID: "${{ secrets.VAULT_ROLE_ID }}" + VAULT_SECRET_ID: "${{ secrets.VAULT_SECRET_ID }}" + + deploy-to-production: + name: "Deploy" + runs-on: "ubuntu-latest" + needs: + - "build-for-production" + steps: + - name: "Checkout the code" + uses: "actions/checkout@v2" + - name: "Get build options" + id: "get-build-options" + uses: "./.gitea/actions/get-build-options" + - name: "Get `backend` version" + id: "get-backend-version" + uses: "./.gitea/actions/get-service-version" + with: + service: "backend" + - name: "Import Secrets" + id: "import-secrets" + uses: "hashicorp/vault-action@v2" + with: + url: "https://vault.bthlabs.pl/" + method: "approle" + roleId: "${{ secrets.VAULT_ROLE_ID }}" + secretId: "${{ secrets.VAULT_SECRET_ID }}" + secrets: | + gitea/data/hotpocket.app ansible_vault_payload | ANSIBLE_VAULT_PAYLOAD ; + gitea/data/hotpocket.app ansible_vault_password | ANSIBLE_VAULT_PASSWORD ; + gitea/data/hotpocket.app ansible_inventory_payload | ANSIBLE_INVENTORY_PAYLOAD ; + gitea/data/hotpocket.app ssh_key_payload | SSH_KEY_PAYLOAD + - name: "Setup Ansible" + uses: "./.gitea/actions/setup-ansible" + - name: "Prepare Ansible secrets" + run: | + set -x + + mkdir deployment/hotpocket_app/.ci + echo "${ANSIBLE_VAULT_PAYLOAD}" | base64 -d >"deployment/hotpocket_app/env_vars/production/vault.yaml" + echo "${ANSIBLE_VAULT_PASSWORD}" >"deployment/hotpocket_app/.ci/vault_password" + echo "${ANSIBLE_INVENTORY_PAYLOAD}" | base64 -d >"deployment/hotpocket_app/inventory_ci.yaml" + echo "${SSH_KEY_PAYLOAD}" | base64 -d >"deployment/hotpocket_app/.ci/ssh_key" + chmod 600 deployment/hotpocket_app/.ci/ssh_key + - name: "Engage!" + env: + VERSION: "${{ steps.get-backend-version.outputs.version }}" + BUILD: "${{ steps.get-backend-version.outputs.build-number }}" + run: | + set -x + + ( + cd deployment/hotpocket_app ; + ANSIBLE_HOST_KEY_CHECKING="False" /opt/ansible/bin/ansible-playbook \ + -i inventory_ci.yaml \ + --vault-id hotpocket@.ci/vault_password \ + -e @env_vars/production/vars.yaml \ + -e @env_vars/production/vault.yaml \ + -e hotpocket_app_image_tag="deployment-${VERSION}-${BUILD}" \ + --limit "*.production.hotpocket.app" \ + deploy.yaml + ) diff --git a/.gitea/workflows/staging.yaml b/.gitea/workflows/staging.yaml new file mode 100644 index 0000000..51be70b --- /dev/null +++ b/.gitea/workflows/staging.yaml @@ -0,0 +1,76 @@ +name: "Staging deployment" + +on: + release: + types: ["published"] + +jobs: + build-for-staging: + name: "Build" + uses: "./.gitea/workflows/build-deployment-images.yaml" + with: + target: "aio" + platform: "linux/amd64" + secrets: + VAULT_ROLE_ID: "${{ secrets.VAULT_ROLE_ID }}" + VAULT_SECRET_ID: "${{ secrets.VAULT_SECRET_ID }}" + + deploy-to-staging: + name: "Deploy" + runs-on: "ubuntu-latest" + needs: + - "build-for-staging" + steps: + - name: "Checkout the code" + uses: "actions/checkout@v2" + - name: "Get build options" + id: "get-build-options" + uses: "./.gitea/actions/get-build-options" + - name: "Get `backend` version" + id: "get-backend-version" + uses: "./.gitea/actions/get-service-version" + with: + service: "backend" + - name: "Import Secrets" + id: "import-secrets" + uses: "hashicorp/vault-action@v2" + with: + url: "https://vault.bthlabs.pl/" + method: "approle" + roleId: "${{ secrets.VAULT_ROLE_ID }}" + secretId: "${{ secrets.VAULT_SECRET_ID }}" + secrets: | + gitea/data/staging.hotpocket.app ansible_vault_payload | ANSIBLE_VAULT_PAYLOAD ; + gitea/data/staging.hotpocket.app ansible_vault_password | ANSIBLE_VAULT_PASSWORD ; + gitea/data/staging.hotpocket.app ansible_inventory_payload | ANSIBLE_INVENTORY_PAYLOAD ; + gitea/data/staging.hotpocket.app ssh_key_payload | SSH_KEY_PAYLOAD + - name: "Setup Ansible" + uses: "./.gitea/actions/setup-ansible" + - name: "Prepare Ansible secrets" + run: | + set -x + + mkdir deployment/hotpocket_app/.ci + echo "${ANSIBLE_VAULT_PAYLOAD}" | base64 -d >"deployment/hotpocket_app/env_vars/staging/vault.yaml" + echo "${ANSIBLE_VAULT_PASSWORD}" >"deployment/hotpocket_app/.ci/vault_password" + echo "${ANSIBLE_INVENTORY_PAYLOAD}" | base64 -d >"deployment/hotpocket_app/inventory_ci.yaml" + echo "${SSH_KEY_PAYLOAD}" | base64 -d >"deployment/hotpocket_app/.ci/ssh_key" + chmod 600 deployment/hotpocket_app/.ci/ssh_key + - name: "Engage!" + env: + VERSION: "${{ steps.get-backend-version.outputs.version }}" + BUILD: "${{ steps.get-backend-version.outputs.build-number }}" + run: | + set -x + + ( + cd deployment/hotpocket_app ; + ANSIBLE_HOST_KEY_CHECKING="False" /opt/ansible/bin/ansible-playbook \ + -i inventory_ci.yaml \ + --vault-id hotpocket@.ci/vault_password \ + -e @env_vars/staging/vars.yaml \ + -e @env_vars/staging/vault.yaml \ + -e hotpocket_app_image_tag="aio-${VERSION}-${BUILD}" \ + --limit "*.staging.hotpocket.app" \ + deploy.yaml + ) diff --git a/deployment/hotpocket.bthlab/resources/backend/config-map-local-deps.yaml b/deployment/hotpocket.bthlab/resources/backend/config-map-local-deps.yaml index 62d9a49..a08b565 100644 --- a/deployment/hotpocket.bthlab/resources/backend/config-map-local-deps.yaml +++ b/deployment/hotpocket.bthlab/resources/backend/config-map-local-deps.yaml @@ -15,4 +15,4 @@ data: ./manage.py collectstatic --no-input ) requirements.txt: | - hotpocket_bthlabs==25.10.27 + hotpocket_bthlabs>=25.10.28 diff --git a/deployment/hotpocket.bthlab/resources/backend/webapp.yaml b/deployment/hotpocket.bthlab/resources/backend/webapp.yaml index 29d27d9..a839b47 100644 --- a/deployment/hotpocket.bthlab/resources/backend/webapp.yaml +++ b/deployment/hotpocket.bthlab/resources/backend/webapp.yaml @@ -52,6 +52,8 @@ spec: secretKeyRef: name: backend-vault key: secret_id + - name: HOTPOCKET_BACKEND_CREATE_INITIAL_ACCOUNT + value: "true" ports: - containerPort: 8000 name: http diff --git a/deployment/hotpocket_app/.gitignore b/deployment/hotpocket_app/.gitignore new file mode 100644 index 0000000..c3aa08c --- /dev/null +++ b/deployment/hotpocket_app/.gitignore @@ -0,0 +1,3 @@ +.ci/ +inventory_ci.yaml +vault.yaml diff --git a/deployment/hotpocket_app/deploy.yaml b/deployment/hotpocket_app/deploy.yaml new file mode 100644 index 0000000..abc3fce --- /dev/null +++ b/deployment/hotpocket_app/deploy.yaml @@ -0,0 +1,5 @@ +- name: "Deploy HotPocket" + hosts: "hotpocket_app" + roles: + - role: "hotpocket_app" + tags: ["hotpocket-app"] diff --git a/deployment/hotpocket_app/env_vars/production/etc/backend/entrypoint.d/01-install-customized-deps.sh b/deployment/hotpocket_app/env_vars/production/etc/backend/entrypoint.d/01-install-customized-deps.sh new file mode 100755 index 0000000..82dae02 --- /dev/null +++ b/deployment/hotpocket_app/env_vars/production/etc/backend/entrypoint.d/01-install-customized-deps.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +export PIP_INDEX_URL="https://nexus.bthlabs.pl/repository/pypi/simple/" +/srv/venv/bin/pip install -r /srv/lib/backend/requirements.txt diff --git a/deployment/hotpocket_app/env_vars/production/etc/backend/entrypoint.d/99-collectstatic.sh b/deployment/hotpocket_app/env_vars/production/etc/backend/entrypoint.d/99-collectstatic.sh new file mode 100755 index 0000000..db15d87 --- /dev/null +++ b/deployment/hotpocket_app/env_vars/production/etc/backend/entrypoint.d/99-collectstatic.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +( + cd /srv/app; + ./manage.py collectstatic --no-input +) diff --git a/deployment/hotpocket_app/env_vars/production/lib/backend/requirements.txt b/deployment/hotpocket_app/env_vars/production/lib/backend/requirements.txt new file mode 100644 index 0000000..11e1e36 --- /dev/null +++ b/deployment/hotpocket_app/env_vars/production/lib/backend/requirements.txt @@ -0,0 +1 @@ +hotpocket-bthlabs>=25.10.28 diff --git a/deployment/hotpocket_app/env_vars/production/vars.yaml b/deployment/hotpocket_app/env_vars/production/vars.yaml new file mode 100644 index 0000000..baf9c02 --- /dev/null +++ b/deployment/hotpocket_app/env_vars/production/vars.yaml @@ -0,0 +1,60 @@ +hotpocket_app: + deployment_directory: "/srv/hotpocket" + owner: "hotpocket" + group: "hotpocket" + mode: "fullstack" + loki: + url: "http://monitoring.vm.snakeweb.net.bthlabs.net:3100/loki/api/v1/push" + node: "home.vm.snakeweb.net" + docker: + extra_hosts: + - "home.vm:10.0.1.2" + backend: + image_tag: "{{ hotpocket_app_image_tag|default('deployment-v25.10.21-01') }}" + database: + name: "thisissecret" + user: "thisissecret" + host: "thisissecret" + rabbitmq: + vhost: "thisissecret" + user: "thisissecret" + host: "thisissecret" + model_auth_is_disabled: true + env: "production" + extra_env: + - "HOTPOCKET_BACKEND_SECRETS_PACKAGE=hotpocket_bthlabs.secrets" + - "VAULT_URL={{ hotpocket_app_secrets.backend.vault.url }}" + - "VAULT_ROLE_ID={{ hotpocket_app_secrets.backend.vault.role_id }}" + - "VAULT_SECRET_ID={{ hotpocket_app_secrets.backend.vault.secret_id }}" + oidc: + enabled: true + endpoint: "thisissecret" + display_name: "thisissecret" + webapp: + settings_module: "hotpocket_bthlabs.settings.webapp" + loki: + external_labels: "job=hotpocket,service=backend-webapp,environment=production" + allowed_hosts: + - "my.hotpocket.app" + admin: + settings_module: "hotpocket_bthlabs.settings.admin" + loki: + external_labels: "job=hotpocket,service=backend-admin,environment=production" + allowed_hosts: + - "admin.hotpocket.app" + celery_worker: + concurrency: 2 + loki: + external_labels: "job=hotpocket,service=backend-celery-worker,environment=production" + celery_beat: + loki: + external_labels: "job=hotpocket,service=backend-celery-beat,environment=production" + customization: + - src: "{{ inventory_dir }}/env_vars/production/etc/backend/entrypoint.d/01-install-customized-deps.sh" + dest: "etc/backend/entrypoint.d/01-install-customized-deps.sh" + mode: "755" + - src: "{{ inventory_dir }}/env_vars/production/etc/backend/entrypoint.d/99-collectstatic.sh" + dest: "etc/backend/entrypoint.d/99-collectstatic.sh" + mode: "755" + - src: "{{ inventory_dir }}/env_vars/production/lib/backend/requirements.txt" + dest: "lib/backend/requirements.txt" diff --git a/deployment/hotpocket_app/env_vars/staging/etc/backend/entrypoint.d/01-install-customized-deps.sh b/deployment/hotpocket_app/env_vars/staging/etc/backend/entrypoint.d/01-install-customized-deps.sh new file mode 100755 index 0000000..82dae02 --- /dev/null +++ b/deployment/hotpocket_app/env_vars/staging/etc/backend/entrypoint.d/01-install-customized-deps.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +export PIP_INDEX_URL="https://nexus.bthlabs.pl/repository/pypi/simple/" +/srv/venv/bin/pip install -r /srv/lib/backend/requirements.txt diff --git a/deployment/hotpocket_app/env_vars/staging/etc/backend/entrypoint.d/99-collectstatic.sh b/deployment/hotpocket_app/env_vars/staging/etc/backend/entrypoint.d/99-collectstatic.sh new file mode 100755 index 0000000..db15d87 --- /dev/null +++ b/deployment/hotpocket_app/env_vars/staging/etc/backend/entrypoint.d/99-collectstatic.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +( + cd /srv/app; + ./manage.py collectstatic --no-input +) diff --git a/deployment/hotpocket_app/env_vars/staging/lib/backend/requirements.txt b/deployment/hotpocket_app/env_vars/staging/lib/backend/requirements.txt new file mode 100644 index 0000000..11e1e36 --- /dev/null +++ b/deployment/hotpocket_app/env_vars/staging/lib/backend/requirements.txt @@ -0,0 +1 @@ +hotpocket-bthlabs>=25.10.28 diff --git a/deployment/hotpocket_app/env_vars/staging/vars.yaml b/deployment/hotpocket_app/env_vars/staging/vars.yaml new file mode 100644 index 0000000..52defd4 --- /dev/null +++ b/deployment/hotpocket_app/env_vars/staging/vars.yaml @@ -0,0 +1,37 @@ +hotpocket_app: + deployment_directory: "/srv/hotpocket_staging" + owner: "hotpocket_staging" + group: "hotpocket_staging" + mode: "aio" + loki: + url: "http://monitoring.vm.snakeweb.net.bthlabs.net:3100/loki/api/v1/push" + node: "home.vm.snakeweb.net" + docker: + extra_hosts: + - "home.vm:10.0.1.2" + backend: + image_tag: "{{ hotpocket_app_image_tag|default('aio-v25.10.29-rc1-01') }}" + model_auth_is_disabled: false + env: "staging" + extra_env: + - "HOTPOCKET_BACKEND_SECRETS_PACKAGE=hotpocket_bthlabs.secrets" + - "VAULT_URL={{ hotpocket_app_secrets.backend.vault.url }}" + - "VAULT_ROLE_ID={{ hotpocket_app_secrets.backend.vault.role_id }}" + - "VAULT_SECRET_ID={{ hotpocket_app_secrets.backend.vault.secret_id }}" + oidc: + enabled: false + webapp: + settings_module: "hotpocket_bthlabs.settings.webapp" + loki: + external_labels: "job=hotpocket,service=backend-webapp,environment=staging" + allowed_hosts: + - "staging.hotpocket.app" + customization: + - src: "{{ inventory_dir }}/env_vars/staging/etc/backend/entrypoint.d/01-install-customized-deps.sh" + dest: "etc/backend/entrypoint.d/01-install-customized-deps.sh" + mode: "755" + - src: "{{ inventory_dir }}/env_vars/staging/etc/backend/entrypoint.d/99-collectstatic.sh" + dest: "etc/backend/entrypoint.d/99-collectstatic.sh" + mode: "755" + - src: "{{ inventory_dir }}/env_vars/staging/lib/backend/requirements.txt" + dest: "lib/backend/requirements.txt" diff --git a/deployment/hotpocket_app/host_vars/home.vm.snakeweb.net/vars.yaml b/deployment/hotpocket_app/host_vars/home.vm.snakeweb.net/vars.yaml new file mode 100644 index 0000000..e69de29 diff --git a/deployment/hotpocket_app/host_vars/vm-125.homelab01.bthlab/vars.yaml b/deployment/hotpocket_app/host_vars/vm-125.homelab01.bthlab/vars.yaml new file mode 100644 index 0000000..e69de29 diff --git a/deployment/hotpocket_app/inventory.yaml b/deployment/hotpocket_app/inventory.yaml new file mode 100644 index 0000000..6d14f38 --- /dev/null +++ b/deployment/hotpocket_app/inventory.yaml @@ -0,0 +1,10 @@ +hotpocket_app: + hosts: + web1.staging.hotpocket.app: + ansible_host: vm-125.homelab01.bthlab + ansible_port: 22 + ansible_user: hotpocket_staging + web1.production.hotpocket.app: + ansible_host: vm-125.homelab01.bthlab + ansible_port: 22 + ansible_user: hotpocket diff --git a/deployment/hotpocket_app/roles/hotpocket_app/tasks/main.yaml b/deployment/hotpocket_app/roles/hotpocket_app/tasks/main.yaml new file mode 100644 index 0000000..72b392f --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/tasks/main.yaml @@ -0,0 +1,73 @@ +- name: "Create workspace directories" + ansible.builtin.file: + path: "{{ hotpocket_app.deployment_directory }}/{{ item }}" + state: "directory" + loop: + - "etc" + - "etc/backend" + - "etc/backend/entrypoint.d" + - "lib" + - "lib/backend" + - "log" + - "run" + - "run/backend-admin" + - "run/backend-celery-beat" + - "run/backend-celery-worker" + - "run/backend-webapp" + - "run/uploads" +- name: "Install docker-compose.yml" + ansible.builtin.template: + src: "templates/{{ hotpocket_app.mode }}/docker-compose.yaml.jinja2" + dest: "{{ hotpocket_app.deployment_directory }}/docker-compose.yaml" + owner: "{{ hotpocket_app.owner }}" + group: "{{ hotpocket_app.group }}" +- name: "Install env files" + ansible.builtin.template: + src: "templates/{{ hotpocket_app.mode }}/{{ item }}.jinja2" + dest: "{{ hotpocket_app.deployment_directory }}/etc/{{ item }}" + owner: "{{ hotpocket_app.owner }}" + group: "{{ hotpocket_app.group }}" + loop: "{{ hotpocket_app_role.env_files[hotpocket_app.mode] }}" +- name: "Upload customization files" + ansible.builtin.copy: + src: "{{ item.src }}" + dest: "{{ hotpocket_app.deployment_directory }}/{{ item.dest }}" + owner: "{{ hotpocket_app.owner }}" + group: "{{ hotpocket_app.group }}" + mode: "{{ item.mode|default('644') }}" + loop: "{{ hotpocket_app.customization }}" + when: "hotpocket_app.customization is defined" +- name: "Install hotpocket_app.service unit" + ansible.builtin.template: + src: "templates/{{ hotpocket_app_role.services[hotpocket_app.mode].src }}.jinja2" + dest: "{{ hotpocket_app.deployment_directory }}/etc/{{ hotpocket_app_role.services[hotpocket_app.mode].dest }}" + owner: "{{ hotpocket_app.owner }}" + group: "{{ hotpocket_app.group }}" +- name: "Stop the stack" + ansible.builtin.command: + argv: + - "docker" + - "compose" + - "down" + chdir: "{{ hotpocket_app.deployment_directory }}" +- name: "Run backend migrations" + ansible.builtin.command: + argv: + - "docker" + - "compose" + - "run" + - "--rm" + - "backend-webapp" + - "./manage.py" + - "migrate" + chdir: "{{ hotpocket_app.deployment_directory }}" + when: "hotpocket_app.mode == 'fullstack' and is_manual_run is not defined" +- name: "Start the stack" + ansible.builtin.command: + argv: + - "docker" + - "compose" + - "up" + - "-d" + chdir: "{{ hotpocket_app.deployment_directory }}" + when: "is_manual_run is not defined" diff --git a/deployment/hotpocket_app/roles/hotpocket_app/templates/aio/backend_base.env.jinja2 b/deployment/hotpocket_app/roles/hotpocket_app/templates/aio/backend_base.env.jinja2 new file mode 100644 index 0000000..eba5404 --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/templates/aio/backend_base.env.jinja2 @@ -0,0 +1,9 @@ +DJANGO_SETTINGS_MODULE="{{ hotpocket_app.backend.webapp.settings_module|default('hotpocket_backend.settings.aio')}}" +HOTPOCKET_BACKEND_ENV="{{ hotpocket_app.backend.env|default('aio') }}" +HOTPOCKET_BACKEND_MODEL_AUTH_IS_DISABLED="{% if hotpocket_app.backend.model_auth_is_disabled %}true{% else %}false{% endif %}" + +{% if hotpocket_app.backend.oidc.enabled %}HOTPOCKET_BACKEND_OIDC_PAYLOAD='{"endpoint":"{{ hotpocket_app.backend.oidc.endpoint }}","key":"{{ hotpocket_app_secrets.backend.oidc.key }}","secret":"{{ hotpocket_app_secrets.backend.oidc.secret }}","display_name":"{{ hotpocket_app.backend.oidc.display_name }}"}'{% else %}#noop{% endif %} + +{% for extra_env in hotpocket_app.backend.extra_env|default([]) %} +{{ extra_env }} +{% endfor %} diff --git a/deployment/hotpocket_app/roles/hotpocket_app/templates/aio/backend_webapp.env.jinja2 b/deployment/hotpocket_app/roles/hotpocket_app/templates/aio/backend_webapp.env.jinja2 new file mode 100644 index 0000000..17b174b --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/templates/aio/backend_webapp.env.jinja2 @@ -0,0 +1,7 @@ +HOTPOCKET_BACKEND_SECRET_KEY: "{{ hotpocket_app_secrets.backend.webapp.secret_key }}" +HOTPOCKET_BACKEND_ALLOWED_HOSTS="{{ hotpocket_app.backend.webapp.allowed_hosts|join(',') }}" +HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME: "{{ hotpocket_app_secrets.backend.webapp.initial_account.username }}" +HOTPOCKET_BACKEND_INITIAL_ACCOUNT_PASSWORD: "{{ hotpocket_app_secrets.backend.webapp.initial_account.password }}" +{% for extra_env in hotpocket_app.backend.webapp.extra_env|default([]) %} +{{ extra_env }} +{% endfor %} diff --git a/deployment/hotpocket_app/roles/hotpocket_app/templates/aio/docker-compose.yaml.jinja2 b/deployment/hotpocket_app/roles/hotpocket_app/templates/aio/docker-compose.yaml.jinja2 new file mode 100644 index 0000000..a1714dd --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/templates/aio/docker-compose.yaml.jinja2 @@ -0,0 +1,28 @@ +services: + backend-webapp: + image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:{{ hotpocket_app.backend.image_tag }}" + command: + - "/srv/venv/bin/gunicorn" + - "-c" + - "/srv/lib/gunicorn.conf.py" + - "-b" + - "unix:///srv/run/gunicorn.sock" + - "hotpocket_backend.wsgi:application" + logging: + driver: "loki" + options: + loki-url: "{{ hotpocket_app.loki.url }}" + loki-external-labels: "{{ hotpocket_app.backend.webapp.loki.external_labels }}" + labels: "node" + labels: + node: "{{ hotpocket_app.loki.node }}" + env_file: + - "etc/backend_base.env" + - "etc/backend_webapp.env" + extra_hosts: [{% for extra_host in hotpocket_app.docker.extra_hosts|default([]) %}"{{ extra_host }}"{% endfor %}] + restart: "unless-stopped" + volumes: + - "{{ hotpocket_app.deployment_directory }}/etc/backend:/srv/etc" + - "{{ hotpocket_app.deployment_directory }}/lib/backend:/srv/lib/backend" + - "{{ hotpocket_app.deployment_directory }}/run/backend-webapp:/srv/run" + - "{{ hotpocket_app.deployment_directory }}/run/uploads:/srv/uploads" diff --git a/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/backend_admin.env.jinja2 b/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/backend_admin.env.jinja2 new file mode 100644 index 0000000..56f600d --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/backend_admin.env.jinja2 @@ -0,0 +1,8 @@ +DJANGO_SETTINGS_MODULE="{{ hotpocket_app.backend.admin.settings_module|default('hotpocket_backend.settings.deployment.admin')}}" +HOTPOCKET_BACKEND_GUNICORN_WORKERS=2 +HOTPOCKET_BACKEND_APP="admin" +HOTPOCKET_BACKEND_SECRET_KEY="{{ hotpocket_app_secrets.backend.admin.secret_key }}" +HOTPOCKET_BACKEND_ALLOWED_HOSTS="{{ hotpocket_app.backend.admin.allowed_hosts|join(',') }}" +{% for extra_env in hotpocket_app.backend.admin.extra_env|default([]) %} +{{ extra_env }} +{% endfor %} diff --git a/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/backend_base.env.jinja2 b/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/backend_base.env.jinja2 new file mode 100644 index 0000000..3dfa4fe --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/backend_base.env.jinja2 @@ -0,0 +1,15 @@ +HOTPOCKET_BACKEND_ENV="{{ hotpocket_app.backend.env|default('deployment') }}" +HOTPOCKET_BACKEND_DATABASE_NAME="{{ hotpocket_app.backend.database.name }}" +HOTPOCKET_BACKEND_DATABASE_USER="{{ hotpocket_app.backend.database.user }}" +HOTPOCKET_BACKEND_DATABASE_PASSWORD="{{ hotpocket_app_secrets.backend.database.password }}" +HOTPOCKET_BACKEND_DATABASE_HOST="{{ hotpocket_app.backend.database.host }}" +HOTPOCKET_BACKEND_CELERY_BROKER_URL="amqp://{{ hotpocket_app.backend.rabbitmq.user }}:{{ hotpocket_app_secrets.backend.rabbitmq.password }}@{{ hotpocket_app.backend.rabbitmq.host }}/{{ hotpocket_app.backend.rabbitmq.vhost }}" +HOTPOCKET_BACKEND_CELERY_RESULT_BACKEND="db+postgresql+psycopg://{{ hotpocket_app.backend.database.user }}:{{ hotpocket_app_secrets.backend.database.password }}@{{ hotpocket_app.backend.database.host }}/{{ hotpocket_app.backend.database.name }}" +HOTPOCKET_BACKEND_MODEL_AUTH_IS_DISABLED="{% if hotpocket_app.backend.model_auth_is_disabled %}true{% else %}false{% endif %}" + +{% if hotpocket_app.backend.oidc.enabled %}HOTPOCKET_BACKEND_OIDC_PAYLOAD='{"endpoint":"{{ hotpocket_app.backend.oidc.endpoint }}","key":"{{ hotpocket_app_secrets.backend.oidc.key }}","secret":"{{ hotpocket_app_secrets.backend.oidc.secret }}","display_name":"{{ hotpocket_app.backend.oidc.display_name }}"}'{% else %}#noop{% endif %} + +{% for extra_env in hotpocket_app.backend.extra_env|default([]) %} +{{ extra_env }} +{% endfor %} + diff --git a/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/backend_webapp.env.jinja2 b/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/backend_webapp.env.jinja2 new file mode 100644 index 0000000..f3f322a --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/backend_webapp.env.jinja2 @@ -0,0 +1,9 @@ +DJANGO_SETTINGS_MODULE="{{ hotpocket_app.backend.webapp.settings_module|default('hotpocket_backend.settings.deployment.webapp')}}" +HOTPOCKET_BACKEND_APP="webapp" +HOTPOCKET_BACKEND_SECRET_KEY="{{ hotpocket_app_secrets.backend.webapp.secret_key }}" +HOTPOCKET_BACKEND_ALLOWED_HOSTS="{{ hotpocket_app.backend.webapp.allowed_hosts|join(',') }}" +HOTPOCKET_BACKEND_SAVES_SAVE_ADAPTER="hotpocket_backend.apps.saves.adapters.postgres:PostgresSaveAdapter" +HOTPOCKET_BACKEND_SAVES_ASSOCIATION_ADAPTER="hotpocket_backend.apps.saves.adapters.postgres:PostgresAssociationAdapter" +{% for extra_env in hotpocket_app.backend.webapp.extra_env|default([]) %} +{{ extra_env }} +{% endfor %} diff --git a/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/docker-compose.yaml.jinja2 b/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/docker-compose.yaml.jinja2 new file mode 100644 index 0000000..1eb7a0d --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/templates/fullstack/docker-compose.yaml.jinja2 @@ -0,0 +1,118 @@ +services: + backend-webapp: + image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:{{ hotpocket_app.backend.image_tag }}" + command: + - "/srv/venv/bin/gunicorn" + - "-c" + - "/srv/lib/gunicorn.conf.py" + - "-b" + - "unix:///srv/run/gunicorn.sock" + - "hotpocket_backend.wsgi:application" + logging: + driver: "loki" + options: + loki-url: "{{ hotpocket_app.loki.url }}" + loki-external-labels: "{{ hotpocket_app.backend.webapp.loki.external_labels }}" + labels: "node" + labels: + node: "{{ hotpocket_app.loki.node }}" + env_file: + - "etc/backend_base.env" + - "etc/backend_webapp.env" + extra_hosts: [{% for extra_host in hotpocket_app.docker.extra_hosts %}"{{ extra_host }}"{% endfor %}] + restart: "unless-stopped" + volumes: + - "{{ hotpocket_app.deployment_directory }}/etc/backend:/srv/etc" + - "{{ hotpocket_app.deployment_directory }}/lib/backend:/srv/lib/backend" + - "{{ hotpocket_app.deployment_directory }}/run/backend-webapp:/srv/run" + - "{{ hotpocket_app.deployment_directory }}/run/uploads:/srv/uploads" + + backend-admin: + image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:{{ hotpocket_app.backend.image_tag }}" + command: + - "/srv/venv/bin/gunicorn" + - "-c" + - "/srv/lib/gunicorn.conf.py" + - "-b" + - "unix:///srv/run/gunicorn.sock" + - "hotpocket_backend.wsgi:application" + logging: + driver: "loki" + options: + loki-url: "{{ hotpocket_app.loki.url }}" + loki-external-labels: "{{ hotpocket_app.backend.admin.loki.external_labels }}" + labels: "node" + labels: + node: "{{ hotpocket_app.loki.node }}" + env_file: + - "etc/backend_base.env" + - "etc/backend_admin.env" + extra_hosts: [{% for extra_host in hotpocket_app.docker.extra_hosts %}"{{ extra_host }}"{% endfor %}] + restart: "unless-stopped" + volumes: + - "{{ hotpocket_app.deployment_directory }}/etc/backend:/srv/etc" + - "{{ hotpocket_app.deployment_directory }}/lib/backend:/srv/lib/backend" + - "{{ hotpocket_app.deployment_directory }}/run/backend-admin:/srv/run" + - "{{ hotpocket_app.deployment_directory }}/run/uploads:/srv/uploads" + + backend-celery-worker: + image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:{{ hotpocket_app.backend.image_tag }}" + command: + - "/srv/venv/bin/celery" + - "-A" + - "hotpocket_backend.celery:app" + - "worker" + - "-l" + - "INFO" + - "-Q" + - "celery,webapp" + - "-c" + - "{{ hotpocket_app.backend.celery_worker.concurrency }}" + logging: + driver: "loki" + options: + loki-url: "{{ hotpocket_app.loki.url }}" + loki-external-labels: "{{ hotpocket_app.backend.celery_worker.loki.external_labels }}" + labels: "node" + labels: + node: "{{ hotpocket_app.loki.node }}" + env_file: + - "etc/backend_base.env" + - "etc/backend_webapp.env" + extra_hosts: [{% for extra_host in hotpocket_app.docker.extra_hosts %}"{{ extra_host }}"{% endfor %}] + restart: "unless-stopped" + volumes: + - "{{ hotpocket_app.deployment_directory }}/etc/backend:/srv/etc" + - "{{ hotpocket_app.deployment_directory }}/lib/backend:/srv/lib/backend" + - "{{ hotpocket_app.deployment_directory }}/run/backend-celery-worker:/srv/run" + - "{{ hotpocket_app.deployment_directory }}/run/uploads:/srv/uploads" + + backend-celery-beat: + image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:{{ hotpocket_app.backend.image_tag }}" + command: + - "/srv/venv/bin/celery" + - "-A" + - "hotpocket_backend.celery:app" + - "beat" + - "-l" + - "INFO" + - "-s" + - "/srv/run/celery-beat-schedule" + logging: + driver: "loki" + options: + loki-url: "{{ hotpocket_app.loki.url }}" + loki-external-labels: "{{ hotpocket_app.backend.celery_beat.loki.external_labels }}" + labels: "node" + labels: + node: "{{ hotpocket_app.loki.node }}" + env_file: + - "etc/backend_base.env" + - "etc/backend_webapp.env" + extra_hosts: [{% for extra_host in hotpocket_app.docker.extra_hosts %}"{{ extra_host }}"{% endfor %}] + restart: "unless-stopped" + volumes: + - "{{ hotpocket_app.deployment_directory }}/etc/backend:/srv/etc" + - "{{ hotpocket_app.deployment_directory }}/lib/backend:/srv/lib/backend" + - "{{ hotpocket_app.deployment_directory }}/run/backend-celery-beat:/srv/run" + - "{{ hotpocket_app.deployment_directory }}/run/uploads:/srv/uploads" diff --git a/deployment/hotpocket_app/roles/hotpocket_app/templates/hotpocket_app.service.jinja2 b/deployment/hotpocket_app/roles/hotpocket_app/templates/hotpocket_app.service.jinja2 new file mode 100644 index 0000000..112e0a6 --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/templates/hotpocket_app.service.jinja2 @@ -0,0 +1,15 @@ +[Unit] +Description=hotpocket_backend.webapp +Requires=docker.service +After=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory={{ hotpocket_app.deployment_directory }} +ExecStart=/usr/bin/docker compose up -d +ExecStop=/usr/bin/docker compose down +TimeoutStartSec=0 + +[Install] +WantedBy=multi-user.target diff --git a/deployment/hotpocket_app/roles/hotpocket_app/vars/main.yaml b/deployment/hotpocket_app/roles/hotpocket_app/vars/main.yaml new file mode 100644 index 0000000..fdb0b52 --- /dev/null +++ b/deployment/hotpocket_app/roles/hotpocket_app/vars/main.yaml @@ -0,0 +1,16 @@ +hotpocket_app_role: + env_files: + fullstack: + - "backend_admin.env" + - "backend_base.env" + - "backend_webapp.env" + aio: + - "backend_base.env" + - "backend_webapp.env" + services: + fullstack: + src: "hotpocket_app.service" + dest: "hotpocket_app.service" + aio: + src: "hotpocket_app.service" + dest: "staging_hotpocket_app.service" diff --git a/services/backend/Dockerfile b/services/backend/Dockerfile index 9aa2bf4..751879f 100644 --- a/services/backend/Dockerfile +++ b/services/backend/Dockerfile @@ -45,11 +45,12 @@ COPY --from=deployment-build /srv/packages /srv/packages COPY --from=deployment-build /srv/venv /srv/venv COPY --chown=$APP_USER_UID:$APP_USER_GID backend/ops/bin/*.sh /srv/bin/ COPY --chown=$APP_USER_UID:$APP_USER_GID backend/ops/deployment/gunicorn.conf.py backend/ops/deployment/gunicorn.logging.conf /srv/lib/ +COPY --chown=root:root backend/ops/etc/sudoers_app /etc/sudoers.d/app USER root RUN apt-get update && \ - apt-get install -y libpq5 dumb-init && \ + apt-get install -y libpq5 dumb-init sudo && \ apt-get clean autoclean && \ apt-get autoremove --yes && \ rm -rf /var/lib/apt /var/lib/dpkg && \ @@ -67,6 +68,8 @@ ARG APP_USER_GID ARG IMAGE_ID ENV DJANGO_SETTINGS_MODULE=hotpocket_backend.settings.deployment.webapp +ENV HOTPOCKET_BACKEND_APP_USER_UID=${APP_USER_UID} +ENV HOTPOCKET_BACKEND_APP_USER_GID=${APP_USER_GID} ENV HOTPOCKET_BACKEND_ENV=deployment ENV HOTPOCKET_BACKEND_APP=webapp @@ -79,6 +82,8 @@ ARG APP_USER_GID ARG IMAGE_ID ENV DJANGO_SETTINGS_MODULE=hotpocket_backend.settings.aio +ENV HOTPOCKET_BACKEND_APP_USER_UID=${APP_USER_UID} +ENV HOTPOCKET_BACKEND_APP_USER_GID=${APP_USER_GID} ENV HOTPOCKET_BACKEND_ENV=aio ENV HOTPOCKET_BACKEND_APP=webapp ENV HOTPOCKET_BACKEND_DEBUG=false @@ -92,6 +97,7 @@ ENV HOTPOCKET_BACKEND_CELERY_IGNORE_RESULT=true ENV HOTPOCKET_BACKEND_CELERY_ALWAYS_EAGER=true ENV HOTPOCKET_BACKEND_GUNICORN_WORKERS=2 ENV HOTPOCKET_BACKEND_RUN_MIGRATIONS=true +ENV HOTPOCKET_BACKEND_CREATE_INITIAL_ACCOUNT=true ENV HOTPOCKET_BACKEND_UPLOADS_PATH=/srv/run/uploads VOLUME ["/srv/run"] diff --git a/services/backend/hotpocket_backend/_meta.py b/services/backend/hotpocket_backend/_meta.py index e4ebb6e..b3548eb 100644 --- a/services/backend/hotpocket_backend/_meta.py +++ b/services/backend/hotpocket_backend/_meta.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from __future__ import annotations -version = '25.10.21' +version = '25.11.06.b0' diff --git a/services/backend/hotpocket_backend/apps/accounts/management/commands/create_initial_account.py b/services/backend/hotpocket_backend/apps/accounts/management/commands/create_initial_account.py index bab10af..466869e 100644 --- a/services/backend/hotpocket_backend/apps/accounts/management/commands/create_initial_account.py +++ b/services/backend/hotpocket_backend/apps/accounts/management/commands/create_initial_account.py @@ -8,6 +8,7 @@ from django.core.management import BaseCommand import django.db from hotpocket_backend.apps.accounts.models import Account +from hotpocket_backend.apps.core.conf import settings LOGGER = logging.getLogger(__name__) @@ -17,12 +18,12 @@ class Command(BaseCommand): def add_arguments(self, parser: ArgumentParser): parser.add_argument( - 'username', - help='Username for the Account', + '-u', '--username', default=None, + help='Override username for the Account', ) parser.add_argument( - 'password', - help='Password for the Account', + '-p', '--password', default=None, + help='Override Password for the Account', ) parser.add_argument( '-d', '--dry-run', action='store_true', default=False, @@ -31,10 +32,22 @@ class Command(BaseCommand): def handle(self, *args, **options): LOGGER.debug('args=`%s` options=`%s`', args, options) - username = options.get('username') - password = options.get('password') dry_run = options.get('dry_run', False) + username = options.get('username') or settings.SECRETS.INITIAL_ACCOUNT.username + if not username: + LOGGER.info('Not creating initial Account: empty `username`') + return + + password = options.get('password') or settings.SECRETS.INITIAL_ACCOUNT.password + assert password, 'Unable to proceed: empty `password`' + + LOGGER.debug( + 'Creating initial Account: username=`%s` password=`%s`', + username, + password, + ) + with django.db.transaction.atomic(): current_account = Account.objects.filter(username=username).first() if current_account is not None: diff --git a/services/backend/hotpocket_backend/secrets/base.py b/services/backend/hotpocket_backend/secrets/base.py index e843dee..31b6b4e 100644 --- a/services/backend/hotpocket_backend/secrets/base.py +++ b/services/backend/hotpocket_backend/secrets/base.py @@ -3,7 +3,13 @@ from __future__ import annotations import json -from keep_it_secret import AbstractField, LiteralField, Secrets, SecretsField +from keep_it_secret import ( + AbstractField, + EnvField, + LiteralField, + Secrets, + SecretsField, +) class DatabaseSecrets(Secrets): @@ -84,6 +90,19 @@ class CelerySecrets(Secrets): result_backend: str = AbstractField.new() +class InitialAccountSecrets(Secrets): + username: str = EnvField.new( + 'HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME', + default=None, + required=False, + ) + password: str = EnvField.new( + 'HOTPOCKET_BACKEND_INITIAL_ACCOUNT_PASSWORD', + default=None, + required=False, + ) + + class BaseSecrets(Secrets): SECRET_KEY: str = AbstractField.new() @@ -91,3 +110,4 @@ class BaseSecrets(Secrets): CELERY: CelerySecrets = SecretsField.new(CelerySecrets) OIDC: OIDCSecrets = SecretsField.new(OIDCSecrets) + INITIAL_ACCOUNT: InitialAccountSecrets = SecretsField.new(InitialAccountSecrets) diff --git a/services/backend/hotpocket_backend/settings/aio.py b/services/backend/hotpocket_backend/settings/aio.py index 4ea7dc7..6974bed 100644 --- a/services/backend/hotpocket_backend/settings/aio.py +++ b/services/backend/hotpocket_backend/settings/aio.py @@ -17,6 +17,9 @@ MIDDLEWARE = [ 'whitenoise.middleware.WhiteNoiseMiddleware', ] +SESSION_COOKIE_SAMESITE = 'Lax' +SESSION_COOKIE_SECURE = False + STORAGES['staticfiles'] = { # noqa: F405 'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage', } diff --git a/services/backend/hotpocket_backend/settings/base.py b/services/backend/hotpocket_backend/settings/base.py index 60ffa9c..4dafd13 100644 --- a/services/backend/hotpocket_backend/settings/base.py +++ b/services/backend/hotpocket_backend/settings/base.py @@ -115,6 +115,8 @@ STATIC_URL = 'static/' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +LOGGING_LEVEL = os.environ.get('HOTPOCKET_BACKEND_LOGGING_LEVEL', 'INFO') + LOG_FORMAT = '%(asctime)s %(levelname)s [%(name)s] [%(request_id)s] (%(funcName)s:%(lineno)s) %(message)s' LOGGING = { 'version': 1, @@ -149,17 +151,17 @@ LOGGING = { 'loggers': { 'hotpocket_backend': { 'handlers': ['hotpocket'], - 'level': 'INFO', + 'level': LOGGING_LEVEL, 'propagate': False, }, 'hotpocket_common': { 'handlers': ['hotpocket'], - 'level': 'INFO', + 'level': LOGGING_LEVEL, 'propagate': False, }, 'hotpocket_soa': { 'handlers': ['hotpocket'], - 'level': 'INFO', + 'level': LOGGING_LEVEL, 'propagate': False, }, 'django': { diff --git a/services/backend/ops/bin/entrypoint-deployment.sh b/services/backend/ops/bin/entrypoint-deployment.sh index b4268c7..261020f 100755 --- a/services/backend/ops/bin/entrypoint-deployment.sh +++ b/services/backend/ops/bin/entrypoint-deployment.sh @@ -13,26 +13,21 @@ cat <"] license = "Apache-2.0" diff --git a/tasks.py b/tasks.py index 421f6af..1cd9f47 100644 --- a/tasks.py +++ b/tasks.py @@ -291,5 +291,5 @@ def bump_version(ctx: Context, f'inv bump-version {next_version} --build {build}', ) - if 'backend' in services_to_bump: + if service is None: tools_bump_version_task(ctx, next_version, build=build)