[wip]helmchart
Some checks failed
CI / Rustfmt (push) Successful in 1m30s
Publish Images And Chart / Resolve Publish Metadata (push) Failing after 2s
Publish Images And Chart / Publish init-packs (push) Has been skipped
Publish Images And Chart / Publish init-user (push) Has been skipped
Publish Images And Chart / Publish migrations (push) Has been skipped
Publish Images And Chart / Publish sensor (push) Has been skipped
Publish Images And Chart / Publish web (push) Has been skipped
Publish Images And Chart / Publish worker (push) Has been skipped
Publish Images And Chart / Publish api (push) Has been skipped
Publish Images And Chart / Publish executor (push) Has been skipped
Publish Images And Chart / Publish notifier (push) Has been skipped
Publish Images And Chart / Publish Helm Chart (push) Has been skipped
CI / Web Blocking Checks (push) Successful in 1m55s
CI / Security Advisory Checks (push) Failing after 13m14s
CI / Web Advisory Checks (push) Failing after 13m20s
CI / Security Blocking Checks (push) Failing after 13m31s
CI / Cargo Audit & Deny (push) Failing after 14m51s
CI / Tests (push) Failing after 14m53s
CI / Clippy (push) Failing after 14m59s

This commit is contained in:
2026-03-14 18:11:10 -05:00
parent 6307888722
commit 6a86dd7ca6
16 changed files with 1733 additions and 0 deletions

View File

@@ -0,0 +1,252 @@
name: Publish Images And Chart
on:
workflow_dispatch:
push:
branches:
- main
- master
tags:
- "v*"
env:
REGISTRY_HOST: ${{ vars.GITEA_REGISTRY_HOST }}
REGISTRY_NAMESPACE: ${{ vars.GITEA_REGISTRY_NAMESPACE }}
CHART_NAME: attune
jobs:
metadata:
name: Resolve Publish Metadata
runs-on: ubuntu-latest
outputs:
registry: ${{ steps.meta.outputs.registry }}
namespace: ${{ steps.meta.outputs.namespace }}
image_tag: ${{ steps.meta.outputs.image_tag }}
image_tags: ${{ steps.meta.outputs.image_tags }}
chart_version: ${{ steps.meta.outputs.chart_version }}
app_version: ${{ steps.meta.outputs.app_version }}
release_channel: ${{ steps.meta.outputs.release_channel }}
steps:
- name: Resolve tags and registry paths
id: meta
shell: bash
run: |
set -euo pipefail
registry="${REGISTRY_HOST}"
namespace="${REGISTRY_NAMESPACE}"
if [ -z "$registry" ]; then
echo "GITEA_REGISTRY_HOST repository variable is required"
exit 1
fi
if [ -z "$namespace" ]; then
namespace="${{ github.repository_owner }}"
fi
short_sha="$(printf '%s' "${{ github.sha }}" | cut -c1-12)"
ref_type="${{ github.ref_type }}"
ref_name="${{ github.ref_name }}"
if [ "$ref_type" = "tag" ] && printf '%s' "$ref_name" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+([-.].*)?$'; then
version="${ref_name#v}"
image_tags="${version},latest,sha-${short_sha}"
chart_version="$version"
release_channel="release"
else
version="sha-${short_sha}"
image_tags="edge,sha-${short_sha}"
chart_version="0.0.0-dev.${{ github.run_number }}"
release_channel="edge"
fi
{
echo "registry=$registry"
echo "namespace=$namespace"
echo "image_tag=$version"
echo "image_tags=$image_tags"
echo "chart_version=$chart_version"
echo "app_version=$version"
echo "release_channel=$release_channel"
} >> "$GITHUB_OUTPUT"
publish-images:
name: Publish ${{ matrix.image.name }}
runs-on: ubuntu-latest
needs: metadata
strategy:
fail-fast: false
matrix:
image:
- name: api
repository: attune-api
dockerfile: docker/Dockerfile.optimized
context: .
target: ""
build_args: |
SERVICE=api
- name: executor
repository: attune-executor
dockerfile: docker/Dockerfile.optimized
context: .
target: ""
build_args: |
SERVICE=executor
- name: notifier
repository: attune-notifier
dockerfile: docker/Dockerfile.optimized
context: .
target: ""
build_args: |
SERVICE=notifier
- name: sensor
repository: attune-sensor
dockerfile: docker/Dockerfile.sensor.optimized
context: .
target: sensor-full
build_args: ""
- name: worker
repository: attune-worker
dockerfile: docker/Dockerfile.worker.optimized
context: .
target: worker-full
build_args: ""
- name: web
repository: attune-web
dockerfile: docker/Dockerfile.web
context: .
target: ""
build_args: ""
- name: migrations
repository: attune-migrations
dockerfile: docker/Dockerfile.migrations
context: .
target: ""
build_args: ""
- name: init-user
repository: attune-init-user
dockerfile: docker/Dockerfile.init-user
context: .
target: ""
build_args: ""
- name: init-packs
repository: attune-init-packs
dockerfile: docker/Dockerfile.init-packs
context: .
target: ""
build_args: ""
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Gitea OCI registry
shell: bash
env:
REGISTRY_USERNAME: ${{ secrets.GITEA_REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.GITEA_REGISTRY_PASSWORD }}
GITHUB_TOKEN_FALLBACK: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
username="${REGISTRY_USERNAME:-${{ github.actor }}}"
password="${REGISTRY_PASSWORD:-${GITHUB_TOKEN_FALLBACK:-}}"
if [ -z "$password" ]; then
echo "Set GITEA_REGISTRY_PASSWORD or enable GITHUB_TOKEN package writes"
exit 1
fi
printf '%s' "$password" | docker login "${{ needs.metadata.outputs.registry }}" \
--username "$username" \
--password-stdin
- name: Prepare image tags
id: tags
shell: bash
run: |
set -euo pipefail
image_ref_base="${{ needs.metadata.outputs.registry }}/${{ needs.metadata.outputs.namespace }}/${{ matrix.image.repository }}"
tag_lines=""
IFS=',' read -ra tags <<< "${{ needs.metadata.outputs.image_tags }}"
for tag in "${tags[@]}"; do
tag_lines="${tag_lines}${image_ref_base}:${tag}"$'\n'
done
printf 'tags<<EOF\n%sEOF\n' "$tag_lines" >> "$GITHUB_OUTPUT"
- name: Build and push image
shell: bash
run: |
set -euo pipefail
build_cmd=(
docker buildx build
"${{ matrix.image.context }}"
--file "${{ matrix.image.dockerfile }}"
--push
)
if [ -n "${{ matrix.image.target }}" ]; then
build_cmd+=(--target "${{ matrix.image.target }}")
fi
while IFS= read -r tag; do
[ -n "$tag" ] && build_cmd+=(--tag "$tag")
done <<< "${{ steps.tags.outputs.tags }}"
while IFS= read -r build_arg; do
[ -n "$build_arg" ] && build_cmd+=(--build-arg "$build_arg")
done <<< "${{ matrix.image.build_args }}"
"${build_cmd[@]}"
publish-chart:
name: Publish Helm Chart
runs-on: ubuntu-latest
needs:
- metadata
- publish-images
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Helm
uses: azure/setup-helm@v4
- name: Log in to Gitea OCI registry
shell: bash
env:
REGISTRY_USERNAME: ${{ secrets.GITEA_REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.GITEA_REGISTRY_PASSWORD }}
GITHUB_TOKEN_FALLBACK: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
registry_username="${REGISTRY_USERNAME:-${{ github.actor }}}"
registry_password="${REGISTRY_PASSWORD:-${GITHUB_TOKEN_FALLBACK:-}}"
if [ -z "$registry_password" ]; then
echo "Set GITEA_REGISTRY_PASSWORD or enable GITHUB_TOKEN package writes"
exit 1
fi
printf '%s' "$registry_password" | helm registry login "${{ needs.metadata.outputs.registry }}" \
--username "$registry_username" \
--password-stdin
- name: Lint chart
run: |
helm lint charts/attune
- name: Package chart
run: |
mkdir -p dist
helm package charts/attune \
--destination dist \
--version "${{ needs.metadata.outputs.chart_version }}" \
--app-version "${{ needs.metadata.outputs.app_version }}"
- name: Push chart to OCI registry
run: |
helm push "dist/${CHART_NAME}-${{ needs.metadata.outputs.chart_version }}.tgz" \
"oci://${{ needs.metadata.outputs.registry }}/${{ needs.metadata.outputs.namespace }}/helm"

