# 画面設計書 30-kubectl apply

## 概要

本ドキュメントは、Kubernetes CLIツール kubectl の `apply` コマンドに関する画面設計書である。kubectl apply は宣言的なリソース管理の中核コマンドであり、ファイルに定義されたリソース設定をクラスターに適用する。

### 本画面の処理概要

kubectl apply コマンドは、JSON/YAML形式の設定ファイルまたは標準入力からリソース定義を読み込み、Kubernetes クラスターに対して宣言的に適用する。存在しないリソースは作成され、既存リソースはパッチ（3-way merge）が適用される。

**業務上の目的・背景**：宣言的なインフラストラクチャ管理（Infrastructure as Code）の基盤コマンドであり、GitOps、CI/CD パイプライン、手動デプロイメントのすべてにおいて中心的な役割を果たす。`create` コマンド（命令的）との違いは、apply は冪等であり、何度実行しても同じ結果が得られる点である。

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

**主要な操作・処理内容**：
1. リソース定義ファイルの読み込みとバリデーション
2. 既存リソースの取得（存在チェック）
3. 新規リソースの場合: Create
4. 既存リソースの場合: 3-way merge patch（クライアントサイド）または Server-Side Apply
5. last-applied-configuration アノテーションの管理
6. プルーニング（--prune 指定時）: 設定ファイルにないリソースの削除

**画面遷移**：kubectl diff の後に実行されることが多い。サブコマンドとして view-last-applied、set-last-applied、edit-last-applied を持つ。

**権限による表示制御**：対象リソースの create, patch, get 権限が必要。--prune 使用時は delete 権限も必要。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 95 | Apply | 主機能 | 宣言的リソース管理の適用処理 |
| 94 | Diff | 連携 | apply 前の変更確認に使用される |

## 画面種別

CLIバッチコマンド（非対話型）

## URL/ルーティング

```
kubectl apply (-f FILENAME | -k DIRECTORY) [options]
```

内部的には以下の REST API エンドポイントを使用：
```
GET    /api/v1/namespaces/{namespace}/{resource}/{name}          (既存確認)
POST   /api/v1/namespaces/{namespace}/{resource}                  (新規作成)
PATCH  /api/v1/namespaces/{namespace}/{resource}/{name}           (更新: Strategic Merge Patch)
PATCH  /api/v1/namespaces/{namespace}/{resource}/{name}           (更新: Apply Patch Type - SSA)
DELETE /api/v1/namespaces/{namespace}/{resource}/{name}           (プルーニング)
```

## 入出力項目

| 項目名 | 種別 | 必須 | 型 | 説明 |
|--------|------|------|-----|------|
| -f, --filename | フラグ | 条件付き | string[] | 設定ファイルパス（-f または -k のいずれかが必須） |
| -k, --kustomize | フラグ | 条件付き | string | Kustomize ディレクトリ |
| -R, --recursive | フラグ | No | bool | ディレクトリの再帰処理 |
| --server-side | フラグ | No | bool | サーバーサイド apply を使用 |
| --field-manager | フラグ | No | string | フィールドマネージャー名（CSA: kubectl-client-side-apply, SSA: kubectl） |
| --force-conflicts | フラグ | No | bool | SSA時のコンフリクト強制解決 |
| --force | フラグ | No | bool | リソースの強制削除と再作成 |
| --dry-run | フラグ | No | string | ドライラン（none/client/server） |
| --prune | フラグ | No | bool | 設定にないリソースをプルーニング |
| --all | フラグ | No | bool | 全リソースをプルーニング対象 |
| --prune-allowlist | フラグ | No | string[] | プルーニング許可リスト |
| --applyset | フラグ | No | string | ApplySet の親リソース参照 |
| -l, --selector | フラグ | No | string | ラベルセレクター |
| --overwrite | フラグ | No | bool | 自動コンフリクト解決（デフォルト: true） |
| --openapi-patch | フラグ | No | bool | OpenAPI スキーマによるパッチ計算（デフォルト: true） |
| --validate | フラグ | No | string | バリデーション方式（strict/warn/ignore） |
| --subresource | フラグ | No | string | サブリソース指定（SSA時のみ） |
| -o, --output | フラグ | No | string | 出力形式 |

## 表示項目

| 項目名 | 説明 |
|--------|------|
| 適用結果 | "pod/mypod created" / "pod/mypod configured" / "pod/mypod unchanged" |
| SSA結果 | "pod/mypod serverside-applied" |
| 警告: last-applied-configuration なし | 既存リソースにアノテーションがない場合の警告 |
| 警告: 削除中リソースへの変更 | DeletionTimestamp が設定されたリソースへの変更検出 |
| 警告: SSA マイグレーション失敗 | CSA -> SSA のマイグレーション失敗時の警告 |

## イベント仕様

### 1-新規リソース作成

