本文已超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

CSI 驅動程式測試

在開發 容器儲存介面 (CSI) 驅動程式時,盡可能利用先前的成果非常有用。這包括原始碼(例如 範例 CSI hostpath 驅動程式)以及現有的測試。除了節省時間外,使用其他人編寫的測試還有一個優點,它可以指出可能被忽略的規格方面。

一篇關於 端對端測試 的早期部落格文章已經展示了如何使用 Kubernetes 儲存測試 來測試第三方 CSI 驅動程式。當目標是也新增自訂 E2E 測試時,這種方法是有意義的,但需要相當多的努力來設定和維護測試套件。

當目標僅僅是運行現有的測試時,有更簡單的方法。這篇部落格文章介紹了這些方法。

健全性測試

csi-test sanity 透過以各種方式呼叫 gRPC 方法並檢查結果是否符合要求,來確保 CSI 驅動程式符合 CSI 規格。儘管目前託管在 Kubernetes-CSI 組織下,但它完全獨立於 Kubernetes。測試透過其 Unix 網域 socket 連接到正在運行的 CSI 驅動程式,因此,儘管測試是用 Go 語言編寫的,但驅動程式本身可以使用任何語言實作。

主要的 README 解釋了如何將這些測試包含到現有的 Go 測試套件中。更簡單的替代方案是直接調用 csi-sanity 命令。

安裝

從 csi-test v3.0.0 開始,您可以使用 go get github.com/kubernetes-csi/csi-test/cmd/csi-sanity 建構 csi-sanity 命令,您會在 $GOPATH/bin/csi-sanity 中找到編譯後的二進位檔案。

go get 始終從 master 分支建構最新的修訂版本。若要建構特定版本,取得原始碼 並運行 make -C cmd/csi-sanity。這會產生 cmd/csi-sanity/csi-sanity

用法

csi-sanity 二進位檔案是一個完整的 Ginkgo 測試套件,因此具有常用的 -gingko 命令列標誌。特別是,可以使用 -ginkgo.focus-ginkgo.skip 來選擇要運行或不運行的測試。

在測試運行期間,csi-sanity 透過建立 CSI 規格要求的暫存和目標目錄,並透過 gRPC 呼叫 CSI 驅動程式來模擬容器協調器 (CO) 的行為。驅動程式必須在調用 csi-sanity 之前啟動。儘管測試目前僅檢查 gRPC 回傳碼,但情況可能會改變,因此驅動程式確實應該進行呼叫請求的變更,例如掛載檔案系統。這可能意味著它必須以 root 身分運行。

