Add OneLab Helm chart, Argo CD Application, and GitOps values for k3s

Made-with: Cursor
This commit is contained in:
timotheereausanofi
2026-03-20 10:15:15 +01:00
commit 52847814e0
102 changed files with 4476 additions and 0 deletions

52
gitops/README.md Normal file
View File

@@ -0,0 +1,52 @@
# OneLab GitOps (k3s + Argo CD)
This directory holds the **Helm chart** that replaces `docker stack deploy` from the legacy Swarm installer (`app/docker-compose.yml`).
## Layout
| Path | Purpose |
|------|---------|
| `charts/onelab` | Helm chart (StatefulSets, Deployments, Services, ConfigMaps, Secrets) |
| `values/*.yaml` | Environment-specific overrides (non-secret defaults; use sealed/external secrets for prod) |
| `argocd/application.yaml` | Example `Application` — set `repoURL` / `targetRevision` to your remote |
## Prerequisites
1. **k3s** (or any Kubernetes) with default storage class for Postgres/Rabbit PVCs (e.g. `local-path`).
2. **Image pull access** to `hub.andrewalliance.com` — create a docker-registry secret and reference it in `imagePullSecrets`:
```bash
kubectl create namespace onelab
kubectl create secret docker-registry hub-andrewalliance -n onelab \
--docker-server=hub.andrewalliance.com --docker-username=... --docker-password=...
```
3. **RabbitMQ TLS secret** (name `onelab-rabbit-tls` by default) — see `values/k3s-example.yaml` comments, or set `rabbitmq.tls.embed: true` with PEM strings in a **private** values file.
4. **Host paths** (default): ensure `/opt/onelab/data` and `/opt/onelab/logs` exist on nodes that run workloads using `persistence.mode: hostPath`, or switch to RWX storage for multi-node.
## Helm (without Argo CD)
```bash
cd gitops/charts/onelab
helm upgrade --install onelab . -n onelab --create-namespace \
-f ../../values/k3s-example.yaml
```
## Argo CD
1. Push this repository to a Git remote Argo CD can read.
2. Edit `argocd/application.yaml`: `repoURL`, `targetRevision`, and values file as needed.
3. `kubectl apply -f gitops/argocd/application.yaml` (from a machine with a working kubeconfig).
Sync waves order Postgres → Redis/Rabbit/config → application pods.
## kubectl / credentials
If `kubectl` reports *You must be logged in*, refresh your kubeconfig (e.g. copy `/etc/rancher/k3s/k3s.yaml` from the server or re-run your auth plugin) before applying manifests.
## Helm note (Windows)
Helm 3.19 may return empty content for `.Files.Get` on Windows; this chart uses `fromYaml (.Files.AsConfig)` as a workaround so packaged files still render correctly.
## Not migrated in this chart
- **Edge proxy stack** (`app/proxy/docker-compose.yml`, host 80/443) — use k3s **Traefik** / **Ingress** + **cert-manager**, or a separate DaemonSet/nginx chart.
- **Swarm-only secrets** (e.g. `ssl_passphrase`) — handle via Kubernetes Secrets or external operators.

View File

@@ -0,0 +1,26 @@
# Syncs chart from Git; ensure Argo CD can clone repoURL (add credentials in Argo if private).
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: onelab
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://git.luneski.fr/luneski/onelab-k8s.git
targetRevision: main
path: gitops/charts/onelab
helm:
valueFiles:
- ../../values/k3s-example.yaml
destination:
server: https://kubernetes.default.svc
namespace: onelab
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: onelab
description: OneLab stack for Kubernetes (migrated from Docker Swarm compose)
type: application
version: 0.1.0
appVersion: "1.27.0"

View File

@@ -0,0 +1,5 @@
[
{rabbit, [
{tcp_listeners, []}
]}
].

View File

