# 機能設計書 141-PersistentVolume管理

## 概要

本ドキュメントは、KubernetesにおけるPersistentVolume（PV）リソースのレジストリ層での管理機能について記述する。PVの作成、取得、更新、削除およびステータス管理のREST APIエンドポイントの設計を対象とする。

### 本機能の処理概要

**業務上の目的・背景**：PersistentVolumeはKubernetesクラスター内の永続的なストレージリソースを抽象化するクラスタースコープのリソースである。管理者がストレージをプロビジョニングし、ユーザー（PersistentVolumeClaim）がそのストレージを要求するモデルを実現するために、PVの作成・バインディング・リサイクルといったライフサイクル管理が必要となる。

**機能の利用シーン**：クラスター管理者が物理ストレージ（NFS、iSCSI、クラウドディスクなど）をPVリソースとして登録する際、またはCSIドライバーによる動的プロビジョニングでPVが自動作成される際に本機能が呼び出される。PVコントローラーによるバインディングやフェーズ遷移の管理においても、ステータスサブリソースの更新が行われる。

**主要な処理内容**：
1. PVリソースのCRUD操作をREST APIとして提供（etcdストレージへの永続化）
2. 作成時のステータス初期化（Phase=Pendingの設定、LastPhaseTransitionTimeの記録）
3. 更新時のSpec/Statusフィールド分離保護（SpecはメインAPI、StatusはサブリソースAPIで更新）
4. バリデーションの実行（PVスペックの検証、ボリューム固有の検証）
5. フェーズ遷移時のLastPhaseTransitionTime自動更新
6. フィーチャーゲートに基づく無効フィールドのドロップ処理

**関連システム・外部連携**：PVコントローラー（PV-PVCバインディング）、CSIドライバー（動的プロビジョニング）、各種ボリュームプラグイン（NFS、iSCSI等）、API Server（etcdストレージ）

**権限による制御**：PVはクラスタースコープのリソースであり、作成・更新・削除にはクラスター管理者権限（ClusterRole）が必要。RBACにより操作制限が行われる。

## 関連画面

本機能は直接的にkubectl画面からの操作ではなく、API Server経由でPVリソースのCRUD操作を提供するバックエンド機能である。kubectlからは`kubectl get pv`、`kubectl create`、`kubectl apply`等のコマンドで間接的に利用される。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | kubectl get | 参照画面 | PVリソースの一覧取得・詳細表示 |
| - | kubectl create/apply | 操作画面 | PVリソースの作成・更新 |

## 機能種別

CRUD操作 / リソースライフサイクル管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| metadata.name | string | Yes | PV名（クラスター内で一意） | DNS Subdomain Name形式 |
| spec.capacity | ResourceList | Yes | ストレージ容量 | storage項目が必須 |
| spec.accessModes | []PersistentVolumeAccessMode | Yes | アクセスモード（RWO/ROX/RWX） | 有効なアクセスモード値 |
| spec.persistentVolumeReclaimPolicy | PersistentVolumeReclaimPolicy | No | 回収ポリシー（Retain/Delete/Recycle） | 有効なポリシー値 |
| spec.storageClassName | string | No | ストレージクラス名 | 存在するStorageClassを参照 |
| spec.volumeMode | *PersistentVolumeMode | No | ボリュームモード（Filesystem/Block） | 有効なモード値 |
| spec.claimRef | *ObjectReference | No | バインド先PVCへの参照 | 有効なNamespace/Name |
| spec.[volumeSource] | various | Yes | ボリュームソース（NFS, CSI等のいずれか） | ソースタイプ固有のバリデーション |

### 入力データソース

