ついに kubectl plugin のシェル補完がサポートされるぞ (Kubernetes 1.26)

## はじめに

Kubernetes のコマンドラインツールである kubectl にはシェル補完が実装されており、サブコマンドやフラグからクラスタに存在するオブジェクト名といった動的なものまで補完してくれてクラスタ操作を効率に行えるようになっています。Kubernetes を利用されている方でまだシェル補完を使っていない方はぜひ使ってみてください。

```
$ kubectl <tab>
alpha          -- Commands for features in alpha
annotate       -- Update the annotations on a resource
api-resources  -- Print the supported API resources on the server
api-versions   -- Print the supported API versions on the server, in the form of "group/version"
apply          -- Apply a configuration to a resource by file name or stdin
attach         -- Attach to a running container
auth           -- Inspect authorization
autoscale      -- Auto-scale a deployment, replica set, stateful set, or replication controller
...
```

2022年12月にリリース予定の Kubernetes 1.26 からはついに kubectl plugin のコマンド名のシェル補完サポートが入ります。加えて kubectl plugin 自体のサブコマンドやフラグ、オブジェクト名等の動的な補完までもがサポートされました。すごい。

ここでは、新しくサポートされる kubectl plugin 関連のシェル補完機能について紹介します。

## そもそも kubectl plugin とは

kubectl plugin は kubectl に任意のサブコマンドを追加する機能です。Kubernetes は拡張性を重視していて、その一環として kubectl の拡張機能として提供されています。Kubernetes 1.14 で GA になりました。

