Tanzu Application Platform 1.6でアプリを立ち上げるところまで動かしてみる

Tanzu Application Platform(以下TAP)を試しに動かしてみた時のメモ。
なお、手順を確認するにあたりmakingさんの[Tanzu Application Platform 1.6 (Full Profile) をKind on OrbStackにインストールするメモ](https://ik.am/entries/754)を参考にしている。

## インストール要件
TAP1.6だと[こちら](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/prerequisites.html)に記載がある。
細かい部分は省いて、気をつけた方がいい点は以下あたりとなる。
- コンテナレジストリの用意
- コンテナレジストリには10G以上の空きがある
- ドメインの用意
- Gitリポジトリ
- fullで利用する場合は以下のリソースが必要
  - 8GBメモリの空き/ノード
  - 16vCPU/クラスタ
  - 100GBストレージ/ノード

Tanzu PackageでHarborをデプロイした場合、PVのデフォルトサイズが10Gで最初からカツカツになってしまうため、ストレージのサイズを変更して対応すること。
ドメインはsslip.ioなどのドメイン名にIPを入れるとIPを返してくれるサービスを使えば準備は不要。

## 検証環境
今回は以下の環境で実施した。
- TKG2.2
- `type: LoadBalancer`を使うためにMetalLBを導入済み
- HarborはVM上に構築済み
- Harborの証明書はノード、kapp-controllerに導入済み
- GitはGiteaを構築済み、リポジトリも作成済み

イメージレジストリの証明書の登録はノードに関しては[こちら](https://docs.vmware.com/en/VMware-Tanzu-Kubernetes-Grid/2.2/using-tkg-22/workload-clusters-secret.html#trusted-registries-for-a-classbased-cluster-5)、kapp-controllerに関しては[こちら](https://qiita.com/ipppppei/items/887b02838fa44899281d)を参照するとよい。
Giteaに関してはインストール方法を最後に載せている。

## インストール
### 環境変数の設定
インストール時に使う環境変数を設定しておく。
```sh
# VMware Tanzu Networkのログインで使うID
export TANZUNET_USERNAME=xxx@xxx.info
# VMware Tanzu Networkのログインで使うパスワード
export TANZUNET_PASSWORD=xxxx
# イメージレジストリのURI
export INSTALL_REGISTRY_HOSTNAME=myharbor.hogeeee.info
# イメージレジストリのログインID
export INSTALL_REGISTRY_USERNAME=admin
# イメージレジストリのログインパスワード
export INSTALL_REGISTRY_PASSWORD=xxxx
# インストールするTAPのバージョン
export TAP_VERSION=1.6.2
# TAPのイメージを格納するリポジトリ
export INSTALL_REPOSITORY=${INSTALL_REGISTRY_HOSTNAME}/tap/tap-packages
```
### 前提の準備
Airgap用の手順も[用意されている](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/install-offline-intro.html)が、ここではインターネット接続ありでのインストールを行う。
参考にする手順は[こちら](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/install-online-intro.html)。

最初にEULAに承認する。
一旦[VMware Tanzu Network](https://network.tanzu.vmware.com/)にアクセス・ログインする。
次に以下にそれぞれアクセスし、EULAを承認(AGREE)する。
- [Tanzu Application Platform](https://network.tanzu.vmware.com/products/tanzu-application-platform/)
- [Cluster Essentials for VMware Tanzu](https://network.tanzu.vmware.com/products/tanzu-cluster-essentials/)

次にTanzu CLIをダウンロードする。
TKG2.3以降であればクラスタ構築に使ったTanzu CLIでも問題ないが、TKG2.2などでは別途入手する必要がある。
```sh
wget https://github.com/vmware-tanzu/tanzu-cli/releases/download/v1.0.0/tanzu-cli-linux-amd64.tar.gz
tar xvzf tanzu-cli-linux-amd64.tar.gz -C /tmp
install /tmp/v1.0.0/tanzu-cli-linux_amd64 ~/bin/tanzu-v1.0.0
alias tanzu=tanzu-v1.0.0
rm tanzu-cli-linux-amd64.tar.gz
```
Tanzu CLIを使ってTAPのpluginをインストールする。
```sh
tanzu plugin install --group vmware-tap/default:v1.6.2
```
次にCluster Essentialsをインストールする手順になっているが、クラスタ内にsecretgen-controllerとkapp-controllerが入っている場合は飛ばすことが出来る。
TKGであれば飛ばせるため、ここでは飛ばして次のパッケージインストールに進む。
パッケージインストール前に、TAPに必要なイメージをプライベートリポジトリに移動させる。
これは必須作業ではないが、TAPのイメージレジストリの稼働時間が保証されていないため、信頼性向上の観点から実施する。
最初に、TAPのイメージ配布元レジストリ、転送先プライベートレジストリのそれぞれにログインする。
```sh
echo $TANZUNET_PASSWORD | docker login registry.tanzu.vmware.com -u $TANZUNET_USERNAME --password-stdin
echo $INSTALL_REGISTRY_PASSWORD | docker login $INSTALL_REGISTRY_HOSTNAME -u $INSTALL_REGISTRY_USERNAME --password-stdin
```
次にイメージを転送する。公式手順の記述だと自己署名証明書の利用を考慮していないので注意。
```sh
imgpkg copy -b registry.tanzu.vmware.com/tanzu-application-platform/tap-packages:${TAP_VERSION} --to-repo ${INSTALL_REPOSITORY} --registry-ca-cert-path=./harbor-vm/harbor/cert/ca.crt --registry-verify-certs=false
```
環境にもよるが、自分の環境では25分程度で転送が完了した。

### TAPのインストール

Namespaceを作成し、TAPのイメージレジストリの認証情報(tap-registry)をSecretとして登録する。
```sh
kubectl create ns tap-install
tanzu secret registry add tap-registry \
  --username ${INSTALL_REGISTRY_USERNAME} --password ${INSTALL_REGISTRY_PASSWORD} \
  --server ${INSTALL_REGISTRY_HOSTNAME} \
  --export-to-all-namespaces --yes --namespace tap-install
```
次にエンドユーザが利用するイメージレジストリ（TBSで作成したイメージの格納先）を登録する。今回はエンドユーザも同じレジストリを使う想定なので、先程のSecret作成コマンドのSecret名だけ変更して実行しなおす。
```sh
tanzu secret registry add registry-credentials \
  --username ${INSTALL_REGISTRY_USERNAME} --password ${INSTALL_REGISTRY_PASSWORD} \
  --server ${INSTALL_REGISTRY_HOSTNAME} \
  --export-to-all-namespaces --yes --namespace tap-install
```
TAPのpackageを利用可能にするために、Packageリポジトリを追加する。
```sh
tanzu package repository add tanzu-tap-repository \
  --url ${INSTALL_REPOSITORY}:$TAP_VERSION \
  --namespace tap-install
```
問題なければ以下のようなメッセージが表示される。
```sh
Waiting for package repository to be added

2:15:16AM: Waiting for package repository reconciliation for 'tanzu-tap-repository'
2:15:16AM: Fetch started (1s ago)
2:15:17AM: Fetching
	    | apiVersion: vendir.k14s.io/v1alpha1
：（省略）
	    | 2:15:22AM: ---- applying complete [114/114 done] ----
	    | 2:15:22AM: ---- waiting complete [114/114 done] ----
	    | Succeeded
2:15:22AM: Deploy succeeded
```

なお、kapp-controllerにHarborの証明書を渡していない場合は以下のエラーとなる。
```sh
12:22:26AM: Fetch failed
	    | vendir: Error: Syncing directory '0':
	    |   Syncing directory '.' with imgpkgBundle contents:
	    |     Imgpkg: exit status 1 (stderr: imgpkg: Error: Fetching image:
	    |   Error while preparing a transport to talk with the registry:
	    |     Unable to create round tripper:
	    |       Get "https://oss-harbor.10.220.157.229.sslip.io/v2/": x509: certificate signed by unknown authority
	    | )
	    |
	    | Fetching resources: Error (see .status.usefulErrorMessage for details)

```
リポジトリの追加後、TAPパッケージをインストールする。
インストールにあたり、何をどのような設定でインストールするかを書いた設定ファイルを作成する。
なお、設定にあたりProfileを選択する必要があり、今回は`full`を利用する。
Profileの公式な説明は[こちら](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/about-package-profiles.html#installation-profiles-in-tanzu-application-platform-v16-1)にあるが、目的別にインストールするパッケージを選択するためのグループみたいなものと考えるとよい。
full profileの[公式サンプル](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/install-online-profile.html#full-profile-3)をダウンロードして編集する。
設定値の意味はざっくりこんな感じ。

|パラメータ|意味|設定例|
|---|---|---|
|shared|共通のセクション。設定した値が様々なコンポーネント・サービスでのデフォルト値となる|
|shared.ingress_domain|外部にサービスを公開する際のサブドメイン|tap.127.0.0.1.sslip.io|
|shared.ingress_issuer|cert-managerを使って自己署名証明書を使う際のClusterIssuerの名前|tap-ingress-selfsigned|
|shared.image_registry.project_path|TBSなどで利用するイメージの格納先|my-harbor.io/my-project/build-service|
|shared.image_registry.secret|上記のproject_pathへアクセスするための認証情報のSecret|-|
|shared.kubernetes_version|K8sのバージョン|"1.25"|
|shared.ca_cert_data|持ち込みの証明書を使う場合などに指定|-|
|ceip_policy_disclosed|trueにしないとインストールに失敗するため、trueを書く|true|
|profile|Profile種別を指定|full|
|supply_chain|コードコミット時に流すパイプラインの指定|testing_scanning|
|ootb_supply_chain_basic|上のsupply_chainで指定したパイプラインに関する設定。詳細は[こちら](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scc-ootb-supply-chain-testing-scanning.html)　※パラメータ名(key)は`ootb_supply_chain_<上記で指定したパラメータ名>`となる。上の例では`ootb_supply_chain_testing_scanning`になる|-|
|contour|一緒にデプロイするContourの設定|-|
|buildservice.kp_default_repository|TBSで利用するイメージのリポジトリ。指定しなければsharedで書いたimage_registry.project_pathが使われる|my-harbor.io/my-project/build-service|
|buildservice.kp_default_repository_secret|TBSで利用するイメージのリポジトリの認証情報のSecret。指定しなければsharedで書いたimage_registry.secretが使われる||
|local_source_proxy.repository|Local Source Proxyで使うソースコードのアップロード先リポジトリ。詳細な説明は[こちら](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/local-sorce-proxy-install.html)|my-harbor.io/my-project/local-source|
|local_source_proxy.push_secret|コードをPushするための認証情報を格納したSecret|-|
|local_source_proxy.push_secret.create_export|`kind: SecretExport`オブジェクトを作成する|true|
|tap_gui.metadataStoreAutoconfiguration|接続用のServiceAccountの作成などを自動で行う|true|
|tap_gui.app_config.auth.allowGuestAccess|GUIに対してゲストログインを許可するかどうか|true|
|tap_gui.catalog|TAP展開時にデフォルトで認識させるカタログ情報を設定|-|
|metadata_store.ns_for_export_app_cert|開発者向けに証明書を展開するNamespaceを指定。`"*"`で全Namespaceに公開する。||
|metadata_store.app_service_type|アプリケーション公開時のServiceのtype|LoadBalancer|
|scanning.metadataStore.url|Scan時のMetadata Storeとの接続で使うURL。基本的に空を指定する|""|
|policy.tuf_enabled|[Policy Controller](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scst-policy-overview.html)で署名する際に、The Update Framework (TUF) serverを利用する|true|
|tap_telemetry.customer_entitlement_account_number|TAPの使用状況を取得するTelemetry利用するための自身のAccount番号を指定|"123456789"|

ここでは以下のようにProfileを作成した。`shared.ca_cert_data`にはHarborのCA証明書を記載する。`shared.ingress_domain`はsslip.ioを使う場合、ドメイン名にsslip.ioが返すIPを含む必要があるが、現時点ではContourがインストールされておらずExternal IPが不明なため、一旦適当な値を設定しておき、IP取得後に更新する。
```yaml
cat <<EOF > tap-values.yaml 
shared:
  ingress_domain: "tap.127.0.0.1.sslip.io"
  ingress_issuer: tap-ingress-selfsigned

  image_registry:
    project_path: oss-harbor.10.220.157.229.sslip.io/tap
    secret:
      name: registry-credentials
      namespace: tap-install

  kubernetes_version: "1.25"
  ca_cert_data: |
    -----BEGIN CERTIFICATE-----
    MIIFyTCCA7GgAwIBAgIUVnUy+1PZ2cycIjXCNhLoFgsCw5cwDQYJKoZIhvcNAQEN
    :（省略）
    V73yewAQF+OECiocVi4OriKH+XaalpdD1VE6qjBaSEF45a0jRjwgRNPl0jFK
    -----END CERTIFICATE-----
ceip_policy_disclosed: true

profile: full

supply_chain: testing_scanning

contour:
  envoy:
    service:
      type: LoadBalancer

tap_gui:
  metadataStoreAutoconfiguration: true
  app_config:
    auth:
      allowGuestAccess: true

metadata_store:
  ns_for_export_app_cert: "*"
  app_service_type: ClusterIP

scanning:
  metadataStore:
    url: ""
EOF
```
作成したファイルを使ってインストールする。
```sh
 tanzu package install tap -p tap.tanzu.vmware.com -v $TAP_VERSION --values-file tap-values.yaml -n tap-install
```
コンポーネントが多いので、インストールが完全に完了するまでに少し時間が掛かる。
今回検証した環境では10分弱掛かった。
インストールが終わると、`tap-install`のNamespaceに`kind: app`のオブジェクトが展開された状態となる。
```sh
$ kubectl get app -n tap-install
NAME                                 DESCRIPTION           SINCE-DEPLOY   AGE
accelerator                          Reconcile succeeded   7m2s           7m46s
api-auto-registration                Reconcile succeeded   8m56s          9m28s
api-portal                           Reconcile succeeded   6m15s          7m43s
appliveview                          Reconcile succeeded   6m             7m42s
appliveview-apiserver                Reconcile succeeded   7m58s          9m20s
appliveview-connector                Reconcile succeeded   36s            11m
appliveview-conventions              Reconcile succeeded   5m21s          7m36s
appsso                               Reconcile succeeded   7m11s          9m1s
base-jammy-builder-lite              Reconcile succeeded   7m9s           7m50s
base-jammy-stack-lite                Reconcile succeeded   9m15s          10m
bitnami-services                     Reconcile succeeded   5m16s          7m10s
buildservice                         Reconcile succeeded   65s            11m
cartographer                         Reconcile succeeded   8m47s          9m24s
cert-manager                         Reconcile succeeded   11m            11m
cnrs                                 Reconcile succeeded   6m58s          7m46s
contour                              Reconcile succeeded   8m49s          9m26s
crossplane                           Reconcile succeeded   11m            11m
developer-conventions                Reconcile succeeded   5m20s          7m33s
dotnet-core-lite-buildpack           Reconcile succeeded   9m13s          10m
eventing                             Reconcile succeeded   10m            11m
fluxcd-source-controller             Reconcile succeeded   9m40s          11m
go-lite-buildpack                    Reconcile succeeded   9m12s          10m
grype                                Reconcile succeeded   5m8s           6m13s
java-lite-buildpack                  Reconcile succeeded   9m17s          10m
java-native-image-lite-buildpack     Reconcile succeeded   9m6s           10m
learningcenter                       Reconcile succeeded   5m41s          7m40s
learningcenter-workshops             Reconcile succeeded   4m53s          4m55s
local-source-proxy                   Reconcile succeeded   9m34s          11m
metadata-store                       Reconcile succeeded   6m45s          7m44s
namespace-provisioner                Reconcile succeeded   9m30s          11m
nodejs-lite-buildpack                Reconcile succeeded   9m7s           10m
ootb-delivery-basic                  Reconcile succeeded   5m2s           5m5s
ootb-supply-chain-testing-scanning   Reconcile succeeded   4m56s          5m3s
ootb-templates                       Reconcile succeeded   5m25s          7m37s
policy-controller                    Reconcile succeeded   7m52s          9m19s
python-lite-buildpack                Reconcile succeeded   9m3s           10m
ruby-lite-buildpack                  Reconcile succeeded   9m4s           10m
scanning                             Reconcile succeeded   7m29s          9m16s
service-bindings                     Reconcile succeeded   9m26s          11m
services-toolkit                     Reconcile succeeded   8m26s          9m23s
source-controller                    Reconcile succeeded   7m41s          9m17s
spring-boot-conventions              Reconcile succeeded   5m28s          7m38s
tap                                  Reconcile succeeded   3m21s          12m
tap-auth                             Reconcile succeeded   9m23s          11m
tap-gui                              Reconcile succeeded   5m3s           5m19s
tap-telemetry                        Reconcile succeeded   116s           12m
tekton-pipelines                     Reconcile succeeded   44s            11m
web-servers-lite-buildpack           Reconcile succeeded   9m15s          10m
```
ただし、ドメインを適当に割り当てたため、これをContourのIPに変更する。現状は以下。
```sh
$ kubectl get httpproxy -A
NAMESPACE        NAME                     FQDN                                    TLS SECRET            STATUS   STATUS DESCRIPTION
api-portal       api-portal               api-portal.tap.127.0.0.1.sslip.io       api-portal-tls-cert   valid    Valid HTTPProxy
metadata-store   metadata-store-ingress   metadata-store.tap.127.0.0.1.sslip.io   ingress-cert          valid    Valid HTTPProxy
tap-gui          tap-gui                  tap-gui.tap.127.0.0.1.sslip.io          tap-gui-cert          valid    Valid HTTPProxy
```
tap-values.yamlを編集し、`shared.ingress_domain`を以下のように変更する。
```yaml
shared:
  ingress_domain: "tap.10.220.157.170.sslip.io"
```
updateする。
```sh
tanzu package installed update --values-file tap-values.yaml tap -n tap-install
```
設定した値が確認できる。
```sh
$ kubectl get httpproxy -A
NAMESPACE        NAME                     FQDN                                         TLS SECRET            STATUS   STATUS DESCRIPTION
api-portal       api-portal               api-portal.tap.10.220.157.170.sslip.io       api-portal-tls-cert   valid    Valid HTTPProxy
metadata-store   metadata-store-ingress   metadata-store.tap.10.220.157.170.sslip.io   ingress-cert          valid    Valid HTTPProxy
tap-gui          tap-gui                  tap-gui.tap.10.220.157.170.sslip.io          tap-gui-cert          valid    Valid HTTPProxy
```
tap-guiにブラウザでアクセスすると、正常に表示される。
![1693358673436.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/a26b34b0-1f88-0b18-d3b3-097d13a2de61.png)

## 動作確認

### Application Acceleratorを用いたAngular Frontendのデプロイ
Application Acceleratorはアプリ開発の際に使える雛形を提供する。これを使ってTAPでアプリをデプロイする。
TAPのGUIにアクセスし、左サイドバーの上から4つ目の⊕みたいなアイコンをクリックすると、利用可能なアプリケーション一覧が表示される。
![1693813495899.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/0301e17b-01c1-4000-d7bb-f8f8a2102738.png)


今回はAngular Frontendでデプロイしてみる。
Angular Frontendを選択すると以下のような設定画面が出る。
![1693814001098.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/d474cea9-a5a0-45ca-3420-12bc134fc9ec.png)


設定項目の意味は以下の通り。
- `Name`: プロジェクト名。accelerator-info.yamlとaccelerator-log.mdの`projectName`に設定される。
- `Application artifact name`: アーティファクト名。プロジェクトのID的に扱われる。accelerator-info.yamlとaccelerator-log.md以外にもworkload.yaml、catalog-info.yamlの名前やAnnotaionに設定される。
- `Hostname of the backend services app`: Frontendの接続先のBackendのサービス名。nginx.confに`proxy_pass http://customer-profile-backend.namespace/api/;`のような感じで設定される

本来はバックエンドを適切に設定しないとアプリが動作しないが、ここではパイプラインを動かすことを目的として、特に設定値を変更せずに先に進み、`GENERATE ACCELERATOR`をクリック後、`DOWNLOAD ZIP FILE`をクリックしてアプリのソースコードを生成する。

ダウンロードしたファイルを解凍すると、中に`config/workload.yaml`というファイルが配置されている。
これがTAPが使うWorkloadリソースの定義となる。
ファイルを開くと`spec.source.git.url`が`<https URL for your generated project's Git repository>`となっているので、このソースを格納するGitリポジトリを指定する。
以下は指定例となる。
```yaml
source:
    git:
      url: http://gitea.10.220.157.170.sslip.io/bn_user/angular-frontend
      ref:
        branch: main
```
ソースコードをGitにpushする。
```sh
git clone http://gitea.10.220.157.170.sslip.io/bn_user/angular-frontend.git angular-frontend-git
cp -r angular-frontend-git/.git angular-frontend
cd angular-frontend
git config --global user.email bn_user@hoge.com
git config --global user.name "bn_user"
git add .
git commit -m "first commit"
git checkout -b main
git push origin main
```
次にデプロイ先のNamespaceを作成する。
```sh
kubectl create ns tap-demo
```
作成したNamespaceに、TAPの各種リソースが動作できるようにRole等の設定をしていく。
[公式手順](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/install-online-set-up-namespaces.html)によると、[Namespace Provisioner](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/namespace-provisioner-about.html)を使ってNamespaceを設定することになっているため、こちらでNamespaceを設定する。
具体的な手順は[こちら](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/namespace-provisioner-provision-developer-ns.html)を参考にした。

GitのリポジトリのSecret作成が前提条件となっているので作成する。
```sh
export GIT_SERVER=gitea.10.220.157.170.sslip.io
export GIT_USER=bn_user
export GIT_PASSWORD=xxxx
tanzu secret registry add git-credentials \
  --server ${GIT_SERVER} \
  --username ${GIT_USER} \
  --password ${GIT_PASSWORD} \
  --namespace tap-install \
  --export-to-all-namespaces \
  -y
```

TAPのデプロイ先となるNamespaceがNamespace Provisionerの対象となるようラベルを設定する。
```sh
kubectl label namespaces tap-demo apps.tanzu.vmware.com/tap-ns=""
```
設定が反映されると、`tap-namespace-provisioning`というNamespaceにある`desired-namespaces`というConfigMapにNamespaceが追加される。
```jq
$ kubectl get cm -o jsonpath={.data} -n tap-namespace-provisioning desired-namespaces | jq
{
  "namespaces.yaml": "#@data/values\n---\nnamespaces:\n- name: tap-demo\n"
}
```

次に`kind: Pipeline`を作成する。TAPインストール時に`supply_chain: testing_scanning`を指定すると、test用に`kind: Pipeline`で定義されたテスト用パイプラインを実行するのだが、今のままでは実行するパイプラインが1つもないので、以下のエラーを吐いて失敗する。
```sh
    message: 'unable to resolve selector [map[apps.tanzu.vmware.com/pipeline:test]],
      apiVersion [tekton.dev/v1beta1], kind [Pipeline]: selector did not match any
      objects'****
```
エラーにあるように、`apps.tanzu.vmware.com/pipeline: test`というラベルを持つ`Pipeline`リソースをダミーで用意する。
[公式のサンプル](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scc-ootb-supply-chain-testing.html#updates-to-the-developer-namespace-2)を参考に以下のような感じで書く。
```yaml
cat <<'EOF' > ./dummy-pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: skip-test-pipeline
  labels:
    apps.tanzu.vmware.com/pipeline: test
    apps.tanzu.vmware.com/language: skip
spec:
  params:
  - name: source-url
  - name: source-revision
  tasks:
  - name: test
    params:
    - name: source-url
      value: $(params.source-url)
    - name: source-revision
      value: $(params.source-revision)
    taskSpec:
      params:
      - name: source-url
      - name: source-revision
      steps:
      - name: test
        image: alpine
        script: |-
          echo 'skip'
EOF
```
作成後、applyする。
```sh
kubectl apply -f dummy-pipeline.yaml -n tap-demo
```

また、同様にイメージスキャンについてもScanPolicyが用意されていないため、このままだと以下のエラーとなって最後まで走りきらない。
```sh
  - error: ScanPolicy tap-demo/scan-policy not found
    lastTransitionTime: "2023-09-05T10:22:09Z"
    message: The scan job could not be started. ScanPolicy tap-demo/scan-policy not
      found
```
[こちら](https://docs.vmware.com/en/VMware-Tanzu-Application-Platform/1.6/tap/scc-ootb-supply-chain-testing-scanning.html#developer-namespace-1)のManifestを使ってScanPolicyを作成する。
```yaml
cat << EOF > ./scan-policy.yaml
apiVersion: scanning.apps.tanzu.vmware.com/v1beta1
kind: ScanPolicy
metadata:
  name: scan-policy
  labels:
    'app.kubernetes.io/part-of': 'enable-in-gui'
spec:
  regoFile: |
    package main

    # Accepted Values: "Critical", "High", "Medium", "Low", "Negligible", "UnknownSeverity"
    notAllowedSeverities := ["Critical", "High", "UnknownSeverity"]
    ignoreCves := []

    contains(array, elem) = true {
      array[_] = elem
    } else = false { true }

    isSafe(match) {
      severities := { e | e := match.ratings.rating.severity } | { e | e := match.ratings.rating[_].severity }
      some i
      fails := contains(notAllowedSeverities, severities[i])
      not fails
    }

    isSafe(match) {
      ignore := contains(ignoreCves, match.id)
      ignore
    }

    deny[msg] {
      comps := { e | e := input.bom.components.component } | { e | e := input.bom.components.component[_] }
      some i
      comp := comps[i]
      vulns := { e | e := comp.vulnerabilities.vulnerability } | { e | e := comp.vulnerabilities.vulnerability[_] }
      some j
      vuln := vulns[j]
      ratings := { e | e := vuln.ratings.rating.severity } | { e | e := vuln.ratings.rating[_].severity }
      not isSafe(vuln)
      msg = sprintf("CVE %s %s %s", [comp.name, vuln.id, ratings])
    }
EOF
```
作成後、applyする。
```sh
kubectl apply -f ./scan-policy.yaml -n tap-demo
```



アプリをデプロイする。
```sh
tanzu apps workload create angular-frontend \
  --git-repo http://gitea.10.220.157.170.sslip.io/bn_user/angular-frontend \
  --git-branch main \
  --type web \
  --label app.kubernetes.io/part-of=angular-frontend \
  --label apps.tanzu.vmware.com/has-tests="true" \
  --yes \
  --namespace tap-demo
```
デプロイ後、tanzuコマンドでデプロイ状況が分かる。
```sh
$ tanzu apps workload get angular-frontend -n tap-demo
📡 Overview
   name:        angular-frontend
   type:        web
   namespace:   tap-demo

💾 Source
   type:       git
   url:        http://gitea.10.220.157.170.sslip.io/bn_user/angular-frontend
   branch:     main
   revision:   main@sha1:e3993727678e5df64775ba18e19d21a8a3d9b239
：（省略）
```
なお、デプロイ中は途中で以下のようなエラーが見受けられるが、イメージ作成前にイメージのチェックが走ったりしているだけなので、Supply Chainの状況だけ確認しておけば基本問題ない。
```sh
💬 Messages
   Workload [MissingValueAtPath]:   waiting to read value [.status.latestImage] from resource [images.kpack.io/angular-frontend] in namespace [tap-demo]
   Deliverable [HealthyConditionRule]:   Unable to resolve image with tag "oss-harbor.10.220.157.229.sslip.io/tap/workloads/angular-frontend-tap-demo-bundle:fe73ab71-aa3f-4da6-a6ee-9f77e1ecdd47" to a digest: HEAD https://oss-harbor.10.220.157.229.sslip.io/v2/tap/workloads/angular-frontend-tap-demo-bundle/manifests/fe73ab71-aa3f-4da6-a6ee-9f77e1ecdd47: unexpected status code 404 Not Found (HEAD responses have no body, use GET for details)
```


また、Kubernetesリソースとして確認する場合は`kubectl tree`や`kubectl lineage`を使った方が分かりやすい。
```sh
$ kubectl lineage workload angular-frontend -n tap-demo
NAME                                          READY     STATUS                                                                                                                            AGE
Workload/angular-frontend                     Unknown   MissingValueAtPath                                                                                                                9s
├── Deliverable/angular-frontend              -                                                                                                                                           4s
├── Event/angular-frontend.1781f94b23f35945   -         StampedObjectApplied: Created object [gitrepositories.source.toolkit.fluxcd.io/angular-frontend]                                  5s
├── Event/angular-frontend.1781f94b2c049a49   -         StampedObjectApplied: Created object [deliverables.carto.run/angular-frontend]                                                    4s
├── Event/angular-frontend.1781f94b2c066e17   -         ResourceHealthyStatusChanged: [deliverable] found healthy status in [deliverables.carto.run/angular-frontend] changed to [True]   4s
└── GitRepository/angular-frontend            Unknown   Progressing

```
しばらくすると、`tanzu apps workload get angular-frontend -n tap-demo`のSupply Chainが全てTrueとなる。
TAPのGUIからも`Suppliy Chain`からangular-frontendを見ると全てが完了していることが分かる。
![1693995235946.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/493d2df9-7322-ecda-8478-37f17f5de0a9.png)
ただし、今回Angularのバックエンドを用意しなかったため、AngularのPod自体は起動しない。
KnativeのRevisionリソースを見ると、失敗した原因が確認できる。
```sh
$ kubectl get revision -o yaml angular-frontend-00001 -n tap-demo
：（省略）
  - lastTransitionTime: "2023-09-05T10:44:26Z"
    message: |
      Container failed with: 2023/09/05 10:40:25 [emerg] 1#0: host not found in upstream "customer-profile-backend.namespace" in /workspace/nginx.conf:162
```
Application Acceleratorで設定したバックエンド`customer-profile-backend.namespace`が見つからないため失敗している。
ということで、次にちゃんと起動するサンプルを使って確認する。

### Application Acceleratorを用いたTanzu Java Web Appのデプロイ
Acceleratorから選べるTanzu Java Web Appはバックエンドを必要としないサンプルなので、こちらでアプリにアクセス出来るところまで確認する。
Application AcceleratorからTanzu Java Web Appを選択する。
![1693970005005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/5ab4fc81-9c6b-87fc-a27c-95097a96a1e6.png)
設定値は特に変えずに`NEXT`を押して先に進む。
![1693970032831.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/9d844480-98cf-9f8b-b89b-a99f9f18e2ee.png)
この後の流れはangular-frontendと同じため、基本的なところは割愛する。
1点だけ変更点があり、ScanPolicyがangular-frontendと同じものを使うと、脆弱性が見つかってパイプラインが止まってしまう。
```sh
$ kubectl get imagescans.scanning.apps.tanzu.vmware.com/tanzu-java-web-app -n tap-demo
NAME                 PHASE    SCANNEDIMAGE                                                                                                                                           AGE     CRITICAL   HIGH   MEDIUM   LOW   UNKNOWN   CVETOTAL
tanzu-java-web-app   Failed   oss-harbor.10.220.157.229.sslip.io/tap/workloads/tanzu-java-web-app-tap-demo@sha256:2043a440e9e407898fff97a3fbeff54f4debde4e9ff3e0f0585d6204142f1d82   4m18s   1          1      5        13    0         20
```
そのため、ScanPolicyを以下のようにCritical, Highが見つかっても通すように変更する。
```diff
$ diff -u scan-policy.yaml scan-policy-java.yaml
--- scan-policy.yaml	2023-09-05 10:27:39.760463422 +0000
+++ scan-policy-java.yaml	2023-09-06 10:05:54.733360949 +0000
@@ -9,7 +9,7 @@
     package main

     # Accepted Values: "Critical", "High", "Medium", "Low", "Negligible", "UnknownSeverity"
-    notAllowedSeverities := ["Critical", "High", "UnknownSeverity"]
+    notAllowedSeverities := ["UnknownSeverity"]
     ignoreCves := []

     contains(array, elem) = true {
```

設定を変更して、Workloadを作成する。
```sh
tanzu apps workload create tanzu-java-web-app --git-repo http://gitea.10.220.157.170.sslip.io/bn_user/tanzu-java-web-app   --git-branch main --type web --label app.kubernetes.io/part-of=tanzu-java-web-app --label apps.tanzu.vmware.com/has-tests="true" --yes --namespace tap-demo
```

なお、こちらもSupply Chainが全て流れても、Podは起動しない。これはKnativeというScale-to-Zeroを実現するOSSでデプロイされているせいであり、サービスのエンドポイントにアクセスして始めてPodが作成される。
サービスのエンドポイントはknativeのServiceリソース（通称ksvc）を見ることで確認できる。
```sh
$  kubectl get ksvc -n tap-demo
NAME                 URL                                                               LATESTCREATED              LATESTREADY                READY   REASON
angular-frontend     http://angular-frontend.tap-demo.tap.10.220.157.170.sslip.io      angular-frontend-00001                                False   RevisionMissing
tanzu-java-web-app   https://tanzu-java-web-app.tap-demo.tap.10.220.157.170.sslip.io   tanzu-java-web-app-00001   tanzu-java-web-app-00001   True
```
tanzu-java-web-appのURLにブラウザでアクセスしてみると、以下の画面が表示される。
![1693995105729.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/5404b5b9-2ad7-92c1-5fe0-7d91528cb394.png)

サービスアクセス後、Podを見てみると、エンドポイントへのアクセス契機でPodが作成されたことが分かる。
```sh
$ kubectl get pod -n tap-demo 
：（省略）
tanzu-java-web-app-00001-deployment-f957476dd-tscsg   2/2     Running     0          22s
```
しばらく待って、再度Podを見ると、Podは削除されていることも分かる。

#### GitOpsの確認
ソースコードの修正契機でSupply Chainが動くかも確認する。
ブラウザで表示された文字列は、
`src/main/java/com/example/springboot/HelloController.java`でコーディングされている。
こちらを以下のように修正してGitレポジトリにpushする。
```java
        public String index() {
                return "Greetings from Spring Boot + Tanzu! (edited)";
        }
```
しばらく待って`tanzu apps workload get tanzu-java-web-app -n tap-demo`を叩くと、Supply Chainの箇所でテストやビルドが再度実行されていることが分かる。
```sh
$ tanzu apps workload get tanzu-java-web-app -n tap-demo
：（省略）
📦 Supply Chain
   name:   source-test-scan-to-url

   NAME               READY     HEALTHY   UPDATED   RESOURCE
   source-provider    True      True      33m       gitrepositories.source.toolkit.fluxcd.io/tanzu-java-web-app
   source-tester      True      True      33s       runnables.carto.run/tanzu-java-web-app
   image-provider     Unknown   Unknown   23s       images.kpack.io/tanzu-java-web-app
   image-scanner      True      True      24m       imagescans.scanning.apps.tanzu.vmware.com/tanzu-java-web-app
   config-provider    True      True      24m       podintents.conventions.carto.run/tanzu-java-web-app
   app-config         True      True      24m       configmaps/tanzu-java-web-app
   service-bindings   True      True      24m       configmaps/tanzu-java-web-app-with-claims
   api-descriptors    True      True      24m       configmaps/tanzu-java-web-app-with-api-descriptors
   config-writer      True      True      24m       runnables.carto.run/tanzu-java-web-app-config-writer
：（省略）
```
SupplyChainが流れきると、knativeのServiceが参照しているRevisionが上がっていることが分かる。
```sh
$ kubectl get ksvc tanzu-java-web-app -n tap-demo
NAME                 URL                                                               LATESTCREATED              LATESTREADY                READY   REASON
tanzu-java-web-app   https://tanzu-java-web-app.tap-demo.tap.10.220.157.170.sslip.io   tanzu-java-web-app-00002   tanzu-java-web-app-00002   True
```
再度ブラウザでアクセスすると、更新したコードの内容が反映されていることが分かる。
![1693996635323.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/acc807d2-23c1-621d-15de-7ba48bf00362.png)

## 注意点
crossplaneというコンポーネントがいるが、kapp-controllerのようにPod内からイメージレジストリを参照する模様。
そのため、crossplaneを使う場合でかつプライベートなイメージレジストリを使う場合、`shared.ca_cert_data`にイメージレジストリの証明書を設定しておく必要がある。

## 付録：Giteaの構築
bitnamiでGiteaが[公開されている](https://bitnami.com/stack/gitea/helm)ので、これを使ってインストールする。
設定ファイルを取得する。
```
helm show values oci://registry-1.docker.io/bitnamicharts/gitea > gitea-values.yaml
```
ingressを使いたかったので、以下の変更を行った。
```diff
gitea-values.yaml.orig gitea-values.yaml
--- gitea-values.yaml.orig	2023-09-04 07:29:40.453002106 +0000
+++ gitea-values.yaml	2023-09-04 07:32:22.721681648 +0000
@@ -458,7 +458,7 @@
 ingress:
   ## @param ingress.enabled Enable ingress controller resource
   ##
-  enabled: false
+  enabled: true

   ## @param ingress.pathType Ingress Path type
   ##
@@ -473,7 +473,7 @@
   ingressClassName: ""
   ## @param ingress.hostname Default host for the ingress resource
   ##
-  hostname: "gitea.local"
+  hostname: "gitea.10.220.157.170.sslip.io"
   ## @param ingress.path The Path to Gitea. You may need to set this to '/*' in order to use this
   ## with ALB ingress controllers.
   ##
@@ -494,10 +494,10 @@
   ## TLS certificates will be retrieved from a TLS secret with name: {{- printf "%s-tls" .Values.ingress.hostname }}
   ## You can use the ingress.secrets parameter to create this TLS secret or relay on cert-manager to create it
   ##
-  tls: false
+  tls: true
   ## @param ingress.selfSigned Create a TLS secret for this ingress record using self-signed certificates generated by Helm
   ##
-  selfSigned: false
+  selfSigned: true
   ## @param ingress.extraHosts The list of additional hostnames to be covered with this ingress record.
   ## Most likely the hostname above will be enough, but in the event more hosts are needed, this is an array
   ## extraHosts:
```

インストールする。
```
helm install gitea oci://registry-1.docker.io/bitnamicharts/gitea -f gitea-values.yaml --create-namespace -n gitea
```

初期ユーザ(`bn_user`)のパスワードを取得する。
```
kubectl get secret --namespace gitea gitea -o jsonpath="{.data.admin-password}" | base64 -d
```
ログインすると以下のような画面が表示される。
![1693813122136.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/527cf086-6530-5e92-b144-45f62c57c05b.png)


パスワードを変更しておく。ログインして右上のユーザアイコンをクリックし、`設定`->`アカウント`から変更できる。
![1693820456488.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/de9356e2-a03c-68f7-ea43-b9b000e1fed4.png)