# 機能設計書 22-ResourceQuotaコントローラー

## 概要

本ドキュメントは、KubernetesのResourceQuotaコントローラーの機能設計を記述する。ResourceQuotaコントローラーは、Namespace内のリソース使用量を監視・集計し、ResourceQuotaオブジェクトのステータスを最新の使用状況に更新する。

### 本機能の処理概要

**業務上の目的・背景**：マルチテナント環境では、特定のNamespaceがクラスターリソースを過度に消費することを防ぐ必要がある。ResourceQuotaコントローラーは、Namespace内のリソース（Pod数、CPU、メモリ、ストレージなど）の現在の使用量を定期的に計算し、ResourceQuotaオブジェクトのStatus.Usedフィールドを更新することで、Admission Controlプラグインが正確なクォータ判定を行えるようにする。

**機能の利用シーン**：管理者がNamespaceにResourceQuotaを設定した後、Pod/Service/PVC等のリソース作成・削除に伴い使用量が変動する際に、リアルタイムに使用量ステータスを更新する。また、Spec.Hardが変更された際に即座にステータスを同期する。

**主要な処理内容**：
1. ResourceQuotaオブジェクトの変更（作成・更新・削除）を監視する
2. 2種類のキュー（primaryとpriority）でクォータ同期の優先制御を行う
3. quota.CalculateUsageによりNamespace内の全リソース使用量を算出する
4. 算出結果をResourceQuota.Status.Usedに書き込む
5. QuotaMonitorが各リソース変更を検知し、関連クォータの再計算をトリガーする

**関連システム・外部連携**：API Server（ResourceQuota読み取り・ステータス更新）、Discovery API（クォータ対象リソースの検出）、Admission Controller（クォータ判定のためStatus.Usedを参照）

**権限による制御**：ResourceQuotaコントローラーはsystem:controller:resourcequota-controllerサービスアカウントで動作し、ResourceQuotaの読み取り・ステータス更新権限、および全リソースのlist/watch権限を持つ。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本コントローラーに直接関連する画面はない |

## 機能種別

計算処理 / データ連携（バックグラウンド自動処理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| workers | int | Yes | 同時実行ワーカー数 | 正の整数 |
| ResyncPeriod | time.Duration | Yes | 全クォータの再計算間隔 | 正の値。0の場合は定期再同期無効 |
| ReplenishmentResyncPeriod | time.Duration | Yes | QuotaMonitor対象リソースの再同期間隔 | 正の値 |

### 入力データソース

- ResourceQuota Informer: ResourceQuotaオブジェクトのadd/update/deleteイベント
- QuotaMonitor: 各リソース（Pod、Service、PVC等）の変更イベント
- Discovery API: クォータ対象リソースタイプの一覧

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ResourceQuota.Status.Hard | v1.ResourceList | クォータのハードリミット（Specからコピー） |
| ResourceQuota.Status.Used | v1.ResourceList | 現在のリソース使用量 |

### 出力先

- API Server: ResourceQuotaのStatusサブリソース更新（UpdateStatus）

## 処理フロー

### 処理シーケンス

```
1. NewController()でコントローラーを初期化する
   └─ ResourceQuota Informerのイベントハンドラ登録、QuotaMonitor初期化
2. Run()でワーカーとQuotaMonitorを起動する
   └─ キャッシュ同期を待機後、workers個のワーカーを起動
3. addQuota()でキューへの振り分けを行う
   └─ Spec.Hard != Status.Hard → missingUsageQueue（優先）
   └─ Status.Usedに未計上の制約あり → missingUsageQueue（優先）
   └─ それ以外 → queue（通常）
4. syncResourceQuota()で使用量を計算する
   └─ quota.CalculateUsage()でNamespace内の全リソースをカウント
5. ステータスが変更されていればAPI Serverに更新する
   └─ UpdateStatus()でStatus.Hard/Status.Usedを更新
6. replenishQuota()でリソース変更時にクォータを再計算対象にする
   └─ QuotaMonitorが変更を検知し、関連するResourceQuotaをエンキュー
```

### フローチャート

