# 通知設計書 54-VolumeFailedRecycle

## 概要

本ドキュメントは、PersistentVolume（PV）のリサイクル処理に失敗した際に発行されるイベント通知「VolumeFailedRecycle」の設計について記述する。

### 本通知の処理概要

**業務上の目的・背景**：PVのReclaimPolicyが `Recycle` に設定されている場合、PVCが解放された後にPVの内容をクリーンアップして再利用可能にするリサイクル処理が実行される。このリサイクル処理が失敗した場合、PVは再利用できず `Failed` 状態に遷移する。この通知はリサイクル失敗をクラスタ管理者に通知し、手動でのストレージ管理やリサイクル設定の修正を促す。なお、Recycle ReclaimPolicyは非推奨となっており、Dynamic Provisioningの使用が推奨される。

**通知の送信タイミング**：PV ControllerのrecycleVolumeOperation内で、(1) ボリュームがPodによって使用中でリサイクルできない場合、(2) リサイクル可能なプラグインが見つからない場合、(3) プラグインのRecycle処理が失敗した場合に発行される。

**通知の受信者**：PVオブジェクトに対するKubernetes Eventとして発行される。`kubectl describe pv` や `kubectl get events` で監視しているクラスタ管理者、ストレージ管理者が受信者となる。

**通知内容の概要**：リサイクル失敗の具体的理由（使用中のPod名一覧、プラグイン未検出、Recycle処理エラー）が含まれる。

**期待されるアクション**：受信者は、PVを使用しているPodの確認と終了、リサイクルプラグインの設定確認、またはReclaimPolicyをRetainやDeleteに変更する対応を行う。

## 通知種別

Kubernetes Event（EventType: Normal / Warning）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（recycleVolumeOperationはgoroutineで実行） |
| 優先度 | 中（PVの再利用がブロックされる） |
| リトライ | コントローラの同期ループにより自動再試行 |

### 送信先決定ロジック

PVオブジェクトに対してEventを発行する。

## 通知テンプレート

### Kubernetes Event

| 項目 | 内容 |
|-----|------|
| 送信元コンポーネント | pv controller |
| EventType | Normal（Pod使用中時） / Warning（プラグイン未検出時・リサイクル失敗時） |
| Reason | VolumeFailedRecycle |
| 関連オブジェクト | PersistentVolume |

### 本文テンプレート

Pod使用中：
```
Volume is used by pods: %s
```

プラグイン未検出：
```
No recycler plugin found for the volume!
```

