ArgoCD ApplicationSet v0.2.0 と v0.3.0 の変更点

前回から新しいバージョンがリリースされ[前回](https://qiita.com/shmurata/items/a6ee4193e2d2c8a2d34c)の記事で軽く紹介した機能なども追加されたのでまとめて紹介しようと思います。
ApplicationSet自体の話は前回の記事に記載してあるのでよければ見てみてください。


# v0.2.0

リリースノート: https://github.com/argoproj-labs/applicationset/releases/tag/v0.2.0
リリース日: 2021/8/10

## 新機能

### Matrix generator

Matrix generatorは2つのジェネレータを組み合わせてそれぞれのジェネレータで検知された内容でApplicationを生成します。

Specは以下のように記述します。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cluster-git
spec:
  generators:
    - matrix: # 'Parent' Matrix Generator
        generators:
          - git: # 'Child' generator A
              repoURL: https://github.com/argoproj-labs/applicationset.git
              revision: HEAD
              directories:
                - path: examples/matrix/cluster-addons/*
          - clusters: # 'Child' generator B
              selector:
                matchLabels:
                  argocd.argoproj.io/secret-type: cluster
  template:
  # (...)
```
*サンプルは https://github.com/argoproj-labs/applicationset/releases/tag/v0.2.0 より抜粋*

:memo: このジェネレータによって、複数のクラスタに複数のApplicationを一つのApplicationSetでデプロイするといったことができるようになります。

### SCM Provider generator

SCM Provider generatorは、GitHub/GitLabのAPIを利用してorg内のリポジトリを自動的に検出し、生成されたArgo CDアプリケーションのターゲットにリポジトリの値を使用できるようにする新しいジェネレータです。これは、マイクロサービスを多くのリポジトリに分割するGitOpsのレイアウトパターンによく合っており、単一のリポジトリに固執するパターン（これは他のApplicationSetジェネレータで処理できます）よりも適しています。

Specは以下のように記述します。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapps
spec:
  generators:
  - scmProvider:
      github:
        organization: myorg         # The GitHub organization to scan.
        tokenRef: { } # Reference to a Secret containing an access token. (optional)
  template:
  # ...
```
*サンプルは https://github.com/argoproj-labs/applicationset/releases/tag/v0.2.0 より抜粋*

:memo: 上手くチーム内でルールを作れればかなりの Application をまとめることができそうです
:memo: リリースノートには書いてないですがブランチ単位やリポジトリのラベルをフィルタすることもできるようです
:memo: 個人的には pull request 単位で作りたいことがあってこれでは地味に解決できなくて困りました :cry: 

### Cluster Decision Resource generator

Cluster Decision Resource generatorは、外部のカスタムリソース（CR）の内容に基づいてArgo CDのクラスターのリストを生成する新しいジェネレーターで、そのカスタムリソースは外部のコントローラーによって管理されます。このジェネレータでは、どのクラスターをターゲットにするかというロジックを、Open Cluster ManagementのPlacementsのようなサードパーティのコントローラー/CRに「アウトソース」することができます。

これは、参照されるKubernetesリソースの完全な形状の知識を必要としないduck-typingを使用してシームレスに処理されます。以下は、cluster-decision-resourceベースのApplicationSetジェネレータの例です。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
 name: guestbook
spec:
 generators:
 - clusterDecisionResource:
    # ConfigMap with GVK information for the duck type resource
    configMapRef: my-configmap      
    name: quak  # Choose either "name" of the resource or "labelSelector"
```

```yaml
apiVersion: mallard.io/v1beta1
kind: Duck
metadata:
  name: quak
spec: {}
status:
  decisions:   # Duck-typing ignores all other aspects of the resource except the "decisions" list
  - clusterName: cluster-01
  - clusterName: cluster-02
```

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-configmap
data:
  # apiVersion of the target resource
  apiVersion: mallard.io/v1beta1  
  # kind of the target resource
  kind: ducks
  # status key name that holds the list of Argo CD clusters
  statusListKey: decisions
  # The key in the status list whose value is the cluster name found in Argo CD
  matchKey: clusterName
```
*サンプルは https://github.com/argoproj-labs/applicationset/releases/tag/v0.2.0 より抜粋*

:pencil: Open Cluster Management の Placements は https://open-cluster-management.io/concepts/placement/ のことのようで、マルチクラスタの管理をするための仕組みのようです。
:memo: 個人的には面白い使い方ができそうには思ってますが、有用なユースケースがまだ発見できていません。

### Preserve Application child resources on deletion of parent ApplicationSet

デフォルトでは、ApplicationSetコントローラによって作成、管理されるアプリケーションには、Argo CDのリソース削除ファイナライザーが含まれています。つまり、ApplicationSetが削除されると、その子のApplicationも削除され、その子のApplicationのクラスタリソースも削除されます。これは、Argo CDのカスケード削除と同じ動作です。

しかし、この動作は必ずしも望ましいものではありません。親アプリケーションが削除されても、子アプリケーションのリソースを保持したい場合があります。これを可能にするには、親の ApplicationSet で `.spec.syncPolicy.prespreserveResourcesOnDeletion` の値を使用します。

```
kind: ApplicationSet
spec:
  generators:
    - clusters: {}
  template:
    # (...)
  syncPolicy:
    # Don't delete Application's child resources, on parent deletion:
    preserveResourcesOnDeletion: true
```

:memo: 地味に欲しかった機能でこの機能がないときは ApplicationSet の移行をする際に Application リソースでデプロイしたリソースが削除されてしまい、デプロイしているアプリケーションのダウンタイムが発生してしまうので困りましたが、この機能が追加されたので削除することなく移行できるようになりました。

### Add YAML configuration file support to Git File generator

ApplicationSetの前のリリースでは、JSON形式の設定ファイルのみがGitファイルジェネレータでサポートされていました。今回のリリースでは、JSONとYAMLの両方のファイルがサポートされ、互換性を持って使用できるようになりました。

### Allow any key/value pair in List generator

リスト・ジェネレーターはこれまで、クラスター名/URL値の固定リストのみをサポートしていましたが、オプションでユーザー定義値用の値フィールドが用意されていました。今回、任意のキー/値のペアを指定できるようになりました。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
spec:
 generators:
 - list:
     elements:
     # current form, still supported:
     - cluster: engineering-dev
       url: https://kubernetes.default.svc
       values:
         additional: value
     # new form, does not require cluster/URL keys:
     - staging: true
       gitRepo: https://kubernetes.default.svc   
 template:
   # (...)
```


:memo: https://qiita.com/shmurata/items/a6ee4193e2d2c8a2d34c#list-generator で言っていた件ですが指定できるようになりました :tada: 

### Added additional path params to Git File generator

Git File generator は、`{{ path }}` と `{{ path.basename }}` パラメータを生成するようになりました。これは、Git Directory ジェネレーターで生成されるパラメータと同様に、設定ファイルのパスを含んでいます。


### Add exclude path support to Git directories

Git Directoryジェネレータは、Gitリポジトリ内のディレクトリをスキャンし、特定の条件にマッチするディレクトリを探します。しかし、以前のApplicationSetのリリースでは、スキャン対象からフォルダを除外する方法がありませんでした。

今回のリリースでは、個々のパスをスキャン対象から外すことができるようになりました。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cluster-addons
spec:
  generators:
  - git:
      repoURL: https://github.com/argoproj-labs/applicationset.git
      revision: HEAD
      directories:
      # Include this first path 
      - path: examples/git-generator-directory/excludes/cluster-addons/* 
      # But, exclude this second path:
      - path: examples/git-generator-directory/excludes/cluster-addons/exclude-helm-guestbook
        exclude: true

```

### Quality of Life Improvements

- Gitディレクトリジェネレータが'.'で始まるすべてのフォルダを無視するようになりました
- アプリケーションのテンプレートにカスタムの'finalizer'をサポートするようになりました
- ApplicationSetコントローラの起動時にログレベルを設定する機能を有効になりました
- ApplicationSetのCRDに'appset/appsets'のショートネームを追加されました
- コントローラ起動時のログにappsetのバージョンを記録するようになりました

その他細かいバグ修正なども入っていますがそちらはリリースノートを参照ください。

# v0.3.0

リリースノート: https://github.com/argoproj-labs/applicationset/releases/tag/v0.3.0
リリース日: 2021/12/16

## 新機能

### Pull Request generator

ApplicationSet v0.3.0では、SCMaaSプロバイダー（例：GitHub）のAPIを利用して、リポジトリ内のオープンなプルリクエストを自動的に検出するプルリクエストジェネレーターを新たに追加しました。これは、公開されているプルリクエストに基づいてテスト環境を構築したいというユーザーに適しています。

この例では、オープンなプルリクエストごとにArgo CD Applicationリソースを作成します。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapps
spec:
  generators:
  - pullRequest:
      github:
        # The GitHub organization or user.
        owner: myorg
        # The Github repository
        repo: myrepository
        # For GitHub Enterprise (optional)
        api: https://git.example.com/
        # Reference to a Secret containing an access token. (optional)
        tokenRef:
          secretName: github-token
          key: token
        # Labels is used to filter the PRs that you want to target. (optional)
        labels:
        - preview
  template:
  # ...
```

:memo: 自分のチームだと PR が投げられた時に preview 環境を自動的に作るようにしているんですが ApplicationSet でその再現ができなかったため、generator をコントリビュートして追加しました。

### Merge generator

このジェネレーターは、あるジェネレーターで生成されたパラメータを、別のジェネレーターで生成されたパラメータで選択的に上書きしたい場合に便利です。

この例では、まずArgo CDからクラスターのリストを収集し、use-kakfa: falseのラベルを持つクラスターのみを「パッチ」し、最後に特定のクラスターでredisを有効にします。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: cluster-git
spec:
  generators:
    # merge 'parent' generator
    - merge:
        mergeKeys:
          - server
        generators:
          # Generate parameters for all Argo CD clusters
          - clusters:
              values:
                kafka: 'true'
                redis: 'false'
          # For clusters with a specific label, enable Kafka.
          - clusters:
              selector:
                matchLabels:
                  use-kafka: 'false'
              values:
                kafka: 'false'
          # For a specific cluster, enable Redis.
          - list:
              elements: 
                - server: https://2.4.6.8
                  values.redis: 'true'
```

:memo: これは spec を見ただけでは動作を予想するのは難しそうですが、[ドキュメント](https://argocd-applicationset.readthedocs.io/en/master/Generators-Merge/)に詳しい説明が追加されていますのでそちらを見てみることをおすすめします
:memo: 簡単に説明すると mergeKeys に指定したキーの値が一致する対象に対して上から順番に他のキーの値を上書きしていくという動作をするようです。（結果的に下に記載した値の優先度が高い）

### Report error conditions/status for ApplicationSet CR

ユーザーが提供したジェネレータ/テンプレートで無効なArgo CDアプリケーションが生成された場合、ApplicationSetリソースのステータスフィールドにエラーが表示されるようになりました。例えば、以下のようになります。

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: myapps
spec:
  generators: # (...)
  template: # (...)
status:
  conditions:
  - lastTransitionTime: "2021-11-23T05:47:08Z"
    type: ErrorOccurred
    status: "False"
    reason: ApplicationSetUpToDate
    message: Successfully generated parameters for all Applications
  - lastTransitionTime: "2021-11-23T05:47:08Z"
    type: ParametersGenerated
    message: Successfully generated parameters for all Applications
    reason: ParametersGenerated
    status: "True"
  - lastTransitionTime: "2021-11-23T05:47:08Z"
    type: ResourcesUpToDate
    status: "True"
    reason: ApplicationSetUpToDate
    message: ApplicationSet up to date
```


### Git Generator: Refresh ApplicationSet resource with Git generator using webhook

この機能は、GitHub/GitLab webhookトリガーによるApplicationSetの更新をサポートします。この機能は、受信するWebhook payload を listen するサービスを公開し、受信するとApplicationSetコントローラをトリガーしてリソースを再生成します。一方、以前のリリースでは、ApplicationSetコントローラは、Gitジェネレーターが使用するGitリポジトリを3分ごとにポーリングすることしかサポートしていませんでした（ただし、これは少なくともカスタマイズ可能です）。

:memo: 今まではポーリングでリポジトリの差分検知を行っていたところを webhook でも検知できるように追加したという話です

### Pull Request generator: Support for webhooks

Pull Requestジェネレーターを使用する場合、ApplicationSetコントローラは、変更を検出するためにrequeueAfterSeconds間隔（デフォルトでは30分ごと）でポーリングを行います。このポーリングによる遅延をなくすために、ApplicationSetのWebhookサーバーをWebhookイベントを受信するように設定することができ、これがPull Requestジェネレーターでサポートされるようになりました。

:memo: git generator と同様に pull requests generator も webhook での差分検知を追加しました

### Support -logformat=json as parameter to applicationset-controller

applicationset-controller のログ出力を json に変更するオプションが追加されました。

### SCM Generator: Provide SHA for latest commit on a branch in variables 

SCM Generator に最新コミットハッシュ(SHA)のパラメータが追加されました。

### Improve Git files generator performance

Gitファイル生成機能は、リポジトリ内で発見されたファイルごとに `git fetch/git checkout` を不用意に実行するため、時間がかかりすぎていました（Gitリクエストも増加していました）。ApplicationSet v0.3.0では、これが改善され、1回の更新ごとにGitチェックアウト/フェッチレポを発行するようになりました。

## Upgrade Notes

0.1.0/0.2.0 から 0.3.0 にアップグレードする場合の挙動の変更が2点あります。

### cluster generator の `{{name}}` パラメータの値は正規化されなくなりました

cluster generator の `{{name}}` パラメータは元の動作に戻りました。Argo CD内のクラスタ名はもはや正規化されません。ApplicationSet内のCluster generatorで生成された`{{name}}`パラメータはApplicationSetテンプレートにそのまま渡されるようになりました。

0.2.0の動作を維持する新しいパラメータ`{{nameNormalized}}`が導入されました。これにより、パラメータを定義したまま使用するか、正規化された形で使用するか（Applicationリソースのnameフィールドで使用可能）、使用される状況に応じてApplicationSetで使用する動作を選択することができます。

Argo CDのクラスタ名がすでに有効な場合は、変更する必要はありません。ただし、アプリケーションセットのv0.2.0の動作を維持するためには、アプリケーションセットのテンプレート内で`{{name}}`を`{{nameNormalized}}`に置き換えてください。

:memo: 正規化というのは[こちらのコードの処理](https://github.com/argoproj-labs/applicationset/blob/11f1fe893b019c9a530865fa83ee78b16af2c090/pkg/generators/cluster.go#L168)のことのようで、ドメイン名として利用できるように利用できる文字と文字数の調整などを行っているようです。詳細はコードを参照してください。

### ApplicationSet に無効な生成アプリケーションが含まれていても、有効な生成アプリケーションは処理されます

ApplicationSetコントローラは、ApplicationSetリソースを1つまたは複数のApplicationリソースに変換することを責務としています。しかし、以前のリリースでは、生成されたアプリケーション・リソースの少なくとも1つが無効な場合（例えば、内部の検証ロジックに失敗した場合）、生成されたアプリケーションはどれも処理されませんでした（作成も変更もされません）。

最新のApplicationSetリリースでは、ジェネレーターが無効なApplicationを生成した場合、それらの無効な生成されたApplicationはまだスキップされますが、有効な生成されたApplicationは処理（作成／変更）されるようになりました。

このため、今回のアップグレードではApplicationSetのリソースの変更は必要ありませんが、失敗したApplicationによってブロックされていたApplicationSetがブロックされなくなる可能性があることは覚えておいた方がよいでしょう。この変更により、以前は処理されなかった有効なアプリケーションが作成／変更されるようになるかもしれません。

:memo: 気づいていなかったですが、一つの ApplicationSet リソースから生成される Application のうちどれか不正なものがあった場合はその ApplicationSet　から生成されるすべての Application の生成が止まっていたようです。この修正で問題あるものだけ生成されなくなるようです。

その他細かいバグ修正などはリリースノートを参照ください。

# 最後に

今回はArgoCD ApplicationSet v0.2.0/0.3.0 の変更点についてまとめました。ApplicationSetはまだ作られ始めということもあり、小さなツールの割には大きな変更がたくさんあり変更点の把握は大変です。その分新しい機能を追加したい場合は気軽に変更を受け入れてもらえるようなので改善したい点などがあればissueを作ったりコントリビュートしてみるとよいと思います。