diff --git a/Chart.yaml b/Chart.yaml new file mode 100644 index 0000000..bd273cc --- /dev/null +++ b/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: admin-platform-v3 +description: | + OdooSky v3 Tower platform — backend API + (eventually) frontend UI. + Tower deploys itself the same way it deploys tenants: forked + + overlaid + reconciled by ArgoCD. Same mechanism, no special-casing. +type: application +version: 0.1.0 +appVersion: "0.1.0" +keywords: + - odoosky + - tower + - control-plane +maintainers: + - name: OdooSky platform diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl new file mode 100644 index 0000000..d0cd37c --- /dev/null +++ b/templates/_helpers.tpl @@ -0,0 +1,6 @@ +{{- define "tower.labels" -}} +app.kubernetes.io/name: tower +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/version: {{ .Values.backend.image.tag | quote }} +odoosky.io/component: control-plane +{{- end -}} diff --git a/templates/backend-deployment.yaml b/templates/backend-deployment.yaml new file mode 100644 index 0000000..1b49b18 --- /dev/null +++ b/templates/backend-deployment.yaml @@ -0,0 +1,73 @@ +{{- if .Values.backend.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tower-backend + labels: + {{- include "tower.labels" . | nindent 4 }} + odoosky.io/role: backend +spec: + replicas: {{ .Values.backend.replicas }} + selector: + matchLabels: + app.kubernetes.io/name: tower + odoosky.io/role: backend + template: + metadata: + labels: + {{- include "tower.labels" . | nindent 8 }} + odoosky.io/role: backend + spec: + containers: + - name: tower + image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}" + imagePullPolicy: {{ .Values.backend.image.pullPolicy }} + ports: + - name: http + containerPort: 8080 + env: + - name: TOWER_LISTEN_ADDR + value: ":8080" + - name: TOWER_GITEA_URL + value: {{ .Values.config.giteaURL | quote }} + - name: TOWER_GITEA_ORG + value: {{ .Values.config.giteaOrg | quote }} + - name: TOWER_CHART_REPO + value: {{ .Values.config.chartRepo | quote }} + - name: TOWER_ARGOCD_URL + value: {{ .Values.config.argoCDURL | quote }} + - name: TOWER_ARGOCD_USERNAME + value: {{ .Values.config.argoCDUsername | quote }} + - name: TOWER_ARGOCD_DESTINATION + value: {{ .Values.config.argoCDDestination | quote }} + - name: TOWER_ARGOCD_PROJECT + value: {{ .Values.config.argoCDProject | quote }} + - name: TOWER_ARGOCD_NAMESPACE + value: {{ .Values.config.argoCDNamespace | quote }} + - name: TOWER_TENANT_NAMESPACE + value: {{ .Values.config.tenantNamespace | quote }} + - name: TOWER_GITEA_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.config.existingSecret }} + key: GITEA_TOKEN + - name: TOWER_ARGOCD_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.config.existingSecret }} + key: ARGOCD_PASSWORD + readinessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 2 + periodSeconds: 5 + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 15 + resources: + {{- toYaml .Values.backend.resources | nindent 12 }} +{{- end }} diff --git a/templates/backend-service.yaml b/templates/backend-service.yaml new file mode 100644 index 0000000..feb0f02 --- /dev/null +++ b/templates/backend-service.yaml @@ -0,0 +1,17 @@ +{{- if .Values.backend.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: tower-backend + labels: + {{- include "tower.labels" . | nindent 4 }} +spec: + selector: + app.kubernetes.io/name: tower + odoosky.io/role: backend + ports: + - name: http + port: 8080 + targetPort: 8080 + type: ClusterIP +{{- end }} diff --git a/templates/certificate.yaml b/templates/certificate.yaml new file mode 100644 index 0000000..a67e717 --- /dev/null +++ b/templates/certificate.yaml @@ -0,0 +1,14 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: tower-tls + labels: + {{- include "tower.labels" . | nindent 4 }} +spec: + secretName: tower-tls + issuerRef: + name: {{ .Values.ingress.certIssuer }} + kind: ClusterIssuer + dnsNames: + - {{ .Values.ingress.domain | quote }} + renewBefore: 720h diff --git a/templates/ingressroute.yaml b/templates/ingressroute.yaml new file mode 100644 index 0000000..9bb29e5 --- /dev/null +++ b/templates/ingressroute.yaml @@ -0,0 +1,36 @@ +# Tower IngressRoute. Routes by path: +# /api/* → backend (8080) +# /* → frontend (when Step 4 builds it; currently a 503 from +# Traefik because backend.frontend.enabled=false) +# +# Once frontend lands, we add a second route block with a Path matcher +# for the `/api` prefix going to backend, and the catch-all for static +# files going to frontend. +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: tower + labels: + {{- include "tower.labels" . | nindent 4 }} +spec: + entryPoints: + - {{ .Values.ingress.entryPoint }} + routes: +{{- if .Values.backend.enabled }} + - match: Host(`{{ .Values.ingress.domain }}`) + kind: Rule + services: + - name: tower-backend + port: 8080 +{{- end }} +{{- if .Values.frontend.enabled }} + # Frontend slot — enabled when Step 4 builds the UI. + - match: Host(`{{ .Values.ingress.domain }}`) && !PathPrefix(`/api`) && !PathPrefix(`/healthz`) && !PathPrefix(`/instances`) + kind: Rule + priority: 100 + services: + - name: tower-frontend + port: 80 +{{- end }} + tls: + secretName: tower-tls diff --git a/values.yaml b/values.yaml new file mode 100644 index 0000000..e46661c --- /dev/null +++ b/values.yaml @@ -0,0 +1,56 @@ +# admin-platform-v3 — Tower platform default values. + +backend: + enabled: true + image: + repository: docker.io/odoosky/tower + tag: "0.1.0" + pullPolicy: IfNotPresent # IfNotPresent because the image is in + # containerd's k8s.io namespace already + # (lab1 single-server). Replace with + # Always when we move to a real registry. + replicas: 1 + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: "1" + memory: 256Mi + +frontend: + # Off until Step 4 builds it. The chart slot is reserved here so + # turning it on later is a values-flip, not a chart edit. + enabled: false + image: + repository: docker.io/odoosky/tower-ui + tag: "0.1.0" + replicas: 1 + +# Tower needs to talk to: +# - Gitea (create tenant repos, commit values.yaml) +# - ArgoCD (apply Application manifests) +# +# The credentials live in a K8s Secret in the same namespace, populated +# from the ExistingSecret pattern (so they aren't checked into Git). +# In Step 5+ we replace this with External Secrets sourcing from +# OpenBao at vault.odoosky.org. +config: + giteaURL: https://git.odoosky.org + giteaOrg: odoo-tower + chartRepo: instance-template-v3 + argoCDURL: https://argocd.odoosky.org + argoCDUsername: admin + argoCDDestination: https://kubernetes.default.svc + argoCDProject: default + argoCDNamespace: argocd + tenantNamespace: tenants + # The Secret name (in the same namespace as Tower) that holds + # GITEA_TOKEN and ARGOCD_PASSWORD keys. Created out-of-band before + # this chart is applied. + existingSecret: tower-credentials + +ingress: + domain: tower.odoosky.org + certIssuer: letsencrypt-prod + entryPoint: websecure