至少必須透過 -csi.endpoint 參數指定一個 gRPC 端點,以調用 csi-sanity,可以是 Unix 網域 socket 的絕對路徑 (unix:/tmp/csi.sock),也可以是 TCP 的主機名稱加上連接埠 (dns:///my-machine:9000)。然後 csi-sanity 將該端點用於節點和控制器操作。可以使用 -csi.controllerendpoint 指定控制器操作的單獨端點。目錄預設在 /tmp 中建立。這可以使用 -csi.mountdir-csi.stagingdir 變更。

某些驅動程式無法部署,以確保所有內容都在同一主機上運行。在這種情況下,必須使用自訂腳本來處理目錄:它們登入 CSI 節點控制器運行的主機,並在那裡建立或移除目錄。

例如,在 CI 測試期間,CSI hostpath 範例驅動程式 在調用 csi-sanity 之前部署在真正的 Kubernetes 叢集上,然後 csi-sanity 透過 socat 提供的連接埠轉發連接到它。腳本 用於建立和移除目錄。

以下是如何使用 CSI hostpath 驅動程式的 v1.2.0 版本重現該操作的方法

$ cd csi-driver-host-path
$ git describe --tags HEAD
v1.2.0
$ kubectl get nodes
NAME        STATUS   ROLES    AGE   VERSION
127.0.0.1   Ready    <none>   42m   v1.16.0

$ deploy/kubernetes-1.16/deploy-hostpath.sh 
applying RBAC rules
kubectl apply -f https://raw.githubusercontent.com/kubernetes-csi/external-provisioner/v1.4.0/deploy/kubernetes/rbac.yaml
...
deploying hostpath components
   deploy/kubernetes-1.16/hostpath/csi-hostpath-attacher.yaml
        using           image: quay.io/k8scsi/csi-attacher:v2.0.0
service/csi-hostpath-attacher created
statefulset.apps/csi-hostpath-attacher created
   deploy/kubernetes-1.16/hostpath/csi-hostpath-driverinfo.yaml
csidriver.storage.k8s.io/hostpath.csi.k8s.io created
   deploy/kubernetes-1.16/hostpath/csi-hostpath-plugin.yaml
        using           image: quay.io/k8scsi/csi-node-driver-registrar:v1.2.0
        using           image: quay.io/k8scsi/hostpathplugin:v1.2.0
        using           image: quay.io/k8scsi/livenessprobe:v1.1.0
...
service/hostpath-service created
statefulset.apps/csi-hostpath-socat created
07:38:46 waiting for hostpath deployment to complete, attempt #0
deploying snapshotclass
volumesnapshotclass.snapshot.storage.k8s.io/csi-hostpath-snapclass created

$ cat >mkdir_in_pod.sh <<EOF
#!/bin/sh
kubectl exec csi-hostpathplugin-0 -c hostpath -- mktemp -d /tmp/csi-sanity.XXXXXX
EOF

$ cat >rmdir_in_pod.sh <<EOF
#!/bin/sh
kubectl exec csi-hostpathplugin-0 -c hostpath -- rmdir "\$@"
EOF

$ chmod u+x *_in_pod.sh
$ csi-sanity -ginkgo.v \
             -csi.endpoint dns:///127.0.0.1:$(kubectl get "services/hostpath-service" -o "jsonpath={..nodePort}") \
             -csi.createstagingpathcmd ./mkdir_in_pod.sh \
             -csi.createmountpathcmd ./mkdir_in_pod.sh \
             -csi.removestagingpathcmd ./rmdir_in_pod.sh \
             -csi.removemountpathcmd ./rmdir_in_pod.sh

Running Suite: CSI Driver Test Suite
====================================
Random Seed: 1570540138
Will run 72 of 72 specs
...
Controller Service [Controller Server] ControllerGetCapabilities 
  should return appropriate capabilities
  /nvme/gopath/src/github.com/kubernetes-csi/csi-test/pkg/sanity/controller.go:111
STEP: connecting to CSI driver
STEP: creating mount and staging directories
STEP: checking successful response
•
------------------------------
Controller Service [Controller Server] GetCapacity 
  should return capacity (no optional values added)
  /nvme/gopath/src/github.com/kubernetes-csi/csi-test/pkg/sanity/controller.go:149
STEP: reusing connection to CSI driver at dns:///127.0.0.1:30056
STEP: creating mount and staging directories
...
Ran 53 of 72 Specs in 148.206 seconds
SUCCESS! -- 53 Passed | 0 Failed | 0 Pending | 19 Skipped
PASS

一些評論

  • 這些測試的原始碼位於 pkg/sanity 套件 中。
  • 如何確定節點的外部 IP 位址取決於叢集。在本範例中,叢集是使用 hack/local-up-cluster.sh 啟動的,因此在本機主機 (127.0.0.1) 上運行。它使用 Kubernetes 分配的連接埠,如上所述使用 kubectl get "services/hostpath-service" 取得。Kubernetes-CSI CI 使用 kind,並且 Docker 命令 可以使用。
  • 建立腳本必須列印最終目錄。為每個測試案例使用唯一的目錄具有以下優點:如果一個測試案例中出現問題,其他測試案例仍然以乾淨的狀態開始。
  • CSI 規格中的「暫存目錄」,又名 NodePublishVolumeRequest.target_path,必須由 CSI 驅動程式建立和刪除,而 CO 負責父目錄。csi-sanity 透過建立目錄,然後在路徑末尾附加 /target 來向 CSI 驅動程式提供該目錄路徑來處理此問題。Kubernetes 在這方面犯了錯,並建立了實際的 target_path 目錄,因此想要與 Kubernetes 協同運作的 CSI 驅動程式目前必須寬容,並且在該目錄已存在時不得失敗。
  • 「掛載目錄」對應於 NodeStageVolumeRequest.staging_target_path,並且確實由 CO(即 csi-sanity)建立。

端對端測試

csi-sanity 相反,端對端測試透過 Kubernetes API 與 CSI 驅動程式互動,也就是說,它模擬來自一般使用者的操作,例如建立 PersistentVolumeClaim。Kubernetes 1.14.0 中 新增 了對測試外部 CSI 驅動程式的支援。

安裝

對於每個 Kubernetes 版本,都會發布測試 tar 封存檔。它未在發行說明中列出(例如,1.16 的發行說明),因此必須知道完整的 URL 是 https://dl.k8s.io/<version>/kubernetes-test-linux-amd64.tar.gz(例如 v1.16.0)。

這些檔案包括適用於 Linux on x86-64 的 e2e.test 二進位檔案。其他平台的封存檔也可用,請參閱 此 KEPe2e.test 二進位檔案是完全獨立的,因此可以使用以下命令「安裝」它和 ginkgo 測試執行器

curl --location https://dl.k8s.io/v1.16.0/kubernetes-test-linux-amd64.tar.gz | \
  tar --strip-components=3 -zxf - kubernetes/test/bin/e2e.test kubernetes/test/bin/ginkgo

每個 e2e.test 二進位檔案都包含與相應版本中可用的功能相符的測試。特別是,[Feature: xyz] 標籤在版本之間會有所不同:它們將 alpha 功能的測試與非 alpha 功能的測試分開。此外,較舊版本的測試可能依賴於在較新 Kubernetes 版本中移除的 API。為避免問題,最好直接使用與用於測試的 Kubernetes 版本相符的 e2e.test 二進位檔案。

用法

並非 CSI 驅動程式的所有功能都可以透過 Kubernetes API 發現。因此,需要 YAML 或 JSON 格式的組態檔,以描述要測試的驅動程式。該檔案用於填充 driverDefinition 結構DriverInfo 結構,後者嵌入在其中。有關個別欄位的詳細使用說明,請參閱這些結構。

警告:測試通常僅在設定某些欄位時運行,並且檔案剖析器不會警告未知的欄位,因此始終檢查檔案是否真的與這些結構相符。

以下範例測試了 csi-driver-host-path

$ cat >test-driver.yaml <<EOF
StorageClass:
  FromName: true
SnapshotClass:
  FromName: true
DriverInfo:
  Name: hostpath.csi.k8s.io
  Capabilities:
    block: true
    controllerExpansion: true
    exec: true
    multipods: true
    persistence: true
    pvcDataSource: true
    snapshotDataSource: true
InlineVolumes:
- Attributes: {}
EOF

您至少需要定義要在測試中使用的儲存類別、驅動程式的名稱以及要測試的功能。與 csi-sanity 一樣,驅動程式必須在測試之前在叢集中運行。然後,實際的 e2e.test 調用使用 -storage.testdriver 為此驅動程式啟用測試,並使用 -ginkgo.focus 為其選擇儲存測試

$ ./e2e.test -ginkgo.v \
             -ginkgo.focus='External.Storage' \
             -storage.testdriver=test-driver.yaml
Oct  8 17:17:42.230: INFO: The --provider flag is not set. Continuing as if --provider=skeleton had been used.
I1008 17:17:42.230210  648569 e2e.go:92] Starting e2e run "90b9adb0-a3a2-435f-80e0-640742d56104" on Ginkgo node 1
Running Suite: Kubernetes e2e suite
===================================
Random Seed: 1570547861 - Will randomize all specs
Will run 163 of 5060 specs

