# 如何编写一个支持 Krew 的 kubectl 插件

## krew 简介

[Krew](https://github.com/kubernetes-sigs/krew) 是一个用来管理 Kubectl 插件的工具，名字大概来自于 OS X 下著名的软件包管理器 `Homebrew`，使用 Krew 能够方便的查找、安装和使用 Kubectl 插件，例如：

~~~shell
$ kubectl krew search
NAME                            DESCRIPTION                                         INSTALLED
access-matrix                   Show an RBAC access matrix for server resources     no
advise-psp                      Suggests PodSecurityPolicies for cluster.           no
...

$ kubectl krew install tree
Updated the local copy of plugin index.
Installing plugin: tree
...

$  kubectl tree deployment coredns -nkube-system
NAMESPACE    NAME                                READY  REASON  AGE
kube-system  Deployment/coredns                  -              140d
kube-system  └─ReplicaSet/coredns-76d9d9bcc7   -              140d
kube-system    ├─Pod/coredns-76d9d9bcc7-m6d4c  True           4d10h
kube-system    └─Pod/coredns-76d9d9bcc7-zvf9c  True           4d10h
~~~

很方便的几个步骤，就可以查询、安装和使用新插件了。

Krew 除了落在客户端的可执行文件之外，和其它软件包管理系统一样，也同样需要有一个索引系统，并根据索引进行软件查询和下载，下载之后的软件保存在本地，供 kubectl 调用。

### 索引

Krew 的索引保存在一个名为 [krew-index](https://github.com/kubernetes-sigs/krew-index) 的代码库中。其中的 `plugins` 目录保存了一组 yaml 文件，就是插件的目录。

### YAML 清单

随意打开一个清单文件，可以看到这样的内容：

~~~yaml
apiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
  name: access-matrix
spec:
  version: v0.4.4
  platforms:
  - bin: access-matrix
    uri: https://github.com/corneliusweig/rakkess/releases/download/v0.4.4/access-matrix-amd64-linux.tar.gz
    sha256: 53b1ee5865d11360cea3e59b91cdc6707ee30845567e63657782ee11815f1de4
    files:
      - from: ./LICENSE
        to: .
      - from: ./access-matrix-amd64-linux
        to: access-matrix
    selector:
      matchLabels:
        os: linux
        arch: amd64
  shortDescription: Show an RBAC access matrix for server resources
  homepage: https://github.com/corneliusweig/rakkess
  caveats: |
      Usage:
        kubectl access-matrix
  description: ..
~~~

其中 `apiVersion` 和 `kind` 是固定内容。`platforms` 是一个数组，指定不同平台下的不同用法。下一级的 `bin` 表明了执行命令；`uri` 和 `sha256` 分别指的是下载位置以及压缩包的校验码；接下来的 `files` 是一个拷贝命令——从解压后的文件夹中拷贝文件；最后的 `selector` 则是针对不同平台的选择标准。

所以要编写一个能够通过 Krew 进行管理的 kubectl 插件，需要以下几个步骤：

1. 编写插件代码
1. 制作清单和调试
1. 上传到 krew-index

下面用一个实际的例子来说明一下这个过程。

## 编写插件代码

插件代码本身的编写非常简单和随意，可以用你喜欢的任何语言，例如 golang、python 或者 shell。只有一个推荐的命名规则：`kubectl-rm`，在 kubectl 中调用时就可以使用 `kubectl rm` 了。例如我要编写一个对输出 JSON 进行过滤的插件，代码如下：

~~~bash
#!/bin/sh

METADATA=${JSON_METADATA-".metadata.resourceVersion, .metadata.selfLink, .metadata.managedFields, .metadata.generation, .metadata.uid, .metadata.creationTimestamp"}
STATUS=${JSON_STATUS-".status"}
ANNOTATION=${JSON_ANNOTATION-".metadata.annotations.\"kubectl.kubernetes.io/last-applied-configuration\", .metadata.annotations.\"deployment.kubernetes.io/revision\""}
SPEC=${JSON_SPEC-".spec.template.metadata.creationTimestamp, .spec.revisionHistoryLimit, .spec.templateGeneration"}

if ! [ -x "$(command -v jq)" ]; then
  echo 'Error: jq is not installed.' >&2
  exit 1
fi

if [ $# -lt 2 ]
  then
    echo "Usage: $0 [workload-type] [object-name] [other parameters for kubectl]"
    echo "Workload types: 'deployment', 'daemonset', 'configmap', 'statefulset', 'secret'"
    echo "Example: $0 deploy coredns -n kube-system"
    exit 1
fi

TYPE=$1
NAME=$2
OTHER=$*

kubectl get ${OTHER} -ojson | jq -S "del(${METADATA}, ${STATUS}, ${ANNOTATION}, ${SPEC})"
~~~

想法很简单，获取运行中的对象描述，使用 JQ 对数据进行清理和排序，输出一个相对标准的结果，便于不同环境间的比较和部署的导出。

> 虽然最后是通过 `kubectl std-json` 的方式调用，这里的 `$0` 指的仍然是脚本自身。

## 制作清单和测试

照猫画虎，按照上面的 YAML 代码，编写自己的清单。

清单要求，需要打一个压缩包便于下载，我们把可执行文件和 LICENSE 文件放置到单独的目录 `kubectl-std-json-v0.1.0` 中，压缩生成一个 `.tar.gz` 文件，部分清单如下

~~~yaml
    uri: https://github.com/fleeto/kubectl-std-json/releases/download/v0.1.0/kubectl-std-json-v0.1.0.tar.gz
    sha256: e1ad2398eaed5442042da134fb046fa8276042dd4122da4d872a8e91aeb2a339
    bin: kubectl-std-json
    files:
    - from: kubectl-std-json-*/kubectl-std-json
      to: .
    - from: kubectl-std-json-*/LICENSE
      to: .
~~~

平台选择方面，我们只支持 OSX 和 Linux，因此只要一个平台元素即可。

压缩包的校验码可以使用 `shasum -a 256` 命令生成。

上传压缩包之后，可以使用 `kubectl krew install --manifest` 命令来测试安装。如果一切顺利，在本地就可以使用了。

## krew-index

接下来的操作很常规：fork krew-index，把你的清单写入 `plugins` 目录，提交 PR 即可。

## 相关链接

- Krew：`https://github.com/kubernetes-sigs/krew`
- Krew index：`https://github.com/kubernetes-sigs/krew-index`