kubectl plugin のプラグイン機構は一般的に git-style プラグイン機構と呼ばれています。このプラグイン機構は、PATH の通ったディレクトリに配置された実行ファイルの名前が特定のプレフィックスで開始しているものを呼び出すというもので、kubectl plugin の場合はプレフィックスが `kubectl-` です。例えば `kubectl-foo` という実行ファイルを作成し、PATH の通ったディレクトリに配置すると、`kubectl foo` コマンドとして実行できます。kubectl plugin を便利に使うには kubectl plugin マネージャの [Krew ツール](https://github.com/kubernetes-sigs/krew)の利用がおすすめです。便利なプラグインが多く登録されていて、好みのものを検索して簡単にインストールできます。私が開発しているプラグインも登録しているので、よければ使ってみてください。

- [stern/stern: ⎈ Multi pod and container log tailing for Kubernetes \-\- Friendly fork of https://github\.com/wercker/stern](https://github.com/stern/stern)
- [superbrothers/kubectl\-open\-svc\-plugin: kubectl open\-svc plugin makes services accessible via their ClusterIP from outside your cluster](https://github.com/superbrothers/kubectl-open-svc-plugin)
- [superbrothers/kubectl\-view\-serviceaccount\-kubeconfig\-plugin: A kubectl plugin that show a kubeconfig to access the apiserver with a specified serviceaccount\.](https://github.com/superbrothers/kubectl-view-serviceaccount-kubeconfig-plugin)

kubectl plugin は便利に使えるのですが、残念なことにシェル補完がサポートされていませんでした。そのためわざわざプラグインとして追加されているサブコマンドをすべて入力し切る必要があり面倒でした。

## kubectl plugin のコマンド名のシェル補完がサポートされる

2022年12月にリリース予定の Kubernetes 1.26 でついに kubectl plugin のシェル補完がサポートされます。例えば Krew をインストールしている場合には `kubectl k<tab>` と入力するとプラグインである `krew` が補完候補として出てきます。さいこう。

```
$ kubectl k<tab>
krew       -- The command krew is a plugin installed by the user
kustomize  -- Build a kustomization target from a directory or URL.
```

## kubectl plugin 自体のシェル補完もサポートされる

これだけでも十分うれしいのですが、今回さらに kubectl plugin 自体のサブコマンドやフラグ、そのほか動的なオブジェクト名の補完も一緒にサポートされます。すごい。

kubectl plugin 自体のシェル補完というのは、つまり `kubectl krew <tab>` と入力すると、Krew が持つサブコマンドが補完候補として出てくるようになるということです。さいこうに便利。

```
$ kubectl krew <tab>
completion  -- generate the autocompletion script for the specified shell
help        -- Help about any command
index       -- Manage custom plugin indexes
info        -- Show information about an available plugin
install     -- Install kubectl plugins
list        -- List installed kubectl plugins
search      -- Discover kubectl plugins
uninstall   -- Uninstall plugins
update      -- Update the local copy of the plugin index
upgrade     -- Upgrade installed plugins to newer versions
version     -- Show krew version and diagnostics
```

使うには少し手間があり、PATH の通ったディレクトリにプラグインに対応する `kubectl_complete-<name>` コマンドが存在し、これが spf13/cobra ライブラリの第二世代補完機能の補完形式で候補を返す必要があります。例えば Krew の場合は次のファイルが PATH の通ったディレクトリに配置されていれば補完が有効になります。

```bash
#!/usr/bin/env sh

# Call the __complete command passing it all arguments
kubectl krew __complete "$@"
```

## kubectl plugin 自体のシェル補完のしくみ

kubectl plugin は kubectl とは別の独立した実行可能ファイルです。そのため、通常であれば kubectl がプラグインが持つサブコマンドやフラグを知るはずがなく、補完候補を出すことができません。そこで kubectl が補完候補を返すコマンド `kubectl_complete-<name>` を裏で実行することにより外部コマンドであるプラグインの補完候補を得られるようにしました。わざわざ補完候補を返すコマンドを用意するのはとても面倒なんですが、spf13/cobra ライブラリを使って実装されたプラグインには実は補完候補を返す隠しサブコマンド (`__complete`) が用意されているので、それをそのまま使える形とすることで補完候補を返すコマンドを楽に用意できるのです。

spf13/cobra は Go の CLI 開発向けのライブラリで、kubectl でも使われています。

- [spf13/cobra: A Commander for modern Go CLI interactions](https://github.com/spf13/cobra)

kubectl のシェル補完機能はこのライブラリが持つ機能が使われていて、このライブラリのシェル補完は補完候補を `__complete` サブコマンドの出力から得るという仕組みになっているのです。なので、kubectl もこの隠しコマンドを持っています。`kubectl __complete k` を実行すると、補完候補となるコマンドが出力されます。

```
$ kubectl __complete k
krew    The command krew is a plugin installed by the user
kustomize       Build a kustomization target from a directory or URL.
:4
Completion ended with directive: ShellCompDirectiveNoFileComp
```

出力する補完候補の仕様は下記に記述があります。spf13/cobra を使用していないプラグインでも仕様に沿って出力すればシェル補完を提供できます。

- https://github.com/spf13/cobra/blob/main/shell_completions.md


:::note info
Tip
spf13/cobra のシェル補完機能には第一世代と第二世代の2つの実装があります。

第一世代ではシェルスクリプトの補完コードにすべてのサブコマンドとそれらが持つフラグを情報が含まれていました。そのため、コマンドが大きくなると補完コードのサイズがとても大きくなり読み込みに非常に時間がかかる問題がありました。下記のように v1.19 では補完コードが13090行もあります😱 このせいで kubectl でシェル補完を有効にするとシェル起動時に長時間待たされていました。また、オブジェクト名等の動的な補完をシェルスクリプトで実装しなければならず、テストが難しいという問題もありました。

```
$ kubectl version --client
Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.0", GitCommit:"e19964183377d0ec2052d1f1fa930c4d7575bd50", GitTreeState:"clean", BuildDate:"2020-08-26T14:30:33Z", GoVersion:"go1.15", Compiler:"gc", Platform:"linux/amd64"}
$ kubectl completion zsh | wc -l
13090
```

第二世代では `__complete` サブコマンドから補完候補を取得するというしくみに変わりました。これにより、補完コードにサブコマンドやフラグの情報が含まれなくなったため、サイズが小さくなり読み込みにかかる時間が短くなりました。下記のように v1.25 では補完コードがたった193行になってます。また、隠しサブコマンドから補完候補を得るという仕組みになったことで、動的な補完の実装を Go で書けるようになりました。テストも容易に実装できるようにもなってさいこうの仕組みです。

```
$ kubectl version --client
Client Version: version.Info{Major:"1", Minor:"25", GitVersion:"v1.25.0", GitCommit:"a866cbe2e5bbaa01cfd5e969aa3e033f3282a8a2", GitTreeState:"clean", BuildDate:"2022-08-23T17:44:59Z", GoVersion:"go1.19", Compiler:"gc", Platform:"linux/amd64"}
Kustomize Version: v4.5.7
$ kubectl completion zsh | wc -l
193
```
:::

## kubectl plugin 自体のシェル補完の今後の流れ

現時点ではプラグイン自体の補完を有効にしたい場合には、自分自身で `kubectl-complete-<name>` コマンドを用意する必要があります。今後 Krew でプラグインをインストールした場合には自動的にコマンドが用意されるようになっていくはずです。2022年11月30日時点ではまだ Design Doc もまだ作成されておらず、提供には時間がかかりそうです。

- [Proposal: Installing an additional executable for plugin's completion script · Issue \#812 · kubernetes\-sigs/krew](https://github.com/kubernetes-sigs/krew/issues/812)

もしあなたがプラグイン開発者なら、`kubectl-complete-<name>` コマンドを作成する手順をドキュメントに追加できます。リリースのアーカイブファイルに補完候補を返すコマンドを含めておいてもいいかもしれません。

## まとめ

ここでは Kubernetes 1.26 から提供される kubectl plugin に関連するシェル補完について紹介しました。私は、kubectl が第一世代の仕組みを採用していた頃にシェル補完機能に多くパッチを提供していたことがあり、このあたりの変更には思い入れがあります。今回入った kubectl plugin のコマンド名の補完をサポートするパッチも私が書いて PR したものだったんですが、プラグイン自体の補完も一緒に入れたいということでクローズされました。その後、他の方が引き継いで続きを実装してくれたものが今回の実装の PR というわけです。

- [Shell completion for plugins by marckhouzam · Pull Request \#105867 · kubernetes/kubernetes](https://github.com/kubernetes/kubernetes/pull/105867)

私としては念願の機能になりました。Kubernetes 1.26 がリリースされた際はぜひ使ってみてください。

## 参考

- [kubectl completion: Bash/Zsh 向けの kubectl のシェル自動補完 \- Qiita](https://qiita.com/superbrothers/items/631508630320aa1dbcbc)
- [kubectl のプラグイン機能 kubectl plugin を使おう！ \- Qiita](https://qiita.com/superbrothers/items/b4a0aab0575ca6d65739)