```mermaid
flowchart TD
    A[ResourceQuota変更検知] --> B{Spec.Hard != Status.Hard?}
    B -->|Yes| C[missingUsageQueueに投入]
    B -->|No| D{未計上の制約あり?}
    D -->|Yes| C
    D -->|No| E[queueに投入]

    C --> F[syncResourceQuota]
    E --> F

    F --> G[quota.CalculateUsage実行]
    G --> H{使用量に変化あり?}
    H -->|Yes| I[UpdateStatusでAPI Server更新]
    H -->|No| J[スキップ]

    K[リソース変更検知] --> L[QuotaMonitor.replenishQuota]
    L --> M[関連ResourceQuotaをqueueに投入]
    M --> F
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-22-01 | 優先キュー制御 | Spec.HardとStatus.Hardが一致しない場合、またはStatus.Usedに未計上の制約がある場合はmissingUsageQueue（優先キュー）で処理する | addQuota時 |
| BR-22-02 | Spec変更のみ監視 | Update時はSpec.Hardの変更のみを監視し、Status変更は無視する（自コントローラーの更新による再帰を防止） | ResourceQuota Update時 |
| BR-22-03 | 使用量マスキング | 計算された使用量はSpec.Hardに定義されたリソース名のみに絞り込む | syncResourceQuota内 |
| BR-22-04 | 部分障害時の継続 | quota.CalculateUsageが一部エラーを返しても、計算済みの使用量でステータス更新を試行する | syncResourceQuota内 |
| BR-22-05 | 定期再同期 | resyncPeriod間隔で全ResourceQuotaを再計算キューに投入する | resyncPeriod > 0の場合 |

### 計算ロジック

- **使用量計算**: `quota.CalculateUsage(namespace, scopes, hardLimits, registry, scopeSelector)`でNamespace内リソースをカウント
- **dirty判定**: `statusLimitsDirty || Status.Hard == nil || Status.Used == nil || !quota.Equals(usage.Status.Used, resourceQuota.Status.Used)`
- **使用量マスク**: `quota.Mask(used, hardResources)`でハードリミットに定義されたリソースのみを残す

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| クォータ取得 | ResourceQuota（etcd） | SELECT（List/Get） | 全ResourceQuotaの取得 |
| ステータス更新 | ResourceQuota（etcd） | UPDATE | Status.Hard/Status.Usedの更新 |
| リソースカウント | 各リソース（etcd） | SELECT（List） | Namespace内リソースの使用量カウント |

### テーブル別操作詳細

#### ResourceQuota（etcd経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | status.hard | Spec.Hardからコピー | UpdateStatusサブリソース |
| UPDATE | status.used | quota.CalculateUsageの結果 | UpdateStatusサブリソース |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NotFound | API応答 | ResourceQuotaが削除済み | ログ出力し正常終了 |
| CalculateUsage失敗 | 内部 | リソース一覧取得の部分失敗 | 取得済みの使用量で更新を試行 |
| UpdateStatus失敗 | API応答 | ステータス更新の競合等 | RateLimitingキューでリトライ |
| Discovery失敗 | API応答 | Discovery APIの部分/全体障害 | 部分障害は既存モニター維持、全体障害はスキップ |

### リトライ仕様

- syncHandler失敗時: RateLimitingキューによるエクスポネンシャルバックオフでリトライ
- 成功時: queue.Forget()でバックオフをリセット
- ワーカーはworkerLock.RLock()を取得して処理し、Sync中はworkerLock.Lock()で排他制御

## トランザクション仕様

各ResourceQuotaのステータス更新はUpdateStatusサブリソースAPIで行われ、ResourceVersionによる楽観的並行性制御が適用される。workerLockによりSync中のモニター再構成とワーカー処理の排他制御を実現している。

## パフォーマンス要件

- 2種類のキュー（primary/priority）により、初期使用量計算を優先処理
- QuotaMonitorによるリソース変更駆動で、必要な場合のみ再計算を実行
- quota.CalculateUsageはNamespace単位でリソースをリストするため、大規模Namespaceではレイテンシが増加する可能性がある

## セキュリティ考慮事項

- ResourceQuotaコントローラーはNamespace内の全リソースをリストする権限を持つ
- ステータス更新はResourceQuotaのStatusサブリソースのみ。Spec変更は行わない
- QuotaMonitorは対象リソースのlist/watch権限を必要とする

## 備考

- ResourceQuotaのSpec.Hardの値自体はAdmission Controlプラグイン（ResourceQuota Admission）がリクエスト時に検証する。本コントローラーは使用量の「計算と報告」のみを担当する
- QuotaMonitorはGCコントローラーと同様のDiscovery-drivenなモニター管理パターンを使用する

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | resource_quota_controller.go | `pkg/controller/resourcequota/resource_quota_controller.go` | ControllerOptions構造体（56-77行目）とController構造体（80-103行目）。2つのキュー（queue/missingUsageQueue）、registry、quotaMonitorを保持 |

**読解のコツ**: Controller構造体のqueueは通常のResourceQuota再計算用、missingUsageQueueは初期使用量が未計算のResourceQuota用の優先キューである。workerLockはSync中のワーカー排他制御に使用される。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | resource_quota_controller.go | `pkg/controller/resourcequota/resource_quota_controller.go` | NewController（106-191行目）。Informerのイベントハンドラ登録、QuotaMonitor初期化、初期リソースDiscovery |
| 2-2 | resource_quota_controller.go | `pkg/controller/resourcequota/resource_quota_controller.go` | Run（293-337行目）。QuotaMonitor起動、キャッシュ同期待機、ワーカー起動、定期再同期 |

**主要処理フロー**:
1. **128-157行目**: ResourceQuota InformerにAdd/Update/Deleteハンドラを登録
2. **144-146行目**: Update時はSpec.Hardの変更のみを検出（Status変更は無視）
3. **307-311行目**: QuotaMonitorをgoroutineで起動
4. **318-325行目**: workers個のワーカーをqueue/missingUsageQueue両方に起動
5. **328-334行目**: resyncPeriod > 0ならenqueueAllで定期再同期

#### Step 3: キュー振り分けロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | resource_quota_controller.go | `pkg/controller/resourcequota/resource_quota_controller.go` | addQuota（222-252行目）。Spec.Hard != Status.Hardまたは未計上制約があればmissingUsageQueueに投入 |

#### Step 4: 使用量計算ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | resource_quota_controller.go | `pkg/controller/resourcequota/resource_quota_controller.go` | syncResourceQuota（367-415行目）。dirty判定、CalculateUsage呼び出し、使用量マスキング、UpdateStatus |
| 4-2 | resource_quota_controller.go | `pkg/controller/resourcequota/resource_quota_controller.go` | replenishQuota（418-450行目）。リソース変更時に関連するResourceQuotaをキューに投入 |

#### Step 5: Discovery同期を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | resource_quota_controller.go | `pkg/controller/resourcequota/resource_quota_controller.go` | Sync（453-521行目）。Discovery APIのポーリング、workerLockによる排他制御、モニター再同期 |
| 5-2 | resource_quota_controller.go | `pkg/controller/resourcequota/resource_quota_controller.go` | GetQuotableResources（558-570行目）。create/list/watch/deleteサポートリソースを抽出 |

### プログラム呼び出し階層図

```
Controller.Run()
    │
    ├─ QuotaMonitor.Run()
    │      └─ 各リソースのInformer起動
    │             └─ replenishQuota() ── 変更検知時にクォータ再計算をトリガー
    │
    ├─ worker(queue) ── 通常キューのワーカー
    │      └─ syncResourceQuotaFromKey()
    │             └─ syncResourceQuota()
    │                    ├─ quota.CalculateUsage() ── 使用量算出
    │                    └─ UpdateStatus() ── ステータス更新
    │
    ├─ worker(missingUsageQueue) ── 優先キューのワーカー
    │      └─ syncResourceQuotaFromKey() ── 同上
    │
    └─ enqueueAll() ── 定期再同期（resyncPeriod間隔）
```

### データフロー図

```
[入力]                        [処理]                              [出力]

ResourceQuota変更 ──────▶ addQuota()
(Informerイベント)           │ 優先度判定
                            ├─▶ missingUsageQueue（優先）
                            └─▶ queue（通常）
                                    │
                                    ▼
                            syncResourceQuota()
                                    │
                                    ├─ quota.CalculateUsage() ──▶ API Server List
                                    │   (Namespace内リソースカウント)    (各リソース)
                                    │
                                    └─▶ API Server UpdateStatus ──▶ ResourceQuota
                                        (Status.Hard/Status.Used)      Status更新

リソース変更 ──────────▶ QuotaMonitor
(Pod/Service等)            │ replenishQuota()
                           └─▶ queue（関連Quotaをエンキュー）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| resource_quota_controller.go | `pkg/controller/resourcequota/resource_quota_controller.go` | ソース | ResourceQuotaコントローラーのメインロジック |
| resource_quota_monitor.go | `pkg/controller/resourcequota/resource_quota_monitor.go` | ソース | QuotaMonitorによるリソース変更監視 |
| config.go | `pkg/controller/resourcequota/config.go` | ソース | コントローラー設定の定義 |
