From 30e5de24982b9bd0cffb5013169df619755da474 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 8 Jul 2026 12:10:36 -0700 Subject: [PATCH 1/4] feat: Update workflows and add SAML SSO support - Updated GitHub Actions workflows to use actions/checkout@v5 and other updated action versions. - Bumped chart version to 0.7.0 and added SAML SSO configuration options in values.yaml and documentation. - Enhanced deployment templates to include SAML SSO environment variables and secret mounts. - Updated quickstart guide to include steps for enabling SAML SSO. --- .github/workflows/docs-check.yaml | 2 +- .github/workflows/lint-test.yaml | 46 +++++++++++++--- .github/workflows/release.yaml | 2 +- .github/workflows/sync-docs.yaml | 2 +- charts/currents/Chart.yaml | 2 +- charts/currents/templates/_common.tpl | 29 +++++++++++ .../currents/templates/server/deployment.yaml | 11 ++++ charts/currents/values.yaml | 32 ++++++++++++ docs/configuration.md | 17 +++++- docs/eks/quickstart.md | 52 ++++++++++++++++++- samples/eks/eks-config.yaml | 7 +++ samples/local/chart-config.yaml | 7 +++ 12 files changed, 196 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docs-check.yaml b/.github/workflows/docs-check.yaml index ab662d1..c629095 100644 --- a/.github/workflows/docs-check.yaml +++ b/.github/workflows/docs-check.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: ref: ${{ github.event.pull_request.head.sha }} - name: Ensure documentation is updated diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index 82ee622..ae3aabf 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -5,24 +5,27 @@ on: pull_request jobs: lint-test: runs-on: ubuntu-latest + permissions: + id-token: write # Required for OIDC (assume the ECR pull role) + contents: read # Required for checkout steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: fetch-depth: 0 - name: Set up Helm - uses: azure/setup-helm@v4.2.0 + uses: azure/setup-helm@v5.0.1 with: - version: v3.17.0 + version: v3.21.2 - - uses: actions/setup-python@v5.3.0 + - uses: actions/setup-python@v6.3.0 with: python-version: '3.x' check-latest: true - name: Set up chart-testing - uses: helm/chart-testing-action@v2.7.0 + uses: helm/chart-testing-action@v2.8.0 with: yamale_version: "6.0.0" @@ -38,10 +41,39 @@ jobs: if: steps.list-changed.outputs.changed == 'true' run: ct lint --check-version-increment=false --target-branch ${{ github.event.repository.default_branch }} + - name: Configure AWS credentials + if: steps.list-changed.outputs.changed == 'true' + uses: aws-actions/configure-aws-credentials@v6 + with: + role-to-assume: arn:aws:iam::761136292957:role/GitHubActions-CurrentsDevDocker-ECRPull + aws-region: us-east-1 + + - name: Login to Currents ECR + if: steps.list-changed.outputs.changed == 'true' + uses: aws-actions/amazon-ecr-login@v2 + with: + registries: "513558712013" + - name: Create kind cluster if: steps.list-changed.outputs.changed == 'true' - uses: helm/kind-action@v1.12.0 + uses: helm/kind-action@v1.14.0 + + # The chart pulls private images from Currents' ECR. kind nodes don't share + # the runner's registry auth, so pull on the runner (authenticated above) and + # side-load into the cluster. imagePullPolicy defaults to IfNotPresent, so the + # pods use these preloaded images without needing an in-cluster pull secret. + - name: Preload Currents images into kind + if: steps.list-changed.outputs.changed == 'true' + run: | + images=$(helm template ct-release charts/currents -f charts/currents/ci/lint-values.yaml \ + | awk '$1 == "image:" { print $2 }' | tr -d '"' | grep '513558712013' | sort -u) + echo "Preloading images into kind:" + echo "$images" + for img in $images; do + docker pull "$img" + kind load docker-image "$img" --name chart-testing + done - name: Run chart-testing (install) if: steps.list-changed.outputs.changed == 'true' - run: ct install --target-branch ${{ github.event.repository.default_branch }} || true \ No newline at end of file + run: ct install --target-branch ${{ github.event.repository.default_branch }} \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 56a36ea..7ea4ce8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 diff --git a/.github/workflows/sync-docs.yaml b/.github/workflows/sync-docs.yaml index dd61b34..67db41a 100644 --- a/.github/workflows/sync-docs.yaml +++ b/.github/workflows/sync-docs.yaml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: fetch-depth: 0 diff --git a/charts/currents/Chart.yaml b/charts/currents/Chart.yaml index 8eb8749..dd64063 100644 --- a/charts/currents/Chart.yaml +++ b/charts/currents/Chart.yaml @@ -5,7 +5,7 @@ home: https://currents.dev type: application # The chart version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.6.0 +version: 0.7.0 # Version number of the application being deployed. # Versions are not expected to follow Semantic Versioning. They should reflect the version the application is using. appVersion: "2026-01-26-001" diff --git a/charts/currents/templates/_common.tpl b/charts/currents/templates/_common.tpl index 5ada27e..3f0da3a 100644 --- a/charts/currents/templates/_common.tpl +++ b/charts/currents/templates/_common.tpl @@ -232,4 +232,33 @@ Create the name of the service account to use - name: EMAIL_LINKS_BASE_URL value: {{ .Values.currents.email.linksBaseUrl }} {{- end }} +{{- end -}} + +{{- define "currents.samlSSOEnv" -}} +{{- if .Values.currents.sso.saml.enabled }} +- name: SSO_SAML_IDP_METADATA_FILE + value: {{ printf "/etc/currents/sso/%s" .Values.currents.sso.saml.metadataKey | quote }} +- name: SSO_SAML_ISSUER + value: {{ .Values.currents.sso.saml.issuer | quote }} +{{- if .Values.currents.sso.saml.providerId }} +- name: SSO_SAML_PROVIDER_ID + value: {{ .Values.currents.sso.saml.providerId | quote }} +{{- end }} +{{- if .Values.currents.sso.saml.defaultRole }} +- name: SSO_SAML_DEFAULT_ROLE + value: {{ .Values.currents.sso.saml.defaultRole | quote }} +{{- end }} +{{- if .Values.currents.sso.saml.allowedDomains }} +- name: SSO_ALLOWED_DOMAINS + value: {{ .Values.currents.sso.saml.allowedDomains | quote }} +{{- end }} +{{- if .Values.currents.sso.saml.authnRequestsSigned }} +- name: SSO_SAML_AUTHN_REQUESTS_SIGNED + value: "true" +- name: SSO_SAML_SP_CERT_FILE + value: {{ printf "/etc/currents/sso/%s" .Values.currents.sso.saml.spCertKey | quote }} +- name: SSO_SAML_SP_KEY_FILE + value: {{ printf "/etc/currents/sso/%s" .Values.currents.sso.saml.spKeyKey | quote }} +{{- end }} +{{- end }} {{- end -}} \ No newline at end of file diff --git a/charts/currents/templates/server/deployment.yaml b/charts/currents/templates/server/deployment.yaml index 5413622..2843370 100644 --- a/charts/currents/templates/server/deployment.yaml +++ b/charts/currents/templates/server/deployment.yaml @@ -70,6 +70,7 @@ spec: {{- include "currents.connectionConfigEnv" . | nindent 12 }} {{- include "currents.URLConfigEnv" . | nindent 12 }} {{- include "currents.emailSMTPEnv" . | nindent 12 }} + {{- include "currents.samlSSOEnv" . | nindent 12 }} {{- if .Values.currents.betterAuth.secretName }} - name: BETTER_AUTH_SECRET valueFrom: @@ -94,6 +95,11 @@ spec: volumeMounts: - name: pm2-data mountPath: /home/node/.pm2 + {{- if .Values.currents.sso.saml.enabled }} + - name: sso-saml + mountPath: /etc/currents/sso + readOnly: true + {{- end }} {{- with .Values.server.livenessProbe }} livenessProbe: {{- toYaml . | nindent 12 }} @@ -116,6 +122,11 @@ spec: - name: pm2-data emptyDir: medium: "Memory" + {{- if .Values.currents.sso.saml.enabled }} + - name: sso-saml + secret: + secretName: {{ .Values.currents.sso.saml.metadataSecretName }} + {{- end }} {{- with .Values.global.volumes }} {{- toYaml . | nindent 8 }} {{- end }} diff --git a/charts/currents/values.yaml b/charts/currents/values.yaml index 2e44a9d..a0a7958 100644 --- a/charts/currents/values.yaml +++ b/charts/currents/values.yaml @@ -68,6 +68,38 @@ currents: # -- The K8s secret key for the Better Auth secret # @section -- Frequently Used key: secret + sso: + saml: + # -- Enable SAML SSO. Requires `issuer` and `metadataSecretName` below (a K8s secret holding your IdP metadata XML). Turns on email-first SSO login. + # @section -- SAML SSO + enabled: false + # -- SP entityID / audience presented to your IdP. A stable opaque identifier (e.g. `currents-onprem:your-org`), NOT a URL. Must match the Audience / SP Entity ID configured in your IdP. + # @section -- SAML SSO + issuer: "" + # -- Provider id; recommend your IdP service name (e.g. `okta`). Becomes the last path segment of the ACS callback URL. + # @section -- SAML SSO + providerId: onprem-saml + # -- K8s secret holding the IdP metadata XML (and any SP cert/key). Mounted read-only at /etc/currents/sso. See the SAML SSO guide for how to create it. + # @section -- SAML SSO + metadataSecretName: "" + # -- The secret key (file name) of the IdP metadata XML + # @section -- SAML SSO + metadataKey: idp-metadata.xml + # -- Role granted to auto-provisioned SSO users + # @section -- SAML SSO + defaultRole: member + # -- Comma-separated email domains allowed to sign in via SSO (optional allow-list) + # @section -- SAML SSO + allowedDomains: "" + # -- Sign outbound AuthnRequests. Requires the SP cert/key (below) to be present in the same secret. + # @section -- SAML SSO + authnRequestsSigned: false + # -- Secret key (file name) of the SP certificate PEM (only when authnRequestsSigned=true) + # @section -- SAML SSO + spCertKey: sp-cert.pem + # -- Secret key (file name) of the SP private key PEM (only when authnRequestsSigned=true) + # @section -- SAML SSO + spKeyKey: sp-key.pem apiInternalToken: # -- The K8s secret to use for the internal API token # @section -- Required diff --git a/docs/configuration.md b/docs/configuration.md index e84f901..646deaf 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,6 +1,6 @@ # Configuration Reference -![Version: 0.6.0](https://img.shields.io/badge/Version-0.6.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2026-01-26-001](https://img.shields.io/badge/AppVersion-2026--01--26--001-informational?style=flat-square) +![Version: 0.7.0](https://img.shields.io/badge/Version-0.7.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2026-01-26-001](https://img.shields.io/badge/AppVersion-2026--01--26--001-informational?style=flat-square) ## Requirements @@ -68,6 +68,21 @@ The following table lists the configurable parameters of the `currents` chart an | changestreams.resources | object | `{}` (defaults to global.resources) | Resources to provide [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) | | webhooks.resources | object | `{}` (defaults to global.resources) | Resources to provide [Resource Management for Pods and Containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) | +### SAML SSO + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| currents.sso.saml.enabled | bool | `false` | Enable SAML SSO. Requires `issuer` and `metadataSecretName` below (a K8s secret holding your IdP metadata XML). Turns on email-first SSO login. | +| currents.sso.saml.issuer | string | `""` | SP entityID / audience presented to your IdP. A stable opaque identifier (e.g. `currents-onprem:your-org`), NOT a URL. Must match the Audience / SP Entity ID configured in your IdP. | +| currents.sso.saml.providerId | string | `"onprem-saml"` | Provider id; recommend your IdP service name (e.g. `okta`). Becomes the last path segment of the ACS callback URL. | +| currents.sso.saml.metadataSecretName | string | `""` | K8s secret holding the IdP metadata XML (and any SP cert/key). Mounted read-only at /etc/currents/sso. See the SAML SSO guide for how to create it. | +| currents.sso.saml.metadataKey | string | `"idp-metadata.xml"` | The secret key (file name) of the IdP metadata XML | +| currents.sso.saml.defaultRole | string | `"member"` | Role granted to auto-provisioned SSO users | +| currents.sso.saml.allowedDomains | string | `""` | Comma-separated email domains allowed to sign in via SSO (optional allow-list) | +| currents.sso.saml.authnRequestsSigned | bool | `false` | Sign outbound AuthnRequests. Requires the SP cert/key (below) to be present in the same secret. | +| currents.sso.saml.spCertKey | string | `"sp-cert.pem"` | Secret key (file name) of the SP certificate PEM (only when authnRequestsSigned=true) | +| currents.sso.saml.spKeyKey | string | `"sp-key.pem"` | Secret key (file name) of the SP private key PEM (only when authnRequestsSigned=true) | + ### Other Values | Key | Type | Default | Description | diff --git a/docs/eks/quickstart.md b/docs/eks/quickstart.md index e1607cb..310e0d7 100644 --- a/docs/eks/quickstart.md +++ b/docs/eks/quickstart.md @@ -213,4 +213,54 @@ And you can have Currents Test reporters access the `director` DNS by setting th For example: ```sh CURRENTS_API_URL=https://currents-record.eks.example.com npx pwc --key --project-id -``` \ No newline at end of file +``` + +## Enable SAML SSO (optional) + +Delegate sign-in to your SAML 2.0 identity provider (Okta, Microsoft Entra ID, etc.). Users sign in **email-first** — they enter their email, are redirected to your IdP to authenticate, and are auto-provisioned into your organization on return. SMTP must be configured (used for provisioning and invitations). + +1. **Create the SAML app in your IdP.** Using Okta as the example, set: + - **Single Sign-On / ACS URL:** `https:///api/auth/sso/saml2/callback/okta` + — the last segment is your `providerId`; `` is your `currents.domains.appHost`. + - **Audience / SP Entity ID:** `currents-onprem:your-org` — a stable opaque identifier, **not a URL**. + - **Name ID format:** `Persistent` + - **Application username:** Email + + Then download the app's **IdP metadata XML** (it contains the IdP sign-on URL, entity ID, and signing certificate). + +2. **Create a secret from the metadata file:** + ```sh + kubectl create secret generic currents-sso-saml --from-file=idp-metadata.xml=./idp-metadata.xml + ``` + +3. **Enable SSO in your values file** (`currents-helm-config.yaml`, under `currents:`): + ```yaml + currents: + sso: + saml: + enabled: true + # Must match the Audience / SP Entity ID in your IdP (opaque, not a URL) + issuer: "currents-onprem:your-org" + # Your IdP name; the last path segment of the ACS callback URL + providerId: okta + metadataSecretName: currents-sso-saml + # Optional: + # defaultRole: member + # allowedDomains: "acme.com,acme.io" + ``` + +4. **Apply:** + ```sh + helm upgrade --install currents currents --repo https://currents-dev.github.io/helm-charts/ -f currents-helm-config.yaml + ``` + +Sign in from the dashboard by entering your email — you'll be redirected to your IdP. New SSO users join your organization automatically with the role from `defaultRole` (default `member`); the root admin continues to sign in with email + password. + +To rotate the IdP signing certificate, replace the secret with the refreshed metadata and restart the server pod: +```sh +kubectl create secret generic currents-sso-saml --from-file=idp-metadata.xml=./idp-metadata.xml \ + --dry-run=client -o yaml | kubectl apply -f - +kubectl rollout restart deploy -l app.kubernetes.io/component=server +``` + +See the [Configuration Reference](../configuration.md#saml-sso) for all SSO values. \ No newline at end of file diff --git a/samples/eks/eks-config.yaml b/samples/eks/eks-config.yaml index 818655a..85578bb 100644 --- a/samples/eks/eks-config.yaml +++ b/samples/eks/eks-config.yaml @@ -17,6 +17,13 @@ currents: secretName: currents-api-jwt-token apiInternalToken: secretName: currents-api-internal-token + # SAML SSO (optional) — see docs/eks/quickstart.md#enable-saml-sso-optional + # sso: + # saml: + # enabled: true + # issuer: "currents-onprem:your-org" # opaque id, must match the IdP Audience / SP Entity ID + # providerId: okta # your IdP name; last segment of the ACS callback URL + # metadataSecretName: currents-sso-saml # kubectl create secret generic ... --from-file=idp-metadata.xml=... gitlab: state: secretName: currents-gitlab-key diff --git a/samples/local/chart-config.yaml b/samples/local/chart-config.yaml index 5e79916..7877eb6 100644 --- a/samples/local/chart-config.yaml +++ b/samples/local/chart-config.yaml @@ -23,6 +23,13 @@ currents: secretName: currents-root-user apiInternalToken: secretName: currents-api-internal-token + # SAML SSO (optional) — see docs/eks/quickstart.md#enable-saml-sso-optional + # sso: + # saml: + # enabled: true + # issuer: "currents-onprem:your-org" # opaque id, must match the IdP Audience / SP Entity ID + # providerId: okta # your IdP name; last segment of the ACS callback URL + # metadataSecretName: currents-sso-saml # kubectl create secret generic ... --from-file=idp-metadata.xml=... gitlab: state: secretName: currents-gitlab-key From 79db2b890c335abc29404785fe9b11bd309cc790 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 8 Jul 2026 12:42:17 -0700 Subject: [PATCH 2/4] chore: Add MongoDB and ClickHouse dependencies for CI testing - Introduced a new dependencies.yaml file to deploy ephemeral MongoDB and ClickHouse instances for chart testing. - Updated lint-test.yaml to create the `currents-ci` namespace and apply the new dependencies, ensuring the app pods can reach the database services. - Enhanced lint-values.yaml to reference the new in-cluster MongoDB and ClickHouse configurations and secrets. --- .github/ci/dependencies.yaml | 95 +++++++++++++++++++++++++++++ .github/workflows/lint-test.yaml | 46 ++++++++++++-- charts/currents/ci/lint-values.yaml | 33 ++++++++++ 3 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 .github/ci/dependencies.yaml diff --git a/.github/ci/dependencies.yaml b/.github/ci/dependencies.yaml new file mode 100644 index 0000000..e7b7895 --- /dev/null +++ b/.github/ci/dependencies.yaml @@ -0,0 +1,95 @@ +# Ephemeral MongoDB (single-node replica set) + ClickHouse for the chart-testing +# install gate. Applied into the `currents-ci` namespace by lint-test.yaml; the +# chart's pods reach them via cluster DNS. NOT for production use — no auth on +# MongoDB, throwaway ClickHouse credentials. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mongodb + labels: + app: mongodb +spec: + replicas: 1 + selector: + matchLabels: + app: mongodb + template: + metadata: + labels: + app: mongodb + spec: + containers: + - name: mongodb + image: mongo:8 + args: ["--replSet", "rs0", "--bind_ip_all"] + ports: + - containerPort: 27017 + readinessProbe: + exec: + command: ["mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok"] + initialDelaySeconds: 5 + periodSeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: mongodb +spec: + selector: + app: mongodb + ports: + - port: 27017 + targetPort: 27017 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: clickhouse + labels: + app: clickhouse +spec: + replicas: 1 + selector: + matchLabels: + app: clickhouse + template: + metadata: + labels: + app: clickhouse + spec: + containers: + - name: clickhouse + image: clickhouse/clickhouse-server:25.6 + env: + - name: CLICKHOUSE_USER + value: currents + - name: CLICKHOUSE_PASSWORD + value: currents + - name: CLICKHOUSE_DB + value: currents + - name: CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT + value: "1" + ports: + - containerPort: 8123 + - containerPort: 9000 + readinessProbe: + httpGet: + path: /ping + port: 8123 + initialDelaySeconds: 5 + periodSeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: clickhouse +spec: + selector: + app: clickhouse + ports: + - name: http + port: 8123 + targetPort: 8123 + - name: native + port: 9000 + targetPort: 9000 diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index ae3aabf..272b676 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -69,11 +69,49 @@ jobs: | awk '$1 == "image:" { print $2 }' | tr -d '"' | grep '513558712013' | sort -u) echo "Preloading images into kind:" echo "$images" - for img in $images; do - docker pull "$img" - kind load docker-image "$img" --name chart-testing + # Pull all images concurrently (the slow, network-bound part) + echo "$images" | xargs -P 0 -n 1 docker pull + # Side-load them in one invocation (avoids concurrent containerd imports) + kind load docker-image $images --name chart-testing + + # The chart expects an external MongoDB (replica set — change streams need it) + # and ClickHouse. Stand up throwaway ones in-cluster so `ct install` can bring + # the app pods to readiness. They live in the `currents-ci` namespace that + # `ct install --namespace` targets, reachable via cluster DNS. + - name: Deploy test dependencies (MongoDB + ClickHouse) + if: steps.list-changed.outputs.changed == 'true' + run: | + kubectl create namespace currents-ci + kubectl apply -n currents-ci -f .github/ci/dependencies.yaml + kubectl -n currents-ci rollout status deploy/mongodb --timeout=180s + kubectl -n currents-ci rollout status deploy/clickhouse --timeout=180s + # Initialise the single-node replica set (|| true: idempotent on re-run) + kubectl -n currents-ci exec deploy/mongodb -- mongosh --quiet --eval \ + 'rs.initiate({_id:"rs0",members:[{_id:0,host:"mongodb.currents-ci.svc.cluster.local:27017"}]})' || true + echo "Waiting for the replica set to elect a PRIMARY..." + for i in $(seq 1 30); do + if kubectl -n currents-ci exec deploy/mongodb -- mongosh --quiet --eval 'db.hello().isWritablePrimary' 2>/dev/null | grep -q true; then + echo "MongoDB replica set is PRIMARY"; break + fi + echo " attempt $i/30..."; sleep 3 done + # The chart reads these via secretKeyRef; they must exist in the install + # namespace. Values are throwaway CI credentials. + - name: Create chart secrets + if: steps.list-changed.outputs.changed == 'true' + run: | + kubectl -n currents-ci create secret generic mongodb-currents-currents-user \ + --from-literal=connectionString.standardSrv='mongodb://mongodb.currents-ci.svc.cluster.local:27017/currents?replicaSet=rs0' + kubectl -n currents-ci create secret generic clickhouse-currents-pass \ + --from-literal=password=currents + kubectl -n currents-ci create secret generic currents-better-auth \ + --from-literal=secret="$(openssl rand -hex 32)" + kubectl -n currents-ci create secret generic currents-root-user \ + --from-literal=password="$(openssl rand -hex 16)" + kubectl -n currents-ci create secret generic currents-api-internal-token \ + --from-literal=token="$(openssl rand -hex 32)" + - name: Run chart-testing (install) if: steps.list-changed.outputs.changed == 'true' - run: ct install --target-branch ${{ github.event.repository.default_branch }} \ No newline at end of file + run: ct install --target-branch ${{ github.event.repository.default_branch }} --namespace currents-ci --helm-extra-args "--timeout 600s" \ No newline at end of file diff --git a/charts/currents/ci/lint-values.yaml b/charts/currents/ci/lint-values.yaml index 497df16..aa0ee90 100644 --- a/charts/currents/ci/lint-values.yaml +++ b/charts/currents/ci/lint-values.yaml @@ -1,8 +1,41 @@ +# Values used by chart-testing for BOTH `ct lint` and the `ct install` gate. +# The install gate (.github/workflows/lint-test.yaml) deploys MongoDB + ClickHouse +# and creates the referenced secrets in the `currents-ci` namespace before running +# `ct install --namespace currents-ci`, so these point at those in-cluster deps. currents: + # The API validates SMTP config exists; a dummy host is enough to boot. + email: + smtp: + host: localhost + rootUser: + password: + secretName: currents-root-user + key: password + betterAuth: + secretName: currents-better-auth + key: secret + apiInternalToken: + secretName: currents-api-internal-token + key: token mongoConnection: secretName: mongodb-currents-currents-user key: connectionString.standardSrv clickhouse: + host: clickhouse.currents-ci.svc.cluster.local + port: 8123 + tls: + enabled: false user: + username: currents secretName: clickhouse-currents-pass secretPasswordKey: password + +# Reaches every component (all deployments append global.env); the chart only sets +# MONGODB_URI, so name the database explicitly. +global: + env: + - name: MONGODB_DATABASE + value: currents + +redis: + enabled: true From cb74b09ee456318ae349fef9c14b8af796903c3d Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 8 Jul 2026 13:26:36 -0700 Subject: [PATCH 3/4] chore: Add Redis dependency and update CI configurations - Introduced Redis deployment and service in dependencies.yaml - Updated lint-test.yaml to include rollout status check - Modified lint-values.yaml to reference the new Redis configuration --- .github/ci/dependencies.yaml | 42 +++++++++++++++++++++++++++++ .github/workflows/lint-test.yaml | 3 +++ charts/currents/ci/lint-values.yaml | 12 ++++++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/.github/ci/dependencies.yaml b/.github/ci/dependencies.yaml index e7b7895..637a37f 100644 --- a/.github/ci/dependencies.yaml +++ b/.github/ci/dependencies.yaml @@ -29,6 +29,7 @@ spec: command: ["mongosh", "--quiet", "--eval", "db.adminCommand('ping').ok"] initialDelaySeconds: 5 periodSeconds: 5 + timeoutSeconds: 5 --- apiVersion: v1 kind: Service @@ -93,3 +94,44 @@ spec: - name: native port: 9000 targetPort: 9000 +--- +# redis-stack-server (the app uses RedisJSON). Pre-started here — instead of the +# Bitnami subchart — so it is Ready before the app pods start, avoiding the +# crash-loop race while the app retries an unavailable Redis. +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + labels: + app: redis +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + containers: + - name: redis + image: redis/redis-stack-server:7.4.0-v8 + ports: + - containerPort: 6379 + readinessProbe: + exec: + command: ["redis-cli", "ping"] + initialDelaySeconds: 3 + periodSeconds: 3 +--- +apiVersion: v1 +kind: Service +metadata: + name: redis +spec: + selector: + app: redis + ports: + - port: 6379 + targetPort: 6379 diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index 272b676..f691927 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -85,6 +85,7 @@ jobs: kubectl apply -n currents-ci -f .github/ci/dependencies.yaml kubectl -n currents-ci rollout status deploy/mongodb --timeout=180s kubectl -n currents-ci rollout status deploy/clickhouse --timeout=180s + kubectl -n currents-ci rollout status deploy/redis --timeout=180s # Initialise the single-node replica set (|| true: idempotent on re-run) kubectl -n currents-ci exec deploy/mongodb -- mongosh --quiet --eval \ 'rs.initiate({_id:"rs0",members:[{_id:0,host:"mongodb.currents-ci.svc.cluster.local:27017"}]})' || true @@ -111,6 +112,8 @@ jobs: --from-literal=password="$(openssl rand -hex 16)" kubectl -n currents-ci create secret generic currents-api-internal-token \ --from-literal=token="$(openssl rand -hex 32)" + kubectl -n currents-ci create secret generic currents-gitlab-key \ + --from-literal=gitlab-key.pem="$(openssl genrsa 2048 2>/dev/null)" - name: Run chart-testing (install) if: steps.list-changed.outputs.changed == 'true' diff --git a/charts/currents/ci/lint-values.yaml b/charts/currents/ci/lint-values.yaml index aa0ee90..84769b3 100644 --- a/charts/currents/ci/lint-values.yaml +++ b/charts/currents/ci/lint-values.yaml @@ -17,6 +17,11 @@ currents: apiInternalToken: secretName: currents-api-internal-token key: token + # The director validates GITLAB_STATE_SECRET is present at boot. + gitlab: + state: + secretName: currents-gitlab-key + secretKey: gitlab-key.pem mongoConnection: secretName: mongodb-currents-currents-user key: connectionString.standardSrv @@ -29,6 +34,10 @@ currents: username: currents secretName: clickhouse-currents-pass secretPasswordKey: password + # Use the pre-started in-cluster Redis (see .github/ci/dependencies.yaml) rather + # than the Bitnami subchart, so Redis is Ready before the app pods start. + redis: + host: redis.currents-ci.svc.cluster.local # Reaches every component (all deployments append global.env); the chart only sets # MONGODB_URI, so name the database explicitly. @@ -37,5 +46,6 @@ global: - name: MONGODB_DATABASE value: currents +# Subchart Redis disabled — we run our own (deployed and waited-for before install). redis: - enabled: true + enabled: false From 45f6e91bf2ba974b1945fadcf69bd572d19bd8b7 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 8 Jul 2026 13:43:20 -0700 Subject: [PATCH 4/4] chore: Refactor CI workflow and enhance image pulling mechanism - Removed the ECR login step and preloading of images into the kind cluster from the GitHub Actions workflow. - Added a new secret for ECR credentials to allow kind to pull private images directly. - Updated lint-values.yaml to include the new imagePullSecrets configuration. - Improved error handling for MongoDB replica set initialization in the CI workflow. - Ensured required fields for SAML configuration in deployment templates are validated. --- .github/workflows/lint-test.yaml | 36 +++++++------------ charts/currents/ci/lint-values.yaml | 4 +++ charts/currents/templates/_common.tpl | 2 +- .../currents/templates/server/deployment.yaml | 2 +- 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index f691927..6945b5e 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -48,32 +48,10 @@ jobs: role-to-assume: arn:aws:iam::761136292957:role/GitHubActions-CurrentsDevDocker-ECRPull aws-region: us-east-1 - - name: Login to Currents ECR - if: steps.list-changed.outputs.changed == 'true' - uses: aws-actions/amazon-ecr-login@v2 - with: - registries: "513558712013" - - name: Create kind cluster if: steps.list-changed.outputs.changed == 'true' uses: helm/kind-action@v1.14.0 - # The chart pulls private images from Currents' ECR. kind nodes don't share - # the runner's registry auth, so pull on the runner (authenticated above) and - # side-load into the cluster. imagePullPolicy defaults to IfNotPresent, so the - # pods use these preloaded images without needing an in-cluster pull secret. - - name: Preload Currents images into kind - if: steps.list-changed.outputs.changed == 'true' - run: | - images=$(helm template ct-release charts/currents -f charts/currents/ci/lint-values.yaml \ - | awk '$1 == "image:" { print $2 }' | tr -d '"' | grep '513558712013' | sort -u) - echo "Preloading images into kind:" - echo "$images" - # Pull all images concurrently (the slow, network-bound part) - echo "$images" | xargs -P 0 -n 1 docker pull - # Side-load them in one invocation (avoids concurrent containerd imports) - kind load docker-image $images --name chart-testing - # The chart expects an external MongoDB (replica set — change streams need it) # and ClickHouse. Stand up throwaway ones in-cluster so `ct install` can bring # the app pods to readiness. They live in the `currents-ci` namespace that @@ -90,12 +68,18 @@ jobs: kubectl -n currents-ci exec deploy/mongodb -- mongosh --quiet --eval \ 'rs.initiate({_id:"rs0",members:[{_id:0,host:"mongodb.currents-ci.svc.cluster.local:27017"}]})' || true echo "Waiting for the replica set to elect a PRIMARY..." + primary=false for i in $(seq 1 30); do if kubectl -n currents-ci exec deploy/mongodb -- mongosh --quiet --eval 'db.hello().isWritablePrimary' 2>/dev/null | grep -q true; then - echo "MongoDB replica set is PRIMARY"; break + primary=true; echo "MongoDB replica set is PRIMARY"; break fi echo " attempt $i/30..."; sleep 3 done + if [ "$primary" != true ]; then + echo "::error::MongoDB replica set did not reach PRIMARY after 30 attempts" + kubectl -n currents-ci exec deploy/mongodb -- mongosh --quiet --eval 'rs.status()' || true + exit 1 + fi # The chart reads these via secretKeyRef; they must exist in the install # namespace. Values are throwaway CI credentials. @@ -114,6 +98,12 @@ jobs: --from-literal=token="$(openssl rand -hex 32)" kubectl -n currents-ci create secret generic currents-gitlab-key \ --from-literal=gitlab-key.pem="$(openssl genrsa 2048 2>/dev/null)" + # Short-lived ECR pull token (valid ~12h) so kind's kubelet pulls the + # private images directly, instead of duplicating a runner pull + kind load. + kubectl -n currents-ci create secret docker-registry ecr-creds \ + --docker-server=513558712013.dkr.ecr.us-east-1.amazonaws.com \ + --docker-username=AWS \ + --docker-password="$(aws ecr get-login-password --region us-east-1)" - name: Run chart-testing (install) if: steps.list-changed.outputs.changed == 'true' diff --git a/charts/currents/ci/lint-values.yaml b/charts/currents/ci/lint-values.yaml index 84769b3..27edab9 100644 --- a/charts/currents/ci/lint-values.yaml +++ b/charts/currents/ci/lint-values.yaml @@ -42,6 +42,10 @@ currents: # Reaches every component (all deployments append global.env); the chart only sets # MONGODB_URI, so name the database explicitly. global: + # kind pulls the private ECR images directly using this pre-created secret + # (see the "Create chart secrets" step in .github/workflows/lint-test.yaml). + imagePullSecrets: + - name: ecr-creds env: - name: MONGODB_DATABASE value: currents diff --git a/charts/currents/templates/_common.tpl b/charts/currents/templates/_common.tpl index 3f0da3a..0b6368f 100644 --- a/charts/currents/templates/_common.tpl +++ b/charts/currents/templates/_common.tpl @@ -239,7 +239,7 @@ Create the name of the service account to use - name: SSO_SAML_IDP_METADATA_FILE value: {{ printf "/etc/currents/sso/%s" .Values.currents.sso.saml.metadataKey | quote }} - name: SSO_SAML_ISSUER - value: {{ .Values.currents.sso.saml.issuer | quote }} + value: {{ required "currents.sso.saml.issuer is required when currents.sso.saml.enabled is true" .Values.currents.sso.saml.issuer | quote }} {{- if .Values.currents.sso.saml.providerId }} - name: SSO_SAML_PROVIDER_ID value: {{ .Values.currents.sso.saml.providerId | quote }} diff --git a/charts/currents/templates/server/deployment.yaml b/charts/currents/templates/server/deployment.yaml index 2843370..942945b 100644 --- a/charts/currents/templates/server/deployment.yaml +++ b/charts/currents/templates/server/deployment.yaml @@ -125,7 +125,7 @@ spec: {{- if .Values.currents.sso.saml.enabled }} - name: sso-saml secret: - secretName: {{ .Values.currents.sso.saml.metadataSecretName }} + secretName: {{ required "currents.sso.saml.metadataSecretName is required when currents.sso.saml.enabled is true" .Values.currents.sso.saml.metadataSecretName }} {{- end }} {{- with .Values.global.volumes }} {{- toYaml . | nindent 8 }}