6
charts/attune/Chart.yaml Normal file
View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: attune
description: Helm chart for deploying the Attune automation platform
type: application
version: 0.1.0
appVersion: "0.1.0"

View File

@@ -0,0 +1,3 @@
1. Set `global.imageRegistry`, `global.imageNamespace`, and `global.imageTag` so the chart pulls the images published by the Gitea workflow.
2. Set `web.config.apiUrl` and `web.config.wsUrl` to browser-reachable endpoints before exposing the web UI.
3. The shared `packs`, `runtime_envs`, and `artifacts` PVCs default to `ReadWriteMany`; your cluster storage class must support RWX or you need to override those claims.

View File

@@ -0,0 +1,113 @@
{{- define "attune.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "attune.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name (include "attune.name" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- define "attune.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" -}}
{{- end -}}
{{- define "attune.labels" -}}
helm.sh/chart: {{ include "attune.chart" . }}
app.kubernetes.io/name: {{ include "attune.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{- define "attune.selectorLabels" -}}
app.kubernetes.io/name: {{ include "attune.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{- define "attune.componentLabels" -}}
{{ include "attune.selectorLabels" .root }}
app.kubernetes.io/component: {{ .component }}
{{- end -}}
{{- define "attune.image" -}}
{{- $root := .root -}}
{{- $image := .image -}}
{{- $registry := $root.Values.global.imageRegistry -}}
{{- $namespace := $root.Values.global.imageNamespace -}}
{{- $repository := $image.repository -}}
{{- $tag := default $root.Values.global.imageTag $image.tag -}}
{{- if and $registry $namespace -}}
{{- printf "%s/%s/%s:%s" $registry $namespace $repository $tag -}}
{{- else if $registry -}}
{{- printf "%s/%s:%s" $registry $repository $tag -}}
{{- else -}}
{{- printf "%s:%s" $repository $tag -}}
{{- end -}}
{{- end -}}
{{- define "attune.secretName" -}}
{{- if .Values.security.existingSecret -}}
{{- .Values.security.existingSecret -}}
{{- else -}}
{{- printf "%s-secrets" (include "attune.fullname" .) -}}
{{- end -}}
{{- end -}}
{{- define "attune.postgresqlServiceName" -}}
{{- if .Values.database.host -}}
{{- .Values.database.host -}}
{{- else -}}
{{- printf "%s-postgresql" (include "attune.fullname" .) -}}
{{- end -}}
{{- end -}}
{{- define "attune.rabbitmqServiceName" -}}
{{- if .Values.rabbitmq.host -}}
{{- .Values.rabbitmq.host -}}
{{- else -}}
{{- printf "%s-rabbitmq" (include "attune.fullname" .) -}}
{{- end -}}
{{- end -}}
{{- define "attune.redisServiceName" -}}
{{- if .Values.redis.host -}}
{{- .Values.redis.host -}}
{{- else -}}
{{- printf "%s-redis" (include "attune.fullname" .) -}}
{{- end -}}
{{- end -}}
{{- define "attune.databaseUrl" -}}
{{- if .Values.database.url -}}
{{- .Values.database.url -}}
{{- else -}}
{{- printf "postgresql://%s:%s@%s:%v/%s" .Values.database.username .Values.database.password (include "attune.postgresqlServiceName" .) .Values.database.port .Values.database.database -}}
{{- end -}}
{{- end -}}
{{- define "attune.rabbitmqUrl" -}}
{{- if .Values.rabbitmq.url -}}
{{- .Values.rabbitmq.url -}}
{{- else -}}
{{- printf "amqp://%s:%s@%s:%v" .Values.rabbitmq.username .Values.rabbitmq.password (include "attune.rabbitmqServiceName" .) .Values.rabbitmq.port -}}
{{- end -}}
{{- end -}}
{{- define "attune.redisUrl" -}}
{{- if .Values.redis.url -}}
{{- .Values.redis.url -}}
{{- else -}}
{{- printf "redis://%s:%v" (include "attune.redisServiceName" .) .Values.redis.port -}}
{{- end -}}
{{- end -}}
{{- define "attune.apiServiceName" -}}
{{- printf "%s-api" (include "attune.fullname" .) -}}
{{- end -}}
{{- define "attune.notifierServiceName" -}}
{{- printf "%s-notifier" (include "attune.fullname" .) -}}
{{- end -}}

View File

@@ -0,0 +1,523 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "attune.apiServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
type: {{ .Values.api.service.type }}
selector:
{{- include "attune.componentLabels" (dict "root" . "component" "api") | nindent 4 }}
ports:
- name: http
port: {{ .Values.api.service.port }}
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "attune.apiServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.api.replicaCount }}
selector:
matchLabels:
{{- include "attune.componentLabels" (dict "root" . "component" "api") | nindent 6 }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "api") | nindent 8 }}
spec:
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
initContainers:
- name: wait-for-schema
image: postgres:16-alpine
command: ["/bin/sh", "-ec"]
args:
- |
until PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT to_regclass('${DB_SCHEMA}.identity')" | grep -q identity; do
echo "waiting for schema";
sleep 2;
done
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
- name: wait-for-packs
image: busybox:1.36
command: ["/bin/sh", "-ec"]
args:
- |
until [ -f /opt/attune/packs/core/pack.yaml ]; do
echo "waiting for packs";
sleep 2;
done
volumeMounts:
- name: packs
mountPath: /opt/attune/packs
containers:
- name: api
image: {{ include "attune.image" (dict "root" . "image" .Values.images.api) }}
imagePullPolicy: {{ .Values.images.api.pullPolicy }}
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
env:
- name: ATTUNE_CONFIG
value: /opt/attune/config.yaml
- name: ATTUNE__DATABASE__SCHEMA
value: {{ .Values.database.schema | quote }}
- name: ATTUNE__WORKER__WORKER_TYPE
value: container
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 20
periodSeconds: 15
resources:
{{- toYaml .Values.api.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /opt/attune/config.yaml
subPath: config.yaml
- name: packs
mountPath: /opt/attune/packs
- name: runtime-envs
mountPath: /opt/attune/runtime_envs
- name: artifacts
mountPath: /opt/attune/artifacts
volumes:
- name: config
configMap:
name: {{ include "attune.fullname" . }}-config
- name: packs
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-packs
- name: runtime-envs
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-runtime-envs
- name: artifacts
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-artifacts
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "attune.fullname" . }}-executor
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.executor.replicaCount }}
selector:
matchLabels:
{{- include "attune.componentLabels" (dict "root" . "component" "executor") | nindent 6 }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "executor") | nindent 8 }}
spec:
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
initContainers:
- name: wait-for-schema
image: postgres:16-alpine
command: ["/bin/sh", "-ec"]
args:
- |
until PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT to_regclass('${DB_SCHEMA}.identity')" | grep -q identity; do
echo "waiting for schema";
sleep 2;
done
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
- name: wait-for-packs
image: busybox:1.36
command: ["/bin/sh", "-ec"]
args:
- |
until [ -f /opt/attune/packs/core/pack.yaml ]; do
echo "waiting for packs";
sleep 2;
done
volumeMounts:
- name: packs
mountPath: /opt/attune/packs
containers:
- name: executor
image: {{ include "attune.image" (dict "root" . "image" .Values.images.executor) }}
imagePullPolicy: {{ .Values.images.executor.pullPolicy }}
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
env:
- name: ATTUNE_CONFIG
value: /opt/attune/config.yaml
- name: ATTUNE__DATABASE__SCHEMA
value: {{ .Values.database.schema | quote }}
- name: ATTUNE__WORKER__WORKER_TYPE
value: container
resources:
{{- toYaml .Values.executor.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /opt/attune/config.yaml
subPath: config.yaml
- name: packs
mountPath: /opt/attune/packs
- name: artifacts
mountPath: /opt/attune/artifacts
volumes:
- name: config
configMap:
name: {{ include "attune.fullname" . }}-config
- name: packs
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-packs
- name: artifacts
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-artifacts
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "attune.fullname" . }}-worker
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.worker.replicaCount }}
selector:
matchLabels:
{{- include "attune.componentLabels" (dict "root" . "component" "worker") | nindent 6 }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "worker") | nindent 8 }}
spec:
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
initContainers:
- name: wait-for-schema
image: postgres:16-alpine
command: ["/bin/sh", "-ec"]
args:
- |
until PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT to_regclass('${DB_SCHEMA}.identity')" | grep -q identity; do
echo "waiting for schema";
sleep 2;
done
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
- name: wait-for-packs
image: busybox:1.36
command: ["/bin/sh", "-ec"]
args:
- |
until [ -f /opt/attune/packs/core/pack.yaml ]; do
echo "waiting for packs";
sleep 2;
done
volumeMounts:
- name: packs
mountPath: /opt/attune/packs
containers:
- name: worker
image: {{ include "attune.image" (dict "root" . "image" .Values.images.worker) }}
imagePullPolicy: {{ .Values.images.worker.pullPolicy }}
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
env:
- name: ATTUNE_CONFIG
value: /opt/attune/config.yaml
- name: ATTUNE__DATABASE__SCHEMA
value: {{ .Values.database.schema | quote }}
- name: ATTUNE_WORKER_RUNTIMES
value: {{ .Values.worker.runtimes | quote }}
- name: ATTUNE_WORKER_TYPE
value: container
- name: ATTUNE_WORKER_NAME
value: {{ .Values.worker.name | quote }}
- name: ATTUNE_API_URL
value: http://{{ include "attune.apiServiceName" . }}:{{ .Values.api.service.port }}
resources:
{{- toYaml .Values.worker.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /opt/attune/config.yaml
subPath: config.yaml
- name: packs
mountPath: /opt/attune/packs
- name: runtime-envs
mountPath: /opt/attune/runtime_envs
- name: artifacts
mountPath: /opt/attune/artifacts
volumes:
- name: config
configMap:
name: {{ include "attune.fullname" . }}-config
- name: packs
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-packs
- name: runtime-envs
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-runtime-envs
- name: artifacts
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-artifacts
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "attune.fullname" . }}-sensor
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.sensor.replicaCount }}
selector:
matchLabels:
{{- include "attune.componentLabels" (dict "root" . "component" "sensor") | nindent 6 }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "sensor") | nindent 8 }}
spec:
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
initContainers:
- name: wait-for-schema
image: postgres:16-alpine
command: ["/bin/sh", "-ec"]
args:
- |
until PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT to_regclass('${DB_SCHEMA}.identity')" | grep -q identity; do
echo "waiting for schema";
sleep 2;
done
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
- name: wait-for-packs
image: busybox:1.36
command: ["/bin/sh", "-ec"]
args:
- |
until [ -f /opt/attune/packs/core/pack.yaml ]; do
echo "waiting for packs";
sleep 2;
done
volumeMounts:
- name: packs
mountPath: /opt/attune/packs
containers:
- name: sensor
image: {{ include "attune.image" (dict "root" . "image" .Values.images.sensor) }}
imagePullPolicy: {{ .Values.images.sensor.pullPolicy }}
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
env:
- name: ATTUNE_CONFIG
value: /opt/attune/config.yaml
- name: ATTUNE__DATABASE__SCHEMA
value: {{ .Values.database.schema | quote }}
- name: ATTUNE__WORKER__WORKER_TYPE
value: container
- name: ATTUNE_API_URL
value: http://{{ include "attune.apiServiceName" . }}:{{ .Values.api.service.port }}
- name: ATTUNE_MQ_URL
value: {{ include "attune.rabbitmqUrl" . | quote }}
- name: ATTUNE_PACKS_BASE_DIR
value: /opt/attune/packs
resources:
{{- toYaml .Values.sensor.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /opt/attune/config.yaml
subPath: config.yaml
- name: packs
mountPath: /opt/attune/packs
- name: runtime-envs
mountPath: /opt/attune/runtime_envs
volumes:
- name: config
configMap:
name: {{ include "attune.fullname" . }}-config
- name: packs
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-packs
- name: runtime-envs
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-runtime-envs
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "attune.notifierServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
type: {{ .Values.notifier.service.type }}
selector:
{{- include "attune.componentLabels" (dict "root" . "component" "notifier") | nindent 4 }}
ports:
- name: ws
port: {{ .Values.notifier.service.port }}
targetPort: ws
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "attune.notifierServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.notifier.replicaCount }}
selector:
matchLabels:
{{- include "attune.componentLabels" (dict "root" . "component" "notifier") | nindent 6 }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "notifier") | nindent 8 }}
spec:
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
initContainers:
- name: wait-for-schema
image: postgres:16-alpine
command: ["/bin/sh", "-ec"]
args:
- |
until PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT to_regclass('${DB_SCHEMA}.identity')" | grep -q identity; do
echo "waiting for schema";
sleep 2;
done
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
containers:
- name: notifier
image: {{ include "attune.image" (dict "root" . "image" .Values.images.notifier) }}
imagePullPolicy: {{ .Values.images.notifier.pullPolicy }}
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
env:
- name: ATTUNE_CONFIG
value: /opt/attune/config.yaml
- name: ATTUNE__DATABASE__SCHEMA
value: {{ .Values.database.schema | quote }}
- name: ATTUNE__WORKER__WORKER_TYPE
value: container
ports:
- name: ws
containerPort: 8081
readinessProbe:
httpGet:
path: /health
port: ws
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: ws
initialDelaySeconds: 20
periodSeconds: 15
resources:
{{- toYaml .Values.notifier.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /opt/attune/config.yaml
subPath: config.yaml
volumes:
- name: config
configMap:
name: {{ include "attune.fullname" . }}-config
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "attune.fullname" . }}-web
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
type: {{ .Values.web.service.type }}
selector:
{{- include "attune.componentLabels" (dict "root" . "component" "web") | nindent 4 }}
ports:
- name: http
port: {{ .Values.web.service.port }}
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "attune.fullname" . }}-web
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.web.replicaCount }}
selector:
matchLabels:
{{- include "attune.componentLabels" (dict "root" . "component" "web") | nindent 6 }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "web") | nindent 8 }}
spec:
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
containers:
- name: web
image: {{ include "attune.image" (dict "root" . "image" .Values.images.web) }}
imagePullPolicy: {{ .Values.images.web.pullPolicy }}
env:
- name: API_URL
value: {{ .Values.web.config.apiUrl | quote }}
- name: WS_URL
value: {{ .Values.web.config.wsUrl | quote }}
- name: ENVIRONMENT
value: {{ .Values.web.config.environment | quote }}
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 20
periodSeconds: 15
resources:
{{- toYaml .Values.web.resources | nindent 12 }}