リサイクル処理失敗：
```
Recycle failed: %s
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| pods | ボリューム使用中のPod名一覧 | isVolumeUsed()の戻り値 | Yes（Pod使用中時） |
| err | リサイクル失敗のエラー詳細 | plugin.Recycle()の戻り値 | Yes（リサイクル失敗時） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| PV Controller同期 | recycleVolumeOperation | PVがPodにより使用中 | pv_controller.go 1265-1268行目 |
| PV Controller同期 | recycleVolumeOperation | リサイクルプラグイン未検出 | pv_controller.go 1281行目 |
| PV Controller同期 | recycleVolumeOperation | plugin.Recycle()が失敗 | pv_controller.go 1294-1297行目 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| PVが再利用不要 | isVolumeReleasedがfalseの場合、リサイクル処理自体がスキップ |
| PVが使用中かつPVCがキャッシュに存在 | 同名の新しいPVCが存在する場合、古いPVはリサイクル対象となる |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[recycleVolumeOperation開始] --> B[最新のPV情報を取得]
    B --> C{PVは再利用が必要?}
    C -->|No| D[処理スキップ]
    C -->|Yes| E[isVolumeUsed チェック]
    E --> F{Podが使用中?}
    F -->|Yes| G{PVCがキャッシュに存在?}
    G -->|No| H[Event: VolumeFailedRecycle<br/>Volume is used by pods]
    G -->|Yes| I[リサイクル続行]
    F -->|No| I
    I --> J[リサイクルプラグイン検索]
    J --> K{プラグイン見つかった?}
    K -->|No| L[PVをFailed状態に更新<br/>Event: VolumeFailedRecycle<br/>No recycler plugin found]
    K -->|Yes| M[plugin.Recycle実行]
    M --> N{リサイクル成功?}
    N -->|No| O[PVをFailed状態に更新<br/>Event: VolumeFailedRecycle<br/>Recycle failed]
    N -->|Yes| P[Event: VolumeRecycled]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| PersistentVolumes | PV状態の最新情報取得 | API Server経由で直接取得 |
| PersistentVolumeClaims | PVCの存在確認 | コントローラキャッシュ経由 |
| Pods | ボリューム使用状況の確認 | isVolumeUsed()内で参照 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Events | INSERT | VolumeFailedRecycleイベントの記録 |
| PersistentVolumes | UPDATE | フェーズをFailedに更新（プラグイン未検出時・リサイクル失敗時） |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ボリューム使用中 | PVを使用するPodが存在 | Podの終了を待つか手動削除 |
| プラグイン未検出 | PVタイプに対応するリサイクラープラグインが未登録 | プラグイン設定確認またはReclaimPolicy変更 |
| リサイクル処理失敗 | プラグインのRecycle処理がエラーを返す | エラー詳細を確認し、ストレージバックエンドの状態を検証 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | PVがFailed状態でも、syncVolume()呼び出し時に再試行される（無制限） |
| リトライ間隔 | PV Controller同期ループの間隔に依存 |
| リトライ対象エラー | 全リサイクルエラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | Kubernetes Event APIのレート制限に準拠 |
| 1日あたり上限 | 同一理由のイベントは集約される |

### 配信時間帯

制限なし

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

- イベントメッセージにはPod名やエラー詳細が含まれるが、ストレージの接続情報は含まれない
- Kubernetes RBACによりEventの読み取り権限が制御される

## 備考

- Recycle ReclaimPolicyは非推奨であり、Dynamic ProvisioningとDelete ReclaimPolicyの使用が推奨される
- PVがFailed状態に遷移しても、コントローラは次の同期でリサイクルを再試行する
- `updateVolumePhaseWithEvent` を使用してフェーズ更新とイベント発行を原子的に行う

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.go | `pkg/controller/volume/events/event.go` | 23行目: `VolumeFailedRecycle` 定数定義 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | 1230-1319行目: recycleVolumeOperation全体の処理フロー |

**主要処理フロー**:
1. **1234行目**: 最新のPV情報をAPI Serverから取得
2. **1239行目**: `isVolumeReleased` でリサイクルが必要か判定
3. **1248行目**: `isVolumeUsed` でボリューム使用状況を確認
4. **1265-1268行目**: Pod使用中の場合のVolumeFailedRecycleイベント発行
5. **1278行目**: `FindRecyclablePluginBySpec` でプラグイン検索
6. **1281行目**: プラグイン未検出時のVolumeFailedRecycleイベント発行（Failed状態への更新含む）
7. **1294行目**: `plugin.Recycle` 実行
8. **1297行目**: リサイクル失敗時のVolumeFailedRecycleイベント発行（Failed状態への更新含む）

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

```
PersistentVolumeController.syncVolume()
    |
    +-- reclaimVolume()
            |
            +-- scheduleOperation("recycle-...", recycleVolumeOperation)
                    |
                    +-- kubeClient.CoreV1().PersistentVolumes().Get()
                    +-- isVolumeReleased()
                    +-- isVolumeUsed()
                    |       +-- (used) eventRecorder.Event(volume, Normal, VolumeFailedRecycle, ...)
                    |
                    +-- volumePluginMgr.FindRecyclablePluginBySpec()
                    |       +-- (not found) updateVolumePhaseWithEvent(Failed, Warning, VolumeFailedRecycle, ...)
                    |
                    +-- plugin.Recycle()
                            +-- (error) updateVolumePhaseWithEvent(Failed, Warning, VolumeFailedRecycle, ...)
                            +-- (success) eventRecorder.Event(volume, Normal, VolumeRecycled, ...)
```

### データフロー図

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

PV (Released状態)  ---->  recycleVolumeOperation()  ---->  Kubernetes Event
                          |                                (VolumeFailedRecycle)
                          +-> isVolumeReleased()           |
                          +-> isVolumeUsed()               PV状態更新
                          +-> FindRecyclablePlugin()       (Failed)
                          +-> plugin.Recycle()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/controller/volume/events/event.go` | ソース | VolumeFailedRecycle定数定義（23行目） |
| pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | ソース | recycleVolumeOperation（1230-1319行目） |
| recycle_test.go | `pkg/controller/volume/persistentvolume/recycle_test.go` | テスト | リサイクルロジックのユニットテスト |
