argocd app diff の仕組みを理解する

# argocd app diff の仕組みを理解する

## はじめに

Argo CD でアプリケーションの差分 (diff) を確認する場合は、以下のいくつかの方法があります:

1. `argocd app diff <アプリ名>`  
   - Git リポジトリなどの「`spec.source`」を参照し、リポジトリサーバーでマニフェストを生成  
   - 実行中のリソース（live state）と比較して差分を出力

2. `argocd app diff --local /path/to/local`  
   - ユーザーのローカルマシンに存在するファイルを使って、差分を確認 (client side)  
   - ただし、この場合はローカル環境に Helm/Kustomize などが必要で、CMP (Config Management Plugin) は標準的には動作しにくい
   - Deprecated

3. `argocd app diff --local /path/to/local --server-side-generate`  
   - ローカルファイルを tar 化してリポジトリサーバー (あるいは Argo CD API サーバー) に送信し、サーバーサイドでマニフェストを生成  
   - ユーザーのローカル環境に特定バージョンの Helm/Kustomize は不要、さらに CMP (Config Management Plugin) も動作可能  

Argo CD v2.7 以降では `--server-side-generate` がデフォルトになるとドキュメントに記載されています。
https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/2.4-2.5/#deprecated-client-side-manifest-diffs

---

## `argocd app diff <アプリ名>` がやっていること

`argocd app diff <アプリ名>` を実行すると、以下のような流れになります:

1. 指定した Application リソース (<アプリ名>) の `spec.source` (GitリポジトリURL, Helm, Kustomize, Plugin名など) を読み取る  
2. Argo CD リポジトリサーバーで **desired state** のマニフェストを生成  
3. Kubernetes クラスターから **live state** を取得  
4. 両者の差分を CLI で表示

---

## `--local` オプションを指定した場合

### client side (`--local` のみ)

`argocd app diff --local /path/to/dir` を指定すると、Argo CD はユーザーのローカルファイルを直接読み込み、マニフェストを生成し、ライブリソースとの diff を行います。  
- 利点: ネットワーク越しにファイルを送らずに済む  
- 欠点: ユーザーのローカル環境に Helm/Kustomize が必要、CMP (プラグイン) は動作しづらい.

### server side (`--local` + `--server-side-generate`)

`argocd app diff --local /path/to/dir --server-side-generate` を指定すると、下記のフローでサーバーサイド生成が走ります:

1. CLI が Argo CD API サーバー (アプリケーションサービス) に対して gRPC メソッド `GetManifestsWithFiles(...)` を開始  
2. CLI 側で `SendApplicationManifestQueryWithFiles(...)` が呼ばれ、`/path/to/dir` 以下のファイルを tar 化してストリーミング送信  
3. Argo CD API サーバーがリポジトリサーバーへ「このファイルを用いてマニフェストを生成して」と依頼  
4. リポジトリサーバーでヘルムやカスタマイズ、CMP サイドカーなどを使ってマニフェストを生成し、API サーバーへ返却  
5. API サーバーが最終的なマニフェストを CLI に返す  
6. 受け取ったマニフェストを CLI がライブリソースと突き合わせ、diff を表示

この方法ならユーザーのローカルマシンは`argocd cli`の管理と、`argocd`へアクセスできる状況を維持するだけで済み、Helm/Kustomize のバージョン整合性や CMP の有無を管理しなくて済みます。

---

## マーメイド図 (サーバーサイド生成時のシーケンス)

```mermaid
sequenceDiagram
    autonumber
    participant CLI as Argo CD CLI (ユーザーの端末)
    participant ArgoCDAPI as Argo CD APIサーバー
    participant RepoServer as リポジトリサーバー

    note over CLI,ArgoCDAPI: "argocd app diff --local /my-repo --server-side-generate" 実行

    CLI->>ArgoCDAPI: 1) CLIが gRPCメソッド GetManifestsWithFiles(...) を呼び出し開始
    CLI->>ArgoCDAPI: 2) CLIの SendApplicationManifestQueryWithFiles(...) が /my-repo のファイルをtar化しストリーミング送信
    ArgoCDAPI->>RepoServer: 3) APIサーバーがリポジトリサーバーへ「アップロードされたファイルでマニフェストを生成せよ」と依頼
    RepoServer-->>ArgoCDAPI: 4) リポジトリサーバーがマニフェストを生成し、APIサーバーへ返却
    ArgoCDAPI-->>CLI: 5) APIサーバーが最終的なマニフェストを CLI に gRPC応答で返す
    CLI->>CLI: 6) CLIがライブリソースとマニフェストの差分を算出し、表示
```

---

## `spec.source` とローカル転送の関係

「ローカルからファイルを送るなら、そもそも `spec.source` は読まなくていいかもしれない」と疑問に思う方もいるかもしれません。しかし、以下のような理由で実際には部分的に参照されるケースがあります:

1. アプリケーション名や Plugin 名、Project 情報などを取得するため:  
   - どのプラグインを使うのか, あるいは Helm の設定や Kustomize の設定があるのか、といった **メタ情報** を確認する際に `spec.source` を利用する  
2. Revision やデフォルトブランチの参照:  
   - 実際にはローカルファイルで差分を取る場合でも、revision の扱いをどうするか (現在の revision と比較する等) のために `spec.source` の `TargetRevision` や `RepoURL` を参照することがある  
3. ただし、Git リポジトリから実際に **checkout** しにいくわけではない:  
   - `--server-side-generate` を使ってローカルファイルが転送される場合は、リポジトリサーバーで Git をダウンロードしてくる処理がスキップされ、tar 化されたローカルファイルが直接使われる  
   - `spec.source` はあくまで他の設定 (ネームスペースや Plugin 名判定など) に利用されるに留まる

結果として、Application リソースの `spec.source` は *完全に無視されているわけではない* が、マニフェスト取得本体としては **ローカルでアップロードしたファイル** が優先されます。

---

## まとめ

- `argocd app diff <アプリ名>` は、Application CR の `spec.source` からリポジトリ情報やプラグイン設定を読み取り、サーバーサイド (repo server) でマニフェストを生成 → ライブリソースと差分表示する  
- `argocd app diff --local /path` は、ローカルファイルをベースに差分を算出するやり方で、Helm/Kustomize/CMP をローカルにインストールしておく必要がある (client side)  
- `argocd app diff --local /path --server-side-generate` は、ローカルファイルをサーバーに送信してサーバーサイドでマニフェスト生成を行うため、ローカルへの事前インストール不要かつ CMP などの高度な機能も利用可能  
- `spec.source` はローカル転送でも設定情報 (Revision、Plugin種別など) を確認するために一部参照されるが、Git リポジトリダウンロードは行われない