# 画面設計書 29-kubectl diff

## 概要

本ドキュメントは、Kubernetes CLIツール kubectl の `diff` コマンドに関する画面設計書である。kubectl diff は現在のクラスター状態とローカル設定ファイルの差分を表示する。

### 本画面の処理概要

kubectl diff コマンドは、ファイルまたは標準入力で指定された設定と現在のクラスター上のオブジェクト状態を比較し、apply を実行した場合の変更内容を差分（unified diff）形式で表示する。

**業務上の目的・背景**：apply 実行前に変更内容を事前確認するために使用される。設定ミスの早期検出、レビュープロセスでの変更内容の可視化、CI/CDパイプラインでの変更検証において重要な役割を果たす。

**画面へのアクセス方法**：ターミナルから `kubectl diff -f <FILENAME>` を実行する。

**主要な操作・処理内容**：
1. ローカル設定ファイルの読み込み
2. 現在のクラスター状態（LIVE）の取得
3. apply をドライランで実行した場合の状態（MERGED）の計算
4. LIVE と MERGED の差分計算
5. 外部 diff プログラムによる差分表示
6. Secret のデータ値のマスキング

**画面遷移**：通常、kubectl apply の前に実行される。

**権限による表示制御**：対象リソースの get 権限と、ドライラン時の create/patch 権限が必要。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 94 | 差分表示（diff） | 主機能 | クラスター状態とローカル設定の差分表示 |
| 95 | Apply | 連携 | Patcher によるマージ計算に apply のロジックを使用 |

## 画面種別

CLI出力コマンド（バッチ）

## URL/ルーティング

```
kubectl diff -f FILENAME
```

内部的には以下の REST API エンドポイントを使用（ドライラン）：
```
GET    /api/v1/namespaces/{namespace}/{resource}/{name}       (LIVE取得)
PATCH  /api/v1/namespaces/{namespace}/{resource}/{name}?dryRun=All  (MERGED計算)
POST   /api/v1/namespaces/{namespace}/{resource}?dryRun=All          (新規リソース)
```

## 入出力項目

| 項目名 | 種別 | 必須 | 型 | 説明 |
|--------|------|------|-----|------|
| -f, --filename | フラグ | Yes | string[] | 設定ファイルパス |
| -k, --kustomize | フラグ | No | string | Kustomize ディレクトリ |
| -R, --recursive | フラグ | No | bool | ディレクトリを再帰処理 |
| --server-side | フラグ | No | bool | サーバーサイド apply でマージ計算 |
| --field-manager | フラグ | No | string | フィールドマネージャー名（デフォルト: kubectl-client-side-apply） |
| --force-conflicts | フラグ | No | bool | サーバーサイド時のコンフリクト強制解決 |
| --show-managed-fields | フラグ | No | bool | managedFields を差分に含める（デフォルト: false） |
| --concurrency | フラグ | No | int | 並列処理数（デフォルト: 1） |
| --prune | フラグ | No | bool | プルーニング対象リソースを差分に含める |
| --prune-allowlist | フラグ | No | string[] | プルーニング許可リスト |
| -l, --selector | フラグ | No | string | ラベルセレクター |

## 表示項目

| 項目名 | 説明 |
|--------|------|
| 差分出力 | unified diff 形式のYAML差分 |
| Secret マスク | Secret の data フィールドは `***` でマスク |

## イベント仕様

### 1-差分表示

1. Complete: ファイルオプション確認、DynamicClient初期化、Builder初期化、OpenAPI設定
2. Validate: 検証（現在は常にnil返却）
3. Run: Differ作成 -> Builder.Visit -> InfoObject生成 -> Diff.Diff -> DiffProgram.Run

### 2-差分表示（コンフリクト時リトライ）

ResourceVersion の衝突が検出された場合、最大4回リトライする（maxRetries = 4、行85）。

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| diff実行 | 対象リソース | SELECT + dryRun | ドライランのためリソースの実際の変更は発生しない |

kubectl diff はドライランモードで動作するため、クラスター上のリソースを実際に変更しない。

### テーブル別更新項目詳細

該当なし（ドライラン）

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|------------|------|--------------|----------|
| M-01 | 終了コード0 | （差分なし） | 変更がない場合 |
| M-02 | 終了コード1 | （差分あり） | 変更がある場合 |
| M-03 | 終了コード>1 | エラーメッセージ | kubectl または diff プログラムのエラー |
| M-04 | エラー | "Unexpected args: %v" | 不正な引数 |
| M-05 | エラー | "--force-conflicts only works with --server-side" | SSA未使用でforce-conflicts指定 |

## 例外処理

| 例外 | 状態 | 対処 |
|------|------|------|
| ファイル未指定 | エラー終了 | RequireFilenameOrKustomize によるエラー |
| ResourceVersion コンフリクト | リトライ | 最大4回リトライ後、ロックなしで差分計算 |
| 外部 diff プログラムエラー | エラー終了 | 終了コード2以上のエラー |
| ドライラン非対応 | エラー終了 | サーバーがドライランをサポートしていない |