1. GetObjects: Builder でリソース定義を読み込み
2. applyOneObject: info.Get() -> NotFound -> CreateApplyAnnotation -> helper.Create
3. 結果表示: "{resource} created"

### 2-既存リソース更新（クライアントサイド Apply）

1. GetObjects -> applyOneObject: info.Get() -> 成功（既存リソース取得）
2. GetModifiedConfiguration -> Patcher.Patch（3-way merge）
3. 結果表示: "{resource} configured" / "{resource} unchanged"

### 3-既存リソース更新（サーバーサイド Apply）

1. GetObjects -> applyOneObject: helper.Patch(ApplyPatchType) でサーバーに全オブジェクト送信
2. サーバー側でフィールド所有権管理とマージ
3. CSA -> SSA マイグレーション（必要に応じて）
4. 結果表示: "{resource} serverside-applied"

### 4-プルーニング

1. PostProcessorFn: printObjects -> prune
2. VisitedUids に含まれないリソースを検出
3. 該当リソースの DELETE

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 新規リソース作成 | 対象リソース | CREATE | 設定ファイルからリソースを作成 |
| 既存リソース更新（CSA） | 対象リソース | PATCH（Strategic Merge Patch） | 3-way mergeによる更新 |
| 既存リソース更新（SSA） | 対象リソース | PATCH（Apply Patch Type） | サーバーサイド apply |
| プルーニング | 対象リソース | DELETE | 設定ファイルにないリソースの削除 |
| SSAマイグレーション | 対象リソース | PATCH（JSON Patch） | managedFields の移行 |

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

#### 新規リソース作成時
- 全フィールド: 設定ファイルの内容
- metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"]: 設定ファイルのJSON表現

#### 既存リソース更新時（CSA）
- 変更のあるフィールド: 3-way merge の結果
- metadata.annotations["kubectl.kubernetes.io/last-applied-configuration"]: 更新

#### 既存リソース更新時（SSA）
- 送信されたフィールド: サーバー側でのフィールド所有権管理に基づく更新
- metadata.managedFields: フィールドマネージャー情報の更新

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|------------|------|--------------|----------|
| M-01 | 情報 | "{resource} created" | 新規リソース作成時 |
| M-02 | 情報 | "{resource} configured" | 既存リソース更新時（変更あり） |
| M-03 | 情報 | "{resource} unchanged" | 既存リソース更新時（変更なし） |
| M-04 | 情報 | "{resource} serverside-applied" | SSA適用時 |
| M-05 | 警告 | "Warning: resource %s is missing the %s annotation..." | last-applied-configuration アノテーション欠落（行174） |
| M-06 | 警告 | "Warning: Detected changes to resource %s which is currently being deleted." | DeletionTimestamp 設定済みリソースへの変更（行175） |
| M-07 | エラー | "no objects passed to apply" | 適用対象が0件 |
| M-08 | エラー | "--force-conflicts only works with --server-side" | SSA未使用でforce-conflicts指定（行391-393） |
| M-09 | エラー | "--dry-run=client doesn't work with --server-side" | クライアントドライランとSSAの併用（行395-397） |
| M-10 | エラー | "cannot set --all and --selector at the same time" | --all と -l の同時指定（行407-409） |

## 例外処理

| 例外 | 状態 | 対処 |
|------|------|------|
| ファイル未指定 | エラー終了 | RequireFilenameOrKustomize |
| SSA 非対応サーバー | エラー終了 | Unsupported media type (415) エラー |
| コンフリクト（SSA） | エラー終了 | コンフリクト内容と解決方法を表示 |
| generateName 使用 | エラー終了 | "cannot use generate name with apply" |
| バリデーションエラー | エラー終了 | 設定ファイルの形式エラーを表示 |
| SSA マイグレーション失敗 | 警告（非致命的） | 次回 apply 時に再試行される |

## 備考

- CSA のフィールドマネージャーは `kubectl-client-side-apply`、SSA は `kubectl`（行1088-1098）。
- CSA は last-applied-configuration アノテーション（`kubectl.kubernetes.io/last-applied-configuration`）を使用して3-way mergeを行う。
- SSA は managedFields を使用してフィールド所有権を管理する。
- CSA -> SSA マイグレーション: 初回 SSA 実行時に自動的に CSA の managedFields を SSA に移行する（行905-980: migrateToSSAIfNecessary）。最大リトライ回数はコード内で定義される（maxPatchRetry）。
- last-applied-configuration アノテーションは SSA 使用時も保持される（行819-888: saveLastApplyAnnotationIfNecessary）。
- --prune は Alpha 機能であり、SSA との互換性がない（行438-441）。
- ApplySet（`--applyset`）は KEP-3659 に基づくプルーニング管理機能。
- Overwrite のデフォルトは true（行190-191）。
- OpenAPIPatch のデフォルトは true（行190-191）。
- ContinueOnError がBuilderに設定されており、一部のリソースで失敗しても処理を継続する（行472）。
- サブコマンド: view-last-applied, set-last-applied, edit-last-applied（行218-220）。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | ApplyFlags（行60-79）: CLI入力の直接反映構造体 |
| 1-2 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | ApplyOptions（行82-142）: ランタイムオプション。ServerSideApply, Prune, Builder, objects, VisitedUids 等 |