@@ -0,0 +1,91 @@
---
onelab:
domain: {{ .Values.onelab.domain | quote }}
logs:
level: info
assets:
purge: 1d
shared:
inputs:
path: "./data/shared/inputs"
archive_path: "./data/shared/archived"
security:
cors: '*'
auth:
token:
expiration: 5m
key: {{ .Values.onelab.secrets.authTokenKey | quote }}
password:
expiration: 90d
min_length: 8
prevent_reuse: 5
allow_list: []
block_list: []
authentifier: "email"
ratelimit:
ip:
max: 1000
duration: 1d
auth:
max: 5
duration: 5m
delay_after: 2
delay_ms: 1000
devices:
cors: '*'
monitoring:
token: {{ .Values.onelab.secrets.monitoringToken | quote }}
params:
session:
idle: 45m
remember_me: true
lab:
creation_policy: many
signup: false
{{- if .Values.onelab.intercom.appid }}
intercom:
appid: {{ .Values.onelab.intercom.appid | quote }}
secret: {{ .Values.onelab.intercom.secret | quote }}
{{- end }}
mailer:
noreply: {{ .Values.onelab.mailer.noreply | quote }}
queue:
scheduling: 15
maxsize: 50
error:
maxtries: 3
timeout: 60
ldap:
enabled: {{ .Values.features.ldapWorker }}
services:
db:
host: db
database: postgres
username: postgres
password: {{ .Values.postgresql.auth.password | quote }}
schema: onelab
redis:
host: redis
port: "6379"
rabbit:
url: rabbitmq
port: 5671
token: {{ .Values.onelab.secrets.rabbitToken | quote }}
api:
replicas: {{ .Values.replicas.api }}
apidevice:
replicas: {{ .Values.replicas.apidevice }}
apirabbit:
replicas: {{ .Values.replicas.apirabbit }}
devices:
replicas: {{ .Values.replicas.devices }}
experiments:
replicas: {{ .Values.replicas.experiments }}
images:
replicas: {{ .Values.replicas.images }}
manual:
replicas: {{ .Values.replicas.manual }}
website:
ssr: {{ .Values.website.ssr }}
ws:
replicas: {{ .Values.replicas.ws }}

View File

@@ -0,0 +1 @@
[rabbitmq_auth_backend_http, rabbitmq_auth_backend_cache, rabbitmq_management, rabbitmq_event_exchange].

View File