API ServerへのHTTPリクエスト（REST API）。PVマニフェスト（YAML/JSON）がリクエストボディとして送信される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| metadata | ObjectMeta | リソースメタデータ（name, uid, resourceVersion等） |
| spec | PersistentVolumeSpec | PVのスペック情報 |
| status.phase | PersistentVolumePhase | PVのフェーズ（Pending/Available/Bound/Released/Failed） |
| status.message | string | フェーズに関する詳細メッセージ |
| status.reason | string | フェーズの理由 |
| status.lastPhaseTransitionTime | *Time | 最後のフェーズ遷移時刻 |

### 出力先

etcdストレージへの永続化、APIレスポンスとしてクライアントに返却。

## 処理フロー

### 処理シーケンス

```
1. REST APIリクエスト受信
   └─ API ServerがHTTPリクエストを受け付け、認証・認可を通過
2. リソースオブジェクトのデシリアライズ
   └─ リクエストボディをPersistentVolumeオブジェクトに変換
3. PrepareForCreate / PrepareForUpdate実行
   └─ 作成時: Status初期化（Phase=Pending, LastPhaseTransitionTime設定）
   └─ 更新時: Statusフィールドを旧オブジェクトから復元（Spec更新のみ許可）
   └─ フィーチャーゲートに基づく無効フィールドのドロップ
4. バリデーション実行
   └─ PV固有バリデーション + ボリュームバリデーション
5. etcdへの永続化
   └─ genericregistry.Storeを通じてetcdに保存
6. レスポンス返却
   └─ 作成/更新後のオブジェクトをクライアントに返却
```

### フローチャート