## 備考

- 出力は常にYAML形式（行57）。
- KUBECTL_EXTERNAL_DIFF 環境変数で外部 diff プログラムを指定可能。デフォルトは `diff -u -N`（行59-64）。
- Secret の data フィールドは自動的にマスクされる。値が異なる場合は `*** (before)` / `*** (after)` で区別される（行88-92）。
- `--show-managed-fields=false`（デフォルト）の場合、managedFields はdiff出力から除外される（行576-580）。
- ResourceVersion コンフリクト時の最大リトライ回数は4回（行85: maxRetries = 4）。
- `--concurrency` で並列処理数を指定可能。大きい値はより高速だが、メモリ/IO消費が増加する（行172）。
- 終了コード: 0=差分なし、1=差分あり、>1=エラー（行66-72）。
- --prune フラグにより、プルーニング対象のリソースも差分表示に含められる。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | DiffOptions（行103-123）: FilenameOptions, ServerSideApply, Concurrency, Builder, Diff 等 |
| 1-2 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | InfoObject（行324-335）: LocalObj, Info, OpenAPIGetter, ServerSideApply 等 |
| 1-3 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | Masker（行444-447）: Secret マスク処理 |

**読解のコツ**: Object インターフェース（行315-320）の Live() と Merged() が差分計算の核。InfoObject がその実装。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | NewCmdDiff（行134-179）: コマンド定義、フラグ登録 |

#### Step 3: 差分計算ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | Run（行684-780）: Differ作成 -> Builder.Visit -> InfoObject -> Differ.Diff |
| 3-2 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | InfoObject.Live（行340-342）: 現在のクラスター状態 |
| 3-3 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | InfoObject.Merged（行346-408）: apply ドライラン結果 |
| 3-4 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | Differ.Diff（行566-597）: LIVE/MERGEDの比較、Secret マスク適用 |

#### Step 4: 外部 diff プログラム実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | DiffProgram.Run（行217-228）: 外部 diff コマンド実行 |
| 4-2 | diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | getCommand（行189-214）: KUBECTL_EXTERNAL_DIFF 環境変数処理 |

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

```
NewCmdDiff (行134)
    |
    +-- Complete (行624)
    |       +-- FilenameOptions.RequireFilenameOrKustomize
    |       +-- ServerSideApply, FieldManager 取得
    |       +-- DynamicClient, Builder 初期化
    |
    +-- Validate (行783)
    |
    +-- Run (行684)
            |
            +-- NewDiffer("LIVE", "MERGED") (行685)
            |       +-- 2つの一時ディレクトリ作成
            |
            +-- Builder.Visit (行705)
            |       |
            |       +-- [各リソースについて]
            |               +-- info.Get() (LIVEオブジェクト取得)
            |               +-- InfoObject 作成
            |               +-- Differ.Diff (行744)
            |                       +-- InfoObject.Live() (行340)
            |                       +-- InfoObject.Merged() (行346)
            |                       |       +-- [SSA] helper.Patch(ApplyPatchType, dryRun)
            |                       |       +-- [CSA-new] helper.Create(dryRun)
            |                       |       +-- [CSA-exist] Patcher.Patch(dryRun)
            |                       +-- omitManagedFields (行599)
            |                       +-- [Secret] NewMasker (行583)
            |                       +-- Print to temp files
            |
            +-- [--prune 指定時]
            |       +-- pruner.pruneAll
            |       +-- Print pruned objects to LIVE dir
            |
            +-- Differ.Run (行779)
                    +-- DiffProgram.Run (行217)
                            +-- getCommand (行189)
                            +-- diff(1) or KUBECTL_EXTERNAL_DIFF
```

### データフロー図

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

ローカルファイル         Complete
(-f FILENAME) --------> (Builder, Client初期化)
                              |
                         Builder.Visit
                         (各リソース処理)
                              |
                    +---------+---------+
                    |                   |
              info.Get()          InfoObject.Merged()
              (LIVE取得)          (dryRun apply)
                    |                   |
              temp/LIVE/           temp/MERGED/
              (YAMLファイル)       (YAMLファイル)
                    |                   |
                    +-------+-----------+
                            |
                    [Secret] Masker
                    (dataフィールドマスク)
                            |
                    DiffProgram.Run
                    (diff -u -N or KUBECTL_EXTERNAL_DIFF)
                            |
                    stdout (差分出力)
                    exit code: 0=変更なし, 1=変更あり
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | ソース | diff コマンドの主実装 |
| prune.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/prune.go` | ソース | diff 用プルーニングロジック |
| apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | ソース | Patcher (マージ計算に使用) |
| diff_test.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff_test.go` | テスト | ユニットテスト |
