diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0aaab4a..c60f06e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,6 +87,9 @@ jobs: - name: Install dependencies run: uv sync --all-extras + - name: Set up providers + run: uv run ref providers setup + - name: Fetch test data run: | mkdir -p "$REF_TEST_DATA_DIR" diff --git a/.github/workflows/packaging.yaml b/.github/workflows/packaging.yaml new file mode 100644 index 0000000..1f539c5 --- /dev/null +++ b/.github/workflows/packaging.yaml @@ -0,0 +1,172 @@ +name: Packaging + +on: + pull_request: + workflow_dispatch: + push: + branches: + - "main" + tags: + - "v*" + +permissions: + contents: read + packages: write + attestations: write + id-token: write + +jobs: + changes: + name: Detect Changes + runs-on: ubuntu-latest + outputs: + helm: ${{ steps.filter.outputs.helm }} + steps: + - uses: actions/checkout@v6 + # - uses: dorny/paths-filter@v3 + # id: filter + # with: + # filters: | + # helm: + # - 'helm/**' + + + helm: + name: Helm Chart + runs-on: ubuntu-latest + needs: [changes] + if: needs.changes.outputs.helm == 'true' || github.event_name != 'pull_request' + permissions: + packages: write + outputs: + generated-semver: ${{ steps.semantic-version.outputs.generated-semver }} + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + - name: Install jq + run: | + sudo apt-get install --yes jq + - name: Install yq + run: | + pip install yq + - name: Generate SemVer + id: semantic-version + run: | + CHART_VERSION=$(yq -r '.version' helm/Chart.yaml) + if [ "${{ github.event_name }}" = "pull_request" ]; then + LOCAL_SEGMENT=+pr-${{ github.event.pull_request.number }} + elif [ "${{ github.ref_type }}" = "tag" ]; then + LOCAL_SEGMENT="" + else + SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) + LOCAL_SEGMENT=+${SHORT_SHA} + fi + GENERATED_VERSION=${CHART_VERSION}${LOCAL_SEGMENT} + yq -Y -i ".version = \"$GENERATED_VERSION\"" helm/Chart.yaml + echo "generated-semver=$GENERATED_VERSION" >> $GITHUB_OUTPUT + - name: Chart | Push + uses: appany/helm-oci-chart-releaser@v0.5.0 + with: + name: climate-ref-aft + repository: climate-ref/charts + tag: ${{ steps.semantic-version.outputs.generated-semver }} + path: helm + registry: ghcr.io + registry_username: ${{ github.actor }} + registry_password: ${{ secrets.GITHUB_TOKEN }} + update_dependencies: 'true' + + test: + name: Test Helm Deployment + runs-on: ubuntu-latest + timeout-minutes: 30 + if: github.event_name == 'pull_request' && needs.changes.outputs.helm == 'true' + needs: [changes, helm] + steps: + - uses: actions/checkout@v6 + - name: Cache Sample Data (Restore) + id: cache-sample-data-restore + uses: actions/cache/restore@v5 + with: + path: ${{ github.workspace }}/cache/ref-config + key: ${{ runner.os }}-sample-data + enableCrossOsArchive: true + - name: Set permissions for cached data + run: | + sudo install -d --owner=1000 --group=1000 ${GITHUB_WORKSPACE}/cache/ref-config + - name: Start minikube + uses: medyagh/setup-minikube@latest + with: + mount-path: '${{ github.workspace }}/cache/ref-config:/cache/ref-config' + - name: Set up Helm + uses: azure/setup-helm@v4.3.1 + - name: Install Chart + run: | + helm install test oci://ghcr.io/climate-ref/charts/climate-ref-aft \ + --version=${{ needs.helm.outputs.generated-semver }} \ + --set defaults.image.tag=pr-${{ github.event.pull_request.number }} \ + -f helm/ci/gh-actions-values.yaml + + sleep 60 + kubectl get pods + echo "" + kubectl describe pod -l app.kubernetes.io/component=pmp + echo "" + kubectl logs -l app.kubernetes.io/component=pmp + - name: Run Migrations + run: | + kubectl exec deployment/test-climate-ref-aft-orchestrator -- ref config list + - name: Initialize Providers + run: | + # Imports ilamb3 which tries to create /home/app/.config/ilamb3 on import, no way to tell it to live somewhere else + # First, set up all providers without fetching data (handles conda envs) + kubectl exec deployment/test-climate-ref-aft-orchestrator -- ref providers setup --skip-data --skip-validate + # Fetch data for providers except esmvaltool (ERA5 data is too large for CI) + kubectl exec deployment/test-climate-ref-aft-orchestrator -- ref providers setup --provider pmp + kubectl exec deployment/test-climate-ref-aft-orchestrator -- ref providers setup --provider ilamb + - name: Fetch Test Data + run: | + kubectl exec deployment/test-climate-ref-aft-orchestrator -- ref datasets fetch-data --registry sample-data --output-directory /ref/sample-data + + - name: Cache Sample Data (Save) + uses: actions/cache/save@v5 + with: + path: ${{ github.workspace }}/cache/ref-config + key: ${{ runner.os }}-sample-data + + - name: Ingest Test Data (CMIP6) + run: | + kubectl exec deployment/test-climate-ref-aft-orchestrator -- ref -v datasets ingest --source-type cmip6 /ref/sample-data/CMIP6 + - name: Ingest Test Data (obs4mips) + run: | + kubectl exec deployment/test-climate-ref-aft-orchestrator -- ref -v datasets ingest --source-type obs4mips /ref/sample-data/obs4REF + - name: Simple Solve + run: | + # Use a fixed set of fast diagnostics to keep CI times predictable + kubectl exec deployment/test-climate-ref-aft-orchestrator -- ref -v solve --timeout 720 --one-per-provider \ + --diagnostic global-mean-timeseries \ + --diagnostic annual-cycle \ + --diagnostic gpp-wecann + - name: Capture Worker Logs on Failure + if: failure() + run: | + echo "=== PMP Worker Logs ===" + kubectl logs -l app.kubernetes.io/component=pmp --tail=500 || true + echo "" + echo "=== ESMValTool Worker Logs ===" + kubectl logs -l app.kubernetes.io/component=esmvaltool --tail=500 || true + echo "" + echo "=== ILAMB Worker Logs ===" + kubectl logs -l app.kubernetes.io/component=ilamb --tail=500 || true + echo "" + echo "=== Example Worker Logs ===" + kubectl logs -l app.kubernetes.io/component=example --tail=500 || true + echo "" + echo "=== Orchestrator Worker Logs ===" + kubectl logs -l app.kubernetes.io/component=orchestrator --tail=500 || true + echo "" + echo "=== Flower Logs ===" + kubectl logs -l app.kubernetes.io/component=flower --tail=500 || true + echo "" + echo "=== Dragonfly Logs ===" + kubectl logs -l app.kubernetes.io/name=dragonfly --tail=500 || true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a2150f..d6718d9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: - name: Push chart run: | CHART_VERSION=$(grep '^version:' helm/Chart.yaml | awk '{print $2}') - helm push ref-${CHART_VERSION}.tgz oci://ghcr.io/climate-ref/charts + helm push climate-ref-aft-${CHART_VERSION}.tgz oci://ghcr.io/climate-ref/charts release: name: GitHub Release diff --git a/README.md b/README.md index 61f9527..e83d40e 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,11 @@ The AFT deployment brings together independently versioned packages: | `climate-ref-core` | [Climate-REF/climate-ref](https://github.com/Climate-REF/climate-ref) | Core library with base classes and interfaces | | `climate-ref` | [Climate-REF/climate-ref](https://github.com/Climate-REF/climate-ref) | Main application, CLI, database, solver | | `climate-ref-celery` | [Climate-REF/climate-ref](https://github.com/Climate-REF/climate-ref) | Celery executor for distributed execution | -| `climate-ref-esmvaltool` | [Climate-REF/climate-ref-esmvaltool](https://github.com/Climate-REF/climate-ref-esmvaltool) | ESMValTool diagnostic provider | -| `climate-ref-pmp` | [Climate-REF/climate-ref-pmp](https://github.com/Climate-REF/climate-ref-pmp) | PCMDI Metrics Package diagnostic provider | -| `climate-ref-ilamb` | [Climate-REF/climate-ref-ilamb](https://github.com/Climate-REF/climate-ref-ilamb) | ILAMB diagnostic provider | +| `climate-ref-esmvaltool` | [Climate-REF/climate-ref](https://github.com/Climate-REF/climate-ref) | ESMValTool diagnostic provider | +| `climate-ref-pmp` | [Climate-REF/climate-ref](https://github.com/Climate-REF/climate-ref) | PCMDI Metrics Package diagnostic provider | +| `climate-ref-ilamb` | [Climate-REF/climate-ref](https://github.com/Climate-REF/climate-ref) | ILAMB diagnostic provider | + +Note: we intend to split the providers out into their own repositories in the coming weeks. ## Versioning @@ -53,7 +55,7 @@ bash scripts/smoke-test.sh helm install ref ./helm -f helm/local-test-values.yaml # Or from the OCI registry -helm install ref oci://ghcr.io/climate-ref/charts/ref --version 0.9.1 +helm install ref oci://ghcr.io/climate-ref/charts/climate-ref-aft --version 0.9.1 ``` ### Integration Tests @@ -74,6 +76,7 @@ uv run pytest tests/ -v --slow | Workflow | Trigger | What It Does | |----------|---------|--------------| | `ci.yml` | Push, PR | Lint, install pinned versions, run integration tests | +| `packaging.yaml` | Push, PR | Helm chart OCI publish and minikube deployment test | | `nightly.yml` | Scheduled (daily) | Test against latest versions of all components | | `release.yml` | Tag push | Publish Helm chart, create GitHub release | diff --git a/changelog/3.feature.md b/changelog/3.feature.md new file mode 100644 index 0000000..0d9c409 --- /dev/null +++ b/changelog/3.feature.md @@ -0,0 +1 @@ +Add ref-app API component and Gateway API HTTPRoute support to the Helm chart. diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 8b074ae..e5ad9fe 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -26,6 +26,21 @@ services: ports: - "5432:5432" + ref-app: + image: ghcr.io/climate-ref/climate-ref-frontend:main + platform: linux/amd64 + restart: always + depends_on: + - postgres + - redis + environment: + - REF_DATABASE_URL=postgresql://postgres:example@postgres:5432/postgres + - ENVIRONMENT=local + - SECRET_KEY=local-dev-secret + - REF_CONFIGURATION=/app/.ref + ports: + - "8000:8000" + flower: image: mher/flower:2.0.1 restart: always @@ -40,7 +55,8 @@ services: # Base worker to track the executions of async tasks climate-ref: - image: ghcr.io/climate-ref/climate-ref:latest + image: ghcr.io/climate-ref/climate-ref:main + platform: linux/amd64 restart: always depends_on: - postgres diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 016dc29..84a37e6 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 -name: ref -description: A Helm chart for Kubernetes +name: climate-ref-aft +description: A Helm chart for deploying the Climate REF application for the CMIP7 Assessment Fasttrack (AFT) type: application # This is the chart version. This version number should be incremented each time you make changes diff --git a/helm/README.md b/helm/README.md index 405e3d7..573af41 100644 --- a/helm/README.md +++ b/helm/README.md @@ -1,12 +1,17 @@ # Climate REF Helm Chart A Helm chart for deploying the Climate REF (Rapid Evaluation Framework) on Kubernetes. -This chart deploys a distributed task execution system using Celery workers for running climate diagnostics. +This chart deploys the full stack: + +- the ref-app API/website +- celery monitoring UI +- distributed Celery workers for running climate diagnostics. ## Overview The chart deploys: +- **ref-app (API)**: FastAPI application serving the REST API and static React frontend - **Dragonfly** (Redis-compatible): Message broker and result backend for Celery - **Flower**: Web UI for monitoring Celery tasks - **Provider Workers**: Celery workers for each diagnostic provider (orchestrator, esmvaltool, pmp, ilamb, example) @@ -16,6 +21,7 @@ The chart deploys: - Kubernetes 1.19+ - Helm 3.0+ - Access to container images: + - `ghcr.io/climate-ref/ref-app` - `ghcr.io/climate-ref/climate-ref` - `mher/flower` @@ -57,9 +63,12 @@ helm dependency update ```mermaid flowchart TB - ingress[Ingress
optional] + apiIngress[API Ingress
optional] + flowerIngress[Flower Ingress
optional] + api[ref-app
API + frontend] flower[Flower
monitoring] dragonfly[Dragonfly
Redis broker] + db[(PostgreSQL
external)] subgraph workers[Provider Workers] orchestrator[Orchestrator
Worker] @@ -70,7 +79,9 @@ flowchart TB pvcs[(PVCs
shared data storage)] - ingress --> flower + apiIngress --> api + flowerIngress --> flower + api --> db flower --> dragonfly dragonfly --> orchestrator dragonfly --> esmvaltool @@ -104,15 +115,55 @@ Each provider worker listens to a specific Celery queue: | `nameOverride` | Override chart name | `""` | | `fullnameOverride` | Override full release name | `""` | -### Ingress Configuration - -| Parameter | Description | Default | -| --------------------- | ------------------- | ------- | -| `ingress.enabled` | Enable ingress | `false` | -| `ingress.host` | Ingress hostname | `""` | -| `ingress.className` | Ingress class name | `""` | -| `ingress.annotations` | Ingress annotations | `{}` | -| `ingress.labels` | Ingress labels | `{}` | +### API Configuration + +The `api` section configures the ref-app (FastAPI + React frontend). + +| Parameter | Description | Default | +| --------------------------------- | --------------------------- | --------------------------------- | +| `api.enabled` | Enable the API deployment | `true` | +| `api.replicaCount` | Number of API replicas | `1` | +| `api.image.repository` | API image repository | `ghcr.io/climate-ref/ref-app` | +| `api.image.tag` | API image tag | `latest` | +| `api.image.pullPolicy` | Image pull policy | `IfNotPresent` | +| `api.service.type` | Service type | `ClusterIP` | +| `api.service.port` | Service port | `80` | +| `api.resources` | Resource requests/limits | `{}` | +| `api.nodeSelector` | Node selector | `{}` | +| `api.tolerations` | Tolerations | `[]` | +| `api.affinity` | Affinity rules | `{}` | + +#### API Environment Variables + +Set via `api.env`: + +| Variable | Description | Default | +| ------------------- | ------------------------------ | --------------------------------- | +| `ENVIRONMENT` | Runtime environment | `production` | +| `LOG_LEVEL` | Logging level | `INFO` | +| `SECRET_KEY` | Application secret key | `changethis` (override in prod!) | +| `REF_DATABASE_URL` | Database connection string | `""` (required) | +| `REF_CONFIGURATION` | Path to REF configuration | `/app/.ref` | + +#### API Ingress + +| Parameter | Description | Default | +| -------------------------- | ------------------- | ------- | +| `api.ingress.enabled` | Enable API ingress | `false` | +| `api.ingress.host` | Ingress hostname | `""` | +| `api.ingress.className` | Ingress class name | `""` | +| `api.ingress.annotations` | Ingress annotations | `{}` | +| `api.ingress.labels` | Ingress labels | `{}` | + +#### API HTTPRoute (Gateway API) + +| Parameter | Description | Default | +| ---------------------------- | ---------------------------- | ------- | +| `api.httpRoute.enabled` | Enable API HTTPRoute | `false` | +| `api.httpRoute.hostnames` | List of hostnames to match | `[]` | +| `api.httpRoute.parentRefs` | Gateway parent references | `[]` | +| `api.httpRoute.annotations` | HTTPRoute annotations | `{}` | +| `api.httpRoute.labels` | HTTPRoute labels | `{}` | ### Dragonfly (Redis) Configuration @@ -139,6 +190,26 @@ See [Dragonfly Helm chart](https://github.com/dragonflydb/dragonfly/tree/main/co | `flower.tolerations` | Tolerations | `[]` | | `flower.affinity` | Affinity rules | `{}` | +#### Flower Ingress + +| Parameter | Description | Default | +| ----------------------------- | ----------------------- | ------- | +| `flower.ingress.enabled` | Enable Flower ingress | `false` | +| `flower.ingress.host` | Ingress hostname | `""` | +| `flower.ingress.className` | Ingress class name | `""` | +| `flower.ingress.annotations` | Ingress annotations | `{}` | +| `flower.ingress.labels` | Ingress labels | `{}` | + +#### Flower HTTPRoute (Gateway API) + +| Parameter | Description | Default | +| ------------------------------- | ---------------------------- | ------- | +| `flower.httpRoute.enabled` | Enable Flower HTTPRoute | `false` | +| `flower.httpRoute.hostnames` | List of hostnames to match | `[]` | +| `flower.httpRoute.parentRefs` | Gateway parent references | `[]` | +| `flower.httpRoute.annotations` | HTTPRoute annotations | `{}` | +| `flower.httpRoute.labels` | HTTPRoute labels | `{}` | + ### Provider Defaults These defaults apply to all providers unless overridden per-provider. @@ -279,17 +350,32 @@ kubectl port-forward svc/ref-flower 5555:5555 Open in your browser. +### Accessing the API + +Access the ref-app API: + +```bash +kubectl port-forward svc/ref-api 8000:80 +``` + +Open in your browser, or check the health endpoint: + +```bash +curl http://localhost:8000/api/v1/utils/health-check/ +``` + ## Resources Created The chart creates the following Kubernetes resources: -| Resource | Count | Description | -| ----------------------- | --------------- | -------------------------------- | -| Deployment | 1 + N providers | Flower + one per provider | -| Service | 2 | Flower + Dragonfly | -| ServiceAccount | 1 + N providers | Flower + one per provider | -| Secret | 1 + N providers | Environment config per component | -| ServiceMonitor | 0-1 | Optional Prometheus integration | -| HorizontalPodAutoscaler | 0-N | Optional per-provider | -| PersistentVolumeClaim | N | As configured in createPVCs | -| Ingress | 0-1 | Optional | +| Resource | Count | Description | +| ----------------------- | --------------- | ---------------------------------- | +| Deployment | 2 + N providers | API + Flower + one per provider | +| Service | 3 | API + Flower + Dragonfly | +| ServiceAccount | 2 + N providers | API + Flower + one per provider | +| Secret | 2 + N providers | Environment config per component | +| ServiceMonitor | 0-1 | Optional Prometheus integration | +| HorizontalPodAutoscaler | 0-N | Optional per-provider | +| PersistentVolumeClaim | N | As configured in createPVCs | +| Ingress | 0-2 | Optional API and/or Flower ingress | +| HTTPRoute | 0-2 | Optional Gateway API routes | diff --git a/helm/ci/gh-actions-values.yaml b/helm/ci/gh-actions-values.yaml index c13a40a..d7318ea 100644 --- a/helm/ci/gh-actions-values.yaml +++ b/helm/ci/gh-actions-values.yaml @@ -1,5 +1,14 @@ -ingress: - host: Climate-ref.test +api: + env: + SECRET_KEY: ci-test-secret + REF_DATABASE_URL: sqlite:////tmp/ref.db + volumes: + - name: tmp + emptyDir: {} + volumeMounts: + - name: tmp + mountPath: /tmp + readOnly: false service: type: NodePort diff --git a/helm/local-test-values.yaml b/helm/local-test-values.yaml index 8809004..44ceebe 100644 --- a/helm/local-test-values.yaml +++ b/helm/local-test-values.yaml @@ -4,7 +4,7 @@ defaults: image: repository: ghcr.io/climate-ref/climate-ref pullPolicy: IfNotPresent - tag: pr-492 # Using current PR tag + tag: latest podSecurityContext: runAsUser: 1000 @@ -52,3 +52,19 @@ providers: pmp: {} ilamb: {} example: {} + +api: + image: + tag: latest + env: + ENVIRONMENT: local + SECRET_KEY: local-test-secret + REF_DATABASE_URL: sqlite:////tmp/ref.db + REF_CONFIGURATION: /app/.ref + volumes: + - name: tmp + emptyDir: {} + volumeMounts: + - name: tmp + mountPath: /tmp + readOnly: false diff --git a/helm/templates/api/deployment.yaml b/helm/templates/api/deployment.yaml new file mode 100644 index 0000000..162c035 --- /dev/null +++ b/helm/templates/api/deployment.yaml @@ -0,0 +1,80 @@ +{{- if .Values.api.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "ref.fullname" . }}-api + labels: + app.kubernetes.io/component: api + {{- include "ref.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.api.replicaCount }} + selector: + matchLabels: + app.kubernetes.io/component: api + {{- include "ref.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/api/secret.yaml") . | sha256sum }} + {{- with .Values.api.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + app.kubernetes.io/component: api + {{- include "ref.labels" . | nindent 8 }} + {{- with .Values.api.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "ref.fullname" . }}-api + {{- with .Values.api.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: api + {{- with .Values.api.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: "{{ .Values.api.image.repository }}:{{ .Values.api.image.tag }}" + imagePullPolicy: {{ .Values.api.image.pullPolicy }} + envFrom: + - secretRef: + name: {{ include "ref.fullname" . }}-api + ports: + - name: http + containerPort: 8000 + readinessProbe: + httpGet: + path: /api/v1/utils/health-check/ + port: http + {{- with .Values.api.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.api.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.api.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.api.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.api.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.api.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +{{- end }} diff --git a/helm/templates/api/httproute.yaml b/helm/templates/api/httproute.yaml new file mode 100644 index 0000000..6fec81e --- /dev/null +++ b/helm/templates/api/httproute.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.api.enabled .Values.api.httpRoute.enabled -}} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "ref.fullname" . }}-api + labels: + app.kubernetes.io/component: api + {{- include "ref.labels" . | nindent 4 }} + {{- with .Values.api.httpRoute.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.api.httpRoute.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.api.httpRoute.parentRefs }} + parentRefs: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.api.httpRoute.hostnames }} + hostnames: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: {{ include "ref.fullname" . }}-api + port: {{ .Values.api.service.port }} +{{- end }} diff --git a/helm/templates/api/ingress.yaml b/helm/templates/api/ingress.yaml new file mode 100644 index 0000000..b3f1058 --- /dev/null +++ b/helm/templates/api/ingress.yaml @@ -0,0 +1,35 @@ +{{- if and .Values.api.enabled .Values.api.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "ref.fullname" . }}-api + labels: + app.kubernetes.io/component: api + {{- include "ref.labels" . | nindent 4 }} + {{- with .Values.api.ingress.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.api.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.api.ingress.className }} + ingressClassName: {{ . }} + {{- end }} + tls: + - hosts: + - {{ .Values.api.ingress.host | quote }} + secretName: {{ include "ref.fullname" . }}-api-ingress-cert + rules: + - host: {{ .Values.api.ingress.host | quote }} + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: {{ include "ref.fullname" . }}-api + port: + number: {{ .Values.api.service.port }} +{{- end }} diff --git a/helm/templates/api/secret.yaml b/helm/templates/api/secret.yaml new file mode 100644 index 0000000..a3b831f --- /dev/null +++ b/helm/templates/api/secret.yaml @@ -0,0 +1,11 @@ +{{- if .Values.api.enabled -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "ref.fullname" . }}-api + labels: + app.kubernetes.io/component: api + {{- include "ref.labels" . | nindent 4 }} +stringData: + {{- tpl (toYaml .Values.api.env) . | nindent 2}} +{{- end }} diff --git a/helm/templates/api/service.yaml b/helm/templates/api/service.yaml new file mode 100644 index 0000000..0452521 --- /dev/null +++ b/helm/templates/api/service.yaml @@ -0,0 +1,19 @@ +{{- if .Values.api.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ref.fullname" . }}-api + labels: + app.kubernetes.io/component: api + {{- include "ref.labels" . | nindent 4 }} +spec: + type: {{ .Values.api.service.type }} + ports: + - port: {{ .Values.api.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/component: api + {{- include "ref.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/helm/templates/api/serviceaccount.yaml b/helm/templates/api/serviceaccount.yaml new file mode 100644 index 0000000..074d043 --- /dev/null +++ b/helm/templates/api/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.api.enabled .Values.api.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "ref.fullname" . }}-api + labels: + app.kubernetes.io/component: api + {{- include "ref.labels" . | nindent 4 }} + {{- with .Values.api.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.api.serviceAccount.automount }} +{{- end }} diff --git a/helm/templates/flower/httproute.yaml b/helm/templates/flower/httproute.yaml new file mode 100644 index 0000000..6f194a0 --- /dev/null +++ b/helm/templates/flower/httproute.yaml @@ -0,0 +1,33 @@ +{{- if .Values.flower.httpRoute.enabled -}} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "ref.fullname" . }}-flower + labels: + app.kubernetes.io/component: flower + {{- include "ref.labels" . | nindent 4 }} + {{- with .Values.flower.httpRoute.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.flower.httpRoute.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.flower.httpRoute.parentRefs }} + parentRefs: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.flower.httpRoute.hostnames }} + hostnames: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: {{ include "ref.fullname" . }}-flower + port: {{ .Values.flower.service.port }} +{{- end }} diff --git a/helm/templates/ingress.yaml b/helm/templates/flower/ingress.yaml similarity index 55% rename from helm/templates/ingress.yaml rename to helm/templates/flower/ingress.yaml index 4e87201..fa83e3f 100644 --- a/helm/templates/ingress.yaml +++ b/helm/templates/flower/ingress.yaml @@ -1,27 +1,28 @@ -{{- if .Values.ingress.enabled -}} +{{- if .Values.flower.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: {{ include "ref.fullname" . }} + name: {{ include "ref.fullname" . }}-flower labels: + app.kubernetes.io/component: flower {{- include "ref.labels" . | nindent 4 }} - {{- with .Values.ingress.labels }} + {{- with .Values.flower.ingress.labels }} {{- toYaml . | nindent 4 }} {{- end }} - {{- with .Values.ingress.annotations }} + {{- with .Values.flower.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- with .Values.ingress.className }} + {{- with .Values.flower.ingress.className }} ingressClassName: {{ . }} {{- end }} tls: - hosts: - - {{ .Values.ingress.host | quote }} - secretName: {{ include "ref.fullname" . }}-ingress-cert + - {{ .Values.flower.ingress.host | quote }} + secretName: {{ include "ref.fullname" . }}-flower-ingress-cert rules: - - host: {{ .Values.ingress.host | quote }} + - host: {{ .Values.flower.ingress.host | quote }} http: paths: - path: / diff --git a/helm/values.yaml b/helm/values.yaml index b084e9b..ee70d0f 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -2,14 +2,65 @@ imagePullSecrets: [] nameOverride: "" fullnameOverride: "" -ingress: - enabled: false - host: - className: "" - annotations: {} - # kubernetes.io/ingress.class: nginx - # kubernetes.io/tls-acme: "true" - labels: {} +api: + enabled: true + replicaCount: 1 + + image: + repository: ghcr.io/climate-ref/climate-ref-frontend + pullPolicy: IfNotPresent + tag: main + + env: + ENVIRONMENT: production + LOG_LEVEL: INFO + SECRET_KEY: changethis # Must override in production + REF_DATABASE_URL: "" # Required: e.g. postgresql://user:pass@host:5432/db + REF_CONFIGURATION: /app/.ref + + ingress: + enabled: false + host: + className: "" + annotations: {} + labels: {} + + httpRoute: + enabled: false + hostnames: [] + parentRefs: [] + annotations: {} + labels: {} + + serviceAccount: + create: true + automount: false + annotations: {} + name: "" + + podAnnotations: {} + podLabels: {} + + podSecurityContext: {} + + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + + service: + type: ClusterIP + port: 80 + + resources: {} + + volumes: [] + volumeMounts: [] + nodeSelector: {} + tolerations: [] + affinity: {} dragonfly: storage: @@ -20,6 +71,20 @@ flower: CELERY_BROKER_URL: redis://{{ include "dragonfly.fullname" .Subcharts.dragonfly }}:{{ .Values.dragonfly.service.port }} CELERY_RESULT_BACKEND: redis://{{ include "dragonfly.fullname" .Subcharts.dragonfly }}:{{ .Values.dragonfly.service.port }} + ingress: + enabled: false + host: + className: "" + annotations: {} + labels: {} + + httpRoute: + enabled: false + hostnames: [] + parentRefs: [] + annotations: {} + labels: {} + serviceMonitor: enabled: false