超簡単 kubectlで取得するカスタムリソースの表示項目をカスタマイズする方法

kubectlで取得するカスタムリソース表示項目を超簡単にカスタマイズする方法を紹介します。題材は、みんな大好き[Gatling Operator](https://github.com/st-tech/gatling-operator)のカスタムリソース。

Gatling Operatorとは何ですか？という方、まずはこちらの記事を参照ください。

https://techblog.zozo.com/entry/gatling-operator-introduction

## やりたいこと

Gatling Operatorで実行するGatlingベース負荷試験の処理ステータスはGatlingカスタムリソースのstatus情報を取得して確認できます。Gatlingカスタムリソースではstatus情報をサブリソースとして取得できるように設定しているので、次のようにkubectlで取得できます。ただ、これを毎回叩くの少々面倒ですよね？

```bash
kubectl get gatling gatling-sample01 -o jsonpath='{@.status}' |jq

{
  "reportCompleted": true,
  "reportStoragePath": "s3:my-gatling-reports-0001/gatling-sample01/4010524731",
  "reportUrl": "https://my-gatling-reports-0001.s3.amazonaws.com/gatling-sample01/4010524731/index.html",
  "reporterJobName": "gatling-sample01-reporter",
  "reporterStartTime": 1669524283,
  "runnerCompleted": true,
  "runnerCompletions": "3/3",
  "runnerJobName": "gatling-sample01-runner",
  "runnerStartTime": 1669524120,
  "NotificationCompleted": true,
  "succeeded": 3
}
```

もし、次のように見たい情報がkubectl getで取得するGatlingカスタムリソースの表示項目として含まれていたとすればどうでしょう？ 嬉しいですよね？

```bash
kubectl get gatling gatling-sample01

NAME               RUNNED   REPORTED   NOTIFIED   REPORTURL                                                                                 AGE
gatling-sample01   3/3      true       true       https://my-gatling-reports-0001.s3.amazonaws.com/gatling-sample01/3499717572/index.html   3m50s

```

というわけで本記事ではこれを超簡単に実現する方法を解説します。


## CRDにAdditional Printer Columnsを設定する

Kubernetes 1.11 以降、`kubectl get`でクラスタAPIサーバーに問い合わせて取得するカスタムリソースの表示項目を指定できます。これはCRDの[Additional printer columns](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#additional-printer-columns)に取得したいカラムを指定することで実現できます。

k8s.ioの[サンプル](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#additional-printer-columns)をつかって説明します。

次のようにcrontabs CRDの`additionalPrinterColumns`にSpec, Replicas, Ageカラムを設定を追加します。

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct
  versions:
  - name: v1
    served: true
    storage: true
    schema:
　     #...omit...
    additionalPrinterColumns:
    - name: Spec
      type: string
      description: The cron spec defining the interval a CronJob is run
      jsonPath: .spec.cronSpec
    - name: Replicas
      type: integer
      description: The number of jobs launched by the CronJob
      jsonPath: .spec.replicas
    - name: Age
      type: date
      jsonPath: .metadata.creationTimestamp
```

上記のCRDを環境にapplyすれば`kubectl get`で取得するcrontabリソースに、CRDの`additionalPrinterColumns`に指定したカラム情報が取得できるという感じです。簡単ですよね。

```
kubectl get crontab my-new-cron-object

NAME                 SPEC        REPLICAS   AGE
my-new-cron-object   * * * * *   1          7s
```

## kubebuilder:printcolumnマーカーを設定してCRDを自動生成する

Gatling OperatorはKubebuilderを使って実装していますが、kubebuilderではさらに簡単にCRDのAdditional printer columns設定ができます。`+kubebuilder:printcolumn`というマーカーをGo構造体に付けることでCRDを自動生成してくれます。

ここではGatlingカスタムリソースを例にやっていきます。

まず、`kubectl get`で取得できるGatlingカスタムリソースのデフォルトの表示項目はつぎのようにNAMEとAGEになっています。CRDにAdditional printer columnsを設定しないとデフォルトでNAMEとAGEが取得されます。

```bash
kubectl get gatling gatling-sample01

NAME               AGE
gatling-sample01   97m
```

それでは、GatlingカスタムリソースのGo構造体（`api/v1alpha1/gatling_types.go`）に`+kubebuilder:printcolumn`マーカーを設定します。

まずは[追加前のgatling_types.go](https://github.com/st-tech/gatling-operator/blob/v0.8.1/api/v1alpha1/gatling_types.go#L232-L244)ですが、次のようになっています。

```go:api/v1alpha1/gatling_types.go
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status

// Gatling is the Schema for the gatlings API
type Gatling struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	// GatlingSpec defines the desired state of Gatling
	Spec GatlingSpec `json:"spec,omitempty"`
	// GatlingStatus defines the observed state of Gatling
	Status GatlingStatus `json:"status,omitempty"`
}
```

`+kubebuilder:object:root=true`と`+kubebuilder:subresource:status`の2行はKubebuilderでプロジェクトの雛形を生成した際にデフォルトで付与されているマーカーです。それぞれ次のような内容になっています。

- `+kubebuilder:object:root=true`: Gatling構造体がカスタムリソースのrootオブジェクトであることを表すマーカー
- `+kubebuilder:subresource:status`: Gatlingのstatusをサブリソース（[subresources](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#status-subresource)）として扱えるようにするマーカー。

なお、Kubebuilderのマーカーについて詳しくは[Markers for Config/Code Generation - CRD Generation](https://book.kubebuilder.io/reference/markers/crd.html#crd-generation)を参照ください。


次に、`Runned`、`Reported`、`Notified`、`ReportURL`という名前のstatusのフィールド情報を表示項目として取得できるように`+kubebuilder:printcolumn`マーカーを設定します。また、加えて`Age`というリソースの生存時間を表す項目も追加します。`+kubebuilder:printcolumn`では表示対象のフィールドをJSONPathにより指定可能になっています。

```go:api/v1alpha1/gatling_types.go
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Runned",type=string,JSONPath=`.status.runnerCompletions`
//+kubebuilder:printcolumn:name="Reported",type=boolean,JSONPath=`.status.reportCompleted`
//+kubebuilder:printcolumn:name="Notified",type=boolean,JSONPath=`.status.reportCompleted`
//+kubebuilder:printcolumn:name="ReportURL",type=string,JSONPath=`.status.reportURL`
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// Gatling is the Schema for the gatlings API
type Gatling struct {
	metav1.TypeMeta   `json:",inline"`
	metav1.ObjectMeta `json:"metadata,omitempty"`

	// GatlingSpec defines the desired state of Gatling
	Spec GatlingSpec `json:"spec,omitempty"`
	// GatlingStatus defines the observed state of Gatling
	Status GatlingStatus `json:"status,omitempty"`
}
```
※ 追加後のgatling_types.goのソースコードは[こちら](https://github.com/st-tech/gatling-operator/blob/v0.9.0/api/v1alpha1/gatling_types.go#L233-L250)


GO構造体にマーカーを設定したら`make manifests`でCRDを生成します。

```
make manifests
```

[生成されたCRD](https://github.com/st-tech/gatling-operator/blob/v0.9.0/config/crd/bases/gatling-operator.tech.zozo.com_gatlings.yaml#L19-L34)をみると次のような定義が追加されていることがわかります。

```go
- additionalPrinterColumns:
    - jsonPath: .status.runnerCompletions
      name: Runned
      type: string
    - jsonPath: .status.reportCompleted
      name: Reported
      type: boolean
    - jsonPath: .status.notificationCompleted
      name: Notified
      type: boolean
    - jsonPath: .status.reportUrl
      name: ReportURL
      type: string
    - jsonPath: .metadata.creationTimestamp
      name: Age
      type: date
```

最後に生成されたCRDを環境にインストールします。

```bash
kustomize build config/crd | kubectl apply -f -
```

これで、`kubectl get gatling`で次のように欲しかった表示項目が取得できるようになります:tada: :tada: 

```bash:kubectl get gatlingの出力結果
kubectl get gatling gatling-sample01
NAME               RUNNED   REPORTED   NOTIFIED   REPORTURL                                                                                 AGE
gatling-sample01   3/3      true       true      https://my-gatling-reports-0001.s3.amazonaws.com/gatling-sample01/3499717572/index.html   3m50s
```

このときの`.status`はこんなかんじ。

```bash:Gatling statusの内容
kubectl get gatling gatling-sample01 -o jsonpath='{@.status}' |jq
{
  "reportCompleted": true,
  "reportStoragePath": "s3:my-gatling-reports-0001/gatling-sample01/3499717572",
  "reportUrl": "https://my-gatling-reports-0001.s3.amazonaws.com/gatling-sample01/3499717572/index.html",
  "reporterJobName": "gatling-sample01-reporter",
  "reporterStartTime": 1669533890,
  "runnerCompleted": true,
  "runnerCompletions": "3/3",
  "runnerJobName": "gatling-sample01-runner",
  "runnerStartTime": 1669533727,
  "NotificationCompleted": true,
  "succeeded": 3
}
```

なお、今回の紹介したGatlingカスタムリソースの表示項目設定、実際のPRはこちらです。記事では便宜上話を少し簡略化してますが、PRでは表示用のstatusフィールドを新規で追加するためにコントローラーの修正もしていたりします。参考までに。

https://github.com/st-tech/gatling-operator/pull/66

Happy k8s life!

おわり


## REFERENCES
- https://techblog.zozo.com/entry/gatling-operator-introduction
- https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#additional-printer-columns
- https://book.kubebuilder.io/reference/generating-crd.html#additional-printer-columns
- https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#status-subresource
- https://book.kubebuilder.io/reference/markers/crd.html#crd-generation