Oct  8 17:17:42.237: INFO: >>> kubeConfig: /var/run/kubernetes/admin.kubeconfig
Oct  8 17:17:42.241: INFO: Waiting up to 30m0s for all (but 0) nodes to be schedulable
...
------------------------------
SSSSSSSSSSSSSSSSSSSS
------------------------------
External Storage [Driver: hostpath.csi.k8s.io] [Testpattern: Dynamic PV (filesystem volmode)] multiVolume [Slow] 
  should access to two volumes with different volume mode and retain data across pod recreation on the same node
  /workspace/anago-v1.16.0-rc.2.1+2bd9643cee5b3b/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/test/e2e/storage/testsuites/multivolume.go:191
[BeforeEach] [Testpattern: Dynamic PV (filesystem volmode)] multiVolume [Slow]
...

您可以使用 ginkgo 平行運行某些種類的測試。Alpha 功能測試或那些設計上必須依序運行的測試,然後需要單獨運行

$ ./ginkgo -p -v \
         -focus='External.Storage' \
         -skip='\[Feature:|\[Disruptive\]|\[Serial\]' \
         ./e2e.test \
         -- \
         -storage.testdriver=test-driver.yaml
$ ./ginkgo -v \
         -focus='External.Storage.*(\[Feature:|\[Disruptive\]|\[Serial\])' \
         ./e2e.test \
         -- \
         -storage.testdriver=test-driver.yaml

參與其中

Kubernetes 儲存測試和健全性測試都旨在適用於任意 CSI 驅動程式。但是,測試可能基於額外的假設,並且您的驅動程式未通過測試,儘管它符合 CSI 規格。如果發生這種情況,請提交問題(連結如下)。

這些是開放原始碼專案,它們依賴於使用它們的人的幫助,因此一旦問題被確認,非常歡迎提交解決該問題的提取請求。

這同樣適用於編寫新測試。以下在問題追蹤器中的搜尋選擇了專門標記為需要他人幫助的問題

測試愉快!願它發現的問題少且易於修復。