KubeAssert是一个kubectl插件,用于在命令行声明针对Kubernetes资源的断言(assertion)。KubeAssert是一个位于GitHub上的开源项目。

作为KubeAssert系列文章的第三篇,本文将向大家介绍,当现有断言无法满足需求的时候,如何通过编写自己的断言来扩展KubeAssert的能力。

自己动手写断言

虽然KubeAssert默认提供的断言可以支持Kubernetes集群日常操作的很多场景,但是,也有可能这些断言并不能满足你的特定需求。KubeAssert支持定制断言,它允许你自己动手编写断言,然后让KubeAssert加载并运行。

作为一个例子,本文我们将编写一个自定义断言,用来验证kubeconfig里是否包含指定的集群。其中,集群的名称是在该断言被执行的时候通过命令行参数传入的。

创建Shell脚本文件

首先,让我们创建一个shell脚本文件,取名为custom-assertions.sh,并将它放到$HOME/.kubeassert目录下。这里是KubeAssert加载所有断言的地方。每当KubeAssert启动时,它会寻找该目录下的所有.sh文件,并作为断言加载到内存。

一个脚本文件可以包含多个断言,每个断言都是通过shell函数来实现的。本例中,我们只有一个断言,对应函数名是cluster,目前函数体还是空的:

#!/bin/bash
function cluster {
 :
}

为了测试断言,我们可以调用kubectl assert命令,并指定函数名cluster,作为该断言的名字:

kubectl assert cluster
ASSERT PASS

正常情况下,我们会看到命令能够正确返回,只是返回结果是空的。接下来,让我们为函数加入具体的实现逻辑。

实现断言的逻辑

为了实现一个断言,通常我们需要做下面这几件事情:

  • 验证输入参数:在本例中,就是由用户通过命令行输入的集群名。如果验证失败,比如用户没有指定集群名,可以调用函数logger::error输出错误信息,然后以非零值立即返回并退出断言。
  • 打印断言信息:通过调用函数logger::assert,我们可以在命令行打印出一条断言信息,以便告诉用户这个断言即将要做的事情。
  • 执行断言逻辑:作为断言的主体部分,此处通常会调用一系列kubectl命令或Kubernetes API,例如:调用kubectl config命令,从kubeconfig中查询集群信息。
  • 验证断言结果:通过对kubectl或Kubernetes API的返回结果进行解析,判断断言成功与否。如果断言失败,则调用函数logger::fail输出一条断言失败的信息。如果成功,则不需要做任何事情,或者也可以根据需要调用logger::info函数打印一些日志信息。

将下列逻辑加入cluster函数:

#!/bin/bash
function cluster {
  # Validate input arguments
  [[ -z $1 ]] && logger::error "You must specify a cluster name." && exit 1
  # Print assertion message
  logger::assert "Cluster with name $1 should be included in kubeconfig."
  # Run some kubectl commands
  kubectl config get-clusters
  # Validate results
  if cat $HOME/.kubeassert/result.txt | grep -q ^$1$; then
    # Print normal logs
    logger::info "Found $1 in kubeconfig."
  else
    # Print failure message
    logger::fail "$1 not found."
  fi
}

可能你已经注意到了,函数中对于断言结果的验证,是通过解析一个位置处于$HOME/.kubeassert/目录下的名为result.txt的文件实现的。KubeAssert把调用kubectl的返回结果都存入了该文件。

现在,让我们试一下执行该断言时不指定集群名的情况:

kubectl assert cluster
ERROR You must specify a cluster name.

结果报错,并提示调用断言时必须指定集群名。再试一下指定一个kubeconfig中不存在的集群名:

kubectl assert cluster kind
ASSERT Cluster with name kind should be included in kubeconfig.
ASSERT FAIL kind not found.

结果显示断言失败,并给出了原因。指定一个kubeconfig中存在的集群名:

kubectl assert cluster kind-foo
ASSERT Cluster with name kind-foo should be included in kubeconfig.
INFO Found kind-foo in kubeconfig.
ASSERT PASS

这次结果显示断言成功了。

如果断言执行过程中遇到了问题,需要进行排查,我们也可以在执行断言时加上-v参数,以打开详细日志输出的开关。这样,我们就可以看到断言中涉及了哪些kubectl调用,以及它们各自的返回结果是什么。

kubectl assert cluster kind-foo -v
ASSERT Cluster with name kind-foo should be included in kubeconfig.
INFO kubectl config get-clusters
NAME
kind-foo
kind-bar
INFO Found kind-foo in kubeconfig.
ASSERT PASS