View File

@@ -0,0 +1,9 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "attune.fullname" . }}-config
labels:
{{- include "attune.labels" . | nindent 4 }}
data:
config.yaml: |
{{ .Files.Get "files/config.docker.yaml" | indent 4 }}

View File

@@ -0,0 +1,225 @@
{{- if .Values.database.postgresql.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "attune.postgresqlServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
selector:
{{- include "attune.componentLabels" (dict "root" . "component" "postgresql") | nindent 4 }}
ports:
- name: postgres
port: {{ .Values.database.port }}
targetPort: postgres
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "attune.postgresqlServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
serviceName: {{ include "attune.postgresqlServiceName" . }}
replicas: 1
selector:
matchLabels:
{{- include "attune.componentLabels" (dict "root" . "component" "postgresql") | nindent 6 }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "postgresql") | nindent 8 }}
spec:
containers:
- name: postgresql
image: "{{ .Values.database.postgresql.image.repository }}:{{ .Values.database.postgresql.image.tag }}"
imagePullPolicy: IfNotPresent
env:
- name: POSTGRES_USER
value: {{ .Values.database.username | quote }}
- name: POSTGRES_PASSWORD
value: {{ .Values.database.password | quote }}
- name: POSTGRES_DB
value: {{ .Values.database.database | quote }}
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
ports:
- name: postgres
containerPort: 5432
livenessProbe:
exec:
command: ["pg_isready", "-U", "{{ .Values.database.username }}"]
initialDelaySeconds: 20
periodSeconds: 10
readinessProbe:
exec:
command: ["pg_isready", "-U", "{{ .Values.database.username }}"]
initialDelaySeconds: 10
periodSeconds: 10
resources:
{{- toYaml .Values.database.postgresql.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
{{- toYaml .Values.database.postgresql.persistence.accessModes | nindent 10 }}
resources:
requests:
storage: {{ .Values.database.postgresql.persistence.size }}
{{- if .Values.database.postgresql.persistence.storageClassName }}
storageClassName: {{ .Values.database.postgresql.persistence.storageClassName }}
{{- end }}
{{- end }}
{{- if .Values.rabbitmq.enabled }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "attune.rabbitmqServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
selector:
{{- include "attune.componentLabels" (dict "root" . "component" "rabbitmq") | nindent 4 }}
ports:
- name: amqp
port: {{ .Values.rabbitmq.port }}
targetPort: amqp
- name: management
port: {{ .Values.rabbitmq.managementPort }}
targetPort: management
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "attune.rabbitmqServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
serviceName: {{ include "attune.rabbitmqServiceName" . }}
replicas: 1
selector:
matchLabels:
{{- include "attune.componentLabels" (dict "root" . "component" "rabbitmq") | nindent 6 }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "rabbitmq") | nindent 8 }}
spec:
containers:
- name: rabbitmq
image: "{{ .Values.rabbitmq.image.repository }}:{{ .Values.rabbitmq.image.tag }}"
imagePullPolicy: IfNotPresent
env:
- name: RABBITMQ_DEFAULT_USER
value: {{ .Values.rabbitmq.username | quote }}
- name: RABBITMQ_DEFAULT_PASS
value: {{ .Values.rabbitmq.password | quote }}
- name: RABBITMQ_DEFAULT_VHOST
value: /
ports:
- name: amqp
containerPort: 5672
- name: management
containerPort: 15672
livenessProbe:
exec:
command: ["rabbitmq-diagnostics", "-q", "ping"]
initialDelaySeconds: 20
periodSeconds: 15
readinessProbe:
exec:
command: ["rabbitmq-diagnostics", "-q", "ping"]
initialDelaySeconds: 10
periodSeconds: 10
resources:
{{- toYaml .Values.rabbitmq.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /var/lib/rabbitmq
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
{{- toYaml .Values.rabbitmq.persistence.accessModes | nindent 10 }}
resources:
requests:
storage: {{ .Values.rabbitmq.persistence.size }}
{{- if .Values.rabbitmq.persistence.storageClassName }}
storageClassName: {{ .Values.rabbitmq.persistence.storageClassName }}
{{- end }}
{{- end }}
{{- if .Values.redis.enabled }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "attune.redisServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
selector:
{{- include "attune.componentLabels" (dict "root" . "component" "redis") | nindent 4 }}
ports:
- name: redis
port: {{ .Values.redis.port }}
targetPort: redis
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "attune.redisServiceName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
serviceName: {{ include "attune.redisServiceName" . }}
replicas: 1
selector:
matchLabels:
{{- include "attune.componentLabels" (dict "root" . "component" "redis") | nindent 6 }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "redis") | nindent 8 }}
spec:
containers:
- name: redis
image: "{{ .Values.redis.image.repository }}:{{ .Values.redis.image.tag }}"
imagePullPolicy: IfNotPresent
command: ["redis-server", "--appendonly", "yes"]
ports:
- name: redis
containerPort: 6379
livenessProbe:
exec:
command: ["redis-cli", "ping"]
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
exec:
command: ["redis-cli", "ping"]
initialDelaySeconds: 10
periodSeconds: 10
resources:
{{- toYaml .Values.redis.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
{{- toYaml .Values.redis.persistence.accessModes | nindent 10 }}
resources:
requests:
storage: {{ .Values.redis.persistence.size }}
{{- if .Values.redis.persistence.storageClassName }}
storageClassName: {{ .Values.redis.persistence.storageClassName }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,35 @@
{{- if .Values.web.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "attune.fullname" . }}-web
labels:
{{- include "attune.labels" . | nindent 4 }}
{{- with .Values.web.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.web.ingress.className }}
ingressClassName: {{ .Values.web.ingress.className }}
{{- end }}
rules:
{{- range .Values.web.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "attune.fullname" $ }}-web
port:
number: {{ $.Values.web.service.port }}
{{- end }}
{{- end }}
{{- with .Values.web.ingress.tls }}
tls:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,154 @@
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "attune.fullname" . }}-migrations
labels:
{{- include "attune.labels" . | nindent 4 }}
app.kubernetes.io/component: migrations
annotations:
helm.sh/hook: post-install,post-upgrade
helm.sh/hook-weight: "-20"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
ttlSecondsAfterFinished: {{ .Values.jobs.migrations.ttlSecondsAfterFinished }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "migrations") | nindent 8 }}
spec:
restartPolicy: OnFailure
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
containers:
- name: migrations
image: {{ include "attune.image" (dict "root" . "image" .Values.images.migrations) }}
imagePullPolicy: {{ .Values.images.migrations.pullPolicy }}
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
env:
- name: MIGRATIONS_DIR
value: /migrations
resources:
{{- toYaml .Values.jobs.migrations.resources | nindent 12 }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "attune.fullname" . }}-init-user
labels:
{{- include "attune.labels" . | nindent 4 }}
app.kubernetes.io/component: init-user
annotations:
helm.sh/hook: post-install,post-upgrade
helm.sh/hook-weight: "-10"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
ttlSecondsAfterFinished: {{ .Values.jobs.initUser.ttlSecondsAfterFinished }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "init-user") | nindent 8 }}
spec:
restartPolicy: OnFailure
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
containers:
- name: init-user
image: {{ include "attune.image" (dict "root" . "image" .Values.images.initUser) }}
imagePullPolicy: {{ .Values.images.initUser.pullPolicy }}
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
command: ["/bin/sh", "-ec"]
args:
- |
until PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT to_regclass('${DB_SCHEMA}.identity')" | grep -q identity; do
echo "waiting for database schema";
sleep 2;
done
exec /init-user.sh
resources:
{{- toYaml .Values.jobs.initUser.resources | nindent 12 }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "attune.fullname" . }}-init-packs
labels:
{{- include "attune.labels" . | nindent 4 }}
app.kubernetes.io/component: init-packs
annotations:
helm.sh/hook: post-install,post-upgrade
helm.sh/hook-weight: "0"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
spec:
ttlSecondsAfterFinished: {{ .Values.jobs.initPacks.ttlSecondsAfterFinished }}
template:
metadata:
labels:
{{- include "attune.componentLabels" (dict "root" . "component" "init-packs") | nindent 8 }}
spec:
restartPolicy: OnFailure
{{- if .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
{{- end }}
containers:
- name: init-packs
image: {{ include "attune.image" (dict "root" . "image" .Values.images.initPacks) }}
imagePullPolicy: {{ .Values.images.initPacks.pullPolicy }}
envFrom:
- secretRef:
name: {{ include "attune.secretName" . }}
command: ["/bin/sh", "-ec"]
args:
- |
until python3 - <<'PY'
import os
import psycopg2
conn = psycopg2.connect(
host=os.environ["DB_HOST"],
port=os.environ["DB_PORT"],
user=os.environ["DB_USER"],
password=os.environ["DB_PASSWORD"],
dbname=os.environ["DB_NAME"],
)
try:
with conn.cursor() as cur:
cur.execute("SET search_path TO %s, public" % os.environ["DB_SCHEMA"])
cur.execute("SELECT to_regclass(%s)", (f"{os.environ['DB_SCHEMA']}.identity",))
value = cur.fetchone()[0]
raise SystemExit(0 if value else 1)
finally:
conn.close()
PY
do
echo "waiting for database schema";
sleep 2;
done
exec /init-packs.sh
volumeMounts:
- name: packs
mountPath: /opt/attune/packs
- name: runtime-envs
mountPath: /opt/attune/runtime_envs
- name: artifacts
mountPath: /opt/attune/artifacts
resources:
{{- toYaml .Values.jobs.initPacks.resources | nindent 12 }}
volumes:
- name: packs
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-packs
- name: runtime-envs
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-runtime-envs
- name: artifacts
persistentVolumeClaim:
claimName: {{ include "attune.fullname" . }}-artifacts

View File

@@ -0,0 +1,53 @@
{{- if .Values.sharedStorage.packs.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "attune.fullname" . }}-packs
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
accessModes:
{{- toYaml .Values.sharedStorage.packs.accessModes | nindent 4 }}
resources:
requests:
storage: {{ .Values.sharedStorage.packs.size }}
{{- if .Values.sharedStorage.packs.storageClassName }}
storageClassName: {{ .Values.sharedStorage.packs.storageClassName }}
{{- end }}
---
{{- end }}
{{- if .Values.sharedStorage.runtimeEnvs.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "attune.fullname" . }}-runtime-envs
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
accessModes:
{{- toYaml .Values.sharedStorage.runtimeEnvs.accessModes | nindent 4 }}
resources:
requests:
storage: {{ .Values.sharedStorage.runtimeEnvs.size }}
{{- if .Values.sharedStorage.runtimeEnvs.storageClassName }}
storageClassName: {{ .Values.sharedStorage.runtimeEnvs.storageClassName }}
{{- end }}
---
{{- end }}
{{- if .Values.sharedStorage.artifacts.enabled }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "attune.fullname" . }}-artifacts
labels:
{{- include "attune.labels" . | nindent 4 }}
spec:
accessModes:
{{- toYaml .Values.sharedStorage.artifacts.accessModes | nindent 4 }}
resources:
requests:
storage: {{ .Values.sharedStorage.artifacts.size }}
{{- if .Values.sharedStorage.artifacts.storageClassName }}
storageClassName: {{ .Values.sharedStorage.artifacts.storageClassName }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,31 @@
{{- if not .Values.security.existingSecret }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "attune.secretName" . }}
labels:
{{- include "attune.labels" . | nindent 4 }}
type: Opaque
stringData:
ATTUNE__SECURITY__JWT_SECRET: {{ .Values.security.jwtSecret | quote }}
ATTUNE__SECURITY__ENCRYPTION_KEY: {{ .Values.security.encryptionKey | quote }}
ATTUNE__DATABASE__URL: {{ include "attune.databaseUrl" . | quote }}
ATTUNE__MESSAGE_QUEUE__URL: {{ include "attune.rabbitmqUrl" . | quote }}
ATTUNE__CACHE__URL: {{ include "attune.redisUrl" . | quote }}
DB_HOST: {{ include "attune.postgresqlServiceName" . | quote }}
DB_PORT: {{ .Values.database.port | quote }}
DB_USER: {{ .Values.database.username | quote }}
DB_PASSWORD: {{ .Values.database.password | quote }}
DB_NAME: {{ .Values.database.database | quote }}
DB_SCHEMA: {{ .Values.database.schema | quote }}
TEST_LOGIN: {{ .Values.bootstrap.testUser.login | quote }}
TEST_DISPLAY_NAME: {{ .Values.bootstrap.testUser.displayName | quote }}
TEST_PASSWORD: {{ .Values.bootstrap.testUser.password | quote }}
DEFAULT_ADMIN_LOGIN: {{ .Values.bootstrap.testUser.login | quote }}
DEFAULT_ADMIN_PERMISSION_SET_REF: "core.admin"
SOURCE_PACKS_DIR: "/source/packs"
TARGET_PACKS_DIR: "/opt/attune/packs"
RUNTIME_ENVS_DIR: "/opt/attune/runtime_envs"
ARTIFACTS_DIR: "/opt/attune/artifacts"
LOADER_SCRIPT: "/scripts/load_core_pack.py"
{{- end }}

193
charts/attune/values.yaml Normal file
View File

@@ -0,0 +1,193 @@
nameOverride: ""
fullnameOverride: ""
global:
imageRegistry: ""
imageNamespace: ""
imageTag: edge
imagePullSecrets: []
security:
existingSecret: ""
jwtSecret: change-me-in-production
encryptionKey: change-me-in-production-32-bytes-minimum
database:
schema: public
username: attune
password: attune
database: attune
host: ""
port: 5432
url: ""
postgresql:
enabled: true
image:
repository: timescale/timescaledb
tag: 2.17.2-pg16
persistence:
enabled: true
accessModes:
- ReadWriteOnce
size: 20Gi
storageClassName: ""
resources: {}
rabbitmq:
username: attune
password: attune
host: ""
port: 5672
url: ""
managementPort: 15672
enabled: true
image:
repository: rabbitmq
tag: 3.13-management-alpine
persistence:
enabled: true
accessModes:
- ReadWriteOnce
size: 8Gi
storageClassName: ""
resources: {}
redis:
enabled: true
host: ""
port: 6379
url: ""
image:
repository: redis
tag: 7-alpine
persistence:
enabled: true
accessModes:
- ReadWriteOnce
size: 8Gi
storageClassName: ""
resources: {}
bootstrap:
testUser:
login: test@attune.local
displayName: Test User
password: TestPass123!
sharedStorage:
packs:
enabled: true
accessModes:
- ReadWriteMany
size: 2Gi
storageClassName: ""
runtimeEnvs:
enabled: true
accessModes:
- ReadWriteMany
size: 10Gi
storageClassName: ""
artifacts:
enabled: true
accessModes:
- ReadWriteMany
size: 20Gi
storageClassName: ""
images:
api:
repository: attune-api
tag: ""
pullPolicy: IfNotPresent
executor:
repository: attune-executor
tag: ""
pullPolicy: IfNotPresent
worker:
repository: attune-worker
tag: ""
pullPolicy: IfNotPresent
sensor:
repository: attune-sensor
tag: ""
pullPolicy: IfNotPresent
notifier:
repository: attune-notifier
tag: ""
pullPolicy: IfNotPresent
web:
repository: attune-web
tag: ""
pullPolicy: IfNotPresent
migrations:
repository: attune-migrations
tag: ""
pullPolicy: IfNotPresent
initUser:
repository: attune-init-user
tag: ""
pullPolicy: IfNotPresent
initPacks:
repository: attune-init-packs
tag: ""
pullPolicy: IfNotPresent
jobs:
migrations:
ttlSecondsAfterFinished: 300
resources: {}
initUser:
ttlSecondsAfterFinished: 300
resources: {}
initPacks:
ttlSecondsAfterFinished: 300
resources: {}
api:
replicaCount: 1
service:
type: ClusterIP
port: 8080
resources: {}
executor:
replicaCount: 1
resources: {}
worker:
replicaCount: 1
runtimes: shell,python,node,native
name: worker-full-01
resources: {}
sensor:
replicaCount: 1
resources: {}
notifier:
replicaCount: 1
service:
type: ClusterIP
port: 8081
resources: {}
web:
replicaCount: 1
service:
type: ClusterIP
port: 80
config:
environment: kubernetes
apiUrl: http://localhost:8080
wsUrl: ws://localhost:8081
resources: {}
ingress:
enabled: false
className: ""
annotations: {}
hosts:
- host: attune.local
paths:
- path: /
pathType: Prefix
tls: []

View File

@@ -0,0 +1,10 @@
FROM python:3.11-slim
COPY packs /source/packs
COPY scripts/load_core_pack.py /scripts/load_core_pack.py
COPY docker/init-packs.sh /init-packs.sh
RUN pip install --no-cache-dir psycopg2-binary pyyaml && \
chmod +x /init-packs.sh
CMD ["/bin/sh", "/init-packs.sh"]

View File

@@ -0,0 +1,7 @@
FROM postgres:16-alpine
COPY docker/init-user.sh /init-user.sh
RUN chmod +x /init-user.sh
CMD ["/bin/sh", "/init-user.sh"]

View File

@@ -0,0 +1,9 @@
FROM postgres:16-alpine
COPY migrations /migrations
COPY docker/run-migrations.sh /run-migrations.sh
COPY docker/init-roles.sql /docker/init-roles.sql
RUN chmod +x /run-migrations.sh
CMD ["/bin/sh", "/run-migrations.sh"]

View File

@@ -0,0 +1,110 @@
# Gitea Registry And Helm Publishing
This repository now includes:
- A Gitea Actions publish workflow at `.gitea/workflows/publish.yml`
- OCI-published container images for the Kubernetes deployment path
- A Helm chart at `charts/attune`
## What Gets Published
The workflow publishes these images to the Gitea OCI registry:
- `attune-api`
- `attune-executor`
- `attune-worker`
- `attune-sensor`
- `attune-notifier`
- `attune-web`
- `attune-migrations`
- `attune-init-user`
- `attune-init-packs`
The Helm chart is pushed as an OCI chart to:
- `oci://<registry>/<namespace>/helm/attune`
## Required Gitea Repository Configuration
Set these repository variables:
- `GITEA_REGISTRY_HOST`: Registry hostname only, for example `gitea.example.com`
- `GITEA_REGISTRY_NAMESPACE`: Optional override for the registry namespace. If omitted, the workflow uses the repository owner.
Set one of these authentication options:
- Preferred: `GITEA_REGISTRY_USERNAME` and `GITEA_REGISTRY_PASSWORD`
- Fallback: allow the workflow `GITHUB_TOKEN` or Gitea-provided token to push packages
## Publish Behavior
The workflow runs on:
- pushes to `main`
- pushes to `master`
- tags matching `v*`
- manual dispatch
Tag behavior:
- branch pushes publish `edge` and `sha-<12-char-sha>`
- release tags like `v0.3.0` publish `0.3.0`, `latest`, and `sha-<12-char-sha>`
Chart packaging behavior:
- branch pushes package the chart as `0.0.0-dev.<run_number>`
- release tags package the chart with the tag version, for example `0.3.0`
## Helm Install Flow
Log in to the registry:
```bash
helm registry login gitea.example.com --username <user>
```
Install the chart:
```bash
helm install attune oci://gitea.example.com/<namespace>/helm/attune \
--version 0.3.0 \
--set global.imageRegistry=gitea.example.com \
--set global.imageNamespace=<namespace> \
--set global.imageTag=0.3.0 \
--set web.config.apiUrl=https://attune.example.com/api \
--set web.config.wsUrl=wss://attune.example.com/ws
```
For a branch build:
```bash
helm install attune oci://gitea.example.com/<namespace>/helm/attune \
--version 0.0.0-dev.<run_number> \
--set global.imageRegistry=gitea.example.com \
--set global.imageNamespace=<namespace> \
--set global.imageTag=edge
```
## Chart Expectations
The chart defaults to deploying:
- PostgreSQL via TimescaleDB
- RabbitMQ
- Redis
- Attune API, executor, worker, sensor, notifier, and web services
- Migration, test-user bootstrap, and built-in pack bootstrap jobs
Important constraints:
- The shared `packs`, `runtime_envs`, and `artifacts` claims default to `ReadWriteMany`
- Your cluster storage class must support RWX for the default values to work as written
- `web.config.apiUrl` and `web.config.wsUrl` must be browser-reachable URLs, not cluster-internal service DNS names
- The default security and bootstrap values in `charts/attune/values.yaml` are placeholders and should be overridden
## Suggested First Release Sequence
1. Push the workflow and chart changes to `main`.
2. Verify that the workflow publishes the `edge` images and dev chart package.
3. Create a release tag such as `v0.1.0`.
4. Install the chart using that exact image tag and chart version.