# 機能設計書 82-宣言的適用（apply）

## 概要

本ドキュメントは、kubectlの`apply`コマンドによるKubernetesリソースの宣言的適用機能の設計を記述する。マニフェストファイルに基づくリソースの作成・更新を宣言的に行い、サーバーサイドApplyおよびクライアントサイドApplyの両方をサポートする。

### 本機能の処理概要

**業務上の目的・背景**：Kubernetesにおいて、GitOps等の宣言的なインフラ管理を実現するための中核機能である。`create`が命令型の作成のみを行うのに対し、`apply`はリソースが存在しない場合は作成し、存在する場合は差分のみを更新する。これにより、マニフェストファイルを「あるべき状態」として管理し、繰り返し適用することでリソースを所望の状態に収束させることが可能となる。

**機能の利用シーン**：CI/CDパイプラインでのリソースデプロイ、GitOpsワークフロー、複数リソースの一括管理、Server-Side Applyによるフィールドオーナーシップ管理、--pruneフラグによる不要リソースの自動削除など、本番環境での宣言的リソース管理全般に使用される。

**主要な処理内容**：
1. コマンドライン引数とフラグの解析（ToOptions）
2. 入力バリデーション（Validate）
3. マニフェストファイルの読み込みとオブジェクト構築（GetObjects）
4. 各オブジェクトに対する適用処理（applyOneObject）
   - Server-Side Apply: ApplyPatchType でのPATCHリクエスト
   - Client-Side Apply: Three-way merge patchの計算と適用
5. プルーニング処理（PostProcessorFn）
6. 結果の出力表示

**関連システム・外部連携**：API Serverに対してPATCH/CREATE/GETリクエストを送信する。OpenAPI V3スキーマを取得してクライアントサイドマージに使用する。

**権限による制御**：対象リソースに対するcreate、patch、get権限が必要。--pruneフラグ使用時はdelete権限も必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 30 | kubectl apply | 主機能 | 宣言的設定によるリソースの作成・更新を行う主処理 |

## 機能種別

CRUD操作（Create/Update）/ 宣言的管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| -f / --filename | string[] | Yes | リソース定義ファイルのパス | ファイルまたはKustomizeが必須 |
| -k / --kustomize | string | No | Kustomizeディレクトリ | --filenameと排他 |
| --server-side | bool | No | Server-Side Applyを使用 | --dry-run=clientと排他 |
| --force-conflicts | bool | No | フィールド競合を強制上書き | --server-sideが必要 |
| --field-manager | string | No | フィールドマネージャー名 | CSA: kubectl-client-side-apply、SSA: kubectl |
| -l / --selector | string | No | ラベルセレクター | --allと排他 |
| --prune | bool | No | マニフェストにないリソースを削除 | --allまたは-lが必要（ApplySet以外） |
| --prune-allowlist | string[] | No | プルーニング対象のリソースタイプ | --pruneが必要 |
| --applyset | string | No | ApplySetの親参照 | --pruneが必要 |
| --all | bool | No | 全リソースを対象 | -lと排他 |
| --dry-run | string | No | DryRun戦略 | none/client/server |
| --overwrite | bool | No | 競合時に上書き | デフォルトtrue |
| --openapi-patch | bool | No | OpenAPIパッチを使用 | デフォルトtrue |
| --subresource | string | No | サブリソースを指定 | --server-sideが必要 |
| --force | bool | No | リソースの強制削除・再作成 | --server-side、--dry-run=serverと排他 |
| --cascade | string | No | カスケード削除戦略 | background/foreground/orphan |
| --validate | string | No | バリデーション指示 | true/false/strict/warn/ignore |

### 入力データソース

- ローカルファイルシステム上のマニフェストファイル（YAML/JSON）
- 標準入力（stdin）
- Kustomizeディレクトリ
- OpenAPI V3スキーマ（API Serverから取得）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| リソース状態 | string | created/configured/unchanged/serverside-applied/pruned |
| 警告メッセージ | string | last-applied-configuration欠如、削除中リソースへの変更等 |
| JSON/YAML出力 | object | -o指定時のリソースオブジェクト |

### 出力先

- 標準出力（stdout）：適用結果メッセージまたはリソースオブジェクト
- 標準エラー出力（stderr）：警告メッセージ
- API Server：リソースオブジェクトの永続化

## 処理フロー

### 処理シーケンス

