# 通知設計書 58-ProvisioningFailed

## 概要

本ドキュメントは、PersistentVolumeの動的プロビジョニングに失敗した際に発行されるイベント通知「ProvisioningFailed」の設計について記述する。

### 本通知の処理概要

**業務上の目的・背景**：Kubernetes動的プロビジョニングはPVCの作成に応じてPVを自動的に作成するメカニズムである。プロビジョニングが失敗するとPodがストレージを利用できず起動に失敗する。この通知は、プロビジョニング失敗の具体的な原因をクラスタ管理者に伝達し、StorageClass設定、ストレージバックエンド、プロビジョナープラグインの問題を迅速に特定・解決するために存在する。ProvisioningFailedは最も多くの発行箇所を持つイベントの一つであり、プロビジョニングプロセスの様々な段階で発生しうる。

**通知の送信タイミング**：PV Controllerの `provisionClaim`、`provisionClaimOperation`、`provisionClaimOperationExternal` の各メソッド内で、以下の状況で発行される：(1) プロビジョニング可能なプラグインが見つからない場合、(2) CSIプラグイン以外でDataSourceが指定された場合、(3) マウントオプション非対応のプラグインにマウントオプションが指定された場合、(4) プロビジョナー作成に失敗した場合、(5) ターゲットノード取得に失敗した場合、(6) ボリュームプロビジョニング自体が失敗した場合、(7) PVオブジェクト作成が失敗した場合、(8) CSI名変換が失敗した場合、(9) PVCアノテーション保存が失敗した場合。

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

**通知内容の概要**：プロビジョニング失敗の具体的な原因（プラグイン未検出、DataSource非対応、マウントオプション非対応、プロビジョナーエラー、ボリューム作成エラー、PVオブジェクト作成エラー等）が含まれる。

**期待されるアクション**：受信者はエラーメッセージを分析し、StorageClassの設定確認、プロビジョナーの稼働確認、ストレージバックエンドの状態確認、PVCの仕様確認を行い、問題を解決する。

## 通知種別

Kubernetes Event（EventType: Warning）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（provisionClaim内）/ 非同期（provisionClaimOperation / provisionClaimOperationExternal はgoroutineで実行） |
| 優先度 | 高（ストレージプロビジョニング失敗はPod起動を阻害） |
| リトライ | コントローラの同期ループにより自動再試行 |

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

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

## 通知テンプレート

### Kubernetes Event

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

### 本文テンプレート

プラグイン未検出時：
```
{findProvisionablePluginのエラーメッセージ}
```

DataSource非対応時：
```
plugin "%s" is not a CSI plugin. Only CSI plugin can provision a claim with a datasource
```

マウントオプション非対応時：
```
Mount options are not supported by the provisioner but StorageClass "%s" has mount options %v
```

プロビジョナー作成失敗時：
```
Failed to create provisioner: %v
```

ターゲットノード取得失敗時：
```
Failed to get target node: %v
```

ボリュームプロビジョニング失敗時：
```
Failed to provision volume with StorageClass "%s": %v
```

PVオブジェクト作成失敗時：
```
Error creating provisioned PV object for claim %s: %v. Deleting the volume.
```

CSI名変換失敗時：
```
error getting CSI name for In tree plugin %s: %v
```

