kubectl に自動で namespaceや context を付与するような wrapper を作るとする。
そんなコマンドのインターフェースとして、
$ kubewrapper [...wrapper flags] [kubectl args / flags] (例) $ kubewrapper -p staging describe pod ...
とすると
$ kubectl --context foo --namespace bar [kubectl args / flags] (例) $ kubectl --context foo --namespace bar describe pod ...
といったコマンドが exec される、すなわちいくつかの flags を受け取って kubectl 用に変換しつつ args としては kubectl の引数をまるまる受け取るといったものが考えられる。
こういったコマンドを作るためには、補完をはじめとしていくつか注意点があるのでメモ。
外部 plugin 対応
通常の kubectl サブコマンド(get
、 logs
など)では、 --context や --namespace といった flag はサブコマンドの前に置いても動作する。
しかし、外部プラグインを呼ぶサブコマンドでは動作せず、サブコマンドのあとに flag をつける必要がある。
$ kubectl --context foo awesome-plugin Error: flags cannot be placed before plugin name: --context
get, logs などは後置でも動作するので、常に後置にする(args 1つ目の直後)ように作ると良い。
上述の例でいうと、以下のようになる。
$ kubectl [kubectl 1st arg] --context foo --namespace bar [kubectl remaining args / flags] (例) $ kubectl describe --context foo --namespace bar pod ...
kubectl の補完を利用する
kubectl では強力な補完機能を備えており、
source <(kubectl completion zsh)
すると _kubectl
関数が ZSH にインストールされて、kubectl の呼び出し時に補完用関数として呼び出されるようになる。
上述した kubewrapper
での補完でも、 kubewrapper のオプションの補完もしつつ、kubectl の補完も使えるようにしたら便利だが、どうすればよいのか?
_kubectl は何をするのか
_kubectl
は cobra を通じて生成されているが、その中では type されたコマンドの __complete
サブコマンドが呼ばれている。
($ kubectl completion zsh の結果より) requestComp="${words[1]} __complete ${words[2,-1]}"
色々細かい処理を省略すると、$words
には今まさに打ち込んでいるコマンドの配列となっており、ZSH は 1-index なので words[1]
は実行しようとするコマンド、 ${words[2,-1}
は残りの引数ということになる。
すなわち、 $ kubectl get po<tab>
を打っているとき、裏では _kubectl
内で $ kubectl __complete get po
が呼ばれている。
_kubectl をカスタムコマンドで使う
ここで上述の kubewrapper の補完関数として _kubectl
を設定するとどうなるのか。
_kubectl
はあくまで今打ち込まれているコマンドを打つので、
$ kubewrapper -p staging get po<tab>
と打ったときは
$ kubewrapper __complete -p staging get po
が呼ばれることになる。
ここで kubectl の補完結果を流用するには、 wrapper 独自のオプションを消し、namespace や context flag を付加して kubectl の __complete コマンドを exec する、すなわち kubewrapper が以下を exec すれば良い。
$ kubectl __complete --context foo --namespace bar get po
※ __complete の前に namespace や context をつけると plugin 同様エラーになる
これを実現するには、wrapper の argv で __complete
が 2 つめに来ていたら、$ kubectl __complete
を打とうとしていると解釈すれば良い。wrapper で必須の引数が __complete の後にくることになるので、順序を入れ替えて解釈すると楽。
$ kubewrapper __complete -p staging get po -> wrapper 内部では以下のように解釈する $ kubewrapper -p staging __complete get po -> そうすると wrapper の機能で自然と __complete が打たれる $ kubectl __complete --context foo --namespace bar get po
wrapper のオプション補完
ここまで話した通り kubectl 関係の補完は _kubectl
を通じて wrapper を呼び出して $ kubectl __complete
に変換すればいい感じになるが、 その前に wrapper の必須 flag を埋めてもらう必要がある。
wrapper の必須 flag がなければそれを補完、満たされていれば kubectl の補完を発動させたい。
これは気合でいい感じに補完関数を書くことになる。
How do I check whether a zsh array contains a given value? - Unix & Linux Stack Exchange を参考にしつつ、以下のようにした:
if [[ $words[(Ie)-p] == 0 ]]; then # _arguments に wrapper の補完のみを記載する _arguments ... else # _arguments に kubectl の補完も加える _arguments ... \ '(-)*:kubectl args: _kubectl' fi
(-)*
は kubectl の args が現れた後は全てのハイフンの補完を無視するという意味。
こうすると、最初は wrapper に必須の flag のみが補完され、全て埋まったら wrapper の他の flags とともに kubectl の補完も表示されるようになる。