diff --git a/.github/actions/setup-helmfile/action.yaml b/.github/actions/setup-helmfile/action.yaml
new file mode 100644
index 0000000000..b988198198
--- /dev/null
+++ b/.github/actions/setup-helmfile/action.yaml
@@ -0,0 +1,13 @@
+name: 'Setup helmfile'
+description: 'Sets up helmfile in /usr/local/bin/helmfile'
+runs:
+ using: "composite"
+ steps:
+ - name: install helmfile
+ run: |
+ curl -L https://github.com/helmfile/helmfile/releases/download/v1.1.3/helmfile_1.1.3_linux_amd64.tar.gz -o helmfile.tar.gz
+ tar -xvf /tmp/helmfile.tar.gz
+ mv helmfile /usr/local/bin
+ chmod +x /usr/local/bin/helmfile
+ working-directory: /tmp
+ shell: bash
diff --git a/.github/workflows/deployment-devel.yml b/.github/workflows/deployment-devel.yml
index 4515c9236c..aa607346a6 100644
--- a/.github/workflows/deployment-devel.yml
+++ b/.github/workflows/deployment-devel.yml
@@ -5,6 +5,15 @@ on:
branches:
- devel
workflow_dispatch:
+ inputs:
+ action:
+ description: "Choose action"
+ type: choice
+ required: true
+ default: diff
+ options:
+ - diff
+ - deploy
jobs:
continuous-integration:
@@ -25,8 +34,9 @@ jobs:
upgrade-or-install-deployment:
name: Upgrade or install deployment
needs: build-and-push
- uses: ./.github/workflows/reusable-dev-deployment.yml
+ uses: ./.github/workflows/reusable-ecamp3-deployment.yml
with:
name: dev
env: dev
- secrets: inherit
\ No newline at end of file
+ action: ${{ github.event.inputs.action || 'deploy' }}
+ secrets: inherit
diff --git a/.github/workflows/deployment-pr.yml b/.github/workflows/deployment-pr.yml
index 0fd6e7169c..347a0593fe 100644
--- a/.github/workflows/deployment-pr.yml
+++ b/.github/workflows/deployment-pr.yml
@@ -3,6 +3,16 @@ name: CD for feature branches
on:
pull_request_target:
types: [opened, reopened, labeled, synchronize]
+ workflow_dispatch:
+ inputs:
+ action:
+ description: "Choose action"
+ type: choice
+ required: true
+ default: diff
+ options:
+ - diff
+ - deploy
concurrency:
group: ${{ github.workflow}}-${{ github.event.pull_request.number }}
@@ -23,11 +33,12 @@ jobs:
upgrade-or-install-deployment:
name: Upgrade or install deployment
needs: build-and-push
- uses: ./.github/workflows/reusable-dev-deployment.yml
+ uses: ./.github/workflows/reusable-ecamp3-deployment.yml
with:
name: pr${{ github.event.pull_request.number }}
sha: ${{ github.event.pull_request.head.sha }}
env: feature-branch
pr-number: ${{ github.event.pull_request.number }}
dropDBOnUninstall: true
+ action: ${{ github.event.inputs.action || 'deploy' }}
secrets: inherit
diff --git a/.github/workflows/deployment-stage-prod.yml b/.github/workflows/deployment-stage-prod.yml
index a3f07dce30..a3b528dfe8 100644
--- a/.github/workflows/deployment-stage-prod.yml
+++ b/.github/workflows/deployment-stage-prod.yml
@@ -9,6 +9,15 @@ on:
- staging
- prod
workflow_dispatch:
+ inputs:
+ action:
+ description: "Choose action"
+ type: choice
+ required: true
+ default: diff
+ options:
+ - diff
+ - deploy
jobs:
build-and-push:
@@ -24,5 +33,9 @@ jobs:
upgrade-or-install-deployment:
name: Upgrade or install deployment
needs: build-and-push
- uses: ./.github/workflows/reusable-stage-prod-deployment.yml
+ uses: ./.github/workflows/reusable-ecamp3-deployment.yml
+ with:
+ name: ${{ github.ref_name }}
+ env: ${{ github.ref_name }}
+ action: ${{ github.event.inputs.action || 'deploy' }}
secrets: inherit
diff --git a/.github/workflows/restore-backup-dev-pr.yml b/.github/workflows/restore-backup-dev-pr.yml
index 7857db686a..4d567de94f 100644
--- a/.github/workflows/restore-backup-dev-pr.yml
+++ b/.github/workflows/restore-backup-dev-pr.yml
@@ -21,6 +21,14 @@ on:
description: The environment, if name is dev then dev, else feature-branch
required: true
default: dev
+ action:
+ description: "Choose action"
+ type: choice
+ required: true
+ default: diff
+ options:
+ - diff
+ - deploy
jobs:
@@ -36,7 +44,7 @@ jobs:
upgrade-or-install-deployment:
name: Upgrade or install deployment
needs: build-and-push
- uses: ./.github/workflows/reusable-dev-deployment.yml
+ uses: ./.github/workflows/reusable-ecamp3-deployment.yml
with:
name: ${{ inputs.pr-number == null && 'dev' || format('pr{0}', inputs.pr-number) }}
sha: ${{ github.sha }}
@@ -44,4 +52,5 @@ jobs:
pr-number: ${{ inputs.pr-number }}
dropDBOnUninstall: ${{ inputs.pr-number != null }}
restoreSourceFile: ${{ inputs.restoreSourceFile }}
+ action: ${{ github.event.inputs.action }}
secrets: inherit
diff --git a/.github/workflows/restore-backup-stage-prod.yml b/.github/workflows/restore-backup-stage-prod.yml
index 4e70906b39..f628224145 100644
--- a/.github/workflows/restore-backup-stage-prod.yml
+++ b/.github/workflows/restore-backup-stage-prod.yml
@@ -18,6 +18,14 @@ on:
and restore the database with the given backup file?
Repeat the branch name to confirm. (e.g. staging or prod)
required: true
+ action:
+ description: "Choose action"
+ type: choice
+ required: true
+ default: diff
+ options:
+ - diff
+ - deploy
jobs:
check-parameters:
@@ -46,7 +54,9 @@ jobs:
upgrade-or-install-deployment:
name: Upgrade or install deployment
needs: build-and-push
- uses: ./.github/workflows/reusable-stage-prod-deployment.yml
+ uses: ./.github/workflows/reusable-ecamp3-deployment.yml
with:
restoreSourceFile: ${{ inputs.restoreSourceFile }}
+ env: ${{ github.ref_name }}
+ action: ${{ github.event.inputs.action }}
secrets: inherit
diff --git a/.github/workflows/reusable-dev-deployment.yml b/.github/workflows/reusable-dev-deployment.yml
deleted file mode 100644
index 21a3206eb0..0000000000
--- a/.github/workflows/reusable-dev-deployment.yml
+++ /dev/null
@@ -1,190 +0,0 @@
-name: '[reusable only] Development deployment'
-
-on:
- workflow_call:
- inputs:
- name:
- required: true
- type: string
- sha:
- required: false
- type: string
- default: ${{ github.sha }}
- env:
- required: true
- type: string
- pr-number:
- required: false
- type: string
- dropDBOnUninstall:
- required: false
- type: boolean
- default: false
- restoreSourceFile:
- required: false
- type: string
-
-jobs:
- dev-deployment:
- name: Deploy to Kubernetes
- runs-on: ubuntu-latest
- environment:
- name: ${{ inputs.env }}
- steps:
- - name: Get link to currently running job logs
- uses: Tiryoh/gha-jobid-action@v1.4.0
- id: job-url
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- job_name: Upgrade or install deployment / Deploy to Kubernetes
-
- - name: Create a pending GitHub deployment
- uses: bobheadxi/deployments@v1.5.0
- id: deployment
- with:
- step: start
- token: ${{ secrets.REPO_ACCESS_TOKEN }}
- env: ${{ inputs.name }}
-
- - name: Comment progress on PR
- uses: thollander/actions-comment-pull-request@v3
- if: ${{ inputs.env == 'feature-branch' }}
- with:
- pr-number: ${{ inputs.pr-number }}
- message: |
- ### 🏗️ Feature branch deployment in progress
-
-
- | Name | Link |
- |---------------------------------|------------------------|
- |🔨 Latest commit | [${{ inputs.sha }}](https://github.com/${{ github.repository }}/commit/${{ inputs.sha }}) |
- |🔍 Latest deploy log | [${{ steps.job-url.outputs.html_url }}](${{ steps.job-url.outputs.html_url }}) |
- comment-tag: feature-branch-deployment-status
-
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- with:
- ref: ${{ inputs.sha }}
-
- - name: Upgrade or install helm release
- env:
- never_uninstall: ${{ needs.find-prs-to-deploy.outputs.never-uninstall }}
- run: |
- # Setup authentication
- mkdir ~/.kube && echo '${{ secrets.KUBECONFIG }}' > ~/.kube/config && chmod go-r ~/.kube/config
- # Switch to the helm chart directory
- cd .helm/ecamp3
- # Install dependency charts
- helm dependency update
- # Set the appVersion, workaround from https://github.com/helm/helm/issues/8194 so that we can
- # later find out which deployments need to be upgraded
- sed -i 's/^appVersion:.*$/appVersion: "${{ inputs.sha }}"/' Chart.yaml
- # Install or upgrade the release
- helm upgrade --install ecamp3-${{ inputs.name }} . \
- --set imageTag=${{ inputs.sha }} \
- --set frontend.image.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-frontend' \
- --set print.image.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-print' \
- --set api.image.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-api' \
- --set apiCache.image.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-varnish' \
- --set postgresql.dbBackupRestoreImage.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-db-backup-restore' \
- --set termsOfServiceLinkTemplate='https://ecamp3.ch/{lang}/tos' \
- --set newsLink='https://ecamp3.ch/blog' \
- --set helpLink='https://ecamp3.ch/faq' \
- --set domain=${{ inputs.name }}.${{ vars.DOMAIN }} \
- --set ingress.basicAuth.enabled=${{ vars.BASIC_AUTH_ENABLED || false }} \
- --set ingress.basicAuth.username=${{ secrets.BASIC_AUTH_USERNAME }} \
- --set ingress.basicAuth.password='${{ secrets.BASIC_AUTH_PASSWORD }}' \
- --set apiCache.enabled=${{ vars.API_CACHE_ENABLED || false }} \
- --set apiCache.sendXKeyHeadersDownstream=${{ vars.SEND_XKEY_HEADERS_DOWNSTREAM != null && format('''{0}''', vars.SEND_XKEY_HEADERS_DOWNSTREAM) || null }} \
- --set mail.dummyEnabled=true \
- --set postgresql.url='${{ secrets.POSTGRES_URL }}/ecamp3${{ inputs.name }}?sslmode=require' \
- --set postgresql.adminUrl='${{ secrets.POSTGRES_ADMIN_URL }}/ecamp3${{ inputs.name }}?sslmode=require' \
- --set postgresql.dropDBOnUninstall=${{ inputs.dropDBOnUninstall }} \
- --set postgresql.backup.schedule=${{ vars.BACKUP_SCHEDULE != null && format('''{0}''', vars.BACKUP_SCHEDULE) || null }} \
- --set postgresql.backup.s3.endpoint='${{ vars.BACKUP_S3_ENDPOINT }}' \
- --set postgresql.backup.s3.bucket='${{ vars.BACKUP_S3_BUCKET }}' \
- --set postgresql.backup.s3.accessKeyId='${{ secrets.BACKUP_S3_ACCESS_KEY_ID }}' \
- --set postgresql.backup.s3.accessKey='${{ secrets.BACKUP_S3_ACCESS_KEY }}' \
- --set postgresql.backup.encryptionKey=${{ secrets.BACKUP_ENCRYPTION_KEY != null && format('''{0}''', secrets.BACKUP_ENCRYPTION_KEY) || null }} \
- --set postgresql.restore.sourceFile=${{ inputs.restoreSourceFile != null && format('''{0}''', inputs.restoreSourceFile) || null }} \
- --set postgresql.restore.sourceAppName=${{ vars.RESTORE_SOURCE_APP != null && format('''{0}''', vars.RESTORE_SOURCE_APP) || null }} \
- --set postgresql.restore.s3.endpoint='${{ vars.RESTORE_S3_ENDPOINT }}' \
- --set postgresql.restore.s3.bucket='${{ vars.RESTORE_S3_BUCKET }}' \
- --set postgresql.restore.s3.accessKeyId='${{ secrets.RESTORE_S3_ACCESS_KEY_ID }}' \
- --set postgresql.restore.s3.accessKey='${{ secrets.RESTORE_S3_ACCESS_KEY }}' \
- --set postgresql.restore.encryptionKey=${{ secrets.RESTORE_ENCRYPTION_KEY != null && format('''{0}''', secrets.RESTORE_ENCRYPTION_KEY) || null }} \
- --set api.dataMigrationsDir='${{ vars.DATA_MIGRATIONS_DIR }}' \
- --set api.appSecret='${{ secrets.API_APP_SECRET }}' \
- --set api.sentryDsn='${{ secrets.API_SENTRY_DSN }}' \
- --set api.jwt.passphrase='${{ secrets.JWT_PASSPHRASE }}' \
- --set api.jwt.publicKey='${{ secrets.JWT_PUBLIC_KEY }}' \
- --set api.jwt.privateKey='${{ secrets.JWT_PRIVATE_KEY }}' \
- --set frontend.sentryDsn='${{ secrets.FRONTEND_SENTRY_DSN }}' \
- --set print.sentryDsn='${{ secrets.PRINT_SENTRY_DSN }}' \
- --set print.browserWsEndpoint='${{ secrets.BROWSER_WS_ENDPOINT }}' \
- --set print.ingress.readTimeoutSeconds='${{ vars.PRINT_INGRESS_READ_TIMEOUT_SECONDS }}' \
- --set print.renderHTMLTimeoutMs='${{ vars.PRINT_RENDER_HTML_TIMEOUT_MS }}' \
- --set print.renderPDFTimeoutMs='${{ vars.PRINT_RENDER_PDF_TIMEOUT_MS }}' \
- --set browserless.connectionTimeout=${{ vars.BROWSERLESS_CONNECTION_TIMEOUT_MS || '30000' }} \
- --set deploymentTime="$(date -u +%s)" \
- --set deployedVersion="$(git rev-parse --short '${{ inputs.sha }}')" \
- --set recaptcha.siteKey='${{ secrets.RECAPTCHA_SITE_KEY }}' \
- --set recaptcha.secret='${{ secrets.RECAPTCHA_SECRET }}' \
- --set frontend.loginInfoTextKey=${{ vars.LOGIN_INFO_TEXT_KEY }} \
- --set featureToggle.developer=true \
- --set featureToggle.checklist=${{ vars.FEATURE_CHECKLIST || 'false' }} \
- --timeout ${{ vars.HELM_TIMEOUT || '5m0s' }}
-
- - name: Finish the GitHub deployment
- uses: bobheadxi/deployments@v1.5.0
- if: always()
- with:
- step: finish
- token: ${{ secrets.REPO_ACCESS_TOKEN }}
- status: ${{ job.status }}
- deployment_id: ${{ steps.deployment.outputs.deployment_id }}
- env_url: https://${{ inputs.name }}.${{ vars.DOMAIN }}
- env: ${{ steps.deployment.outputs.env }}
-
- - name: Get current time
- uses: josStorer/get-current-time@v2
- if: always()
- id: current-time
- with:
- timezone: Europe/Zurich
-
- - name: Comment success on PR
- uses: thollander/actions-comment-pull-request@v3
- if: ${{ success() && inputs.env == 'feature-branch' }}
- with:
- pr-number: ${{ inputs.pr-number }}
- message: |
- ### ✅ Feature branch deployment ready!
-
-
- | Name | Link |
- |---------------------------------|------------------------|
- |😎 **Deployment** | [https://${{ inputs.name }}.${{ vars.DOMAIN }}/](https://${{ inputs.name }}.${{ vars.DOMAIN }}/) |
- |🔑 Login | `test@example.com` / `test` |
- |🕒 Last deployed at | ${{ steps.current-time.outputs.readableTime }} |
- |🔨 Latest commit | [${{ inputs.sha }}](https://github.com/${{ github.repository }}/commit/${{ inputs.sha }}) |
- |🔍 Latest deploy log | [${{ steps.job-url.outputs.html_url }}](${{ steps.job-url.outputs.html_url }}) |
- |📱 Preview on mobile | Toggle QR Code...

_Use your smartphone camera to open QR code link._ |
- ---
- comment-tag: feature-branch-deployment-status
-
- - name: Comment failure on PR
- uses: thollander/actions-comment-pull-request@v3
- if: ${{ failure() && inputs.env == 'feature-branch' }}
- with:
- pr-number: ${{ inputs.pr-number }}
- message: |
- ### 💥 Feature branch deployment failed
-
-
- | Name | Link |
- |---------------------------------|------------------------|
- |🕒 Last attempted at | ${{ steps.current-time.outputs.readableTime }} |
- |🔨 Latest commit | [${{ inputs.sha }}](https://github.com/${{ github.repository }}/commit/${{ inputs.sha }}) |
- |🔍 Latest deploy log | [${{ steps.job-url.outputs.html_url }}](${{ steps.job-url.outputs.html_url }}) |
- ---
- comment-tag: feature-branch-deployment-status
diff --git a/.github/workflows/reusable-ecamp3-deployment.yml b/.github/workflows/reusable-ecamp3-deployment.yml
new file mode 100644
index 0000000000..de641e7337
--- /dev/null
+++ b/.github/workflows/reusable-ecamp3-deployment.yml
@@ -0,0 +1,184 @@
+name: '[reusable only] ecamp3 deployment'
+
+on:
+ workflow_call:
+ inputs:
+ name:
+ description: Deployment short name for subdomain (e.g. dev, pr123).
+ required: false
+ type: string
+ sha:
+ required: false
+ type: string
+ default: ${{ github.sha }}
+ env:
+ description: Target environment (dev, feature-branch, staging, prod)
+ required: true
+ type: string
+ pr-number:
+ required: false
+ type: string
+ dropDBOnUninstall:
+ required: false
+ type: boolean
+ default: false
+ restoreSourceFile:
+ required: false
+ type: string
+ action:
+ description: "Choose action of (diff|deploy)"
+ type: string
+ required: true
+ default: diff
+
+env:
+ NAME: ${{ inputs.name }}
+ IMAGE_TAG: ${{ inputs.sha }}
+ HELM_TIMEOUT: ${{ vars.HELM_TIMEOUT }}
+
+jobs:
+ deploy-ecamp3:
+ name: Deploy to Kubernetes
+ runs-on: ubuntu-latest
+ environment:
+ name: ${{ inputs.env }}
+ steps:
+ - name: Get link to currently running job logs
+ if: ${{ inputs.env == 'feature-branch' }}
+ uses: Tiryoh/gha-jobid-action@v1.4.0
+ id: job-url
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ job_name: Upgrade or install deployment / Deploy to Kubernetes
+
+ - name: Create a pending GitHub deployment
+ uses: bobheadxi/deployments@v1.5.0
+ id: deployment
+ with:
+ step: start
+ token: ${{ secrets.REPO_ACCESS_TOKEN }}
+ env: ${{ inputs.env }}
+
+ - name: Comment progress on PR
+ uses: thollander/actions-comment-pull-request@v3
+ if: ${{ inputs.env == 'feature-branch' }}
+ with:
+ pr-number: ${{ inputs.pr-number }}
+ message: |
+ ### 🏗️ Feature branch deployment in progress
+
+
+ | Name | Link |
+ |---------------------------------|------------------------|
+ |🔨 Latest commit | [${{ inputs.sha }}](https://github.com/${{ github.repository }}/commit/${{ inputs.sha }}) |
+ |🔍 Latest deploy log | [${{ steps.job-url.outputs.html_url }}](${{ steps.job-url.outputs.html_url }}) |
+ comment-tag: feature-branch-deployment-status
+
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
+ with:
+ ref: ${{ inputs.sha }}
+
+ - name: Dump secrets to /tmp/secrets.json
+ run: |
+ cat << 'EOF' | tee /tmp/secrets.json
+ ${{ toJSON(secrets) }}
+ EOF
+ jq '.' /tmp/secrets.json
+
+ - name: Dump variables to /tmp/vars.json
+ run: |
+ cat << 'EOF' | tee /tmp/vars.json
+ ${{ toJSON(vars) }}
+ EOF
+ jq '.' /tmp/vars.json
+
+ - name: Merge secrets, variables, and workflow inputs to env.yaml
+ run: |
+ jq -n \
+ '{
+ NAME: "${{ inputs.name }}",
+ IMAGE_TAG: "${{ inputs.sha }}",
+ RESTORE_SOURCE_FILE: "${{ inputs.restoreSourceFile || '' }}",
+ DROP_DB_ON_UNINSTALL: ${{ inputs.dropDBOnUninstall }}
+ }' > /tmp/additions.json
+ jq -s '.[0] + .[1] + .[2]' /tmp/secrets.json /tmp/vars.json /tmp/additions.json > .helm/ecamp3/env.yaml
+ jq '.' .helm/ecamp3/env.yaml
+
+ - name: Setup kubectl authentication
+ run: |
+ mkdir -p ~/.kube
+ echo '${{ secrets.KUBECONFIG }}' > ~/.kube/config
+ chmod go-r ~/.kube/config
+
+ - uses: ./.github/actions/setup-helmfile
+
+ - name: Diff deployment
+ run: |
+ ./.helm/ecamp3/deploy.sh diff || true
+
+ - name: Show values.yaml
+ run: cat ./.helm/ecamp3/values.yaml
+
+ - name: Deploy
+ if: ${{ inputs.action == 'deploy' }}
+ run: |
+ ./.helm/ecamp3/deploy.sh deploy
+
+ - name: Finish the GitHub deployment
+ uses: bobheadxi/deployments@v1.5.0
+ if: always()
+ with:
+ step: finish
+ token: ${{ secrets.REPO_ACCESS_TOKEN }}
+ status: ${{ job.status }}
+ deployment_id: ${{ steps.deployment.outputs.deployment_id }}
+ env_url: ${{ (inputs.env == 'staging' || inputs.env == 'prod') && format('https://{0}.{1}', vars.SUBDOMAIN, vars.DOMAIN) || format('https://{0}.{1}', inputs.name, vars.DOMAIN) }}
+ env: ${{ steps.deployment.outputs.env }}
+
+ - name: Get current time
+ uses: josStorer/get-current-time@v2
+ if: always()
+ id: current-time
+ with:
+ timezone: Europe/Zurich
+
+ - name: Comment success on PR
+ uses: thollander/actions-comment-pull-request@v3
+ if: ${{ success() && inputs.env == 'feature-branch' }}
+ with:
+ pr-number: ${{ inputs.pr-number }}
+ message: |
+ ### ✅ Feature branch deployment ready!
+
+
+ | Name | Link |
+ |---------------------------------|------------------------|
+ |😎 **Deployment** | [https://${{ inputs.name }}.${{ vars.DOMAIN }}/](https://${{ inputs.name }}.${{ vars.DOMAIN }}/) |
+ |🔑 Login | `test@example.com` / `test` |
+ |🕒 Last deployed at | ${{ steps.current-time.outputs.readableTime }} |
+ |🔨 Latest commit | [${{ inputs.sha }}](https://github.com/${{ github.repository }}/commit/${{ inputs.sha }}) |
+ |🔍 Latest deploy log | [${{ steps.job-url.outputs.html_url }}](${{ steps.job-url.outputs.html_url }}) |
+ |📱 Preview on mobile | Toggle QR Code...

_Use your smartphone camera to open QR code link._ |
+ ---
+ comment-tag: feature-branch-deployment-status
+
+ - name: Comment failure on PR
+ uses: thollander/actions-comment-pull-request@v3
+ if: ${{ failure() && inputs.env == 'feature-branch' }}
+ with:
+ pr-number: ${{ inputs.pr-number }}
+ message: |
+ ### 💥 Feature branch deployment failed
+
+
+ | Name | Link |
+ |---------------------------------|------------------------|
+ |🕒 Last attempted at | ${{ steps.current-time.outputs.readableTime }} |
+ |🔨 Latest commit | [${{ inputs.sha }}](https://github.com/${{ github.repository }}/commit/${{ inputs.sha }}) |
+ |🔍 Latest deploy log | [${{ steps.job-url.outputs.html_url }}](${{ steps.job-url.outputs.html_url }}) |
+ ---
+ comment-tag: feature-branch-deployment-status
+
+ - name: Cleanup temp files
+ if: always()
+ run: rm -f /tmp/secrets.json /tmp/vars.json .helm/ecamp3/env.yaml
diff --git a/.github/workflows/reusable-stage-prod-deployment.yml b/.github/workflows/reusable-stage-prod-deployment.yml
deleted file mode 100644
index 0cbc4a39cf..0000000000
--- a/.github/workflows/reusable-stage-prod-deployment.yml
+++ /dev/null
@@ -1,126 +0,0 @@
-name: '[reusable only] Staging and Prod deployment '
-
-on:
- workflow_call:
- inputs:
- restoreSourceFile:
- type: string
- required: false
-
-jobs:
- upgrade-or-install-deployment:
- name: Upgrade or install deployment
- runs-on: ubuntu-latest
- environment: ${{ github.ref_name }}
- env:
- environment: ${{ github.ref_name }}
- domain: ${{ vars.SUBDOMAIN }}.${{ vars.DOMAIN }}
- steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
-
- - name: Create a pending GitHub deployment
- uses: bobheadxi/deployments@v1.5.0
- id: deployment
- with:
- step: start
- token: ${{ secrets.REPO_ACCESS_TOKEN }}
- env: ${{ env.environment }}
-
- - name: Upgrade or install helm release
- run: |
- # Setup authentication
- mkdir ~/.kube && echo '${{ secrets.KUBECONFIG }}' > ~/.kube/config && chmod go-r ~/.kube/config
- # Switch to the helm chart directory
- cd .helm/ecamp3
- # Install dependency charts
- helm dependency update
- # Set the appVersion, workaround from https://github.com/helm/helm/issues/8194 so that we can
- # later find out which deployments need to be upgraded
- sed -i 's/^appVersion:.*$/appVersion: "${{ github.sha }}"/' Chart.yaml
- # Install or upgrade the release
- helm upgrade --install ecamp3-${{ env.environment }} . \
- --set imageTag=${{ github.sha }} \
- --set frontend.image.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-frontend' \
- --set print.image.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-print' \
- --set api.image.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-api' \
- --set apiCache.image.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-varnish' \
- --set postgresql.dbBackupRestoreImage.repository='docker.io/${{ vars.DOCKER_HUB_USERNAME }}/ecamp3-db-backup-restore' \
- --set termsOfServiceLinkTemplate='https://ecamp3.ch/{lang}/tos' \
- --set newsLink='https://ecamp3.ch/blog' \
- --set helpLink='https://ecamp3.ch/faq' \
- --set domain=${{ env.domain }} \
- --set ingress.basicAuth.enabled=${{ vars.BASIC_AUTH_ENABLED || false }} \
- --set ingress.basicAuth.username=${{ secrets.BASIC_AUTH_USERNAME }} \
- --set ingress.basicAuth.password='${{ secrets.BASIC_AUTH_PASSWORD }}' \
- --set apiCache.enabled=${{ vars.API_CACHE_ENABLED || false }} \
- --set mail.dsn=${{ secrets.MAILER_DSN }} \
- --set postgresql.url='${{ secrets.POSTGRES_URL }}/${{ secrets.DB_NAME }}?sslmode=require' \
- --set postgresql.dropDBOnUninstall=false \
- --set postgresql.backup.schedule=${{ vars.BACKUP_SCHEDULE != null && format('''{0}''', vars.BACKUP_SCHEDULE) || null }} \
- --set postgresql.backup.s3.endpoint='${{ vars.BACKUP_S3_ENDPOINT }}' \
- --set postgresql.backup.s3.bucket='${{ vars.BACKUP_S3_BUCKET }}' \
- --set postgresql.backup.s3.accessKeyId='${{ secrets.BACKUP_S3_ACCESS_KEY_ID }}' \
- --set postgresql.backup.s3.accessKey='${{ secrets.BACKUP_S3_ACCESS_KEY }}' \
- --set postgresql.backup.encryptionKey=${{ secrets.BACKUP_ENCRYPTION_KEY != null && format('''{0}''', secrets.BACKUP_ENCRYPTION_KEY) || null }} \
- --set postgresql.restore.sourceFile=${{ inputs.restoreSourceFile != null && format('''{0}''', inputs.restoreSourceFile) || null }} \
- --set postgresql.restore.sourceAppName=${{ vars.RESTORE_SOURCE_APP != null && format('''{0}''', vars.RESTORE_SOURCE_APP) || null }} \
- --set postgresql.restore.s3.endpoint='${{ vars.RESTORE_S3_ENDPOINT }}' \
- --set postgresql.restore.s3.bucket='${{ vars.RESTORE_S3_BUCKET }}' \
- --set postgresql.restore.s3.accessKeyId='${{ secrets.RESTORE_S3_ACCESS_KEY_ID }}' \
- --set postgresql.restore.s3.accessKey='${{ secrets.RESTORE_S3_ACCESS_KEY }}' \
- --set postgresql.restore.encryptionKey=${{ secrets.RESTORE_ENCRYPTION_KEY != null && format('''{0}''', secrets.RESTORE_ENCRYPTION_KEY) || null }} \
- --set postgresql.restore.inviteSupportAccountToInterestingCamps=${{ vars.RESTORE_INVITE_TO_INTERESTING_CAMPS != null && format('''{0}''', vars.RESTORE_INVITE_TO_INTERESTING_CAMPS) || false }} \
- --set api.dataMigrationsDir='${{ vars.DATA_MIGRATIONS_DIR }}' \
- --set api.appSecret='${{ secrets.API_APP_SECRET }}' \
- --set api.sentryDsn='${{ secrets.API_SENTRY_DSN }}' \
- --set api.jwt.passphrase='${{ secrets.JWT_PASSPHRASE }}' \
- --set api.jwt.publicKey='${{ secrets.JWT_PUBLIC_KEY }}' \
- --set api.jwt.privateKey='${{ secrets.JWT_PRIVATE_KEY }}' \
- --set api.oauth.google.clientId='${{ secrets.OAUTH_GOOGLE_CLIENT_ID }}' \
- --set api.oauth.google.clientSecret='${{ secrets.OAUTH_GOOGLE_CLIENT_SECRET }}' \
- --set api.oauth.pbsmidata.clientId='${{ secrets.OAUTH_PBSMIDATA_CLIENT_ID }}' \
- --set api.oauth.pbsmidata.clientSecret='${{ secrets.OAUTH_PBSMIDATA_CLIENT_SECRET }}' \
- --set api.oauth.pbsmidata.baseUrl='${{ secrets.OAUTH_PBSMIDATA_BASE_URL }}' \
- --set api.oauth.cevidb.clientId='${{ secrets.OAUTH_CEVIDB_CLIENT_ID }}' \
- --set api.oauth.cevidb.clientSecret='${{ secrets.OAUTH_CEVIDB_CLIENT_SECRET }}' \
- --set api.oauth.cevidb.baseUrl='${{ secrets.OAUTH_CEVIDB_BASE_URL }}' \
- --set api.oauth.jubladb.clientId='${{ secrets.OAUTH_JUBLADB_CLIENT_ID }}' \
- --set api.oauth.jubladb.clientSecret='${{ secrets.OAUTH_JUBLADB_CLIENT_SECRET }}' \
- --set api.oauth.jubladb.baseUrl='${{ secrets.OAUTH_JUBLADB_BASE_URL }}' \
- --set frontend.sentryDsn='${{ secrets.FRONTEND_SENTRY_DSN }}' \
- --set print.sentryDsn='${{ secrets.PRINT_SENTRY_DSN }}' \
- --set print.ingress.readTimeoutSeconds='${{ vars.PRINT_INGRESS_READ_TIMEOUT_SECONDS }}' \
- --set print.renderHTMLTimeoutMs='${{ vars.PRINT_RENDER_HTML_TIMEOUT_MS }}' \
- --set print.renderPDFTimeoutMs='${{ vars.PRINT_RENDER_PDF_TIMEOUT_MS }}' \
- --set deploymentTime="$(date -u +%s)" \
- --set deployedVersion="$(git rev-parse --short HEAD)" \
- --set recaptcha.siteKey='${{ secrets.RECAPTCHA_SITE_KEY }}' \
- --set recaptcha.secret='${{ secrets.RECAPTCHA_SECRET }}' \
- --set frontend.loginInfoTextKey=${{ vars.LOGIN_INFO_TEXT_KEY }} \
- --set browserless.maxConcurrentSessions=${{ vars.BROWSERLESS_MAXCONCURRENTSESSIONS || 3 }} \
- --set browserless.maxQueueLength=${{ vars.BROWSERLESS_MAXQUEUELENGTH || 9 }} \
- --set browserless.connectionTimeout=${{ vars.BROWSERLESS_CONNECTION_TIMEOUT_MS || '30000' }} \
- --set browserless.resources.requests.cpu=${{ vars.BROWSERLESS_CPU || '500m' }} \
- --set browserless.resources.requests.memory=${{ vars.BROWSERLESS_MEMORY || '800Mi' }} \
- --set api.num_threads=${{ vars.API_NUM_THREADS != null && vars.API_NUM_THREADS || null }} \
- --set api.resources.requests.cpu=${{ vars.PHP_CPU || '1000m' }} \
- --set api.resources.requests.memory=${{ vars.PHP_MEMORY || '500Mi' }} \
- --set api.resources.limits.cpu=${{ vars.PHP_CPULIMIT || '1900m' }} \
- --set frontend.resources.requests.cpu=50m \
- --set print.resources.requests.cpu=${{ vars.PRINT_CPU || '300m' }} \
- --set print.resources.requests.memory=${{ vars.PRINT_MEMORY || '150Mi' }} \
- --set autoscaling.enabled=true \
- --set autoscaling.targetCPUUtilizationPercentage=90 \
- --set featureToggle.checklist=${{ vars.FEATURE_CHECKLIST || 'false' }} \
- --timeout ${{ vars.HELM_TIMEOUT || '5m0s' }}
-
- - name: Finish the GitHub deployment
- uses: bobheadxi/deployments@v1.5.0
- if: always()
- with:
- step: finish
- token: ${{ secrets.REPO_ACCESS_TOKEN }}
- status: ${{ job.status }}
- deployment_id: ${{ steps.deployment.outputs.deployment_id }}
- env_url: https://${{ env.domain }}
- env: ${{ steps.deployment.outputs.env }}
diff --git a/.helm/.env-example b/.helm/.env-example
deleted file mode 100644
index 40d36b3e65..0000000000
--- a/.helm/.env-example
+++ /dev/null
@@ -1,36 +0,0 @@
-docker_hub_account=
-version=$(git rev-parse HEAD)
-instance_name=${USER}
-domain=ecamp3.ch
-POSTGRES_URL=
-POSTGRES_ADMIN_URL=
-
-API_CACHE_ENABLED=false
-#API_NUM_THREADS=16
-
-BASIC_AUTH_ENABLED=false
-BASIC_AUTH_USERNAME=test
-BASIC_AUTH_PASSWORD=test
-
-API_SENTRY_DSN=
-FRONTEND_SENTRY_DSN=
-PRINT_SENTRY_DSN=
-
-SENTRY_AUTH_TOKEN=
-SENTRY_RELEASE_NAME=$version
-
-BACKUP_SCHEDULE=@hourly
-BACKUP_S3_ENDPOINT=
-BACKUP_S3_BUCKET=
-BACKUP_S3_ACCESS_KEY_ID=
-BACKUP_S3_ACCESS_KEY=
-BACKUP_ENCRYPTION_KEY=test
-
-#RESTORE_SOURCE_FILE=latest
-RESTORE_SOURCE_APP=${instance_name}-1
-RESTORE_S3_ENDPOINT=
-RESTORE_S3_BUCKET=
-RESTORE_S3_ACCESS_KEY_ID=
-RESTORE_S3_ACCESS_KEY=
-RESTORE_ENCRYPTION_KEY=test
-RESTORE_INVITE_TO_INTERESTING_CAMPS=false
diff --git a/.helm/README.md b/.helm/README.md
new file mode 100644
index 0000000000..9c23965ade
--- /dev/null
+++ b/.helm/README.md
@@ -0,0 +1,55 @@
+# Helm infrastructure for ecamp3
+
+Here you also have some scripts to deploy ecamp3 from your local machine.
+
+## Prepare
+
+First you need to have the following dependencies:
+
+- jq
+- kubectl (with a kubeconfig for the cluster you want to deploy to)
+- helm
+- helmfile
+- docker (with a public repository to push images to)
+- openssl
+
+## Setup
+
+If you don't have JWT Passphrase, public and private key yet, you have to run:
+
+```shell
+./generate-jwt-values.sh
+```
+This copies [env.example.yaml](ecamp3/env.example.yaml) to [env.yaml](ecamp3/env.yaml)
+if not exists and sets the jwt values.
+
+Then you have to set the values in [env.yaml](ecamp3/env.yaml which are not set to any value.
+(e.g. POSTGRES_URL).
+
+## Build images
+
+```shell
+./build-images.sh
+```
+
+## Deploy to cluster
+
+To diff the deployment
+
+```shell
+./deploy-to-cluster.sh
+```
+
+To deploy
+
+```shell
+./deploy-to-cluster.sh deploy
+```
+
+## For convenience
+
+If you did not build the images for a long time, you have the convenience script:
+
+```shell
+./build-and-deploy.sh
+```
diff --git a/.helm/build-images.sh b/.helm/build-images.sh
index acad4b3fcc..67209d4cd0 100755
--- a/.helm/build-images.sh
+++ b/.helm/build-images.sh
@@ -1,20 +1,13 @@
-#!/bin/bash
+#!/bin/sh
set -e
SCRIPT_DIR=$(realpath "$(dirname "$0")")
REPO_DIR=$(realpath "$SCRIPT_DIR"/..)
-source "$SCRIPT_DIR"/.env
-
-if [ -z "$docker_hub_account" ] \
- || [ -z "$version" ] \
- ; then
- echo "Please specify the needed env variables in $SCRIPT_DIR/.env"
- echo "An example can be seen here:"
- cat $SCRIPT_DIR/.env-example
- exit 1
-fi
+version=$(jq -r '.IMAGE_TAG' $SCRIPT_DIR/ecamp3/env.yaml)
+docker_hub_account=$(jq -r '.DOCKER_HUB_USERNAME' $SCRIPT_DIR/ecamp3/env.yaml)
+SENTRY_AUTH_TOKEN=$(jq -r '.SENTRY_AUTH_TOKEN' $SCRIPT_DIR/ecamp3/env.yaml)
sentry_build_args="--build-arg SENTRY_AUTH_TOKEN=$SENTRY_AUTH_TOKEN --build-arg SENTRY_ORG=$SENTRY_ORG"
sentry_build_args="$sentry_build_args --build-arg SENTRY_RELEASE_NAME=$SENTRY_RELEASE_NAME"
diff --git a/.helm/deploy-to-cluster.sh b/.helm/deploy-to-cluster.sh
index 5ffc3dece4..2ee1784750 100755
--- a/.helm/deploy-to-cluster.sh
+++ b/.helm/deploy-to-cluster.sh
@@ -1,111 +1,10 @@
-#!/bin/bash
-
-set -e
+#!/bin/sh
SCRIPT_DIR=$(realpath "$(dirname "$0")")
REPO_DIR=$(realpath "$SCRIPT_DIR"/..)
-source "$SCRIPT_DIR"/.env
-
-if [ -z "$version" ] \
- || [ -z "$instance_name" ] \
- || [ -z "$POSTGRES_URL" ] \
- || [ -z "$POSTGRES_ADMIN_URL" ] \
- ; then
- echo "Please specify the needed env variables in $SCRIPT_DIR/.env"
- echo "An example can be seen here:"
- cat $SCRIPT_DIR/.env-example
- exit 1
-fi
-
-pull_policy="Always"
-domain="${domain:-ecamp3.ch}"
-values="--set imageTag=${version}"
-migrations_dir="dev-data"
-
-#generated values
-app_secret=$(uuidgen)
-app_jwt_passphrase=$(uuidgen)
-app_jwt_public_key=$(echo -n "$app_jwt_passphrase" | openssl genpkey -out "$SCRIPT_DIR"/private.pem -pass stdin -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096)
-app_jwt_private_key=$(echo -n "$app_jwt_passphrase" | openssl pkey -in "$SCRIPT_DIR"/private.pem -passin stdin -out "$SCRIPT_DIR"/public.pem -pubout)
-
-#secrets POSTGRES_URL, POSTGRES_ADMIN_URL and sentry dsn are in .env
-
-#use 1 2 instead of "1" to deploy multiple deployments
-for i in 1; do
- values="$values --set imageTag=${version}"
- values="$values --set termsOfServiceLinkTemplate=https://ecamp3.ch/{lang}/tos"
- values="$values --set newsLink=https://ecamp3.ch/blog"
- values="$values --set helpLink=https://ecamp3.ch/faq"
- values="$values --set domain=$instance_name-"$i".$domain"
- values="$values --set mail.dummyEnabled=true"
- values="$values --set ingress.basicAuth.enabled=$BASIC_AUTH_ENABLED"
- values="$values --set ingress.basicAuth.username=$BASIC_AUTH_USERNAME"
- values="$values --set ingress.basicAuth.password=$BASIC_AUTH_PASSWORD"
- values="$values --set apiCache.enabled=$API_CACHE_ENABLED"
- values="$values --set postgresql.enabled=false"
- values="$values --set postgresql.url=$POSTGRES_URL/ecamp3$instance_name-"$i"?sslmode=require"
- values="$values --set postgresql.adminUrl=$POSTGRES_ADMIN_URL/ecamp3$instance_name-"$i"?sslmode=require"
- values="$values --set postgresql.dropDBOnUninstall=true"
- values="$values --set api.dataMigrationsDir=$migrations_dir"
- values="$values --set api.appSecret=$app_secret"
- if [ -n "$API_SENTRY_DSN" ]; then
- values="$values --set api.sentryDsn=$API_SENTRY_DSN"
- fi
- if [ -n "$API_NUM_THREADS" ]; then
- values="$values --set api.num_threads=$API_NUM_THREADS"
- fi
- if [ -n "$FRONTEND_SENTRY_DSN" ]; then
- values="$values --set frontend.sentryDsn=$FRONTEND_SENTRY_DSN"
- fi
- if [ -n "$PRINT_SENTRY_DSN" ]; then
- values="$values --set print.sentryDsn=$PRINT_SENTRY_DSN"
- fi
- values="$values --set api.jwt.passphrase=$app_jwt_passphrase"
- values="$values --set-file api.jwt.publicKey=$SCRIPT_DIR/public.pem"
- values="$values --set-file api.jwt.privateKey=$SCRIPT_DIR/private.pem"
- values="$values --set deploymentTime=$(date -u +%s)"
- values="$values --set deployedVersion=\"$(git rev-parse --short HEAD)\""
- values="$values --set featureToggle.developer=true"
- values="$values --set featureToggle.checklist=true"
-
- if [ -n "$BACKUP_SCHEDULE" ]; then
- values="$values --set postgresql.backup.schedule=$BACKUP_SCHEDULE"
- values="$values --set postgresql.backup.s3.endpoint=$BACKUP_S3_ENDPOINT"
- values="$values --set postgresql.backup.s3.bucket=$BACKUP_S3_BUCKET"
- values="$values --set postgresql.backup.s3.accessKeyId=$BACKUP_S3_ACCESS_KEY_ID"
- values="$values --set postgresql.backup.s3.accessKey=$BACKUP_S3_ACCESS_KEY"
- if [ -n $BACKUP_ENCRYPTION_KEY ]; then
- values="$values --set postgresql.backup.encryptionKey=$BACKUP_ENCRYPTION_KEY"
- fi
- fi
-
- if [ -n "$RESTORE_SOURCE_FILE" ]; then
- values="$values --set postgresql.restore.sourceFile=$RESTORE_SOURCE_FILE"
- values="$values --set postgresql.restore.s3.endpoint=$RESTORE_S3_ENDPOINT"
- values="$values --set postgresql.restore.s3.bucket=$RESTORE_S3_BUCKET"
- values="$values --set postgresql.restore.s3.accessKeyId=$RESTORE_S3_ACCESS_KEY_ID"
- values="$values --set postgresql.restore.s3.accessKey=$RESTORE_S3_ACCESS_KEY"
- values="$values --set postgresql.restore.sourceAppName=$RESTORE_SOURCE_APP"
- if [ -n $RESTORE_ENCRYPTION_KEY ]; then
- values="$values --set postgresql.restore.encryptionKey=$RESTORE_ENCRYPTION_KEY"
- fi
- values="$values --set postgresql.restore.inviteSupportAccountToInterestingCamps=$RESTORE_INVITE_TO_INTERESTING_CAMPS"
- fi
-
- for imagespec in "frontend" "print" "api"; do
- values="$values --set $imagespec.image.pullPolicy=$pull_policy"
- values="$values --set $imagespec.image.repository=docker.io/${docker_hub_account}/ecamp3-$imagespec"
- done
-
- values="$values --set apiCache.image.repository=docker.io/${docker_hub_account}/ecamp3-varnish"
-
- values="$values --set postgresql.dbBackupRestoreImage.pullPolicy=$pull_policy"
- values="$values --set postgresql.dbBackupRestoreImage.repository=docker.io/${docker_hub_account}/ecamp3-db-backup-restore"
+ACTION="${1:-diff}"
- helm uninstall ecamp3-"$instance_name"-"$i" || true
- helm upgrade --install ecamp3-"$instance_name"-"$i" $SCRIPT_DIR/ecamp3 $values
-done
+export NAME=$(jq -r '.NAME' $SCRIPT_DIR/ecamp3/env.yaml)
-rm -f private.pem
-rm -f public.pem
+"$REPO_DIR/.helm/ecamp3/deploy.sh" "${ACTION}"
diff --git a/.helm/ecamp3/.gitignore b/.helm/ecamp3/.gitignore
new file mode 100644
index 0000000000..c5d46b987d
--- /dev/null
+++ b/.helm/ecamp3/.gitignore
@@ -0,0 +1,3 @@
+.env
+/env.yaml
+/values.yaml
diff --git a/.helm/ecamp3/.helmignore b/.helm/ecamp3/.helmignore
index 45735c7c00..dcf3784a42 100644
--- a/.helm/ecamp3/.helmignore
+++ b/.helm/ecamp3/.helmignore
@@ -25,3 +25,8 @@ docker-compose.yml
README.md
.env
.env-example
+.env
+/deploy.sh
+/values.yaml
+/values.yaml.gotmpl
+/env.yaml
\ No newline at end of file
diff --git a/.helm/ecamp3/deploy.sh b/.helm/ecamp3/deploy.sh
new file mode 100755
index 0000000000..255dfdd7f3
--- /dev/null
+++ b/.helm/ecamp3/deploy.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -e
+
+SCRIPT_DIR=$(realpath "$(dirname "$0")")
+REPO_DIR=$(realpath "$SCRIPT_DIR/../..")
+
+if [ -z "${NAME:-}" ]; then
+ echo "Please specify the NAME"
+ exit 1
+fi
+
+cd "$SCRIPT_DIR"
+
+helmfile write-values --environment default --output-file-template values.yaml
+
+HELM_TIMEOUT="${HELM_TIMEOUT:-5m0s}"
+
+if [ "${1:-}" = "deploy" ]; then
+ helm upgrade --install ecamp3-$NAME \
+ $SCRIPT_DIR \
+ --values $SCRIPT_DIR/values.yaml \
+ --timeout "$HELM_TIMEOUT"
+ exit 0
+fi
+
+if [ "${1:-}" = "diff" ]; then
+ helm template --no-hooks --skip-tests ecamp3-$NAME \
+ $SCRIPT_DIR \
+ --values $SCRIPT_DIR/values.yaml | kubectl diff -f -
+ exit 0
+fi
diff --git a/.helm/ecamp3/env.example.yaml b/.helm/ecamp3/env.example.yaml
new file mode 100644
index 0000000000..79365f4e3d
--- /dev/null
+++ b/.helm/ecamp3/env.example.yaml
@@ -0,0 +1,45 @@
+{
+ "API_CACHE_ENABLED": true,
+ "API_DATA_MIGRATIONS_DIR": "dev-data",
+ "API_NUM_THREADS": null,
+ "API_SENTRY_DSN": null,
+ "AUTOSCALING_ENABLED": false,
+ "BACKUP_ENCRYPTION_KEY": null,
+ "BACKUP_S3_ACCESS_KEY": null,
+ "BACKUP_S3_ACCESS_KEY_ID": null,
+ "BACKUP_S3_BUCKET": null,
+ "BACKUP_S3_ENDPOINT": "https://s3.eu-west-3.amazonaws.com",
+ "BACKUP_SCHEDULE": null,
+ "BASIC_AUTH_ENABLED": null,
+ "BASIC_AUTH_PASSWORD": null,
+ "BASIC_AUTH_USERNAME": null,
+ "BROWSERLESS_CPU": "10m",
+ "BROWSERLESS_MEMORY": "200Mi",
+ "DOCKER_HUB_USERNAME": ,
+ "DOMAIN": "ecamp3.ch",
+ "FEATURE_CHECKLIST": true,
+ "FEATURE_DEVELOPER": true,
+ "FRONTEND_SENTRY_DSN": null,
+ "IMAGE_TAG": "my-version",
+ "JWT_PASSPHRASE": ,
+ "JWT_PRIVATE_KEY": ,
+ "JWT_PUBLIC_KEY": ,
+ "LOGIN_INFO_TEXT_KEY": null,
+ "NAME": "my-ecamp",
+ "PHP_CPU": "10m",
+ "PHP_MEMORY": "120Mi",
+ "POSTGRES_ADMIN_URL": ,
+ "POSTGRES_URL": ,
+ "PRINT_CPU": "10m",
+ "PRINT_MEMORY": "150Mi",
+ "PRINT_SENTRY_DSN": null,
+ "RESTORE_ENCRYPTION_KEY": null,
+ "RESTORE_INVITE_TO_INTERESTING_CAMPS": false,
+ "RESTORE_S3_ACCESS_KEY": null,
+ "RESTORE_S3_ACCESS_KEY_ID": null,
+ "RESTORE_S3_BUCKET": null,
+ "RESTORE_S3_ENDPOINT": null,
+ "RESTORE_SOURCE_APP": null,
+ "RESTORE_SOURCE_FILE": null,
+ "SENTRY_AUTH_TOKEN": null
+}
diff --git a/.helm/ecamp3/helmfile.yaml b/.helm/ecamp3/helmfile.yaml
new file mode 100644
index 0000000000..3d63810b60
--- /dev/null
+++ b/.helm/ecamp3/helmfile.yaml
@@ -0,0 +1,10 @@
+environments:
+ default:
+ values:
+ - ./env.yaml
+---
+releases:
+ - name: ""
+ chart: .
+ values:
+ - ./values.yaml.gotmpl
diff --git a/.helm/ecamp3/templates/frontend_configmap.yaml b/.helm/ecamp3/templates/frontend_configmap.yaml
index d58504aa32..589b6aa801 100644
--- a/.helm/ecamp3/templates/frontend_configmap.yaml
+++ b/.helm/ecamp3/templates/frontend_configmap.yaml
@@ -16,6 +16,7 @@ data:
SENTRY_ENVIRONMENT: '{{ .Values.domain }}',
{{- else }}
SENTRY_FRONTEND_DSN: null,
+ SENTRY_ENVIRONMENT: '{{ .Values.domain }}',
{{- end }}
DEPLOYMENT_TIME: '{{ .Values.deploymentTime }}',
VERSION: '{{ .Values.deployedVersion }}',
diff --git a/.helm/ecamp3/values.yaml b/.helm/ecamp3/values.yaml
deleted file mode 100644
index 58a0342d8f..0000000000
--- a/.helm/ecamp3/values.yaml
+++ /dev/null
@@ -1,262 +0,0 @@
-# Default values for ecamp3.
-# Declare configuration values to be passed into the templates.
-
-chartNameOverride: ""
-imageTag: "latest"
-imagePullSecrets: []
-deploymentTime: ""
-domain:
-deployedVersion: "devel"
-versionLinkTemplate: 'https://github.com/ecamp/ecamp3/commit/{version}'
-termsOfServiceLinkTemplate: # 'https://ecamp3.ch/{lang}/tos'
-newsLink: # 'https://ecamp3.ch/blog'
-helpLink: # 'https://ecamp3.ch/faq'
-
-# enable/disable feature across the complete deployment
-featureToggle:
- developer: false # enables various tools/features foreseen for development deployments (language switcher, form controls view, performance measurement view, etc.)
- checklist: false # enables checklist feature in frontend
-
-api:
- subpath: "/api"
- image:
- repository: "docker.io/ecamp/ecamp3-api"
- pullPolicy: IfNotPresent
- # Overrides the image tag whose shared default is .Values.imageTag
- tag:
- service:
- type: ClusterIP
- port: 3001
- metrics:
- port: 2019
- replicaCount: 1
- appEnv: prod
- appDebug: "0"
- appSecret: ""
- corsAllowOrigin: "^https://.*?\\.chart-example\\.local$"
- trustedProxies:
- - "::1"
- - "127.0.0.1"
- - "10.0.0.0/8"
- - "172.16.0.0/12"
- - "192.168.0.0/16"
- caddyGlobalOptions: ""
- sentryDsn:
- jwt:
- passphrase:
- privateKey:
- publicKey:
- oauth:
- google:
- clientId: '889440431087-ueuhpadf2g7h5ucdke92mvfaf4l779m4.apps.googleusercontent.com'
- clientSecret: 'HNaD1FNO-a1qliacIrIfcGqO'
- pbsmidata:
- clientId: '2a955efdaaac73f665b29ec182cd9a114db01675ced710a464d33d10f58be600'
- clientSecret: '00a23e48bcb776d453b255428ffe810643db7155a9f3d743d7edf52eac400580'
- baseUrl: 'https://pbs.puzzle.ch'
- cevidb:
- clientId: 'raT1QFf6TOQzpn3yVH-My6YLrmsvOrfMhYypxzjPMWk'
- clientSecret: 'fTxMrzjBn3gPGg3eB0bNMmjRqg4ccs3_su7CaTXtljE'
- baseUrl: 'https://cevi.puzzle.ch'
- jubladb:
- clientId: 'WrKABq7GwmC6h1F0W73OGX_fOTHWWXnKXfrPMHOdQWY'
- clientSecret: 'oQ164RDMIAocL6PhmCoeT1Ymcg-7WcOJZdxCnIph5gM'
- baseUrl: 'https://jubla.puzzle.ch'
- num_threads:
- resources:
- requests:
- cpu: 10m
- memory: 120Mi
-
-frontend:
- image:
- repository: "docker.io/ecamp/ecamp3-frontend"
- pullPolicy: IfNotPresent
- # Overrides the image tag whose shared default is .Values.imageTag
- tag:
- sentryDsn:
- service:
- type: ClusterIP
- port: 3000
- replicaCount: 1
- resources:
- requests:
- cpu: 10m
- memory: 10Mi
- loginInfoTextKey: 'prod'
-
-print:
- subpath: "/print"
- ingress:
- readTimeoutSeconds:
- image:
- repository: "docker.io/ecamp/ecamp3-print"
- pullPolicy: IfNotPresent
- # Overrides the image tag whose shared default is .Values.imageTag
- tag:
- sentryDsn:
- browserWsEndpoint:
- service:
- type: ClusterIP
- port: 3003
- replicaCount: 1
- renderHTMLTimeoutMs:
- renderPDFTimeoutMs:
- resources:
- requests:
- cpu: 10m
- memory: 150Mi
-
-browserless:
- enabled: true
- domain:
- image:
- repository: "docker.io/browserless/chrome"
- pullPolicy: IfNotPresent
- # renovate: datasource=docker depName=browserless/chrome
- tag: "1.61.1-puppeteer-21.4.1"
- service:
- type: ClusterIP
- port: 3000
- maxConcurrentSessions: 1
- connectionTimeout: 30000
- maxQueueLength: 5
- resources:
- requests:
- cpu: 10m
- memory: 200Mi
-
-mail:
- dummyEnabled: true
- # If using a real mail server, the connection uri to send emails to
- dsn: # smtp://myuser:mypass@mymailserver:1025
- # If the dummy mail server is enabled, the domain where the web interface is available
- subpath: "/mail"
- image:
- repository: "docker.io/maildev/maildev"
- pullPolicy: IfNotPresent
- # Overrides the image tag whose shared default is .Values.imageTag
- tag: "latest"
- service:
- type: ClusterIP
- port: 1080
- resources:
- requests:
- cpu: 10m
- memory: 10Mi
-
-postgresql:
- url:
- dropDBOnUninstall: false
- # An uri with privileges to create and drop a database for the application.
- # Can be left empty if the required database specified in postgresql.url already exists.
- adminUrl:
- dbBackupRestoreImage:
- repository: "docker.io/ecamp/ecamp3-db-backup-restore"
- pullPolicy: IfNotPresent
- # Overrides the image tag whose shared default is .Values.imageTag
- tag:
- backup:
- schedule:
- s3:
- endpoint:
- bucket:
- accessKeyId:
- accessKey:
- encryptionKey:
- restore:
- # null => no restore, latest => restore latest backup, s3 key => restore this backup
- sourceFile:
- sourceAppName:
- s3:
- endpoint:
- bucket:
- accessKeyId:
- accessKey:
- inviteSupportAccountToInterestingCamps: false
- encryptionKey:
-
-recaptcha:
- siteKey:
- secret:
-
-serviceAccount:
- # Specifies whether a service account should be created
- create: true
- # Annotations to add to the service account
- annotations: {}
- # The name of the service account to use.
- # If not set and create is true, a name is generated using the fullname template
- name: ""
-
-podSecurityContext: {}
- # fsGroup: 2000
-
-securityContext: {}
- # capabilities:
- # drop:
- # - ALL
- # readOnlyRootFilesystem: true
- # runAsNonRoot: true
- # runAsUser: 1000
-
-ingress:
- enabled: true
- basicAuth:
- enabled: false
- username:
- password:
- annotations:
- # kubernetes.io/tls-acme: "true"
- className: nginx
- tls:
-
-apiCache:
- enabled: false
- image:
- repository: "docker.io/ecamp/ecamp3-varnish"
- pullPolicy: IfNotPresent
- # Overrides the image tag whose shared default is .Values.imageTag
- tag:
- service:
- type: ClusterIP
- ports:
- http: 3000
- purge: 3001
- varnishSize: 50M
- varnishHttpPort: 8080
- varnishPurgePort: 8081
- sendXKeyHeadersDownstream: false
- resources:
- requests:
- cpu: 10m
- memory: 100Mi
- logging:
- enabled: true
- customOutput: '{ "received_at": "%t", "varnish_side": "%{Varnish:side}x", "method": "%m", "host": "%h", "url": "%U", "query": "%q", "response_bytes": %b, "time_taken": %D, "status": %s, "handling": "%{Varnish:handling}x", "response_reason": "%{VSL:RespReason}x", "fetch_error": "%{VSL:FetchError}x", "httpRequest": { "requestMethod": "%m", "requestUrl": "%{Host}i%U%q", "status": "%s" } }'
- customOutputJsonFormat: true
- # Timeout before returning error on initial VSM connection.
- # If set the VSM connection is retried every 0.5 seconds for this many seconds.
- # If zero the connection is attempted only once and will fail immediately if unsuccessful.
- # If set to "off", the connection will not fail, allowing the utility to start and wait indefinitely for the Varnish instance to appear.
- # Defaults to "off" in this case.
- timeout: "off"
- resources:
- requests:
- cpu: 10m
- memory: 20Mi
- prometheus:
- enabled: true
- path: "/metrics"
- port: 9131
- resources:
- requests:
- cpu: 10m
- memory: 20Mi
-
-autoscaling:
- enabled: false
- minReplicas: 1
- maxReplicas: 5
- targetCPUUtilizationPercentage: 80
- # targetMemoryUtilizationPercentage: 80
diff --git a/.helm/ecamp3/values.yaml.gotmpl b/.helm/ecamp3/values.yaml.gotmpl
new file mode 100644
index 0000000000..68e9bf926c
--- /dev/null
+++ b/.helm/ecamp3/values.yaml.gotmpl
@@ -0,0 +1,280 @@
+{{- /* Input parameters from helmfile env.yaml */ -}}
+{{- $name := .Environment.Values | getOrNil "NAME" | required "NAME is required" -}}
+{{- $domain := .Environment.Values | getOrNil "DOMAIN" | required "DOMAIN is required" -}}
+{{- $dockerUser := .Environment.Values | getOrNil "DOCKER_HUB_USERNAME" | required "DOCKER_HUB_USERNAME is required" -}}
+{{- $imageTag := .Environment.Values | getOrNil "IMAGE_TAG" | required "IMAGE_TAG is required" -}}
+
+# Default values for ecamp3.
+# Declare configuration values to be passed into the templates.
+
+chartNameOverride: ""
+imageTag: {{ $imageTag | quote }}
+imagePullSecrets: []
+deploymentTime: {{ exec "date" (list "-u" "+%s") | trim | quote }}
+domain: {{ printf "%s.%s" $name $domain | quote }}
+deployedVersion: {{ (exec "git" (list "rev-parse" "--short" "HEAD")) | trim | quote }}
+versionLinkTemplate: 'https://github.com/ecamp/ecamp3/commit/{version}'
+termsOfServiceLinkTemplate: 'https://ecamp3.ch/{lang}/tos'
+newsLink: 'https://ecamp3.ch/blog'
+helpLink: 'https://ecamp3.ch/faq'
+
+# enable/disable feature across the complete deployment
+featureToggle:
+ developer: {{ .Environment.Values | getOrNil "FEATURE_DEVELOPER" | default false }}
+ checklist: {{ .Environment.Values | getOrNil "FEATURE_CHECKLIST" | default false }}
+
+api:
+ dataMigrationsDir: {{ .Environment.Values | getOrNil "API_DATA_MIGRATIONS_DIR" | default "prod-data" }}
+ subpath: "/api"
+ image:
+ repository: "docker.io/{{ $dockerUser }}/ecamp3-api"
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose shared default is .Values.imageTag
+ tag:
+ service:
+ type: ClusterIP
+ port: 3001
+ metrics:
+ port: 2019
+ replicaCount: 1
+ appEnv: prod
+ appDebug: "0"
+ appSecret: {{ .Environment.Values | getOrNil "API_APP_SECRET" }}
+ corsAllowOrigin: "^https://.*?\\.chart-example\\.local$"
+ trustedProxies:
+ - "::1"
+ - "127.0.0.1"
+ - "10.0.0.0/8"
+ - "172.16.0.0/12"
+ - "192.168.0.0/16"
+ caddyGlobalOptions: ""
+ sentryDsn: {{ .Environment.Values | getOrNil "API_SENTRY_DSN" | quote }}
+ jwt:
+ passphrase: {{ .Environment.Values | getOrNil "JWT_PASSPHRASE" | required "JWT_PASSPHRASE is required" }}
+ privateKey: |
+ {{- .Environment.Values | getOrNil "JWT_PRIVATE_KEY" | required "JWT_PRIVATE_KEY is required" | nindent 6 }}
+ publicKey: |
+ {{- .Environment.Values | getOrNil "JWT_PUBLIC_KEY" | required "JWT_PUBLIC_KEY is required" | nindent 6 }}
+ oauth:
+ google:
+ clientId: {{ .Environment.Values | getOrNil "OAUTH_GOOGLE_CLIENT_ID" | default "889440431087-ueuhpadf2g7h5ucdke92mvfaf4l779m4.apps.googleusercontent.com" | quote }}
+ clientSecret: {{ .Environment.Values | getOrNil "OAUTH_GOOGLE_CLIENT_SECRET" | default "HNaD1FNO-a1qliacIrIfcGqO" | quote }}
+ pbsmidata:
+ clientId: {{ .Environment.Values | getOrNil "OAUTH_PBSMIDATA_CLIENT_ID" | default "2a955efdaaac73f665b29ec182cd9a114db01675ced710a464d33d10f58be600" | quote }}
+ clientSecret: {{ .Environment.Values | getOrNil "OAUTH_PBSMIDATA_CLIENT_SECRET" | default "00a23e48bcb776d453b255428ffe810643db7155a9f3d743d7edf52eac400580" | quote }}
+ baseUrl: {{ .Environment.Values | getOrNil "OAUTH_PBSMIDATA_BASE_URL" | default "https://pbs.puzzle.ch" | quote }}
+ cevidb:
+ clientId: {{ .Environment.Values | getOrNil "OAUTH_CEVIDB_CLIENT_ID" | default "raT1QFf6TOQzpn3yVH-My6YLrmsvOrfMhYypxzjPMWk" | quote }}
+ clientSecret: {{ .Environment.Values | getOrNil "OAUTH_CEVIDB_CLIENT_SECRET" | default "fTxMrzjBn3gPGg3eB0bNMmjRqg4ccs3_su7CaTXtljE" | quote }}
+ baseUrl: {{ .Environment.Values | getOrNil "OAUTH_CEVIDB_BASE_URL" | default "https://cevi.puzzle.ch" | quote }}
+ jubladb:
+ clientId: {{ .Environment.Values | getOrNil "OAUTH_JUBLADB_CLIENT_ID" | default "WrKABq7GwmC6h1F0W73OGX_fOTHWWXnKXfrPMHOdQWY" | quote }}
+ clientSecret: {{ .Environment.Values | getOrNil "OAUTH_JUBLADB_CLIENT_SECRET" | default "oQ164RDMIAocL6PhmCoeT1Ymcg-7WcOJZdxCnIph5gM" | quote }}
+ baseUrl: {{ .Environment.Values | getOrNil "OAUTH_JUBLADB_BASE_URL" | default "https://jubla.puzzle.ch" | quote }}
+ num_threads: {{ .Environment.Values | getOrNil "API_NUM_THREADS" | default false }}
+ resources:
+ requests:
+ cpu: {{ .Environment.Values | getOrNil "PHP_CPU" | default "1000m" | quote }}
+ memory: {{ .Environment.Values | getOrNil "PHP_MEMORY" | default "500Mi" | quote }}
+ {{- $phpCpuLimit := .Environment.Values | get "PHP_CPULIMIT" "1900m" -}}
+ {{ if not ( $phpCpuLimit | empty ) }}
+ limits:
+ cpu: {{ $phpCpuLimit | quote }}
+ {{ end }}
+
+frontend:
+ image:
+ repository: "docker.io/{{ $dockerUser }}/ecamp3-frontend"
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose shared default is .Values.imageTag
+ tag:
+ sentryDsn: {{ .Environment.Values | getOrNil "FRONTEND_SENTRY_DSN" | quote }}
+ service:
+ type: ClusterIP
+ port: 3000
+ replicaCount: 1
+ resources:
+ requests:
+ cpu: {{ .Environment.Values | getOrNil "FRONTEND_CPU" | default "50m" | quote }}
+ memory: {{ .Environment.Values | getOrNil "FRONTEND_MEMORY" | default "10Mi" | quote }}
+ loginInfoTextKey: {{ .Environment.Values | get "LOGIN_INFO_TEXT_KEY" | default "prod" | quote }}
+
+print:
+ subpath: "/print"
+ ingress:
+ readTimeoutSeconds: {{ .Environment.Values | getOrNil "PRINT_INGRESS_READ_TIMEOUT_SECONDS" | quote }}
+ image:
+ repository: "docker.io/{{ $dockerUser }}/ecamp3-print"
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose shared default is .Values.imageTag
+ tag:
+ sentryDsn: {{ .Environment.Values | getOrNil "PRINT_SENTRY_DSN" | quote }}
+ browserWsEndpoint: {{ .Environment.Values | getOrNil "BROWSER_WS_ENDPOINT" | quote }}
+ service:
+ type: ClusterIP
+ port: 3003
+ replicaCount: 1
+ renderHTMLTimeoutMs: {{ .Environment.Values | getOrNil "PRINT_RENDER_HTML_TIMEOUT_MS" }}
+ renderPDFTimeoutMs: {{ .Environment.Values | getOrNil "PRINT_RENDER_PDF_TIMEOUT_MS" }}
+ resources:
+ requests:
+ cpu: {{ .Environment.Values | getOrNil "PRINT_CPU" | default "300m" | quote }}
+ memory: {{ .Environment.Values | getOrNil "PRINT_MEMORY" | default "150Mi" | quote }}
+
+browserless:
+ enabled: true
+ domain:
+ image:
+ repository: "docker.io/browserless/chrome"
+ pullPolicy: IfNotPresent
+ # renovate: datasource=docker depName=browserless/chrome
+ tag: "1.61.1-puppeteer-21.4.1"
+ service:
+ type: ClusterIP
+ port: 3000
+ maxConcurrentSessions: {{ .Environment.Values | getOrNil "BROWSERLESS_MAXCONCURRENTSESSIONS" | default 1 }}
+ connectionTimeout: {{ .Environment.Values | getOrNil "BROWSERLESS_CONNECTION_TIMEOUT_MS" | default "30000" | quote }}
+ maxQueueLength: {{ .Environment.Values | getOrNil "BROWSERLESS_MAXQUEUELENGTH" | default 5 }}
+ resources:
+ requests:
+ cpu: {{ .Environment.Values | getOrNil "BROWSERLESS_CPU" | default "500m" }}
+ memory: {{ .Environment.Values | getOrNil "BROWSERLESS_MEMORY" | default "800Mi" }}
+
+mail:
+ dummyEnabled: {{ not (.Environment.Values | getOrNil "MAILER_DSN") }}
+ # If using a real mail server, the connection uri to send emails to
+ dsn: {{ .Environment.Values | getOrNil "MAILER_DSN" | quote }}
+ # If the dummy mail server is enabled, the domain where the web interface is available
+ subpath: "/mail"
+ image:
+ repository: "docker.io/maildev/maildev"
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose shared default is .Values.imageTag
+ tag: "latest"
+ service:
+ type: ClusterIP
+ port: 1080
+ resources:
+ requests:
+ cpu: 10m
+ memory: 10Mi
+
+postgresql:
+ url: "{{ .Environment.Values | getOrNil "POSTGRES_URL" | required "POSTGRES_URL is required" }}/ecamp3{{ $name }}?sslmode=require"
+ dropDBOnUninstall: {{ .Environment.Values | getOrNil "DROP_DB_ON_UNINSTALL" | default false }}
+ # An uri with privileges to create and drop a database for the application.
+ # Can be left empty if the required database specified in postgresql.url already exists.
+ {{ if .Environment.Values | getOrNil "POSTGRES_ADMIN_URL" | default false }}
+ adminUrl: "{{ .Environment.Values | getOrNil "POSTGRES_ADMIN_URL" }}/ecamp3{{ $name }}?sslmode=require"
+ {{ else }}
+ adminUrl:
+ {{ end }}
+ dbBackupRestoreImage:
+ repository: "docker.io/{{ $dockerUser }}/ecamp3-db-backup-restore"
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose shared default is .Values.imageTag
+ tag:
+ backup:
+ schedule: {{ .Environment.Values | getOrNil "BACKUP_SCHEDULE" | quote }}
+ s3:
+ endpoint: {{ .Environment.Values | getOrNil "BACKUP_S3_ENDPOINT" | quote }}
+ bucket: {{ .Environment.Values | getOrNil "BACKUP_S3_BUCKET" | quote }}
+ accessKeyId: {{ .Environment.Values | getOrNil "BACKUP_S3_ACCESS_KEY_ID" | quote }}
+ accessKey: {{ .Environment.Values | getOrNil "BACKUP_S3_ACCESS_KEY" | quote }}
+ encryptionKey: {{ .Environment.Values | getOrNil "BACKUP_ENCRYPTION_KEY" | quote }}
+ restore:
+ # null => no restore, latest => restore latest backup, s3 key => restore this backup
+ sourceFile: {{ .Environment.Values | getOrNil "RESTORE_SOURCE_FILE" | quote }}
+ sourceAppName: {{ .Environment.Values | getOrNil "RESTORE_SOURCE_APP" | quote }}
+ s3:
+ endpoint: {{ .Environment.Values | getOrNil "RESTORE_S3_ENDPOINT" | quote }}
+ bucket: {{ .Environment.Values | getOrNil "RESTORE_S3_BUCKET" | quote }}
+ accessKeyId: {{ .Environment.Values | getOrNil "RESTORE_S3_ACCESS_KEY_ID" | quote }}
+ accessKey: {{ .Environment.Values | getOrNil "RESTORE_S3_ACCESS_KEY" | quote }}
+ inviteSupportAccountToInterestingCamps: {{ .Environment.Values | get "RESTORE_INVITE_TO_INTERESTING_CAMPS" false }}
+ encryptionKey: {{ .Environment.Values | getOrNil "RESTORE_ENCRYPTION_KEY" | quote }}
+
+recaptcha:
+ siteKey: {{ .Environment.Values | getOrNil "RECAPTCHA_SITE_KEY" | quote }}
+ secret: {{ .Environment.Values | getOrNil "RECAPTCHA_SECRET" | quote }}
+
+serviceAccount:
+ # Specifies whether a service account should be created
+ create: true
+ # Annotations to add to the service account
+ annotations: {}
+ # The name of the service account to use.
+ # If not set and create is true, a name is generated using the fullname template
+ name: ""
+
+podSecurityContext: {}
+# fsGroup: 2000
+
+securityContext: {}
+ # capabilities:
+ # drop:
+ # - ALL
+ # readOnlyRootFilesystem: true
+ # runAsNonRoot: true
+# runAsUser: 1000
+
+ingress:
+ enabled: true
+ basicAuth:
+ enabled: {{ .Environment.Values | getOrNil "BASIC_AUTH_ENABLED" | default false }}
+ username: {{ .Environment.Values | getOrNil "BASIC_AUTH_USERNAME" | quote }}
+ password: {{ .Environment.Values | getOrNil "BASIC_AUTH_PASSWORD" | quote }}
+ annotations:
+ # kubernetes.io/tls-acme: "true"
+ className: nginx
+ tls:
+
+apiCache:
+ enabled: {{ .Environment.Values | get "API_CACHE_ENABLED" false }}
+ image:
+ repository: "docker.io/{{ $dockerUser }}/ecamp3-varnish"
+ pullPolicy: IfNotPresent
+ # Overrides the image tag whose shared default is .Values.imageTag
+ tag:
+ service:
+ type: ClusterIP
+ ports:
+ http: 3000
+ purge: 3001
+ varnishSize: 50M
+ varnishHttpPort: 8080
+ varnishPurgePort: 8081
+ sendXKeyHeadersDownstream: {{ .Environment.Values | get "SEND_XKEY_HEADERS_DOWNSTREAM" false }}
+ resources:
+ requests:
+ cpu: {{ .Environment.Values | getOrNil "API_CACHE_CPU" | default "10m" }}
+ memory: {{ .Environment.Values | getOrNil "API_CACHE_MEMORY" | default "100Mi" }}
+ logging:
+ enabled: true
+ customOutput: '{ "received_at": "%t", "varnish_side": "%{Varnish:side}x", "method": "%m", "host": "%h", "url": "%U", "query": "%q", "response_bytes": %b, "time_taken": %D, "status": %s, "handling": "%{Varnish:handling}x", "response_reason": "%{VSL:RespReason}x", "fetch_error": "%{VSL:FetchError}x", "httpRequest": { "requestMethod": "%m", "requestUrl": "%{Host}i%U%q", "status": "%s" } }'
+ customOutputJsonFormat: true
+ # Timeout before returning error on initial VSM connection.
+ # If set the VSM connection is retried every 0.5 seconds for this many seconds.
+ # If zero the connection is attempted only once and will fail immediately if unsuccessful.
+ # If set to "off", the connection will not fail, allowing the utility to start and wait indefinitely for the Varnish instance to appear.
+ # Defaults to "off" in this case.
+ timeout: "off"
+ resources:
+ requests:
+ cpu: 10m
+ memory: 20Mi
+ prometheus:
+ enabled: true
+ path: "/metrics"
+ port: 9131
+ resources:
+ requests:
+ cpu: 10m
+ memory: 20Mi
+
+autoscaling:
+ enabled: {{ .Environment.Values | get "AUTOSCALING_ENABLED" true }}
+ minReplicas: 1
+ maxReplicas: 5
+ targetCPUUtilizationPercentage: {{ .Environment.Values | getOrNil "AUTOSCALING_TARGETCPU" | default 90 }}
+ # targetMemoryUtilizationPercentage: 80
diff --git a/.helm/generate-jwt-values.sh b/.helm/generate-jwt-values.sh
new file mode 100755
index 0000000000..439b6bbb52
--- /dev/null
+++ b/.helm/generate-jwt-values.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+set -euo pipefail
+
+set -e
+
+SCRIPT_DIR=$(realpath "$(dirname "$0")")
+REPO_DIR=$(realpath "$SCRIPT_DIR"/..)
+
+TMP_DIR=$(mktemp -d)
+
+jwt_passphrase=$(uuidgen)
+echo -n "$jwt_passphrase" | openssl genpkey -out "$TMP_DIR"/private.pem -pass stdin -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096
+echo -n "$jwt_passphrase" | openssl pkey -in "$TMP_DIR"/private.pem -passin stdin -out "$TMP_DIR"/public.pem -pubout
+
+jwt_public_key=$(cat "$TMP_DIR"/public.pem)
+jwt_private_key=$(cat "$TMP_DIR"/private.pem)
+
+tmp="$TMP_DIR/tmp.yaml"
+
+json_file="$SCRIPT_DIR/ecamp3/env.yaml"
+
+if [ ! -f $json_file ]; then
+ cp "$SCRIPT_DIR/ecamp3/env.example.yaml" $json_file
+fi
+
+jq --arg v "$jwt_passphrase" '.JWT_PASSPHRASE = $v' < $json_file > "$tmp"
+mv "$tmp" $json_file
+
+jq --arg v "$jwt_public_key" '.JWT_PUBLIC_KEY = $v' < $json_file > "$tmp"
+mv "$tmp" $json_file
+
+jq --arg v "$jwt_private_key" '.JWT_PRIVATE_KEY = $v' < $json_file > "$tmp"
+mv "$tmp" $json_file
+
+rm -rf "$TMP_DIR"