```
1. NewCmdApply: コマンド定義とサブコマンド登録
2. ToOptions: CLIフラグからランタイムオプションへの変換
3. Validate: 入力バリデーション（排他制約チェック等）
4. Run: 適用処理の実行
   ├─ PreProcessorFn（存在する場合）
   ├─ GetObjects: リソースオブジェクトの構築
   ├─ ApplySet.BeforeApply（ApplySet使用時）
   ├─ applyOneObject（各オブジェクト）
   │   ├─ [SSA] helper.Patch(ApplyPatchType)
   │   │   ├─ saveLastApplyAnnotationIfNecessary
   │   │   └─ migrateToSSAIfNecessary
   │   └─ [CSA]
   │       ├─ info.Get（既存リソース取得）
   │       ├─ [NotFound] helper.Create（新規作成）
   │       └─ [Exists] patcher.Patch（three-way merge）
   └─ PostProcessorFn: 出力とプルーニング
```

### フローチャート

```mermaid
flowchart TD
    A[開始: Run] --> B[GetObjects]
    B --> C{ApplySet?}
    C -->|Yes| D[ApplySet.BeforeApply]
    C -->|No| E[各オブジェクトを処理]
    D --> E
    E --> F{ServerSideApply?}
    F -->|Yes| G[helper.Patch ApplyPatchType]
    G --> H[SSA Migration処理]
    F -->|No| I[GetModifiedConfiguration]
    I --> J{リソース存在?}
    J -->|No| K[helper.Create]
    J -->|Yes| L[patcher.Patch three-way merge]
    H --> M[MarkObjectVisited]
    K --> M
    L --> M
    M --> N{次のオブジェクト?}
    N -->|Yes| E
    N -->|No| O[PostProcessorFn]
    O --> P{Prune?}
    P -->|Yes| Q[プルーニング実行]
    P -->|No| R[終了]
    Q --> R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | SSA/DryRunClient排他 | --dry-run=clientは--server-sideと併用不可 | SSA使用時 |
| BR-02 | force-conflicts制約 | --force-conflictsは--server-sideが必要 | --force-conflicts指定時 |
| BR-03 | SSA/Force排他 | --forceは--server-sideと併用不可 | SSA使用時 |
| BR-04 | all/selector排他 | --allと--selectorは同時指定不可 | 常時 |
| BR-05 | prune制約 | --pruneには--allまたは-lが必要（ApplySet以外） | --prune指定時 |
| BR-06 | subresource制約 | --subresourceは--server-sideが必要 | --subresource指定時 |
| BR-07 | CSAフィールドマネージャー | クライアントサイドApplyのデフォルトはkubectl-client-side-apply | CSA使用時 |
| BR-08 | SSAフィールドマネージャー | サーバーサイドApplyのデフォルトはkubectl | SSA使用時 |
| BR-09 | ApplySet/prune必須 | --applysetには--pruneが必要 | --applyset指定時 |

### 計算ロジック

- Three-way merge: 元のリソース、変更後のリソース、サーバー上の現在のリソースの3者間で差分を計算
- Strategic Merge Patch: Kubernetesスキーマに基づくリスト要素のマージ戦略

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| リソース作成 | etcd（API Server経由） | INSERT | 存在しないリソースの新規作成 |
| リソース更新 | etcd（API Server経由） | UPDATE | 既存リソースの差分更新（PATCH） |
| リソース削除 | etcd（API Server経由） | DELETE | --prune時の不要リソース削除 |

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

#### etcd（API Server経由）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | リソースオブジェクト | マニフェストの内容とThree-way mergeの結果 | SSA時はApplyPatchTypeを使用 |
| DELETE | リソースオブジェクト | VisitedUidsに含まれないリソース | --prune時のみ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | --force-conflictsを--server-sideなしで指定 | --server-sideを追加 |
| - | バリデーションエラー | --dry-run=clientと--server-sideの同時指定 | --dry-run=serverを使用 |
| 409 | Conflict | SSA時にフィールド競合が発生 | --force-conflictsを使用 |
| 415 | UnsupportedMediaType | SSA非対応サーバー | CSAにフォールバック |
| 422 | Unprocessable Entity | 無効なリソース定義 | マニフェストを修正 |

### リトライ仕様

SSAマイグレーション処理（migrateToSSAIfNecessary）では、ResourceVersion競合時に最大リトライが行われる（maxPatchRetry回）。

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

各リソースは個別に適用される。複数リソースの適用中にエラーが発生した場合、ContinueOnErrorにより処理を継続し、最終的にエラーを集約して返す。

## パフォーマンス要件

- OpenAPI V3スキーマの取得はキャッシュされる
- 大量リソースの適用時はContinueOnErrorで並列処理ではなく逐次処理

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

- create、patch、get権限が必要（RBAC）
- --prune使用時はdelete権限も必要
- Server-Side Applyによるフィールドオーナーシップ管理で、複数管理者間のフィールド競合を検出可能
- last-applied-configuration アノテーションにリソースの完全な設定が保存されるため、Secretなどの機密情報が含まれる可能性がある

## 備考

- サブコマンドとしてview-last-applied、set-last-applied、edit-last-appliedが存在
- ApplySetはKEP-3659に基づくプルーニング機能の改善
- CSAからSSAへのマイグレーションロジック（migrateToSSAIfNecessary）が含まれる

---

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

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

### 推奨読解順序

#### 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行目）でランタイムオプションを把握 |

**読解のコツ**: ApplyFlagsがCLI入力、ApplyOptionsがランタイム要件を表す。ToOptions()でFlags->Optionsの変換が行われる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | NewCmdApply関数（198-223行目）でコマンド定義を理解 |

**主要処理フロー**:
1. **207-212行目**: flags.ToOptions -> Validate -> Run のパイプライン
2. **218-220行目**: サブコマンド（view-last-applied, set-last-applied, edit-last-applied）の登録

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | Validate関数（390-448行目）で各種排他制約を理解 |

#### Step 4: 適用処理の実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | Run関数（500-553行目）でメイン実行フローを理解 |
| 4-2 | apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | applyOneObject関数（555-780行目）で個別適用処理を理解 |

**主要処理フロー**:
- **575-670行目**: Server-Side Apply処理（ApplyPatchType、マイグレーション処理含む）
- **675-722行目**: Client-Side Apply処理（リソース取得→新規作成or Three-way merge）
- **734-778行目**: パッチ適用と結果表示

#### Step 5: パッチ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | patcher.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/patcher.go` | Patch関数でThree-way mergeの詳細を理解 |

