Compare commits
2 Commits
feature/ar
...
ac0f464b2a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac0f464b2a | ||
|
|
a681a9ae81 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,2 +0,0 @@
|
|||||||
# Force LF line endings for shell scripts
|
|
||||||
*.sh text eol=lf
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
name: AI Code Review
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [ labeled, synchronize ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ai-review:
|
|
||||||
if: >-
|
|
||||||
(github.event.action == 'synchronized' && contains(toJSON(github.event.pull_request.labels), 'ai-review')) || contains(toJSON(gitea.event.changes.added_labels), 'ai-review')
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
env:
|
|
||||||
AI_REVIEW_CONFIG_FILE_YAML: ./shared-prompts/iac/.ai-review.yaml
|
|
||||||
# VCS configuration
|
|
||||||
VCS__PROVIDER: GITEA
|
|
||||||
VCS__PIPELINE__OWNER: ${{ github.repository_owner }}
|
|
||||||
VCS__PIPELINE__REPO: ${{ github.event.repository.name }}
|
|
||||||
VCS__PIPELINE__PULL_NUMBER: ${{ github.event.pull_request.number }}
|
|
||||||
VCS__HTTP_CLIENT__API_URL: https://git.forteapps.net/api/v1
|
|
||||||
VCS__HTTP_CLIENT__API_TOKEN: ${{ secrets.AI_REVIEW_TOKEN }}
|
|
||||||
# Review — disable fallback to see real Gitea API errors
|
|
||||||
REVIEW__INLINE_COMMENT_FALLBACK: "false"
|
|
||||||
# LLM configuration
|
|
||||||
LLM__PROVIDER: CLAUDE
|
|
||||||
LLM__META__MODEL: claude-sonnet-4-20250514
|
|
||||||
LLM__META__MAX_TOKENS: "4096"
|
|
||||||
LLM__HTTP_CLIENT__API_URL: https://api.anthropic.com
|
|
||||||
LLM__HTTP_CLIENT__API_TOKEN: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
fetch-depth: 0
|
|
||||||
token: ${{ secrets.AI_REVIEW_TOKEN }}
|
|
||||||
|
|
||||||
- name: Run inline review
|
|
||||||
uses: docker://nikitafilonov/ai-review:v0.64.0
|
|
||||||
with:
|
|
||||||
args: ai-review run-inline
|
|
||||||
|
|
||||||
- name: Run summary review
|
|
||||||
uses: docker://nikitafilonov/ai-review:v0.64.0
|
|
||||||
with:
|
|
||||||
args: ai-review run-summary
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,6 +6,7 @@
|
|||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|
||||||
|
.github/
|
||||||
private/
|
private/
|
||||||
.helm/
|
.helm/
|
||||||
temp/
|
temp/
|
||||||
@@ -15,4 +16,3 @@ CLAUDE.md
|
|||||||
devbox.d/
|
devbox.d/
|
||||||
devbox.lock
|
devbox.lock
|
||||||
.devbox/
|
.devbox/
|
||||||
bash.exe.stackdump
|
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
|||||||
[submodule "shared-prompts"]
|
|
||||||
path = shared-prompts
|
|
||||||
url = https://git.forteapps.net/Forte/ai-review-prompts.git
|
|
||||||
97
README.md
97
README.md
@@ -1,9 +1,9 @@
|
|||||||
# Kubernetes Cluster - GitOps Configuration
|
# Kubernetes Cluster - GitOps Configuration
|
||||||
|
|
||||||
> **Kubernetes cluster bootstrapping and GitOps configuration repository** using ArgoCD for multi-cloud Kubernetes (UpCloud, AWS EKS, Azure AKS, GCP GKE)
|
> **Kubernetes cluster bootstrapping and GitOps configuration repository** using ArgoCD for UpCloud Managed Kubernetes
|
||||||
|
|
||||||
[](https://argoproj.github.io/cd/)
|
[](https://argoproj.github.io/cd/)
|
||||||
[]()
|
[](https://upcloud.com/)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -27,8 +27,8 @@
|
|||||||
### For New Developers
|
### For New Developers
|
||||||
```bash
|
```bash
|
||||||
# 1. Clone repositories
|
# 1. Clone repositories
|
||||||
git clone https://git.forteapps.net/Forte/launchpad.git
|
git clone https://github.com/fortedigital/sturdy-adventure.git
|
||||||
git clone ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git
|
git clone git@github.com:fortedigital/helm-values.git
|
||||||
|
|
||||||
# 2. Read the guides
|
# 2. Read the guides
|
||||||
# - Start: docs/GITOPS-ARCHITECTURE.md
|
# - Start: docs/GITOPS-ARCHITECTURE.md
|
||||||
@@ -57,10 +57,10 @@ This repository contains the complete GitOps configuration for our Kubernetes cl
|
|||||||
|
|
||||||
### What's Inside
|
### What's Inside
|
||||||
|
|
||||||
- **Infrastructure Applications**: Traefik, Cert-Manager, Kyverno, Prometheus, Grafana, Loki, Tempo, Sealed Secrets
|
- **Infrastructure Applications**: Traefik, Cert-Manager, Kyverno, Prometheus, Grafana, Loki, Sealed Secrets
|
||||||
- **Business Applications**: MCP10X, MusicMan, Dot-AI Stack, ArgoCD MCP
|
- **Business Applications**: MCP10X, MusicMan, Dot-AI Stack, ArgoCD MCP
|
||||||
- **Policies**: Kyverno security policies for secret management, namespace controls, pod verification
|
- **Policies**: Kyverno security policies for secret management, namespace controls, pod verification
|
||||||
- **Monitoring**: Full observability stack with metrics, logs, traces, and alerting
|
- **Monitoring**: Full observability stack with metrics, logs, and alerting
|
||||||
- **Secrets**: Sealed Secrets for secure Git storage
|
- **Secrets**: Sealed Secrets for secure Git storage
|
||||||
|
|
||||||
### Key Features
|
### Key Features
|
||||||
@@ -72,7 +72,7 @@ This repository contains the complete GitOps configuration for our Kubernetes cl
|
|||||||
✅ **Policy Enforcement**: Kyverno ensures security and compliance
|
✅ **Policy Enforcement**: Kyverno ensures security and compliance
|
||||||
✅ **Authentication**: Automatic sidecar injection (token & OIDC support)
|
✅ **Authentication**: Automatic sidecar injection (token & OIDC support)
|
||||||
✅ **TLS Everywhere**: Automatic Let's Encrypt certificates
|
✅ **TLS Everywhere**: Automatic Let's Encrypt certificates
|
||||||
✅ **Full Observability**: Prometheus, Grafana, Loki, Tempo integration
|
✅ **Full Observability**: Prometheus, Grafana, Loki integration
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -83,38 +83,18 @@ This repository contains the complete GitOps configuration for our Kubernetes cl
|
|||||||
├── bootstrap.sh # Cluster initialization script
|
├── bootstrap.sh # Cluster initialization script
|
||||||
├── _app-of-apps.yaml # Root ArgoCD Application (App-of-Apps pattern)
|
├── _app-of-apps.yaml # Root ArgoCD Application (App-of-Apps pattern)
|
||||||
│
|
│
|
||||||
├── infra/ # Infrastructure ArgoCD Applications (Kustomize multi-cluster)
|
├── infra/ # Infrastructure ArgoCD Applications
|
||||||
│ ├── base/ # Base ArgoCD Application manifests (EU defaults)
|
│ ├── enterprise-apps.yaml # Manages all apps in apps/ folder
|
||||||
│ │ ├── kustomization.yaml
|
│ ├── traefik-application.yaml
|
||||||
│ │ ├── traefik-application.yaml
|
│ ├── cert-manager-application.yaml
|
||||||
│ │ ├── keycloak.yaml
|
│ ├── kyverno.yaml
|
||||||
│ │ ├── grafana.yaml
|
│ ├── prometheus.yaml
|
||||||
│ │ ├── gitea.yaml
|
│ ├── grafana.yaml
|
||||||
│ │ ├── gitea-actions.yaml
|
│ ├── loki.yaml
|
||||||
│ │ ├── tempo.yaml
|
│ ├── fluent-bit.yaml
|
||||||
│ │ ├── renovate.yaml
|
│ ├── trivy.yaml
|
||||||
│ │ ├── ... # All other Application manifests
|
│ ├── sealedsecrets.yaml
|
||||||
│ │ └── secrets.yaml
|
|
||||||
│ ├── overlays/ # Per-cluster overrides (Kustomize)
|
|
||||||
│ │ ├── upc-dev/ # UpCloud Dev (uses base as-is)
|
|
||||||
│ │ ├── upc-prod/ # UpCloud Prod (patches value paths)
|
|
||||||
│ │ ├── eks-dev/ # AWS EKS Dev
|
|
||||||
│ │ ├── eks-prod/ # AWS EKS Prod
|
|
||||||
│ │ ├── aks-dev/ # Azure AKS Dev
|
|
||||||
│ │ ├── aks-prod/ # Azure AKS Prod
|
|
||||||
│ │ ├── gke-dev/ # GCP GKE Dev
|
|
||||||
│ │ └── gke-prod/ # GCP GKE Prod
|
|
||||||
│ ├── dashboards/ # Grafana dashboard ConfigMaps
|
|
||||||
│ └── values/ # Helm value overrides
|
│ └── values/ # Helm value overrides
|
||||||
│ ├── base/ # Shared cloud-agnostic values
|
|
||||||
│ ├── upc-dev/ # UpCloud Dev (storage, LB, pricing)
|
|
||||||
│ ├── upc-prod/ # UpCloud Prod
|
|
||||||
│ ├── eks-dev/ # AWS EKS Dev
|
|
||||||
│ ├── eks-prod/ # AWS EKS Prod
|
|
||||||
│ ├── aks-dev/ # Azure AKS Dev
|
|
||||||
│ ├── aks-prod/ # Azure AKS Prod
|
|
||||||
│ ├── gke-dev/ # GCP GKE Dev
|
|
||||||
│ └── gke-prod/ # GCP GKE Prod
|
|
||||||
│
|
│
|
||||||
├── apps/ # Business Applications
|
├── apps/ # Business Applications
|
||||||
│ ├── mcp10x.yaml
|
│ ├── mcp10x.yaml
|
||||||
@@ -156,14 +136,14 @@ This repository contains the complete GitOps configuration for our Kubernetes cl
|
|||||||
|
|
||||||
| Repository | Purpose | Who Edits | How Often |
|
| Repository | Purpose | Who Edits | How Often |
|
||||||
|------------|---------|-----------|-----------|
|
|------------|---------|-----------|-----------|
|
||||||
| **[launchpad](https://git.forteapps.net/Forte/launchpad)** (this repo) | ArgoCD Applications, cluster resources | Platform / DevOps engineers | ✅ Often |
|
| **[sturdy-adventure](https://github.com/fortedigital/sturdy-adventure.git)** (this repo) | ArgoCD Applications, cluster resources | Platform / DevOps engineers | ✅ Often |
|
||||||
| **[forte-helm](https://git.forteapps.net/Forte/forte-helm)** | Generic Helm chart templates | Platform engineers | ❌ Rarely |
|
| **[forte-helm](https://github.com/snothub/forte-helm)** | Generic Helm chart templates | Platform engineers | ❌ Rarely |
|
||||||
| **[helm-prod-values](ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git)** | App-specific configuration & versions | Developers / CI pipelines | ✅ Sometimes |
|
| **[helm-values](git@github.com:fortedigital/helm-values.git)** | App-specific configuration & versions | Developers / CI pipelines | ✅ Sometimes |
|
||||||
|
|
||||||
### GitOps Workflow
|
### GitOps Workflow
|
||||||
|
|
||||||
```
|
```
|
||||||
Developer commits code → CI/CD builds image → Updates helm-prod-values → ArgoCD syncs → Deployed to cluster
|
Developer commits code → CI/CD builds image → Updates helm-values → ArgoCD syncs → Deployed to cluster
|
||||||
```
|
```
|
||||||
|
|
||||||
**Learn more**: [GitOps Architecture - GitOps Workflow](docs/GITOPS-ARCHITECTURE.md#gitops-workflow)
|
**Learn more**: [GitOps Architecture - GitOps Workflow](docs/GITOPS-ARCHITECTURE.md#gitops-workflow)
|
||||||
@@ -178,7 +158,7 @@ Developer commits code → CI/CD builds image → Updates helm-prod-values → A
|
|||||||
|
|
||||||
**Quick version**:
|
**Quick version**:
|
||||||
1. Create `apps/myapp.yaml` (ArgoCD Application manifest)
|
1. Create `apps/myapp.yaml` (ArgoCD Application manifest)
|
||||||
2. Create `helm-prod-values/myapp/values.yaml` (configuration)
|
2. Create `helm-values/myapp/values.yaml` (configuration)
|
||||||
3. Create sealed secrets if needed
|
3. Create sealed secrets if needed
|
||||||
4. Commit and push - ArgoCD auto-syncs!
|
4. Commit and push - ArgoCD auto-syncs!
|
||||||
|
|
||||||
@@ -187,8 +167,8 @@ Developer commits code → CI/CD builds image → Updates helm-prod-values → A
|
|||||||
**See detailed guide**: [Developer Guide - Updating an Existing Application](docs/DEVELOPER-GUIDE.md#updating-an-existing-application)
|
**See detailed guide**: [Developer Guide - Updating an Existing Application](docs/DEVELOPER-GUIDE.md#updating-an-existing-application)
|
||||||
|
|
||||||
**Quick version**:
|
**Quick version**:
|
||||||
- **Update code**: Push to app repo → CI/CD updates image tag in helm-prod-values
|
- **Update code**: Push to app repo → CI/CD updates image tag in helm-values
|
||||||
- **Update config**: Edit `helm-prod-values/myapp/values.yaml` → commit → push
|
- **Update config**: Edit `helm-values/myapp/values.yaml` → commit → push
|
||||||
|
|
||||||
### Manage Secrets
|
### Manage Secrets
|
||||||
|
|
||||||
@@ -216,7 +196,7 @@ git push
|
|||||||
|
|
||||||
**Quick version**:
|
**Quick version**:
|
||||||
```yaml
|
```yaml
|
||||||
# In helm-prod-values/myapp/values.yaml
|
# In helm-values/myapp/values.yaml
|
||||||
|
|
||||||
# Token-based auth (simple)
|
# Token-based auth (simple)
|
||||||
auth:
|
auth:
|
||||||
@@ -351,10 +331,8 @@ kubectl patch application myapp -n argocd \
|
|||||||
| **Prometheus** | Metrics | `monitoring` | 1 |
|
| **Prometheus** | Metrics | `monitoring` | 1 |
|
||||||
| **Grafana** | Dashboards | `monitoring` | 1 |
|
| **Grafana** | Dashboards | `monitoring` | 1 |
|
||||||
| **Loki** | Logs | `monitoring` | 1 |
|
| **Loki** | Logs | `monitoring` | 1 |
|
||||||
| **Tempo** | Distributed tracing | `monitoring` | 1 |
|
|
||||||
| **Fluent-Bit** | Log shipping | `monitoring` | DaemonSet |
|
| **Fluent-Bit** | Log shipping | `monitoring` | DaemonSet |
|
||||||
| **OpenCost** | Cost monitoring | `monitoring` | 1 |
|
| **Trivy** | Vulnerability scanning | `trivy-system` | 1 |
|
||||||
| **Renovate** | Dependency updates | `renovate` | CronJob |
|
|
||||||
|
|
||||||
**Full specs**: [Technical Reference - Infrastructure Components](docs/REFERENCE.md#infrastructure-components)
|
**Full specs**: [Technical Reference - Infrastructure Components](docs/REFERENCE.md#infrastructure-components)
|
||||||
|
|
||||||
@@ -372,12 +350,12 @@ kubectl patch application myapp -n argocd \
|
|||||||
## 📖 Key Concepts
|
## 📖 Key Concepts
|
||||||
|
|
||||||
### App-of-Apps Pattern
|
### App-of-Apps Pattern
|
||||||
`_app-of-apps-{cluster}.yaml` is the root Application that manages all other Applications in `infra/`. Kustomize overlays in `infra/overlays/{cluster}/` render the base Applications with per-cluster patches (e.g., swapping value file paths). Supported clusters: `upc-dev`, `upc-prod`, `eks-dev`, `eks-prod`, `aks-dev`, `aks-prod`, `gke-dev`, `gke-prod`.
|
`_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.
|
||||||
|
|
||||||
### Multi-Source Pattern
|
### Multi-Source Pattern
|
||||||
Applications reference both:
|
Applications reference both:
|
||||||
1. **Helm charts** from `forte-helm` (templates)
|
1. **Helm charts** from `forte-helm` (templates)
|
||||||
2. **Values** from `helm-prod-values` (configuration)
|
2. **Values** from `helm-values` (configuration)
|
||||||
|
|
||||||
This separates reusable templates from environment-specific config.
|
This separates reusable templates from environment-specific config.
|
||||||
|
|
||||||
@@ -446,7 +424,7 @@ Applications deploy in order using `argocd.argoproj.io/sync-wave`:
|
|||||||
### Adding a New Application
|
### Adding a New Application
|
||||||
1. Read [Developer Guide - Deploying Your First Application](docs/DEVELOPER-GUIDE.md#deploying-your-first-application)
|
1. Read [Developer Guide - Deploying Your First Application](docs/DEVELOPER-GUIDE.md#deploying-your-first-application)
|
||||||
2. Create ArgoCD Application manifest in `apps/`
|
2. Create ArgoCD Application manifest in `apps/`
|
||||||
3. Create Helm values in `helm-prod-values/`
|
3. Create Helm values in `helm-values/`
|
||||||
4. Create sealed secrets if needed
|
4. Create sealed secrets if needed
|
||||||
5. Commit and push - ArgoCD handles the rest!
|
5. Commit and push - ArgoCD handles the rest!
|
||||||
|
|
||||||
@@ -469,14 +447,16 @@ Documentation lives in `docs/`. To update:
|
|||||||
## 📝 Notes
|
## 📝 Notes
|
||||||
|
|
||||||
### Current Environment
|
### Current Environment
|
||||||
- **Provider**: Multi-cloud (UpCloud, AWS EKS, Azure AKS, GCP GKE)
|
- **Provider**: UpCloud Managed Kubernetes
|
||||||
- **Active clusters**: UpCloud (upc-dev, upc-prod)
|
|
||||||
- **Environment**: Production (internal use only)
|
- **Environment**: Production (internal use only)
|
||||||
|
- **Cluster**: Single cluster
|
||||||
- **Auth**: Disabled for ArgoCD (internal access)
|
- **Auth**: Disabled for ArgoCD (internal access)
|
||||||
- **Backup**: Gitea daily backup to S3-compatible storage
|
- **Backup**: None (cluster rebuildable via GitOps)
|
||||||
|
|
||||||
### Known Limitations
|
### Known Limitations
|
||||||
|
- No automated backups (yet)
|
||||||
- Secret rotation not automated
|
- Secret rotation not automated
|
||||||
|
- Single cluster (no multi-cluster setup)
|
||||||
- DNS management is manual
|
- DNS management is manual
|
||||||
|
|
||||||
**Future improvements**: See [Operations Runbook - Disaster Recovery](docs/OPERATIONS-RUNBOOK.md#disaster-recovery)
|
**Future improvements**: See [Operations Runbook - Disaster Recovery](docs/OPERATIONS-RUNBOOK.md#disaster-recovery)
|
||||||
@@ -490,12 +470,11 @@ Documentation lives in `docs/`. To update:
|
|||||||
- [Kyverno Documentation](https://kyverno.io/docs/)
|
- [Kyverno Documentation](https://kyverno.io/docs/)
|
||||||
- [Traefik Documentation](https://doc.traefik.io/traefik/)
|
- [Traefik Documentation](https://doc.traefik.io/traefik/)
|
||||||
- [Cert-Manager Documentation](https://cert-manager.io/docs/)
|
- [Cert-Manager Documentation](https://cert-manager.io/docs/)
|
||||||
- [Grafana Tempo Documentation](https://grafana.com/docs/tempo/)
|
|
||||||
- [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)
|
- [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)
|
||||||
|
|
||||||
### Related Repositories
|
### Related Repositories
|
||||||
- [forte-helm](https://git.forteapps.net/Forte/forte-helm) - Helm chart templates
|
- [forte-helm](https://github.com/snothub/forte-helm) - Helm chart templates
|
||||||
- [helm-prod-values](git@github.com:fortedigital/helm-prod-values.git) - Application values
|
- [helm-values](git@github.com:fortedigital/helm-values.git) - Application values
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -513,7 +492,7 @@ Internal use only. Not for public distribution.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: 2026-04-22
|
**Last Updated**: 2026-03-16
|
||||||
**Documentation Version**: 1.0.0
|
**Documentation Version**: 1.0.0
|
||||||
|
|
||||||
**🚀 Ready to get started? Check out the [Documentation Index](docs/README.md)!**
|
**🚀 Ready to get started? Check out the [Documentation Index](docs/README.md)!**
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
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: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
path: infra/overlays/aks-dev
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: default
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
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: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
path: infra/overlays/aks-prod
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: default
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
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: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
path: infra/overlays/eks-prod
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: default
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
@@ -18,9 +18,9 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
source:
|
source:
|
||||||
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
path: infra/overlays/gke-dev
|
path: infra/overlays/eu
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
namespace: default
|
namespace: default
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
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: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
path: infra/overlays/gke-prod
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: default
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
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: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
path: infra/overlays/upc-dev
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: default
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
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: ssh://git@git.forteapps.net:2222/Forte/launchpad.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
|
|
||||||
@@ -18,9 +18,9 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
source:
|
source:
|
||||||
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
path: infra/overlays/eks-dev
|
path: infra/overlays/us
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
namespace: default
|
namespace: default
|
||||||
@@ -16,14 +16,14 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
sources:
|
sources:
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git
|
- repoURL: https://github.com/snothub/forte-helm
|
||||||
path: forteapp
|
path: forteapp
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
helm:
|
helm:
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/argocd-mcp/values.yaml
|
- $values/argocd-mcp/values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git
|
- repoURL: git@github.com:fortedigital/helm-values.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ spec:
|
|||||||
releaseName: dot-ai-stack
|
releaseName: dot-ai-stack
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/infra/values/base/dot-ai-stack-values.yaml
|
- $values/infra/values/base/dot-ai-stack-values.yaml
|
||||||
- $values/infra/values/upc-dev/dot-ai-stack-values.yaml
|
- $values/infra/values/eu/dot-ai-stack-values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ resources:
|
|||||||
- dot-ai-stack.yaml
|
- dot-ai-stack.yaml
|
||||||
- mcp10x.yaml
|
- mcp10x.yaml
|
||||||
- musicman.yaml
|
- musicman.yaml
|
||||||
- ts-mcp.yaml
|
- mcpcoder.yaml
|
||||||
- argo-mcp.yaml
|
- argo-mcp.yaml
|
||||||
|
|||||||
@@ -17,14 +17,14 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
sources:
|
sources:
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git
|
- repoURL: https://github.com/snothub/forte-helm
|
||||||
path: forteapp
|
path: forteapp
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
helm:
|
helm:
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/mcp10x/values.yaml
|
- $values/mcp10x/values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git
|
- repoURL: git@github.com:fortedigital/helm-values.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
apiVersion: argoproj.io/v1alpha1
|
||||||
kind: Application
|
kind: Application
|
||||||
metadata:
|
metadata:
|
||||||
name: ts-mcp
|
name: mcpcoder
|
||||||
namespace: argocd
|
namespace: argocd
|
||||||
annotations:
|
annotations:
|
||||||
argocd.argoproj.io/sync-wave: "11"
|
argocd.argoproj.io/sync-wave: "1"
|
||||||
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: ""
|
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: ""
|
||||||
notifications.argoproj.io/subscribe.on-sync-failed.slack: ""
|
notifications.argoproj.io/subscribe.on-sync-failed.slack: ""
|
||||||
notifications.argoproj.io/subscribe.on-degraded.slack: ""
|
notifications.argoproj.io/subscribe.on-degraded.slack: ""
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: ts-mcp
|
app.kubernetes.io/name: mcpcoder
|
||||||
app.kubernetes.io/part-of: apps
|
app.kubernetes.io/part-of: apps
|
||||||
app.kubernetes.io/managed-by: argocd
|
app.kubernetes.io/managed-by: argocd
|
||||||
finalizers:
|
finalizers:
|
||||||
@@ -17,21 +17,20 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
sources:
|
sources:
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git
|
- repoURL: https://github.com/snothub/forte-helm
|
||||||
path: forteapp
|
path: forteapp
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
helm:
|
helm:
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/ts-mcp/values.yaml
|
- $values/mcpcoder/values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git
|
- repoURL: git@github.com:fortedigital/helm-values.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
namespace: ts-mcp
|
namespace: mcpcoder
|
||||||
|
|
||||||
syncPolicy:
|
syncPolicy:
|
||||||
automated:
|
automated:
|
||||||
prune: true
|
prune: true
|
||||||
@@ -17,14 +17,14 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
sources:
|
sources:
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git
|
- repoURL: https://github.com/snothub/forte-helm
|
||||||
path: forteapp
|
path: forteapp
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
helm:
|
helm:
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/musicman/values.yaml
|
- $values/musicman/values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git
|
- repoURL: git@github.com:fortedigital/helm-values.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
|
|||||||
@@ -2,3 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
|||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- ../../base
|
- ../../base
|
||||||
|
|
||||||
|
# No patches needed — base already has "eu" paths
|
||||||
|
# EU is the default/base cluster
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -4,11 +4,11 @@ resources:
|
|||||||
- ../../base
|
- ../../base
|
||||||
|
|
||||||
patches:
|
patches:
|
||||||
# dot-ai-stack: swap upc-dev → upc-prod
|
# dot-ai-stack: swap eu → us
|
||||||
- target:
|
- target:
|
||||||
kind: Application
|
kind: Application
|
||||||
name: dot-ai-stack
|
name: dot-ai-stack
|
||||||
patch: |
|
patch: |
|
||||||
- op: replace
|
- op: replace
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
path: /spec/sources/0/helm/valueFiles/1
|
||||||
value: $values/infra/values/upc-prod/dot-ai-stack-values.yaml
|
value: $values/infra/values/us/dot-ai-stack-values.yaml
|
||||||
20
bootstrap.sh
20
bootstrap.sh
@@ -1,9 +1,8 @@
|
|||||||
#!/bin/zsh
|
#!/bin/zsh
|
||||||
|
|
||||||
# in case of $'\r': command not found error, run command below first
|
# in case of $'\r': command not found error, run command below first
|
||||||
# sed -i 's/\r$//' ./bootstrap.sh
|
# sed -i 's/\r$//' ./bootstrap.sh
|
||||||
|
|
||||||
CLUSTER="${1:?Usage: ./bootstrap.sh <cluster> (upc-dev|upc-prod|aks-dev|aks-prod|eks-dev|eks-prod|gke-dev|gke-prod)}"
|
CLUSTER="${1:?Usage: ./bootstrap.sh <cluster> (eu|us)}"
|
||||||
|
|
||||||
echo "running $0 for cluster: ${CLUSTER}..."
|
echo "running $0 for cluster: ${CLUSTER}..."
|
||||||
|
|
||||||
@@ -18,18 +17,18 @@ echo "Bootstrapping cluster: ${clusterName} (${CLUSTER})..."
|
|||||||
Bootstrap()
|
Bootstrap()
|
||||||
{
|
{
|
||||||
ArgoCd
|
ArgoCd
|
||||||
# Gitea
|
Github
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# Gitea #
|
# Github #
|
||||||
############################################################
|
############################################################
|
||||||
Gitea()
|
Github()
|
||||||
{
|
{
|
||||||
echo "Installing secret..."
|
echo "Installing secret..."
|
||||||
kubectl apply -f "private/${CLUSTER}/gitea-repo-main.yaml"
|
kubectl apply -f private/github-${CLUSTER}.yaml
|
||||||
kubectl apply -f "private/${CLUSTER}/main.key"
|
kubectl apply -f private/main-${CLUSTER}.key
|
||||||
}
|
}
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
@@ -37,15 +36,10 @@ Gitea()
|
|||||||
############################################################
|
############################################################
|
||||||
ArgoCd()
|
ArgoCd()
|
||||||
{
|
{
|
||||||
# Pre-create ConfigMap for repo-server env (must exist before Helm upgrade)
|
|
||||||
kubectl create namespace argocd --dry-run=client -o yaml | kubectl apply -f -
|
|
||||||
kubectl apply -f cluster-resources/argocd-repo-server-config.yaml
|
|
||||||
|
|
||||||
# install argocd
|
# install argocd
|
||||||
echo "Installing ArgoCD..."
|
echo "Installing ArgoCD..."
|
||||||
helm upgrade --install argocd argo-cd \
|
helm upgrade --install argocd argo-cd \
|
||||||
--repo https://argoproj.github.io/argo-helm \
|
--repo https://argoproj.github.io/argo-helm \
|
||||||
--version "7.8.0" \
|
|
||||||
--namespace argocd --create-namespace \
|
--namespace argocd --create-namespace \
|
||||||
--values infra/values/base/argocd-values.yaml \
|
--values infra/values/base/argocd-values.yaml \
|
||||||
--values "infra/values/${CLUSTER}/argocd-values.yaml" \
|
--values "infra/values/${CLUSTER}/argocd-values.yaml" \
|
||||||
@@ -55,4 +49,4 @@ ArgoCd()
|
|||||||
kubectl apply -f "_app-of-apps-${CLUSTER}.yaml" -n argocd
|
kubectl apply -f "_app-of-apps-${CLUSTER}.yaml" -n argocd
|
||||||
}
|
}
|
||||||
|
|
||||||
Bootstrap
|
# Bootstrap
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
# CronJob: syncs OIDC client secret from registrar-managed
|
|
||||||
# argocd-oidc-credentials into argocd-secret (oidc.clientSecret key).
|
|
||||||
# Runs every 2 min. No-ops if source secret doesn't exist yet
|
|
||||||
# (safe for fresh deploys before Keycloak is up).
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: argocd-oidc-sync
|
|
||||||
namespace: argocd
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: Role
|
|
||||||
metadata:
|
|
||||||
name: argocd-oidc-sync
|
|
||||||
namespace: argocd
|
|
||||||
rules:
|
|
||||||
- apiGroups: [""]
|
|
||||||
resources: ["secrets"]
|
|
||||||
resourceNames: ["argocd-oidc-credentials", "argocd-secret"]
|
|
||||||
verbs: ["get", "patch"]
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: RoleBinding
|
|
||||||
metadata:
|
|
||||||
name: argocd-oidc-sync
|
|
||||||
namespace: argocd
|
|
||||||
roleRef:
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: Role
|
|
||||||
name: argocd-oidc-sync
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: argocd-oidc-sync
|
|
||||||
namespace: argocd
|
|
||||||
---
|
|
||||||
apiVersion: batch/v1
|
|
||||||
kind: CronJob
|
|
||||||
metadata:
|
|
||||||
name: argocd-oidc-sync
|
|
||||||
namespace: argocd
|
|
||||||
spec:
|
|
||||||
schedule: "*/2 * * * *"
|
|
||||||
concurrencyPolicy: Forbid
|
|
||||||
successfulJobsHistoryLimit: 1
|
|
||||||
failedJobsHistoryLimit: 3
|
|
||||||
jobTemplate:
|
|
||||||
spec:
|
|
||||||
backoffLimit: 1
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
serviceAccountName: argocd-oidc-sync
|
|
||||||
restartPolicy: Never
|
|
||||||
containers:
|
|
||||||
- name: sync
|
|
||||||
image: bitnami/kubectl:latest
|
|
||||||
command: ["/bin/sh", "-c"]
|
|
||||||
args:
|
|
||||||
- |
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Exit gracefully if source secret doesn't exist yet
|
|
||||||
if ! kubectl get secret argocd-oidc-credentials -n argocd >/dev/null 2>&1; then
|
|
||||||
echo "argocd-oidc-credentials not found — skipping (Keycloak not ready yet)"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Read current OIDC client secret
|
|
||||||
NEW_SECRET=$(kubectl get secret argocd-oidc-credentials -n argocd \
|
|
||||||
-o jsonpath='{.data.client-secret}' | base64 -d)
|
|
||||||
|
|
||||||
# Read current value in argocd-secret (if any)
|
|
||||||
CURRENT=$(kubectl get secret argocd-secret -n argocd \
|
|
||||||
-o jsonpath='{.data.oidc\.clientSecret}' 2>/dev/null | base64 -d || echo "")
|
|
||||||
|
|
||||||
# Only patch if changed
|
|
||||||
if [ "$NEW_SECRET" = "$CURRENT" ]; then
|
|
||||||
echo "oidc.clientSecret already up to date"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
kubectl patch secret argocd-secret -n argocd --type merge \
|
|
||||||
-p "{\"stringData\":{\"oidc.clientSecret\":\"${NEW_SECRET}\"}}"
|
|
||||||
echo "Patched argocd-secret with oidc.clientSecret"
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: argocd-repo-server-config
|
|
||||||
namespace: argocd
|
|
||||||
data:
|
|
||||||
# Disable git submodule checkout - submodules (e.g. shared-prompts)
|
|
||||||
# are not needed for K8s manifest generation
|
|
||||||
ARGOCD_GIT_MODULES_ENABLED: "false"
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
apiVersion: batch/v1
|
|
||||||
kind: CronJob
|
|
||||||
metadata:
|
|
||||||
name: gitea-backup
|
|
||||||
namespace: gitea
|
|
||||||
spec:
|
|
||||||
schedule: "0 3 * * *" # daily at 03:00 UTC
|
|
||||||
concurrencyPolicy: Forbid
|
|
||||||
successfulJobsHistoryLimit: 3
|
|
||||||
failedJobsHistoryLimit: 3
|
|
||||||
jobTemplate:
|
|
||||||
spec:
|
|
||||||
backoffLimit: 1
|
|
||||||
activeDeadlineSeconds: 1800
|
|
||||||
template:
|
|
||||||
spec:
|
|
||||||
restartPolicy: Never
|
|
||||||
securityContext:
|
|
||||||
runAsUser: 1000
|
|
||||||
runAsGroup: 1000
|
|
||||||
fsGroup: 1000
|
|
||||||
# Must run on the same node as Gitea to share the RWO volume
|
|
||||||
affinity:
|
|
||||||
podAffinity:
|
|
||||||
requiredDuringSchedulingIgnoredDuringExecution:
|
|
||||||
- labelSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: gitea
|
|
||||||
topologyKey: kubernetes.io/hostname
|
|
||||||
initContainers:
|
|
||||||
- name: gitea-dump
|
|
||||||
image: gitea/gitea:1.25.4
|
|
||||||
command:
|
|
||||||
- sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
gitea dump \
|
|
||||||
-c /data/gitea/conf/app.ini \
|
|
||||||
-f /backup/gitea-dump.zip \
|
|
||||||
-t /tmp/gitea-dump && \
|
|
||||||
echo "Dump completed: $(ls -lh /backup/gitea-dump.zip)"
|
|
||||||
volumeMounts:
|
|
||||||
- name: data
|
|
||||||
mountPath: /data
|
|
||||||
readOnly: true
|
|
||||||
- name: backup
|
|
||||||
mountPath: /backup
|
|
||||||
- name: tmp
|
|
||||||
mountPath: /tmp/gitea-dump
|
|
||||||
containers:
|
|
||||||
- name: upload
|
|
||||||
image: minio/mc:latest
|
|
||||||
env:
|
|
||||||
- name: HOME
|
|
||||||
value: /tmp
|
|
||||||
command:
|
|
||||||
- sh
|
|
||||||
- -c
|
|
||||||
- |
|
|
||||||
mc alias set s3 "${S3_ENDPOINT}" "${AWS_ACCESS_KEY_ID}" "${AWS_SECRET_ACCESS_KEY}"
|
|
||||||
|
|
||||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
||||||
KEY="gitea-dump-${TIMESTAMP}.zip"
|
|
||||||
echo "Uploading ${KEY}..."
|
|
||||||
mc cp /backup/gitea-dump.zip "s3/${S3_BUCKET}/${KEY}" && \
|
|
||||||
echo "Upload complete."
|
|
||||||
|
|
||||||
# Prune backups older than 7 days
|
|
||||||
echo "Pruning backups older than 7 days..."
|
|
||||||
mc rm --older-than 7d --force "s3/${S3_BUCKET}/" 2>&1 || true
|
|
||||||
echo "Pruning complete."
|
|
||||||
envFrom:
|
|
||||||
- secretRef:
|
|
||||||
name: gitea-backup-s3
|
|
||||||
volumeMounts:
|
|
||||||
- name: backup
|
|
||||||
mountPath: /backup
|
|
||||||
readOnly: true
|
|
||||||
volumes:
|
|
||||||
- name: data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: gitea-shared-storage
|
|
||||||
- name: backup
|
|
||||||
emptyDir:
|
|
||||||
sizeLimit: 5Gi
|
|
||||||
- name: tmp
|
|
||||||
emptyDir:
|
|
||||||
sizeLimit: 5Gi
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
apiVersion: traefik.io/v1alpha1
|
|
||||||
kind: IngressRouteTCP
|
|
||||||
metadata:
|
|
||||||
name: gitea-ssh
|
|
||||||
namespace: gitea
|
|
||||||
spec:
|
|
||||||
entryPoints:
|
|
||||||
- giteassh
|
|
||||||
routes:
|
|
||||||
- match: HostSNI(`*`)
|
|
||||||
services:
|
|
||||||
- name: gitea-ssh
|
|
||||||
port: 22
|
|
||||||
@@ -10,7 +10,7 @@ metadata:
|
|||||||
policies.kyverno.io/severity: medium
|
policies.kyverno.io/severity: medium
|
||||||
policies.kyverno.io/subject: Pod
|
policies.kyverno.io/subject: Pod
|
||||||
policies.kyverno.io/description: >-
|
policies.kyverno.io/description: >-
|
||||||
Injects an auth sidecar container into Pods annotated with policies.forteapps.io/auth: "true". Supports three auth modes controlled by the policies.forteapps.io/auth-type annotation: "token" (default), "oidc", and "mcp". In token mode the sidecar reads credentials from a mounted Secret volume. In OIDC mode the sidecar uses OpenID Connect with authority and client-id provided via required annotations (policies.forteapps.io/auth-oidc-authority and policies.forteapps.io/auth-oidc-client-id) and secrets from an auth-oidc Secret. In MCP mode the sidecar implements OAuth 2.0 for MCP servers per RFC 9728 (Protected Resource Metadata) and RFC 7591 (Dynamic Client Registration), configured via policies.forteapps.io/auth-mcp-resource and policies.forteapps.io/auth-mcp-authority annotations. The sidecar port defaults to 9001 and can be overridden via the policies.forteapps.io/auth-port annotation. A NetworkPolicy is generated to restrict ingress to the sidecar port only.
|
Injects an auth sidecar container into Pods annotated with policies.forteapps.io/auth: "true". Supports three auth modes controlled by the policies.forteapps.io/auth-type annotation: "token" (default), "oidc", and "mcp". In token mode the sidecar reads credentials from a mounted Secret volume. In OIDC mode the sidecar uses OpenID Connect with authority and client-id provided via required annotations (policies.forteapps.io/auth-oidc-authority and policies.forteapps.io/auth-oidc-client-id) and secrets from an auth-oidc Secret. In MCP mode the sidecar implements OAuth 2.0 for MCP servers per RFC 9728 (Protected Resource Metadata) and RFC 7591 (Dynamic Client Registration), configured via policies.forteapps.io/auth-mcp-resource and policies.forteapps.io/auth-mcp-authority annotations. A NetworkPolicy is generated to restrict ingress to the sidecar port only.
|
||||||
spec:
|
spec:
|
||||||
background: false
|
background: false
|
||||||
rules:
|
rules:
|
||||||
@@ -119,26 +119,21 @@ spec:
|
|||||||
- name: appPort
|
- name: appPort
|
||||||
variable:
|
variable:
|
||||||
jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000`
|
jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000`
|
||||||
- name: sidecarPort
|
|
||||||
variable:
|
|
||||||
jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '9001')
|
|
||||||
mutate:
|
mutate:
|
||||||
patchStrategicMerge:
|
patchStrategicMerge:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: authn
|
- name: authn
|
||||||
image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'git.forteapps.net/forte/auth-sidecar' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}"
|
image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'ghcr.io/snothub/stunning-memory' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}"
|
||||||
ports:
|
ports:
|
||||||
- containerPort: "{{ sidecarPort }}"
|
- containerPort: 8080
|
||||||
name: auth
|
name: auth
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
env:
|
env:
|
||||||
- name: AUTH_LISTEN_ADDR
|
- name: AUTH_LISTEN_ADDR
|
||||||
value: ":{{ sidecarPort }}"
|
value: ":8080"
|
||||||
- name: AUTH_UPSTREAM_URL
|
- name: AUTH_UPSTREAM_URL
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-upstream-url\" || join('', ['http://localhost:', to_string(appPort)]) }}"
|
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-upstream-url\" || join('', ['http://localhost:', to_string(appPort)]) }}"
|
||||||
- name: AUTH_PUBLIC_PATHS
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-public-paths\" || '/healthz' }}"
|
|
||||||
- name: AUTH_TOKEN_FILE
|
- name: AUTH_TOKEN_FILE
|
||||||
value: "/etc/auth/tokens"
|
value: "/etc/auth/tokens"
|
||||||
- name: AUTH_MODE
|
- name: AUTH_MODE
|
||||||
@@ -157,13 +152,13 @@ spec:
|
|||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: "{{ sidecarPort }}"
|
port: 8080
|
||||||
initialDelaySeconds: 2
|
initialDelaySeconds: 2
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: "{{ sidecarPort }}"
|
port: 8080
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
securityContext:
|
securityContext:
|
||||||
@@ -200,25 +195,22 @@ spec:
|
|||||||
- name: appPort
|
- name: appPort
|
||||||
variable:
|
variable:
|
||||||
jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000`
|
jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000`
|
||||||
- name: sidecarPort
|
|
||||||
variable:
|
|
||||||
jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '9001')
|
|
||||||
mutate:
|
mutate:
|
||||||
patchStrategicMerge:
|
patchStrategicMerge:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: authn
|
- name: authn
|
||||||
image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'git.forteapps.net/forte/auth-sidecar' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}"
|
image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'ghcr.io/snothub/stunning-memory' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}"
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
- containerPort: "{{ sidecarPort }}"
|
- containerPort: 8080
|
||||||
name: auth
|
name: auth
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
env:
|
env:
|
||||||
- name: AUTH_MODE
|
- name: AUTH_MODE
|
||||||
value: "oidc"
|
value: "oidc"
|
||||||
- name: AUTH_LISTEN_ADDR
|
- name: AUTH_LISTEN_ADDR
|
||||||
value: ":{{ sidecarPort }}"
|
value: ":8080"
|
||||||
- name: AUTH_LOG_LEVEL
|
- name: AUTH_LOG_LEVEL
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-log-level\" || 'info' }}"
|
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-log-level\" || 'info' }}"
|
||||||
- name: AUTH_UPSTREAM_URL
|
- name: AUTH_UPSTREAM_URL
|
||||||
@@ -233,8 +225,6 @@ spec:
|
|||||||
value: "{{ regex_replace_all('https?://[^/]*', request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-callback-path\", '') }}"
|
value: "{{ regex_replace_all('https?://[^/]*', request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-callback-path\", '') }}"
|
||||||
- name: AUTH_OIDC_SCOPES
|
- name: AUTH_OIDC_SCOPES
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-scopes\" || 'openid,profile,email' }}"
|
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-scopes\" || 'openid,profile,email' }}"
|
||||||
- name: AUTH_PUBLIC_PATHS
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-public-paths\" || '/healthz' }}"
|
|
||||||
- name: AUTH_OIDC_COOKIE_SECRET
|
- name: AUTH_OIDC_COOKIE_SECRET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
@@ -243,8 +233,8 @@ spec:
|
|||||||
- name: AUTH_OIDC_CLIENT_SECRET
|
- name: AUTH_OIDC_CLIENT_SECRET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-credentials-secret\" || 'auth-oidc' }}"
|
name: auth-oidc
|
||||||
key: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-credentials-secret-key\" || 'client-secret' }}"
|
key: client-secret
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
cpu: 50m
|
cpu: 50m
|
||||||
@@ -255,13 +245,13 @@ spec:
|
|||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: "{{ sidecarPort }}"
|
port: 8080
|
||||||
initialDelaySeconds: 2
|
initialDelaySeconds: 2
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: "{{ sidecarPort }}"
|
port: 8080
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
securityContext:
|
securityContext:
|
||||||
@@ -293,25 +283,22 @@ spec:
|
|||||||
- name: appPort
|
- name: appPort
|
||||||
variable:
|
variable:
|
||||||
jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000`
|
jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000`
|
||||||
- name: sidecarPort
|
|
||||||
variable:
|
|
||||||
jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '9001')
|
|
||||||
mutate:
|
mutate:
|
||||||
patchStrategicMerge:
|
patchStrategicMerge:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: authn
|
- name: authn
|
||||||
image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'git.forteapps.net/forte/auth-sidecar' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}"
|
image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'ghcr.io/snothub/stunning-memory' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}"
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
- containerPort: "{{ sidecarPort }}"
|
- containerPort: 8080
|
||||||
name: auth
|
name: auth
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
env:
|
env:
|
||||||
- name: AUTH_MODE
|
- name: AUTH_MODE
|
||||||
value: "mcp"
|
value: "mcp"
|
||||||
- name: AUTH_LISTEN_ADDR
|
- name: AUTH_LISTEN_ADDR
|
||||||
value: ":{{ sidecarPort }}"
|
value: ":8080"
|
||||||
- name: AUTH_LOG_LEVEL
|
- name: AUTH_LOG_LEVEL
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-log-level\" || 'info' }}"
|
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-log-level\" || 'info' }}"
|
||||||
- name: AUTH_UPSTREAM_URL
|
- name: AUTH_UPSTREAM_URL
|
||||||
@@ -320,10 +307,8 @@ spec:
|
|||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-resource\" }}"
|
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-resource\" }}"
|
||||||
- name: AUTH_MCP_AUTHORIZATION_SERVERS
|
- name: AUTH_MCP_AUTHORIZATION_SERVERS
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-authority\" }}"
|
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-authority\" }}"
|
||||||
- name: AUTH_PUBLIC_PATHS
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-public-paths\" || '/healthz' }}"
|
|
||||||
- name: AUTH_MCP_SCOPES_SUPPORTED
|
- name: AUTH_MCP_SCOPES_SUPPORTED
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-scopes\" || 'profile' }}"
|
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-scopes\" || 'read,write' }}"
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
cpu: 50m
|
cpu: 50m
|
||||||
@@ -334,106 +319,13 @@ spec:
|
|||||||
readinessProbe:
|
readinessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: "{{ sidecarPort }}"
|
port: 8080
|
||||||
initialDelaySeconds: 2
|
initialDelaySeconds: 2
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: /healthz
|
path: /healthz
|
||||||
port: "{{ sidecarPort }}"
|
port: 8080
|
||||||
initialDelaySeconds: 5
|
|
||||||
periodSeconds: 10
|
|
||||||
securityContext:
|
|
||||||
allowPrivilegeEscalation: false
|
|
||||||
readOnlyRootFilesystem: true
|
|
||||||
capabilities:
|
|
||||||
drop:
|
|
||||||
- ALL
|
|
||||||
- name: inject-sidecar-oauth
|
|
||||||
skipBackgroundRequests: true
|
|
||||||
match:
|
|
||||||
any:
|
|
||||||
- resources:
|
|
||||||
kinds:
|
|
||||||
- Pod
|
|
||||||
annotations:
|
|
||||||
policies.forteapps.io/auth: "true"
|
|
||||||
policies.forteapps.io/auth-type: "oauth"
|
|
||||||
exclude:
|
|
||||||
any:
|
|
||||||
- resources:
|
|
||||||
namespaces:
|
|
||||||
- kube-system
|
|
||||||
- kyverno
|
|
||||||
- argocd
|
|
||||||
- cert-manager
|
|
||||||
- monitoring
|
|
||||||
context:
|
|
||||||
- name: appPort
|
|
||||||
variable:
|
|
||||||
jmesPath: request.object.spec.containers[?name != 'authn'] | [0].ports[0].containerPort || `3000`
|
|
||||||
- name: sidecarPort
|
|
||||||
variable:
|
|
||||||
jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '9001')
|
|
||||||
mutate:
|
|
||||||
patchStrategicMerge:
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: authn
|
|
||||||
image: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image\" || 'git.forteapps.net/forte/auth-sidecar' }}:{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-image-version\" || 'latest' }}"
|
|
||||||
imagePullPolicy: Always
|
|
||||||
ports:
|
|
||||||
- containerPort: "{{ sidecarPort }}"
|
|
||||||
name: auth
|
|
||||||
protocol: TCP
|
|
||||||
env:
|
|
||||||
- name: AUTH_MODE
|
|
||||||
value: "oauth"
|
|
||||||
- name: AUTH_LISTEN_ADDR
|
|
||||||
value: ":{{ sidecarPort }}"
|
|
||||||
- name: AUTH_LOG_LEVEL
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-log-level\" || 'info' }}"
|
|
||||||
- name: AUTH_UPSTREAM_URL
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-upstream-url\" || join('', ['http://localhost:', to_string(appPort)]) }}"
|
|
||||||
- name: AUTH_OAUTH_AUTHORITY
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-authority\" }}"
|
|
||||||
- name: AUTH_OAUTH_CLIENT_ID
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-client-id\" }}"
|
|
||||||
- name: AUTH_OAUTH_SCOPES
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-scopes\" || 'openid,profile,email' }}"
|
|
||||||
- name: AUTH_OAUTH_DELEGATION_ENABLED
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-delegation-enabled\" || 'false' }}"
|
|
||||||
- name: AUTH_OAUTH_DELEGATION_CLIENT_ID
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-delegation-client-id\" || '' }}"
|
|
||||||
- name: AUTH_OAUTH_DELEGATION_SCOPES
|
|
||||||
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-delegation-scopes\" || '' }}"
|
|
||||||
- name: AUTH_OAUTH_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-credentials-secret\" || 'auth-oauth' }}"
|
|
||||||
key: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oauth-credentials-secret-key\" || 'client-secret' }}"
|
|
||||||
- name: AUTH_OAUTH_DELEGATION_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: auth-oauth
|
|
||||||
key: delegation-client-secret
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpu: 50m
|
|
||||||
memory: 64Mi
|
|
||||||
requests:
|
|
||||||
cpu: 10m
|
|
||||||
memory: 32Mi
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /healthz
|
|
||||||
port: "{{ sidecarPort }}"
|
|
||||||
initialDelaySeconds: 2
|
|
||||||
periodSeconds: 5
|
|
||||||
livenessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /healthz
|
|
||||||
port: "{{ sidecarPort }}"
|
|
||||||
initialDelaySeconds: 5
|
initialDelaySeconds: 5
|
||||||
periodSeconds: 10
|
periodSeconds: 10
|
||||||
securityContext:
|
securityContext:
|
||||||
@@ -466,10 +358,6 @@ spec:
|
|||||||
operator: In
|
operator: In
|
||||||
value:
|
value:
|
||||||
- CREATE
|
- CREATE
|
||||||
context:
|
|
||||||
- name: sidecarPort
|
|
||||||
variable:
|
|
||||||
jmesPath: to_number(request.object.metadata.annotations."policies.forteapps.io/auth-port" || '9001')
|
|
||||||
generate:
|
generate:
|
||||||
synchronize: false
|
synchronize: false
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
@@ -488,5 +376,5 @@ spec:
|
|||||||
- Ingress
|
- Ingress
|
||||||
ingress:
|
ingress:
|
||||||
- ports:
|
- ports:
|
||||||
- port: "{{ sidecarPort }}"
|
- port: 8080
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
|||||||
71
cluster-resources/policies/deployment-verifier.yaml
Normal file
71
cluster-resources/policies/deployment-verifier.yaml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
apiVersion: kyverno.io/v1
|
||||||
|
kind: ClusterPolicy
|
||||||
|
metadata:
|
||||||
|
name: require-deployment-owner
|
||||||
|
spec:
|
||||||
|
validationFailureAction: Audit
|
||||||
|
background: false
|
||||||
|
rules:
|
||||||
|
- name: check-pod-owner-is-replicaset-from-deployment
|
||||||
|
skipBackgroundRequests: true
|
||||||
|
match:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
kinds:
|
||||||
|
- Pod
|
||||||
|
exclude:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
namespaces:
|
||||||
|
- kube-system
|
||||||
|
- kyverno
|
||||||
|
- cert-manager
|
||||||
|
- monitoring
|
||||||
|
- argocd
|
||||||
|
- traefik-system
|
||||||
|
context:
|
||||||
|
- name: ownerReplicaSet
|
||||||
|
apiCall:
|
||||||
|
method: GET
|
||||||
|
urlPath: "/apis/apps/v1/namespaces/{{request.namespace}}/replicasets/{{request.object.metadata.ownerReferences[0].name}}"
|
||||||
|
jmesPath: "@"
|
||||||
|
preconditions:
|
||||||
|
all:
|
||||||
|
- key: "{{ request.object.metadata.ownerReferences || `[]` | [?kind=='ReplicaSet'] | length(@) }}"
|
||||||
|
operator: GreaterThanOrEquals
|
||||||
|
value: 1
|
||||||
|
validate:
|
||||||
|
allowExistingViolations: true
|
||||||
|
message: "Pods must be created through a Deployment resource."
|
||||||
|
deny:
|
||||||
|
conditions:
|
||||||
|
any:
|
||||||
|
- key: "{{ownerReplicaSet.metadata.ownerReferences[0].kind}}"
|
||||||
|
operator: NotEquals
|
||||||
|
value: Deployment
|
||||||
|
- name: deny-pods-without-replicaset-owner
|
||||||
|
match:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
kinds:
|
||||||
|
- Pod
|
||||||
|
exclude:
|
||||||
|
any:
|
||||||
|
- resources:
|
||||||
|
namespaces:
|
||||||
|
- kube-system
|
||||||
|
- kyverno
|
||||||
|
- cert-manager
|
||||||
|
- monitoring
|
||||||
|
- argocd
|
||||||
|
- traefik-system
|
||||||
|
skipBackgroundRequests: true
|
||||||
|
validate:
|
||||||
|
allowExistingViolations: true
|
||||||
|
message: "Direct pod creation is not allowed. Pods must come from a Deployment managed by ArgoCD."
|
||||||
|
deny:
|
||||||
|
conditions:
|
||||||
|
all:
|
||||||
|
- key: "{{ request.object.metadata.ownerReferences || `[]` | [?kind=='ReplicaSet'] | length(@) }}"
|
||||||
|
operator: LessThan
|
||||||
|
value: 1
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
apiVersion: kyverno.io/v1
|
|
||||||
kind: ClusterPolicy
|
|
||||||
metadata:
|
|
||||||
name: keycloak-client-config-cloner
|
|
||||||
spec:
|
|
||||||
rules:
|
|
||||||
- name: clone-client-config-to-keycloak
|
|
||||||
skipBackgroundRequests: false
|
|
||||||
match:
|
|
||||||
any:
|
|
||||||
- resources:
|
|
||||||
kinds:
|
|
||||||
- Secret
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
keycloak.forteapps.net/client-config: "true"
|
|
||||||
exclude:
|
|
||||||
any:
|
|
||||||
- resources:
|
|
||||||
namespaces:
|
|
||||||
- keycloak
|
|
||||||
generate:
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
name: "{{request.object.metadata.name}}"
|
|
||||||
namespace: keycloak
|
|
||||||
synchronize: true
|
|
||||||
data:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
keycloak.forteapps.net/client-config: "true"
|
|
||||||
keycloak.forteapps.net/source-namespace: "{{request.object.metadata.namespace}}"
|
|
||||||
annotations:
|
|
||||||
keycloak.forteapps.net/source-name: "{{request.object.metadata.name}}"
|
|
||||||
keycloak.forteapps.net/source-namespace: "{{request.object.metadata.namespace}}"
|
|
||||||
data: "{{request.object.data}}"
|
|
||||||
type: "{{request.object.type}}"
|
|
||||||
@@ -16,6 +16,7 @@ spec:
|
|||||||
- resources:
|
- resources:
|
||||||
namespaces:
|
namespaces:
|
||||||
- kube-system
|
- kube-system
|
||||||
|
- trivy-system
|
||||||
- monitoring
|
- monitoring
|
||||||
- argocd
|
- argocd
|
||||||
- cert-manager
|
- cert-manager
|
||||||
|
|||||||
20
cluster-resources/snothub-repo-credentials-sealed.yaml
Normal file
20
cluster-resources/snothub-repo-credentials-sealed.yaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
apiVersion: bitnami.com/v1alpha1
|
||||||
|
kind: SealedSecret
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
name: snothub-repo-creds
|
||||||
|
namespace: argocd
|
||||||
|
spec:
|
||||||
|
encryptedData:
|
||||||
|
sshPrivateKey: AgCBd+i8jXBDwvWM0YC9OWvNTKyynVpW6hF0F3aH0GXBIYxzFo1g9kMajE/Ce3bTl8DiVs7VGzPdI6lmJtSC3+fVMm4wavDGrUUbhUHSR/HnEqq51NVjxU1Uj+VRz550z9r6gB0VLAcYqN0WLKd/4Qn7tvQOmKsaXMd5jsIYpSB8nZK87awZ5niFmT8DIKu2DOzoDBeN7Yqrf1aELq3m3kaDxGcDMYSglB0xRU3fF8FYr5inCic1eTrCUdfIwnrBSublNxI9FrGnlylRC40XCPxNpy6zyoQt7yeJTNgRSvtfceqXAwh25mcvga+BfDBTLPQ0EDbCoNDnE9EyMUa4kWcXBTFZ1Qx5NGRz8HAjWoYDbuNOonl9ik5dvv9UKKA+/04ZEuPjsbdkBq9xKuowx0zLL1IVv/jeuViFdvNY6LKNu/hLwMn8aMlTLlIkB+1fsIqV7Vkva9Vk7IXNz575lMwIGUTo1dsK9FQ5+uIe2bsRnY8RJ3lpyndZQ1HDPh7P7KZLZPH8fUuAHH1UF74njMQyve79zCRcisBAewpXdq4UsYAYUQOluS1Ak+sFcIdQ0jHjfklGCcJnTvMyO7obIsPQSv39/bsCqQX6uisrTzcb0s7wnbzhcxf1gm7IyZMIhi2Vub3GoLCIMnb6ViO8k+itLUa5eZpoEeg+BpP1mgL7O2nVfrZYgueULgMSvN89ct/THITsFAR8614An3DCHSnZLv5ZmY7yC6rmO405IlrnjvfFqEt9MdqTgR7uTYDSdI7UjIFJp3rchzikF1pSDMu/siKmD/Vi9+S0KqBcSENz7EUppVuYIk0aRBqHZ9Awoe0qPIjp1AMg947FjIYXzkGk3Kz0P85fGwkktyfHNZdIrXvpDAIP2739Mr7Hde+EHpuajrhuBgozfless5PqLFfGWFnt5COW2HrdSrvrY56MXuJUfiV0nq3eEhCju9f/cA41VbxfO0Hj+KMCjxiL+MCgIt1eWD24P7GQHb0D7+JiuwqgcH0ZqXRaKFYSOJ3/U1o9RRX9v5yebNTm8ErQTSnIN5bNRgE+t8CrTulGVcpaL/rW+XW8cO8MXLC/R4eeNwVjoiK4yXSHpSjf5dF9MZ6bS6SSZPMOa2besEzzQIj0c/EkUsp/GmIF1utDbJVI2VZLFGklwRo92OJs3URXNCqbzzgte43Y9dJp2VdWyi3Zx32pXVWaNdOzeifVOASP7ags7Kbdfvoaa5CmTu17iRUHCOv6164/KFeDCfY=
|
||||||
|
type: AgCIKe+SzLHIp+6gjOVbD7wcpZkeg5UwgXabFjtonavkPbzX+txHWN7IH2HbDmd7TdpgGFiqMGFSQC8mOVXnj+Pw1XnI8trH6wavvKjQ1SsRWteB8o7lGe2PcG2h2v5yW2vk5rrmuB0ehogeJez3ynlk508HguRtidzxnKdZyc6SHOh2hbWq1clspJicREsHlz3Rfn7upOSUyFmx+Tilfnjuom5FFYGBNZt0aEjaW5S7fjcYJBTEerjGjTz3vUs2DK3C5ymyCKKasA628fVZ20uIZhmn5NUGGK6bKDusFOQhYjRnxRYwS2fToBHiVfC8wo7bWW8ZwOrbguvedJU2q9pwjKvXy9upw8Ra3EcYzXASqwI6rqUQv6htGqzTYtVkRsdVxaQqITc3FYrhKIroru4Iup4xYtcVojppz0+HQxiv5WtxosVtfXXX4Zj4spr9ThoZgzLe3ebILEIK3wJNmK/AxV80NXq7JOI6gaiLK/fbm7gd9G7oW9VuM50OJELDmR4jU2k1KSx6sD5lB+c+Ajp1iw+iS9ETD/je93+eyBRKGM9fqP0+DpNsJBBlVLuWK/BwidBN186pFi4DlMzo13Wd0zYKQce4NVrf5s4f7T01KeBU+nnvvcuI+rW7tsY3R18zgP9D9uX2E5066qJQdrFov2YuXwovS4hCQDttIfz9YoGUKKQ3PObWUVynhtBptoGbhRw=
|
||||||
|
url: AgCjVnAWNarZplxbAurz++eBiKXIuYUcoleDcsrMdwdflBOOf6ayguCr7CDHzUbuKFnlNKoFvIvjUOqFb1v5Yy4iWju3ajvlo+ncEIezxetYOmQSft3nSeOD3+RCZn4Qf4K2C1D113IyPCo7T2h01KuVLWOItfuWuVwFDpLTvmKfTRAs8eP1L4ohvAcY0/J5mAmi7tXV2hN1r9R2MEuvHOusujtrRqewXHhzBksSb9/wxilruU/BHQAhYKeKHzi7QoIOeXJTnGEYxuTvskwKQhschIOIBPAOLaUbgKkuHuDIf8y1Gv2b8ENu5uNvTb/ZD43jtmx9P4pS+Hwc7OW411TrkRO7XbV7qo/PqYGYpkKYDK4g7ONGnzrsmXbSmip34vXll/jAknY61QQ6D6JbbONw/psX72p+ZFvOedhlKbHRuUnDYXyQgKFWBODLb63RaMYai79qbv8mJcwDLJaPXYXpwLumHeZX91uPPjahxfvOe8VoryTpvHIbxJO85VJJ1q+7uyFy6h7LVLhifYSbb+M55p/e5Ds5gNcgUv6npPUHdbf0yjYbT1tXGjhaqk4Tx93WoOKPQ/j+nPB41akooY0YfL/ZTDzr2iCMByrx/uPQz3JE+m7VrH5BZyYjj9sSASVsULabJGFiSuGpD+u/lcUNJW+WHQetMEU7+wNo/WB3E5iG9J8qPSPFcTiTslEa8cvGPNtNgyCQ2PphlinscQsvcAQ+ALtEd1dWXPhIGbF8udK6Wx6NYt8=
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: null
|
||||||
|
labels:
|
||||||
|
argocd.argoproj.io/secret-type: repository
|
||||||
|
name: snothub-repo-creds
|
||||||
|
namespace: argocd
|
||||||
|
type: Opaque
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Cluster config reference — values must match the corresponding overlay files.
|
|
||||||
# Read by bootstrap.sh at install time; NOT auto-propagated to ArgoCD value files.
|
|
||||||
clusterName: dev-aks # → infra/values/aks-dev/argocd-values.yaml (notifications.context.clusterName)
|
|
||||||
domain: example.com # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
|
|
||||||
argocdDomain: argocd.example.com # → infra/values/aks-dev/argocd-values.yaml (global.domain)
|
|
||||||
grafanaDomain: grafana.example.com # → infra/values/aks-dev/grafana-values.yaml (ingress.hosts)
|
|
||||||
keycloakDomain: id.example.com # → infra/values/aks-dev/keycloak-values.yaml (ingress.hostname)
|
|
||||||
dotaiDomain: kubemcp.example.com # → infra/values/aks-dev/dot-ai-stack-values.yaml (dot-ai.ingress.host) — create if needed
|
|
||||||
dotaiUiDomain: kubemcpui.example.com # → infra/values/aks-dev/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host) — create if needed
|
|
||||||
letsencryptEmail: admin@example.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
|
|
||||||
trustedIPs: "10.0.0.0/8,168.63.129.16/32" # → infra/values/aks-dev/traefik-values.yaml (ports.*.trustedIPs) — VNet CIDR + Azure health probe
|
|
||||||
cloudProvider: azure # → determines overlay directory and cloud-specific LB/storage annotations
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Cluster config reference — values must match the corresponding overlay files.
|
|
||||||
# Read by bootstrap.sh at install time; NOT auto-propagated to ArgoCD value files.
|
|
||||||
clusterName: prod-aks # → infra/values/aks-prod/argocd-values.yaml (notifications.context.clusterName)
|
|
||||||
domain: example.com # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
|
|
||||||
argocdDomain: argocd.example.com # → infra/values/aks-prod/argocd-values.yaml (global.domain)
|
|
||||||
grafanaDomain: grafana.example.com # → infra/values/aks-prod/grafana-values.yaml (ingress.hosts)
|
|
||||||
keycloakDomain: id.example.com # → infra/values/aks-prod/keycloak-values.yaml (ingress.hostname)
|
|
||||||
dotaiDomain: kubemcp.example.com # → infra/values/aks-prod/dot-ai-stack-values.yaml (dot-ai.ingress.host) — create if needed
|
|
||||||
dotaiUiDomain: kubemcpui.example.com # → infra/values/aks-prod/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host) — create if needed
|
|
||||||
letsencryptEmail: admin@example.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
|
|
||||||
trustedIPs: "10.0.0.0/8,168.63.129.16/32" # → infra/values/aks-prod/traefik-values.yaml (ports.*.trustedIPs) — VNet CIDR + Azure health probe
|
|
||||||
cloudProvider: azure # → determines overlay directory and cloud-specific LB/storage annotations
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Cluster config reference — values must match the corresponding overlay files.
|
|
||||||
# Read by bootstrap.sh at install time; NOT auto-propagated to ArgoCD value files.
|
|
||||||
clusterName: dev-eks # → infra/values/eks-dev/argocd-values.yaml (notifications.context.clusterName)
|
|
||||||
domain: example.com # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
|
|
||||||
argocdDomain: argocd.example.com # → infra/values/eks-dev/argocd-values.yaml (global.domain)
|
|
||||||
grafanaDomain: grafana.example.com # → infra/values/eks-dev/grafana-values.yaml (ingress.hosts)
|
|
||||||
keycloakDomain: id.example.com # → infra/values/eks-dev/keycloak-values.yaml (ingress.hostname)
|
|
||||||
dotaiDomain: kubemcp.example.com # → infra/values/eks-dev/dot-ai-stack-values.yaml (dot-ai.ingress.host) — create if needed
|
|
||||||
dotaiUiDomain: kubemcpui.example.com # → infra/values/eks-dev/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host) — create if needed
|
|
||||||
letsencryptEmail: admin@example.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
|
|
||||||
trustedIPs: "10.0.0.0/8" # → infra/values/eks-dev/traefik-values.yaml (ports.*.trustedIPs) — VPC CIDR
|
|
||||||
cloudProvider: eks # → determines overlay directory and cloud-specific LB/storage annotations
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Cluster config reference — values must match the corresponding overlay files.
|
|
||||||
# Read by bootstrap.sh at install time; NOT auto-propagated to ArgoCD value files.
|
|
||||||
clusterName: prod-eks # → infra/values/eks-prod/argocd-values.yaml (notifications.context.clusterName)
|
|
||||||
domain: example.com # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
|
|
||||||
argocdDomain: argocd.example.com # → infra/values/eks-prod/argocd-values.yaml (global.domain)
|
|
||||||
grafanaDomain: grafana.example.com # → infra/values/eks-prod/grafana-values.yaml (ingress.hosts)
|
|
||||||
keycloakDomain: id.example.com # → infra/values/eks-prod/keycloak-values.yaml (ingress.hostname)
|
|
||||||
dotaiDomain: kubemcp.example.com # → infra/values/eks-prod/dot-ai-stack-values.yaml (dot-ai.ingress.host) — create if needed
|
|
||||||
dotaiUiDomain: kubemcpui.example.com # → infra/values/eks-prod/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host) — create if needed
|
|
||||||
letsencryptEmail: admin@example.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
|
|
||||||
trustedIPs: "10.0.0.0/8" # → infra/values/eks-prod/traefik-values.yaml (ports.*.trustedIPs) — VPC CIDR
|
|
||||||
cloudProvider: eks # → determines overlay directory and cloud-specific LB/storage annotations
|
|
||||||
10
clusters/eu.yaml
Normal file
10
clusters/eu.yaml
Normal file
@@ -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
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Cluster config reference — values must match the corresponding overlay files.
|
|
||||||
# Read by bootstrap.sh at install time; NOT auto-propagated to ArgoCD value files.
|
|
||||||
clusterName: dev-gke # → infra/values/gke-dev/argocd-values.yaml (notifications.context.clusterName)
|
|
||||||
domain: example.com # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
|
|
||||||
argocdDomain: argocd.example.com # → infra/values/gke-dev/argocd-values.yaml (global.domain)
|
|
||||||
grafanaDomain: grafana.example.com # → infra/values/gke-dev/grafana-values.yaml (ingress.hosts)
|
|
||||||
keycloakDomain: id.example.com # → infra/values/gke-dev/keycloak-values.yaml (ingress.hostname)
|
|
||||||
dotaiDomain: kubemcp.example.com # → infra/values/gke-dev/dot-ai-stack-values.yaml (dot-ai.ingress.host) — create if needed
|
|
||||||
dotaiUiDomain: kubemcpui.example.com # → infra/values/gke-dev/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host) — create if needed
|
|
||||||
letsencryptEmail: admin@example.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
|
|
||||||
trustedIPs: "10.0.0.0/8,35.191.0.0/16,130.211.0.0/22" # → infra/values/gke-dev/traefik-values.yaml (ports.*.trustedIPs) — subnet + GCP health checks
|
|
||||||
cloudProvider: gke # → determines overlay directory and cloud-specific LB/storage annotations
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Cluster config reference — values must match the corresponding overlay files.
|
|
||||||
# Read by bootstrap.sh at install time; NOT auto-propagated to ArgoCD value files.
|
|
||||||
clusterName: prod-gke # → infra/values/gke-prod/argocd-values.yaml (notifications.context.clusterName)
|
|
||||||
domain: example.com # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
|
|
||||||
argocdDomain: argocd.example.com # → infra/values/gke-prod/argocd-values.yaml (global.domain)
|
|
||||||
grafanaDomain: grafana.example.com # → infra/values/gke-prod/grafana-values.yaml (ingress.hosts)
|
|
||||||
keycloakDomain: id.example.com # → infra/values/gke-prod/keycloak-values.yaml (ingress.hostname)
|
|
||||||
dotaiDomain: kubemcp.example.com # → infra/values/gke-prod/dot-ai-stack-values.yaml (dot-ai.ingress.host) — create if needed
|
|
||||||
dotaiUiDomain: kubemcpui.example.com # → infra/values/gke-prod/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host) — create if needed
|
|
||||||
letsencryptEmail: admin@example.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
|
|
||||||
trustedIPs: "10.0.0.0/8,35.191.0.0/16,130.211.0.0/22" # → infra/values/gke-prod/traefik-values.yaml (ports.*.trustedIPs) — subnet + GCP health checks
|
|
||||||
cloudProvider: gke # → determines overlay directory and cloud-specific LB/storage annotations
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Cluster config reference — values must match the corresponding overlay files.
|
|
||||||
# Read by bootstrap.sh at install time; NOT auto-propagated to ArgoCD value files.
|
|
||||||
clusterName: dev-fd-no-svg1 # → infra/values/upc-dev/argocd-values.yaml (notifications.context.clusterName)
|
|
||||||
domain: forteapps.net # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
|
|
||||||
argocdDomain: argocd.127.0.0.1.nip.io # → infra/values/upc-dev/argocd-values.yaml (global.domain)
|
|
||||||
grafanaDomain: grafana.forteapps.net # → infra/values/upc-dev/grafana-values.yaml (ingress.hosts)
|
|
||||||
keycloakDomain: id.forteapps.net # → infra/values/upc-dev/keycloak-values.yaml (ingress.hostname)
|
|
||||||
dotaiDomain: kubemcp.forteapps.net # → infra/values/upc-dev/dot-ai-stack-values.yaml (dot-ai.ingress.host)
|
|
||||||
dotaiUiDomain: kubemcpui.forteapps.net # → infra/values/upc-dev/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host)
|
|
||||||
letsencryptEmail: danijels@gmail.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
|
|
||||||
trustedIPs: "172.16.1.0/24" # → infra/values/upc-dev/traefik-values.yaml (ports.*.trustedIPs)
|
|
||||||
cloudProvider: upcloud # → determines overlay directory and cloud-specific LB/storage annotations
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Cluster config reference — values must match the corresponding overlay files.
|
|
||||||
# Read by bootstrap.sh at install time; NOT auto-propagated to ArgoCD value files.
|
|
||||||
clusterName: prod-fd-no-svg1 # → infra/values/upc-prod/argocd-values.yaml (notifications.context.clusterName)
|
|
||||||
domain: fortedigital.com # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
|
|
||||||
argocdDomain: argocd.127.0.0.1.nip.io # → infra/values/upc-prod/argocd-values.yaml (global.domain)
|
|
||||||
grafanaDomain: grafana.fortedigital.com # → infra/values/upc-prod/grafana-values.yaml (ingress.hosts)
|
|
||||||
keycloakDomain: id.fortedigital.com # → infra/values/upc-prod/keycloak-values.yaml (ingress.hostname)
|
|
||||||
dotaiDomain: kubemcp.fortedigital.com # → infra/values/upc-prod/dot-ai-stack-values.yaml (dot-ai.ingress.host)
|
|
||||||
dotaiUiDomain: kubemcpui.fortedigital.com # → infra/values/upc-prod/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host)
|
|
||||||
letsencryptEmail: danijel.simeunovic@fortedigital.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
|
|
||||||
trustedIPs: "172.16.1.0/24" # → infra/values/upc-prod/traefik-values.yaml (ports.*.trustedIPs)
|
|
||||||
cloudProvider: upcloud # → determines overlay directory and cloud-specific LB/storage annotations
|
|
||||||
10
clusters/us.yaml
Normal file
10
clusters/us.yaml
Normal file
@@ -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
|
||||||
32
devbox.json
32
devbox.json
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.16.0/.schema/devbox.schema.json",
|
|
||||||
"packages": [
|
|
||||||
"kubectl@1.33.2",
|
|
||||||
"kubernetes-helm@3.18.4",
|
|
||||||
"k9s@0.50.7",
|
|
||||||
"kubeseal@0.30.0",
|
|
||||||
"argocd@2.14.11",
|
|
||||||
"kubecm@0.33.1",
|
|
||||||
"kubectl-tree@0.4.3",
|
|
||||||
"kind@0.29.0",
|
|
||||||
"kustomize@5.7.0",
|
|
||||||
"kyverno@1.14.3",
|
|
||||||
"syft@1.29.0",
|
|
||||||
"grype@0.92.2",
|
|
||||||
"traefik@3.6.7",
|
|
||||||
"claude-code@latest",
|
|
||||||
"go@latest",
|
|
||||||
"dotnet-sdk@latest",
|
|
||||||
"opentofu@1.11.6"
|
|
||||||
],
|
|
||||||
"shell": {
|
|
||||||
"init_hook": [
|
|
||||||
"echo 'Welcome to devbox!' > /dev/null"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"test": [
|
|
||||||
"echo \"Error: no test specified\" && exit 1"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
- [Updating an Existing Application](#updating-an-existing-application)
|
- [Updating an Existing Application](#updating-an-existing-application)
|
||||||
- [Working with Secrets](#working-with-secrets)
|
- [Working with Secrets](#working-with-secrets)
|
||||||
- [Enabling Authentication for Applications](#enabling-authentication-for-applications)
|
- [Enabling Authentication for Applications](#enabling-authentication-for-applications)
|
||||||
- [Adding a New Keycloak Client](#adding-a-new-keycloak-client)
|
|
||||||
- [Troubleshooting](#troubleshooting)
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Best Practices](#best-practices)
|
- [Best Practices](#best-practices)
|
||||||
|
|
||||||
@@ -90,21 +89,21 @@ If you do need cluster access, install:
|
|||||||
|
|
||||||
You'll need read/write access to these repositories:
|
You'll need read/write access to these repositories:
|
||||||
|
|
||||||
1. **launchpad** (Config repo)
|
1. **sturdy-adventure** (Config repo)
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.forteapps.net/Forte/launchpad.git
|
git clone https://github.com/fortedigital/sturdy-adventure.git
|
||||||
cd launchpad
|
cd sturdy-adventure
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **helm-prod-values** (Values repo)
|
2. **helm-values** (Values repo)
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.forteapps.net/Forte/helm-prod-values.git
|
git clone git@github.com:fortedigital/helm-values.git
|
||||||
cd helm-prod-values
|
cd helm-values
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **forte-helm** (Chart repo - read-only for most developers)
|
3. **forte-helm** (Chart repo - read-only for most developers)
|
||||||
```bash
|
```bash
|
||||||
git clone https://git.forteapps.net/Forte/forte-helm.git
|
git clone https://github.com/snothub/forte-helm.git
|
||||||
cd forte-helm
|
cd forte-helm
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -133,9 +132,9 @@ mkdir -p ~/dev/k8s
|
|||||||
cd ~/dev/k8s
|
cd ~/dev/k8s
|
||||||
|
|
||||||
# Clone repositories
|
# Clone repositories
|
||||||
git clone https://git.forteapps.net/Forte/launchpad.git launchpad
|
git clone https://github.com/fortedigital/sturdy-adventure.git launchpad
|
||||||
git clone https://git.forteapps.net/Forte/helm-prod-values helm-prod-values
|
git clone git@github.com:fortedigital/helm-values.git helm-prod-values
|
||||||
git clone https://git.forteapps.net/Forte/forte-helm forte-helm
|
git clone https://github.com/snothub/forte-helm.git forte-helm
|
||||||
|
|
||||||
# Your folder structure:
|
# Your folder structure:
|
||||||
# ~/dev/k8s/
|
# ~/dev/k8s/
|
||||||
@@ -175,13 +174,13 @@ npm run dev
|
|||||||
│ - GitHub Actions builds image │
|
│ - GitHub Actions builds image │
|
||||||
│ - Pushes to container registry (GHCR, Docker Hub) │
|
│ - Pushes to container registry (GHCR, Docker Hub) │
|
||||||
│ - Tags with version (e.g., v2.0.4) │
|
│ - Tags with version (e.g., v2.0.4) │
|
||||||
│ - Updates helm-prod-values repository with new tag │
|
│ - Updates helm-values repository with new tag │
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
│ Step 3: GitOps Sync (Automated) │
|
│ Step 3: GitOps Sync (Automated) │
|
||||||
│ - ArgoCD detects change in helm-prod-values │
|
│ - ArgoCD detects change in helm-values │
|
||||||
│ - Pulls updated configuration │
|
│ - Pulls updated configuration │
|
||||||
│ - Syncs to Kubernetes cluster │
|
│ - Syncs to Kubernetes cluster │
|
||||||
│ - Sends Slack notification on success/failure │
|
│ - Sends Slack notification on success/failure │
|
||||||
@@ -201,8 +200,8 @@ Our setup uses three repositories:
|
|||||||
| Repository | Purpose | Who Edits | How Often |
|
| Repository | Purpose | Who Edits | How Often |
|
||||||
|------------|---------|-----------|-----------|
|
|------------|---------|-----------|-----------|
|
||||||
| **forte-helm** | Helm chart templates (generic, reusable) | Platform engineers | ❌ Rarely |
|
| **forte-helm** | Helm chart templates (generic, reusable) | Platform engineers | ❌ Rarely |
|
||||||
| **helm-prod-values** | Application configuration (image tag, env vars) | Developers / CI pipelines | ✅ Sometimes |
|
| **helm-values** | Application configuration (image tag, env vars) | Developers / CI pipelines | ✅ Sometimes |
|
||||||
| **launchpad** | ArgoCD Applications (what gets deployed) | Platform / DevOps engineers | ✅ Per new app |
|
| **sturdy-adventure** | ArgoCD Applications (what gets deployed) | Platform / DevOps engineers | ✅ Per new app |
|
||||||
|
|
||||||
### Example: Deploying "myapp"
|
### Example: Deploying "myapp"
|
||||||
|
|
||||||
@@ -223,7 +222,7 @@ spec:
|
|||||||
value: {{ .Values.app.port }}
|
value: {{ .Values.app.port }}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Repository: `helm-prod-values` (Your App Config)
|
#### Repository: `helm-values` (Your App Config)
|
||||||
```yaml
|
```yaml
|
||||||
# myapp/values.yaml
|
# myapp/values.yaml
|
||||||
# Your app's specific configuration
|
# Your app's specific configuration
|
||||||
@@ -237,7 +236,7 @@ app:
|
|||||||
value: https://api.example.com
|
value: https://api.example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Repository: `launchpad` (ArgoCD Application)
|
#### Repository: `sturdy-adventure` (ArgoCD Application)
|
||||||
```yaml
|
```yaml
|
||||||
# apps/myapp.yaml
|
# apps/myapp.yaml
|
||||||
# Tells ArgoCD to deploy your app
|
# Tells ArgoCD to deploy your app
|
||||||
@@ -248,13 +247,13 @@ metadata:
|
|||||||
namespace: argocd
|
namespace: argocd
|
||||||
spec:
|
spec:
|
||||||
sources:
|
sources:
|
||||||
- repoURL: https://git.forteapps.net/Forte/forte-helm
|
- repoURL: https://github.com/snothub/forte-helm
|
||||||
path: forteapp
|
path: forteapp
|
||||||
helm:
|
helm:
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/myapp/values.yaml
|
- $values/myapp/values.yaml
|
||||||
|
|
||||||
- repoURL: git@github.com:fortedigital/helm-prod-values.git
|
- repoURL: git@github.com:fortedigital/helm-values.git
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
destination:
|
destination:
|
||||||
@@ -316,10 +315,10 @@ Ensure your app repository has:
|
|||||||
docker build -t ghcr.io/fortedigital/hello-world:${{ steps.version.outputs.VERSION }} .
|
docker build -t ghcr.io/fortedigital/hello-world:${{ steps.version.outputs.VERSION }} .
|
||||||
docker push ghcr.io/fortedigital/hello-world:${{ steps.version.outputs.VERSION }}
|
docker push ghcr.io/fortedigital/hello-world:${{ steps.version.outputs.VERSION }}
|
||||||
|
|
||||||
- name: Update helm-prod-values
|
- name: Update helm-values
|
||||||
run: |
|
run: |
|
||||||
git clone git@github.com:fortedigital/helm-prod-values.git
|
git clone git@github.com:fortedigital/helm-values.git
|
||||||
cd helm-prod-values
|
cd helm-values
|
||||||
mkdir -p hello-world
|
mkdir -p hello-world
|
||||||
cat > hello-world/values.yaml <<EOF
|
cat > hello-world/values.yaml <<EOF
|
||||||
app:
|
app:
|
||||||
@@ -334,7 +333,7 @@ Ensure your app repository has:
|
|||||||
|
|
||||||
### Step 2: Create Helm Values
|
### Step 2: Create Helm Values
|
||||||
|
|
||||||
Create a folder in `helm-prod-values` repository:
|
Create a folder in `helm-values` repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/dev/k8s/helm-prod-values
|
cd ~/dev/k8s/helm-prod-values
|
||||||
@@ -387,7 +386,7 @@ git push
|
|||||||
|
|
||||||
### Step 3: Create ArgoCD Application Manifest
|
### Step 3: Create ArgoCD Application Manifest
|
||||||
|
|
||||||
In the `launchpad` repository, create `apps/hello-world.yaml`:
|
In the `sturdy-adventure` repository, create `apps/hello-world.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: argoproj.io/v1alpha1
|
apiVersion: argoproj.io/v1alpha1
|
||||||
@@ -412,7 +411,7 @@ spec:
|
|||||||
|
|
||||||
sources:
|
sources:
|
||||||
# Source 1: Helm chart templates
|
# Source 1: Helm chart templates
|
||||||
- repoURL: https://git.forteapps.net/Forte/forte-helm
|
- repoURL: https://github.com/snothub/forte-helm
|
||||||
path: forteapp
|
path: forteapp
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
helm:
|
helm:
|
||||||
@@ -420,7 +419,7 @@ spec:
|
|||||||
- $values/hello-world/values.yaml
|
- $values/hello-world/values.yaml
|
||||||
|
|
||||||
# Source 2: Helm values
|
# Source 2: Helm values
|
||||||
- repoURL: git@github.com:fortedigital/helm-prod-values.git
|
- repoURL: git@github.com:fortedigital/helm-values.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
@@ -528,7 +527,7 @@ git push origin main
|
|||||||
2. ✅ Builds new Docker image
|
2. ✅ Builds new Docker image
|
||||||
3. ✅ Tags with new version (e.g., `v20260316-143022`)
|
3. ✅ Tags with new version (e.g., `v20260316-143022`)
|
||||||
4. ✅ Pushes to container registry
|
4. ✅ Pushes to container registry
|
||||||
5. ✅ Updates `helm-prod-values/myapp/values.yaml` with new tag
|
5. ✅ Updates `helm-values/myapp/values.yaml` with new tag
|
||||||
6. ✅ ArgoCD detects change
|
6. ✅ ArgoCD detects change
|
||||||
7. ✅ Syncs new version to cluster
|
7. ✅ Syncs new version to cluster
|
||||||
8. ✅ Sends Slack notification
|
8. ✅ Sends Slack notification
|
||||||
@@ -654,11 +653,21 @@ kubectl create secret generic myapp-credentials \
|
|||||||
|
|
||||||
#### Step 2: Seal the Secret
|
#### Step 2: Seal the Secret
|
||||||
|
|
||||||
|
Get the public certificate (one-time setup):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Fetch public cert from cluster
|
||||||
|
kubeseal --fetch-cert \
|
||||||
|
--controller-name=sealed-secrets-controller \
|
||||||
|
--controller-namespace=kube-system \
|
||||||
|
> pub-cert.pem
|
||||||
|
```
|
||||||
|
|
||||||
Seal your secret:
|
Seal your secret:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubeseal --format=yaml \
|
kubeseal --format=yaml \
|
||||||
--namespace=myapp \
|
--cert=pub-cert.pem \
|
||||||
< private/myapp-credentials.yaml \
|
< private/myapp-credentials.yaml \
|
||||||
> secrets/myapp-credentials-sealed.yaml
|
> secrets/myapp-credentials-sealed.yaml
|
||||||
```
|
```
|
||||||
@@ -673,7 +682,7 @@ git push
|
|||||||
|
|
||||||
#### Step 4: Reference Secret in Application
|
#### Step 4: Reference Secret in Application
|
||||||
|
|
||||||
Update your `helm-prod-values/myapp/values.yaml`:
|
Update your `helm-values/myapp/values.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
app:
|
app:
|
||||||
@@ -701,7 +710,7 @@ kubectl create secret generic myapp-credentials \
|
|||||||
|
|
||||||
# 2. Seal it
|
# 2. Seal it
|
||||||
kubeseal --format=yaml \
|
kubeseal --format=yaml \
|
||||||
--namespace=myapp \
|
--cert=pub-cert.pem \
|
||||||
< private/myapp-credentials.yaml \
|
< private/myapp-credentials.yaml \
|
||||||
> secrets/myapp-credentials-sealed.yaml
|
> secrets/myapp-credentials-sealed.yaml
|
||||||
|
|
||||||
@@ -781,7 +790,7 @@ Three authentication modes are supported:
|
|||||||
#### Step 1: Configure Helm Values
|
#### Step 1: Configure Helm Values
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# In helm-prod-values/myapp/values.yaml
|
# In helm-values/myapp/values.yaml
|
||||||
auth:
|
auth:
|
||||||
enabled: true
|
enabled: true
|
||||||
type: token # Token mode (default)
|
type: token # Token mode (default)
|
||||||
@@ -903,7 +912,7 @@ rm private/myapp-auth-oidc.yaml
|
|||||||
#### Step 3: Configure Helm Values
|
#### Step 3: Configure Helm Values
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# In helm-prod-values/myapp/values.yaml
|
# In helm-values/myapp/values.yaml
|
||||||
auth:
|
auth:
|
||||||
enabled: true
|
enabled: true
|
||||||
type: oidc # OIDC mode
|
type: oidc # OIDC mode
|
||||||
@@ -952,46 +961,6 @@ User sees application (authenticated)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Accessing Authenticated User Information
|
|
||||||
|
|
||||||
The auth sidecar handles all authentication before requests reach your application. Your app never sees unauthenticated traffic — the sidecar returns 401 or redirects to the IdP first.
|
|
||||||
|
|
||||||
After successful authentication, the sidecar forwards the request to your application with user identity injected as HTTP headers:
|
|
||||||
|
|
||||||
| Header | Description | Available in |
|
|
||||||
|--------|-------------|-------------|
|
|
||||||
| `X-Auth-User` | Username or display name | Token, OIDC, MCP |
|
|
||||||
| `X-Auth-Email` | User email address | OIDC |
|
|
||||||
| `X-Auth-Subject` | OIDC `sub` claim (stable user ID) | OIDC, MCP |
|
|
||||||
| `X-Auth-Groups` | Comma-separated group memberships | OIDC (if scope includes `groups`) |
|
|
||||||
| `X-Auth-Token` | The validated access token | All modes |
|
|
||||||
|
|
||||||
**Your application reads these headers — no auth library needed:**
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Express.js example
|
|
||||||
app.get('/profile', (req, res) => {
|
|
||||||
const user = req.headers['x-auth-user'];
|
|
||||||
const email = req.headers['x-auth-email'];
|
|
||||||
res.json({ user, email });
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
# Flask example
|
|
||||||
@app.route('/profile')
|
|
||||||
def profile():
|
|
||||||
user = request.headers.get('X-Auth-User')
|
|
||||||
email = request.headers.get('X-Auth-Email')
|
|
||||||
return jsonify(user=user, email=email)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Why this is safe**: The Kyverno-generated NetworkPolicy restricts ingress to the sidecar port only. Traffic cannot bypass the sidecar to reach the application port directly, so the `X-Auth-*` headers can be trusted unconditionally.
|
|
||||||
|
|
||||||
**Key principle**: Your application is zero-trust-unaware by design. It reads headers and renders UI. All authentication complexity lives in the sidecar and Kyverno policy.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Authentication Configuration Reference
|
### Authentication Configuration Reference
|
||||||
|
|
||||||
#### Helm Values Schema
|
#### Helm Values Schema
|
||||||
@@ -1056,7 +1025,7 @@ policies.forteapps.io/auth-upstream-url: "http://localhost:3000"
|
|||||||
#### Sidecar Configuration
|
#### Sidecar Configuration
|
||||||
|
|
||||||
The auth sidecar container:
|
The auth sidecar container:
|
||||||
- **Image**: `ghcr.io/fortedigital/auth-sidecar:latest`
|
- **Image**: `ghcr.io/snothub/stunning-memory:latest`
|
||||||
- **Port**: 8080
|
- **Port**: 8080
|
||||||
- **Resources**: 10m CPU / 32Mi memory (requests), 50m CPU / 64Mi memory (limits)
|
- **Resources**: 10m CPU / 32Mi memory (requests), 50m CPU / 64Mi memory (limits)
|
||||||
- **Health checks**: `/healthz` endpoint
|
- **Health checks**: `/healthz` endpoint
|
||||||
@@ -1079,7 +1048,7 @@ policies.forteapps.io/auth-image-version: "v1.2.3"
|
|||||||
#### Example 1: Internal API with Token Auth
|
#### Example 1: Internal API with Token Auth
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# helm-prod-values/internal-api/values.yaml
|
# helm-values/internal-api/values.yaml
|
||||||
app:
|
app:
|
||||||
image:
|
image:
|
||||||
repository: ghcr.io/company/internal-api
|
repository: ghcr.io/company/internal-api
|
||||||
@@ -1107,7 +1076,7 @@ curl -H "Authorization: Bearer d4f88f..." \
|
|||||||
#### Example 2: User-Facing App with OIDC
|
#### Example 2: User-Facing App with OIDC
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# helm-prod-values/web-app/values.yaml
|
# helm-values/web-app/values.yaml
|
||||||
app:
|
app:
|
||||||
image:
|
image:
|
||||||
repository: ghcr.io/company/web-app
|
repository: ghcr.io/company/web-app
|
||||||
@@ -1142,7 +1111,7 @@ kubectl create secret generic auth-oidc \
|
|||||||
#### Example 3: MCP Server with OAuth 2.0
|
#### Example 3: MCP Server with OAuth 2.0
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# helm-prod-values/mcp-server/values.yaml
|
# helm-values/mcp-server/values.yaml
|
||||||
app:
|
app:
|
||||||
image:
|
image:
|
||||||
repository: ghcr.io/company/mcp-server
|
repository: ghcr.io/company/mcp-server
|
||||||
@@ -1166,7 +1135,7 @@ The MCP auth mode implements RFC 9728 (OAuth 2.0 Protected Resource Metadata) fo
|
|||||||
#### Example 4: Disabling Authentication
|
#### Example 4: Disabling Authentication
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# helm-prod-values/public-api/values.yaml
|
# helm-values/public-api/values.yaml
|
||||||
auth:
|
auth:
|
||||||
enabled: false # No authentication
|
enabled: false # No authentication
|
||||||
|
|
||||||
@@ -1278,202 +1247,6 @@ kubectl logs -n myapp <pod-name> -c authn
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Adding a New Keycloak Client
|
|
||||||
|
|
||||||
There are two ways to add an OIDC client, depending on your use case:
|
|
||||||
|
|
||||||
| Method | Best for | Who edits the infra repo? |
|
|
||||||
|--------|----------|--------------------------|
|
|
||||||
| **Self-service** (recommended) | New apps that deploy their own resources | App developer — no infra changes needed |
|
|
||||||
| **Legacy (realm JSON)** | Existing clients already defined in forte-realm.json (e.g., Gitea) | Platform engineer |
|
|
||||||
|
|
||||||
Both methods are served by the **Keycloak Client Registrar** CronJob, which runs every 2 minutes.
|
|
||||||
|
|
||||||
### Self-Service OIDC Client Registration
|
|
||||||
|
|
||||||
This is the recommended flow for new applications. Your app deploys a labeled config Secret in its own namespace; the platform handles everything else.
|
|
||||||
|
|
||||||
#### How It Works
|
|
||||||
|
|
||||||
1. You deploy a Secret with label `keycloak.forteapps.net/client-config: "true"` containing a `client.json` definition
|
|
||||||
2. A **Kyverno ClusterPolicy** (`keycloak-client-config-cloner`) clones it to the `keycloak` namespace
|
|
||||||
3. The **Client Registrar CronJob** picks it up within 2 minutes:
|
|
||||||
- Registers (or updates) the client in Keycloak
|
|
||||||
- Fetches the auto-generated client secret
|
|
||||||
- Creates a credential Secret in your app's namespace
|
|
||||||
- Annotates the config Secret with sync status
|
|
||||||
|
|
||||||
#### Step 1: Create the Config Secret
|
|
||||||
|
|
||||||
Deploy this Secret in your application's namespace (e.g., as part of your Helm chart or Kustomize overlay):
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Secret
|
|
||||||
metadata:
|
|
||||||
name: keycloak-client-myapp
|
|
||||||
namespace: myapp
|
|
||||||
labels:
|
|
||||||
keycloak.forteapps.net/client-config: "true"
|
|
||||||
stringData:
|
|
||||||
client.json: |
|
|
||||||
{
|
|
||||||
"clientId": "myapp",
|
|
||||||
"name": "My Application",
|
|
||||||
"redirectUris": ["https://myapp.forteapps.net/*"],
|
|
||||||
"webOrigins": ["https://myapp.forteapps.net"],
|
|
||||||
"defaultClientScopes": ["openid", "email", "profile"],
|
|
||||||
"protocolMappers": [],
|
|
||||||
"secret": {
|
|
||||||
"namespace": "myapp",
|
|
||||||
"name": "myapp-oidc-credentials",
|
|
||||||
"keys": { "clientId": "client-id", "clientSecret": "client-secret" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**`client.json` fields**:
|
|
||||||
|
|
||||||
| Field | Required | Description |
|
|
||||||
|-------|----------|-------------|
|
|
||||||
| `clientId` | Yes | Keycloak client ID |
|
|
||||||
| `name` | Yes | Display name in Keycloak |
|
|
||||||
| `redirectUris` | Yes | Allowed redirect URIs |
|
|
||||||
| `webOrigins` | Yes | Allowed web origins (CORS) |
|
|
||||||
| `defaultClientScopes` | No | Scopes (default: `["openid", "email", "profile"]`) |
|
|
||||||
| `protocolMappers` | No | Custom claim mappers (default: `[]`) |
|
|
||||||
| `secret.namespace` | No | Namespace for the credential Secret (default: source namespace) |
|
|
||||||
| `secret.name` | No | Name of the credential Secret (default: `<clientId>-oidc-credentials`) |
|
|
||||||
| `secret.keys.clientId` | No | Key name for client ID in credential Secret (default: `client-id`) |
|
|
||||||
| `secret.keys.clientSecret` | No | Key name for client secret in credential Secret (default: `client-secret`) |
|
|
||||||
|
|
||||||
#### Step 2: Reference the Credential Secret
|
|
||||||
|
|
||||||
In your application's deployment config, reference the credential Secret that the registrar creates:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
env:
|
|
||||||
- name: OIDC_CLIENT_ID
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: myapp-oidc-credentials
|
|
||||||
key: client-id
|
|
||||||
- name: OIDC_CLIENT_SECRET
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: myapp-oidc-credentials
|
|
||||||
key: client-secret
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 3: Deploy and Wait
|
|
||||||
|
|
||||||
Commit and push your changes. The credential Secret will appear within 2 minutes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Watch for the credential Secret to be created
|
|
||||||
kubectl get secret myapp-oidc-credentials -n myapp -w
|
|
||||||
|
|
||||||
# Check registrar logs
|
|
||||||
kubectl logs -n keycloak job/$(kubectl get jobs -n keycloak --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[-1].metadata.name}')
|
|
||||||
|
|
||||||
# Check sync status on the config Secret
|
|
||||||
kubectl get secret keycloak-client-myapp -n keycloak -o jsonpath='{.metadata.annotations}'
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Change Detection
|
|
||||||
|
|
||||||
The registrar computes a SHA-256 hash of `client.json` and stores it as an annotation. On subsequent runs, it skips processing if:
|
|
||||||
- The hash hasn't changed, AND
|
|
||||||
- The credential Secret already exists in the target namespace
|
|
||||||
|
|
||||||
To force a re-sync, update any field in `client.json` (e.g., add a trailing space to `name`).
|
|
||||||
|
|
||||||
### Legacy Method: Realm JSON
|
|
||||||
|
|
||||||
Existing clients (like Gitea) are defined directly in `forte-realm.json` inside `keycloak-values.yaml`. The registrar syncs their secrets via client attributes.
|
|
||||||
|
|
||||||
#### Step 1: Add Client to Realm Config
|
|
||||||
|
|
||||||
In `infra/values/base/keycloak-values.yaml`, add a new entry to the `clients` array in `forte-realm.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"clientId": "myapp",
|
|
||||||
"name": "My Application",
|
|
||||||
"enabled": true,
|
|
||||||
"protocol": "openid-connect",
|
|
||||||
"clientAuthenticatorType": "client-secret",
|
|
||||||
"standardFlowEnabled": true,
|
|
||||||
"directAccessGrantsEnabled": false,
|
|
||||||
"publicClient": false,
|
|
||||||
"redirectUris": ["https://myapp.forteapps.net/*"],
|
|
||||||
"webOrigins": ["https://myapp.forteapps.net"],
|
|
||||||
"defaultClientScopes": ["openid", "email", "profile"],
|
|
||||||
"attributes": {
|
|
||||||
"k8s.secret.sync": "true",
|
|
||||||
"k8s.secret.namespace": "myapp",
|
|
||||||
"k8s.secret.name": "myapp-oidc-credentials",
|
|
||||||
"k8s.secret.client-id-key": "key",
|
|
||||||
"k8s.secret.client-secret-key": "secret"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Important**:
|
|
||||||
- Do **NOT** include a `"secret"` field — Keycloak generates one automatically
|
|
||||||
- The `attributes` block tells the registrar where to create the K8s Secret
|
|
||||||
- Set `client-id-key` / `client-secret-key` to match what the consuming app expects (defaults: `client-id` / `client-secret`)
|
|
||||||
|
|
||||||
#### Step 2: Reference the Secret in Your Application
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
existingSecret: myapp-oidc-credentials
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Step 3: Commit and Push
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/dev/k8s/launchpad
|
|
||||||
git add infra/values/base/keycloak-values.yaml
|
|
||||||
git commit -m "Add myapp Keycloak client with auto-sync"
|
|
||||||
git push
|
|
||||||
```
|
|
||||||
|
|
||||||
ArgoCD will sync the Keycloak config, and the registrar CronJob will pick up the new client within 2 minutes.
|
|
||||||
|
|
||||||
#### Legacy Sync Attribute Reference
|
|
||||||
|
|
||||||
| Attribute | Required | Default | Description |
|
|
||||||
|-----------|----------|---------|-------------|
|
|
||||||
| `k8s.secret.sync` | Yes | — | Set to `"true"` to enable syncing |
|
|
||||||
| `k8s.secret.namespace` | Yes | — | Target K8s namespace for the secret |
|
|
||||||
| `k8s.secret.name` | Yes | — | Name of the K8s Secret to create |
|
|
||||||
| `k8s.secret.client-id-key` | No | `client-id` | Field name for the client ID in the K8s Secret |
|
|
||||||
| `k8s.secret.client-secret-key` | No | `client-secret` | Field name for the client secret in the K8s Secret |
|
|
||||||
|
|
||||||
### Retrieving Secrets for External Deployments
|
|
||||||
|
|
||||||
The registrar always writes a **central copy** of every synced secret to the `secrets` namespace, in addition to the target namespace. This allows operators to retrieve client credentials for applications deployed outside this cluster:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# View the central copy
|
|
||||||
kubectl get secret gitea-oidc-credentials -n secrets -o yaml
|
|
||||||
|
|
||||||
# Extract the client secret for use elsewhere
|
|
||||||
kubectl get secret myapp-oidc-credentials -n secrets \
|
|
||||||
-o jsonpath='{.data.client-secret}' | base64 -d
|
|
||||||
```
|
|
||||||
|
|
||||||
### Registrar Behavior Notes
|
|
||||||
|
|
||||||
- The registrar runs as a CronJob every 2 minutes (`concurrencyPolicy: Forbid`)
|
|
||||||
- If the target namespace doesn't exist, the target write is skipped with a warning (the central copy still happens)
|
|
||||||
- A central copy is **always** written to the `secrets` namespace for every synced client
|
|
||||||
- The registrar uses the `keycloak-credentials` secret for admin authentication
|
|
||||||
- Created secrets have the label `app.kubernetes.io/managed-by: keycloak-client-registrar`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Application Not Deploying
|
### Application Not Deploying
|
||||||
@@ -1530,7 +1303,7 @@ kubectl exec -n myapp <pod-name> -- env
|
|||||||
# Check if secrets exist
|
# Check if secrets exist
|
||||||
kubectl get secrets -n myapp
|
kubectl get secrets -n myapp
|
||||||
|
|
||||||
# Increase resources in helm-prod-values
|
# Increase resources in helm-values
|
||||||
vim ~/dev/k8s/helm-prod-values/myapp/values.yaml
|
vim ~/dev/k8s/helm-prod-values/myapp/values.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1679,7 +1452,7 @@ If you're stuck:
|
|||||||
### Configuration Management
|
### Configuration Management
|
||||||
|
|
||||||
✅ **DO**:
|
✅ **DO**:
|
||||||
- Keep configuration in `helm-prod-values` repository
|
- Keep configuration in `helm-values` repository
|
||||||
- Use environment variables for config
|
- Use environment variables for config
|
||||||
- Document what each value does
|
- Document what each value does
|
||||||
- Use reasonable resource limits
|
- Use reasonable resource limits
|
||||||
@@ -1806,4 +1579,4 @@ Now that you understand the basics:
|
|||||||
- Docs: [Full documentation index](README.md)
|
- Docs: [Full documentation index](README.md)
|
||||||
- Help: Contact platform team
|
- Help: Contact platform team
|
||||||
|
|
||||||
**Last Updated**: 2026-04-16
|
**Last Updated**: 2026-03-16
|
||||||
|
|||||||
@@ -12,16 +12,16 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where Git repositories serve as the single source of truth for both infrastructure and application deployments. The cluster setup is **cloud-agnostic**, with ready-to-use configurations for **UpCloud**, **AWS EKS**, **Azure AKS**, and **GCP GKE**.
|
This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where Git repositories serve as the single source of truth for both infrastructure and application deployments. The cluster is running on **UpCloud Managed Kubernetes** but is designed to be cloud-agnostic.
|
||||||
|
|
||||||
### Key Characteristics
|
### Key Characteristics
|
||||||
- **Environment**: Production (internal use only)
|
- **Environment**: Production (internal use only)
|
||||||
- **Cluster Type**: Multi-cloud, multi-cluster via Kustomize overlays (UpCloud, AWS, Azure, GCP)
|
- **Cluster Type**: Single cluster, single environment
|
||||||
- **GitOps Tool**: ArgoCD
|
- **GitOps Tool**: ArgoCD
|
||||||
- **Deployment Pattern**: App-of-Apps
|
- **Deployment Pattern**: App-of-Apps
|
||||||
- **Secret Management**: Sealed Secrets (kubeseal)
|
- **Secret Management**: Sealed Secrets (kubeseal)
|
||||||
- **Ingress**: Traefik with Let's Encrypt TLS
|
- **Ingress**: Traefik with Let's Encrypt TLS
|
||||||
- **Monitoring**: Prometheus + Grafana + Loki + Tempo + Fluent-Bit
|
- **Monitoring**: Prometheus + Grafana + Loki + Fluent-Bit
|
||||||
- **Policy Engine**: Kyverno
|
- **Policy Engine**: Kyverno
|
||||||
- **Notifications**: Slack integration for sync status
|
- **Notifications**: Slack integration for sync status
|
||||||
|
|
||||||
@@ -47,14 +47,14 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where
|
|||||||
│ │ │
|
│ │ │
|
||||||
│ │ │
|
│ │ │
|
||||||
└────────► Update image tag ─┴──────────────────────────┘
|
└────────► Update image tag ─┴──────────────────────────┘
|
||||||
in helm-prod-values │
|
in helm-values │
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌────────────────────────────────┐
|
┌────────────────────────────────┐
|
||||||
│ Config Repository │
|
│ Config Repository │
|
||||||
│ (ArgoCD Applications) │
|
│ (ArgoCD Applications) │
|
||||||
│ git.forteapps.net/Forte/ │
|
│ github.com/snothub/ │
|
||||||
│ launchpad │
|
│ sturdy-adventure │
|
||||||
└────────────────────────────────┘
|
└────────────────────────────────┘
|
||||||
│
|
│
|
||||||
│
|
│
|
||||||
@@ -62,8 +62,8 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where
|
|||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌────────────────────────────────┐
|
┌────────────────────────────────┐
|
||||||
│ Kubernetes Clusters │
|
│ Kubernetes Cluster │
|
||||||
│ (UpCloud, AWS, Azure, GCP) │
|
│ (UpCloud Managed) │
|
||||||
│ │
|
│ │
|
||||||
│ ┌──────────────────────────┐ │
|
│ ┌──────────────────────────┐ │
|
||||||
│ │ ArgoCD │ │
|
│ │ ArgoCD │ │
|
||||||
@@ -83,7 +83,6 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where
|
|||||||
│ │ - Prometheus │ │
|
│ │ - Prometheus │ │
|
||||||
│ │ - Grafana │ │
|
│ │ - Grafana │ │
|
||||||
│ │ - Loki │ │
|
│ │ - Loki │ │
|
||||||
│ │ - Tempo │ │
|
|
||||||
│ │ - Fluent-Bit │ │
|
│ │ - Fluent-Bit │ │
|
||||||
│ └──────────────────────────┘ │
|
│ └──────────────────────────┘ │
|
||||||
│ │
|
│ │
|
||||||
@@ -109,78 +108,87 @@ This Kubernetes cluster uses a **GitOps approach** powered by **ArgoCD**, where
|
|||||||
## Repository Structure
|
## Repository Structure
|
||||||
|
|
||||||
### 1. **Config Repository** (Current Repo)
|
### 1. **Config Repository** (Current Repo)
|
||||||
**Repository**: `https://git.forteapps.net/Forte/launchpad`
|
**Repository**: `https://github.com/fortedigital/sturdy-adventure.git`
|
||||||
**Purpose**: GitOps configuration - ArgoCD Applications and cluster resources
|
**Purpose**: GitOps configuration - ArgoCD Applications and cluster resources
|
||||||
**Location**: `C:\dev\k8s\launchpad`
|
**Location**: `C:\dev\k8s\launchpad`
|
||||||
|
|
||||||
```
|
```
|
||||||
launchpad/
|
sturdy-adventure/
|
||||||
├── bootstrap.sh # Cluster initialization script
|
├── bootstrap.sh # Cluster initialization script
|
||||||
├── _app-of-apps-upc-dev.yaml # Root ArgoCD Application (upc-dev cluster)
|
├── _app-of-apps.yaml # Root ArgoCD Application (App-of-Apps pattern)
|
||||||
├── _app-of-apps-upc-prod.yaml # Root ArgoCD Application (upc-prod cluster)
|
|
||||||
│
|
│
|
||||||
├── infra/ # Infrastructure ArgoCD Applications (Kustomize)
|
├── infra/ # Infrastructure ArgoCD Applications
|
||||||
│ ├── base/ # Base Application manifests (upc-dev defaults)
|
│ ├── enterprise-apps.yaml # Parent app managing all apps in apps/
|
||||||
│ │ ├── kustomization.yaml
|
│ ├── cluster-resources-application.yaml
|
||||||
│ │ ├── traefik-application.yaml
|
│ ├── traefik-application.yaml
|
||||||
│ │ ├── keycloak.yaml
|
│ ├── cert-manager-application.yaml
|
||||||
│ │ ├── grafana.yaml
|
│ ├── kyverno.yaml
|
||||||
│ │ ├── gitea.yaml
|
│ ├── kyverno-policies.yaml
|
||||||
│ │ ├── gitea-actions.yaml
|
│ ├── prometheus.yaml
|
||||||
│ │ ├── tempo.yaml
|
│ ├── grafana.yaml
|
||||||
│ │ ├── renovate.yaml
|
│ ├── loki.yaml
|
||||||
│ │ ├── ... # All other Application manifests
|
│ ├── fluent-bit.yaml
|
||||||
│ │ └── secrets.yaml
|
│ ├── trivy.yaml
|
||||||
│ ├── overlays/ # Per-cluster Kustomize overrides
|
│ ├── sealedsecrets.yaml
|
||||||
│ │ ├── upc-dev/ # UpCloud Dev (uses base as-is)
|
│ ├── secrets.yaml
|
||||||
│ │ ├── upc-prod/ # UpCloud Prod (patches value paths)
|
|
||||||
│ │ ├── eks-dev/ # AWS EKS Dev
|
|
||||||
│ │ ├── eks-prod/ # AWS EKS Prod
|
|
||||||
│ │ ├── aks-dev/ # Azure AKS Dev
|
|
||||||
│ │ ├── aks-prod/ # Azure AKS Prod
|
|
||||||
│ │ ├── gke-dev/ # GCP GKE Dev
|
|
||||||
│ │ └── gke-prod/ # GCP GKE Prod
|
|
||||||
│ ├── dashboards/ # Grafana dashboard ConfigMaps
|
|
||||||
│ └── values/ # Helm value overrides for infra
|
│ └── values/ # Helm value overrides for infra
|
||||||
│ ├── base/ # Cloud-agnostic shared values
|
│ ├── argocd-values.yaml
|
||||||
│ ├── upc-{dev,prod}/ # UpCloud: storage class, LB, pricing
|
│ ├── prometheus-values.yaml
|
||||||
│ ├── aws-{dev,prod}/ # AWS: gp3, NLB, CUR pricing
|
│ ├── grafana-values.yaml
|
||||||
│ ├── aks-{dev,prod}/ # Azure: managed-csi-premium, Standard LB
|
│ ├── loki-values.yaml
|
||||||
│ └── gcp-{dev,prod}/ # GCP: premium-rwo, L4 LB
|
│ └── fluent-bit-values.yaml
|
||||||
│
|
│
|
||||||
├── apps/ # Business Application ArgoCD manifests (Kustomize)
|
├── apps/ # Business Application ArgoCD manifests
|
||||||
│ ├── base/ # Base app manifests
|
│ ├── mcp10x.yaml # MCP 10X application
|
||||||
│ │ ├── kustomization.yaml
|
│ ├── musicman.yaml # Music Man application
|
||||||
│ │ ├── dot-ai-stack.yaml
|
│ ├── dot-ai-stack.yaml # Dot AI Stack
|
||||||
│ │ └── ...
|
│ └── argo-mcp.yaml # ArgoCD MCP server
|
||||||
│ └── overlays/
|
|
||||||
│ ├── upc-dev/ # Uses base as-is
|
|
||||||
│ └── upc-prod/ # Patches value paths
|
|
||||||
│
|
│
|
||||||
├── cluster-resources/ # Cluster-wide Kubernetes resources
|
├── 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
|
||||||
|
│ ├── snothub-repo-credentials-sealed.yaml
|
||||||
|
│ ├── forte10x-repo-credentials-sealed.yaml
|
||||||
|
│ ├── mcp10x-repo-credentials-sealed.yaml
|
||||||
│ └── policies/ # Kyverno policies
|
│ └── 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, per-cluster)
|
├── secrets/ # Application secrets (sealed)
|
||||||
│ └── upc-dev/ # Secrets for upc-dev cluster
|
│ ├── argocd-mcp-credentials.yaml
|
||||||
|
│ ├── dot-ai-secrets.yaml
|
||||||
|
│ ├── mcp10x-credentials-sealed.yaml
|
||||||
|
│ └── musicman-credentials.yaml
|
||||||
│
|
│
|
||||||
├── private/ # Local-only files (NOT in Git)
|
├── private/ # Local-only files (NOT in Git)
|
||||||
|
│ ├── *.yaml # Unsealed secrets
|
||||||
|
│ └── *.sh # Helper scripts
|
||||||
│
|
│
|
||||||
└── docs/ # Documentation
|
└── docs/ # Documentation
|
||||||
|
├── GITOPS-ARCHITECTURE.md # This file
|
||||||
|
├── DEVELOPER-GUIDE.md
|
||||||
|
├── OPERATIONS-RUNBOOK.md
|
||||||
|
└── REFERENCE.md
|
||||||
```
|
```
|
||||||
|
|
||||||
**Key Points**:
|
**Key Points**:
|
||||||
- `_app-of-apps-upc-dev.yaml` and `_app-of-apps-upc-prod.yaml` are the per-cluster root Applications
|
- `_app-of-apps.yaml` is the root Application that ArgoCD monitors
|
||||||
- Kustomize overlays in `infra/overlays/` render base Applications with per-cluster patches
|
- `infra/enterprise-apps.yaml` auto-discovers all apps in `apps/` folder
|
||||||
- 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
|
- Changes pushed to this repo trigger automatic syncs in ArgoCD
|
||||||
- `private/` folder contains local-only files (Git-ignored)
|
- `private/` folder contains local-only files (Git-ignored)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2. **Helm Charts Repository**
|
### 2. **Helm Charts Repository**
|
||||||
**Repository**: `https://git.forteapps.net/Forte/forte-helm`
|
**Repository**: `https://github.com/snothub/forte-helm`
|
||||||
**Purpose**: Reusable Helm chart templates for Forte applications
|
**Purpose**: Reusable Helm chart templates for Forte applications
|
||||||
**Location**: `C:\dev\k8s\forte-helm`
|
**Location**: `C:\dev\k8s\forte-helm`
|
||||||
|
|
||||||
@@ -214,7 +222,7 @@ forte-helm/
|
|||||||
---
|
---
|
||||||
|
|
||||||
### 3. **Helm Values Repository**
|
### 3. **Helm Values Repository**
|
||||||
**Repository**: `git@github.com:fortedigital/helm-prod-values.git`
|
**Repository**: `git@github.com:fortedigital/helm-values.git`
|
||||||
**Purpose**: Environment-specific configuration for each application
|
**Purpose**: Environment-specific configuration for each application
|
||||||
**Location**: `C:\dev\k8s\helm-prod-values`
|
**Location**: `C:\dev\k8s\helm-prod-values`
|
||||||
|
|
||||||
@@ -224,6 +232,8 @@ helm-prod-values/
|
|||||||
│ └── values.yaml # MCP 10X configuration
|
│ └── values.yaml # MCP 10X configuration
|
||||||
├── musicman/
|
├── musicman/
|
||||||
│ └── values.yaml # Music Man configuration
|
│ └── values.yaml # Music Man configuration
|
||||||
|
├── mcpcoder/
|
||||||
|
│ └── values.yaml # MCP Coder configuration
|
||||||
└── argocd-mcp/
|
└── argocd-mcp/
|
||||||
└── values.yaml # ArgoCD MCP configuration
|
└── values.yaml # ArgoCD MCP configuration
|
||||||
```
|
```
|
||||||
@@ -273,7 +283,7 @@ app-repository/
|
|||||||
2. Build Docker image
|
2. Build Docker image
|
||||||
3. Tag with version (e.g., `v2.0.4`)
|
3. Tag with version (e.g., `v2.0.4`)
|
||||||
4. Push to container registry (GHCR, Docker Hub, etc.)
|
4. Push to container registry (GHCR, Docker Hub, etc.)
|
||||||
5. Update image tag in `helm-prod-values` repository
|
5. Update image tag in `helm-values` repository
|
||||||
6. ArgoCD detects change and syncs automatically
|
6. ArgoCD detects change and syncs automatically
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -283,7 +293,7 @@ app-repository/
|
|||||||
### The App-of-Apps Pattern
|
### The App-of-Apps Pattern
|
||||||
|
|
||||||
```
|
```
|
||||||
_app-of-apps-{cluster}.yaml (Root, per cluster — e.g. upc-dev, eks-prod, gke-dev)
|
_app-of-apps.yaml (Root)
|
||||||
│
|
│
|
||||||
├── infrastructure-apps (manages infra/)
|
├── infrastructure-apps (manages infra/)
|
||||||
│ ├── cluster-resources-application
|
│ ├── cluster-resources-application
|
||||||
@@ -292,7 +302,6 @@ _app-of-apps-{cluster}.yaml (Root, per cluster — e.g. upc-dev, eks-prod, gke-d
|
|||||||
│ ├── kyverno
|
│ ├── kyverno
|
||||||
│ ├── prometheus
|
│ ├── prometheus
|
||||||
│ ├── grafana
|
│ ├── grafana
|
||||||
│ ├── tempo
|
|
||||||
│ └── ... (other infra apps)
|
│ └── ... (other infra apps)
|
||||||
│
|
│
|
||||||
└── enterprise-apps (manages apps/)
|
└── enterprise-apps (manages apps/)
|
||||||
@@ -303,10 +312,10 @@ _app-of-apps-{cluster}.yaml (Root, per cluster — e.g. upc-dev, eks-prod, gke-d
|
|||||||
```
|
```
|
||||||
|
|
||||||
**How It Works**:
|
**How It Works**:
|
||||||
1. Bootstrap script installs ArgoCD and applies `_app-of-apps-upc-dev.yaml` (or `upc-prod`)
|
1. Bootstrap script installs ArgoCD and applies `_app-of-apps.yaml`
|
||||||
2. ArgoCD creates the root Application which monitors the appropriate `infra/overlays/` folder
|
2. ArgoCD creates the root Application which monitors `infra/` folder
|
||||||
3. Kustomize renders base Applications with cluster-specific patches
|
3. Each YAML in `infra/` becomes a child Application
|
||||||
4. `enterprise-apps` Application monitors the cluster's `apps/overlays/` folder
|
4. `enterprise-apps.yaml` monitors `apps/` folder and auto-discovers applications
|
||||||
5. ArgoCD continuously syncs (every 60s) and auto-heals drift
|
5. ArgoCD continuously syncs (every 60s) and auto-heals drift
|
||||||
|
|
||||||
### Sync Waves & Ordering
|
### Sync Waves & Ordering
|
||||||
@@ -334,13 +343,13 @@ Applications like `mcp10x` and `musicman` use multiple sources:
|
|||||||
```yaml
|
```yaml
|
||||||
spec:
|
spec:
|
||||||
sources:
|
sources:
|
||||||
- repoURL: https://git.forteapps.net/Forte/forte-helm
|
- repoURL: https://github.com/snothub/forte-helm
|
||||||
path: forteapp # Helm chart templates
|
path: forteapp # Helm chart templates
|
||||||
helm:
|
helm:
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/mcp10x/values.yaml # Reference to second source
|
- $values/mcp10x/values.yaml # Reference to second source
|
||||||
|
|
||||||
- repoURL: git@github.com:fortedigital/helm-prod-values.git
|
- repoURL: git@github.com:fortedigital/helm-values.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values # Named reference
|
ref: values # Named reference
|
||||||
```
|
```
|
||||||
@@ -351,43 +360,6 @@ spec:
|
|||||||
- Easy to update all apps by changing the chart
|
- Easy to update all apps by changing the chart
|
||||||
- Environment-specific values isolated in separate repo
|
- 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
|
|
||||||
```
|
|
||||||
|
|
||||||
Cloud-specific values (storage classes, load balancer annotations, cost model) are isolated in per-cluster value files. Base values are fully cloud-agnostic:
|
|
||||||
|
|
||||||
| Cloud | Storage Class | Load Balancer | OpenCost Provider |
|
|
||||||
|-------|--------------|---------------|-------------------|
|
|
||||||
| **UpCloud** | `upcloud-block-storage-maxiops` | UpCloud LB (ProxyProtocol v2) | Custom pricing |
|
|
||||||
| **AWS EKS** | `gp3` (EBS CSI) | NLB (ProxyProtocol v2) | AWS CUR |
|
|
||||||
| **Azure AKS** | `managed-csi-premium` | Standard LB (`externalTrafficPolicy: Local`) | Azure Billing API |
|
|
||||||
| **GCP GKE** | `premium-rwo` (PD CSI) | L4 passthrough NLB | GCP Cloud Billing |
|
|
||||||
|
|
||||||
**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
|
## CI/CD Pipeline
|
||||||
@@ -417,8 +389,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Update Helm values
|
- name: Update Helm values
|
||||||
run: |
|
run: |
|
||||||
git clone git@github.com:fortedigital/helm-prod-values.git
|
git clone git@github.com:fortedigital/helm-values.git
|
||||||
cd helm-prod-values/app
|
cd helm-values/app
|
||||||
sed -i "s/tag: .*/tag: $VERSION/" values.yaml
|
sed -i "s/tag: .*/tag: $VERSION/" values.yaml
|
||||||
git commit -am "Update app to $VERSION"
|
git commit -am "Update app to $VERSION"
|
||||||
git push
|
git push
|
||||||
@@ -430,12 +402,12 @@ jobs:
|
|||||||
|
|
||||||
1. **Config Repo Change**:
|
1. **Config Repo Change**:
|
||||||
- Developer updates `apps/myapp.yaml`
|
- Developer updates `apps/myapp.yaml`
|
||||||
- Pushes to `launchpad` repo
|
- Pushes to `sturdy-adventure` repo
|
||||||
- ArgoCD detects change (60s reconciliation)
|
- ArgoCD detects change (60s reconciliation)
|
||||||
- Syncs application to cluster
|
- Syncs application to cluster
|
||||||
|
|
||||||
2. **Helm Values Change**:
|
2. **Helm Values Change**:
|
||||||
- CI/CD updates `helm-prod-values/myapp/values.yaml`
|
- CI/CD updates `helm-values/myapp/values.yaml`
|
||||||
- ArgoCD detects change
|
- ArgoCD detects change
|
||||||
- Pulls new Helm chart with updated values
|
- Pulls new Helm chart with updated values
|
||||||
- Applies to cluster
|
- Applies to cluster
|
||||||
@@ -517,6 +489,7 @@ git commit -m "Add app credentials"
|
|||||||
**Private Repository Credentials** stored as SealedSecrets:
|
**Private Repository Credentials** stored as SealedSecrets:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
# cluster-resources/snothub-repo-credentials-sealed.yaml
|
||||||
# cluster-resources/forte10x-repo-credentials-sealed.yaml
|
# cluster-resources/forte10x-repo-credentials-sealed.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -555,9 +528,8 @@ annotations:
|
|||||||
1. **Prometheus**: Metrics collection and storage
|
1. **Prometheus**: Metrics collection and storage
|
||||||
2. **Grafana**: Metrics visualization and dashboards
|
2. **Grafana**: Metrics visualization and dashboards
|
||||||
3. **Loki**: Log aggregation
|
3. **Loki**: Log aggregation
|
||||||
4. **Tempo**: Distributed tracing (OTLP)
|
4. **Fluent-Bit**: Log shipping from pods to Loki
|
||||||
5. **Fluent-Bit**: Log shipping from pods to Loki
|
5. **Trivy**: Container vulnerability scanning
|
||||||
6. **Trivy**: Container vulnerability scanning
|
|
||||||
|
|
||||||
### Slack Notifications
|
### Slack Notifications
|
||||||
|
|
||||||
@@ -586,7 +558,7 @@ Notifications include:
|
|||||||
|
|
||||||
**Rebuild Process**:
|
**Rebuild Process**:
|
||||||
1. Provision new Kubernetes cluster
|
1. Provision new Kubernetes cluster
|
||||||
2. Clone `launchpad` repository
|
2. Clone `sturdy-adventure` repository
|
||||||
3. Run `./bootstrap.sh`
|
3. Run `./bootstrap.sh`
|
||||||
4. ArgoCD installs and syncs all applications
|
4. ArgoCD installs and syncs all applications
|
||||||
5. Manually recreate unsealed secrets and seal them
|
5. Manually recreate unsealed secrets and seal them
|
||||||
@@ -642,7 +614,7 @@ Notifications include:
|
|||||||
✅ **DO**:
|
✅ **DO**:
|
||||||
- Follow the `forteapp` chart pattern
|
- Follow the `forteapp` chart pattern
|
||||||
- Use semantic versioning for image tags
|
- Use semantic versioning for image tags
|
||||||
- Update helm-prod-values via CI/CD
|
- Update helm-values via CI/CD
|
||||||
- Test locally with Docker Compose
|
- Test locally with Docker Compose
|
||||||
- Document environment variables
|
- Document environment variables
|
||||||
|
|
||||||
@@ -663,6 +635,6 @@ Notifications include:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: 2026-04-22
|
**Last Updated**: 2026-03-16
|
||||||
**Maintained By**: Platform Team
|
**Maintained By**: Platform Team
|
||||||
**Questions?**: Contact #platform-support on Slack
|
**Questions?**: Contact #platform-support on Slack
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ Bootstrap a new cluster from scratch:
|
|||||||
|
|
||||||
#### Prerequisites
|
#### Prerequisites
|
||||||
|
|
||||||
1. **Kubernetes cluster running** (UpCloud, AWS EKS, Azure AKS, GCP GKE, or any K8s cluster)
|
1. **Kubernetes cluster running** (UpCloud or any K8s cluster)
|
||||||
2. **kubectl configured** with admin access
|
2. **kubectl configured** with admin access
|
||||||
3. **Repositories cloned** locally
|
3. **Repositories cloned** locally
|
||||||
|
|
||||||
@@ -51,16 +51,14 @@ kubectl get nodes
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Clone config repository
|
# 1. Clone config repository
|
||||||
git clone https://git.forteapps.net/Forte/launchpad
|
git clone https://github.com/fortedigital/sturdy-adventure.git
|
||||||
cd launchpad
|
cd sturdy-adventure
|
||||||
|
|
||||||
# 2. Run bootstrap script with cluster target
|
# 2. Set cluster name (optional)
|
||||||
# Available clusters: upc-dev, upc-prod, eks-dev, eks-prod,
|
export CLUSTER_NAME="prod-cluster-01"
|
||||||
# aks-dev, aks-prod, gke-dev, gke-prod
|
|
||||||
./bootstrap.sh upc-dev
|
|
||||||
|
|
||||||
# Cluster config is loaded from clusters/<cluster>.yaml
|
# 3. Run bootstrap script
|
||||||
# (cloudProvider, trustedIPs, domain, etc.)
|
./bootstrap.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
**What Happens:**
|
**What Happens:**
|
||||||
@@ -87,8 +85,7 @@ kubectl get applications -n argocd
|
|||||||
|
|
||||||
1. **Configure DNS** for ingress domains:
|
1. **Configure DNS** for ingress domains:
|
||||||
- `argocd.127.0.0.1.nip.io` (local dev)
|
- `argocd.127.0.0.1.nip.io` (local dev)
|
||||||
- `*.forteapps.net` (dev)
|
- `*.forteapps.net` (production)
|
||||||
- `*.fortedigital.com` (production)
|
|
||||||
|
|
||||||
2. **Verify Let's Encrypt certificates**:
|
2. **Verify Let's Encrypt certificates**:
|
||||||
```bash
|
```bash
|
||||||
@@ -110,7 +107,7 @@ kubectl get applications -n argocd
|
|||||||
|
|
||||||
### ArgoCD Repository Access Setup
|
### ArgoCD Repository Access Setup
|
||||||
|
|
||||||
ArgoCD needs SSH access to private Git repositories to pull manifests and Helm values. This section covers setting up deploy keys for Gitea repositories.
|
ArgoCD needs SSH access to private Git repositories to pull manifests and Helm values. This section covers setting up deploy keys for GitHub repositories.
|
||||||
|
|
||||||
#### Why Deploy Keys?
|
#### Why Deploy Keys?
|
||||||
|
|
||||||
@@ -122,7 +119,7 @@ ArgoCD needs SSH access to private Git repositories to pull manifests and Helm v
|
|||||||
#### Prerequisites
|
#### Prerequisites
|
||||||
|
|
||||||
- kubectl access to the cluster
|
- kubectl access to the cluster
|
||||||
- Write access to the Gitea repository
|
- Write access to the GitHub repository
|
||||||
- ArgoCD installed and running
|
- ArgoCD installed and running
|
||||||
|
|
||||||
#### Setup Procedure
|
#### Setup Procedure
|
||||||
@@ -133,25 +130,25 @@ Generate a dedicated SSH key for ArgoCD without a passphrase (required for autom
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate ED25519 key (recommended - smaller and more secure)
|
# Generate ED25519 key (recommended - smaller and more secure)
|
||||||
ssh-keygen -t ed25519 -C "argocd-deploy-key-launchpad" -f argocd-deploy-key -N ""
|
ssh-keygen -t ed25519 -C "argocd-deploy-key-sturdy-adventure" -f argocd-deploy-key -N ""
|
||||||
|
|
||||||
# Or RSA key if ED25519 is not supported
|
# Or RSA key if ED25519 is not supported
|
||||||
ssh-keygen -t rsa -b 4096 -C "argocd-deploy-key-launchpad" -f argocd-deploy-key -N ""
|
ssh-keygen -t rsa -b 4096 -C "argocd-deploy-key-sturdy-adventure" -f argocd-deploy-key -N ""
|
||||||
```
|
```
|
||||||
|
|
||||||
This creates two files:
|
This creates two files:
|
||||||
- `argocd-deploy-key` - Private key (keep secret)
|
- `argocd-deploy-key` - Private key (keep secret)
|
||||||
- `argocd-deploy-key.pub` - Public key (add to Gitea)
|
- `argocd-deploy-key.pub` - Public key (add to GitHub)
|
||||||
|
|
||||||
**Step 2: Add Public Key to Gitea**
|
**Step 2: Add Public Key to GitHub**
|
||||||
|
|
||||||
1. Copy the public key:
|
1. Copy the public key:
|
||||||
```bash
|
```bash
|
||||||
cat argocd-deploy-key.pub
|
cat argocd-deploy-key.pub
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Go to Gitea repository settings:
|
2. Go to GitHub repository settings:
|
||||||
- Navigate to: `https://git.forteapps.net/Forte/launchpad/settings/keys`
|
- Navigate to: `https://github.com/fortedigital/sturdy-adventure/settings/keys`
|
||||||
- Or: Repository → Settings → Deploy keys
|
- Or: Repository → Settings → Deploy keys
|
||||||
|
|
||||||
3. Click **"Add deploy key"**
|
3. Click **"Add deploy key"**
|
||||||
@@ -160,45 +157,90 @@ This creates two files:
|
|||||||
- ☐ Allow write access (leave unchecked - read-only is sufficient)
|
- ☐ Allow write access (leave unchecked - read-only is sufficient)
|
||||||
- Click **"Add key"**
|
- Click **"Add key"**
|
||||||
|
|
||||||
4. Repeat for the `helm-prod-values` repository if it's private:
|
4. Repeat for the `helm-values` repository if it's private:
|
||||||
```bash
|
```bash
|
||||||
# Generate separate key for helm-prod-values repo
|
# Generate separate key for helm-values repo
|
||||||
ssh-keygen -t ed25519 -C "argocd-deploy-key-helm-prod-values" -f argocd-helm-prod-values-key -N ""
|
ssh-keygen -t ed25519 -C "argocd-deploy-key-helm-values" -f argocd-helm-values-key -N ""
|
||||||
|
|
||||||
# Add to: https://git.forteapps.net/Forte/helm-prod-values/settings/keys
|
# Add to: https://github.com/fortedigital/helm-values/settings/keys
|
||||||
```
|
```
|
||||||
|
|
||||||
**Step 3: Create Kubernetes Secret**
|
**Step 3: Create Kubernetes Secret**
|
||||||
|
|
||||||
Add the private key to ArgoCD as a repository secret:
|
Add the private key to ArgoCD as a repository secret:
|
||||||
|
|
||||||
Save the following file in private/ (gitignored) folder as secret.yaml
|
|
||||||
```bash
|
```bash
|
||||||
apiVersion: v1
|
# Create secret for sturdy-adventure repository
|
||||||
kind: Secret
|
kubectl create secret generic repo-sturdy-adventure \
|
||||||
metadata:
|
--from-file=sshPrivateKey=argocd-deploy-key \
|
||||||
name: forte-helm-repo
|
|
||||||
namespace: argocd
|
|
||||||
labels:
|
|
||||||
argocd.argoproj.io/secret-type: repository
|
|
||||||
stringData:
|
|
||||||
type: git
|
|
||||||
url: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git
|
|
||||||
sshPrivateKey: |
|
|
||||||
<paste your private key here>
|
|
||||||
project: default
|
|
||||||
```
|
|
||||||
Seal the secret using `kubeseal` command
|
|
||||||
```bash
|
|
||||||
kubeseal --format=yaml \
|
|
||||||
--namespace=argocd \
|
--namespace=argocd \
|
||||||
< private/secret.yaml \
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
> secrets/forte-helm-repo-secret-sealed.yaml
|
|
||||||
|
# Label it for ArgoCD to recognize
|
||||||
|
kubectl label secret repo-sturdy-adventure \
|
||||||
|
-n argocd \
|
||||||
|
argocd.argoproj.io/secret-type=repository
|
||||||
|
|
||||||
|
# Add repository annotations
|
||||||
|
kubectl annotate secret repo-sturdy-adventure \
|
||||||
|
-n argocd \
|
||||||
|
managed-by=argocd.argoproj.io
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, create a complete repository secret with all metadata:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f - <<EOF
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: repo-sturdy-adventure
|
||||||
|
namespace: argocd
|
||||||
|
labels:
|
||||||
|
argocd.argoproj.io/secret-type: repository
|
||||||
|
annotations:
|
||||||
|
managed-by: argocd.argoproj.io
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
type: git
|
||||||
|
url: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
|
sshPrivateKey: |
|
||||||
|
$(cat argocd-deploy-key | sed 's/^/ /')
|
||||||
|
EOF
|
||||||
```
|
```
|
||||||
|
|
||||||
**Step 4: Register Repository in ArgoCD**
|
**Step 4: Register Repository in ArgoCD**
|
||||||
|
|
||||||
Check in secrets/forte-helm-repo-secret-sealed.yaml and let Argo sync and create the secret.
|
Add the repository to ArgoCD's configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Via kubectl (recommended for GitOps)
|
||||||
|
kubectl apply -f - <<EOF
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: repo-sturdy-adventure
|
||||||
|
namespace: argocd
|
||||||
|
labels:
|
||||||
|
argocd.argoproj.io/secret-type: repository
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
type: git
|
||||||
|
url: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
|
sshPrivateKey: |
|
||||||
|
$(cat argocd-deploy-key | sed 's/^/ /')
|
||||||
|
insecure: "false"
|
||||||
|
enableLfs: "false"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Or via ArgoCD UI
|
||||||
|
# 1. Open ArgoCD UI: kubectl port-forward svc/argocd-server -n argocd 8080:443
|
||||||
|
# 2. Navigate to: Settings → Repositories → Connect Repo
|
||||||
|
# 3. Connection Method: Via SSH
|
||||||
|
# 4. Repository URL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
|
# 5. SSH private key: Paste private key content
|
||||||
|
# 6. Click "Connect"
|
||||||
|
```
|
||||||
|
|
||||||
**Step 5: Verify Repository Access**
|
**Step 5: Verify Repository Access**
|
||||||
|
|
||||||
@@ -210,7 +252,7 @@ kubectl get secrets -n argocd -l argocd.argoproj.io/secret-type=repository
|
|||||||
# Settings → Repositories → Should show "Successful" status
|
# Settings → Repositories → Should show "Successful" status
|
||||||
|
|
||||||
# Test by creating an application
|
# Test by creating an application
|
||||||
kubectl apply -f _app-of-apps-upc-dev.yaml # or _app-of-apps-upc-prod.yaml
|
kubectl apply -f _app-of-apps.yaml
|
||||||
|
|
||||||
# Check application sync status
|
# Check application sync status
|
||||||
kubectl get applications -n argocd
|
kubectl get applications -n argocd
|
||||||
@@ -230,7 +272,7 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
source:
|
source:
|
||||||
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: main
|
targetRevision: main
|
||||||
path: cluster-resources
|
path: cluster-resources
|
||||||
destination:
|
destination:
|
||||||
@@ -273,15 +315,15 @@ rm /tmp/test-repo-access.yaml
|
|||||||
# Generate new key
|
# Generate new key
|
||||||
ssh-keygen -t ed25519 -C "argocd-deploy-key-$(date +%Y%m)" -f argocd-new-key -N ""
|
ssh-keygen -t ed25519 -C "argocd-deploy-key-$(date +%Y%m)" -f argocd-new-key -N ""
|
||||||
|
|
||||||
# Add new public key to Gitea (keep old key for now)
|
# Add new public key to GitHub (keep old key for now)
|
||||||
|
|
||||||
# Update Kubernetes secret
|
# Update Kubernetes secret
|
||||||
kubectl create secret generic repo-launchpad \
|
kubectl create secret generic repo-sturdy-adventure \
|
||||||
--from-file=sshPrivateKey=argocd-new-key \
|
--from-file=sshPrivateKey=argocd-new-key \
|
||||||
--namespace=argocd \
|
--namespace=argocd \
|
||||||
--dry-run=client -o yaml | kubectl apply -f -
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
# Test access, then remove old deploy key from Gitea
|
# Test access, then remove old deploy key from GitHub
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
shred -u argocd-new-key
|
shred -u argocd-new-key
|
||||||
@@ -292,8 +334,8 @@ rm /tmp/test-repo-access.yaml
|
|||||||
# List all repository secrets
|
# List all repository secrets
|
||||||
kubectl get secrets -n argocd -l argocd.argoproj.io/secret-type=repository
|
kubectl get secrets -n argocd -l argocd.argoproj.io/secret-type=repository
|
||||||
|
|
||||||
# Review deploy keys in Gitea
|
# Review deploy keys in GitHub
|
||||||
# Visit: https://git.forteapps.net/Forte/launchpad/settings/keys
|
# Visit: https://github.com/fortedigital/sturdy-adventure/settings/keys
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Use Different Keys per Repository**
|
4. **Use Different Keys per Repository**
|
||||||
@@ -307,27 +349,27 @@ rm /tmp/test-repo-access.yaml
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check if secret exists
|
# Check if secret exists
|
||||||
kubectl get secret repo-launchpad -n argocd
|
kubectl get secret repo-sturdy-adventure -n argocd
|
||||||
|
|
||||||
# Verify secret has correct label
|
# Verify secret has correct label
|
||||||
kubectl get secret repo-launchpad -n argocd -o yaml | grep argocd.argoproj.io/secret-type
|
kubectl get secret repo-sturdy-adventure -n argocd -o yaml | grep argocd.argoproj.io/secret-type
|
||||||
|
|
||||||
# Check ArgoCD application controller logs
|
# Check ArgoCD application controller logs
|
||||||
kubectl logs -n argocd deployment/argocd-application-controller | grep -i "permission denied"
|
kubectl logs -n argocd deployment/argocd-application-controller | grep -i "permission denied"
|
||||||
|
|
||||||
# Verify deploy key is added to Gitea
|
# Verify deploy key is added to GitHub
|
||||||
# Visit: https://git.forteapps.net/Forte/launchpad/settings/keys
|
# Visit: https://github.com/fortedigital/sturdy-adventure/settings/keys
|
||||||
```
|
```
|
||||||
|
|
||||||
**Issue: "Host key verification failed"**
|
**Issue: "Host key verification failed"**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Add Gitea to known_hosts
|
# Add GitHub to known_hosts
|
||||||
kubectl exec -n argocd deployment/argocd-repo-server -- \
|
kubectl exec -n argocd deployment/argocd-repo-server -- \
|
||||||
ssh-keyscan git.forteapps.net >> ~/.ssh/known_hosts
|
ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
# Or disable strict host key checking (less secure)
|
# Or disable strict host key checking (less secure)
|
||||||
kubectl patch secret repo-launchpad -n argocd \
|
kubectl patch secret repo-sturdy-adventure -n argocd \
|
||||||
--type merge \
|
--type merge \
|
||||||
-p '{"stringData":{"insecure":"true"}}'
|
-p '{"stringData":{"insecure":"true"}}'
|
||||||
```
|
```
|
||||||
@@ -339,7 +381,7 @@ kubectl patch secret repo-launchpad -n argocd \
|
|||||||
kubectl logs -n argocd deployment/argocd-repo-server
|
kubectl logs -n argocd deployment/argocd-repo-server
|
||||||
|
|
||||||
# Refresh repository connection
|
# Refresh repository connection
|
||||||
kubectl delete secret repo-launchpad -n argocd
|
kubectl delete secret repo-sturdy-adventure -n argocd
|
||||||
# Recreate secret (see Step 3 above)
|
# Recreate secret (see Step 3 above)
|
||||||
|
|
||||||
# Restart ArgoCD components
|
# Restart ArgoCD components
|
||||||
@@ -349,34 +391,34 @@ kubectl rollout restart deployment argocd-application-controller -n argocd
|
|||||||
|
|
||||||
#### Multiple Repository Setup
|
#### Multiple Repository Setup
|
||||||
|
|
||||||
For the three-repository pattern (launchpad, forte-helm, helm-prod-values):
|
For the three-repository pattern (sturdy-adventure, forte-helm, helm-values):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. launchpad (main config repo)
|
# 1. sturdy-adventure (main config repo)
|
||||||
ssh-keygen -t ed25519 -C "argocd-launchpad" -f key-sturdy -N ""
|
ssh-keygen -t ed25519 -C "argocd-sturdy-adventure" -f key-sturdy -N ""
|
||||||
# Add key-sturdy.pub to: https://git.forteapps.net/Forte/launchpad/settings/keys
|
# Add key-sturdy.pub to: https://github.com/fortedigital/sturdy-adventure/settings/keys
|
||||||
|
|
||||||
# 2. helm-prod-values (private values repo)
|
# 2. helm-values (private values repo)
|
||||||
ssh-keygen -t ed25519 -C "argocd-helm-prod-values" -f key-helm-prod-values -N ""
|
ssh-keygen -t ed25519 -C "argocd-helm-values" -f key-helm-values -N ""
|
||||||
# Add key-helm-prod-values.pub to: https://git.forteapps.net/Forte/helm-prod-values/settings/keys
|
# Add key-helm-values.pub to: https://github.com/fortedigital/helm-values/settings/keys
|
||||||
|
|
||||||
# 3. forte-helm (private helm charts repo)
|
# 3. forte-helm is public - no key needed (use HTTPS)
|
||||||
|
|
||||||
# Create secrets
|
# Create secrets
|
||||||
kubectl create secret generic repo-launchpad \
|
kubectl create secret generic repo-sturdy-adventure \
|
||||||
--from-file=sshPrivateKey=key-sturdy \
|
--from-file=sshPrivateKey=key-sturdy \
|
||||||
--namespace=argocd --dry-run=client -o yaml | \
|
--namespace=argocd --dry-run=client -o yaml | \
|
||||||
kubectl label --local -f - argocd.argoproj.io/secret-type=repository --dry-run=client -o yaml | \
|
kubectl label --local -f - argocd.argoproj.io/secret-type=repository --dry-run=client -o yaml | \
|
||||||
kubectl apply -f -
|
kubectl apply -f -
|
||||||
|
|
||||||
kubectl create secret generic repo-helm-prod-values \
|
kubectl create secret generic repo-helm-values \
|
||||||
--from-file=sshPrivateKey=key-helm-prod-values \
|
--from-file=sshPrivateKey=key-helm-values \
|
||||||
--namespace=argocd --dry-run=client -o yaml | \
|
--namespace=argocd --dry-run=client -o yaml | \
|
||||||
kubectl label --local -f - argocd.argoproj.io/secret-type=repository --dry-run=client -o yaml | \
|
kubectl label --local -f - argocd.argoproj.io/secret-type=repository --dry-run=client -o yaml | \
|
||||||
kubectl apply -f -
|
kubectl apply -f -
|
||||||
|
|
||||||
# Clean up keys
|
# Clean up keys
|
||||||
shred -u key-sturdy key-helm-prod-values
|
shred -u key-sturdy key-helm-values
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Converting HTTPS to SSH
|
#### Converting HTTPS to SSH
|
||||||
@@ -388,12 +430,12 @@ If you're currently using HTTPS and want to switch to SSH:
|
|||||||
|
|
||||||
# 2. Update all Application manifests
|
# 2. Update all Application manifests
|
||||||
# Change from:
|
# Change from:
|
||||||
# repoURL: https://git.forteapps.net/Forte/launchpad
|
# repoURL: https://github.com/fortedigital/sturdy-adventure.git
|
||||||
# To:
|
# To:
|
||||||
# repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
# repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
|
|
||||||
# 3. Update and commit
|
# 3. Update and commit
|
||||||
find . -name "*.yaml" -type f -exec sed -i 's|https://git.forteapps.net/Forte/|git@git.forteapps.net:Forte/|g' {} +
|
find . -name "*.yaml" -type f -exec sed -i 's|https://github.com/fortedigital/|git@github.com:fortedigital/|g' {} +
|
||||||
|
|
||||||
git add .
|
git add .
|
||||||
git commit -m "Switch from HTTPS to SSH for repository access"
|
git commit -m "Switch from HTTPS to SSH for repository access"
|
||||||
@@ -497,7 +539,7 @@ spec:
|
|||||||
See [Developer Guide](DEVELOPER-GUIDE.md#deploying-your-first-application) for detailed steps.
|
See [Developer Guide](DEVELOPER-GUIDE.md#deploying-your-first-application) for detailed steps.
|
||||||
|
|
||||||
**Quick checklist:**
|
**Quick checklist:**
|
||||||
- [ ] Create `helm-prod-values/myapp/values.yaml`
|
- [ ] Create `helm-values/myapp/values.yaml`
|
||||||
- [ ] Create `apps/myapp.yaml` in config repo
|
- [ ] Create `apps/myapp.yaml` in config repo
|
||||||
- [ ] Create SealedSecret if needed
|
- [ ] Create SealedSecret if needed
|
||||||
- [ ] Commit and push changes
|
- [ ] Commit and push changes
|
||||||
@@ -562,7 +604,7 @@ kubectl scale deployment myapp -n myapp --replicas=3
|
|||||||
|
|
||||||
#### GitOps Scaling
|
#### GitOps Scaling
|
||||||
|
|
||||||
Update `helm-prod-values/myapp/values.yaml`:
|
Update `helm-values/myapp/values.yaml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
app:
|
app:
|
||||||
@@ -576,7 +618,7 @@ Commit and push - ArgoCD will sync.
|
|||||||
Enable Horizontal Pod Autoscaler:
|
Enable Horizontal Pod Autoscaler:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# In helm-prod-values/myapp/values.yaml
|
# In helm-values/myapp/values.yaml
|
||||||
app:
|
app:
|
||||||
hpa:
|
hpa:
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -625,7 +667,7 @@ kubectl rollout undo deployment myapp -n myapp
|
|||||||
#### Option 3: Change Image Tag
|
#### Option 3: Change Image Tag
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Edit helm-prod-values
|
# Edit helm-values
|
||||||
cd ~/dev/k8s/helm-prod-values
|
cd ~/dev/k8s/helm-prod-values
|
||||||
vim myapp/values.yaml
|
vim myapp/values.yaml
|
||||||
|
|
||||||
@@ -645,7 +687,7 @@ git push
|
|||||||
#### Update Resource Limits
|
#### Update Resource Limits
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# In helm-prod-values/myapp/values.yaml
|
# In helm-values/myapp/values.yaml
|
||||||
app:
|
app:
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
@@ -659,7 +701,7 @@ app:
|
|||||||
#### Enable Database
|
#### Enable Database
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# In helm-prod-values/myapp/values.yaml
|
# In helm-values/myapp/values.yaml
|
||||||
db:
|
db:
|
||||||
enabled: true
|
enabled: true
|
||||||
persistence:
|
persistence:
|
||||||
@@ -957,33 +999,6 @@ curl -G -s 'http://localhost:3100/loki/api/v1/query_range' \
|
|||||||
--data-urlencode 'start=1h' | jq
|
--data-urlencode 'start=1h' | jq
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tempo Traces
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Port forward to Tempo query API
|
|
||||||
kubectl port-forward -n monitoring svc/tempo 3200:3200
|
|
||||||
|
|
||||||
# Access: http://localhost:3200
|
|
||||||
```
|
|
||||||
|
|
||||||
**Query traces via Grafana:**
|
|
||||||
1. Open Grafana → Explore
|
|
||||||
2. Select Tempo datasource
|
|
||||||
3. Use TraceQL or search by service name
|
|
||||||
|
|
||||||
**Verify Traefik is sending traces:**
|
|
||||||
```bash
|
|
||||||
# Check Traefik logs for OTLP export errors
|
|
||||||
kubectl logs -n traefik-system -l app.kubernetes.io/name=traefik | grep -i "traces export"
|
|
||||||
|
|
||||||
# Check Tempo is receiving data
|
|
||||||
kubectl logs -n monitoring -l app.kubernetes.io/name=tempo | grep "receiver"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Trace-to-log correlation:**
|
|
||||||
- Click a trace span in Grafana → linked Loki logs appear (by namespace, pod, container)
|
|
||||||
- Trace-to-metrics links to Prometheus by service name
|
|
||||||
|
|
||||||
### Fluent-Bit Log Shipping
|
### Fluent-Bit Log Shipping
|
||||||
|
|
||||||
Verify Fluent-Bit is shipping logs:
|
Verify Fluent-Bit is shipping logs:
|
||||||
@@ -1264,21 +1279,13 @@ spec:
|
|||||||
|
|
||||||
### Backup Strategy
|
### Backup Strategy
|
||||||
|
|
||||||
**Current State**: Gitea daily backups to S3-compatible storage
|
**Current State**: No automated backups
|
||||||
|
|
||||||
**What Is Backed Up**:
|
**What Needs Backup**:
|
||||||
- ✅ Gitea repositories + database: Daily CronJob (`cluster-resources/gitea-backup-cronjob.yaml`) uploads to S3-compatible storage with 7-day retention
|
- ❌ Cluster state (not backed up - recreate via GitOps)
|
||||||
- ✅ Git repositories: Full cluster config recoverable from Git
|
- ❌ Persistent volumes (currently not critical)
|
||||||
- ⚠️ Secrets: Sealed secrets in Git; unseal keys need safekeeping
|
- ✅ Git repositories (GitHub provides backup)
|
||||||
|
- ⚠️ Secrets (sealed secrets in Git, unseal keys need safekeeping)
|
||||||
**What Is NOT Backed Up**:
|
|
||||||
- ❌ Cluster state (recreate via GitOps)
|
|
||||||
- ❌ Other persistent volumes (Prometheus, Loki, Tempo data)
|
|
||||||
|
|
||||||
**Per-cloud backup scripts** (manual restore helpers):
|
|
||||||
- UpCloud/AWS: `scripts/gitea-backup.sh` / `scripts/gitea-backup-eks.sh` (MinIO CLI, S3-compatible)
|
|
||||||
- Azure: `scripts/gitea-backup-aks.sh` (Azure CLI + Blob Storage)
|
|
||||||
- GCP: `scripts/gitea-backup-gke.sh` (gsutil + GCS)
|
|
||||||
|
|
||||||
### Cluster Rebuild
|
### Cluster Rebuild
|
||||||
|
|
||||||
@@ -1363,13 +1370,13 @@ kubectl get deployment argocd-server -n argocd \
|
|||||||
-o jsonpath='{.spec.template.spec.containers[0].image}'
|
-o jsonpath='{.spec.template.spec.containers[0].image}'
|
||||||
|
|
||||||
# Update version in values
|
# Update version in values
|
||||||
vim infra/values/base/argocd-values.yaml
|
vim infra/values/argocd-values.yaml
|
||||||
|
|
||||||
# Or upgrade via Helm directly
|
# Or upgrade via Helm directly
|
||||||
helm upgrade argocd argo-cd \
|
helm upgrade argocd argo-cd \
|
||||||
--repo https://argoproj.github.io/argo-helm \
|
--repo https://argoproj.github.io/argo-helm \
|
||||||
--namespace argocd \
|
--namespace argocd \
|
||||||
--values infra/values/base/argocd-values.yaml \
|
--values infra/values/argocd-values.yaml \
|
||||||
--version 6.0.0 # New version
|
--version 6.0.0 # New version
|
||||||
|
|
||||||
# Verify
|
# Verify
|
||||||
@@ -1380,9 +1387,6 @@ kubectl get pods -n argocd
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# UpCloud: Upgrade via control panel or CLI
|
# UpCloud: Upgrade via control panel or CLI
|
||||||
# AWS EKS: eksctl upgrade cluster / AWS Console
|
|
||||||
# Azure AKS: az aks upgrade / Azure Portal
|
|
||||||
# GCP GKE: gcloud container clusters upgrade / Cloud Console
|
|
||||||
|
|
||||||
# After upgrade, verify cluster
|
# After upgrade, verify cluster
|
||||||
kubectl version
|
kubectl version
|
||||||
@@ -1468,8 +1472,8 @@ kubectl top pods --all-namespaces --sort-by=cpu
|
|||||||
Example: Adding Redis
|
Example: Adding Redis
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Create application manifest in base/
|
# 1. Create application manifest
|
||||||
cat > infra/base/redis-application.yaml <<EOF
|
cat > infra/redis-application.yaml <<EOF
|
||||||
apiVersion: argoproj.io/v1alpha1
|
apiVersion: argoproj.io/v1alpha1
|
||||||
kind: Application
|
kind: Application
|
||||||
metadata:
|
metadata:
|
||||||
@@ -1479,17 +1483,15 @@ metadata:
|
|||||||
argocd.argoproj.io/sync-wave: "1"
|
argocd.argoproj.io/sync-wave: "1"
|
||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
sources:
|
source:
|
||||||
- repoURL: https://charts.bitnami.com/bitnami
|
repoURL: https://charts.bitnami.com/bitnami
|
||||||
chart: redis
|
chart: redis
|
||||||
targetRevision: 18.0.0
|
targetRevision: 18.0.0
|
||||||
helm:
|
helm:
|
||||||
releaseName: redis
|
values: |
|
||||||
valueFiles:
|
auth:
|
||||||
- \$values/infra/values/base/redis-values.yaml
|
enabled: true
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
password: changeme
|
||||||
targetRevision: HEAD
|
|
||||||
ref: values
|
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
namespace: redis
|
namespace: redis
|
||||||
@@ -1501,54 +1503,30 @@ spec:
|
|||||||
- CreateNamespace=true
|
- CreateNamespace=true
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# 2. Add to base kustomization
|
# 2. Commit and push
|
||||||
# Edit infra/base/kustomization.yaml and add: - redis-application.yaml
|
git add infra/redis-application.yaml
|
||||||
|
|
||||||
# 3. Create base values file
|
|
||||||
cat > infra/values/base/redis-values.yaml <<EOF
|
|
||||||
auth:
|
|
||||||
enabled: true
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# 4. Commit and push
|
|
||||||
git add infra/base/redis-application.yaml infra/values/base/redis-values.yaml infra/base/kustomization.yaml
|
|
||||||
git commit -m "Add Redis infrastructure component"
|
git commit -m "Add Redis infrastructure component"
|
||||||
git push
|
git push
|
||||||
|
|
||||||
# 5. ArgoCD will auto-sync within 60 seconds
|
# 3. ArgoCD will auto-sync within 60 seconds
|
||||||
```
|
```
|
||||||
|
|
||||||
### Multi-Cluster Setup
|
### Multi-Cluster Setup (Future)
|
||||||
|
|
||||||
The repository supports multiple clusters across multiple clouds via Kustomize overlays:
|
For multi-cluster deployments:
|
||||||
|
|
||||||
**Active clusters:**
|
```yaml
|
||||||
- **upc-dev** (default): `infra/overlays/upc-dev/` — uses base Applications as-is
|
# Different destinations per environment
|
||||||
- **upc-prod**: `infra/overlays/upc-prod/` — patches value file paths from `upc-dev` to `upc-prod`
|
# dev-cluster
|
||||||
|
destination:
|
||||||
|
server: https://dev.k8s.example.com
|
||||||
|
namespace: myapp
|
||||||
|
|
||||||
**Cloud-ready templates (fill in `clusters/*.yaml` before use):**
|
# prod-cluster
|
||||||
- **eks-dev** / **eks-prod**: AWS EKS with NLB, gp3 storage, AWS CUR pricing
|
destination:
|
||||||
- **aks-dev** / **aks-prod**: Azure AKS with Standard LB, managed-csi-premium storage
|
server: https://prod.k8s.example.com
|
||||||
- **gke-dev** / **gke-prod**: GCP GKE with L4 LB, premium-rwo storage
|
namespace: myapp
|
||||||
|
```
|
||||||
Each cluster has its own:
|
|
||||||
- Root app-of-apps: `_app-of-apps-{cluster}.yaml`
|
|
||||||
- Cluster config: `clusters/{cluster}.yaml` (domain, trustedIPs, cloudProvider)
|
|
||||||
- Kustomize overlay: `infra/overlays/{cluster}/kustomization.yaml`
|
|
||||||
- Helm value overrides: `infra/values/{cluster}/` (traefik, gitea, opencost)
|
|
||||||
- Sealed secrets: `secrets/{cluster}/` (as needed)
|
|
||||||
- Apps overlay: `apps/overlays/{cluster}/`
|
|
||||||
|
|
||||||
Cloud-specific values handled per-cluster:
|
|
||||||
|
|
||||||
| Concern | UpCloud | AWS EKS | Azure AKS | GCP GKE |
|
|
||||||
|---------|---------|---------|-----------|---------|
|
|
||||||
| **Storage class** | `upcloud-block-storage-maxiops` | `gp3` | `managed-csi-premium` | `premium-rwo` |
|
|
||||||
| **Load balancer** | UpCloud LB + ProxyProtocol v2 | NLB + ProxyProtocol v2 | Standard LB + `externalTrafficPolicy: Local` | L4 passthrough NLB |
|
|
||||||
| **Cost monitoring** | Custom pricing | AWS CUR | Azure Billing API | GCP Cloud Billing |
|
|
||||||
| **Backup storage** | UpCloud S3-compat | AWS S3 (native) | Azure Blob Storage | GCS |
|
|
||||||
|
|
||||||
To add a new cluster, create a new overlay directory (e.g., `infra/overlays/eks-staging/`) with patches that swap the value file paths, and a matching `clusters/eks-staging.yaml`.
|
|
||||||
|
|
||||||
### Blue-Green Deployments
|
### Blue-Green Deployments
|
||||||
|
|
||||||
@@ -1592,7 +1570,7 @@ git push
|
|||||||
kubectl scale deployment myapp -n myapp --replicas=0
|
kubectl scale deployment myapp -n myapp --replicas=0
|
||||||
|
|
||||||
# Update Git
|
# Update Git
|
||||||
vim helm-prod-values/myapp/values.yaml
|
vim helm-values/myapp/values.yaml
|
||||||
# Set replicaCount: 0
|
# Set replicaCount: 0
|
||||||
git commit -am "Scale down myapp for maintenance"
|
git commit -am "Scale down myapp for maintenance"
|
||||||
git push
|
git push
|
||||||
@@ -1665,7 +1643,7 @@ echo "Remember to delete: $SECRET_FILE"
|
|||||||
|
|
||||||
- [ ] Application code repository created
|
- [ ] Application code repository created
|
||||||
- [ ] Dockerfile created and tested
|
- [ ] Dockerfile created and tested
|
||||||
- [ ] Gitea Actions workflow configured
|
- [ ] GitHub Actions workflow configured
|
||||||
- [ ] Helm values created in `helm-prod-values/`
|
- [ ] Helm values created in `helm-prod-values/`
|
||||||
- [ ] ArgoCD application manifest created in `apps/`
|
- [ ] ArgoCD application manifest created in `apps/`
|
||||||
- [ ] Secrets created and sealed
|
- [ ] Secrets created and sealed
|
||||||
@@ -1691,6 +1669,6 @@ echo "Remember to delete: $SECRET_FILE"
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: 2026-04-22
|
**Last Updated**: 2026-03-16
|
||||||
**Maintained By**: Platform Team
|
**Maintained By**: Platform Team
|
||||||
**Emergency Contact**: #platform-support on Slack
|
**Emergency Contact**: #platform-support on Slack
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ Reference for:
|
|||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
┌──────────────────────────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────────────┐
|
||||||
│ Kubernetes Clusters (UpCloud, AWS, Azure, GCP) │
|
│ Kubernetes Cluster (UpCloud) │
|
||||||
│ ┌──────────────────────────────────────────────────────┐ │
|
│ ┌──────────────────────────────────────────────────────┐ │
|
||||||
│ │ Infrastructure: Traefik, Cert-Manager, Kyverno │ │
|
│ │ Infrastructure: Traefik, Cert-Manager, Kyverno │ │
|
||||||
│ ├──────────────────────────────────────────────────────┤ │
|
│ ├──────────────────────────────────────────────────────┤ │
|
||||||
@@ -194,7 +194,7 @@ Reference for:
|
|||||||
### Key Technologies
|
### Key Technologies
|
||||||
|
|
||||||
- **GitOps**: ArgoCD
|
- **GitOps**: ArgoCD
|
||||||
- **Kubernetes**: Multi-cloud (UpCloud, AWS EKS, Azure AKS, GCP GKE)
|
- **Kubernetes**: UpCloud Managed Kubernetes
|
||||||
- **Ingress**: Traefik v2
|
- **Ingress**: Traefik v2
|
||||||
- **Certificates**: Cert-Manager + Let's Encrypt
|
- **Certificates**: Cert-Manager + Let's Encrypt
|
||||||
- **Policies**: Kyverno
|
- **Policies**: Kyverno
|
||||||
@@ -299,16 +299,11 @@ docs/
|
|||||||
## 🔄 Documentation Versions
|
## 🔄 Documentation Versions
|
||||||
|
|
||||||
**Current Version**: 1.0.0
|
**Current Version**: 1.0.0
|
||||||
**Last Updated**: 2026-04-22
|
**Last Updated**: 2026-03-16
|
||||||
**Maintained By**: Platform Team
|
**Maintained By**: Platform Team
|
||||||
|
|
||||||
### Changelog
|
### Changelog
|
||||||
|
|
||||||
- **v1.1.0 (2026-04-22)**: Multi-cloud support
|
|
||||||
- Cloud-agnostic base values (storage, LB, pricing moved to per-cluster overlays)
|
|
||||||
- Added AWS EKS, Azure AKS, GCP GKE configurations
|
|
||||||
- Per-cloud backup scripts
|
|
||||||
- Updated all documentation
|
|
||||||
- **v1.0.0 (2026-03-16)**: Initial comprehensive documentation release
|
- **v1.0.0 (2026-03-16)**: Initial comprehensive documentation release
|
||||||
- GitOps Architecture guide
|
- GitOps Architecture guide
|
||||||
- Developer Onboarding guide
|
- Developer Onboarding guide
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -48,10 +48,10 @@ spec:
|
|||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
cpu: 50m
|
cpu: 50m
|
||||||
memory: 128Mi
|
memory: 64Mi
|
||||||
limits:
|
limits:
|
||||||
cpu: 100m
|
cpu: 100m
|
||||||
memory: 256Mi
|
memory: 128Mi
|
||||||
|
|
||||||
# Service account
|
# Service account
|
||||||
serviceAccount:
|
serviceAccount:
|
||||||
|
|||||||
@@ -15,11 +15,9 @@ spec:
|
|||||||
project: default
|
project: default
|
||||||
|
|
||||||
source:
|
source:
|
||||||
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
path: cluster-resources
|
path: cluster-resources
|
||||||
directory:
|
|
||||||
exclude: 'network'
|
|
||||||
|
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
source:
|
source:
|
||||||
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
path: apps/overlays/upc-dev
|
path: apps/overlays/eu
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
namespace: apps
|
namespace: apps
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/infra/values/base/fluent-bit-values.yaml
|
- $values/infra/values/base/fluent-bit-values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: gitea-actions
|
|
||||||
namespace: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "2"
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: gitea-actions
|
|
||||||
app.kubernetes.io/part-of: platform
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
finalizers:
|
|
||||||
- resources-finalizer.argocd.argoproj.io
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
|
|
||||||
sources:
|
|
||||||
- repoURL: https://dl.gitea.com/charts
|
|
||||||
chart: actions
|
|
||||||
targetRevision: "0.0.5"
|
|
||||||
helm:
|
|
||||||
releaseName: gitea-actions
|
|
||||||
valueFiles:
|
|
||||||
- $values/infra/values/base/gitea-actions-values.yaml
|
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
ref: values
|
|
||||||
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: gitea
|
|
||||||
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
allowEmpty: false
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
- Validate=true
|
|
||||||
- ServerSideApply=true
|
|
||||||
|
|
||||||
ignoreDifferences:
|
|
||||||
- group: apps
|
|
||||||
kind: StatefulSet
|
|
||||||
jsonPointers:
|
|
||||||
- /spec/volumeClaimTemplates
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: gitea
|
|
||||||
namespace: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "1"
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: gitea
|
|
||||||
app.kubernetes.io/part-of: platform
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
finalizers:
|
|
||||||
- resources-finalizer.argocd.argoproj.io
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
|
|
||||||
sources:
|
|
||||||
- repoURL: https://dl.gitea.com/charts
|
|
||||||
chart: gitea
|
|
||||||
targetRevision: "12.5.0"
|
|
||||||
helm:
|
|
||||||
releaseName: gitea
|
|
||||||
valueFiles:
|
|
||||||
- $values/infra/values/base/gitea-values.yaml
|
|
||||||
- $values/infra/values/upc-dev/gitea-values.yaml
|
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
ref: values
|
|
||||||
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: gitea
|
|
||||||
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
allowEmpty: false
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
- Validate=true
|
|
||||||
- ServerSideApply=true
|
|
||||||
|
|
||||||
ignoreDifferences:
|
|
||||||
- group: apps
|
|
||||||
kind: StatefulSet
|
|
||||||
jsonPointers:
|
|
||||||
- /spec/volumeClaimTemplates
|
|
||||||
- group: v1
|
|
||||||
kind: Secret
|
|
||||||
jsonPointers:
|
|
||||||
- /data/postgres-password
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: grafana-dashboards
|
|
||||||
namespace: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "2"
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: grafana-dashboards
|
|
||||||
app.kubernetes.io/part-of: monitoring
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
finalizers:
|
|
||||||
- resources-finalizer.argocd.argoproj.io
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
|
|
||||||
source:
|
|
||||||
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
path: infra/dashboards
|
|
||||||
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: monitoring
|
|
||||||
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
allowEmpty: false
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
- Validate=true
|
|
||||||
- ServerSideApply=true
|
|
||||||
@@ -22,9 +22,9 @@ spec:
|
|||||||
releaseName: grafana
|
releaseName: grafana
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/infra/values/base/grafana-values.yaml
|
- $values/infra/values/base/grafana-values.yaml
|
||||||
- $values/infra/values/upc-dev/grafana-values.yaml
|
- $values/infra/values/eu/grafana-values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: karpor
|
|
||||||
namespace: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "1"
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: karpor
|
|
||||||
app.kubernetes.io/part-of: developer-portal
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
finalizers:
|
|
||||||
- resources-finalizer.argocd.argoproj.io
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
|
|
||||||
sources:
|
|
||||||
- repoURL: https://kusionstack.github.io/charts
|
|
||||||
chart: karpor
|
|
||||||
targetRevision: "0.7.6"
|
|
||||||
helm:
|
|
||||||
releaseName: karpor
|
|
||||||
valueFiles:
|
|
||||||
- $values/infra/values/base/karpor-values.yaml
|
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
ref: values
|
|
||||||
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: karpor
|
|
||||||
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
allowEmpty: false
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
- Validate=true
|
|
||||||
- ServerSideApply=true
|
|
||||||
|
|
||||||
ignoreDifferences:
|
|
||||||
- group: apps
|
|
||||||
kind: StatefulSet
|
|
||||||
jsonPointers:
|
|
||||||
- /spec/volumeClaimTemplates
|
|
||||||
@@ -15,16 +15,16 @@ spec:
|
|||||||
project: default
|
project: default
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- repoURL: registry-1.docker.io/bitnamicharts
|
- repoURL: https://charts.bitnami.com/bitnami
|
||||||
chart: keycloak
|
chart: keycloak
|
||||||
targetRevision: "25.2.0"
|
targetRevision: "25.2.0"
|
||||||
helm:
|
helm:
|
||||||
releaseName: keycloak
|
releaseName: keycloak
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/infra/values/base/keycloak-values.yaml
|
- $values/infra/values/base/keycloak-values.yaml
|
||||||
- $values/infra/values/upc-dev/keycloak-values.yaml
|
- $values/infra/values/eu/keycloak-values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
@@ -41,13 +41,3 @@ spec:
|
|||||||
- CreateNamespace=true
|
- CreateNamespace=true
|
||||||
- Validate=true
|
- Validate=true
|
||||||
- ServerSideApply=true
|
- ServerSideApply=true
|
||||||
|
|
||||||
ignoreDifferences:
|
|
||||||
- group: batch
|
|
||||||
kind: CronJob
|
|
||||||
jsonPointers:
|
|
||||||
- /spec/jobTemplate/spec/template/spec/containers/0/args
|
|
||||||
- group: apps
|
|
||||||
kind: StatefulSet
|
|
||||||
jsonPointers:
|
|
||||||
- /spec/volumeClaimTemplates
|
|
||||||
|
|||||||
@@ -10,15 +10,8 @@ resources:
|
|||||||
- prometheus.yaml
|
- prometheus.yaml
|
||||||
- loki.yaml
|
- loki.yaml
|
||||||
- fluent-bit.yaml
|
- fluent-bit.yaml
|
||||||
|
- trivy.yaml
|
||||||
- enterprise-apps.yaml
|
- enterprise-apps.yaml
|
||||||
- cluster-resources-application.yaml
|
- cluster-resources-application.yaml
|
||||||
- kyverno-policies.yaml
|
- kyverno-policies.yaml
|
||||||
- secrets.yaml
|
- secrets.yaml
|
||||||
- gitea.yaml
|
|
||||||
- gitea-actions.yaml
|
|
||||||
- opencost.yaml
|
|
||||||
- renovate.yaml
|
|
||||||
- tempo.yaml
|
|
||||||
- grafana-dashboards.yaml
|
|
||||||
- network-policies-application.yaml
|
|
||||||
- karpor.yaml
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
project: default
|
project: default
|
||||||
|
|
||||||
source:
|
source:
|
||||||
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
path: cluster-resources/policies
|
path: cluster-resources/policies
|
||||||
|
|
||||||
|
|||||||
@@ -39,10 +39,6 @@ spec:
|
|||||||
targetRevision: v3.7.0 # Update to latest stable version
|
targetRevision: v3.7.0 # Update to latest stable version
|
||||||
helm:
|
helm:
|
||||||
releaseName: kyverno
|
releaseName: kyverno
|
||||||
valuesObject:
|
|
||||||
grafana:
|
|
||||||
enabled: true
|
|
||||||
namespace: monitoring
|
|
||||||
|
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/infra/values/base/loki-values.yaml
|
- $values/infra/values/base/loki-values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: network-policies
|
|
||||||
namespace: argocd
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: network-policies
|
|
||||||
app.kubernetes.io/part-of: platform
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "1"
|
|
||||||
finalizers:
|
|
||||||
- resources-finalizer.argocd.argoproj.io
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
|
|
||||||
source:
|
|
||||||
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
path: cluster-resources/network
|
|
||||||
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
allowEmpty: false
|
|
||||||
|
|
||||||
syncOptions:
|
|
||||||
- Validate=true
|
|
||||||
- ServerSideApply=true
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: opencost
|
|
||||||
namespace: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "1"
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: opencost
|
|
||||||
app.kubernetes.io/part-of: monitoring
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
finalizers:
|
|
||||||
- resources-finalizer.argocd.argoproj.io
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
|
|
||||||
sources:
|
|
||||||
- repoURL: https://opencost.github.io/opencost-helm-chart
|
|
||||||
chart: opencost
|
|
||||||
targetRevision: "1.42.0"
|
|
||||||
helm:
|
|
||||||
releaseName: opencost
|
|
||||||
valueFiles:
|
|
||||||
- $values/infra/values/base/opencost-values.yaml
|
|
||||||
- $values/infra/values/upc-dev/opencost-values.yaml
|
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
ref: values
|
|
||||||
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: monitoring
|
|
||||||
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
allowEmpty: false
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
- Validate=true
|
|
||||||
- ServerSideApply=true
|
|
||||||
@@ -23,7 +23,7 @@ spec:
|
|||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/infra/values/base/prometheus-values.yaml
|
- $values/infra/values/base/prometheus-values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: renovate
|
|
||||||
namespace: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "2"
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: renovate
|
|
||||||
app.kubernetes.io/part-of: platform
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
finalizers:
|
|
||||||
- resources-finalizer.argocd.argoproj.io
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
|
|
||||||
sources:
|
|
||||||
- repoURL: ghcr.io/renovatebot/charts
|
|
||||||
chart: renovate
|
|
||||||
targetRevision: "46.109.0"
|
|
||||||
helm:
|
|
||||||
releaseName: renovate
|
|
||||||
valueFiles:
|
|
||||||
- $values/infra/values/base/renovate-values.yaml
|
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
ref: values
|
|
||||||
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: renovate
|
|
||||||
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
allowEmpty: false
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
- Validate=true
|
|
||||||
- ServerSideApply=true
|
|
||||||
@@ -17,8 +17,8 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
project: default
|
project: default
|
||||||
source:
|
source:
|
||||||
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
path: secrets/overlays/upc-dev
|
path: secrets/eu
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
namespace: secrets
|
namespace: secrets
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: tempo
|
|
||||||
namespace: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "1"
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: tempo
|
|
||||||
app.kubernetes.io/part-of: monitoring
|
|
||||||
app.kubernetes.io/managed-by: argocd
|
|
||||||
finalizers:
|
|
||||||
- resources-finalizer.argocd.argoproj.io
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
|
|
||||||
sources:
|
|
||||||
- repoURL: https://grafana.github.io/helm-charts
|
|
||||||
chart: tempo
|
|
||||||
targetRevision: "1.24.4"
|
|
||||||
helm:
|
|
||||||
releaseName: tempo
|
|
||||||
valueFiles:
|
|
||||||
- $values/infra/values/base/tempo-values.yaml
|
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
|
||||||
targetRevision: HEAD
|
|
||||||
ref: values
|
|
||||||
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: monitoring
|
|
||||||
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
allowEmpty: false
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
- Validate=true
|
|
||||||
- ServerSideApply=true
|
|
||||||
@@ -29,9 +29,9 @@ spec:
|
|||||||
releaseName: traefik
|
releaseName: traefik
|
||||||
valueFiles:
|
valueFiles:
|
||||||
- $values/infra/values/base/traefik-values.yaml
|
- $values/infra/values/base/traefik-values.yaml
|
||||||
- $values/infra/values/upc-dev/traefik-values.yaml
|
- $values/infra/values/eu/traefik-values.yaml
|
||||||
|
|
||||||
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
|
- repoURL: git@github.com:fortedigital/sturdy-adventure.git
|
||||||
targetRevision: HEAD
|
targetRevision: HEAD
|
||||||
ref: values
|
ref: values
|
||||||
|
|
||||||
|
|||||||
67
infra/base/trivy.yaml
Normal file
67
infra/base/trivy.yaml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: trivy-system
|
||||||
|
annotations:
|
||||||
|
argocd.argoproj.io/sync-wave: "-1"
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: Application
|
||||||
|
metadata:
|
||||||
|
name: trivy-operator
|
||||||
|
namespace: argocd
|
||||||
|
annotations:
|
||||||
|
argocd.argoproj.io/sync-wave: "0"
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: trivy-operator
|
||||||
|
app.kubernetes.io/part-of: platform
|
||||||
|
app.kubernetes.io/managed-by: argocd
|
||||||
|
finalizers:
|
||||||
|
- resources-finalizer.argocd.argoproj.io
|
||||||
|
spec:
|
||||||
|
project: default
|
||||||
|
|
||||||
|
source:
|
||||||
|
repoURL: https://aquasecurity.github.io/helm-charts
|
||||||
|
chart: trivy-operator
|
||||||
|
targetRevision: 0.31.0
|
||||||
|
helm:
|
||||||
|
releaseName: trivy-operator
|
||||||
|
valuesObject:
|
||||||
|
operator:
|
||||||
|
targetNamespaces: ""
|
||||||
|
excludeNamespaces: "argocd,trivy-system,kube-system,monitoring,kyverno,cert-manager"
|
||||||
|
scanJobsInSameNamespace: true
|
||||||
|
metricsVulnIdEnabled: true
|
||||||
|
metricsImageInfo: true
|
||||||
|
trivy:
|
||||||
|
ignoreUnfixed: false
|
||||||
|
|
||||||
|
destination:
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
namespace: trivy-system
|
||||||
|
|
||||||
|
syncPolicy:
|
||||||
|
automated:
|
||||||
|
prune: true
|
||||||
|
selfHeal: true
|
||||||
|
allowEmpty: false
|
||||||
|
syncOptions:
|
||||||
|
- CreateNamespace=true
|
||||||
|
- Validate=true
|
||||||
|
- ServerSideApply=true
|
||||||
|
retry:
|
||||||
|
limit: 5
|
||||||
|
backoff:
|
||||||
|
duration: 5s
|
||||||
|
factor: 2
|
||||||
|
maxDuration: 3m
|
||||||
|
|
||||||
|
ignoreDifferences:
|
||||||
|
- group: apiextensions.k8s.io
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
jsonPointers:
|
||||||
|
- /metadata/labels
|
||||||
|
- /metadata/annotations
|
||||||
|
- /metadata/finalizers
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
{
|
|
||||||
"annotations": {
|
|
||||||
"list": []
|
|
||||||
},
|
|
||||||
"editable": true,
|
|
||||||
"fiscalYearStartMonth": 0,
|
|
||||||
"graphTooltip": 0,
|
|
||||||
"links": [],
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"title": "Log Volume",
|
|
||||||
"type": "timeseries",
|
|
||||||
"gridPos": {
|
|
||||||
"h": 6,
|
|
||||||
"w": 24,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"datasource": {
|
|
||||||
"type": "loki",
|
|
||||||
"uid": "loki"
|
|
||||||
},
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(count_over_time({namespace=\"dot-ai\"} [1m])) by (pod)",
|
|
||||||
"legendFormat": "{{pod}}",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"custom": {
|
|
||||||
"drawStyle": "bars",
|
|
||||||
"fillOpacity": 50,
|
|
||||||
"stacking": {
|
|
||||||
"mode": "normal"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Logs by Pod",
|
|
||||||
"type": "logs",
|
|
||||||
"gridPos": {
|
|
||||||
"h": 16,
|
|
||||||
"w": 24,
|
|
||||||
"x": 0,
|
|
||||||
"y": 6
|
|
||||||
},
|
|
||||||
"datasource": {
|
|
||||||
"type": "loki",
|
|
||||||
"uid": "loki"
|
|
||||||
},
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "{namespace=\"dot-ai\", pod=~\"$pod\"} | json log=\"log\", message=\"message\", msg=\"msg\", level=\"level\", stream=\"stream\" | label_format level=`{{if .level}}{{.level}}{{else if eq .stream \"stderr\"}}error{{else}}info{{end}}` | line_format `{{.pod}} |{{or .message .msg .log}}`",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"options": {
|
|
||||||
"showTime": true,
|
|
||||||
"showLabels": false,
|
|
||||||
"showCommonLabels": false,
|
|
||||||
"wrapLogMessage": true,
|
|
||||||
"prettifyLogMessage": false,
|
|
||||||
"enableLogDetails": true,
|
|
||||||
"sortOrder": "Descending",
|
|
||||||
"dedupStrategy": "none",
|
|
||||||
"displayedFields": [
|
|
||||||
"pod",
|
|
||||||
"level"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Errors & Warnings",
|
|
||||||
"type": "logs",
|
|
||||||
"gridPos": {
|
|
||||||
"h": 10,
|
|
||||||
"w": 24,
|
|
||||||
"x": 0,
|
|
||||||
"y": 22
|
|
||||||
},
|
|
||||||
"datasource": {
|
|
||||||
"type": "loki",
|
|
||||||
"uid": "loki"
|
|
||||||
},
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "{namespace=\"dot-ai\", pod=~\"$pod\"} | json log=\"log\", message=\"message\", msg=\"msg\", level=\"level\", stream=\"stream\" | label_format level=`{{if .level}}{{.level}}{{else if eq .stream \"stderr\"}}error{{else}}info{{end}}` | level=~`error|warn|warning|fatal|panic` | line_format `{{.pod}} |{{or .message .msg .log}}`",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"options": {
|
|
||||||
"showTime": true,
|
|
||||||
"showLabels": false,
|
|
||||||
"showCommonLabels": false,
|
|
||||||
"wrapLogMessage": true,
|
|
||||||
"prettifyLogMessage": false,
|
|
||||||
"enableLogDetails": true,
|
|
||||||
"sortOrder": "Descending",
|
|
||||||
"dedupStrategy": "none",
|
|
||||||
"displayedFields": [
|
|
||||||
"pod",
|
|
||||||
"level"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"schemaVersion": 39,
|
|
||||||
"tags": [
|
|
||||||
"dot-ai",
|
|
||||||
"logs",
|
|
||||||
"loki"
|
|
||||||
],
|
|
||||||
"templating": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"name": "pod",
|
|
||||||
"type": "query",
|
|
||||||
"datasource": {
|
|
||||||
"type": "loki",
|
|
||||||
"uid": "loki"
|
|
||||||
},
|
|
||||||
"query": {
|
|
||||||
"label": "pod",
|
|
||||||
"stream": "{namespace=\"dot-ai\"}",
|
|
||||||
"type": 1
|
|
||||||
},
|
|
||||||
"includeAll": true,
|
|
||||||
"multi": true,
|
|
||||||
"current": {
|
|
||||||
"selected": true,
|
|
||||||
"text": "All",
|
|
||||||
"value": "$__all"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"time": {
|
|
||||||
"from": "now-1h",
|
|
||||||
"to": "now"
|
|
||||||
},
|
|
||||||
"title": "dot-ai Logs",
|
|
||||||
"uid": "dot-ai-logs"
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
namespace: monitoring
|
|
||||||
|
|
||||||
generatorOptions:
|
|
||||||
disableNameSuffixHash: true
|
|
||||||
labels:
|
|
||||||
grafana_dashboard: "1"
|
|
||||||
|
|
||||||
configMapGenerator:
|
|
||||||
- name: grafana-dashboard-traefik-loki
|
|
||||||
files:
|
|
||||||
- traefik-loki.json
|
|
||||||
- name: grafana-dashboard-dot-ai-logs
|
|
||||||
files:
|
|
||||||
- dot-ai-logs.json
|
|
||||||
- name: grafana-dashboard-opencost
|
|
||||||
files:
|
|
||||||
- opencost.json
|
|
||||||
- name: grafana-dashboard-pod-security
|
|
||||||
files:
|
|
||||||
- pod-security.json
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,399 +0,0 @@
|
|||||||
{
|
|
||||||
"annotations": {
|
|
||||||
"list": []
|
|
||||||
},
|
|
||||||
"editable": true,
|
|
||||||
"fiscalYearStartMonth": 0,
|
|
||||||
"graphTooltip": 1,
|
|
||||||
"links": [],
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"title": "Enforced Denials",
|
|
||||||
"description": "Pods rejected by Pod Security Standards (enforce mode)",
|
|
||||||
"type": "stat",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 5, "w": 6, "x": 0, "y": 0 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(increase(pod_security_evaluations_total{decision=\"deny\", mode=\"enforce\"}[$__range])) or vector(0)",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"noValue": "0",
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{ "value": null, "color": "green" },
|
|
||||||
{ "value": 1, "color": "red" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"reduceOptions": { "calcs": ["lastNotNull"] },
|
|
||||||
"colorMode": "background",
|
|
||||||
"textMode": "auto"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Audit Violations",
|
|
||||||
"description": "Pods that violate audit-level policy (allowed but logged)",
|
|
||||||
"type": "stat",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 5, "w": 6, "x": 6, "y": 0 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(increase(pod_security_evaluations_total{decision=\"deny\", mode=\"audit\"}[$__range])) or vector(0)",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"noValue": "0",
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{ "value": null, "color": "green" },
|
|
||||||
{ "value": 1, "color": "orange" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"reduceOptions": { "calcs": ["lastNotNull"] },
|
|
||||||
"colorMode": "background",
|
|
||||||
"textMode": "auto"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Warnings",
|
|
||||||
"description": "Pods that triggered warn-level policy (allowed with warning)",
|
|
||||||
"type": "stat",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 5, "w": 6, "x": 12, "y": 0 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(increase(pod_security_evaluations_total{decision=\"deny\", mode=\"warn\"}[$__range])) or vector(0)",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"noValue": "0",
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{ "value": null, "color": "green" },
|
|
||||||
{ "value": 1, "color": "yellow" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"reduceOptions": { "calcs": ["lastNotNull"] },
|
|
||||||
"colorMode": "background",
|
|
||||||
"textMode": "auto"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Total Evaluations",
|
|
||||||
"description": "All pod security evaluations across all modes",
|
|
||||||
"type": "stat",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 5, "w": 6, "x": 18, "y": 0 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(increase(pod_security_evaluations_total[$__range])) or vector(0)",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"noValue": "0",
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{ "value": null, "color": "blue" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"reduceOptions": { "calcs": ["lastNotNull"] },
|
|
||||||
"colorMode": "background",
|
|
||||||
"textMode": "auto"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Violation Rate by Mode",
|
|
||||||
"description": "Rate of policy violations over time, grouped by enforcement mode",
|
|
||||||
"type": "timeseries",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 5 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(rate(pod_security_evaluations_total{decision=\"deny\", mode=\"enforce\"}[5m]))",
|
|
||||||
"legendFormat": "enforce (denied)",
|
|
||||||
"refId": "A"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "sum(rate(pod_security_evaluations_total{decision=\"deny\", mode=\"audit\"}[5m]))",
|
|
||||||
"legendFormat": "audit",
|
|
||||||
"refId": "B"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"expr": "sum(rate(pod_security_evaluations_total{decision=\"deny\", mode=\"warn\"}[5m]))",
|
|
||||||
"legendFormat": "warn",
|
|
||||||
"refId": "C"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"custom": {
|
|
||||||
"drawStyle": "line",
|
|
||||||
"lineWidth": 2,
|
|
||||||
"fillOpacity": 15,
|
|
||||||
"pointSize": 5,
|
|
||||||
"showPoints": "auto"
|
|
||||||
},
|
|
||||||
"unit": "ops"
|
|
||||||
},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"matcher": { "id": "byName", "options": "enforce (denied)" },
|
|
||||||
"properties": [{ "id": "color", "value": { "fixedColor": "red", "mode": "fixed" } }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"matcher": { "id": "byName", "options": "audit" },
|
|
||||||
"properties": [{ "id": "color", "value": { "fixedColor": "orange", "mode": "fixed" } }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"matcher": { "id": "byName", "options": "warn" },
|
|
||||||
"properties": [{ "id": "color", "value": { "fixedColor": "yellow", "mode": "fixed" } }]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Violations by Policy Level",
|
|
||||||
"description": "Violation rate grouped by the PSS level that was violated",
|
|
||||||
"type": "timeseries",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 5 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(rate(pod_security_evaluations_total{decision=\"deny\"}[5m])) by (policy_level)",
|
|
||||||
"legendFormat": "{{ policy_level }}",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"custom": {
|
|
||||||
"drawStyle": "line",
|
|
||||||
"lineWidth": 2,
|
|
||||||
"fillOpacity": 15,
|
|
||||||
"pointSize": 5,
|
|
||||||
"showPoints": "auto"
|
|
||||||
},
|
|
||||||
"unit": "ops"
|
|
||||||
},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"matcher": { "id": "byName", "options": "restricted" },
|
|
||||||
"properties": [{ "id": "color", "value": { "fixedColor": "yellow", "mode": "fixed" } }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"matcher": { "id": "byName", "options": "baseline" },
|
|
||||||
"properties": [{ "id": "color", "value": { "fixedColor": "orange", "mode": "fixed" } }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"matcher": { "id": "byName", "options": "privileged" },
|
|
||||||
"properties": [{ "id": "color", "value": { "fixedColor": "red", "mode": "fixed" } }]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Enforced Denials by Namespace",
|
|
||||||
"description": "Pods blocked per namespace (enforce mode only)",
|
|
||||||
"type": "timeseries",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 13 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(rate(pod_security_evaluations_total{decision=\"deny\", mode=\"enforce\"}[5m])) by (resource_namespace)",
|
|
||||||
"legendFormat": "{{ resource_namespace }}",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"custom": {
|
|
||||||
"drawStyle": "bars",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"fillOpacity": 80,
|
|
||||||
"stacking": { "mode": "normal" }
|
|
||||||
},
|
|
||||||
"unit": "ops"
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Audit + Warn Violations by Namespace",
|
|
||||||
"description": "Non-enforced violations per namespace — candidates for tightening",
|
|
||||||
"type": "timeseries",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 13 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(rate(pod_security_evaluations_total{decision=\"deny\", mode=~\"audit|warn\"}[5m])) by (resource_namespace)",
|
|
||||||
"legendFormat": "{{ resource_namespace }}",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"custom": {
|
|
||||||
"drawStyle": "bars",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"fillOpacity": 80,
|
|
||||||
"stacking": { "mode": "normal" }
|
|
||||||
},
|
|
||||||
"unit": "ops"
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Violations Breakdown",
|
|
||||||
"description": "Detailed breakdown of all policy violations",
|
|
||||||
"type": "table",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 10, "w": 24, "x": 0, "y": 21 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(increase(pod_security_evaluations_total{decision=\"deny\"}[$__range])) by (resource_namespace, policy_level, mode, request_operation) > 0",
|
|
||||||
"format": "table",
|
|
||||||
"instant": true,
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"transformations": [
|
|
||||||
{
|
|
||||||
"id": "organize",
|
|
||||||
"options": {
|
|
||||||
"excludeByName": { "Time": true },
|
|
||||||
"renameByName": {
|
|
||||||
"resource_namespace": "Namespace",
|
|
||||||
"policy_level": "Policy Level",
|
|
||||||
"mode": "Mode",
|
|
||||||
"request_operation": "Operation",
|
|
||||||
"Value": "Violations"
|
|
||||||
},
|
|
||||||
"indexByName": {
|
|
||||||
"resource_namespace": 0,
|
|
||||||
"policy_level": 1,
|
|
||||||
"mode": 2,
|
|
||||||
"request_operation": 3,
|
|
||||||
"Value": 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sortBy",
|
|
||||||
"options": {
|
|
||||||
"fields": {},
|
|
||||||
"sort": [
|
|
||||||
{ "field": "Violations", "desc": true }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"matcher": { "id": "byName", "options": "Mode" },
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"id": "mappings",
|
|
||||||
"value": [
|
|
||||||
{ "type": "value", "options": { "enforce": { "text": "Enforce", "color": "red" }, "audit": { "text": "Audit", "color": "orange" }, "warn": { "text": "Warn", "color": "yellow" } } }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"matcher": { "id": "byName", "options": "Violations" },
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"id": "custom.cellOptions",
|
|
||||||
"value": { "type": "color-background", "mode": "gradient" }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "thresholds",
|
|
||||||
"value": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{ "value": null, "color": "transparent" },
|
|
||||||
{ "value": 1, "color": "orange" },
|
|
||||||
{ "value": 100, "color": "red" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Exemptions",
|
|
||||||
"description": "Pods exempted from policy evaluation",
|
|
||||||
"type": "timeseries",
|
|
||||||
"datasource": { "type": "prometheus" },
|
|
||||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 31 },
|
|
||||||
"targets": [
|
|
||||||
{
|
|
||||||
"expr": "sum(rate(pod_security_exemptions_total[5m])) by (request_namespace)",
|
|
||||||
"legendFormat": "{{ request_namespace }}",
|
|
||||||
"refId": "A"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"custom": {
|
|
||||||
"drawStyle": "line",
|
|
||||||
"lineWidth": 2,
|
|
||||||
"fillOpacity": 10
|
|
||||||
},
|
|
||||||
"unit": "ops"
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"schemaVersion": 39,
|
|
||||||
"tags": [
|
|
||||||
"security",
|
|
||||||
"pod-security",
|
|
||||||
"pss",
|
|
||||||
"compliance"
|
|
||||||
],
|
|
||||||
"templating": {
|
|
||||||
"list": []
|
|
||||||
},
|
|
||||||
"time": {
|
|
||||||
"from": "now-24h",
|
|
||||||
"to": "now"
|
|
||||||
},
|
|
||||||
"title": "Pod Security Violations",
|
|
||||||
"uid": "pod-security-violations"
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,68 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
resources:
|
|
||||||
- ../../base
|
|
||||||
|
|
||||||
patches:
|
|
||||||
# Traefik: swap upc-dev → aks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: traefik
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-dev/traefik-values.yaml
|
|
||||||
|
|
||||||
# Keycloak: swap upc-dev → aks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: keycloak
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-dev/keycloak-values.yaml
|
|
||||||
|
|
||||||
# Grafana: swap upc-dev → aks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: grafana
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-dev/grafana-values.yaml
|
|
||||||
|
|
||||||
# Gitea: swap upc-dev → aks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: gitea
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-dev/gitea-values.yaml
|
|
||||||
|
|
||||||
# OpenCost: swap upc-dev → aks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: opencost
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-dev/opencost-values.yaml
|
|
||||||
|
|
||||||
# Secrets: change path to aks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: secrets
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: secrets/aks-dev
|
|
||||||
|
|
||||||
# Enterprise-apps: point to aks-dev overlay
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: enterprise-apps
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: apps/overlays/aks-dev
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
resources:
|
|
||||||
- ../../base
|
|
||||||
|
|
||||||
patches:
|
|
||||||
# Traefik: swap upc-dev → aks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: traefik
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-prod/traefik-values.yaml
|
|
||||||
|
|
||||||
# Keycloak: swap upc-dev → aks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: keycloak
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-prod/keycloak-values.yaml
|
|
||||||
|
|
||||||
# Grafana: swap upc-dev → aks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: grafana
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-prod/grafana-values.yaml
|
|
||||||
|
|
||||||
# Gitea: swap upc-dev → aks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: gitea
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-prod/gitea-values.yaml
|
|
||||||
|
|
||||||
# OpenCost: swap upc-dev → aks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: opencost
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/aks-prod/opencost-values.yaml
|
|
||||||
|
|
||||||
# Secrets: change path to aks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: secrets
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: secrets/aks-prod
|
|
||||||
|
|
||||||
# Enterprise-apps: point to aks-prod overlay
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: enterprise-apps
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: apps/overlays/aks-prod
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
resources:
|
|
||||||
- ../../base
|
|
||||||
|
|
||||||
patches:
|
|
||||||
# Traefik: swap upc-dev → eks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: traefik
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-dev/traefik-values.yaml
|
|
||||||
|
|
||||||
# Keycloak: swap upc-dev → eks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: keycloak
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-dev/keycloak-values.yaml
|
|
||||||
|
|
||||||
# Grafana: swap upc-dev → eks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: grafana
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-dev/grafana-values.yaml
|
|
||||||
|
|
||||||
# Gitea: swap upc-dev → eks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: gitea
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-dev/gitea-values.yaml
|
|
||||||
|
|
||||||
# OpenCost: swap upc-dev → eks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: opencost
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-dev/opencost-values.yaml
|
|
||||||
|
|
||||||
# Secrets: change path to eks-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: secrets
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: secrets/eks-dev
|
|
||||||
|
|
||||||
# Enterprise-apps: point to eks-dev overlay
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: enterprise-apps
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: apps/overlays/eks-dev
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
resources:
|
|
||||||
- ../../base
|
|
||||||
|
|
||||||
patches:
|
|
||||||
# Traefik: swap upc-dev → eks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: traefik
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-prod/traefik-values.yaml
|
|
||||||
|
|
||||||
# Keycloak: swap upc-dev → eks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: keycloak
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-prod/keycloak-values.yaml
|
|
||||||
|
|
||||||
# Grafana: swap upc-dev → eks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: grafana
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-prod/grafana-values.yaml
|
|
||||||
|
|
||||||
# Gitea: swap upc-dev → eks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: gitea
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-prod/gitea-values.yaml
|
|
||||||
|
|
||||||
# OpenCost: swap upc-dev → eks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: opencost
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/eks-prod/opencost-values.yaml
|
|
||||||
|
|
||||||
# Secrets: change path to eks-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: secrets
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: secrets/eks-prod
|
|
||||||
|
|
||||||
# Enterprise-apps: point to eks-prod overlay
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: enterprise-apps
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: apps/overlays/eks-prod
|
|
||||||
@@ -2,3 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
|||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
resources:
|
resources:
|
||||||
- ../../base
|
- ../../base
|
||||||
|
|
||||||
|
# No patches needed — base already has "eu" paths
|
||||||
|
# EU is the default/base cluster
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
resources:
|
|
||||||
- ../../base
|
|
||||||
|
|
||||||
patches:
|
|
||||||
# Traefik: swap upc-dev → gke-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: traefik
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-dev/traefik-values.yaml
|
|
||||||
|
|
||||||
# Keycloak: swap upc-dev → gke-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: keycloak
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-dev/keycloak-values.yaml
|
|
||||||
|
|
||||||
# Grafana: swap upc-dev → gke-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: grafana
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-dev/grafana-values.yaml
|
|
||||||
|
|
||||||
# Gitea: swap upc-dev → gke-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: gitea
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-dev/gitea-values.yaml
|
|
||||||
|
|
||||||
# OpenCost: swap upc-dev → gke-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: opencost
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-dev/opencost-values.yaml
|
|
||||||
|
|
||||||
# Secrets: change path to gke-dev
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: secrets
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: secrets/gke-dev
|
|
||||||
|
|
||||||
# Enterprise-apps: point to gke-dev overlay
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: enterprise-apps
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: apps/overlays/gke-dev
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
resources:
|
|
||||||
- ../../base
|
|
||||||
|
|
||||||
patches:
|
|
||||||
# Traefik: swap upc-dev → gke-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: traefik
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-prod/traefik-values.yaml
|
|
||||||
|
|
||||||
# Keycloak: swap upc-dev → gke-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: keycloak
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-prod/keycloak-values.yaml
|
|
||||||
|
|
||||||
# Grafana: swap upc-dev → gke-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: grafana
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-prod/grafana-values.yaml
|
|
||||||
|
|
||||||
# Gitea: swap upc-dev → gke-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: gitea
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-prod/gitea-values.yaml
|
|
||||||
|
|
||||||
# OpenCost: swap upc-dev → gke-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: opencost
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/gke-prod/opencost-values.yaml
|
|
||||||
|
|
||||||
# Secrets: change path to gke-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: secrets
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: secrets/gke-prod
|
|
||||||
|
|
||||||
# Enterprise-apps: point to gke-prod overlay
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: enterprise-apps
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: apps/overlays/gke-prod
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
# Gitea: swap upc-dev → upc-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: gitea
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/upc-prod/gitea-values.yaml
|
|
||||||
|
|
||||||
# OpenCost: swap upc-dev → upc-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: opencost
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/sources/0/helm/valueFiles/1
|
|
||||||
value: $values/infra/values/upc-prod/opencost-values.yaml
|
|
||||||
|
|
||||||
# Secrets: change path to upc-prod
|
|
||||||
- target:
|
|
||||||
kind: Application
|
|
||||||
name: secrets
|
|
||||||
patch: |
|
|
||||||
- op: replace
|
|
||||||
path: /spec/source/path
|
|
||||||
value: secrets/overlays/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
|
|
||||||
50
infra/overlays/us/kustomization.yaml
Normal file
50
infra/overlays/us/kustomization.yaml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
|
||||||
|
patches:
|
||||||
|
# Traefik: swap eu → us in valueFiles
|
||||||
|
- target:
|
||||||
|
kind: Application
|
||||||
|
name: traefik
|
||||||
|
patch: |
|
||||||
|
- op: replace
|
||||||
|
path: /spec/sources/0/helm/valueFiles/1
|
||||||
|
value: $values/infra/values/us/traefik-values.yaml
|
||||||
|
|
||||||
|
# Keycloak: swap eu → us
|
||||||
|
- target:
|
||||||
|
kind: Application
|
||||||
|
name: keycloak
|
||||||
|
patch: |
|
||||||
|
- op: replace
|
||||||
|
path: /spec/sources/0/helm/valueFiles/1
|
||||||
|
value: $values/infra/values/us/keycloak-values.yaml
|
||||||
|
|
||||||
|
# Grafana: swap eu → us
|
||||||
|
- target:
|
||||||
|
kind: Application
|
||||||
|
name: grafana
|
||||||
|
patch: |
|
||||||
|
- op: replace
|
||||||
|
path: /spec/sources/0/helm/valueFiles/1
|
||||||
|
value: $values/infra/values/us/grafana-values.yaml
|
||||||
|
|
||||||
|
# Secrets: change path to us
|
||||||
|
- target:
|
||||||
|
kind: Application
|
||||||
|
name: secrets
|
||||||
|
patch: |
|
||||||
|
- op: replace
|
||||||
|
path: /spec/source/path
|
||||||
|
value: secrets/us
|
||||||
|
|
||||||
|
# Enterprise-apps: point to us overlay
|
||||||
|
- target:
|
||||||
|
kind: Application
|
||||||
|
name: enterprise-apps
|
||||||
|
patch: |
|
||||||
|
- op: replace
|
||||||
|
path: /spec/source/path
|
||||||
|
value: apps/overlays/us
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# AKS-specific: Azure managed disk storage class
|
|
||||||
persistence:
|
|
||||||
storageClass: managed-csi-premium
|
|
||||||
postgresql:
|
|
||||||
primary:
|
|
||||||
persistence:
|
|
||||||
storageClass: managed-csi-premium
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# AKS-specific: Grafana hostname
|
|
||||||
ingress:
|
|
||||||
hosts:
|
|
||||||
- grafana.forteapps.net
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# AKS-specific: Keycloak hostname
|
|
||||||
ingress:
|
|
||||||
hostname: id.forteapps.net
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
# AKS-specific: Azure pricing via Cloud Billing API
|
|
||||||
opencost:
|
|
||||||
exporter:
|
|
||||||
cloudProviderApiKey: ""
|
|
||||||
customPricing:
|
|
||||||
enabled: false
|
|
||||||
azure:
|
|
||||||
secretName: opencost-azure-billing
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# AKS-specific: Azure Load Balancer for Traefik
|
|
||||||
service:
|
|
||||||
annotations:
|
|
||||||
service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: /ping
|
|
||||||
ports:
|
|
||||||
web:
|
|
||||||
forwardedHeaders:
|
|
||||||
trustedIPs: "10.0.0.0/8"
|
|
||||||
websecure:
|
|
||||||
forwardedHeaders:
|
|
||||||
trustedIPs: "10.0.0.0/8"
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# AKS-specific: Azure managed disk storage class (prod)
|
|
||||||
persistence:
|
|
||||||
storageClass: managed-csi-premium
|
|
||||||
postgresql:
|
|
||||||
primary:
|
|
||||||
persistence:
|
|
||||||
storageClass: managed-csi-premium
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# AKS-specific: Grafana hostname (prod)
|
|
||||||
ingress:
|
|
||||||
hosts:
|
|
||||||
- grafana.fortedigital.com
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# AKS-specific: Keycloak hostname (prod)
|
|
||||||
ingress:
|
|
||||||
hostname: id.fortedigital.com
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
# AKS-specific: Azure pricing via Cloud Billing API (prod)
|
|
||||||
opencost:
|
|
||||||
exporter:
|
|
||||||
cloudProviderApiKey: ""
|
|
||||||
customPricing:
|
|
||||||
enabled: false
|
|
||||||
azure:
|
|
||||||
secretName: opencost-azure-billing
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# AKS-specific: Azure Load Balancer for Traefik (prod)
|
|
||||||
service:
|
|
||||||
annotations:
|
|
||||||
service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: /ping
|
|
||||||
service.beta.kubernetes.io/azure-load-balancer-internal: "false"
|
|
||||||
ports:
|
|
||||||
web:
|
|
||||||
forwardedHeaders:
|
|
||||||
trustedIPs: "10.0.0.0/8"
|
|
||||||
websecure:
|
|
||||||
forwardedHeaders:
|
|
||||||
trustedIPs: "10.0.0.0/8"
|
|
||||||
@@ -2,40 +2,20 @@ configs:
|
|||||||
secret:
|
secret:
|
||||||
createSecret: true
|
createSecret: true
|
||||||
argocdServerAdminPassword: "$2b$12$Tmb1jH7ADvwWoUoNPXXsfOf6JqEluqhq8mL06a8DGT2AP1GzbNsCm"
|
argocdServerAdminPassword: "$2b$12$Tmb1jH7ADvwWoUoNPXXsfOf6JqEluqhq8mL06a8DGT2AP1GzbNsCm"
|
||||||
# oidc.clientSecret managed by argocd-oidc-sync CronJob
|
|
||||||
# (reads from argocd-oidc-credentials, patches argocd-secret)
|
|
||||||
ssh:
|
|
||||||
knownHosts: |
|
|
||||||
[git.forteapps.net]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDTwi40de8yTGUuRT0i/XGicQ672BLhYR6D/lDquJrp/tdrWoZhVVPy0wxSkWsq1V92iiAUuQnXagOGsLBGZT9uDLWKvEmNDnCfjzTMq3J1iA3vk2rQ8WBlCzhvmeCV/r0ufl6vsgfwxSRomLZeqa2UkLHx69gy2Njb1S2/aZK1Q53f466hCUfDULZrTn2Nn5Sj8cEbJ8EyvVN2YG9HYBxQdzKRPZEmS1vyzmn8YrYIkZseIRQElabzWGh86owuaaqnwJhTJj1j2sEUeIet04sGKJcnxx2UL4H90N66LKMldmMiuli+ve/CjJmMwDl0zGkjIniT3XR8CyEXYHli7B1hR8Z+dbK6DBgjz+28lFgMIRY70KkZJNsJcBNZLZ5fHwCI13a9U3Uhg3Pu/6s0zlosM4CrAQNQCRe95ZPtCpdFhlGrOl4m1rdSK2meL6rND0TBBuZbaFF6Py7TawLCAiO2KRaVqhu9OFVjwJ/nifgLzFGwWj+WcYmpuR+DwozrF/Hl7QYsz1x4GO1SONY07KbIFkUCHOMAh0AELY5YE4eGI4mtG6SecdPaAdLREGZYK4IcyP5i1QW9g0wmfRSsV9jy+r0ivBxixxh4yJiNpkg6NXak40gQtGIme9EJ+DxrRLruNsfDILWcdSuH/wvuorv56NpQFGB0FzB6LXMloSYptQ==
|
|
||||||
cm:
|
cm:
|
||||||
application.resourceTrackingMethod: annotation
|
application.resourceTrackingMethod: annotation
|
||||||
timeout.reconciliation: 60s
|
timeout.reconciliation: 60s
|
||||||
# Admin login disabled — SSO only. Break-glass: kubectl patch cm argocd-cm -n argocd -p '{"data":{"admin.enabled":"true"}}'
|
admin.enabled: "true"
|
||||||
admin.enabled: "false"
|
repositories: |
|
||||||
url: https://argocd.forteapps.net
|
- type: git
|
||||||
oidc.config: |
|
url: https://github.com/snothub
|
||||||
name: Forte SSO
|
name: github-repo
|
||||||
issuer: https://id.forteapps.net/realms/forte
|
|
||||||
clientID: argocd
|
|
||||||
clientSecret: $oidc.clientSecret
|
|
||||||
requestedScopes: ["openid", "email", "profile"]
|
|
||||||
rbac:
|
|
||||||
policy.csv: |
|
|
||||||
g, ArgoCD Admins, role:admin
|
|
||||||
g, ArgoCD Viewers, role:readonly
|
|
||||||
# Deny users not in any declared KC group (ArgoCD Admins / ArgoCD Viewers)
|
|
||||||
policy.default: ""
|
|
||||||
scopes: '[groups]'
|
|
||||||
params:
|
params:
|
||||||
"server.insecure": true
|
"server.insecure": true
|
||||||
"reposerver.enable.git.submodule": "false"
|
|
||||||
server:
|
server:
|
||||||
ingress:
|
ingress:
|
||||||
enabled: true
|
enabled: false
|
||||||
ingressClassName: traefik
|
ingressClassName: nginx
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
||||||
tls: true
|
|
||||||
extraArgs:
|
extraArgs:
|
||||||
- --insecure
|
- --insecure
|
||||||
|
|
||||||
@@ -52,7 +32,7 @@ notifications:
|
|||||||
method: POST
|
method: POST
|
||||||
body: |
|
body: |
|
||||||
{
|
{
|
||||||
"payload": "🖥️ {{ .context.clusterName }}: 🔄 *{{ .app.metadata.name }}* is syncing...\n📦 Revision: {{ .app.status.sync.revision | default `n/a` | substr 0 7 }}"
|
"payload": "🖥️ {{ .context.clusterName }}: 🔄 *{{ .app.metadata.name }}* is syncing...\n📦 Revision: {{ .app.status.sync.revision | substr 0 7 }}"
|
||||||
}
|
}
|
||||||
template.app-sync-succeeded: |
|
template.app-sync-succeeded: |
|
||||||
webhook:
|
webhook:
|
||||||
@@ -60,7 +40,7 @@ notifications:
|
|||||||
method: POST
|
method: POST
|
||||||
body: |
|
body: |
|
||||||
{
|
{
|
||||||
"payload": "🖥️ {{ .context.clusterName }}: ✅ *{{ .app.metadata.name }}* sync succeeded\n📦 Revision: {{ .app.status.sync.revision | default `n/a` | substr 0 7 }}{{ range .app.status.summary.images }}\n🏷️ Image: {{ . }}{{ end }}"
|
"payload": "🖥️ {{ .context.clusterName }}: ✅ *{{ .app.metadata.name }}* sync succeeded\n📦 Revision: {{ .app.status.sync.revision | substr 0 7 }}{{ range .app.status.summary.images }}\n🏷️ Image: {{ . }}{{ end }}"
|
||||||
}
|
}
|
||||||
template.app-sync-failed: |
|
template.app-sync-failed: |
|
||||||
webhook:
|
webhook:
|
||||||
@@ -68,7 +48,7 @@ notifications:
|
|||||||
method: POST
|
method: POST
|
||||||
body: |
|
body: |
|
||||||
{
|
{
|
||||||
"payload": "🖥️ {{ .context.clusterName }}: ❌ *{{ .app.metadata.name }}* sync failed\n📦 Revision: {{ .app.status.sync.revision | default `n/a` | substr 0 7 }}\n⚠️ Message: {{ .app.status.operationState.message }}"
|
"payload": "🖥️ {{ .context.clusterName }}: ❌ *{{ .app.metadata.name }}* sync failed\n📦 Revision: {{ .app.status.sync.revision | substr 0 7 }}\n⚠️ Message: {{ .app.status.operationState.message }}"
|
||||||
}
|
}
|
||||||
template.app-degraded: |
|
template.app-degraded: |
|
||||||
webhook:
|
webhook:
|
||||||
@@ -76,7 +56,7 @@ notifications:
|
|||||||
method: POST
|
method: POST
|
||||||
body: |
|
body: |
|
||||||
{
|
{
|
||||||
"payload": "🖥️ {{ .context.clusterName }}: ⚠️ *{{ .app.metadata.name }}* is degraded\n🏥 Health: {{ .app.status.health.status }}\n📦 Revision: {{ .app.status.sync.revision | default `n/a` | substr 0 7 }}{{ range .app.status.summary.images }}\n🏷️ Image: {{ . }}{{ end }}"
|
"payload": "🖥️ {{ .context.clusterName }}: ⚠️ *{{ .app.metadata.name }}* is degraded\n🏥 Health: {{ .app.status.health.status }}\n💬 Message: {{ .app.status.health.message }}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Define notification triggers
|
# Define notification triggers
|
||||||
@@ -85,7 +65,7 @@ notifications:
|
|||||||
- when: app.status.operationState.phase in ['Running']
|
- when: app.status.operationState.phase in ['Running']
|
||||||
send: [app-syncing]
|
send: [app-syncing]
|
||||||
trigger.on-sync-succeeded: |
|
trigger.on-sync-succeeded: |
|
||||||
- when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy'
|
- when: app.status.operationState.phase in ['Succeeded']
|
||||||
send: [app-sync-succeeded]
|
send: [app-sync-succeeded]
|
||||||
trigger.on-sync-failed: |
|
trigger.on-sync-failed: |
|
||||||
- when: app.status.operationState.phase in ['Failed']
|
- when: app.status.operationState.phase in ['Failed']
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ config:
|
|||||||
Match kube.*
|
Match kube.*
|
||||||
Host loki-gateway.monitoring.svc.cluster.local
|
Host loki-gateway.monitoring.svc.cluster.local
|
||||||
Port 80
|
Port 80
|
||||||
Labels job=fluent-bit, namespace=$kubernetes['namespace_name'], pod=$kubernetes['pod_name'], container=$kubernetes['container_name'], stream=$stream
|
Labels job=fluent-bit, namespace=$kubernetes['namespace_name'], pod=$kubernetes['pod_name'], container=$kubernetes['container_name']
|
||||||
Auto_Kubernetes_Labels Off
|
|
||||||
Line_Format json
|
Line_Format json
|
||||||
|
|
||||||
[OUTPUT]
|
[OUTPUT]
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
## Gitea Act Runner - Helm values
|
|
||||||
## Chart: actions v0.0.5 (https://dl.gitea.com/charts)
|
|
||||||
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
giteaRootURL: https://git.forteapps.net
|
|
||||||
|
|
||||||
existingSecret: gitea-runner-token
|
|
||||||
existingSecretKey: token
|
|
||||||
|
|
||||||
statefulset:
|
|
||||||
replicas: 3
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 250m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: "1"
|
|
||||||
memory: 1Gi
|
|
||||||
|
|
||||||
actRunner:
|
|
||||||
config: |
|
|
||||||
log:
|
|
||||||
level: info
|
|
||||||
cache:
|
|
||||||
enabled: false
|
|
||||||
container:
|
|
||||||
require_docker: true
|
|
||||||
docker_timeout: 300s
|
|
||||||
runner:
|
|
||||||
labels:
|
|
||||||
- "ubuntu-latest:docker://catthehacker/ubuntu:act-22.04"
|
|
||||||
- "ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04"
|
|
||||||
dind:
|
|
||||||
rootless: false
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
# Gitea Helm Chart Values
|
|
||||||
# Host: git.forteapps.net
|
|
||||||
# Chart: gitea v12.5.0 (app v1.25.4)
|
|
||||||
# Repo: https://dl.gitea.com/charts
|
|
||||||
|
|
||||||
# -- Admin account (password from sealed secret)
|
|
||||||
gitea:
|
|
||||||
admin:
|
|
||||||
existingSecret: gitea-credentials
|
|
||||||
email: admin@forteapps.net
|
|
||||||
|
|
||||||
# -- Gitea app.ini configuration
|
|
||||||
config:
|
|
||||||
APP_NAME: "Forte Git"
|
|
||||||
|
|
||||||
server:
|
|
||||||
DOMAIN: git.forteapps.net
|
|
||||||
ROOT_URL: https://git.forteapps.net
|
|
||||||
SSH_DOMAIN: git.forteapps.net
|
|
||||||
SSH_PORT: 2222
|
|
||||||
LFS_START_SERVER: true
|
|
||||||
ENABLE_GITEA_PAGES: true
|
|
||||||
ENABLE_BASIC_AUTH_CHALLENGE: true
|
|
||||||
|
|
||||||
service:
|
|
||||||
DISABLE_REGISTRATION: false
|
|
||||||
DEFAULT_ALLOW_CREATE_ORGANIZATION: false
|
|
||||||
REQUIRE_SIGNIN_VIEW: false
|
|
||||||
ALLOW_ONLY_EXTERNAL_REGISTRATION: true
|
|
||||||
ENABLE_BASIC_AUTHENTICATION: true
|
|
||||||
ENABLE_PASSWORD_SIGNIN_FORM: false
|
|
||||||
AUTO_WATCH_ON_CHANGES: false
|
|
||||||
AUTO_WATCH_NEW_REPOS: false
|
|
||||||
ENABLE_NOTIFY_MAIL: false
|
|
||||||
ENABLE_TIMETRACKING: false
|
|
||||||
|
|
||||||
openid:
|
|
||||||
ENABLE_OPENID_SIGNIN: false
|
|
||||||
ENABLE_OPENID_SIGNUP: false
|
|
||||||
|
|
||||||
oauth2:
|
|
||||||
ENABLED: true
|
|
||||||
ENABLE_AUTO_REGISTRATION: true
|
|
||||||
USERNAME: email
|
|
||||||
|
|
||||||
session:
|
|
||||||
PROVIDER: db
|
|
||||||
|
|
||||||
cache:
|
|
||||||
ADAPTER: memory
|
|
||||||
|
|
||||||
database:
|
|
||||||
DB_TYPE: postgres
|
|
||||||
|
|
||||||
metrics:
|
|
||||||
ENABLED: true
|
|
||||||
|
|
||||||
repository:
|
|
||||||
DEFAULT_BRANCH: main
|
|
||||||
DEFAULT_PRIVATE: last
|
|
||||||
|
|
||||||
actions:
|
|
||||||
ENABLED: true
|
|
||||||
|
|
||||||
packages:
|
|
||||||
ENABLED: true
|
|
||||||
|
|
||||||
indexer:
|
|
||||||
ISSUE_INDEXER_TYPE: bleve
|
|
||||||
REPO_INDEXER_ENABLED: true
|
|
||||||
|
|
||||||
mailer:
|
|
||||||
ENABLED: true
|
|
||||||
PROTOCOL: smtp+starttls
|
|
||||||
SMTP_ADDR: smtp.office365.com
|
|
||||||
SMTP_PORT: 587
|
|
||||||
FROM: "noreply@fortedigital.com"
|
|
||||||
|
|
||||||
admin:
|
|
||||||
DEFAULT_EMAIL_NOTIFICATIONS: onmention
|
|
||||||
|
|
||||||
# -- SMTP credentials injected from secret (USER and PASSWD)
|
|
||||||
additionalConfigFromEnvs:
|
|
||||||
- name: GITEA__mailer__USER
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: gitea-smtp-secret
|
|
||||||
key: username
|
|
||||||
- name: GITEA__mailer__PASSWD
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: gitea-smtp-secret
|
|
||||||
key: password
|
|
||||||
# -- OIDC authentication via Forte
|
|
||||||
oauth:
|
|
||||||
- name: "Forte"
|
|
||||||
provider: "openidConnect"
|
|
||||||
existingSecret: gitea-oidc-credentials
|
|
||||||
key: gitea
|
|
||||||
autoDiscoverUrl: "https://id.forteapps.net/realms/forte/.well-known/openid-configuration"
|
|
||||||
scopes: "email profile organization"
|
|
||||||
groupClaimName: "groups"
|
|
||||||
adminGroup: ""
|
|
||||||
restrictedGroup: ""
|
|
||||||
# -- Prometheus metrics (scraped via annotations, no ServiceMonitor CRD needed)
|
|
||||||
metrics:
|
|
||||||
enabled: true
|
|
||||||
serviceMonitor:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# -- Ingress via Traefik with Let's Encrypt TLS
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
className: traefik
|
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
||||||
hosts:
|
|
||||||
- host: git.forteapps.net
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
tls:
|
|
||||||
- secretName: gitea-tls
|
|
||||||
hosts:
|
|
||||||
- git.forteapps.net
|
|
||||||
|
|
||||||
# -- Git repository storage
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
size: 10Gi
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
|
|
||||||
# -- Recreate strategy to avoid Multi-Attach errors with RWO volumes
|
|
||||||
strategy:
|
|
||||||
type: Recreate
|
|
||||||
|
|
||||||
# -- Pod resources
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 512Mi
|
|
||||||
|
|
||||||
# -- Embedded PostgreSQL (Bitnami subchart)
|
|
||||||
# Password auto-generated by the subchart; Gitea chart auto-wires the connection.
|
|
||||||
postgresql:
|
|
||||||
enabled: true
|
|
||||||
auth:
|
|
||||||
username: gitea
|
|
||||||
database: gitea
|
|
||||||
primary:
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
size: 8Gi
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 256Mi
|
|
||||||
limits:
|
|
||||||
cpu: 500m
|
|
||||||
memory: 512Mi
|
|
||||||
|
|
||||||
# -- Disable PostgreSQL HA (using single-instance postgresql above)
|
|
||||||
postgresql-ha:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# -- Disable Redis cluster (use in-memory cache instead)
|
|
||||||
redis-cluster:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# -- Disable test pod
|
|
||||||
test:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# -- SSH service (ClusterIP, exposed externally via Traefik TCP IngressRoute on port 2222)
|
|
||||||
service:
|
|
||||||
ssh:
|
|
||||||
type: ClusterIP
|
|
||||||
port: 22
|
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user