From 03a0d7c9ae3ed1c00bd3727fef8ebb190141baac Mon Sep 17 00:00:00 2001 From: Danijel Simeunovic Date: Sat, 18 Apr 2026 18:14:00 +0000 Subject: [PATCH] feature/multicluster Co-authored-by: Danijel Simeunovic Reviewed-on: https://git.forteapps.net/Forte/launchpad/pulls/4 Reviewed-by: gitea_admin --- README.md | 38 +++-- ...-of-apps.yaml => _app-of-apps-upc-dev.yaml | 2 +- _app-of-apps-upc-prod.yaml | 32 ++++ apps/{ => base}/argo-mcp.yaml | 0 apps/{ => base}/dot-ai-stack.yaml | 28 +-- apps/base/kustomization.yaml | 8 + apps/{ => base}/mcp10x.yaml | 0 .../opencost.yaml => apps/base/mcpcoder.yaml | 0 apps/{ => base}/musicman.yaml | 0 apps/overlays/upc-dev/kustomization.yaml | 7 + apps/overlays/upc-prod/kustomization.yaml | 14 ++ bootstrap.sh | 23 ++- clusters/upc-dev.yaml | 10 ++ clusters/upc-prod.yaml | 10 ++ docs/DEVELOPER-GUIDE.md | 4 +- docs/GITOPS-ARCHITECTURE.md | 146 +++++++++------- docs/OPERATIONS-RUNBOOK.md | 63 ++++--- docs/README.md | 4 +- docs/REFERENCE.md | 19 ++- .../{ => base}/cert-manager-application.yaml | 0 .../cluster-resources-application.yaml | 0 infra/{ => base}/enterprise-apps.yaml | 2 +- infra/{ => base}/fluent-bit.yaml | 2 +- infra/{ => base}/gitea-actions.yaml | 2 +- infra/{ => base}/gitea.yaml | 2 +- infra/{ => base}/grafana-dashboards.yaml | 0 infra/{ => base}/grafana.yaml | 3 +- infra/{ => base}/keycloak.yaml | 3 +- infra/base/kustomization.yaml | 23 +++ infra/{ => base}/kyverno-policies.yaml | 0 infra/{ => base}/kyverno.yaml | 0 infra/{ => base}/loki.yaml | 2 +- .../network-policies-application.yaml | 0 infra/{ => base}/prometheus.yaml | 2 +- infra/{ => base}/renovate.yaml | 2 +- infra/{ => base}/sealedsecrets.yaml | 0 infra/{ => base}/secrets.yaml | 2 +- infra/{ => base}/tempo.yaml | 2 +- infra/base/traefik-application.yaml | 51 ++++++ infra/{ => base}/trivy.yaml | 0 infra/overlays/upc-dev/kustomization.yaml | 7 + infra/overlays/upc-prod/kustomization.yaml | 50 ++++++ infra/traefik-application.yaml | 159 ------------------ infra/values/{ => base}/argocd-values.yaml | 6 - infra/values/base/dot-ai-stack-values.yaml | 11 ++ .../values/{ => base}/fluent-bit-values.yaml | 0 .../{ => base}/gitea-actions-values.yaml | 0 infra/values/{ => base}/gitea-values.yaml | 0 infra/values/{ => base}/grafana-values.yaml | 2 - infra/values/{ => base}/keycloak-values.yaml | 2 - infra/values/{ => base}/loki-values.yaml | 0 infra/values/{ => base}/opencost-values.yaml | 0 .../values/{ => base}/prometheus-values.yaml | 0 infra/values/{ => base}/renovate-values.yaml | 0 infra/values/{ => base}/tempo-values.yaml | 0 infra/values/base/traefik-values.yaml | 50 ++++++ infra/values/upc-dev/argocd-values.yaml | 5 + infra/values/upc-dev/dot-ai-stack-values.yaml | 8 + infra/values/upc-dev/grafana-values.yaml | 3 + infra/values/upc-dev/keycloak-values.yaml | 2 + infra/values/upc-dev/traefik-values.yaml | 40 +++++ infra/values/upc-prod/argocd-values.yaml | 5 + .../values/upc-prod/dot-ai-stack-values.yaml | 8 + infra/values/upc-prod/grafana-values.yaml | 3 + infra/values/upc-prod/keycloak-values.yaml | 2 + infra/values/upc-prod/traefik-values.yaml | 13 ++ .../{ => upc-dev}/argocd-mcp-credentials.yaml | 0 .../argocdmcp-auth-oidc-sealed.yaml | 0 secrets/upc-dev/dot-ai-secrets.yaml | 18 ++ .../forte10x-app-credentials-sealed.yaml | 0 .../keycloak-credentials-sealed.yaml | 0 .../{ => upc-dev}/musicman-credentials.yaml | 0 72 files changed, 574 insertions(+), 326 deletions(-) rename _app-of-apps.yaml => _app-of-apps-upc-dev.yaml (95%) create mode 100644 _app-of-apps-upc-prod.yaml rename apps/{ => base}/argo-mcp.yaml (100%) rename apps/{ => base}/dot-ai-stack.yaml (73%) create mode 100644 apps/base/kustomization.yaml rename apps/{ => base}/mcp10x.yaml (100%) rename infra/opencost.yaml => apps/base/mcpcoder.yaml (100%) rename apps/{ => base}/musicman.yaml (100%) create mode 100644 apps/overlays/upc-dev/kustomization.yaml create mode 100644 apps/overlays/upc-prod/kustomization.yaml create mode 100644 clusters/upc-dev.yaml create mode 100644 clusters/upc-prod.yaml rename infra/{ => base}/cert-manager-application.yaml (100%) rename infra/{ => base}/cluster-resources-application.yaml (100%) rename infra/{ => base}/enterprise-apps.yaml (96%) rename infra/{ => base}/fluent-bit.yaml (94%) rename infra/{ => base}/gitea-actions.yaml (94%) rename infra/{ => base}/gitea.yaml (95%) rename infra/{ => base}/grafana-dashboards.yaml (100%) rename infra/{ => base}/grafana.yaml (89%) rename infra/{ => base}/keycloak.yaml (90%) create mode 100644 infra/base/kustomization.yaml rename infra/{ => base}/kyverno-policies.yaml (100%) rename infra/{ => base}/kyverno.yaml (100%) rename infra/{ => base}/loki.yaml (94%) rename infra/{ => base}/network-policies-application.yaml (100%) rename infra/{ => base}/prometheus.yaml (94%) rename infra/{ => base}/renovate.yaml (94%) rename infra/{ => base}/sealedsecrets.yaml (100%) rename infra/{ => base}/secrets.yaml (96%) rename infra/{ => base}/tempo.yaml (94%) create mode 100644 infra/base/traefik-application.yaml rename infra/{ => base}/trivy.yaml (100%) create mode 100644 infra/overlays/upc-dev/kustomization.yaml create mode 100644 infra/overlays/upc-prod/kustomization.yaml delete mode 100644 infra/traefik-application.yaml rename infra/values/{ => base}/argocd-values.yaml (95%) create mode 100644 infra/values/base/dot-ai-stack-values.yaml rename infra/values/{ => base}/fluent-bit-values.yaml (100%) rename infra/values/{ => base}/gitea-actions-values.yaml (100%) rename infra/values/{ => base}/gitea-values.yaml (100%) rename infra/values/{ => base}/grafana-values.yaml (97%) rename infra/values/{ => base}/keycloak-values.yaml (99%) rename infra/values/{ => base}/loki-values.yaml (100%) rename infra/values/{ => base}/opencost-values.yaml (100%) rename infra/values/{ => base}/prometheus-values.yaml (100%) rename infra/values/{ => base}/renovate-values.yaml (100%) rename infra/values/{ => base}/tempo-values.yaml (100%) create mode 100644 infra/values/base/traefik-values.yaml create mode 100644 infra/values/upc-dev/argocd-values.yaml create mode 100644 infra/values/upc-dev/dot-ai-stack-values.yaml create mode 100644 infra/values/upc-dev/grafana-values.yaml create mode 100644 infra/values/upc-dev/keycloak-values.yaml create mode 100644 infra/values/upc-dev/traefik-values.yaml create mode 100644 infra/values/upc-prod/argocd-values.yaml create mode 100644 infra/values/upc-prod/dot-ai-stack-values.yaml create mode 100644 infra/values/upc-prod/grafana-values.yaml create mode 100644 infra/values/upc-prod/keycloak-values.yaml create mode 100644 infra/values/upc-prod/traefik-values.yaml rename secrets/{ => upc-dev}/argocd-mcp-credentials.yaml (100%) rename secrets/{ => upc-dev}/argocdmcp-auth-oidc-sealed.yaml (100%) create mode 100644 secrets/upc-dev/dot-ai-secrets.yaml rename secrets/{ => upc-dev}/forte10x-app-credentials-sealed.yaml (100%) rename secrets/{ => upc-dev}/keycloak-credentials-sealed.yaml (100%) rename secrets/{ => upc-dev}/musicman-credentials.yaml (100%) diff --git a/README.md b/README.md index 1dea06c..9095ee0 100644 --- a/README.md +++ b/README.md @@ -83,20 +83,26 @@ This repository contains the complete GitOps configuration for our Kubernetes cl ├── bootstrap.sh # Cluster initialization script ├── _app-of-apps.yaml # Root ArgoCD Application (App-of-Apps pattern) │ -├── infra/ # Infrastructure ArgoCD Applications -│ ├── enterprise-apps.yaml # Manages all apps in apps/ folder -│ ├── traefik-application.yaml -│ ├── cert-manager-application.yaml -│ ├── kyverno.yaml -│ ├── prometheus.yaml -│ ├── grafana.yaml -│ ├── loki.yaml -│ ├── tempo.yaml -│ ├── fluent-bit.yaml -│ ├── trivy.yaml -│ ├── sealedsecrets.yaml -│ ├── renovate.yaml +├── infra/ # Infrastructure ArgoCD Applications (Kustomize multi-cluster) +│ ├── base/ # Base ArgoCD Application manifests (EU defaults) +│ │ ├── kustomization.yaml +│ │ ├── traefik-application.yaml +│ │ ├── keycloak.yaml +│ │ ├── grafana.yaml +│ │ ├── gitea.yaml +│ │ ├── gitea-actions.yaml +│ │ ├── tempo.yaml +│ │ ├── renovate.yaml +│ │ ├── ... # All other Application manifests +│ │ └── secrets.yaml +│ ├── overlays/ # Per-cluster overrides +│ │ ├── upc-dev/ # UpCloud Dev cluster (uses base as-is) +│ │ └── upc-prod/ # UpCloud Prod cluster (patches value paths) +│ ├── dashboards/ # Grafana dashboard ConfigMaps │ └── values/ # Helm value overrides +│ ├── base/ # Shared values (all clusters) +│ ├── upc-dev/ # UpCloud Dev-specific values +│ └── upc-prod/ # UpCloud Prod-specific values │ ├── apps/ # Business Applications │ ├── mcp10x.yaml @@ -355,7 +361,7 @@ kubectl patch application myapp -n argocd \ ## 📖 Key Concepts ### App-of-Apps Pattern -`_app-of-apps.yaml` is the root Application that manages all other Applications in `infra/`. Each YAML in `infra/` becomes a child Application managed by ArgoCD. +`_app-of-apps.yaml` is the root Application that manages all other Applications in `infra/`. Kustomize overlays in `infra/overlays/{upc-dev,upc-prod}/` render the base Applications with per-cluster patches (e.g., swapping value file paths from `upc-dev` to `upc-prod`). ### Multi-Source Pattern Applications reference both: @@ -454,14 +460,14 @@ Documentation lives in `docs/`. To update: ### Current Environment - **Provider**: UpCloud Managed Kubernetes - **Environment**: Production (internal use only) -- **Cluster**: Single cluster +- **Clusters**: Multi-cluster (upc-dev, upc-prod) via Kustomize overlays - **Auth**: Disabled for ArgoCD (internal access) - **Backup**: None (cluster rebuildable via GitOps) ### Known Limitations - No automated backups (yet) - Secret rotation not automated -- Single cluster (no multi-cluster setup) +- Multi-cluster limited to upc-dev and upc-prod environments - DNS management is manual **Future improvements**: See [Operations Runbook - Disaster Recovery](docs/OPERATIONS-RUNBOOK.md#disaster-recovery) diff --git a/_app-of-apps.yaml b/_app-of-apps-upc-dev.yaml similarity index 95% rename from _app-of-apps.yaml rename to _app-of-apps-upc-dev.yaml index d2fa207..b352c3d 100644 --- a/_app-of-apps.yaml +++ b/_app-of-apps-upc-dev.yaml @@ -20,7 +20,7 @@ spec: source: repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD - path: infra + path: infra/overlays/upc-dev destination: server: https://kubernetes.default.svc namespace: default diff --git a/_app-of-apps-upc-prod.yaml b/_app-of-apps-upc-prod.yaml new file mode 100644 index 0000000..f5ccaca --- /dev/null +++ b/_app-of-apps-upc-prod.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: monitoring + annotations: + argocd.argoproj.io/sync-wave: "-1" +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: infrastructure-apps + namespace: argocd + labels: + app.kubernetes.io/name: infrastructure-apps + app.kubernetes.io/part-of: platform + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: git@github.com:fortedigital/sturdy-adventure.git + targetRevision: HEAD + path: infra/overlays/upc-prod + destination: + server: https://kubernetes.default.svc + namespace: default + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/apps/argo-mcp.yaml b/apps/base/argo-mcp.yaml similarity index 100% rename from apps/argo-mcp.yaml rename to apps/base/argo-mcp.yaml diff --git a/apps/dot-ai-stack.yaml b/apps/base/dot-ai-stack.yaml similarity index 73% rename from apps/dot-ai-stack.yaml rename to apps/base/dot-ai-stack.yaml index b048233..3fd1284 100644 --- a/apps/dot-ai-stack.yaml +++ b/apps/base/dot-ai-stack.yaml @@ -27,29 +27,19 @@ metadata: spec: project: default - source: - repoURL: ghcr.io/vfarcic/dot-ai-stack/charts + sources: + - repoURL: ghcr.io/vfarcic/dot-ai-stack/charts chart: dot-ai-stack targetRevision: "0.56.0" - helm: releaseName: dot-ai-stack - values: | - dot-ai: - ingress: - enabled: true - className: traefik - host: kubemcp.forteapps.net - webUI: - baseUrl: http://kubemcpui.forteapps.net - dot-ai-ui: - uiAuth: - secretRef: - name: dot-ai-secrets - ingress: - enabled: true - className: traefik - host: kubemcpui.forteapps.net + valueFiles: + - $values/infra/values/base/dot-ai-stack-values.yaml + - $values/infra/values/upc-dev/dot-ai-stack-values.yaml + + - repoURL: git@github.com:fortedigital/sturdy-adventure.git + targetRevision: HEAD + ref: values destination: server: https://kubernetes.default.svc diff --git a/apps/base/kustomization.yaml b/apps/base/kustomization.yaml new file mode 100644 index 0000000..3cc3d12 --- /dev/null +++ b/apps/base/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- dot-ai-stack.yaml +- mcp10x.yaml +- musicman.yaml +- mcpcoder.yaml +- argo-mcp.yaml diff --git a/apps/mcp10x.yaml b/apps/base/mcp10x.yaml similarity index 100% rename from apps/mcp10x.yaml rename to apps/base/mcp10x.yaml diff --git a/infra/opencost.yaml b/apps/base/mcpcoder.yaml similarity index 100% rename from infra/opencost.yaml rename to apps/base/mcpcoder.yaml diff --git a/apps/musicman.yaml b/apps/base/musicman.yaml similarity index 100% rename from apps/musicman.yaml rename to apps/base/musicman.yaml diff --git a/apps/overlays/upc-dev/kustomization.yaml b/apps/overlays/upc-dev/kustomization.yaml new file mode 100644 index 0000000..1895aac --- /dev/null +++ b/apps/overlays/upc-dev/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- ../../base + +# No patches needed — base already has "upc-dev" paths +# upc-dev is the default/base cluster diff --git a/apps/overlays/upc-prod/kustomization.yaml b/apps/overlays/upc-prod/kustomization.yaml new file mode 100644 index 0000000..79e912b --- /dev/null +++ b/apps/overlays/upc-prod/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- ../../base + +patches: +# dot-ai-stack: swap upc-dev → upc-prod +- target: + kind: Application + name: dot-ai-stack + patch: | + - op: replace + path: /spec/sources/0/helm/valueFiles/1 + value: $values/infra/values/upc-prod/dot-ai-stack-values.yaml diff --git a/bootstrap.sh b/bootstrap.sh index 23558fe..dc11d10 100644 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -2,7 +2,14 @@ # in case of $'\r': command not found error, run command below first # sed -i 's/\r$//' ./bootstrap.sh -echo "running $0..." +CLUSTER="${1:?Usage: ./bootstrap.sh (eu|us)}" + +echo "running $0 for cluster: ${CLUSTER}..." + +# Source cluster config +eval $(yq -r 'to_entries[] | "export \(.key)=\"\(.value)\""' "clusters/${CLUSTER}.yaml") + +echo "Bootstrapping cluster: ${clusterName} (${CLUSTER})..." ############################################################ # Bootstrap # @@ -20,8 +27,8 @@ Bootstrap() Github() { echo "Installing secret..." - kubectl apply -f private/github.yaml - kubectl apply -f private/main.key + kubectl apply -f private/github-${CLUSTER}.yaml + kubectl apply -f private/main-${CLUSTER}.key } ############################################################ @@ -31,15 +38,15 @@ ArgoCd() { # install argocd echo "Installing ArgoCD..." - CLUSTER_NAME="${CLUSTER_NAME:-dev-fd-no-svg1}" helm upgrade --install argocd argo-cd \ --repo https://argoproj.github.io/argo-helm \ --namespace argocd --create-namespace \ - --values infra/values/argocd-values.yaml \ - --set notifications.context.clusterName="$CLUSTER_NAME" \ + --values infra/values/base/argocd-values.yaml \ + --values "infra/values/${CLUSTER}/argocd-values.yaml" \ + --set notifications.context.clusterName="${clusterName}" \ --timeout 60s --atomic - kubectl apply -f _app-of-apps.yaml -n argocd + kubectl apply -f "_app-of-apps-${CLUSTER}.yaml" -n argocd } -Bootstrap +# Bootstrap diff --git a/clusters/upc-dev.yaml b/clusters/upc-dev.yaml new file mode 100644 index 0000000..4813591 --- /dev/null +++ b/clusters/upc-dev.yaml @@ -0,0 +1,10 @@ +clusterName: dev-fd-no-svg1 +domain: forteapps.net +argocdDomain: argocd.127.0.0.1.nip.io +grafanaDomain: grafana.forteapps.net +keycloakDomain: id.forteapps.net +dotaiDomain: kubemcp.forteapps.net +dotaiUiDomain: kubemcpui.forteapps.net +letsencryptEmail: danijels@gmail.com +trustedIPs: "172.16.1.0/24" +cloudProvider: upcloud diff --git a/clusters/upc-prod.yaml b/clusters/upc-prod.yaml new file mode 100644 index 0000000..98a3a3c --- /dev/null +++ b/clusters/upc-prod.yaml @@ -0,0 +1,10 @@ +clusterName: dev-fd-us-east1 +domain: us.forteapps.net +argocdDomain: argocd.us.forteapps.net +grafanaDomain: grafana.us.forteapps.net +keycloakDomain: id.us.forteapps.net +dotaiDomain: kubemcp.us.forteapps.net +dotaiUiDomain: kubemcpui.us.forteapps.net +letsencryptEmail: danijels@gmail.com +trustedIPs: "10.0.0.0/16" +cloudProvider: tbd diff --git a/docs/DEVELOPER-GUIDE.md b/docs/DEVELOPER-GUIDE.md index 27b7ddc..9a2f127 100644 --- a/docs/DEVELOPER-GUIDE.md +++ b/docs/DEVELOPER-GUIDE.md @@ -1364,7 +1364,7 @@ Existing clients (like Gitea) are defined directly in `forte-realm.json` inside #### Step 1: Add Client to Realm Config -In `infra/values/keycloak-values.yaml`, add a new entry to the `clients` array in `forte-realm.json`: +In `infra/values/base/keycloak-values.yaml`, add a new entry to the `clients` array in `forte-realm.json`: ```json { @@ -1404,7 +1404,7 @@ existingSecret: myapp-oidc-credentials ```bash cd ~/dev/k8s/launchpad -git add infra/values/keycloak-values.yaml +git add infra/values/base/keycloak-values.yaml git commit -m "Add myapp Keycloak client with auto-sync" git push ``` diff --git a/docs/GITOPS-ARCHITECTURE.md b/docs/GITOPS-ARCHITECTURE.md index e663979..c1e3207 100644 --- a/docs/GITOPS-ARCHITECTURE.md +++ b/docs/GITOPS-ARCHITECTURE.md @@ -16,7 +16,7 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where ### Key Characteristics - **Environment**: Production (internal use only) -- **Cluster Type**: Single cluster, single environment +- **Cluster Type**: Multi-cluster (upc-dev, upc-prod) via Kustomize overlays - **GitOps Tool**: ArgoCD - **Deployment Pattern**: App-of-Apps - **Secret Management**: Sealed Secrets (kubeseal) @@ -62,8 +62,8 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where │ ▼ ┌────────────────────────────────┐ - │ Kubernetes Cluster │ - │ (UpCloud Managed) │ + │ Kubernetes Clusters │ + │ (UpCloud: upc-dev, upc-prod) │ │ │ │ ┌──────────────────────────┐ │ │ │ ArgoCD │ │ @@ -116,74 +116,68 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where ``` launchpad/ ├── bootstrap.sh # Cluster initialization script -├── _app-of-apps.yaml # Root ArgoCD Application (App-of-Apps pattern) +├── _app-of-apps-upc-dev.yaml # Root ArgoCD Application (upc-dev cluster) +├── _app-of-apps-upc-prod.yaml # Root ArgoCD Application (upc-prod cluster) │ -├── infra/ # Infrastructure ArgoCD Applications -│ ├── enterprise-apps.yaml # Parent app managing all apps in apps/ -│ ├── cluster-resources-application.yaml -│ ├── traefik-application.yaml -│ ├── cert-manager-application.yaml -│ ├── kyverno.yaml -│ ├── kyverno-policies.yaml -│ ├── prometheus.yaml -│ ├── grafana.yaml -│ ├── loki.yaml -│ ├── tempo.yaml -│ ├── fluent-bit.yaml -│ ├── trivy.yaml -│ ├── sealedsecrets.yaml -│ ├── secrets.yaml +├── infra/ # Infrastructure ArgoCD Applications (Kustomize) +│ ├── base/ # Base Application manifests (upc-dev defaults) +│ │ ├── kustomization.yaml +│ │ ├── traefik-application.yaml +│ │ ├── keycloak.yaml +│ │ ├── grafana.yaml +│ │ ├── gitea.yaml +│ │ ├── gitea-actions.yaml +│ │ ├── tempo.yaml +│ │ ├── renovate.yaml +│ │ ├── ... # All other Application manifests +│ │ └── secrets.yaml +│ ├── overlays/ # Per-cluster overrides +│ │ ├── upc-dev/ # UpCloud Dev (uses base as-is) +│ │ └── upc-prod/ # UpCloud Prod (patches value paths) +│ ├── dashboards/ # Grafana dashboard ConfigMaps │ └── values/ # Helm value overrides for infra -│ ├── argocd-values.yaml -│ ├── prometheus-values.yaml -│ ├── grafana-values.yaml -│ ├── loki-values.yaml -│ ├── tempo-values.yaml -│ └── fluent-bit-values.yaml +│ ├── base/ # Shared values (all clusters) +│ │ ├── traefik-values.yaml +│ │ ├── keycloak-values.yaml +│ │ ├── grafana-values.yaml +│ │ ├── prometheus-values.yaml +│ │ ├── gitea-values.yaml +│ │ └── ... +│ ├── upc-dev/ # upc-dev cluster-specific values +│ │ ├── traefik-values.yaml +│ │ ├── keycloak-values.yaml +│ │ └── grafana-values.yaml +│ └── upc-prod/ # upc-prod cluster-specific values +│ ├── traefik-values.yaml +│ ├── keycloak-values.yaml +│ └── grafana-values.yaml │ -├── apps/ # Business Application ArgoCD manifests -│ ├── mcp10x.yaml # MCP 10X application -│ ├── musicman.yaml # Music Man application -│ ├── dot-ai-stack.yaml # Dot AI Stack -│ └── argo-mcp.yaml # ArgoCD MCP server +├── apps/ # Business Application ArgoCD manifests (Kustomize) +│ ├── base/ # Base app manifests +│ │ ├── kustomization.yaml +│ │ ├── dot-ai-stack.yaml +│ │ └── ... +│ └── overlays/ +│ ├── upc-dev/ # Uses base as-is +│ └── upc-prod/ # Patches value paths │ ├── cluster-resources/ # Cluster-wide Kubernetes resources -│ ├── cert-manager-namespace.yaml -│ ├── secrets-namespace.yaml -│ ├── letsencrypt-issuer.yaml # Let's Encrypt ClusterIssuer -│ ├── kyverno-config.yaml -│ ├── argocd-notifications-secret-sealed.yaml -│ ├── forte10x-repo-credentials-sealed.yaml -│ ├── mcp10x-repo-credentials-sealed.yaml +│ ├── ... │ └── policies/ # Kyverno policies -│ ├── deployment-verifier.yaml -│ ├── label-checker.yaml -│ ├── bare-pod-cleaner.yaml -│ ├── replicaset-cleaner.yaml -│ ├── default-ns-blocker.yaml -│ ├── secret-cloner.yaml -│ └── auth-sidecar-injector.yaml │ -├── secrets/ # Application secrets (sealed) -│ ├── argocd-mcp-credentials.yaml -│ ├── dot-ai-secrets.yaml -│ ├── mcp10x-credentials-sealed.yaml -│ └── musicman-credentials.yaml +├── secrets/ # Application secrets (sealed, per-cluster) +│ └── upc-dev/ # Secrets for upc-dev cluster │ ├── private/ # Local-only files (NOT in Git) -│ ├── *.yaml # Unsealed secrets -│ └── *.sh # Helper scripts │ └── docs/ # Documentation - ├── GITOPS-ARCHITECTURE.md # This file - ├── DEVELOPER-GUIDE.md - ├── OPERATIONS-RUNBOOK.md - └── REFERENCE.md ``` **Key Points**: -- `_app-of-apps.yaml` is the root Application that ArgoCD monitors -- `infra/enterprise-apps.yaml` auto-discovers all apps in `apps/` folder +- `_app-of-apps-upc-dev.yaml` and `_app-of-apps-upc-prod.yaml` are the per-cluster root Applications +- Kustomize overlays in `infra/overlays/` render base Applications with per-cluster patches +- Helm values are split: `values/base/` (shared) + `values/upc-dev/` or `values/upc-prod/` (cluster-specific) +- `apps/` follows the same base/overlays pattern for business applications - Changes pushed to this repo trigger automatic syncs in ArgoCD - `private/` folder contains local-only files (Git-ignored) @@ -295,7 +289,7 @@ app-repository/ ### The App-of-Apps Pattern ``` -_app-of-apps.yaml (Root) +_app-of-apps-{upc-dev,upc-prod}.yaml (Root, per cluster) │ ├── infrastructure-apps (manages infra/) │ ├── cluster-resources-application @@ -315,10 +309,10 @@ _app-of-apps.yaml (Root) ``` **How It Works**: -1. Bootstrap script installs ArgoCD and applies `_app-of-apps.yaml` -2. ArgoCD creates the root Application which monitors `infra/` folder -3. Each YAML in `infra/` becomes a child Application -4. `enterprise-apps.yaml` monitors `apps/` folder and auto-discovers applications +1. Bootstrap script installs ArgoCD and applies `_app-of-apps-upc-dev.yaml` (or `upc-prod`) +2. ArgoCD creates the root Application which monitors the appropriate `infra/overlays/` folder +3. Kustomize renders base Applications with cluster-specific patches +4. `enterprise-apps` Application monitors the cluster's `apps/overlays/` folder 5. ArgoCD continuously syncs (every 60s) and auto-heals drift ### Sync Waves & Ordering @@ -363,6 +357,34 @@ spec: - Easy to update all apps by changing the chart - Environment-specific values isolated in separate repo +### Multi-Cluster Pattern + +Kustomize overlays enable deploying the same Applications across clusters with different configurations: + +```yaml +# infra/base/ contains default (upc-dev) Applications +# Helm values are layered: base + cluster-specific +valueFiles: +- $values/infra/values/base/traefik-values.yaml # Shared config +- $values/infra/values/upc-dev/traefik-values.yaml # Cluster-specific + +# infra/overlays/upc-prod/kustomization.yaml patches the second valueFile +patches: +- target: + kind: Application + name: traefik + patch: | + - op: replace + path: /spec/sources/0/helm/valueFiles/1 + value: $values/infra/values/upc-prod/traefik-values.yaml +``` + +**Benefits**: +- Single source of truth for Application definitions +- Cluster-specific values isolated per overlay +- Easy to add new clusters by creating a new overlay +- Base values shared across all clusters reduce duplication + --- ## CI/CD Pipeline diff --git a/docs/OPERATIONS-RUNBOOK.md b/docs/OPERATIONS-RUNBOOK.md index 9ff6f9f..822ba97 100644 --- a/docs/OPERATIONS-RUNBOOK.md +++ b/docs/OPERATIONS-RUNBOOK.md @@ -207,7 +207,7 @@ kubectl get secrets -n argocd -l argocd.argoproj.io/secret-type=repository # Settings → Repositories → Should show "Successful" status # Test by creating an application -kubectl apply -f _app-of-apps.yaml +kubectl apply -f _app-of-apps-upc-dev.yaml # or _app-of-apps-upc-prod.yaml # Check application sync status kubectl get applications -n argocd @@ -1352,13 +1352,13 @@ kubectl get deployment argocd-server -n argocd \ -o jsonpath='{.spec.template.spec.containers[0].image}' # Update version in values -vim infra/values/argocd-values.yaml +vim infra/values/base/argocd-values.yaml # Or upgrade via Helm directly helm upgrade argocd argo-cd \ --repo https://argoproj.github.io/argo-helm \ --namespace argocd \ - --values infra/values/argocd-values.yaml \ + --values infra/values/base/argocd-values.yaml \ --version 6.0.0 # New version # Verify @@ -1454,8 +1454,8 @@ kubectl top pods --all-namespaces --sort-by=cpu Example: Adding Redis ```bash -# 1. Create application manifest -cat > infra/redis-application.yaml < infra/base/redis-application.yaml < infra/values/base/redis-values.yaml < -n keycloak -o jsonpath='{.metadata.ann **Configuration**: ```yaml -# infra/renovate.yaml + infra/values/renovate-values.yaml +# infra/base/renovate.yaml + infra/values/base/renovate-values.yaml cronjob: schedule: "@daily" concurrencyPolicy: Forbid diff --git a/infra/cert-manager-application.yaml b/infra/base/cert-manager-application.yaml similarity index 100% rename from infra/cert-manager-application.yaml rename to infra/base/cert-manager-application.yaml diff --git a/infra/cluster-resources-application.yaml b/infra/base/cluster-resources-application.yaml similarity index 100% rename from infra/cluster-resources-application.yaml rename to infra/base/cluster-resources-application.yaml diff --git a/infra/enterprise-apps.yaml b/infra/base/enterprise-apps.yaml similarity index 96% rename from infra/enterprise-apps.yaml rename to infra/base/enterprise-apps.yaml index 1d3db4a..40763cb 100644 --- a/infra/enterprise-apps.yaml +++ b/infra/base/enterprise-apps.yaml @@ -18,7 +18,7 @@ spec: source: repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD - path: apps + path: apps/overlays/upc-dev destination: server: https://kubernetes.default.svc namespace: apps diff --git a/infra/fluent-bit.yaml b/infra/base/fluent-bit.yaml similarity index 94% rename from infra/fluent-bit.yaml rename to infra/base/fluent-bit.yaml index b90ccab..748a6b4 100644 --- a/infra/fluent-bit.yaml +++ b/infra/base/fluent-bit.yaml @@ -21,7 +21,7 @@ spec: helm: releaseName: fluent-bit valueFiles: - - $values/infra/values/fluent-bit-values.yaml + - $values/infra/values/base/fluent-bit-values.yaml - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD diff --git a/infra/gitea-actions.yaml b/infra/base/gitea-actions.yaml similarity index 94% rename from infra/gitea-actions.yaml rename to infra/base/gitea-actions.yaml index 1531d69..ae29f29 100644 --- a/infra/gitea-actions.yaml +++ b/infra/base/gitea-actions.yaml @@ -21,7 +21,7 @@ spec: helm: releaseName: gitea-actions valueFiles: - - $values/infra/values/gitea-actions-values.yaml + - $values/infra/values/base/gitea-actions-values.yaml - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD diff --git a/infra/gitea.yaml b/infra/base/gitea.yaml similarity index 95% rename from infra/gitea.yaml rename to infra/base/gitea.yaml index f0c5209..ba806f5 100644 --- a/infra/gitea.yaml +++ b/infra/base/gitea.yaml @@ -21,7 +21,7 @@ spec: helm: releaseName: gitea valueFiles: - - $values/infra/values/gitea-values.yaml + - $values/infra/values/base/gitea-values.yaml - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD diff --git a/infra/grafana-dashboards.yaml b/infra/base/grafana-dashboards.yaml similarity index 100% rename from infra/grafana-dashboards.yaml rename to infra/base/grafana-dashboards.yaml diff --git a/infra/grafana.yaml b/infra/base/grafana.yaml similarity index 89% rename from infra/grafana.yaml rename to infra/base/grafana.yaml index 1238fbc..3e17373 100644 --- a/infra/grafana.yaml +++ b/infra/base/grafana.yaml @@ -21,7 +21,8 @@ spec: helm: releaseName: grafana valueFiles: - - $values/infra/values/grafana-values.yaml + - $values/infra/values/base/grafana-values.yaml + - $values/infra/values/upc-dev/grafana-values.yaml - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD diff --git a/infra/keycloak.yaml b/infra/base/keycloak.yaml similarity index 90% rename from infra/keycloak.yaml rename to infra/base/keycloak.yaml index 4b448f2..a3234d8 100644 --- a/infra/keycloak.yaml +++ b/infra/base/keycloak.yaml @@ -21,7 +21,8 @@ spec: helm: releaseName: keycloak valueFiles: - - $values/infra/values/keycloak-values.yaml + - $values/infra/values/base/keycloak-values.yaml + - $values/infra/values/upc-dev/keycloak-values.yaml - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD diff --git a/infra/base/kustomization.yaml b/infra/base/kustomization.yaml new file mode 100644 index 0000000..e60eb3c --- /dev/null +++ b/infra/base/kustomization.yaml @@ -0,0 +1,23 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- traefik-application.yaml +- keycloak.yaml +- grafana.yaml +- cert-manager-application.yaml +- kyverno.yaml +- sealedsecrets.yaml +- prometheus.yaml +- loki.yaml +- fluent-bit.yaml +- trivy.yaml +- enterprise-apps.yaml +- cluster-resources-application.yaml +- kyverno-policies.yaml +- secrets.yaml +- gitea.yaml +- gitea-actions.yaml +- renovate.yaml +- tempo.yaml +- grafana-dashboards.yaml +- network-policies-application.yaml diff --git a/infra/kyverno-policies.yaml b/infra/base/kyverno-policies.yaml similarity index 100% rename from infra/kyverno-policies.yaml rename to infra/base/kyverno-policies.yaml diff --git a/infra/kyverno.yaml b/infra/base/kyverno.yaml similarity index 100% rename from infra/kyverno.yaml rename to infra/base/kyverno.yaml diff --git a/infra/loki.yaml b/infra/base/loki.yaml similarity index 94% rename from infra/loki.yaml rename to infra/base/loki.yaml index f7cf2f0..d9acdab 100644 --- a/infra/loki.yaml +++ b/infra/base/loki.yaml @@ -21,7 +21,7 @@ spec: helm: releaseName: loki valueFiles: - - $values/infra/values/loki-values.yaml + - $values/infra/values/base/loki-values.yaml - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD diff --git a/infra/network-policies-application.yaml b/infra/base/network-policies-application.yaml similarity index 100% rename from infra/network-policies-application.yaml rename to infra/base/network-policies-application.yaml diff --git a/infra/prometheus.yaml b/infra/base/prometheus.yaml similarity index 94% rename from infra/prometheus.yaml rename to infra/base/prometheus.yaml index d36cb5d..15aa33d 100644 --- a/infra/prometheus.yaml +++ b/infra/base/prometheus.yaml @@ -21,7 +21,7 @@ spec: helm: releaseName: prometheus valueFiles: - - $values/infra/values/prometheus-values.yaml + - $values/infra/values/base/prometheus-values.yaml - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD diff --git a/infra/renovate.yaml b/infra/base/renovate.yaml similarity index 94% rename from infra/renovate.yaml rename to infra/base/renovate.yaml index bc1d34e..4cf87e4 100644 --- a/infra/renovate.yaml +++ b/infra/base/renovate.yaml @@ -21,7 +21,7 @@ spec: helm: releaseName: renovate valueFiles: - - $values/infra/values/renovate-values.yaml + - $values/infra/values/base/renovate-values.yaml - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD diff --git a/infra/sealedsecrets.yaml b/infra/base/sealedsecrets.yaml similarity index 100% rename from infra/sealedsecrets.yaml rename to infra/base/sealedsecrets.yaml diff --git a/infra/secrets.yaml b/infra/base/secrets.yaml similarity index 96% rename from infra/secrets.yaml rename to infra/base/secrets.yaml index d0a99a1..7eb57f9 100644 --- a/infra/secrets.yaml +++ b/infra/base/secrets.yaml @@ -18,7 +18,7 @@ spec: project: default source: repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git - path: secrets + path: secrets/upc-dev destination: server: https://kubernetes.default.svc namespace: secrets diff --git a/infra/tempo.yaml b/infra/base/tempo.yaml similarity index 94% rename from infra/tempo.yaml rename to infra/base/tempo.yaml index 1ce4fbb..0a0c09b 100644 --- a/infra/tempo.yaml +++ b/infra/base/tempo.yaml @@ -21,7 +21,7 @@ spec: helm: releaseName: tempo valueFiles: - - $values/infra/values/tempo-values.yaml + - $values/infra/values/base/tempo-values.yaml - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git targetRevision: HEAD diff --git a/infra/base/traefik-application.yaml b/infra/base/traefik-application.yaml new file mode 100644 index 0000000..eb9fd2c --- /dev/null +++ b/infra/base/traefik-application.yaml @@ -0,0 +1,51 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: traefik-system + annotations: + argocd.argoproj.io/sync-wave: "-1" +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: traefik + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "1" + labels: + app.kubernetes.io/name: traefik + app.kubernetes.io/part-of: platform + app.kubernetes.io/managed-by: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + + sources: + - repoURL: https://traefik.github.io/charts + chart: traefik + targetRevision: "28.0.0" + helm: + releaseName: traefik + valueFiles: + - $values/infra/values/base/traefik-values.yaml + - $values/infra/values/upc-dev/traefik-values.yaml + + - repoURL: git@github.com:fortedigital/sturdy-adventure.git + targetRevision: HEAD + ref: values + + destination: + server: https://kubernetes.default.svc + namespace: traefik-system + + syncPolicy: + automated: + prune: true + selfHeal: true + allowEmpty: false + + syncOptions: + - CreateNamespace=true + - Validate=true + - ServerSideApply=true diff --git a/infra/trivy.yaml b/infra/base/trivy.yaml similarity index 100% rename from infra/trivy.yaml rename to infra/base/trivy.yaml diff --git a/infra/overlays/upc-dev/kustomization.yaml b/infra/overlays/upc-dev/kustomization.yaml new file mode 100644 index 0000000..1895aac --- /dev/null +++ b/infra/overlays/upc-dev/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- ../../base + +# No patches needed — base already has "upc-dev" paths +# upc-dev is the default/base cluster diff --git a/infra/overlays/upc-prod/kustomization.yaml b/infra/overlays/upc-prod/kustomization.yaml new file mode 100644 index 0000000..ebfc179 --- /dev/null +++ b/infra/overlays/upc-prod/kustomization.yaml @@ -0,0 +1,50 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- ../../base + +patches: +# Traefik: swap upc-dev → upc-prod in valueFiles +- target: + kind: Application + name: traefik + patch: | + - op: replace + path: /spec/sources/0/helm/valueFiles/1 + value: $values/infra/values/upc-prod/traefik-values.yaml + +# Keycloak: swap upc-dev → upc-prod +- target: + kind: Application + name: keycloak + patch: | + - op: replace + path: /spec/sources/0/helm/valueFiles/1 + value: $values/infra/values/upc-prod/keycloak-values.yaml + +# Grafana: swap upc-dev → upc-prod +- target: + kind: Application + name: grafana + patch: | + - op: replace + path: /spec/sources/0/helm/valueFiles/1 + value: $values/infra/values/upc-prod/grafana-values.yaml + +# Secrets: change path to upc-prod +- target: + kind: Application + name: secrets + patch: | + - op: replace + path: /spec/source/path + value: secrets/upc-prod + +# Enterprise-apps: point to upc-prod overlay +- target: + kind: Application + name: enterprise-apps + patch: | + - op: replace + path: /spec/source/path + value: apps/overlays/upc-prod diff --git a/infra/traefik-application.yaml b/infra/traefik-application.yaml deleted file mode 100644 index 1fb13ab..0000000 --- a/infra/traefik-application.yaml +++ /dev/null @@ -1,159 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: traefik-system - annotations: - argocd.argoproj.io/sync-wave: "-1" ---- -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: traefik - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "1" - labels: - app.kubernetes.io/name: traefik - app.kubernetes.io/part-of: platform - app.kubernetes.io/managed-by: argocd - finalizers: - - resources-finalizer.argocd.argoproj.io -spec: - project: default - - source: - repoURL: https://traefik.github.io/charts - chart: traefik - targetRevision: "28.0.0" - - helm: - values: | - metrics: - addInternals: true - tracing: - otlp: - enabled: true - logs: - general: - level: DEBUG - access: - format: json - enabled: true - - additionalArguments: - - "--tracing.otlp.http.endpoint=http://tempo.monitoring.svc.cluster.local:4318/v1/traces" - - providers: - kubernetesIngress: - publishedService: # Fixes ArgoCD health checks for LoadBalancer services - enabled: true - deployment: - replicas: 2 - - ingressRoute: - dashboard: - enabled: true - # Optional: specify entrypoint - entrypoint: traefik - - api: - dashboard: true - debug: false - - service: - type: LoadBalancer - annotations: - traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.priority: "42" - traefik.ingress.kubernetes.io/router.tls: "true" - service.beta.kubernetes.io/upcloud-load-balancer-config: | - { - "frontends": [ - { - "name": "web", - "mode": "tcp" - }, - { - "name": "websecure", - "mode": "tcp" - }, - { - "name": "giteassh", - "mode": "tcp" - } - ], - "backends": [ - { - "name": "web", - "properties": { - "outbound_proxy_protocol": "v2" - } - }, - { - "name": "websecure", - "properties": { - "outbound_proxy_protocol": "v2" - } - }, - { - "name": "giteassh" - } - ] - } - - ingressClass: - enabled: true - isDefaultClass: true - - # Configure entry points - ports: - metrics: - expose: - default: true - observability: - accessLogs: true - metrics: true - tracing: true - traceVerbosity: detailed - web: - proxyProtocol: - trustedIPs: "172.16.1.0/24" - forwardedHeaders: - trustedIPs: "172.16.1.0/24" - http: - redirections: - entrypoint: - to: websecure - scheme: https - - websecure: - proxyProtocol: - trustedIPs: "172.16.1.0/24" - forwardedHeaders: - trustedIPs: "172.16.1.0/24" - observability: - accessLogs: true - metrics: true - tracing: true - - giteassh: - port: 2222 - expose: - default: true - exposedPort: 2222 - protocol: TCP - - destination: - server: https://kubernetes.default.svc - namespace: traefik-system - - syncPolicy: - automated: - prune: true - selfHeal: true - allowEmpty: false - - syncOptions: - - CreateNamespace=true - - Validate=true - - ServerSideApply=true diff --git a/infra/values/argocd-values.yaml b/infra/values/base/argocd-values.yaml similarity index 95% rename from infra/values/argocd-values.yaml rename to infra/values/base/argocd-values.yaml index e1e1f8e..83dc1b3 100644 --- a/infra/values/argocd-values.yaml +++ b/infra/values/base/argocd-values.yaml @@ -1,5 +1,3 @@ -global: - domain: argocd.127.0.0.1.nip.io configs: secret: createSecret: true @@ -22,10 +20,6 @@ notifications: secret: create: false - # Shared context variables available in all templates - context: - clusterName: "dev-fd-no-svg1" - # Define notification templates templates: template.app-syncing: | diff --git a/infra/values/base/dot-ai-stack-values.yaml b/infra/values/base/dot-ai-stack-values.yaml new file mode 100644 index 0000000..1d64e0a --- /dev/null +++ b/infra/values/base/dot-ai-stack-values.yaml @@ -0,0 +1,11 @@ +dot-ai: + ingress: + enabled: true + className: traefik +dot-ai-ui: + uiAuth: + secretRef: + name: dot-ai-secrets + ingress: + enabled: true + className: traefik diff --git a/infra/values/fluent-bit-values.yaml b/infra/values/base/fluent-bit-values.yaml similarity index 100% rename from infra/values/fluent-bit-values.yaml rename to infra/values/base/fluent-bit-values.yaml diff --git a/infra/values/gitea-actions-values.yaml b/infra/values/base/gitea-actions-values.yaml similarity index 100% rename from infra/values/gitea-actions-values.yaml rename to infra/values/base/gitea-actions-values.yaml diff --git a/infra/values/gitea-values.yaml b/infra/values/base/gitea-values.yaml similarity index 100% rename from infra/values/gitea-values.yaml rename to infra/values/base/gitea-values.yaml diff --git a/infra/values/grafana-values.yaml b/infra/values/base/grafana-values.yaml similarity index 97% rename from infra/values/grafana-values.yaml rename to infra/values/base/grafana-values.yaml index d6acbcf..9fdf61f 100644 --- a/infra/values/grafana-values.yaml +++ b/infra/values/base/grafana-values.yaml @@ -1,7 +1,5 @@ ingress: enabled: true - hosts: - - grafana.127.0.0.1.nip.io resources: requests: cpu: 50m diff --git a/infra/values/keycloak-values.yaml b/infra/values/base/keycloak-values.yaml similarity index 99% rename from infra/values/keycloak-values.yaml rename to infra/values/base/keycloak-values.yaml index f1d7c4e..7599515 100644 --- a/infra/values/keycloak-values.yaml +++ b/infra/values/base/keycloak-values.yaml @@ -1,5 +1,4 @@ # Bitnami Keycloak Helm Chart Values -# Host: id.forteapps.net # Chart version: 25.2.0 image: @@ -15,7 +14,6 @@ auth: ingress: enabled: true - hostname: id.forteapps.net tls: true ingressClassName: traefik annotations: diff --git a/infra/values/loki-values.yaml b/infra/values/base/loki-values.yaml similarity index 100% rename from infra/values/loki-values.yaml rename to infra/values/base/loki-values.yaml diff --git a/infra/values/opencost-values.yaml b/infra/values/base/opencost-values.yaml similarity index 100% rename from infra/values/opencost-values.yaml rename to infra/values/base/opencost-values.yaml diff --git a/infra/values/prometheus-values.yaml b/infra/values/base/prometheus-values.yaml similarity index 100% rename from infra/values/prometheus-values.yaml rename to infra/values/base/prometheus-values.yaml diff --git a/infra/values/renovate-values.yaml b/infra/values/base/renovate-values.yaml similarity index 100% rename from infra/values/renovate-values.yaml rename to infra/values/base/renovate-values.yaml diff --git a/infra/values/tempo-values.yaml b/infra/values/base/tempo-values.yaml similarity index 100% rename from infra/values/tempo-values.yaml rename to infra/values/base/tempo-values.yaml diff --git a/infra/values/base/traefik-values.yaml b/infra/values/base/traefik-values.yaml new file mode 100644 index 0000000..0df67ba --- /dev/null +++ b/infra/values/base/traefik-values.yaml @@ -0,0 +1,50 @@ +providers: + kubernetesIngress: + publishedService: # Fixes ArgoCD health checks for LoadBalancer services + enabled: true +deployment: + replicas: 2 + +ingressRoute: + dashboard: + enabled: true + # Optional: specify entrypoint + entrypoint: traefik + +api: + dashboard: true + debug: false + +service: + type: LoadBalancer + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.priority: "42" + traefik.ingress.kubernetes.io/router.tls: "true" + +ingressClass: + enabled: true + isDefaultClass: true + +# Configure entry points +ports: + metrics: + expose: + default: true + observability: + accessLogs: true + metrics: true + tracing: true + traceVerbosity: detailed + web: + http: + redirections: + entrypoint: + to: websecure + scheme: https + + websecure: + observability: + accessLogs: true + metrics: true + tracing: true diff --git a/infra/values/upc-dev/argocd-values.yaml b/infra/values/upc-dev/argocd-values.yaml new file mode 100644 index 0000000..6ed9cea --- /dev/null +++ b/infra/values/upc-dev/argocd-values.yaml @@ -0,0 +1,5 @@ +global: + domain: argocd.127.0.0.1.nip.io +notifications: + context: + clusterName: "dev-fd-eu-no-svg1" diff --git a/infra/values/upc-dev/dot-ai-stack-values.yaml b/infra/values/upc-dev/dot-ai-stack-values.yaml new file mode 100644 index 0000000..f96ad02 --- /dev/null +++ b/infra/values/upc-dev/dot-ai-stack-values.yaml @@ -0,0 +1,8 @@ +dot-ai: + ingress: + host: kubemcp.forteapps.net + webUI: + baseUrl: http://kubemcpui.forteapps.net +dot-ai-ui: + ingress: + host: kubemcpui.forteapps.net diff --git a/infra/values/upc-dev/grafana-values.yaml b/infra/values/upc-dev/grafana-values.yaml new file mode 100644 index 0000000..fbbe9fd --- /dev/null +++ b/infra/values/upc-dev/grafana-values.yaml @@ -0,0 +1,3 @@ +ingress: + hosts: + - grafana.forteapps.net diff --git a/infra/values/upc-dev/keycloak-values.yaml b/infra/values/upc-dev/keycloak-values.yaml new file mode 100644 index 0000000..ecc2905 --- /dev/null +++ b/infra/values/upc-dev/keycloak-values.yaml @@ -0,0 +1,2 @@ +ingress: + hostname: id.forteapps.net diff --git a/infra/values/upc-dev/traefik-values.yaml b/infra/values/upc-dev/traefik-values.yaml new file mode 100644 index 0000000..31f07f4 --- /dev/null +++ b/infra/values/upc-dev/traefik-values.yaml @@ -0,0 +1,40 @@ +service: + annotations: + service.beta.kubernetes.io/upcloud-load-balancer-config: | + { + "frontends": [ + { + "name": "web", + "mode": "tcp" + }, + { + "name": "websecure", + "mode": "tcp" + } + ], + "backends": [ + { + "name": "web", + "properties": { + "outbound_proxy_protocol": "v2" + } + }, + { + "name": "websecure", + "properties": { + "outbound_proxy_protocol": "v2" + } + } + ] + } +ports: + web: + proxyProtocol: + trustedIPs: "172.16.1.0/24" + forwardedHeaders: + trustedIPs: "172.16.1.0/24" + websecure: + proxyProtocol: + trustedIPs: "172.16.1.0/24" + forwardedHeaders: + trustedIPs: "172.16.1.0/24" diff --git a/infra/values/upc-prod/argocd-values.yaml b/infra/values/upc-prod/argocd-values.yaml new file mode 100644 index 0000000..e183032 --- /dev/null +++ b/infra/values/upc-prod/argocd-values.yaml @@ -0,0 +1,5 @@ +global: + domain: argocd.us.forteapps.net +notifications: + context: + clusterName: "dev-fd-us-east1" diff --git a/infra/values/upc-prod/dot-ai-stack-values.yaml b/infra/values/upc-prod/dot-ai-stack-values.yaml new file mode 100644 index 0000000..c429d3b --- /dev/null +++ b/infra/values/upc-prod/dot-ai-stack-values.yaml @@ -0,0 +1,8 @@ +dot-ai: + ingress: + host: kubemcp.us.forteapps.net + webUI: + baseUrl: http://kubemcpui.us.forteapps.net +dot-ai-ui: + ingress: + host: kubemcpui.us.forteapps.net diff --git a/infra/values/upc-prod/grafana-values.yaml b/infra/values/upc-prod/grafana-values.yaml new file mode 100644 index 0000000..417c8c4 --- /dev/null +++ b/infra/values/upc-prod/grafana-values.yaml @@ -0,0 +1,3 @@ +ingress: + hosts: + - grafana.us.forteapps.net diff --git a/infra/values/upc-prod/keycloak-values.yaml b/infra/values/upc-prod/keycloak-values.yaml new file mode 100644 index 0000000..df05358 --- /dev/null +++ b/infra/values/upc-prod/keycloak-values.yaml @@ -0,0 +1,2 @@ +ingress: + hostname: id.us.forteapps.net diff --git a/infra/values/upc-prod/traefik-values.yaml b/infra/values/upc-prod/traefik-values.yaml new file mode 100644 index 0000000..06a9076 --- /dev/null +++ b/infra/values/upc-prod/traefik-values.yaml @@ -0,0 +1,13 @@ +service: + annotations: {} +ports: + web: + proxyProtocol: + trustedIPs: "10.0.0.0/16" + forwardedHeaders: + trustedIPs: "10.0.0.0/16" + websecure: + proxyProtocol: + trustedIPs: "10.0.0.0/16" + forwardedHeaders: + trustedIPs: "10.0.0.0/16" diff --git a/secrets/argocd-mcp-credentials.yaml b/secrets/upc-dev/argocd-mcp-credentials.yaml similarity index 100% rename from secrets/argocd-mcp-credentials.yaml rename to secrets/upc-dev/argocd-mcp-credentials.yaml diff --git a/secrets/argocdmcp-auth-oidc-sealed.yaml b/secrets/upc-dev/argocdmcp-auth-oidc-sealed.yaml similarity index 100% rename from secrets/argocdmcp-auth-oidc-sealed.yaml rename to secrets/upc-dev/argocdmcp-auth-oidc-sealed.yaml diff --git a/secrets/upc-dev/dot-ai-secrets.yaml b/secrets/upc-dev/dot-ai-secrets.yaml new file mode 100644 index 0000000..7f6f113 --- /dev/null +++ b/secrets/upc-dev/dot-ai-secrets.yaml @@ -0,0 +1,18 @@ +# SealedSecret created after namespace (sync-wave: 0) +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + creationTimestamp: null + name: dot-ai-secrets + namespace: dot-ai +spec: + encryptedData: + anthropic-api-key: AgASIRVNs7kIvZ/XwFJ4j9TJ05TW4YlFNzi2lx+6URlvzjTkMignK+y4/HD3wTK37BkIonnOXf/DGJcMgqgxOrHBVu9tLkSamve5qgf+SOZ8jxaqpLX2e5hdmHrgMp7rVWKiOoxM+bvu8Gdve3eMwXX8Eazj7W7vi430LsvW4BmPrQp+A9SOP+hwMlV5r+P5pUogC3hddt5KoSwpgnpOb8fRGlLDpc548eDNBJrhsAZiiFqc8+CycHX6idxWzlWOTPR05LBs7YrTIP/LUXWdyq6UG8yB5D0c9JNndnGo6kSfJxdaxCNO7GhMAOmo4CitzYn/zhICwuHPLKwUYQBcEHVpFxgub0U9fY7s4i0SAH9aF1kk8yqHWaCicHzi6B3AxMZYr+knS5vVNB+uGZmSKgZ7QNE1bF6E4Gg5bXRwk7OVQhuJSBzxVNDoXoJ6stL8ejU+MP5FtI792FxZbNDImu3kqor/t9wB49ZeB9Ngks2yvtlSrQ3G2lxJaOgNBPmKYhAX95JFI3xUSHOMa18ftn+aXmEQTXOHtM/IT8A95gUvIKz8Hrt03CwgZ8E+3lcsjAVsfNjqn/erqV+xi93OYOL6o9ivmTIem3MzsqjKuQ57HKaghnd+Ygdt/WfotRBSgZtCweOkZBV0XKxI1RmCQXk6dce8x4T7FDEeaSQwkc4yZ77LfQJnyVgU1rds9Hqv0RbymHK8JCQEMMb6PcK+Ow068fdj4NP27HkuYMB5JdPp682KIRwGznN2d3+QPXRd/ZHshnixudY7CLirlZl9xD0HssfSBI+uCrFEAln8D93UYbn7trF5gkKWYQsQ07CVZ5Z1qXieqwJWjHRY+oY= + auth-token: AgB65rUY3yJuTTLbGyrCtJPZp8UyEOr+kznSMGvUaZ9PoHq+kIEhazRdVKHa/WGvMWuc4x+XYRuIGj+2KiooyilUPrLcE3bz9fUOAFbRw3+iUh3WAgwK+f3eBUG6/0OoFw+GJ583IRidDW1t2BchMxSM1m+3vmQoF24qj7k9j/lWPsnX2IX3DhM0SomD1xmG+LsQMo2e4vQXB0BxhVDIQ621JTrvUPYzYx0NlPqZO/MtR1JWYS7WBTvegvgeBKLxfy9KLnqsuzu7rc2t9BYt7TRqvBg/prrKPdSV6Ei4GOZq+AcG15iOKXhsj8SxejPpM0QDemFTRQkdfz+k8ms4/SM0eylr5fEaa99TMTvjYGCfjJJyRcVm5Ef/XdmXiM3OI1u+9QNPiqXh1zmtp0yQMZ7nRE70kKQ2MHVhEmSUkBjovybIzLk4OL1v0FGDDqa1BN9KmNEBlo8H7DqXzfamVoNPyuuO625B3HgSeR3Udq8hngy9W/wiolmMNSc9C6vBA0HzE7Mkq79fHh/ruTi5zOJLfyEP7KQQqlNKfEtDFQfabaV9ERthQjuUs8ZIrdfCTOyQkrmmGY/sH5yodLRSYSEpHFrhPepwHS7lGzbVucW3Fn34D4OxJXyXQQObZKybLV7g6Oglf2Pdn11CEu/4+19RlykuOlm1dlj852a5NWWmmX319laLGlEd1BpG4cZyc9UZud68dGumwzrUupUMTLlJx8bR5rLgqaMpQMluwvq8MQbBXm4ySOcQ2jQhjw== + openai-api-key: AgBc4cSrd0riSxKPrp7AqrNYMDAZJTe3aCjTOwEb5pS/KARkgoEGz+0ZhLXa2bcCsPlQzqRzZ1c1cUCnBACkp1xoMfhSOoIJUWJQHRgZx5kEDhNIE/M8PQe8es7jYsW7/ui6iK8uj3Ljk21r8l/ctnP/KiKyML4553el28Ya7XsPQynRvpVexFC4BoJw50nkauLnvl3in0cKmEVAkMWorUP3Etapaj4DWkCQQ22RPN0xGNo9yiIUEAVE+uTRFNKGEHMIvJzgB291FJQtG/WoN/Sy/DeoZkUYndA7CbfFgiz9sq3GqJwyqTM+Ikzw6VCQ3TVWYO/6+yaEzT1NZ/rw00YhDyFoH+yGTkX5lrSUo8lGf3T9OODTdSS1203xtj4dhGbY70sPGMxDfucnO6piVcuRfh9bWg1VX+MTjqpBQE1J0DKJanhoh6lKbPOJhRbi0TIoCz1a3btbbKLDq2YJl7FXA3QNdBsR4qVJ3xmgWlH/eTUskCE2YxDzHmkxgZN2mL22SfeUJYtDRswRG0UGc6pvg2xrR0iEDB0UbH4FQK46fWv8aiOejteLlAAyA9HNA8yi73rTEjq55wpO19/MYj+6oUGEHpFaJje77INTHAKpdbfkp8iKotNXFYLM2hsjyau+x/AFjg87kEdIUVVHHLVMDhOq6NO+foM0nFOIv4lr2Yjl15+ImMSTEfcBr+nWaomnb9G8i5ptqz9bMHbxAcHpzWUBH9SZhzrCWZNDzOm2X2K6mpXhg4WX7VJMoIJk/f+bxTTKnWBawdnpCdbdG5GYQh7usqjPuELFYRTsx+6Tqoddp4KIEyMMxO81XObiN5vEBg74ygunxrjKNg5FoOkUA79YvcGBVVU1FNRl3d8IslXFqhEOT35nxGBB0T7ZORii34tZ2E551NkIo1WbCwilD3Dlgw== + ui-auth-token: AgDStP4jHhMehx/SAv5x2aQrJwChnWq8WhV/LVTSuuCSSy9kFQGa3Izyl21sMhLKUgrlS030GH50lbku+IY90S+MSBfaCq0Xb8qwohfH+qJulMRgljoU8r58/3ZsaBzbGJjiNMgBCwGlvuK85t3R3662ftwikahWqLfwAahi12L0llUDNyG9UB0Os3oR2CVAt83+YB07YspCWRzwuOz9D1SgOL/g/JF2hoL43pDD65BqYKdEuLFInJZx1Ul68V/FPmJK1gmJb/Kv8pgtk0sGTzxbbTdIQ1Ugf6+8//CWEq6GsMeEZG9VwExL/KYqobnbqd02FRgSSvWybhWLjiALLQvAFtce6FR6bur0rsariyogGjYCuXYdRnhf7QnB6NopwplhFP37Qf6E4q2pJTJ6Fzv0xq+XlfzNkn9Jvebrdkk/5CThW+wPAfx8r1VIySqBsYnPO/ZQDkfQ5ocX6fmzZ1iCg+0NI24k2YwCeMZtEAGpKEtejS0BLvqrilUG6Oz9sHCGHro4Bv5B/jzUjGbye0DSDj1f6c2RcpqMiUxfPvydJIcJGhrTx8sZS3qhEWME7kwjJCZpHEzfdv0weSJbgVGBSh9e1wjZxxeJGXuZKdFzdhpNEWP4uScGW0UnRDwxZzHMsLjS/W30mQmwmgJVTKdPl6VQrvdq7m4AC6+/d7iXlHazieC1KpBme4hWzO+/h7qRw1P5Va/ZWZtnGs8476J8hIojRtOJ/CdWwVLUa0gHo4CYnempIMwCIS3GtA== + template: + metadata: + creationTimestamp: null + name: dot-ai-secrets + namespace: dot-ai diff --git a/secrets/forte10x-app-credentials-sealed.yaml b/secrets/upc-dev/forte10x-app-credentials-sealed.yaml similarity index 100% rename from secrets/forte10x-app-credentials-sealed.yaml rename to secrets/upc-dev/forte10x-app-credentials-sealed.yaml diff --git a/secrets/keycloak-credentials-sealed.yaml b/secrets/upc-dev/keycloak-credentials-sealed.yaml similarity index 100% rename from secrets/keycloak-credentials-sealed.yaml rename to secrets/upc-dev/keycloak-credentials-sealed.yaml diff --git a/secrets/musicman-credentials.yaml b/secrets/upc-dev/musicman-credentials.yaml similarity index 100% rename from secrets/musicman-credentials.yaml rename to secrets/upc-dev/musicman-credentials.yaml