#### Step 6: プルーニング処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | prune.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/prune.go` | pruneAll関数でリソースプルーニングの詳細を理解 |
| 6-2 | applyset.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/applyset.go` | ApplySetによるプルーニング管理を理解 |

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

```
NewCmdApply (apply.go:198)
    │
    ├─ flags.ToOptions (apply.go:244)
    │      ├─ GetServerSideApplyFlag
    │      ├─ GetDryRunStrategy
    │      ├─ f.DynamicClient
    │      ├─ f.OpenAPIV3Client
    │      ├─ f.Validator
    │      └─ ParseApplySetParentRef（ApplySet使用時）
    │
    ├─ Validate (apply.go:390)
    │
    └─ Run (apply.go:500)
           ├─ PreProcessorFn
           ├─ GetObjects (apply.go:466)
           │      └─ Builder.Unstructured().Schema()...Do()
           ├─ ApplySet.BeforeApply
           ├─ applyOneObject (apply.go:555)
           │      ├─ [SSA] helper.Patch(ApplyPatchType)
           │      │      ├─ saveLastApplyAnnotationIfNecessary
           │      │      └─ migrateToSSAIfNecessary
           │      └─ [CSA]
           │             ├─ info.Get
           │             ├─ helper.Create（新規）
           │             └─ patcher.Patch（更新）
           └─ PostProcessorFn
                  ├─ printObjects
                  └─ pruneAll / ApplySet.Prune
```

### データフロー図

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

マニフェストファイル ──▶ GetObjects ──▶ applyOneObject
(YAML/JSON)                            │
                                       ├─ [SSA] ──▶ PATCH (ApplyPatchType) ──▶ API Server
                                       │
                                       └─ [CSA] ──▶ GET ──▶ Three-way merge ──▶ PATCH ──▶ API Server
                                                                                              │
                                                                                              ▼
                                                                            stdout (created/configured/unchanged)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| apply.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go` | ソース | メインコマンド定義・適用処理 |
| patcher.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/patcher.go` | ソース | Three-way mergeパッチ処理 |
| prune.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/prune.go` | ソース | リソースプルーニング処理 |
| applyset.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/applyset.go` | ソース | ApplySetプルーニング管理 |
| applyset_pruner.go | `staging/src/k8s.io/kubectl/pkg/cmd/apply/applyset_pruner.go` | ソース | ApplySetプルーナー |
| 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サブコマンド |