PVC保存失敗時：
```
Error saving claim: %v
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| pluginName | プロビジョナープラグイン名 | plugin.GetPluginName() | Yes（一部ケース） |
| storageClass.Name | StorageClass名 | StorageClass.ObjectMeta.Name | Yes（一部ケース） |
| err | エラーの詳細 | 各関数の戻り値 | Yes |
| claim | PVCのキー（namespace/name） | claimToClaimKey(claim) | Yes（一部ケース） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| PV Controller同期 | provisionClaim | findProvisionablePluginがエラー | pv_controller.go 1586行目 |
| PV Controller同期 | provisionClaimOperation | CSI以外のプラグインでDataSource指定 | pv_controller.go 1633行目 |
| PV Controller同期 | provisionClaimOperation | マウントオプション非対応プラグイン | pv_controller.go 1688行目 |
| PV Controller同期 | provisionClaimOperation | プロビジョナー作成失敗 | pv_controller.go 1697行目 |
| PV Controller同期 | provisionClaimOperation | ターゲットノード取得失敗 | pv_controller.go 1707行目 |
| PV Controller同期 | provisionClaimOperation | ボリュームプロビジョニング失敗 | pv_controller.go 1724行目 |
| PV Controller同期 | provisionClaimOperation | PVオブジェクト作成失敗 | pv_controller.go 1782行目 |
| PV Controller同期 | provisionClaimOperationExternal | CSI名変換失敗 | pv_controller.go 1838行目 |
| PV Controller同期 | provisionClaimOperationExternal | PVCアノテーション保存失敗 | pv_controller.go 1848行目 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 動的プロビジョニング無効 | enableDynamicProvisioningがfalseの場合、provisionClaim自体が何もせずreturn |
| PV既存 | 同名のPVが既に存在する場合、プロビジョニングはスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[provisionClaim開始] --> B[findProvisionablePlugin]
    B --> C{プラグイン検出成功?}
    C -->|No| D[Event: ProvisioningFailed]
    C -->|Yes| E{in-tree or external?}
    E -->|in-tree| F[provisionClaimOperation]
    E -->|external| G[provisionClaimOperationExternal]

    F --> H{DataSource + non-CSI?}
    H -->|Yes| I[Event: ProvisioningFailed]
    H -->|No| J{マウントオプション非対応?}
    J -->|Yes| K[Event: ProvisioningFailed]
    J -->|No| L[provisioner.Provision実行]
    L --> M{プロビジョニング成功?}
    M -->|No| N[Event: ProvisioningFailed]
    M -->|Yes| O[PVオブジェクト作成]
    O --> P{作成成功?}
    P -->|No| Q[Event: ProvisioningFailed + ボリューム削除試行]
    P -->|Yes| R[Event: ProvisioningSucceeded]

    G --> S{CSI名変換成功?}
    S -->|No| T[Event: ProvisioningFailed]
    S -->|Yes| U[setClaimProvisioner]
    U --> V{保存成功?}
    V -->|No| W[Event: ProvisioningFailed]
    V -->|Yes| X[Event: ExternalProvisioning]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| PersistentVolumeClaims | PVCの情報取得 | コントローラキャッシュ経由 |
| StorageClasses | プロビジョナー設定の取得 | Lister経由 |
| PersistentVolumes | 既存PVの存在確認 | API Server経由 |
| Nodes | ターゲットノードの取得 | NodeLister経由 |

### 更新テーブル一覧

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

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| プラグイン未検出 | StorageClassのProvisionerに対応するプラグインが未登録 | CSIドライバーのデプロイ確認 |
| DataSource非対応 | non-CSIプラグインでDataSourceが指定 | CSI対応のStorageClassに変更 |
| マウントオプション非対応 | プラグインがマウントオプションをサポートしない | StorageClassからマウントオプションを削除 |
| プロビジョナー作成失敗 | プラグイン内部のエラー | プラグインのログを確認 |
| ボリューム作成失敗 | ストレージバックエンドのエラー | バックエンドの容量・設定を確認 |
| PVオブジェクト作成失敗 | API Serverのエラー（複数回リトライ後） | API Serverの状態確認 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | PVオブジェクト作成は5回リトライ（createProvisionedPVRetryCount）、その他はコントローラ同期ループで再試行 |
| リトライ間隔 | PVオブジェクト作成は10秒間隔（createProvisionedPVInterval）、その他はPV Controller同期間隔 |
| リトライ対象エラー | 全プロビジョニングエラー |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし

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

- エラーメッセージにストレージバックエンドの内部情報が含まれる場合がある
- StorageClass名やプラグイン名はインフラ構成の一部であるが、機密情報ではない
- Kubernetes RBACによりEventの読み取り権限が制御される

## 備考

- ProvisioningFailedは9箇所から発行される最も発行箇所の多いイベントの一つ
- PVオブジェクト作成失敗時は、作成されたストレージアセットの削除も試行される
- ボリュームプロビジョニング失敗時は `rescheduleProvisioning` が呼ばれ、スケジューラに再スケジュールを通知する
- PVオブジェクト作成リトライは `createProvisionedPVRetryCount` = 5回、間隔 = 10秒

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | event.go | `pkg/controller/volume/events/event.go` | 29行目: `ProvisioningFailed` 定数定義 |
| 1-2 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | 124-127行目: createProvisionedPVRetryCount/Interval定数定義 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | 1574-1612行目: provisionClaim - プラグイン検索と分岐 |

**主要処理フロー**:
1. **1583行目**: `findProvisionablePlugin(claim)` 呼び出し
2. **1586行目**: プラグイン未検出時のProvisioningFailedイベント発行
3. **1599行目**: plugin == nil で external プロビジョニング
4. **1602行目**: plugin != nil で internal プロビジョニング

#### Step 3: 内部プロビジョニングのエラーパスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | 1614-1818行目: provisionClaimOperation全体 |

**主要処理フロー**:
- **1628-1634行目**: DataSource + non-CSIチェック
- **1685-1689行目**: マウントオプション非対応チェック
- **1693-1698行目**: プロビジョナー作成
- **1701-1710行目**: ターゲットノード取得
- **1714-1725行目**: provisioner.Provision実行
- **1750-1782行目**: PVオブジェクト作成リトライ
- **1805-1810行目**: クリーンアップ失敗時のProvisioningCleanupFailed

#### Step 4: 外部プロビジョニングのエラーパスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | 1820-1861行目: provisionClaimOperationExternal |

**主要処理フロー**:
- **1832-1840行目**: CSI移行と名前変換
- **1843-1849行目**: PVCアノテーション設定

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

```
PersistentVolumeController.syncUnboundClaim()
    |
    +-- provisionClaim(claim)
            |
            +-- findProvisionablePlugin(claim)
            |       +-- (error) eventRecorder.Event(claim, Warning, ProvisioningFailed, ...)  [1586]
            |
            +-- (plugin != nil) provisionClaimOperation(claim, plugin, storageClass)
            |       +-- (DataSource + non-CSI) eventRecorder.Event(..., ProvisioningFailed, ...)  [1633]
            |       +-- (MountOption不可) eventRecorder.Event(..., ProvisioningFailed, ...)  [1688]
            |       +-- (Provisioner作成失敗) eventRecorder.Event(..., ProvisioningFailed, ...)  [1697]
            |       +-- (ノード取得失敗) eventRecorder.Event(..., ProvisioningFailed, ...)  [1707]
            |       +-- (Provision失敗) eventRecorder.Event(..., ProvisioningFailed, ...)  [1724]
            |       +-- (PV作成失敗) eventRecorder.Event(..., ProvisioningFailed, ...)  [1782]
            |
            +-- (plugin == nil) provisionClaimOperationExternal(claim, storageClass)
                    +-- (CSI名変換失敗) eventRecorder.Event(..., ProvisioningFailed, ...)  [1838]
                    +-- (PVC保存失敗) eventRecorder.Event(..., ProvisioningFailed, ...)  [1848]
```

### データフロー図

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

PVC Spec               ---->  provisionClaim()              ---->  Kubernetes Event
(StorageClassName,             |                                    (ProvisioningFailed)
 DataSource,                   +-> findProvisionablePlugin()
 Resources)                    +-> provisionClaimOperation()
                               |   +-> provisioner.Provision()
StorageClass            ---->  |   +-> kubeClient.PV.Create()
(Provisioner,                  +-> provisionClaimOperationExternal()
 ReclaimPolicy,                    +-> setClaimProvisioner()
 MountOptions,
 AllowedTopologies)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| event.go | `pkg/controller/volume/events/event.go` | ソース | ProvisioningFailed定数定義（29行目） |
| pv_controller.go | `pkg/controller/volume/persistentvolume/pv_controller.go` | ソース | 全9箇所のProvisioningFailed発行ロジック |
| provision_test.go | `pkg/controller/volume/persistentvolume/provision_test.go` | テスト | プロビジョニングロジックのユニットテスト |
| pv_controller_test.go | `pkg/controller/volume/persistentvolume/pv_controller_test.go` | テスト | PVコントローラのユニットテスト |