**読解のコツ**: Flags -> Options パターン。ApplyFlags.ToOptions がCLI入力をランタイムオプションに変換する。PostProcessorFn（行137）がプルーニングと最終出力を担当する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | NewCmdApply（行198-223）: コマンド定義、3つのサブコマンド登録 |
| 2-2 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | AddFlags（行226-241）: フラグ登録 |

#### Step 3: apply 実行フローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | ToOptions（行244-387）: CliフラグからOptions変換、ApplySet初期化 |
| 3-2 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | Validate（行390-448）: SSA/CSA整合性、prune要件チェック |
| 3-3 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | Run（行500-553）: PreProcessor -> GetObjects -> applyOneObject ループ -> PostProcessor |
| 3-4 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | applyOneObject（行555-780）: 各リソースの適用処理 |

#### Step 4: SSA とマイグレーションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | SSA パス（行575-670）: helper.Patch(ApplyPatchType) |
| 4-2 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | saveLastApplyAnnotationIfNecessary（行819-888）: アノテーション保持 |
| 4-3 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | migrateToSSAIfNecessary（行905-980）: CSA -> SSA マイグレーション |

#### Step 5: CSA パッチングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | CSA パス（行672-780）: GetModifiedConfiguration -> Patcher.Patch |
| 5-2 | patcher.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/patcher.go` | Patcher.Patch: 3-way merge パッチの計算と適用 |

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

```
NewCmdApply (行198)
    |
    +-- ApplyFlags.ToOptions (行244)
    |       +-- DynamicClient, Builder, Mapper 初期化
    |       +-- ApplySet 初期化（--applyset指定時）
    |       +-- PostProcessorFn = PrintAndPrunePostProcessor
    |
    +-- ApplyOptions.Validate (行390)
    |
    +-- ApplyOptions.Run (行500)
            |
            +-- PreProcessorFn (行501-506)
            +-- GetObjects (行466) -> Builder.Do().Infos()
            +-- ApplySet.BeforeApply (行525)
            |
            +-- [各 info について]
            |       applyOneObject (行555)
            |       |
            |       +-- [SSA]
            |       |       helper.Patch(ApplyPatchType) (行585-593)
            |       |       saveLastApplyAnnotationIfNecessary (行625)
            |       |       migrateToSSAIfNecessary (行627)
            |       |       Printer("serverside-applied")
            |       |
            |       +-- [CSA - 新規]
            |       |       util.CreateApplyAnnotation (行687)
            |       |       helper.Create (行699)
            |       |       Printer("created")
            |       |
            |       +-- [CSA - 既存]
            |               util.GetModifiedConfiguration (行675)
            |               Patcher.Patch (行743)
            |               Printer("configured" / "unchanged")
            |
            +-- PostProcessorFn (行545)
                    +-- printObjects (行993)
                    +-- [Prune]
                            +-- ApplySet.Prune or pruner.pruneAll
```

### データフロー図

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

設定ファイル             ToOptions
(-f FILENAME) --------> (Client, Builder初期化)
                              |
                         Validate
                         (整合性チェック)
                              |
                         Run
                              |
                         GetObjects
                         (Builder -> Infos)
                              |
                    [各リソースについて]
                              |
                    info.Get() (既存確認)
                         /          \
                   [NotFound]    [Found]
                        |            |
                   Create       [SSA?]
                   (新規作成)    /      \
                        |    [SSA]   [CSA]
                        |      |        |
                        | Patch(Apply) Patcher.Patch
                        | (全OBJ送信)  (3-way merge)
                        |      |        |
                        v      v        v
                    Printer({操作名})
                              |
                         PostProcessor
                         (printObjects + Prune)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | ソース | apply コマンドの主実装 |
| patcher.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/patcher.go` | ソース | 3-way merge パッチ計算 |
| applyset.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/applyset.go` | ソース | ApplySet プルーニング管理 |
| prune.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/prune.go` | ソース | プルーニングロジック |
| apply_view_last_applied.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_view_last_applied.go` | ソース | view-last-applied サブコマンド |
| apply_set_last_applied.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_set_last_applied.go` | ソース | set-last-applied サブコマンド |
| apply_edit_last_applied.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_edit_last_applied.go` | ソース | edit-last-applied サブコマンド |
| util.go | `staging/src/k8s.io/kubectl/pkg/util/` | ソース | GetModifiedConfiguration, CreateApplyAnnotation |
| diff.go | `staging/src/k8s.io/kubectl/pkg/cmd/diff/diff.go` | ソース | diff コマンド（Patcherを共有） |
| apply_test.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply_test.go` | テスト | ユニットテスト |