```mermaid
flowchart TD
    A[REST APIリクエスト受信] --> B{操作種別}
    B -->|Create| C[PrepareForCreate]
    B -->|Update| D[PrepareForUpdate]
    B -->|Status Update| E[Status PrepareForUpdate]
    C --> F[Status初期化: Phase=Pending]
    F --> G[DropDisabledSpecFields]
    G --> H[Validate]
    D --> I[Status復元: 旧値保持]
    I --> J[DropDisabledSpecFields]
    J --> K[ValidateUpdate]
    E --> L[Spec復元: 旧値保持]
    L --> M[PhaseTransitionTime更新判定]
    M --> N[ValidateStatusUpdate]
    H --> O[etcdへ永続化]
    K --> O
    N --> O
    O --> P[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-141-01 | NamespaceScoped=false | PVはクラスタースコープリソースであり、Namespaceに属さない | 常時 |
| BR-141-02 | Status保護（作成時） | 作成時にユーザーが指定したStatusはリセットされ、Phase=Pendingに初期化される | PV作成時 |
| BR-141-03 | Status保護（更新時） | Spec更新APIではStatusフィールドは旧オブジェクトの値が維持される | PVのSpec更新時 |
| BR-141-04 | Spec保護（ステータス更新時） | ステータス更新APIではSpecフィールドは旧オブジェクトの値が維持される | PVのStatus更新時 |
| BR-141-05 | PhaseTransitionTime自動更新 | フェーズが変更された場合、クライアントが遷移時刻を指定しなければ自動的に現在時刻が設定される | ステータス更新でフェーズが変わった時 |
| BR-141-06 | 無条件更新許可 | AllowUnconditionalUpdate=trueであり、resourceVersionを指定しなくても更新可能 | PV更新時 |
| BR-141-07 | 削除時オブジェクト返却 | ReturnDeletedObject=trueであり、削除時に削除されたオブジェクトが返却される | PV削除時 |

### 計算ロジック

PhaseTransitionTimeの更新判定ロジック:
- フェーズが変わらず、新しいLastPhaseTransitionTimeがnilの場合: 既存の遷移時刻を保持
- フェーズが変わり、新しいLastPhaseTransitionTimeがnilまたは旧値と同一の場合: 現在時刻に更新

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| PV作成 | etcd /registry/persistentvolumes/{name} | INSERT | PVオブジェクトの新規作成 |
| PV取得 | etcd /registry/persistentvolumes/{name} | SELECT | PVオブジェクトの取得 |
| PV一覧 | etcd /registry/persistentvolumes/ | SELECT | PVオブジェクト一覧の取得 |
| PV更新 | etcd /registry/persistentvolumes/{name} | UPDATE | PVオブジェクトのSpec更新 |
| PVステータス更新 | etcd /registry/persistentvolumes/{name} | UPDATE | PVオブジェクトのStatus更新 |
| PV削除 | etcd /registry/persistentvolumes/{name} | DELETE | PVオブジェクトの削除 |

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

#### etcd: persistentvolumes

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | 全フィールド | PrepareForCreate後のオブジェクト | Status.Phase=Pending |
| UPDATE (spec) | spec.* | 新しいSpec、Status=旧Status | Status変更不可 |
| UPDATE (status) | status.* | 新しいStatus、Spec=旧Spec | Spec変更不可 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | BadRequest | バリデーションエラー（不正なSpec値） | エラーメッセージに基づきマニフェストを修正 |
| 404 | NotFound | 指定PVが存在しない | PV名を確認 |
| 409 | Conflict | resourceVersionの競合（楽観的ロック） | 最新を取得して再試行 |
| 422 | UnprocessableEntity | 更新時のバリデーションエラー | エラー内容に基づき修正 |

### リトライ仕様

resourceVersion競合（409）の場合、クライアント側でGet -> Modify -> Updateのパターンで再試行する。

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

etcdの楽観的並行制御（Optimistic Concurrency Control）に基づく。resourceVersionフィールドによりCAS（Compare-and-Swap）操作が行われる。ただしAllowUnconditionalUpdate=trueのため、resourceVersionを省略した無条件更新も許可される。

## パフォーマンス要件

API Serverのレスポンス時間に準拠。etcdへの単一オブジェクト操作であるため、通常は数ミリ秒以内で完了する。大量のPVリスト取得時はページネーションの利用を推奨。

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

- PVはクラスタースコープリソースであるため、RBAC ClusterRoleによる権限制御が必要
- PVにはストレージバックエンドの接続情報（NFSサーバーアドレス等）が含まれるため、読み取り権限の管理が重要
- CSI PVではnodeStageSecretRef等のSecretへの参照を含む場合があり、適切なアクセス制御が必要

## 備考

- PVの短縮名は「pv」であり、`kubectl get pv`で一覧取得可能
- フィールドセレクタとして`metadata.name`および`name`（後方互換性のためのバグ互換フィールド）が使用可能
- PVのラベルセレクタによるフィルタリングもサポートされる

---

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

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

### 推奨読解順序

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

PersistentVolumeのAPI型定義を理解することが出発点となる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.go | `pkg/apis/core/types.go` | PersistentVolume, PersistentVolumeSpec, PersistentVolumeStatus構造体の定義 |
| 1-2 | types.go | `staging/src/k8s.io/api/core/v1/types.go` | 外部APIバージョン(v1)のPV型定義 |

**読解のコツ**: Kubernetesでは内部型（`pkg/apis/core`）と外部型（`k8s.io/api/core/v1`）が分離されている。レジストリ層は内部型を使用し、APIサーバーが自動的にバージョン変換を行う。

#### Step 2: ストラテジー（ビジネスロジック）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | strategy.go | `pkg/registry/core/persistentvolume/strategy.go` | PVのCRUD操作に関するストラテジーの全体像 |

**主要処理フロー**:
1. **39-47行目**: persistentvolumeStrategy構造体の定義とStrategy変数の初期化
2. **49-51行目**: NamespaceScoped()=false -- PVはクラスタースコープ
3. **66-74行目**: PrepareForCreate -- Status初期化、Phase=Pending設定
4. **76-81行目**: Validate -- PV固有のバリデーション + ボリュームバリデーション
5. **97-102行目**: PrepareForUpdate -- Status保護（旧値復元）
6. **122-138行目**: StatusStrategy -- ステータスサブリソースのストラテジー
7. **143-158行目**: StatusStrategy.PrepareForUpdate -- Spec保護、PhaseTransitionTime自動更新

#### Step 3: RESTストレージを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | storage.go | `pkg/registry/core/persistentvolume/storage/storage.go` | REST/StatusREST構造体とetcdストレージの設定 |

**主要処理フロー**:
- **41-67行目**: NewREST関数 -- genericregistry.Storeの設定とStatusREST分離
- **42-56行目**: Store設定 -- NewFunc, PredicateFunc, CreateStrategy等の設定
- **62-66行目**: statusStore作成 -- UpdateStrategyをStatusStrategyに差し替え
- **73-75行目**: ShortNames -- "pv"の短縮名定義
- **99-103行目**: StatusREST.Update -- forceAllowCreate=false（サブリソースは作成不可）

#### Step 4: バリデーションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | validation.go | `pkg/apis/core/validation/validation.go` | ValidatePersistentVolume関数の詳細 |
| 4-2 | validation.go | `pkg/volume/validation/pv_validation.go` | ボリューム固有のバリデーション |

#### Step 5: ユーティリティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | helpers.go | `pkg/api/persistentvolume/util.go` | DropDisabledSpecFields, GetWarningsForPersistentVolume |

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

```
API Server HTTP Handler
    |
    +-- genericregistry.Store (etcd操作の汎用実装)
    |       |
    |       +-- persistentvolume.Strategy (ビジネスロジック)
    |       |       +-- PrepareForCreate()
    |       |       |       +-- pvutil.DropDisabledSpecFields()
    |       |       +-- Validate()
    |       |       |       +-- validation.ValidatePersistentVolume()
    |       |       |       +-- volumevalidation.ValidatePersistentVolume()
    |       |       +-- PrepareForUpdate()
    |       |       |       +-- pvutil.DropDisabledSpecFields()
    |       |       +-- ValidateUpdate()
    |       |
    |       +-- persistentvolume.StatusStrategy (ステータスサブリソース)
    |               +-- PrepareForUpdate() [Spec保護, PhaseTransitionTime更新]
    |               +-- ValidateUpdate()
    |
    +-- persistentvolume.GetAttrs() (フィルタリング)
            +-- PersistentVolumeToSelectableFields()