@@ -0,0 +1,98 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>OneLab - Page Not Found</title>
<meta name="description" content="The page you are looking for does not exist or has been moved.">
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css?family=Open+Sans&display=swap" rel="stylesheet">
<style>
body {
font-family: "Open Sans", sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.page {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
text-align: center;
padding: 128px;
display: flex;
flex-direction: column;
align-items: center;
}
.content {
width: 360px;
max-width: 90%;
}
.main {
font-size: 32px;
font-weight: 600;
margin-top: 64px;
font-stretch: normal;
font-style: normal;
line-height: normal;
letter-spacing: normal;
text-align: center;
color: #4a4a4a;
}
.code {
margin-top: 8px;
font-size: 13px;
letter-spacing: 2px;
color: #888;
text-transform: uppercase;
}
.sub {
margin-top: 24px;
font-size: 15px;
line-height: 1.47;
color: #4a4a4a;
}
a.login-btn {
display: inline-block;
margin-top: 32px;
padding: 12px 28px;
border-radius: 1000px;
background: #6cb644;
color: #fff;
text-decoration: none;
font-weight: 600;
box-shadow: 0 2px 4px rgba(0, 0, 0, .15);
font-size: 15px;
}
a.login-btn:hover,
a.login-btn:focus {
background: #3a8611;
}
</style>
</head>
<body>
<div class="page" role="main" aria-labelledby="title">
<div class="content">
<div class="img">
<img
src="data:image/png;base64,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"
alt="OneLab logo">
</div>
<div class="main" id="title">Page not found</div>
<div class="code">Error 404</div>
<div class="sub">The page you requested doesnt exist, was removed, or is temporarily unavailable. You can return
to the application by logging in again.</div>
<a href="/login" class="login-btn" aria-label="Go to login page">Go to Home page</a>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,337 @@
worker_processes 4;
events { worker_connections 1024; }
http {
include /etc/nginx/mime.types;
client_max_body_size 2m;
# don't send the nginx version number in error pages and Server header
server_tokens off;
sendfile on;
upstream website {
server website:4000;
}
upstream main {
server main:80;
}
upstream api {
server api:3000;
}
upstream api-device {
server api-device:3000;
}
upstream api-rabbit {
server api-rabbit:3000;
}
upstream designer {
server designer:80;
}
upstream runner {
server runner:80;
}
upstream static {
server static:80;
}
upstream websocket-worker {
server websocket-worker:3030;
}
# Restore the real client IP from the upstream reverse proxy
# Trust all RFC 1918 private ranges (covers any Docker network config)
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
real_ip_header X-Real-IP;
limit_req_zone $binary_remote_addr zone=auth:10m rate=1r/s;
limit_req_zone $binary_remote_addr zone=website:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
# redirect all http traffic to https
server {
listen 80 default_server;
{{- if .Values.revproxy.ipv6Listen }}
listen [::]:80 default_server;
{{- end }}
server_name localhost;
gzip on;
gzip_vary on;
gzip_min_length 10240;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_disable "MSIE [1-6]\.";
root /data/;
location ^~ /lab/ {
rewrite ^/lab/(.*?) /app/lab/$1 last;
}
location ^~ /assets/ {
location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
try_files $uri $uri/ /error-404.html =404;
}
expires -1;
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
try_files $uri $uri/ /error-404.html =404;
}
location ^~ /static/ {
location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
proxy_pass http://static;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
expires -1;
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
proxy_pass http://static;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /sitemap.xml {
limit_req zone=global burst=10 nodelay;
proxy_pass http://api;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /robots.txt {
limit_req zone=global burst=10 nodelay;
proxy_pass http://api;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /api/ {
limit_req zone=global burst=10 nodelay;
proxy_pass http://api;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /api/v1/auth/ {
# apply rate limiting
limit_req zone=auth burst=5 nodelay;
proxy_pass http://api;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /api/v1/firmwares {
limit_req zone=global burst=10 nodelay;
client_max_body_size 600M;
proxy_pass http://api;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /api/v1/rmq/ {
proxy_pass http://api-rabbit;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /device-api/ {
limit_req zone=global burst=10 nodelay;
client_max_body_size 600m;
proxy_pass http://api-device;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /app/designer/ {
location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
proxy_pass http://designer;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
expires -1;
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
proxy_pass http://designer;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /app/runner/ {
location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
proxy_pass http://runner;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
expires -1;
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
proxy_pass http://runner;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
location ^~ /ws/ {
proxy_pass http://websocket-worker;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location ^~ /app/ {
location ~* \.(?:css|js|woff|woff2|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
proxy_pass http://main;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
expires -1;
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
proxy_pass http://main;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
# Static assets for website (including /media/ subdirectory)
location ~* ^/.+\.(?:css|js|woff|woff2|ttf|eot|jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
proxy_pass http://website;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
# Website SSR routes (rate limited)
location ~ ^/(library(/[^/]+)?|login(/(reset|change))?|signup)?$ {
limit_req zone=website burst=10 nodelay;
expires -1;
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
proxy_pass http://website;
proxy_redirect off;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
# Catch-all for junk requests
location / {
try_files $uri $uri/ /error-404.html =404;
}
}
}

View File

@@ -0,0 +1,20 @@
{
"rabbit_version": "3.6.5",
"users": [
],
"vhosts": [
{
"name": "devices"
},
{
"name": "internal"
}
],
"permissions": [
],
"parameters": [],
"policies": [],
"queues": [],
"exchanges": [],
"bindings": []
}

View File

@@ -0,0 +1,54 @@
# Auth server config
auth_backends.1 = cache
auth_cache.cached_backend = http
auth_cache.cache_ttl = 5000
auth_http.user_path = http://revproxy/api/v1/rmq/user
auth_http.vhost_path = http://revproxy/api/v1/rmq/vhost
auth_http.resource_path = http://revproxy/api/v1/rmq/resource
auth_http.topic_path = http://revproxy/api/v1/rmq/topic
listeners.ssl.default = 5671
# generated with "cat server.crt server.key > server.pem"
# fullchain
ssl_options.cacertfile = /etc/rabbitmq/ssl/rabbit.fullchain.pem
ssl_options.certfile = /etc/rabbitmq/ssl/rabbit.crt
ssl_options.keyfile = /etc/rabbitmq/ssl/rabbit.key
# not very secure
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = false
# for TLS version and cipher
ssl_options.versions.1 = tlsv1.2
# these MUST be disabled if TLSv1.3 is used
ssl_options.honor_cipher_order = true
ssl_options.honor_ecc_order = true
# These are highly recommended for TLSv1.2 but cannot be used
# with TLSv1.3. If TLSv1.3 is enabled, these lines MUST be removed.
ssl_options.client_renegotiation = false
ssl_options.secure_renegotiate = true
ssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384
ssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384
ssl_options.ciphers.3 = ECDH-ECDSA-AES256-GCM-SHA384
ssl_options.ciphers.4 = ECDH-RSA-AES256-GCM-SHA384
ssl_options.ciphers.5 = DHE-RSA-AES256-GCM-SHA384
ssl_options.ciphers.6 = DHE-DSS-AES256-GCM-SHA384
ssl_options.ciphers.7 = ECDHE-ECDSA-AES128-GCM-SHA256
ssl_options.ciphers.8 = ECDHE-RSA-AES128-GCM-SHA256
ssl_options.ciphers.9 = ECDH-ECDSA-AES128-GCM-SHA256
ssl_options.ciphers.10 = ECDH-RSA-AES128-GCM-SHA256
ssl_options.ciphers.11 = DHE-RSA-AES128-GCM-SHA256
ssl_options.ciphers.12 = DHE-DSS-AES128-GCM-SHA256
management.load_definitions = /opt/definitions.json
# For connection events
event_exchange.vhost = devices

View File

@@ -0,0 +1,25 @@
{{- define "onelab.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- define "onelab.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{- define "onelab.labels" -}}
app.kubernetes.io/name: {{ include "onelab.name" . }}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | quote }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}

View File

@@ -0,0 +1,18 @@
{{- $cfg := fromYaml (.Files.AsConfig) }}
apiVersion: v1
kind: ConfigMap
metadata:
name: onelab-rabbit-config
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }}
data:
rabbit.conf: |
{{ index $cfg "rabbit.conf" | nindent 4 }}
advanced.conf: |
{{ index $cfg "advanced.conf" | nindent 4 }}
enable_plugins: |
{{ index $cfg "enable_plugins" | nindent 4 }}
definitions.json: |
{{ index $cfg "rabbit-definitions.json" | nindent 4 }}

View File

@@ -0,0 +1,14 @@
{{- $cfg := fromYaml (.Files.AsConfig) }}
apiVersion: v1
kind: ConfigMap
metadata:
name: onelab-revproxy
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.apps | quote }}
data:
nginx.conf: |
{{ tpl (index $cfg "nginx.conf.tpl") . | nindent 4 }}
error-404.html: |
{{ index $cfg "error-404.html" | nindent 4 }}

