## Структурні перевірки HPA і PDB

Rego-пакет: `k8s.hpa_pdb`

**Цільові файли:** `hpa.yaml` (`kind: HorizontalPodAutoscaler`) та `pdb.yaml` (`kind: PodDisruptionBudget`) у overlay-каталогах (не в `base/`).

**Що перевіряється:**

HPA (`kind: HorizontalPodAutoscaler` або `apiVersion: autoscaling/…`):

- `apiVersion: autoscaling/v2` (не v1)
- `kind: HorizontalPodAutoscaler`
- `spec` — присутній об'єкт
- `spec.behavior` — присутній; містить `scaleUp` і `scaleDown`, кожен з непорожнім масивом `policies`
- `spec.metrics` — непорожній масив

PDB (`kind: PodDisruptionBudget` або `apiVersion: policy/v1`):

- `apiVersion: policy/v1`
- `kind: PodDisruptionBudget`
- `spec` — присутній об'єкт
- `spec.selector` — присутній об'єкт
- `spec.selector.matchLabels` — присутній об'єкт

**Примітка:** cross-file перевірки (відповідність `expectedDeployName`, `expectedAppLabel`, `isDevLike`-сегмент) лишаються у JS (`hpaManifestViolations`, `pdbManifestViolations`).

## Канон `components/`, env-залежні межі, прод-оверрайди

Структурні перевірки полів HPA/PDB вище — про **самі поля** (`apiVersion`, `kind`, `spec.metrics`, `spec.selector` тощо). Заборону локальних `hpa.yaml`/`pdb.yaml` у `base/` — див. **`base_kustomization.mdc`**. Цей розділ — про **env-залежні числові межі** і структуру sibling-каталогу **`components/`**.

### Канонічна структура `<pkg>/k8s/components/`

Для **кожного** `kind: Deployment` у `…/k8s/…/base/` обов'язковий sibling-каталог **`…/k8s/…/components/`** (Kustomize Component, фіксована назва каталогу — `components`) з HPA і PDB для цього Deployment:

- **`kustomization.yaml`** — `apiVersion: kustomize.config.k8s.io/v1alpha1`, `kind: Component`, `resources: [hpa.yaml, pdb.yaml]` (як мінімум ці два).
- **`hpa.yaml`** — `autoscaling/v2`, `HorizontalPodAutoscaler`, `spec.scaleTargetRef.name` **= `metadata.name`** Deployment, dev-like значення `minReplicas: 1`, `maxReplicas: 1`.
- **`pdb.yaml`** — `policy/v1`, `PodDisruptionBudget`, `spec.selector.matchLabels.app` **= мітка `app`** Deployment, dev-like `minAvailable: 0`.

**`kind: Component`** (не `kind: Kustomization`) — це **джерело** канонічних HPA/PDB для всіх overlays, а не overlay сам по собі; всередині самого Component prod-патчі не потрібні (env-неутральний).

Перевірка — **`crates/rules-core/src/concerns/k8s_manifests_workloads.rs`** (`validateComponentsForBaseDeployment`).

### Env-залежні межі (за сегментом шляху після `/k8s/`)

**Dev-like середовища** — сегмент `base`, `dev`, або з суфіксом `-qa` (наприклад `tr-qa`):

- HPA: `minReplicas` — рівно **1**, `maxReplicas` — рівно **1**.
- PDB: `minAvailable` — рівно **0**.

**Прод-середовища** — усе інше:

- HPA: `minReplicas` — мінімум **2**, `maxReplicas` — мінімум **2** (і `minReplicas <= maxReplicas`).
- PDB: `minAvailable` — мінімум **1**.

Перевірка — **`crates/rules-core/src/concerns/k8s_manifests_workloads.rs`** (`hpaManifestViolations`, `pdbManifestViolations`; сегмент середовища визначає `isDevLikeK8sEnvSegment`).

### Прод-оверрайди у `kustomization.yaml`

Якщо прод-оверлей (сегмент не dev-like) успадковує HPA/PDB через `components: [- ../components]` (або інший kustomize-tree з HPA/PDB), у `patches[]` **обов'язкові** JSON6902-перевизначення прод-значень:

- **`HorizontalPodAutoscaler`**: `/spec/minReplicas` і `/spec/maxReplicas` (мінімум 2).
- **`PodDisruptionBudget`**: `/spec/minAvailable` (мінімум 1).

```yaml title="k8s/prod/kustomization.yaml (фрагмент)"
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: prod
resources:
  - ../base
components:
  - ../components
patches:
  - target:
      kind: HorizontalPodAutoscaler
      name: backend-api
    patch: |-
      - op: replace
        path: /spec/minReplicas
        value: 2
      - op: replace
        path: /spec/maxReplicas
        value: 10
  - target:
      kind: PodDisruptionBudget
      name: backend-api
    patch: |-
      - op: replace
        path: /spec/minAvailable
        value: 1
```

Не застосовується до dev-like оточень. Перевірка — **`crates/rules-core/src/concerns/k8s_manifests_kustomize.rs`** (`prodOverlayHpaPdbOverrideNeeds`, `validateProdKustomizationOverrides`).

### Приклад: `components/hpa.yaml` і `components/pdb.yaml`

```yaml title="k8s/components/kustomization.yaml"
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
resources:
  - hpa.yaml
  - pdb.yaml
```

```yaml title="k8s/components/hpa.yaml"
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.9-standalone-strict/horizontalpodautoscaler-autoscaling-v2.json
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: backend-api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: backend-api
  minReplicas: 1 # прод overlay підіймає до >= 2
  maxReplicas: 1 # прод overlay підіймає до >= 2
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 15
      policies:
        - type: Percent
          value: 100
          periodSeconds: 30
        - type: Pods
          value: 4
          periodSeconds: 30
      selectPolicy: Max
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 25
          periodSeconds: 120
      selectPolicy: Min
```

```yaml title="k8s/components/pdb.yaml"
# yaml-language-server: $schema=https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.9-standalone-strict/poddisruptionbudget-policy-v1.json
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: backend-api
spec:
  minAvailable: 0 # прод overlay підіймає до >= 1
  selector:
    matchLabels:
      app: backend-api
```

### Overlays без `components/`

У **не-base** оверлеях, що не підключають sibling `components/`, поруч із `Deployment` лишається звична схема: окремі **`hpa.yaml`** і **`pdb.yaml`**, якщо такі потрібні для цього середовища (структурні поля — вище; env-залежні межі — як вище).