为断言添加注释

到目前为止,我们已经实现了断言的全部逻辑。不过,还有一件事情我们需要做。当执行kubectl assert并带着-h/--help参数或者不带任何参数时,为了能够让我们的断言出现在已安装断言的列表里,我们还需要为断言添加一个特别的注释。

此外,当我们执行kubectl assert <assertion>命令并带着-h/--help参数时,为了能够打印输出针对该断言的帮助信息,我们还需要提前准备好相应的帮助信息。这些信息也是通过断言注释添加的。

为断言添加的注释必须以##开头,并以##结尾,两者之间包含了若干个字段,每个字段的名字都是以@打头的。下面是一个注释模版,包含了每个字段的详细解释:

##
# @Name: <Input your single-line assertion name here>
# @Description: <Input your single-line assertion description here>
# @Usage: <Input your single-line assertion usage information here>
# @Options:
# <Input help information for all your options started from here>
# <It supports multiple lines>
# @Examples:
# <Input detailed information for all examples started from here>
# <It supports multiple lines>
##

对于Options字段,KubeAssert提供了几个预定义变量可供我们使用。如果我们的断言支持某些KubeAssert预定义的通用Option,那么在定义Options字段时,我们就可以引用这些预定义变量作为占位符。在输出实际的帮助信息时,KubeAssert会把这些占位符替换成真正的内容。

  • ${GLOBAL_OPTIONS}:该变量代表了适用于所有断言的全局Option,比如:用于输出帮助信息的-h/--help
  • ${SELECT_OPTIONS}:该变量代表了用于对资源进行过滤的Option,比如:用于标签过滤的-l/--selector,以及用于指定namespace的-n/--namespace
  • ${OP_VAL_OPTIONS}:该变量代表了比较运算符,例如:-eq-lt-gt-ge-le

通过使用这些变量,我们可以确保所有断言的帮助信息里涉及这些Option的描述都是一致的。

下面是针对cluster断言的注释内容:

##
# @Name: cluster
# @Description: Assert specified cluster included in kubeconfig
# @Usage: kubectl assert cluster (NAME) [options]
# @Options:
# ${GLOBAL_OPTIONS}
# @Examples:
# # To assert a cluster is included in kubeconfig
# kubectl assert cluster kind-foo
##
function cluster {
 ...
}

为了验证,我们可以执行如下命令,看一下我们的断言是否已经出现在已安装断言的列表中了:

kubectl assert --help

如果一切正常,我们会在返回结果中找到cluster。执行如下命令,打印输出断言的帮助信息:

kubectl assert cluster --help

我们会看到如下输出结果,这正是我们在注释里定义的内容:

Assert cluster with specified name included in kubeconfig file
Usage: kubectl assert cluster (NAME) [options]
Options:
  -h, --help: Print the help information.
  -v, --verbose: Enable the verbose log.
  -V, --version: Print the version information.

小结

断言cluster的最终版本如下:

#!/bin/bash
##
# @Name: cluster
# @Description: Assert specified cluster included in kubeconfig
# @Usage: kubectl assert cluster (NAME) [options]
# @Options:
#   ${GLOBAL_OPTIONS}
# @Examples:
#   # To assert a cluster is included in kubeconfig
#   kubectl assert cluster kind-foo
##
function cluster {
  # Validate input arguments
  [[ -z $1 ]] && logger::error "You must specify a cluster name." && exit 1
  # Print assertion message
  logger::assert "Cluster with name $1 should be included in kubeconfig."
  # Run some kubectl commands
  kubectl config get-clusters
  # Validate results
  if cat $HOME/.kubeassert/result.txt | grep -q ^$1$; then
    # Print normal logs
    logger::info "Found $1 in kubeconfig."
  else
    # Print failure message
    logger::fail "$1 not found."
  fi
}

如你所见,为KubeAssert定制断言是非常容易的。在下一篇文章里,我将和大家分享,如何把KubeAssert和KUTTL集成在一起,让针对Kubernetes集群的自动化测试如虎添翼。

如果你想了解更多有关KubeAssert的信息,请阅读它的在线文档。如果你喜欢这个项目,欢迎给它加星。同时,也非常欢迎针对该项目的任何形式的贡献,比如bug报告以及代码提交。

留下评论

您的电子邮箱地址并不会被展示。请填写标记为必须的字段。 *

正在加载...