# 通知設計書 59-ProvisioningCleanupFailed

## 概要

本ドキュメントは、ボリュームプロビジョニング後のクリーンアップに失敗した際に発行されるイベント通知「ProvisioningCleanupFailed」の設計について記述する。

### 本通知の処理概要

**業務上の目的・背景**：動的プロビジョニングでは、ストレージバックエンド上にボリュームを作成した後、そのボリュームを表すPVオブジェクトをKubernetes API Serverに作成する。PVオブジェクトの作成が失敗した場合、ストレージバックエンド上に孤立したボリューム（orphaned volume）が残る。コントローラはこの孤立ボリュームの削除を試みるが、その削除も失敗した場合にProvisioningCleanupFailedイベントが発行される。これは、ストレージバックエンド上に手動で削除が必要な孤立リソースが存在することをクラスタ管理者に通知する、非常に重要な通知である。

**通知の送信タイミング**：PV Controllerの `provisionClaimOperation` 内で、PVオブジェクトの作成が5回のリトライ後に失敗し、さらにストレージバックエンド上のボリューム削除も5回のリトライ後に失敗した場合に発行される。

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

**通知内容の概要**：「Error cleaning provisioned volume for claim {PVCキー}: {エラー詳細}. Please delete manually.」という手動削除を促すメッセージが含まれる。

**期待されるアクション**：受信者はストレージバックエンド（クラウドプロバイダーのボリューム管理画面、NFSサーバ等）にアクセスし、孤立ボリュームを手動で削除する。この通知は自動回復が不可能なケースを示すため、必ず手動対応が必要である。

## 通知種別

Kubernetes Event（EventType: Warning）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（provisionClaimOperationはgoroutineで実行） |
| 優先度 | 最高（孤立リソースが発生し、手動削除が必要） |
| リトライ | 不要（既にボリューム削除を複数回リトライした後の最終状態通知） |

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

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

## 通知テンプレート

### Kubernetes Event

| 項目 | 内容 |
|-----|------|
| 送信元コンポーネント | pv controller |
| EventType | Warning |
| Reason | ProvisioningCleanupFailed |
| 関連オブジェクト | PersistentVolumeClaim |

### 本文テンプレート

```
Error cleaning provisioned volume for claim %s: %v. Please delete manually.
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| claimKey | PVCのキー（namespace/name） | claimToClaimKey(claim) | Yes |
| deleteErr | ボリューム削除エラーの詳細 | doDeleteVolume()の戻り値 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| PV Controller同期 | provisionClaimOperation | PVオブジェクト作成失敗後、ストレージボリューム削除も失敗 | pv_controller.go 1805-1810行目 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| PVオブジェクト作成成功 | PVが正常に作成された場合はProvisioningSucceededが発行される |
| ボリューム削除成功 | ストレージアセットの削除に成功した場合はProvisioningCleanupFailedは発行されない |
| 削除プラグイン未検出 | doDeleteVolumeのdeletedがfalseの場合（内部プラグインが見つからない場合）はエラーログのみ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[provisionClaimOperation] --> B[provisioner.Provision成功]
    B --> C[PVオブジェクト作成試行<br/>最大5回リトライ]
    C --> D{PV作成成功?}
    D -->|Yes| E[Event: ProvisioningSucceeded]
    D -->|No| F[Event: ProvisioningFailed<br/>Error creating provisioned PV]
    F --> G[ストレージボリューム削除試行<br/>最大5回リトライ]
    G --> H{ボリューム削除成功?}
    H -->|Yes| I[クリーンアップ完了]
    H -->|No| J[Event: ProvisioningCleanupFailed<br/>Please delete manually]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| PersistentVolumeClaims | PVCキーの取得 | provisionClaimOperationの引数 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Events | INSERT | ProvisioningCleanupFailedイベントの記録 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ストレージバックエンド削除失敗 | ストレージバックエンドへのAPI呼び出しが5回連続で失敗 | ストレージバックエンドに直接アクセスし手動削除 |
| 削除プラグイン未検出 | 内部プラグインが見つからない（通常は到達しないコード） | ストレージバックエンドに直接アクセスし手動削除 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | ボリューム削除は5回（createProvisionedPVRetryCount）リトライ済みの後に発行 |
| リトライ間隔 | 10秒間隔（createProvisionedPVInterval） |
| リトライ対象エラー | このイベント発行後の自動リトライは行われない |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし

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

- エラーメッセージにストレージバックエンドの内部エラー情報が含まれる場合がある
- 「Please delete manually」というメッセージがストレージバックエンドへの直接アクセスを促すため、アクセス権限の管理が重要
- Kubernetes RBACによりEventの読み取り権限が制御される

## 備考

- ProvisioningCleanupFailedは非常に深刻な状態を示す通知であり、ストレージバックエンド上に孤立リソースが存在する
- 自動回復が不可能なため、必ず手動介入が必要
- このイベントは内部プロビジョニング（in-treeプラグイン）の場合にのみ発生する。外部プロビジョナーの場合はprovisionClaimOperationExternalが使用され、PVオブジェクト作成は外部プロビジョナーが担当するため

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.go | `pkg/controller/volume/events/event.go` | 30行目: `ProvisioningCleanupFailed` 定数定義 |
| 1-2 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | 124行目: `createProvisionedPVRetryCount = 5` |
| 1-3 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | 127行目: `createProvisionedPVInterval = 10 * time.Second` |

#### Step 2: PVオブジェクト作成失敗とクリーンアップを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | 1750-1811行目: PVオブジェクト作成リトライとクリーンアップロジック |

**主要処理フロー**:
1. **1751-1773行目**: PVオブジェクト作成を最大5回リトライ
2. **1775-1782行目**: 全リトライ失敗後、ProvisioningFailedイベント発行
3. **1786-1803行目**: ストレージボリューム削除を最大5回リトライ
4. **1805-1810行目**: ボリューム削除も失敗した場合、ProvisioningCleanupFailedイベント発行

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

```
PersistentVolumeController.provisionClaimOperation()
    |
    +-- provisioner.Provision() -> success
    |
    +-- kubeClient.CoreV1().PersistentVolumes().Create()  [リトライ x5]
    |       |
    |       +-- (全失敗) eventRecorder.Event(claim, Warning, ProvisioningFailed, ...)
    |
    +-- doDeleteVolume(volume)  [リトライ x5]
            |
            +-- (全失敗) eventRecorder.Event(claim, Warning, ProvisioningCleanupFailed,
                         "Error cleaning provisioned volume... Please delete manually.")
```

### データフロー図

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

プロビジョニング済み    ---->  PVオブジェクト作成試行       ---->  (失敗)
ストレージボリューム           (5回リトライ)                       |
                               |                                   v
                               +-> ボリューム削除試行      ---->  Kubernetes Event
                                   (5回リトライ)                   (ProvisioningCleanupFailed)
                                                                   |
                                                                   孤立ボリューム
                                                                   (手動削除必要)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/controller/volume/events/event.go` | ソース | ProvisioningCleanupFailed定数定義（30行目） |
| pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | ソース | クリーンアップ失敗ロジック（1805-1810行目） |
| provision_test.go | `pkg/controller/volume/persistentvolume/provision_test.go` | テスト | プロビジョニングロジックのユニットテスト |