View File

@@ -0,0 +1,111 @@
{{- $root := . }}
{{- if .Values.features.ldapWorker }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ldap-worker
labels:
app.kubernetes.io/component: ldap-worker
{{- include "onelab.labels" $root | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ $root.Values.syncWaves.apps | quote }}
spec:
replicas: {{ $root.Values.replicas.ldap | default 1 }}
selector:
matchLabels:
app.kubernetes.io/component: ldap-worker
app.kubernetes.io/name: {{ include "onelab.name" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/component: ldap-worker
app.kubernetes.io/name: {{ include "onelab.name" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
spec:
{{- with $root.Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: ldap-worker
image: {{ printf "%s/%s:%s" $root.Values.images.registry "onelab-ldap-worker" $root.Values.images.tag | quote }}
volumeMounts:
- name: configurations
mountPath: /conf/configurations.yml
subPath: configurations.yml
readOnly: true
{{- if eq $root.Values.persistence.mode "hostPath" }}
- name: logs
mountPath: /logs
{{- end }}
volumes:
- name: configurations
secret:
secretName: onelab-configurations
{{- if eq $root.Values.persistence.mode "hostPath" }}
- name: logs
hostPath:
path: {{ $root.Values.persistence.hostPath.logs }}
type: DirectoryOrCreate
{{- else }}
- name: logs
emptyDir: {}
{{- end }}
{{- end }}
{{- if .Values.features.mailerWorker }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mailer-worker
labels:
app.kubernetes.io/component: mailer-worker
{{- include "onelab.labels" $root | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ $root.Values.syncWaves.apps | quote }}
spec:
replicas: {{ $root.Values.replicas.mailer | default 1 }}
selector:
matchLabels:
app.kubernetes.io/component: mailer-worker
app.kubernetes.io/name: {{ include "onelab.name" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/component: mailer-worker
app.kubernetes.io/name: {{ include "onelab.name" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
spec:
{{- with $root.Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: mailer-worker
image: {{ printf "%s/%s:%s" $root.Values.images.registry "onelab-mailer-worker" $root.Values.images.tag | quote }}
volumeMounts:
- name: configurations
mountPath: /conf/configurations.yml
subPath: configurations.yml
readOnly: true
{{- if eq $root.Values.persistence.mode "hostPath" }}
- name: logs
mountPath: /logs
{{- end }}
volumes:
- name: configurations
secret:
secretName: onelab-configurations
{{- if eq $root.Values.persistence.mode "hostPath" }}
- name: logs
hostPath:
path: {{ $root.Values.persistence.hostPath.logs }}
type: DirectoryOrCreate
{{- else }}
- name: logs
emptyDir: {}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,53 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: redis
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/component: redis
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: redis
image: {{ printf "%s/%s:%s" .Values.images.registry .Values.images.redis .Values.images.redisTag | quote }}
ports:
- containerPort: 6379
name: redis
{{- with .Values.redis.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
{{- include "onelab.labels" . | nindent 4 }}
spec:
type: ClusterIP
ports:
- port: 6379
targetPort: redis
name: redis
selector:
app.kubernetes.io/component: redis
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -0,0 +1,73 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: revproxy
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.apps | quote }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: revproxy
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/component: revproxy
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: nginx
image: {{ printf "%s/%s:%s" .Values.images.registry .Values.images.nginx .Values.images.nginxTag | quote }}
ports:
- containerPort: 80
name: http
volumeMounts:
- name: nginx
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: nginx
mountPath: /data/error-404.html
subPath: error-404.html
- name: data
mountPath: /data
volumes:
- name: nginx
configMap:
name: onelab-revproxy
- name: data
{{- if eq .Values.persistence.mode "hostPath" }}
hostPath:
path: {{ .Values.persistence.hostPath.data }}
type: DirectoryOrCreate
{{- else }}
emptyDir: {}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: revproxy
labels:
{{- include "onelab.labels" . | nindent 4 }}
spec:
type: {{ .Values.revproxy.serviceType }}
ports:
- port: 80
targetPort: http
name: http
{{- if and (eq .Values.revproxy.serviceType "NodePort") .Values.revproxy.nodePort }}
nodePort: {{ .Values.revproxy.nodePort }}
{{- end }}
selector:
app.kubernetes.io/component: revproxy
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -0,0 +1,13 @@
{{- $cfg := fromYaml (.Files.AsConfig) }}
apiVersion: v1
kind: Secret
metadata:
name: onelab-configurations
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }}
type: Opaque
stringData:
configurations.yml: |
{{- tpl (index $cfg "configurations.gotmpl") . | nindent 4 }}

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: Secret
metadata:
name: onelab-postgres
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.postgres | quote }}
type: Opaque
stringData:
postgres-password: {{ .Values.postgresql.auth.password | quote }}

View File

@@ -0,0 +1,18 @@
{{- if .Values.rabbitmq.tls.embed }}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.rabbitmq.tls.secretName }}
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }}
type: Opaque
stringData:
rabbit.crt: |
{{ .Values.rabbitmq.tls.crt | nindent 4 }}
rabbit.key: |
{{ .Values.rabbitmq.tls.key | nindent 4 }}
rabbit.fullchain.pem: |
{{ .Values.rabbitmq.tls.fullchain | nindent 4 }}
{{- end }}

View File

@@ -0,0 +1,18 @@
apiVersion: v1
kind: Service
metadata:
name: db
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.postgres | quote }}
spec:
type: ClusterIP
ports:
- port: 5432
targetPort: postgres
name: postgres
selector:
app.kubernetes.io/component: postgres
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: rabbitmq
labels:
{{- include "onelab.labels" . | nindent 4 }}
spec:
type: ClusterIP
ports:
- port: 5671
targetPort: amqps
name: amqps
selector:
app.kubernetes.io/component: rabbitmq
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -0,0 +1,61 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: db
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.postgres | quote }}
spec:
replicas: 1
serviceName: db
selector:
matchLabels:
app.kubernetes.io/component: postgres
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/component: postgres
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: postgres
image: {{ printf "%s/%s:%s" .Values.images.registry .Values.images.postgres .Values.images.postgresTag | quote }}
ports:
- containerPort: 5432
name: postgres
env:
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_DB
value: postgres
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: onelab-postgres
key: postgres-password
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
{{- with .Values.postgresql.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.postgres.size | quote }}

View File

@@ -0,0 +1,80 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rabbitmq
labels:
{{- include "onelab.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ .Values.syncWaves.statefulDeps | quote }}
spec:
replicas: 1
serviceName: rabbitmq
selector:
matchLabels:
app.kubernetes.io/component: rabbitmq
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/component: rabbitmq
app.kubernetes.io/name: {{ include "onelab.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
hostname: onelab
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: rabbitmq
image: {{ printf "%s/%s:%s" .Values.images.registry .Values.images.rabbitmq .Values.images.rabbitmqTag | quote }}
ports:
- containerPort: 5671
name: amqps
volumeMounts:
- name: rabbit-data
mountPath: /var/lib/rabbitmq/mnesia
- name: rabbit-config
mountPath: /etc/rabbitmq/rabbitmq.conf
subPath: rabbit.conf
- name: rabbit-config
mountPath: /etc/rabbitmq/advanced.conf
subPath: advanced.conf
- name: rabbit-config
mountPath: /etc/rabbitmq/enabled_plugins
subPath: enable_plugins
- name: rabbit-config
mountPath: /opt/definitions.json
subPath: definitions.json
- name: rabbit-tls
mountPath: /etc/rabbitmq/ssl/rabbit.crt
subPath: rabbit.crt
- name: rabbit-tls
mountPath: /etc/rabbitmq/ssl/rabbit.key
subPath: rabbit.key
- name: rabbit-tls
mountPath: /etc/rabbitmq/ssl/rabbit.fullchain.pem
subPath: rabbit.fullchain.pem
{{- with .Values.rabbitmq.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
- name: rabbit-config
configMap:
name: onelab-rabbit-config
- name: rabbit-tls
secret:
secretName: {{ .Values.rabbitmq.tls.secretName }}
volumeClaimTemplates:
- metadata:
name: rabbit-data
spec:
accessModes: ["ReadWriteOnce"]
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.rabbitmq.size | quote }}

View File

@@ -0,0 +1,116 @@
{{- $root := . }}
{{- range .Values.workloads }}
{{- $n := .replicas | default 1 | int }}
{{- if and .replicaKey (hasKey $root.Values.replicas .replicaKey) }}
{{- $n = index $root.Values.replicas .replicaKey | int }}
{{- end }}
{{- $vols := or .config (not (empty .mounts)) }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .name }}
labels:
app.kubernetes.io/component: {{ .name }}
{{- include "onelab.labels" $root | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: {{ $root.Values.syncWaves.apps | quote }}
spec:
replicas: {{ $n }}
selector:
matchLabels:
app.kubernetes.io/component: {{ .name }}
app.kubernetes.io/name: {{ include "onelab.name" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/component: {{ .name }}
app.kubernetes.io/name: {{ include "onelab.name" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
spec:
{{- with $root.Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .name }}
image: {{ printf "%s/%s:%s" $root.Values.images.registry .image $root.Values.images.tag | quote }}
{{- if .port }}
ports:
- containerPort: {{ .port }}
name: http
{{- end }}
{{- if and .website (not $root.Values.website.ssr) }}
env:
- name: RENDERING_MODE
value: "no-ssr"
{{- end }}
{{- if $vols }}
volumeMounts:
{{- if .config }}
- name: configurations
mountPath: /conf/configurations.yml
subPath: configurations.yml
readOnly: true
{{- end }}
{{- if has "logs" .mounts }}
- name: logs
mountPath: /logs
{{- end }}
{{- if has "data" .mounts }}
- name: data
mountPath: /data
{{- end }}
{{- if has "shared" .mounts }}
- name: data
mountPath: /shared-inputs
subPath: shared/inputs
- name: data
mountPath: /shared-archived
subPath: shared/archived
{{- end }}
{{- end }}
{{- if $vols }}
volumes:
{{- if .config }}
- name: configurations
secret:
secretName: onelab-configurations
{{- end }}
{{- if eq $root.Values.persistence.mode "hostPath" }}
{{- if has "logs" .mounts }}
- name: logs
hostPath:
path: {{ $root.Values.persistence.hostPath.logs }}
type: DirectoryOrCreate
{{- end }}
{{- if or (has "data" .mounts) (has "shared" .mounts) }}
- name: data
hostPath:
path: {{ $root.Values.persistence.hostPath.data }}
type: DirectoryOrCreate
{{- end }}
{{- end }}
{{- end }}
{{- if and .port (gt (int .port) 0) }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .name }}
labels:
app.kubernetes.io/component: {{ .name }}
{{- include "onelab.labels" $root | nindent 4 }}
spec:
type: ClusterIP
ports:
- port: {{ .port }}
targetPort: http
name: http
selector:
app.kubernetes.io/component: {{ .name }}
app.kubernetes.io/name: {{ include "onelab.name" $root }}
app.kubernetes.io/instance: {{ $root.Release.Name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,187 @@
# Default values for onelab — override per environment (see gitops/values/).
nameOverride: ""
fullnameOverride: ""
images:
registry: hub.andrewalliance.com/releases
tag: "1.27.0"
nginx: nginx
nginxTag: "1.29.5-alpine"
postgres: postgres
postgresTag: "17.8"
redis: redis
redisTag: "7.4.7-alpine"
rabbitmq: rabbitmq
rabbitmqTag: "3.13.7"
imagePullSecrets: []
# - name: hub-andrewalliance
# hostPath: matches typical single-node Swarm-style install (shared /data and /logs).
# Use persistence.mode: pvc + a ReadWriteMany class for multi-node shared storage.
persistence:
mode: hostPath
storageClass: ""
hostPath:
data: /opt/onelab/data
logs: /opt/onelab/logs
postgres:
size: 20Gi
rabbitmq:
size: 5Gi
postgresql:
auth:
password: "changeme-use-strong-password"
resources: {}
redis:
resources: {}
rabbitmq:
resources: {}
# TLS: create Secret `onelab-rabbit-tls` in the release namespace (see README), or set embed: true.
tls:
secretName: onelab-rabbit-tls
embed: false
crt: ""
key: ""
fullchain: ""
syncWaves:
postgres: "-3"
statefulDeps: "-2"
apps: "0"
onelab:
domain: "https://localhost"
mailer:
noreply: "no-reply@andrewalliance.com"
secrets:
authTokenKey: "replace-auth-token-key"
monitoringToken: "replace-monitoring-token"
rabbitToken: "replace-rabbit-token"
intercom:
appid: ""
secret: "replace-intercom-secret"
features:
ldapWorker: false
mailerWorker: false
website:
ssr: true
revproxy:
serviceType: NodePort
nodePort: 30080
ipv6Listen: true
# Replica counts (api.apidevice etc. override defaults in templates/workloads.yaml via this map)
replicas:
api: 2
apidevice: 1
apirabbit: 1
devices: 1
experiments: 1
images: 1
manual: 1
ws: 1
ldap: 1
mailer: 1
resources: {}
workloads:
- name: supervisor
image: onelab-supervisor-worker
replicas: 1
port: 0
config: true
mounts: [logs, data]
- name: file-worker
image: onelab-file-worker
replicas: 1
port: 0
config: true
mounts: [logs, data, shared]
- name: api
image: onelab-api
replicaKey: api
port: 3000
config: true
mounts: [logs, data]
- name: api-device
image: onelab-api-device
replicaKey: apidevice
port: 3000
config: true
mounts: [logs, data]
- name: api-rabbit
image: onelab-api-rabbit
replicaKey: apirabbit
port: 3000
config: true
mounts: [logs, data]
- name: devices-worker
image: onelab-devices-worker
replicaKey: devices
port: 0
config: true
mounts: [logs, data]
- name: experiments-worker
image: onelab-experiments-worker
replicaKey: experiments
port: 0
config: true
mounts: [logs]
- name: images-worker
image: onelab-images-worker
replicaKey: images
port: 0
config: true
mounts: [logs, data]
- name: manual-worker
image: onelab-manual-worker
replicaKey: manual
port: 0
config: true
mounts: [logs]
- name: websocket-worker
image: onelab-websocket-worker
replicaKey: ws
port: 3030
config: true
mounts: [logs]
- name: static
image: onelab-static
replicas: 1
port: 80
config: false
mounts: []
- name: main
image: onelab-main
replicas: 1
port: 80
config: false
mounts: []
- name: designer
image: onelab-designer
replicas: 1
port: 80
config: false
mounts: []
- name: runner
image: onelab-runner
replicas: 1
port: 80
config: false
mounts: []
- name: website
image: onelab-website
replicas: 1
port: 4000
config: false
mounts: []
website: true

View File

@@ -0,0 +1,31 @@
# k3s / Argo CD overlay (private Git — rotate secrets if this file is ever made public).
# Add image pull credentials when using hub.andrewalliance.com:
# kubectl create secret docker-registry hub-andrewalliance -n onelab \
# --docker-server=hub.andrewalliance.com --docker-username=... --docker-password=...
# then set imagePullSecrets below.
imagePullSecrets: []
persistence:
mode: hostPath
hostPath:
data: /opt/onelab/data
logs: /opt/onelab/logs
postgresql:
auth:
password: "9daLpcV7vKS1zXUElQRO5h4u"
onelab:
domain: "https://onelab.example.com"
secrets:
authTokenKey: "ntH0Yd3AcsqwMu7ah8xLbWFS4BK5GUmi"
monitoringToken: "Cj4ix7wdg8XPIsDAFENKRTmh6lkvBLZp"
rabbitToken: "GmSWRv14PXZuyM5QDgb8wpxk0dh7F6IJ"
intercom:
appid: ""
secret: ""
revproxy:
serviceType: NodePort
nodePort: 30080