From 8597cf573029361235d9e259da485b03d4221ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Wed, 12 Aug 2026 13:29:44 +0200 Subject: [PATCH] feat: add charts template and normalize oxicloud's chart with it --- .gitignore | 4 + README.md | 6 + k8s/charts/README.md | 36 +++++ k8s/charts/_template/.helmignore | 13 ++ k8s/charts/_template/Chart.yaml | 16 +++ k8s/charts/_template/README.md | 105 ++++++++++++++ k8s/charts/_template/templates/_helpers.tpl | 37 +++++ k8s/charts/_template/templates/configmap.yaml | 12 ++ .../_template/templates/deployment.yaml | 68 +++++++++ k8s/charts/_template/templates/ingress.yaml | 41 ++++++ k8s/charts/_template/templates/pvc.yaml | 17 +++ k8s/charts/_template/templates/secret.yaml | 13 ++ k8s/charts/_template/templates/service.yaml | 15 ++ k8s/charts/_template/values.yaml | 60 ++++++++ k8s/charts/oxicloud/Chart.yaml | 12 +- k8s/charts/oxicloud/README.md | 133 ++++++++++++++++++ k8s/charts/oxicloud/templates/configmap.yaml | 20 +-- k8s/charts/oxicloud/templates/ingress.yaml | 26 ++-- k8s/charts/oxicloud/templates/secret.yaml | 17 ++- k8s/charts/oxicloud/templates/service.yaml | 6 +- .../oxicloud/templates/statefulset.yaml | 52 +++++-- .../oxicloud/templates/wopi-deployment.yaml | 17 ++- .../oxicloud/templates/wopi-service.yaml | 4 +- k8s/charts/oxicloud/values.yaml | 131 ++++++++++------- scripts/update-argo.sh | 129 ----------------- 25 files changed, 745 insertions(+), 245 deletions(-) create mode 100644 k8s/charts/README.md create mode 100644 k8s/charts/_template/.helmignore create mode 100644 k8s/charts/_template/Chart.yaml create mode 100644 k8s/charts/_template/README.md create mode 100644 k8s/charts/_template/templates/_helpers.tpl create mode 100644 k8s/charts/_template/templates/configmap.yaml create mode 100644 k8s/charts/_template/templates/deployment.yaml create mode 100644 k8s/charts/_template/templates/ingress.yaml create mode 100644 k8s/charts/_template/templates/pvc.yaml create mode 100644 k8s/charts/_template/templates/secret.yaml create mode 100644 k8s/charts/_template/templates/service.yaml create mode 100644 k8s/charts/_template/values.yaml create mode 100644 k8s/charts/oxicloud/README.md delete mode 100755 scripts/update-argo.sh diff --git a/.gitignore b/.gitignore index 79cc54d..7d11c76 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,7 @@ secrets.yaml *~ *.tgz STABILITY.md +AGENTS.md +k8s/charts/README.md +.opencode/ +opencode.json diff --git a/README.md b/README.md index eddc5c0..ac42e9c 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,9 @@ ```bash ethtool -K eno1 tx off rx off ``` + +- upgrade argo + +```bash +helm upgrade argo-cd argo/argo-cd -n argo-cd -f k8s/helm/argo-cd/values.yaml +``` diff --git a/k8s/charts/README.md b/k8s/charts/README.md new file mode 100644 index 0000000..959dd6f --- /dev/null +++ b/k8s/charts/README.md @@ -0,0 +1,36 @@ +# Helm Charts + +This directory contains Helm charts for self-hosted services. + +## Chart Template + +Use the `_template/` directory as a starting point for new charts: + +```bash +cp -r k8s/charts/_template k8s/charts/my-new-service +# Edit Chart.yaml, values.yaml, and update image/repository +``` + +The template includes standard Kubernetes resources (Deployment, Service, Ingress, PVC, ConfigMap, Secret) with sensible defaults. + +## Charts + +| Chart | Version | Status | +|-------|---------|--------| +| dokuwiki | 0.1.0 | Needs normalization | +| helm-rustical | 0.3.0 | Needs normalization | +| immich | 0.2.3 | Partially normalized | +| miniflux | 0.1.0 | Needs normalization | +| oxicloud | 1.0.0 | Normalized | +| vaultwarden | 0.1.0 | Needs normalization | + +## Normalization + +Charts should follow a consistent structure: +- Values nested under chart name key +- LoadBalancer service type +- PVC named `-data` +- Secrets via existingSecretName +- Proper probes and resource limits + +See individual chart READMEs for configuration details. diff --git a/k8s/charts/_template/.helmignore b/k8s/charts/_template/.helmignore new file mode 100644 index 0000000..c113420 --- /dev/null +++ b/k8s/charts/_template/.helmignore @@ -0,0 +1,13 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching. +*.tgz +.env +.git +.git_cache +.idea +*.md +docs/* +tmp/* +vendor/* +.travis.yml +.testignore diff --git a/k8s/charts/_template/Chart.yaml b/k8s/charts/_template/Chart.yaml new file mode 100644 index 0000000..ad3c94c --- /dev/null +++ b/k8s/charts/_template/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +name: chart +description: A Helm chart template with standardized structure +type: application +version: 0.1.0 +appVersion: "latest" +annotations: + artifacthub.io/images: | + - name: chart + image: nginx:latest + artifacthub.io/changes: | + - kind: added + description: Initial chart template + artifacthub.io/maintainers: | + - name: catalin + email: catalin@example.com diff --git a/k8s/charts/_template/README.md b/k8s/charts/_template/README.md new file mode 100644 index 0000000..3be5ac7 --- /dev/null +++ b/k8s/charts/_template/README.md @@ -0,0 +1,105 @@ +# chart + +A Helm chart template with standardized structure for self-hosted services. + +## TL;DR + +```bash +helm install chart oci://git.roboces.dev/catalin/fukuops/chart --version 1.0.0 -n apps-roboces +``` + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3+ +- A NFS storage class (default: `truenas-nfs-csi`) - only if persistence is enabled +- An existing `Secret` with credentials if required (see `secret.existingSecretName`) + +## Configuration + +### Basic Configuration + +```yaml +service: + type: LoadBalancer + port: 80 +ingress: + enabled: true + className: "traefik" + hosts: + - host: chart.example.com + paths: + - path: / + pathType: Prefix +``` + +### With Persistence + +```yaml +persistence: + enabled: true + storageClass: "truenas-nfs-csi" + accessMode: ReadWriteMany + size: 10Gi +``` + +### With Secrets + +```yaml +secret: + existingSecretName: chart-secrets +``` + +## Values Reference + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `replicaCount` | int | `1` | Number of replicas | +| `image.repository` | string | `nginx` | Image repository | +| `image.pullPolicy` | string | `IfNotPresent` | Image pull policy | +| `image.tag` | string | `.Chart.AppVersion` | Image tag | +| `service.type` | string | `LoadBalancer` | Service type | +| `service.port` | int | `80` | Service port | +| `service.targetPort` | int | `80` | Container port | +| `ingress.enabled` | bool | `false` | Enable ingress | +| `ingress.className` | string | `traefik` | Ingress class | +| `persistence.enabled` | bool | `false` | Enable persistence | +| `persistence.storageClass` | string | `truenas-nfs-csi` | Storage class | +| `persistence.accessMode` | string | `ReadWriteMany` | Access mode | +| `persistence.size` | string | `1Gi` | PVC size | +| `persistence.name` | string | `chart-data` | PVC name | +| `secret.existingSecretName` | string | `""` | Existing secret name | +| `resources` | object | `{}` | Container resources | +| `livenessProbe` | object | (see values.yaml) | Liveness probe | +| `readinessProbe` | object | (see values.yaml) | Readiness probe | +| `nodeSelector` | object | `{}` | Node selector | +| `tolerations` | array | `[]` | Tolerations | +| `affinity` | object | `{}` | Affinity rules | + +## Persistence + +When persistence is enabled, a PVC named `chart-data` is created. The PVC uses the `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode. + +## Secret Management + +The chart looks for an existing `Secret` with the name specified in `secret.existingSecretName`. If not provided, no secret is mounted. + +## Troubleshooting + +### Pod not starting + +```bash +kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=chart +``` + +### Check logs + +```bash +kubectl logs chart-0 -n apps-roboces +``` + +### Verify configmap + +```bash +kubectl get configmap chart-config -n apps-roboces -o yaml +``` diff --git a/k8s/charts/_template/templates/_helpers.tpl b/k8s/charts/_template/templates/_helpers.tpl new file mode 100644 index 0000000..4c5e44f --- /dev/null +++ b/k8s/charts/_template/templates/_helpers.tpl @@ -0,0 +1,37 @@ +{{/* Expand the name of the chart. */}} +{{- define "name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "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 }} + +{{/* Common labels */}} +{{- define "labels" -}} +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{ include "selectorLabels" . }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* Selector labels */}} +{{- define "selectorLabels" -}} +app.kubernetes.io/name: {{ include "name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* Chart name as the PVC name */}} +{{- define "pvcName" -}} +{{- .Values.persistence.name | default (printf "%s-data" (include "name" .)) }} +{{- end }} diff --git a/k8s/charts/_template/templates/configmap.yaml b/k8s/charts/_template/templates/configmap.yaml new file mode 100644 index 0000000..83ae129 --- /dev/null +++ b/k8s/charts/_template/templates/configmap.yaml @@ -0,0 +1,12 @@ +{{- if .Values.config }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "fullname" . }}-config + labels: + {{- include "labels" . | nindent 4 }} +data: + {{- range $key, $value := .Values.config }} + {{ $key }}: {{ $value | quote }} + {{- end }} +{{- end }} diff --git a/k8s/charts/_template/templates/deployment.yaml b/k8s/charts/_template/templates/deployment.yaml new file mode 100644 index 0000000..d22f643 --- /dev/null +++ b/k8s/charts/_template/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "selectorLabels" . | nindent 8 }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + {{- if .Values.config }} + envFrom: + - configMapRef: + name: {{ include "fullname" . }}-config + {{- end }} + {{- if .Values.secret.existingSecretName }} + envFrom: + - secretRef: + name: {{ .Values.secret.existingSecretName }} + {{- end }} + {{- if .Values.persistence.enabled }} + volumeMounts: + - name: {{ include "pvcName" . }} + mountPath: /data + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if not .Values.persistence.enabled }} + volumes: + - name: {{ include "pvcName" . }} + emptyDir: {} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/k8s/charts/_template/templates/ingress.yaml b/k8s/charts/_template/templates/ingress.yaml new file mode 100644 index 0000000..3917052 --- /dev/null +++ b/k8s/charts/_template/templates/ingress.yaml @@ -0,0 +1,41 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.className }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType | default "Prefix" }} + backend: + service: + name: {{ include "fullname" $ }} + port: + number: {{ $.Values.service.port }} + {{- end }} + {{- end }} +{{- end }} diff --git a/k8s/charts/_template/templates/pvc.yaml b/k8s/charts/_template/templates/pvc.yaml new file mode 100644 index 0000000..44448fd --- /dev/null +++ b/k8s/charts/_template/templates/pvc.yaml @@ -0,0 +1,17 @@ +{{- if .Values.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "pvcName" . }} + labels: + {{- include "labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.persistence.accessMode | default "ReadWriteMany" }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | default "1Gi" }} +{{- end }} diff --git a/k8s/charts/_template/templates/secret.yaml b/k8s/charts/_template/templates/secret.yaml new file mode 100644 index 0000000..7305ca0 --- /dev/null +++ b/k8s/charts/_template/templates/secret.yaml @@ -0,0 +1,13 @@ +{{- if and .Values.secret.enabled (not .Values.secret.existingSecretName) .Values.secret.data }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "fullname" . }}-secret + labels: + {{- include "labels" . | nindent 4 }} +type: Opaque +data: + {{- range $key, $value := .Values.secret.data }} + {{ $key }}: {{ $value | b64enc | quote }} + {{- end }} +{{- end }} diff --git a/k8s/charts/_template/templates/service.yaml b/k8s/charts/_template/templates/service.yaml new file mode 100644 index 0000000..c00186b --- /dev/null +++ b/k8s/charts/_template/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "selectorLabels" . | nindent 4 }} diff --git a/k8s/charts/_template/values.yaml b/k8s/charts/_template/values.yaml new file mode 100644 index 0000000..acee144 --- /dev/null +++ b/k8s/charts/_template/values.yaml @@ -0,0 +1,60 @@ +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + tag: "" + +service: + type: LoadBalancer + port: 80 + targetPort: 80 + +ingress: + enabled: false + className: "traefik" + annotations: {} + hosts: + - host: chart.example.com + paths: + - path: / + pathType: Prefix + tls: [] + +config: {} + +persistence: + enabled: false + storageClass: "truenas-nfs-csi" + accessMode: ReadWriteMany + size: 1Gi + name: "chart-data" + +resources: {} + +livenessProbe: + tcpSocket: + port: 80 + initialDelaySeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 3 + periodSeconds: 10 + +readinessProbe: + tcpSocket: + port: 80 + initialDelaySeconds: 15 + timeoutSeconds: 2 + successThreshold: 1 + failureThreshold: 3 + periodSeconds: 10 + +nodeSelector: {} +tolerations: [] +affinity: {} + +secret: + enabled: true + existingSecretName: "" + data: {} diff --git a/k8s/charts/oxicloud/Chart.yaml b/k8s/charts/oxicloud/Chart.yaml index b1be9f3..738f76d 100644 --- a/k8s/charts/oxicloud/Chart.yaml +++ b/k8s/charts/oxicloud/Chart.yaml @@ -5,9 +5,13 @@ description: 'Ultra-fast, secure & lightweight self-hosted cloud storage — you ' type: application -version: 0.1.0 +version: 1.0.0 appVersion: 0.8.6 annotations: - artifacthub.io/images: | - - name: oxicloud - image: diocrafts/oxicloud:0.8.6 + artifacthub.io/images: "- name: oxicloud\n image: diocrafts/oxicloud:0.8.6\n" + artifacthub.io/changes: "- kind: changed\n description: \"Normalized structure\ + \ to use hierarchical values under `oxicloud:` key\"\n- kind: changed\n description:\ + \ \"Changed service type default to LoadBalancer\"\n- kind: changed\n description:\ + \ \"Renamed PVC to `oxicloud-data`\"\n- kind: changed\n description: \"Disabled\ + \ WOPI/Collabora by default\"\n- kind: security\n description: \"Removed hardcoded\ + \ credentials from values.yaml; use existingSecretName instead\"\n" diff --git a/k8s/charts/oxicloud/README.md b/k8s/charts/oxicloud/README.md new file mode 100644 index 0000000..bcb9dae --- /dev/null +++ b/k8s/charts/oxicloud/README.md @@ -0,0 +1,133 @@ +# oxicloud + +Ultra-fast, secure & lightweight self-hosted cloud storage — your files, photos, calendars & contacts, all in one place. Built in Rust. + +## TL;DR + +```bash +helm install oxicloud oci://git.roboces.dev/catalin/fukuops/oxicloud --version 1.0.0 -n apps-roboces +``` + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3+ +- A NFS storage class (default: `truenas-nfs-csi`) +- An existing `Secret` containing: + - `OXICLOUD_JWT_SECRET` (optional - generated if not provided) + +## Configuration + +All values are nested under the `oxicloud:` key. Example: + +```yaml +oxicloud: + persistence: + enabled: true + storageClass: "truenas-nfs-csi" + accessMode: ReadWriteMany + size: 50Gi + service: + type: LoadBalancer + port: 8086 + config: + server: + port: 8086 + host: "0.0.0.0" + baseUrl: "https://cloud.example.com" + features: + enableAuth: true + enableSharing: true + secret: + existingSecretName: oxicloud + wopi: + enabled: false + ingress: + enabled: true + className: "traefik" + hosts: + - host: cloud.example.com + paths: + - path: / + pathType: ImplementationSpecific +``` + +## Values Reference + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `oxicloud.replicaCount` | int | `1` | Number of replicas | +| `oxicloud.image.repository` | string | `diocrafts/oxicloud` | Image repository | +| `oxicloud.image.pullPolicy` | string | `Always` | Image pull policy | +| `oxicloud.image.tag` | string | `.Chart.AppVersion` | Image tag | +| `oxicloud.service.type` | string | `LoadBalancer` | Service type | +| `oxicloud.service.port` | int | `8086` | Service port | +| `oxicloud.config.server.port` | int | `8086` | Server port | +| `oxicloud.config.server.host` | string | `0.0.0.0` | Server host | +| `oxicloud.config.server.baseUrl` | string | `https://cloud.example.com` | Public base URL | +| `oxicloud.config.features.enableAuth` | bool | `true` | Enable authentication | +| `oxicloud.config.features.enableSharing` | bool | `true` | Enable file sharing | +| `oxicloud.persistence.enabled` | bool | `true` | Enable persistence | +| `oxicloud.persistence.storageClass` | string | `""` | Storage class | +| `oxicloud.persistence.accessMode` | string | `ReadWriteOnce` | Access mode | +| `oxicloud.persistence.size` | string | `50Gi` | PVC size | +| `oxicloud.secret.existingSecretName` | string | `""` | Existing secret name | +| `oxicloud.wopi.enabled` | bool | `false` | Enable WOPI/Collabora integration | +| `oxicloud.ingress.enabled` | bool | `false` | Enable ingress | +| `oxicloud.ingress.className` | string | `""` | Ingress class | +| `oxicloud.resources` | object | `{}` | Container resources | +| `oxicloud.livenessProbe` | object | (see values.yaml) | Liveness probe | +| `oxicloud.readinessProbe` | object | (see values.yaml) | Readiness probe | + +## WOPI/Collabora + +WOPI integration is disabled by default. To enable it: + +```yaml +oxicloud: + wopi: + enabled: true + collabora: + url: "cloud.example.com" + domain: "cloud.example.com" + image: + repository: collabora/code + tag: latest + service: + port: 9980 + admin: + existingSecretName: "collabora-admin" +``` + +When enabling WOPI, you must provide an existing `Secret` containing `WOPI_ADMIN_USERNAME` and `WOPI_ADMIN_PASSWORD` keys. + +## Persistence + +The chart creates a `StatefulSet` with a PVC named `oxicloud-data`. The PVC uses the `truenas-nfs-csi` storage class by default (when not specified). + +## Secret Management + +The chart looks for an existing `Secret` with the name specified in `oxicloud.secret.existingSecretName`. If not provided, a chart-managed secret will be created (only if additional secrets are needed). + +Required secret keys depend on configuration: +- `OXICLOUD_JWT_SECRET` - JWT signing secret (recommended) +- `WOPI_ADMIN_USERNAME` / `WOPI_ADMIN_PASSWORD` - Collabora admin credentials (when WOPI enabled) + +## Troubleshooting + +### Pod not starting + +Check if the PVC is bound: +```bash +kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=oxicloud +``` + +### Check logs +```bash +kubectl logs oxicloud-0 -n apps-roboces +``` + +### Verify configmap +```bash +kubectl get configmap oxicloud-config -n apps-roboces -o yaml +``` diff --git a/k8s/charts/oxicloud/templates/configmap.yaml b/k8s/charts/oxicloud/templates/configmap.yaml index edd8d27..e38403a 100644 --- a/k8s/charts/oxicloud/templates/configmap.yaml +++ b/k8s/charts/oxicloud/templates/configmap.yaml @@ -4,19 +4,19 @@ kind: ConfigMap metadata: name: {{ include "oxicloud.fullname" . }}-config data: - OXICLOUD_SERVER_PORT: {{ .Values.config.server.port | quote }} - OXICLOUD_SERVER_HOST: {{ .Values.config.server.host | quote }} - {{- if .Values.config.server.baseUrl }} - OXICLOUD_BASE_URL: {{ .Values.config.server.baseUrl | quote }} + OXICLOUD_SERVER_PORT: {{ .Values.oxicloud.config.server.port | quote }} + OXICLOUD_SERVER_HOST: {{ .Values.oxicloud.config.server.host | quote }} + {{- if .Values.oxicloud.config.server.baseUrl }} + OXICLOUD_BASE_URL: {{ .Values.oxicloud.config.server.baseUrl | quote }} {{- end }} - OXICLOUD_ENABLE_AUTH: {{ .Values.config.features.enableAuth | quote }} - OXICLOUD_ENABLE_FILE_SHARING: {{ .Values.config.features.enableSharing | quote }} - MIMALLOC_PURGE_DELAY: {{ .Values.config.mimalloc.purgeDelay | quote }} - MIMALLOC_ALLOW_LARGE_OS_PAGES: {{ .Values.config.mimalloc.allowLargeOsPages | quote }} + OXICLOUD_ENABLE_AUTH: {{ .Values.oxicloud.config.features.enableAuth | quote }} + OXICLOUD_ENABLE_FILE_SHARING: {{ .Values.oxicloud.config.features.enableSharing | quote }} + MIMALLOC_PURGE_DELAY: {{ .Values.oxicloud.config.mimalloc.purgeDelay | quote }} + MIMALLOC_ALLOW_LARGE_OS_PAGES: {{ .Values.oxicloud.config.mimalloc.allowLargeOsPages | quote }} - {{- if .Values.wopi.enabled }} + {{- if .Values.oxicloud.wopi.enabled }} OXICLOUD_WOPI_ENABLED: "true" - OXICLOUD_WOPI_DISCOVERY_URL: "{{ .Values.config.server.baseUrl }}/hosting/discovery" + OXICLOUD_WOPI_DISCOVERY_URL: "{{ .Values.oxicloud.config.server.baseUrl }}/hosting/discovery" {{- else }} OXICLOUD_WOPI_ENABLED: "false" {{- end }} diff --git a/k8s/charts/oxicloud/templates/ingress.yaml b/k8s/charts/oxicloud/templates/ingress.yaml index ab3a14b..d2a2ff2 100644 --- a/k8s/charts/oxicloud/templates/ingress.yaml +++ b/k8s/charts/oxicloud/templates/ingress.yaml @@ -1,22 +1,22 @@ --- -{{- if .Values.ingress.enabled -}} +{{- if .Values.oxicloud.ingress.enabled -}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ include "oxicloud.fullname" . }} labels: {{- include "oxicloud.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} + {{- with .Values.oxicloud.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} {{- end }} spec: - {{- if .Values.ingress.className }} - ingressClassName: {{ .Values.ingress.className }} + {{- if .Values.oxicloud.ingress.className }} + ingressClassName: {{ .Values.oxicloud.ingress.className }} {{- end }} - {{- if .Values.ingress.tls }} + {{- if .Values.oxicloud.ingress.tls }} tls: - {{- range .Values.ingress.tls }} + {{- range .Values.oxicloud.ingress.tls }} - hosts: {{- range .hosts }} - {{ . | quote }} @@ -25,40 +25,38 @@ spec: {{- end }} {{- end }} rules: - - host: {{ (index .Values.ingress.hosts 0).host | quote }} + - host: {{ (index .Values.oxicloud.ingress.hosts 0).host | quote }} http: paths: - {{- if .Values.wopi.enabled }} - # Route Collabora traffic to the WOPI pod + {{- if .Values.oxicloud.wopi.enabled }} - path: /browser pathType: Prefix backend: service: name: {{ include "oxicloud.fullname" $ }}-wopi port: - number: {{ .Values.wopi.collabora.service.port }} + number: {{ .Values.oxicloud.wopi.collabora.service.port }} - path: /hosting pathType: Prefix backend: service: name: {{ include "oxicloud.fullname" $ }}-wopi port: - number: {{ .Values.wopi.collabora.service.port }} + number: {{ .Values.oxicloud.wopi.collabora.service.port }} - path: /cool pathType: Prefix backend: service: name: {{ include "oxicloud.fullname" $ }}-wopi port: - number: {{ .Values.wopi.collabora.service.port }} + number: {{ .Values.oxicloud.wopi.collabora.service.port }} {{- end }} - # Default Catch-All: Route everything else to OxiCloud - path: / pathType: Prefix backend: service: name: {{ include "oxicloud.fullname" $ }} port: - number: {{ $.Values.service.port }} + number: {{ $.Values.oxicloud.service.port }} {{- end }} diff --git a/k8s/charts/oxicloud/templates/secret.yaml b/k8s/charts/oxicloud/templates/secret.yaml index d5aac3c..3c9fafb 100644 --- a/k8s/charts/oxicloud/templates/secret.yaml +++ b/k8s/charts/oxicloud/templates/secret.yaml @@ -1,5 +1,5 @@ --- -{{- if not .Values.secrets.existingSecret }} +{{- if not .Values.oxicloud.secret.existingSecretName }} apiVersion: v1 kind: Secret metadata: @@ -8,12 +8,15 @@ metadata: {{- include "oxicloud.labels" . | nindent 4 }} type: Opaque data: - {{- if .Values.secrets.jwtSecret }} - OXICLOUD_JWT_SECRET: {{ .Values.secrets.jwtSecret | b64enc | quote }} + {{- if .Values.oxicloud.secret.data.jwtSecret }} + OXICLOUD_JWT_SECRET: {{ .Values.oxicloud.secret.data.jwtSecret | b64enc | quote }} + {{- end }} + {{- if .Values.oxicloud.wopi.enabled }} + {{- if .Values.oxicloud.wopi.collabora.admin.username }} + WOPI_ADMIN_USERNAME: {{ .Values.oxicloud.wopi.collabora.admin.username | b64enc | quote }} + {{- end }} + {{- if .Values.oxicloud.wopi.collabora.admin.password }} + WOPI_ADMIN_PASSWORD: {{ .Values.oxicloud.wopi.collabora.admin.password | b64enc | quote }} {{- end }} - DB_PASSWORD: {{ .Values.database.password | b64enc | quote }} - {{- if .Values.wopi.enabled }} - WOPI_ADMIN_USERNAME: {{ .Values.wopi.collabora.admin.username | b64enc | quote }} - WOPI_ADMIN_PASSWORD: {{ .Values.wopi.collabora.admin.password | b64enc | quote }} {{- end }} {{- end }} diff --git a/k8s/charts/oxicloud/templates/service.yaml b/k8s/charts/oxicloud/templates/service.yaml index b0a4bc8..9ae8571 100644 --- a/k8s/charts/oxicloud/templates/service.yaml +++ b/k8s/charts/oxicloud/templates/service.yaml @@ -6,9 +6,9 @@ metadata: labels: {{- include "oxicloud.labels" . | nindent 4 }} spec: - type: {{ .Values.service.type }} + type: {{ .Values.oxicloud.service.type }} ports: - - port: {{ .Values.service.port }} + - port: {{ .Values.oxicloud.service.port }} targetPort: http protocol: TCP name: http @@ -24,7 +24,7 @@ metadata: spec: clusterIP: None ports: - - port: {{ .Values.service.port }} + - port: {{ .Values.oxicloud.service.port }} targetPort: http protocol: TCP name: http diff --git a/k8s/charts/oxicloud/templates/statefulset.yaml b/k8s/charts/oxicloud/templates/statefulset.yaml index 442fac1..71526e8 100644 --- a/k8s/charts/oxicloud/templates/statefulset.yaml +++ b/k8s/charts/oxicloud/templates/statefulset.yaml @@ -6,7 +6,7 @@ metadata: {{- include "oxicloud.labels" . | nindent 4 }} spec: serviceName: {{ include "oxicloud.fullname" . }}-headless - replicas: {{ .Values.replicaCount }} + replicas: {{ .Values.oxicloud.replicaCount }} selector: matchLabels: {{- include "oxicloud.selectorLabels" . | nindent 6 }} @@ -17,37 +17,61 @@ spec: spec: containers: - name: oxicloud - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} + image: "{{ .Values.oxicloud.image.repository }}:{{ .Values.oxicloud.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.oxicloud.image.pullPolicy }} ports: - name: http - containerPort: 8086 + containerPort: {{ .Values.oxicloud.config.server.port }} protocol: TCP envFrom: - configMapRef: name: {{ include "oxicloud.fullname" . }}-config - secretRef: - name: {{ if .Values.secrets.existingSecret }}{{ .Values.secrets.existingSecret }}{{ else }}{{ include "oxicloud.fullname" . }}-secret{{ end }} + name: {{ if .Values.oxicloud.secret.existingSecretName }}{{ .Values.oxicloud.secret.existingSecretName }}{{ else }}{{ include "oxicloud.fullname" . }}-secret{{ end }} volumeMounts: - - name: storage-data + - name: oxicloud-data mountPath: /app/storage - {{- if not .Values.persistence.enabled }} + {{- with .Values.oxicloud.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.oxicloud.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.oxicloud.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if not .Values.oxicloud.persistence.enabled }} volumes: - - name: storage-data + - name: oxicloud-data emptyDir: {} {{- end }} + {{- with .Values.oxicloud.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.oxicloud.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.oxicloud.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} - {{- if .Values.persistence.enabled }} + {{- if .Values.oxicloud.persistence.enabled }} volumeClaimTemplates: - metadata: - name: storage-data + name: oxicloud-data spec: accessModes: - - {{ .Values.persistence.accessMode }} - {{- if .Values.persistence.storageClass }} - storageClassName: {{ .Values.persistence.storageClass }} + - {{ .Values.oxicloud.persistence.accessMode }} + {{- if .Values.oxicloud.persistence.storageClass }} + storageClassName: {{ .Values.oxicloud.persistence.storageClass }} {{- end }} resources: requests: - storage: {{ .Values.persistence.size }} + storage: {{ .Values.oxicloud.persistence.size }} {{- end }} diff --git a/k8s/charts/oxicloud/templates/wopi-deployment.yaml b/k8s/charts/oxicloud/templates/wopi-deployment.yaml index 0cdc0d4..93debc9 100644 --- a/k8s/charts/oxicloud/templates/wopi-deployment.yaml +++ b/k8s/charts/oxicloud/templates/wopi-deployment.yaml @@ -1,5 +1,5 @@ --- -{{- if .Values.wopi.enabled -}} +{{- if .Values.oxicloud.wopi.enabled -}} apiVersion: apps/v1 kind: Deployment metadata: @@ -21,9 +21,8 @@ spec: spec: containers: - name: collabora - image: "{{ .Values.wopi.collabora.image.repository }}:{{ .Values.wopi.collabora.image.tag }}" - imagePullPolicy: {{ .Values.wopi.collabora.image.pullPolicy }} - # Required for Collabora to build chroot jails + image: "{{ .Values.oxicloud.wopi.collabora.image.repository }}:{{ .Values.oxicloud.wopi.collabora.image.tag }}" + imagePullPolicy: {{ .Values.oxicloud.wopi.collabora.image.pullPolicy }} securityContext: capabilities: add: @@ -34,20 +33,20 @@ spec: protocol: TCP env: - name: aliasgroup1 - value: "http://{{ .Values.wopi.collabora.domain }}" + value: "http://{{ .Values.oxicloud.wopi.collabora.domain }}" - name: server_name - value: {{ .Values.wopi.collabora.domain | quote }} + value: {{ .Values.oxicloud.wopi.collabora.domain | quote }} - name: extra_params - value: {{ .Values.wopi.collabora.extraParams | quote }} + value: {{ .Values.oxicloud.wopi.collabora.extraParams | quote }} - name: username valueFrom: secretKeyRef: - name: {{ if .Values.secrets.existingSecret }}{{ .Values.secrets.existingSecret }}{{ else }}{{ include "oxicloud.fullname" . }}-secret{{ end }} + name: {{ if .Values.oxicloud.secret.existingSecretName }}{{ .Values.oxicloud.secret.existingSecretName }}{{ else if .Values.oxicloud.wopi.collabora.admin.existingSecretName }}{{ .Values.oxicloud.wopi.collabora.admin.existingSecretName }}{{ else }}{{ include "oxicloud.fullname" . }}-secret{{ end }} key: WOPI_ADMIN_USERNAME - name: password valueFrom: secretKeyRef: - name: {{ if .Values.secrets.existingSecret }}{{ .Values.secrets.existingSecret }}{{ else }}{{ include "oxicloud.fullname" . }}-secret{{ end }} + name: {{ if .Values.oxicloud.secret.existingSecretName }}{{ .Values.oxicloud.secret.existingSecretName }}{{ else if .Values.oxicloud.wopi.collabora.admin.existingSecretName }}{{ .Values.oxicloud.wopi.collabora.admin.existingSecretName }}{{ else }}{{ include "oxicloud.fullname" . }}-secret{{ end }} key: WOPI_ADMIN_PASSWORD readinessProbe: httpGet: diff --git a/k8s/charts/oxicloud/templates/wopi-service.yaml b/k8s/charts/oxicloud/templates/wopi-service.yaml index 6b27207..a895791 100644 --- a/k8s/charts/oxicloud/templates/wopi-service.yaml +++ b/k8s/charts/oxicloud/templates/wopi-service.yaml @@ -1,5 +1,5 @@ --- -{{- if .Values.wopi.enabled -}} +{{- if .Values.oxicloud.wopi.enabled -}} apiVersion: v1 kind: Service metadata: @@ -10,7 +10,7 @@ metadata: spec: type: ClusterIP ports: - - port: {{ .Values.wopi.collabora.service.port }} + - port: {{ .Values.oxicloud.wopi.collabora.service.port }} targetPort: wopi protocol: TCP name: wopi diff --git a/k8s/charts/oxicloud/values.yaml b/k8s/charts/oxicloud/values.yaml index dbaca4c..0f69bc1 100644 --- a/k8s/charts/oxicloud/values.yaml +++ b/k8s/charts/oxicloud/values.yaml @@ -1,62 +1,87 @@ --- -replicaCount: 1 +oxicloud: + replicaCount: 1 + image: + repository: diocrafts/oxicloud + pullPolicy: Always + tag: "" - -image: - repository: diocrafts/oxicloud - pullpolicy: Always - tag: "" - -config: - server: + service: + type: LoadBalancer port: 8086 - host: "0.0.0.0" - baseUrl: "https://cloud.example.com" - features: - enableAuth: "true" - enableSharing: "true" - mimalloc: - purgeDelay: "0" - allowLargeOsPages: "0" -persistence: - enabled: true - storageClass: "" - accessMode: ReadWriteOnce - size: 50Gi + ingress: + enabled: true + className: "traefik" + annotations: {} + hosts: + - host: cloud.example.com + paths: + - path: / + pathType: ImplementationSpecific + tls: [] -wopi: - enabled: true - collabora: - url: "cloud.example.com" - image: - repository: collabora/code - tag: latest - pullPolicy: IfNotPresent - service: - port: 9980 - admin: - username: admin - password: "wopi_admin_password" - extraParams: "--o:ssl.enable=false --o:ssl.termination=false --o:net.frame_ancestors=http://* https://*" + config: + server: + port: 8086 + host: "0.0.0.0" + baseUrl: "https://cloud.example.com" + features: + enableAuth: true + enableSharing: true + mimalloc: + purgeDelay: "0" + allowLargeOsPages: "0" -secrets: - existingSecret: "" - jwtSecret: "" - oidcClientSecret: "" + persistence: + enabled: true + storageClass: "" + accessMode: ReadWriteOnce + size: 50Gi -service: - type: ClusterIP - port: 8086 + wopi: + enabled: false + collabora: + url: "cloud.example.com" + domain: "cloud.example.com" + image: + repository: collabora/code + tag: latest + pullPolicy: IfNotPresent + service: + port: 9980 + admin: + existingSecretName: "" + username: "" + password: "" + extraParams: "--o:ssl.enable=false --o:ssl.termination=false --o:net.frame_ancestors=http://* https://*" -ingress: - enabled: true - className: "traefik" - annotations: {} - hosts: - - host: cloud.example.com - paths: - - path: / - pathType: ImplementationSpecific - tls: [] + resources: {} + + livenessProbe: + tcpSocket: + port: 8086 + initialDelaySeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 3 + periodSeconds: 10 + + readinessProbe: + tcpSocket: + port: 8086 + initialDelaySeconds: 15 + timeoutSeconds: 2 + successThreshold: 1 + failureThreshold: 3 + periodSeconds: 10 + + nodeSelector: {} + tolerations: [] + affinity: {} + + secret: + enabled: true + existingSecretName: "oxicloud-secret" + data: {} diff --git a/scripts/update-argo.sh b/scripts/update-argo.sh deleted file mode 100755 index fbdd1da..0000000 --- a/scripts/update-argo.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env bash - -check_kubectl() { - if ! command -v kubectl &>/dev/null; then - echo "Error: kubectl is not installed or not in PATH" >&2 - exit 1 - fi - log_info "kubectl found at $(command -v kubectl)" -} - -VERBOSE=0 - -log_debug() { [[ $VERBOSE -ge 3 ]] && echo "[DEBUG] $*" || true; } -log_verbose() { [[ $VERBOSE -ge 2 ]] && echo "[VERBOSE] $*" || true; } -log_info() { [[ $VERBOSE -ge 1 ]] && echo "[INFO] $*" || true; } -log_error() { echo "[ERROR] $*" >&2; } - -usage() { - cat <&2 - usage >&2 - exit 1 - ;; - *) - TARGET_VERSION="$1" - shift - ;; - esac -done - -log_debug "Script started with target version: ${TARGET_VERSION:-auto}" - -check_kubectl - -log_info "Checking current kubectl context" -CURRENT_CONTEXT=$(kubectl config current-context 2>/dev/null) -log_verbose "Current context: $CURRENT_CONTEXT" - -log_info "Checking for ArgoCD installation" -if ! kubectl get ns argocd &>/dev/null; then - log_error "ArgoCD namespace not found. This script only upgrades existing installations." - exit 1 -fi -log_verbose "ArgoCD namespace found" - -log_info "Checking current ArgoCD version" -CURRENT_VERSION=$(kubectl get deployment argocd-server -n argocd -o jsonpath='{.spec.template.spec.containers[0].image}' 2>/dev/null) -if [[ -n "$CURRENT_VERSION" ]]; then - CURRENT_VERSION=$(echo "$CURRENT_VERSION" | sed 's/.*argocd:v\?//' | tr -d ' \n') - if [[ -n "$CURRENT_VERSION" ]]; then - CURRENT_VERSION="${CURRENT_VERSION#v}" - log_verbose "Current ArgoCD version: $CURRENT_VERSION" - else - log_error "Could not extract ArgoCD version from image: $CURRENT_VERSION" - exit 1 - fi -fi - -if [[ -z "$TARGET_VERSION" ]]; then - log_info "No target version specified, querying for latest version" - log_verbose "Fetching latest release from GitHub" - LATEST_VERSION=$(curl -s https://api.github.com/repos/argoproj/argo-cd/releases/latest | grep -oP '"tag_name":\s*"\K[^"]+' | sed 's/^v//') - if [[ -n "$LATEST_VERSION" ]]; then - log_verbose "Latest version available: $LATEST_VERSION" - TARGET_VERSION="$LATEST_VERSION" - else - echo "Error: Could not fetch latest version" >&2 - exit 1 - fi -fi - -log_info "Target version: $TARGET_VERSION" - -log_debug "Determining update path from $CURRENT_VERSION to $TARGET_VERSION" - -log_info "Applying ArgoCD manifests" -log_verbose "Downloading manifest from https://raw.githubusercontent.com/argoproj/argo-cd/v${TARGET_VERSION}/manifests/install.yaml" -curl -sLO "https://raw.githubusercontent.com/argoproj/argo-cd/v${TARGET_VERSION}/manifests/install.yaml" - -log_debug "Applying manifest with kubectl" -if [[ "$DRY_RUN" == true ]]; then - log_verbose "Dry-run mode: would apply manifest" - kubectl apply -n argocd -f install.yaml --dry-run=client -else - kubectl apply -n argocd -f install.yaml -fi - -log_verbose "Cleaning up downloaded manifest" -rm -f install.yaml - -log_info "Update to ArgoCD $TARGET_VERSION initiated"