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...

![QR Code](https://api.qrserver.com/v1/create-qr-code/?size=180x180&ecc=H&format=svg&qzone=4&color=5-72-97&data=https://${{ inputs.name }}.${{ vars.DOMAIN }}/)

_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...

![QR Code](https://api.qrserver.com/v1/create-qr-code/?size=180x180&ecc=H&format=svg&qzone=4&color=5-72-97&data=https://${{ inputs.name }}.${{ vars.DOMAIN }}/)

_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"