# AGENTS.md

## Project Overview

This is an **ArgoCD GitOps repository** that manages Kubernetes applications for the `au-syd1` cluster using a Kustomize + Helm pattern. Applications are deployed via ArgoCD ApplicationSets that watch directory patterns in this repo.

The migration pattern for this repo is: **Terragrunt/Terraform → ArgoCD** (see `migration.md` for full guide).

---

## Essential Commands

```bash
# Build and render manifests for a path (outputs to manifests/<path>/)
make build apps/overlays/au-syd1/<app-name>
make build clusters/au-syd1/bootstrap

# Validate all apps and clusters with kubeconform
make kubeconform

# Clean generated manifests
make clean

# Quick build + inspect without persisting output
kustomize build --enable-helm apps/overlays/au-syd1/<app-name>

# Check all resource kinds produced by an overlay
kustomize build --enable-helm apps/overlays/au-syd1/<app-name> | grep "^kind:" | sort | uniq -c

# Run pre-commit checks against all files
uvx pre-commit run --all-files
```

---

## Directory Structure

```
argocd-apps/
├── argocd/
│   ├── applicationsets/     # ArgoCD ApplicationSet definitions (platform.yaml, storage.yaml)
│   └── projects/            # ArgoCD AppProject definitions (platform.yaml, storage.yaml)
├── apps/
│   ├── base/                # Base Kustomize resources per app (no cluster-specific config)
│   │   └── <app-name>/
│   │       ├── kustomization.yaml
│   │       ├── namespace.yaml
│   │       ├── vaultauth.yaml        # (if Vault-managed secrets)
│   │       └── vaultstaticsecret.yaml
│   └── overlays/
│       └── au-syd1/         # Cluster-specific overlays
│           └── <app-name>/
│               ├── kustomization.yaml  # references base + helmCharts
│               └── values.yaml         # Helm values for this cluster
├── clusters/
│   └── au-syd1/
│       ├── apps/            # Entry point: references apps/base (ArgoCD app-of-apps)
│       └── bootstrap/       # ArgoCD install + initial Application manifest
├── ci/
│   ├── validate-apps.sh     # kubeconform over apps/overlays/*/kustomization.yaml
│   ├── validate-clusters.sh # kubeconform over clusters/*/kustomization.yaml
│   └── validate-no-secrets.sh # pre-commit hook: blocks plain Kubernetes Secrets
└── sources/                 # Reference sources (Terraform configs, upstream charts, etc.)
    └── terraform-k8s/       # Original Terraform configs — reference when migrating
```

---

## Adding a New Application

Follow these 10 steps (detailed in `migration.md`):

### 1. Create base resources
```
apps/base/<app-name>/
├── kustomization.yaml
├── namespace.yaml
├── vaultauth.yaml          # if needed
└── vaultstaticsecret.yaml  # if needed
```

### 2. Create cluster overlay
```
apps/overlays/au-syd1/<app-name>/
├── kustomization.yaml
└── values.yaml
```

**Overlay kustomization.yaml pattern:**
```yaml
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../../base/<app-name>

helmCharts:
  - name: <chart-name>
    repo: <helm-repo-url>
    version: "<version>"
    releaseName: <release-name>
    namespace: <namespace>
    valuesFile: values.yaml
```

### 3. Register in ApplicationSet
Add a directory entry to `argocd/applicationsets/platform.yaml` (or `storage.yaml` for `csi-*` apps):
```yaml
- path: apps/overlays/*/<app-name>
```

### 4. Update AppProject
In `argocd/projects/platform.yaml` (or `storage.yaml`):
- Add the Helm repo URL to `sourceRepos`
- Add the namespace to `destinations`
- Add any required cluster-scoped resource types to `clusterResourceWhitelist`

### 5. Validate
```bash
kustomize build --enable-helm apps/overlays/au-syd1/<app-name>
make kubeconform
```

---

## Secret Management

**Plain Kubernetes `Secret` objects are blocked** by the pre-commit hook. Use Vault Operator CRDs instead:

### VaultAuth template
```yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
  name: default
  namespace: <namespace>
spec:
  method: kubernetes
  mount: k8s/au/syd1
  vaultConnectionRef: vso-system/default
  allowedNamespaces:
    - <namespace>
  kubernetes:
    role: <role>
    serviceAccount: <service-account>
    audiences:
      - vault
    tokenExpirationSeconds: 600
```

### VaultStaticSecret template
```yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
  name: <secret-name>
  namespace: <namespace>
spec:
  vaultAuthRef: default
  mount: kv
  type: kv-v2
  path: kubernetes/namespace/<namespace>/default/<secret-name>
  refreshAfter: 5m
  destination:
    name: <k8s-secret-name>
    create: true
    overwrite: true
  hmacSecretData: true
```

---

## YAML Conventions

- **2-space indentation** (enforced by yamllint)
- All files must end with a newline (`end-of-file-fixer`)
- No trailing whitespace
- YAML linting uses relaxed rules with `line-length: disable` (long base64/URLs are fine)
- yamllint ignores `chart` directories (vendored Helm charts)
- `---` document separator at top of every YAML file
- Multiple documents in one file are allowed (e.g., `vaultstaticsecret.yaml` often contains multiple secrets)

---

## Kubernetes Labels Pattern

Use standard `app.kubernetes.io/*` labels consistently:
```yaml
labels:
  app.kubernetes.io/component: <component>
  app.kubernetes.io/instance: <release-name>
  app.kubernetes.io/name: <app-name>
  app.kubernetes.io/version: <version>
```

---

## Resource Naming Conventions

Files in `apps/base/<app-name>/` follow the pattern:
```
<kind>_<name>.yaml
```
Examples:
- `deployment_puppetserver-master.yaml`
- `cronjob_g10k-code.yaml`
- `configmap_puppetboard-config.yaml`
- `horizontalpodautoscaler_puppetserver-compilers-autoscaler.yaml`
- `service_puppet-headless.yaml`

---

## Helm Chart Vendoring

Some overlays vendor Helm charts locally under `apps/overlays/au-syd1/<app-name>/charts/<chart-name>/`. When a chart is vendored, the overlay's `kustomization.yaml` references the local path. When not vendored, it references the OCI or HTTP repo directly.

Current Kubernetes target version: **1.33.7** (used by kubeconform in CI).

---

## Project Boundaries

| Project    | ApplicationSet            | App pattern              |
|------------|---------------------------|--------------------------|
| `platform` | `argocd/applicationsets/platform.yaml` | Named apps (cert-manager, puppet, woodpecker, etc.) |
| `storage`  | `argocd/applicationsets/storage.yaml`  | `csi-*` apps             |

The `clusters/au-syd1/apps/` entry-point is deployed as a standalone ArgoCD `Application` (not an ApplicationSet) called `au-syd1-apps`.

---

## CI / Pre-commit Hooks

Runs on every PR via Woodpecker CI (`.woodpecker/`):

| Check | Tool | Trigger |
|---|---|---|
| YAML lint + general file checks | `pre-commit` (yamllint + pre-commit-hooks) | PR |
| No plain Secrets | `ci/validate-no-secrets.sh` | PR (staged files) |
| Kubernetes manifest validation | `kubeconform` via `make kubeconform` | PR |

kubeconform skips: `CustomResourceDefinition`, `GpuDevicePlugin` (for apps validation).

---

## Git Workflow

- Branch naming: `benvin/<app-name>` (user prefix)
- **Never `git add .`** — add only relevant files explicitly
- If pre-commit modifies files, `git add -u` then `git commit --amend --no-edit`
- Use `git push --force-with-lease` after amending

---

## Security Policies

- `reloader.stakater.com/auto: "true"` annotation triggers rolling restarts on ConfigMap/Secret changes
- Security contexts follow least-privilege: `drop: [all]` then add only required capabilities
- `fsGroup: 999` on pod security context for Puppet workloads
- `runAsUser: 0` is used only for init containers that need to set file permissions, then regular containers run as non-root
