一篇帶你kubebuilder 進階: 測試
Operator 的測試是一個比較頭疼的問題,在 kubernetes 資源是在不斷變化的,并且想要在測試的時候跑一整套的 kubernetes 環(huán)境也不是一件容易的事情,今天我們大概看一下單元測試和集成測試怎么做。
單元測試
單元測試和 golang 的單元測試沒有什么太大的區(qū)別,一般可以通過單元測試搞定的首先使用單元測試,因為單元測試寫起來最容易,例如下面這一段對節(jié)點標簽更新邏輯進行測試
- func TestNodePoolSpec_ApplyNode(t *testing.T) {
- type fields struct {
- Taints []corev1.Taint
- Labels map[string]string
- Handler string
- }
- type args struct {
- node v1.Node
- }
- tests := []struct {
- name string
- fields fields
- args args
- want *corev1.Node
- }{
- {
- name: "label",
- fields: fields{
- Labels: map[string]string{
- "node-pool.lailin.xyz/test": "",
- },
- },
- args: args{
- node: v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "worker",
- Labels: map[string]string{
- "kubernetes.io/arch": "amd64",
- "a": "b",
- },
- },
- },
- },
- want: &v1.Node{
- ObjectMeta: metav1.ObjectMeta{
- Name: "worker",
- Labels: map[string]string{
- "kubernetes.io/arch": "amd64",
- "node-pool.lailin.xyz/test": "",
- },
- },
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- s := &NodePoolSpec{
- Taints: tt.fields.Taints,
- Labels: tt.fields.Labels,
- Handler: tt.fields.Handler,
- }
- assert.Equal(t, tt.want, s.ApplyNode(tt.args.node))
- })
- }
- }
集成測試
controller-runtime 提供 envtest ,這個包可以幫助你為你在 etcd 和 Kubernetes API server 中設置并啟動的 controllers 實例來寫集成測試,不需要 kubelet,controller-manager 或者其他組件。
envtest
一個 envtest 的簡單例子如下
- import sigs.k8s.io/controller-runtime/pkg/envtest
- //指定 testEnv 配置
- testEnv = &envtest.Environment{
- CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
- }
- //啟動 testEnv
- cfg, err = testEnv.Start()
- //編寫測試邏輯
- //停止 testEnv
- err = testEnv.Stop()
envtest 在啟動的時候需要設置一些環(huán)境變量來說明我們使用什么控制平面來進行測試
- USE_EXISTING_CLUSTER表示使用一個已經存在的控制平面
- KUBEBUILDER_ASSETS 本地控制平面二進制文件的文件夾路徑,里面包含了 kubectl apiserver和 etcd
- KUBEBUILDER_CONTROLPLANE_START_TIMEOUT控制平面啟動的超時時間
- KUBEBUILDER_CONTROLPLANE_STOP_TIMEOUT控制平面停止的超時時間
編寫測試
kubebuilder 在生成代碼的時候已經幫我們生成好了相關的腳手架,已經環(huán)境配置,我們只需要寫具體的測試邏輯就行了
下面我們就以創(chuàng)建一個 NodePool 為例子看看集成測試怎么寫
- controllers/suite_test.go
- var _ = Describe("node labels", func() {
- pool := &nodesv1.NodePool{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test",
- },
- Spec: nodesv1.NodePoolSpec{
- Labels: map[string]string{
- "node-pool.lailin.xyz/xxx": "",
- },
- Handler: "",
- },
- }
- It("create pool", func() {
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
- err := k8sClient.Create(ctx, pool)
- Expect(err).NotTo(HaveOccurred())
- })
- })
使用 make test 執(zhí)行測試
- Using cached envtest tools from blog-code/k8s-operator/07-node-pool-operator/testbin
- setting up env vars
- ? github.com/mohuishou/blog-code/k8s-operator/node-pool-operator [no test files]
- ok github.com/mohuishou/blog-code/k8s-operator/node-pool-operator/api/v1 9.403s coverage: 24.5% of statements
- ok github.com/mohuishou/blog-code/k8s-operator/node-pool-operator/controllers 10.390s coverage: 0.0% of statements
總結
今天這篇文章主要還是希望起一個拋磚引玉的作用,沒有過多的去深入具體改如何寫單元測試和集成測試,只是給了兩個例子,關于集成測試如果感興趣可以看看 https://onsi.github.io/ginkgo 和 envtest 的相關文檔。
對于 Operator 來說建議能寫單元測試的還是寫單元測試,能夠本地寫集成測試的就寫集成測試這樣我們在實際上線的時候就會減少 bug 的概率,因為相對于業(yè)務代碼來說 Operator 的測試實在是比較麻煩,對于測試同學的要求也比較高,一不小心就有可能遺漏一些問題。