```

### データフロー図

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

PVマニフェスト ──────> API Server ─────> Strategy.PrepareForCreate ──> etcd永続化
(YAML/JSON)          (認証/認可)        Strategy.Validate            (PVオブジェクト)
                                        |
PV更新リクエスト ──> API Server ─────> Strategy.PrepareForUpdate ──> etcd更新
                     (認証/認可)        Strategy.ValidateUpdate
                                        |
PVステータス更新 ──> API Server ─────> StatusStrategy.PrepareForUpdate > etcd更新
                     (認証/認可)        StatusStrategy.ValidateUpdate
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| strategy.go | `pkg/registry/core/persistentvolume/strategy.go` | ソース | PVのCRUDストラテジー（ビジネスロジック） |
| storage.go | `pkg/registry/core/persistentvolume/storage/storage.go` | ソース | REST/StatusRESTエンドポイントとetcdストレージ設定 |
| doc.go | `pkg/registry/core/persistentvolume/doc.go` | ソース | パッケージドキュメント |
| strategy_test.go | `pkg/registry/core/persistentvolume/strategy_test.go` | テスト | ストラテジーのユニットテスト |
| storage_test.go | `pkg/registry/core/persistentvolume/storage/storage_test.go` | テスト | ストレージ層のテスト |
| util.go | `pkg/api/persistentvolume/util.go` | ソース | PVユーティリティ（フィーチャーゲート処理等） |
| validation.go | `pkg/apis/core/validation/validation.go` | ソース | PVバリデーション |
| pv_validation.go | `pkg/volume/validation/pv_validation.go` | ソース | ボリューム固有バリデーション |
| types.go | `pkg/apis/core/types.go` | ソース | 内部API型定義 |
