12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496 |
- /*
- Copyright 2017 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package csi
- import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "reflect"
- "sync"
- "testing"
- "time"
- storage "k8s.io/api/storage/v1"
- apierrs "k8s.io/apimachinery/pkg/api/errors"
- meta "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apimachinery/pkg/util/wait"
- "k8s.io/apimachinery/pkg/watch"
- utilfeature "k8s.io/apiserver/pkg/util/feature"
- "k8s.io/client-go/informers"
- clientset "k8s.io/client-go/kubernetes"
- fakeclient "k8s.io/client-go/kubernetes/fake"
- core "k8s.io/client-go/testing"
- utiltesting "k8s.io/client-go/util/testing"
- featuregatetesting "k8s.io/component-base/featuregate/testing"
- "k8s.io/kubernetes/pkg/features"
- "k8s.io/kubernetes/pkg/volume"
- volumetest "k8s.io/kubernetes/pkg/volume/testing"
- )
- var (
- bFalse = false
- bTrue = true
- )
- func makeTestAttachment(attachID, nodeName, pvName string) *storage.VolumeAttachment {
- return &storage.VolumeAttachment{
- ObjectMeta: meta.ObjectMeta{
- Name: attachID,
- },
- Spec: storage.VolumeAttachmentSpec{
- NodeName: nodeName,
- Attacher: "mock",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &pvName,
- },
- },
- Status: storage.VolumeAttachmentStatus{
- Attached: false,
- AttachError: nil,
- DetachError: nil,
- },
- }
- }
- func markVolumeAttached(t *testing.T, client clientset.Interface, watch *watch.RaceFreeFakeWatcher, attachID string, status storage.VolumeAttachmentStatus) {
- ticker := time.NewTicker(10 * time.Millisecond)
- var attach *storage.VolumeAttachment
- var err error
- defer ticker.Stop()
- // wait for attachment to be saved
- for i := 0; i < 100; i++ {
- attach, err = client.StorageV1().VolumeAttachments().Get(attachID, meta.GetOptions{})
- if err != nil {
- if apierrs.IsNotFound(err) {
- <-ticker.C
- continue
- }
- t.Error(err)
- }
- if attach != nil {
- t.Logf("attachment found on try %d, stopping wait...", i)
- break
- }
- }
- t.Logf("stopped waiting for attachment")
- if attach == nil {
- t.Logf("attachment not found for id:%v", attachID)
- } else {
- attach.Status = status
- t.Logf("updating attachment %s with attach status %v", attachID, status)
- _, err := client.StorageV1().VolumeAttachments().Update(attach)
- if err != nil {
- t.Error(err)
- }
- watch.Modify(attach)
- }
- }
- func TestAttacherAttach(t *testing.T) {
- testCases := []struct {
- name string
- nodeName string
- driverName string
- volumeName string
- attachID string
- spec *volume.Spec
- injectAttacherError bool
- shouldFail bool
- }{
- {
- name: "test ok 1",
- nodeName: "testnode-01",
- driverName: "testdriver-01",
- volumeName: "testvol-01",
- attachID: getAttachmentName("testvol-01", "testdriver-01", "testnode-01"),
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("pv01", 10, "testdriver-01", "testvol-01"), false),
- },
- {
- name: "test ok 2",
- nodeName: "node02",
- driverName: "driver02",
- volumeName: "vol02",
- attachID: getAttachmentName("vol02", "driver02", "node02"),
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("pv01", 10, "driver02", "vol02"), false),
- },
- {
- name: "mismatch vol",
- nodeName: "node02",
- driverName: "driver02",
- volumeName: "vol01",
- attachID: getAttachmentName("vol02", "driver02", "node02"),
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("pv01", 10, "driver02", "vol01"), false),
- shouldFail: true,
- },
- {
- name: "mismatch driver",
- nodeName: "node02",
- driverName: "driver000",
- volumeName: "vol02",
- attachID: getAttachmentName("vol02", "driver02", "node02"),
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("pv01", 10, "driver01", "vol02"), false),
- shouldFail: true,
- },
- {
- name: "mismatch node",
- nodeName: "node000",
- driverName: "driver000",
- volumeName: "vol02",
- attachID: getAttachmentName("vol02", "driver02", "node02"),
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("pv01", 10, "driver02", "vol02"), false),
- shouldFail: true,
- },
- {
- name: "attacher error",
- nodeName: "node02",
- driverName: "driver02",
- volumeName: "vol02",
- attachID: getAttachmentName("vol02", "driver02", "node02"),
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("pv01", 10, "driver02", "vol02"), false),
- injectAttacherError: true,
- shouldFail: true,
- },
- {
- name: "test with volume source",
- nodeName: "node000",
- driverName: "driver000",
- volumeName: "vol02",
- attachID: getAttachmentName("vol02", "driver02", "node02"),
- spec: volume.NewSpecFromVolume(makeTestVol("pv01", "driver02")),
- shouldFail: true, // csi not enabled
- },
- {
- name: "missing spec",
- nodeName: "node000",
- driverName: "driver000",
- volumeName: "vol02",
- attachID: getAttachmentName("vol02", "driver02", "node02"),
- shouldFail: true, // csi not enabled
- },
- }
- // attacher loop
- for _, tc := range testCases {
- t.Logf("test case: %s", tc.name)
- plug, fakeWatcher, tmpDir, _ := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err := plug.NewAttacher()
- if err != nil {
- t.Fatalf("failed to create new attacher: %v", err)
- }
- csiAttacher := attacher.(*csiAttacher)
- go func(spec *volume.Spec, nodename string, fail bool) {
- attachID, err := csiAttacher.Attach(spec, types.NodeName(nodename))
- if !fail && err != nil {
- t.Errorf("expecting no failure, but got err: %v", err)
- }
- if fail && err == nil {
- t.Errorf("expecting failure, but got no err")
- }
- if attachID != "" {
- t.Errorf("expecting empty attachID, got %v", attachID)
- }
- }(tc.spec, tc.nodeName, tc.shouldFail)
- var status storage.VolumeAttachmentStatus
- if tc.injectAttacherError {
- status.Attached = false
- status.AttachError = &storage.VolumeError{
- Message: "attacher error",
- }
- } else {
- status.Attached = true
- }
- markVolumeAttached(t, csiAttacher.k8s, fakeWatcher, tc.attachID, status)
- }
- }
- func TestAttacherAttachWithInline(t *testing.T) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
- testCases := []struct {
- name string
- nodeName string
- driverName string
- volumeName string
- attachID string
- spec *volume.Spec
- injectAttacherError bool
- shouldFail bool
- }{
- {
- name: "test ok 1 with PV",
- nodeName: "node01",
- attachID: getAttachmentName("vol01", "driver01", "node01"),
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("pv01", 10, "driver01", "vol01"), false),
- },
- {
- name: "test failure, attach with volSrc",
- nodeName: "node01",
- attachID: getAttachmentName("vol01", "driver01", "node01"),
- spec: volume.NewSpecFromVolume(makeTestVol("vol01", "driver01")),
- shouldFail: true,
- },
- {
- name: "attacher error",
- nodeName: "node02",
- attachID: getAttachmentName("vol02", "driver02", "node02"),
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("pv02", 10, "driver02", "vol02"), false),
- injectAttacherError: true,
- shouldFail: true,
- },
- {
- name: "missing spec",
- nodeName: "node02",
- attachID: getAttachmentName("vol02", "driver02", "node02"),
- shouldFail: true,
- },
- }
- // attacher loop
- for _, tc := range testCases {
- t.Logf("test case: %s", tc.name)
- plug, fakeWatcher, tmpDir, _ := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err := plug.NewAttacher()
- if err != nil {
- t.Fatalf("failed to create new attacher: %v", err)
- }
- csiAttacher := attacher.(*csiAttacher)
- go func(spec *volume.Spec, nodename string, fail bool) {
- attachID, err := csiAttacher.Attach(spec, types.NodeName(nodename))
- if fail != (err != nil) {
- t.Errorf("expecting no failure, but got err: %v", err)
- }
- if attachID != "" {
- t.Errorf("expecting empty attachID, got %v", attachID)
- }
- }(tc.spec, tc.nodeName, tc.shouldFail)
- var status storage.VolumeAttachmentStatus
- if tc.injectAttacherError {
- status.Attached = false
- status.AttachError = &storage.VolumeError{
- Message: "attacher error",
- }
- } else {
- status.Attached = true
- }
- markVolumeAttached(t, csiAttacher.k8s, fakeWatcher, tc.attachID, status)
- }
- }
- func TestAttacherWithCSIDriver(t *testing.T) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIDriverRegistry, true)()
- tests := []struct {
- name string
- driver string
- expectVolumeAttachment bool
- }{
- {
- name: "CSIDriver not attachable",
- driver: "not-attachable",
- expectVolumeAttachment: false,
- },
- {
- name: "CSIDriver is attachable",
- driver: "attachable",
- expectVolumeAttachment: true,
- },
- {
- name: "CSIDriver.AttachRequired not set -> failure",
- driver: "nil",
- expectVolumeAttachment: true,
- },
- {
- name: "CSIDriver does not exist not set -> failure",
- driver: "unknown",
- expectVolumeAttachment: true,
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- fakeClient := fakeclient.NewSimpleClientset(
- getTestCSIDriver("not-attachable", nil, &bFalse),
- getTestCSIDriver("attachable", nil, &bTrue),
- getTestCSIDriver("nil", nil, nil),
- )
- plug, fakeWatcher, tmpDir, _ := newTestWatchPlugin(t, fakeClient)
- defer os.RemoveAll(tmpDir)
- attacher, err := plug.NewAttacher()
- if err != nil {
- t.Fatalf("failed to create new attacher: %v", err)
- }
- csiAttacher := attacher.(*csiAttacher)
- spec := volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, test.driver, "test-vol"), false)
- pluginCanAttach, err := plug.CanAttach(spec)
- if err != nil {
- t.Fatalf("attacher.CanAttach failed: %s", err)
- }
- if pluginCanAttach != test.expectVolumeAttachment {
- t.Errorf("attacher.CanAttach does not match expected attachment status %t", test.expectVolumeAttachment)
- }
- if !pluginCanAttach {
- t.Log("plugin is not attachable")
- return
- }
- var wg sync.WaitGroup
- wg.Add(1)
- go func(volSpec *volume.Spec, expectAttach bool) {
- attachID, err := csiAttacher.Attach(volSpec, types.NodeName("node"))
- defer wg.Done()
- if err != nil {
- t.Errorf("Attach() failed: %s", err)
- }
- if attachID != "" {
- t.Errorf("Expected empty attachID, got %q", attachID)
- }
- }(spec, test.expectVolumeAttachment)
- if test.expectVolumeAttachment {
- expectedAttachID := getAttachmentName("test-vol", test.driver, "node")
- status := storage.VolumeAttachmentStatus{
- Attached: true,
- }
- markVolumeAttached(t, csiAttacher.k8s, fakeWatcher, expectedAttachID, status)
- }
- wg.Wait()
- })
- }
- }
- func TestAttacherWaitForVolumeAttachmentWithCSIDriver(t *testing.T) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIDriverRegistry, true)()
- // In order to detect if the volume plugin would skip WaitForAttach for non-attachable drivers,
- // we do not instantiate any VolumeAttachment. So if the plugin does not skip attach, WaitForVolumeAttachment
- // will return an error that volume attachment was not found.
- tests := []struct {
- name string
- driver string
- expectError bool
- }{
- {
- name: "CSIDriver not attachable -> success",
- driver: "not-attachable",
- expectError: false,
- },
- {
- name: "CSIDriver is attachable -> failure",
- driver: "attachable",
- expectError: true,
- },
- {
- name: "CSIDriver.AttachRequired not set -> failure",
- driver: "nil",
- expectError: true,
- },
- {
- name: "CSIDriver does not exist not set -> failure",
- driver: "unknown",
- expectError: true,
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- fakeClient := fakeclient.NewSimpleClientset(
- getTestCSIDriver("not-attachable", nil, &bFalse),
- getTestCSIDriver("attachable", nil, &bTrue),
- getTestCSIDriver("nil", nil, nil),
- )
- plug, tmpDir := newTestPlugin(t, fakeClient)
- defer os.RemoveAll(tmpDir)
- attacher, err := plug.NewAttacher()
- if err != nil {
- t.Fatalf("failed to create new attacher: %v", err)
- }
- csiAttacher := attacher.(*csiAttacher)
- spec := volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, test.driver, "test-vol"), false)
- pluginCanAttach, err := plug.CanAttach(spec)
- if err != nil {
- t.Fatalf("plugin.CanAttach test failed: %s", err)
- }
- if !pluginCanAttach {
- t.Log("plugin is not attachable")
- return
- }
- _, err = csiAttacher.WaitForAttach(spec, "", nil, time.Second)
- if err != nil && !test.expectError {
- t.Errorf("Unexpected error: %s", err)
- }
- if err == nil && test.expectError {
- t.Errorf("Expected error, got none")
- }
- })
- }
- }
- func TestAttacherWaitForAttach(t *testing.T) {
- tests := []struct {
- name string
- driver string
- makeAttachment func() *storage.VolumeAttachment
- spec *volume.Spec
- expectedAttachID string
- expectError bool
- }{
- {
- name: "successful attach",
- driver: "attachable",
- makeAttachment: func() *storage.VolumeAttachment {
- testAttachID := getAttachmentName("test-vol", "attachable", "node")
- successfulAttachment := makeTestAttachment(testAttachID, "node", "test-pv")
- successfulAttachment.Status.Attached = true
- return successfulAttachment
- },
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "attachable", "test-vol"), false),
- expectedAttachID: getAttachmentName("test-vol", "attachable", "node"),
- expectError: false,
- },
- {
- name: "failed attach with vol source",
- makeAttachment: func() *storage.VolumeAttachment {
- testAttachID := getAttachmentName("test-vol", "attachable", "node")
- successfulAttachment := makeTestAttachment(testAttachID, "node", "volSrc01")
- successfulAttachment.Status.Attached = true
- return successfulAttachment
- },
- spec: volume.NewSpecFromVolume(makeTestVol("volSrc01", "attachable")),
- expectError: true,
- },
- {
- name: "failed attach",
- driver: "attachable",
- expectError: true,
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- plug, _, tmpDir, _ := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err := plug.NewAttacher()
- if err != nil {
- t.Fatalf("failed to create new attacher: %v", err)
- }
- csiAttacher := attacher.(*csiAttacher)
- if test.makeAttachment != nil {
- attachment := test.makeAttachment()
- _, err = csiAttacher.k8s.StorageV1().VolumeAttachments().Create(attachment)
- if err != nil {
- t.Fatalf("failed to create VolumeAttachment: %v", err)
- }
- gotAttachment, err := csiAttacher.k8s.StorageV1().VolumeAttachments().Get(attachment.Name, meta.GetOptions{})
- if err != nil {
- t.Fatalf("failed to get created VolumeAttachment: %v", err)
- }
- t.Logf("created test VolumeAttachment %+v", gotAttachment)
- }
- attachID, err := csiAttacher.WaitForAttach(test.spec, "", nil, time.Second)
- if err != nil && !test.expectError {
- t.Errorf("Unexpected error: %s", err)
- }
- if err == nil && test.expectError {
- t.Errorf("Expected error, got none")
- }
- if attachID != test.expectedAttachID {
- t.Errorf("Expected attachID %q, got %q", test.expectedAttachID, attachID)
- }
- })
- }
- }
- func TestAttacherWaitForAttachWithInline(t *testing.T) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
- tests := []struct {
- name string
- driver string
- makeAttachment func() *storage.VolumeAttachment
- spec *volume.Spec
- expectedAttachID string
- expectError bool
- }{
- {
- name: "successful attach with PV",
- makeAttachment: func() *storage.VolumeAttachment {
- testAttachID := getAttachmentName("test-vol", "attachable", "node")
- successfulAttachment := makeTestAttachment(testAttachID, "node", "test-pv")
- successfulAttachment.Status.Attached = true
- return successfulAttachment
- },
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "attachable", "test-vol"), false),
- expectedAttachID: getAttachmentName("test-vol", "attachable", "node"),
- expectError: false,
- },
- {
- name: "failed attach with volSrc",
- makeAttachment: func() *storage.VolumeAttachment {
- testAttachID := getAttachmentName("test-vol", "attachable", "node")
- successfulAttachment := makeTestAttachment(testAttachID, "node", "volSrc01")
- successfulAttachment.Status.Attached = true
- return successfulAttachment
- },
- spec: volume.NewSpecFromVolume(makeTestVol("volSrc01", "attachable")),
- expectError: true,
- },
- {
- name: "failed attach",
- driver: "non-attachable",
- spec: volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "non-attachable", "test-vol"), false),
- expectError: true,
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- plug, _, tmpDir, _ := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err := plug.NewAttacher()
- if err != nil {
- t.Fatalf("failed to create new attacher: %v", err)
- }
- csiAttacher := attacher.(*csiAttacher)
- if test.makeAttachment != nil {
- attachment := test.makeAttachment()
- _, err = csiAttacher.k8s.StorageV1().VolumeAttachments().Create(attachment)
- if err != nil {
- t.Fatalf("failed to create VolumeAttachment: %v", err)
- }
- gotAttachment, err := csiAttacher.k8s.StorageV1().VolumeAttachments().Get(attachment.Name, meta.GetOptions{})
- if err != nil {
- t.Fatalf("failed to get created VolumeAttachment: %v", err)
- }
- t.Logf("created test VolumeAttachment %+v", gotAttachment)
- }
- attachID, err := csiAttacher.WaitForAttach(test.spec, "", nil, time.Second)
- if test.expectError != (err != nil) {
- t.Errorf("Unexpected error: %s", err)
- return
- }
- if attachID != test.expectedAttachID {
- t.Errorf("Expected attachID %q, got %q", test.expectedAttachID, attachID)
- }
- })
- }
- }
- func TestAttacherWaitForVolumeAttachment(t *testing.T) {
- nodeName := "test-node"
- testCases := []struct {
- name string
- initAttached bool
- finalAttached bool
- trigerWatchEventTime time.Duration
- initAttachErr *storage.VolumeError
- finalAttachErr *storage.VolumeError
- timeout time.Duration
- shouldFail bool
- }{
- {
- name: "attach success at get",
- initAttached: true,
- timeout: 50 * time.Millisecond,
- shouldFail: false,
- },
- {
- name: "attachment error ant get",
- initAttachErr: &storage.VolumeError{Message: "missing volume"},
- timeout: 30 * time.Millisecond,
- shouldFail: true,
- },
- {
- name: "attach success at watch",
- initAttached: false,
- finalAttached: true,
- trigerWatchEventTime: 5 * time.Millisecond,
- timeout: 50 * time.Millisecond,
- shouldFail: false,
- },
- {
- name: "attachment error ant watch",
- initAttached: false,
- finalAttached: false,
- finalAttachErr: &storage.VolumeError{Message: "missing volume"},
- trigerWatchEventTime: 5 * time.Millisecond,
- timeout: 30 * time.Millisecond,
- shouldFail: true,
- },
- {
- name: "time ran out",
- initAttached: false,
- finalAttached: true,
- trigerWatchEventTime: 100 * time.Millisecond,
- timeout: 50 * time.Millisecond,
- shouldFail: true,
- },
- }
- for i, tc := range testCases {
- plug, fakeWatcher, tmpDir, _ := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err := plug.NewAttacher()
- if err != nil {
- t.Fatalf("failed to create new attacher: %v", err)
- }
- csiAttacher := attacher.(*csiAttacher)
- t.Logf("running test: %v", tc.name)
- pvName := fmt.Sprintf("test-pv-%d", i)
- volID := fmt.Sprintf("test-vol-%d", i)
- attachID := getAttachmentName(volID, testDriver, nodeName)
- attachment := makeTestAttachment(attachID, nodeName, pvName)
- attachment.Status.Attached = tc.initAttached
- attachment.Status.AttachError = tc.initAttachErr
- _, err = csiAttacher.k8s.StorageV1().VolumeAttachments().Create(attachment)
- if err != nil {
- t.Fatalf("failed to attach: %v", err)
- }
- trigerWatchEventTime := tc.trigerWatchEventTime
- finalAttached := tc.finalAttached
- finalAttachErr := tc.finalAttachErr
- // after timeout, fakeWatcher will be closed by csiAttacher.waitForVolumeAttachment
- if tc.trigerWatchEventTime > 0 && tc.trigerWatchEventTime < tc.timeout {
- go func() {
- time.Sleep(trigerWatchEventTime)
- attachment := makeTestAttachment(attachID, nodeName, pvName)
- attachment.Status.Attached = finalAttached
- attachment.Status.AttachError = finalAttachErr
- fakeWatcher.Modify(attachment)
- }()
- }
- retID, err := csiAttacher.waitForVolumeAttachment(volID, attachID, tc.timeout)
- if tc.shouldFail && err == nil {
- t.Error("expecting failure, but err is nil")
- }
- if tc.initAttachErr != nil {
- if tc.initAttachErr.Message != err.Error() {
- t.Errorf("expecting error [%v], got [%v]", tc.initAttachErr.Message, err.Error())
- }
- }
- if err == nil && retID != attachID {
- t.Errorf("attacher.WaitForAttach not returning attachment ID")
- }
- }
- }
- func TestAttacherVolumesAreAttached(t *testing.T) {
- type attachedSpec struct {
- volName string
- spec *volume.Spec
- attached bool
- }
- testCases := []struct {
- name string
- attachedSpecs []attachedSpec
- }{
- {
- "attach and detach",
- []attachedSpec{
- {"vol0", volume.NewSpecFromPersistentVolume(makeTestPV("pv0", 10, testDriver, "vol0"), false), true},
- {"vol1", volume.NewSpecFromPersistentVolume(makeTestPV("pv1", 20, testDriver, "vol1"), false), true},
- {"vol2", volume.NewSpecFromPersistentVolume(makeTestPV("pv2", 10, testDriver, "vol2"), false), false},
- {"vol3", volume.NewSpecFromPersistentVolume(makeTestPV("pv3", 10, testDriver, "vol3"), false), false},
- {"vol4", volume.NewSpecFromPersistentVolume(makeTestPV("pv4", 20, testDriver, "vol4"), false), true},
- },
- },
- {
- "all detached",
- []attachedSpec{
- {"vol0", volume.NewSpecFromPersistentVolume(makeTestPV("pv0", 10, testDriver, "vol0"), false), false},
- {"vol1", volume.NewSpecFromPersistentVolume(makeTestPV("pv1", 20, testDriver, "vol1"), false), false},
- {"vol2", volume.NewSpecFromPersistentVolume(makeTestPV("pv2", 10, testDriver, "vol2"), false), false},
- },
- },
- {
- "all attached",
- []attachedSpec{
- {"vol0", volume.NewSpecFromPersistentVolume(makeTestPV("pv0", 10, testDriver, "vol0"), false), true},
- {"vol1", volume.NewSpecFromPersistentVolume(makeTestPV("pv1", 20, testDriver, "vol1"), false), true},
- },
- },
- {
- "include non-attable",
- []attachedSpec{
- {"vol0", volume.NewSpecFromPersistentVolume(makeTestPV("pv0", 10, testDriver, "vol0"), false), true},
- {"vol1", volume.NewSpecFromVolume(makeTestVol("pv1", testDriver)), false},
- },
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- plug, tmpDir := newTestPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err := plug.NewAttacher()
- if err != nil {
- t.Fatalf("failed to create new attacher: %v", err)
- }
- csiAttacher := attacher.(*csiAttacher)
- nodeName := "test-node"
- var specs []*volume.Spec
- // create and save volume attchments
- for _, attachedSpec := range tc.attachedSpecs {
- specs = append(specs, attachedSpec.spec)
- attachID := getAttachmentName(attachedSpec.volName, testDriver, nodeName)
- attachment := makeTestAttachment(attachID, nodeName, attachedSpec.spec.Name())
- attachment.Status.Attached = attachedSpec.attached
- _, err := csiAttacher.k8s.StorageV1().VolumeAttachments().Create(attachment)
- if err != nil {
- t.Fatalf("failed to attach: %v", err)
- }
- }
- // retrieve attached status
- stats, err := csiAttacher.VolumesAreAttached(specs, types.NodeName(nodeName))
- if err != nil {
- t.Fatal(err)
- }
- if len(tc.attachedSpecs) != len(stats) {
- t.Errorf("expecting %d attachment status, got %d", len(tc.attachedSpecs), len(stats))
- }
- // compare attachment status for each spec
- for _, attached := range tc.attachedSpecs {
- stat, ok := stats[attached.spec]
- if attached.attached && !ok {
- t.Error("failed to retrieve attached status for:", attached.spec)
- }
- if attached.attached != stat {
- t.Errorf("expecting volume attachment %t, got %t", attached.attached, stat)
- }
- }
- })
- }
- }
- func TestAttacherVolumesAreAttachedWithInline(t *testing.T) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
- type attachedSpec struct {
- volName string
- spec *volume.Spec
- attached bool
- }
- testCases := []struct {
- name string
- attachedSpecs []attachedSpec
- }{
- {
- "attach and detach with volume sources",
- []attachedSpec{
- {"vol0", volume.NewSpecFromPersistentVolume(makeTestPV("pv0", 10, testDriver, "vol0"), false), true},
- {"vol1", volume.NewSpecFromVolume(makeTestVol("pv1", testDriver)), false},
- {"vol2", volume.NewSpecFromPersistentVolume(makeTestPV("pv2", 10, testDriver, "vol2"), false), true},
- {"vol3", volume.NewSpecFromVolume(makeTestVol("pv3", testDriver)), false},
- {"vol4", volume.NewSpecFromPersistentVolume(makeTestPV("pv4", 20, testDriver, "vol4"), false), true},
- },
- },
- }
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- plug, tmpDir := newTestPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err := plug.NewAttacher()
- if err != nil {
- t.Fatalf("failed to create new attacher: %v", err)
- }
- csiAttacher := attacher.(*csiAttacher)
- nodeName := "test-node"
- var specs []*volume.Spec
- // create and save volume attchments
- for _, attachedSpec := range tc.attachedSpecs {
- specs = append(specs, attachedSpec.spec)
- attachID := getAttachmentName(attachedSpec.volName, testDriver, nodeName)
- attachment := makeTestAttachment(attachID, nodeName, attachedSpec.spec.Name())
- attachment.Status.Attached = attachedSpec.attached
- _, err := csiAttacher.k8s.StorageV1().VolumeAttachments().Create(attachment)
- if err != nil {
- t.Fatalf("failed to attach: %v", err)
- }
- }
- // retrieve attached status
- stats, err := csiAttacher.VolumesAreAttached(specs, types.NodeName(nodeName))
- if err != nil {
- t.Fatal(err)
- }
- if len(tc.attachedSpecs) != len(stats) {
- t.Errorf("expecting %d attachment status, got %d", len(tc.attachedSpecs), len(stats))
- }
- // compare attachment status for each spec
- for _, attached := range tc.attachedSpecs {
- stat, ok := stats[attached.spec]
- if attached.attached && !ok {
- t.Error("failed to retrieve attached status for:", attached.spec)
- }
- if attached.attached != stat {
- t.Errorf("expecting volume attachment %t, got %t", attached.attached, stat)
- }
- }
- })
- }
- }
- func TestAttacherDetach(t *testing.T) {
- nodeName := "test-node"
- testCases := []struct {
- name string
- volID string
- attachID string
- shouldFail bool
- reactor func(action core.Action) (handled bool, ret runtime.Object, err error)
- }{
- {name: "normal test", volID: "vol-001", attachID: getAttachmentName("vol-001", testDriver, nodeName)},
- {name: "normal test 2", volID: "vol-002", attachID: getAttachmentName("vol-002", testDriver, nodeName)},
- {name: "object not found", volID: "vol-non-existing", attachID: getAttachmentName("vol-003", testDriver, nodeName)},
- {
- name: "API error",
- volID: "vol-004",
- attachID: getAttachmentName("vol-004", testDriver, nodeName),
- shouldFail: true, // All other API errors should be propagated to caller
- reactor: func(action core.Action) (handled bool, ret runtime.Object, err error) {
- // return Forbidden to all DELETE requests
- if action.Matches("delete", "volumeattachments") {
- return true, nil, apierrs.NewForbidden(action.GetResource().GroupResource(), action.GetNamespace(), fmt.Errorf("mock error"))
- }
- return false, nil, nil
- },
- },
- }
- for _, tc := range testCases {
- t.Logf("running test: %v", tc.name)
- plug, fakeWatcher, tmpDir, client := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- if tc.reactor != nil {
- client.PrependReactor("*", "*", tc.reactor)
- }
- attacher, err0 := plug.NewAttacher()
- if err0 != nil {
- t.Fatalf("failed to create new attacher: %v", err0)
- }
- csiAttacher := attacher.(*csiAttacher)
- pv := makeTestPV("test-pv", 10, testDriver, tc.volID)
- spec := volume.NewSpecFromPersistentVolume(pv, pv.Spec.PersistentVolumeSource.CSI.ReadOnly)
- attachment := makeTestAttachment(tc.attachID, nodeName, "test-pv")
- _, err := csiAttacher.k8s.StorageV1().VolumeAttachments().Create(attachment)
- if err != nil {
- t.Fatalf("failed to attach: %v", err)
- }
- volumeName, err := plug.GetVolumeName(spec)
- if err != nil {
- t.Errorf("test case %s failed: %v", tc.name, err)
- }
- go func() {
- fakeWatcher.Delete(attachment)
- }()
- err = csiAttacher.Detach(volumeName, types.NodeName(nodeName))
- if tc.shouldFail && err == nil {
- t.Fatal("expecting failure, but err = nil")
- }
- if !tc.shouldFail && err != nil {
- t.Fatalf("unexpected err: %v", err)
- }
- attach, err := csiAttacher.k8s.StorageV1().VolumeAttachments().Get(tc.attachID, meta.GetOptions{})
- if err != nil {
- if !apierrs.IsNotFound(err) {
- t.Fatalf("unexpected err: %v", err)
- }
- } else {
- if attach == nil {
- t.Errorf("expecting attachment not to be nil, but it is")
- }
- }
- }
- }
- func TestAttacherGetDeviceMountPath(t *testing.T) {
- // Setup
- // Create a new attacher
- plug, _, tmpDir, _ := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err0 := plug.NewAttacher()
- if err0 != nil {
- t.Fatalf("failed to create new attacher: %v", err0)
- }
- csiAttacher := attacher.(*csiAttacher)
- pluginDir := csiAttacher.plugin.host.GetPluginDir(plug.GetPluginName())
- testCases := []struct {
- testName string
- pvName string
- expectedMountPath string
- shouldFail bool
- }{
- {
- testName: "normal test",
- pvName: "test-pv1",
- expectedMountPath: pluginDir + "/pv/test-pv1/globalmount",
- },
- {
- testName: "no pv name",
- pvName: "",
- expectedMountPath: pluginDir + "/pv/test-pv1/globalmount",
- shouldFail: true,
- },
- }
- for _, tc := range testCases {
- t.Logf("Running test case: %s", tc.testName)
- var spec *volume.Spec
- // Create spec
- pv := makeTestPV(tc.pvName, 10, testDriver, "testvol")
- spec = volume.NewSpecFromPersistentVolume(pv, pv.Spec.PersistentVolumeSource.CSI.ReadOnly)
- // Run
- mountPath, err := csiAttacher.GetDeviceMountPath(spec)
- // Verify
- if err != nil && !tc.shouldFail {
- t.Errorf("test should not fail, but error occurred: %v", err)
- } else if err == nil {
- if tc.shouldFail {
- t.Errorf("test should fail, but no error occurred")
- } else if mountPath != tc.expectedMountPath {
- t.Errorf("mountPath does not equal expectedMountPath. Got: %s. Expected: %s", mountPath, tc.expectedMountPath)
- }
- }
- }
- }
- func TestAttacherMountDevice(t *testing.T) {
- pvName := "test-pv"
- testCases := []struct {
- testName string
- volName string
- devicePath string
- deviceMountPath string
- stageUnstageSet bool
- shouldFail bool
- spec *volume.Spec
- }{
- {
- testName: "normal PV",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "path2",
- stageUnstageSet: true,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, "test-vol1"), false),
- },
- {
- testName: "normal PV with mount options",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "path2",
- stageUnstageSet: true,
- spec: volume.NewSpecFromPersistentVolume(makeTestPVWithMountOptions(pvName, 10, testDriver, "test-vol1", []string{"test-op"}), false),
- },
- {
- testName: "no vol name",
- volName: "",
- devicePath: "path1",
- deviceMountPath: "path2",
- stageUnstageSet: true,
- shouldFail: true,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, ""), false),
- },
- {
- testName: "no device path",
- volName: "test-vol1",
- devicePath: "",
- deviceMountPath: "path2",
- stageUnstageSet: true,
- shouldFail: false,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, "test-vol1"), false),
- },
- {
- testName: "no device mount path",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "",
- stageUnstageSet: true,
- shouldFail: true,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, "test-vol1"), false),
- },
- {
- testName: "stage_unstage cap not set",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "path2",
- stageUnstageSet: false,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, "test-vol1"), false),
- },
- {
- testName: "failure with volume source",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "path2",
- shouldFail: true,
- spec: volume.NewSpecFromVolume(makeTestVol(pvName, testDriver)),
- },
- }
- for _, tc := range testCases {
- t.Logf("Running test case: %s", tc.testName)
- // Setup
- // Create a new attacher
- plug, fakeWatcher, tmpDir, _ := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err0 := plug.NewAttacher()
- if err0 != nil {
- t.Fatalf("failed to create new attacher: %v", err0)
- }
- csiAttacher := attacher.(*csiAttacher)
- csiAttacher.csiClient = setupClient(t, tc.stageUnstageSet)
- if tc.deviceMountPath != "" {
- tc.deviceMountPath = filepath.Join(tmpDir, tc.deviceMountPath)
- }
- nodeName := string(csiAttacher.plugin.host.GetNodeName())
- attachID := getAttachmentName(tc.volName, testDriver, nodeName)
- // Set up volume attachment
- attachment := makeTestAttachment(attachID, nodeName, pvName)
- _, err := csiAttacher.k8s.StorageV1().VolumeAttachments().Create(attachment)
- if err != nil {
- t.Fatalf("failed to attach: %v", err)
- }
- go func() {
- fakeWatcher.Delete(attachment)
- }()
- // Run
- err = csiAttacher.MountDevice(tc.spec, tc.devicePath, tc.deviceMountPath)
- // Verify
- if err != nil {
- if !tc.shouldFail {
- t.Errorf("test should not fail, but error occurred: %v", err)
- }
- continue
- }
- if err == nil && tc.shouldFail {
- t.Errorf("test should fail, but no error occurred")
- }
- // Verify call goes through all the way
- numStaged := 1
- if !tc.stageUnstageSet {
- numStaged = 0
- }
- cdc := csiAttacher.csiClient.(*fakeCsiDriverClient)
- staged := cdc.nodeClient.GetNodeStagedVolumes()
- if len(staged) != numStaged {
- t.Errorf("got wrong number of staged volumes, expecting %v got: %v", numStaged, len(staged))
- }
- if tc.stageUnstageSet {
- vol, ok := staged[tc.volName]
- if !ok {
- t.Errorf("could not find staged volume: %s", tc.volName)
- }
- if vol.Path != tc.deviceMountPath {
- t.Errorf("expected mount path: %s. got: %s", tc.deviceMountPath, vol.Path)
- }
- if !reflect.DeepEqual(vol.MountFlags, tc.spec.PersistentVolume.Spec.MountOptions) {
- t.Errorf("expected mount options: %v, got: %v", tc.spec.PersistentVolume.Spec.MountOptions, vol.MountFlags)
- }
- }
- }
- }
- func TestAttacherMountDeviceWithInline(t *testing.T) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
- pvName := "test-pv"
- testCases := []struct {
- testName string
- volName string
- devicePath string
- deviceMountPath string
- stageUnstageSet bool
- shouldFail bool
- spec *volume.Spec
- }{
- {
- testName: "normal PV",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "path2",
- stageUnstageSet: true,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, "test-vol1"), false),
- },
- {
- testName: "failure with volSrc",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "path2",
- shouldFail: true,
- spec: volume.NewSpecFromVolume(makeTestVol(pvName, testDriver)),
- },
- {
- testName: "no vol name",
- volName: "",
- devicePath: "path1",
- deviceMountPath: "path2",
- stageUnstageSet: true,
- shouldFail: true,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, ""), false),
- },
- {
- testName: "no device path",
- volName: "test-vol1",
- devicePath: "",
- deviceMountPath: "path2",
- stageUnstageSet: true,
- shouldFail: false,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, "test-vol1"), false),
- },
- {
- testName: "no device mount path",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "",
- stageUnstageSet: true,
- shouldFail: true,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, "test-vol1"), false),
- },
- {
- testName: "stage_unstage cap not set",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "path2",
- stageUnstageSet: false,
- spec: volume.NewSpecFromPersistentVolume(makeTestPV(pvName, 10, testDriver, "test-vol1"), false),
- },
- {
- testName: "missing spec",
- volName: "test-vol1",
- devicePath: "path1",
- deviceMountPath: "path2",
- shouldFail: true,
- },
- }
- for _, tc := range testCases {
- t.Logf("Running test case: %s", tc.testName)
- // Setup
- // Create a new attacher
- plug, fakeWatcher, tmpDir, _ := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err0 := plug.NewAttacher()
- if err0 != nil {
- t.Fatalf("failed to create new attacher: %v", err0)
- }
- csiAttacher := attacher.(*csiAttacher)
- csiAttacher.csiClient = setupClient(t, tc.stageUnstageSet)
- if tc.deviceMountPath != "" {
- tc.deviceMountPath = filepath.Join(tmpDir, tc.deviceMountPath)
- }
- nodeName := string(csiAttacher.plugin.host.GetNodeName())
- attachID := getAttachmentName(tc.volName, testDriver, nodeName)
- // Set up volume attachment
- attachment := makeTestAttachment(attachID, nodeName, pvName)
- _, err := csiAttacher.k8s.StorageV1().VolumeAttachments().Create(attachment)
- if err != nil {
- t.Fatalf("failed to attach: %v", err)
- }
- go func() {
- fakeWatcher.Delete(attachment)
- }()
- // Run
- err = csiAttacher.MountDevice(tc.spec, tc.devicePath, tc.deviceMountPath)
- // Verify
- if err != nil {
- if !tc.shouldFail {
- t.Errorf("test should not fail, but error occurred: %v", err)
- }
- continue
- }
- if err == nil && tc.shouldFail {
- t.Errorf("test should fail, but no error occurred")
- }
- // Verify call goes through all the way
- numStaged := 1
- if !tc.stageUnstageSet {
- numStaged = 0
- }
- cdc := csiAttacher.csiClient.(*fakeCsiDriverClient)
- staged := cdc.nodeClient.GetNodeStagedVolumes()
- if len(staged) != numStaged {
- t.Errorf("got wrong number of staged volumes, expecting %v got: %v", numStaged, len(staged))
- }
- if tc.stageUnstageSet {
- vol, ok := staged[tc.volName]
- if !ok {
- t.Errorf("could not find staged volume: %s", tc.volName)
- }
- if vol.Path != tc.deviceMountPath {
- t.Errorf("expected mount path: %s. got: %s", tc.deviceMountPath, vol.Path)
- }
- }
- }
- }
- func TestAttacherUnmountDevice(t *testing.T) {
- testCases := []struct {
- testName string
- volID string
- deviceMountPath string
- jsonFile string
- createPV bool
- stageUnstageSet bool
- shouldFail bool
- }{
- {
- testName: "normal, json file exists",
- volID: "project/zone/test-vol1",
- deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
- jsonFile: `{"driverName": "csi", "volumeHandle":"project/zone/test-vol1"}`,
- createPV: false,
- stageUnstageSet: true,
- },
- {
- testName: "normal, json file doesn't exist -> use PV",
- volID: "project/zone/test-vol1",
- deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
- jsonFile: "",
- createPV: true,
- stageUnstageSet: true,
- },
- {
- testName: "invalid json -> use PV",
- volID: "project/zone/test-vol1",
- deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
- jsonFile: `{"driverName"}}`,
- createPV: true,
- stageUnstageSet: true,
- },
- {
- testName: "no json, no PV.volID",
- volID: "",
- deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
- jsonFile: "",
- createPV: true,
- shouldFail: true,
- },
- {
- testName: "no json, no PV",
- volID: "project/zone/test-vol1",
- deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
- jsonFile: "",
- createPV: false,
- stageUnstageSet: true,
- shouldFail: true,
- },
- {
- testName: "stage_unstage not set no vars should not fail",
- deviceMountPath: "plugins/csi/pv/test-pv-name/globalmount",
- jsonFile: `{"driverName":"test-driver","volumeHandle":"test-vol1"}`,
- stageUnstageSet: false,
- },
- }
- for _, tc := range testCases {
- t.Logf("Running test case: %s", tc.testName)
- // Setup
- // Create a new attacher
- plug, _, tmpDir, _ := newTestWatchPlugin(t, nil)
- defer os.RemoveAll(tmpDir)
- attacher, err0 := plug.NewAttacher()
- if err0 != nil {
- t.Fatalf("failed to create new attacher: %v", err0)
- }
- csiAttacher := attacher.(*csiAttacher)
- csiAttacher.csiClient = setupClient(t, tc.stageUnstageSet)
- if tc.deviceMountPath != "" {
- tc.deviceMountPath = filepath.Join(tmpDir, tc.deviceMountPath)
- }
- // Add the volume to NodeStagedVolumes
- cdc := csiAttacher.csiClient.(*fakeCsiDriverClient)
- cdc.nodeClient.AddNodeStagedVolume(tc.volID, tc.deviceMountPath, nil)
- // Make JSON for this object
- if tc.deviceMountPath != "" {
- if err := os.MkdirAll(tc.deviceMountPath, 0755); err != nil {
- t.Fatalf("error creating directory %s: %s", tc.deviceMountPath, err)
- }
- }
- dir := filepath.Dir(tc.deviceMountPath)
- if tc.jsonFile != "" {
- dataPath := filepath.Join(dir, volDataFileName)
- if err := ioutil.WriteFile(dataPath, []byte(tc.jsonFile), 0644); err != nil {
- t.Fatalf("error creating %s: %s", dataPath, err)
- }
- }
- if tc.createPV {
- // Make the PV for this object
- pvName := filepath.Base(dir)
- pv := makeTestPV(pvName, 5, "csi", tc.volID)
- _, err := csiAttacher.k8s.CoreV1().PersistentVolumes().Create(pv)
- if err != nil && !tc.shouldFail {
- t.Fatalf("Failed to create PV: %v", err)
- }
- }
- // Run
- err := csiAttacher.UnmountDevice(tc.deviceMountPath)
- // Verify
- if err != nil {
- if !tc.shouldFail {
- t.Errorf("test should not fail, but error occurred: %v", err)
- }
- continue
- }
- if err == nil && tc.shouldFail {
- t.Errorf("test should fail, but no error occurred")
- }
- // Verify call goes through all the way
- expectedSet := 0
- if !tc.stageUnstageSet {
- expectedSet = 1
- }
- staged := cdc.nodeClient.GetNodeStagedVolumes()
- if len(staged) != expectedSet {
- t.Errorf("got wrong number of staged volumes, expecting %v got: %v", expectedSet, len(staged))
- }
- _, ok := staged[tc.volID]
- if ok && tc.stageUnstageSet {
- t.Errorf("found unexpected staged volume: %s", tc.volID)
- } else if !ok && !tc.stageUnstageSet {
- t.Errorf("could not find expected staged volume: %s", tc.volID)
- }
- if tc.jsonFile != "" && !tc.shouldFail {
- dataPath := filepath.Join(dir, volDataFileName)
- if _, err := os.Stat(dataPath); !os.IsNotExist(err) {
- if err != nil {
- t.Errorf("error checking file %s: %s", dataPath, err)
- } else {
- t.Errorf("json file %s should not exists, but it does", dataPath)
- }
- } else {
- t.Logf("json file %s was correctly removed", dataPath)
- }
- }
- }
- }
- // create a plugin mgr to load plugins and setup a fake client
- func newTestWatchPlugin(t *testing.T, fakeClient *fakeclient.Clientset) (*csiPlugin, *watch.RaceFreeFakeWatcher, string, *fakeclient.Clientset) {
- tmpDir, err := utiltesting.MkTmpdir("csi-test")
- if err != nil {
- t.Fatalf("can't create temp dir: %v", err)
- }
- if fakeClient == nil {
- fakeClient = fakeclient.NewSimpleClientset()
- }
- fakeWatcher := watch.NewRaceFreeFake()
- fakeClient.Fake.PrependWatchReactor("volumeattachments", core.DefaultWatchReactor(fakeWatcher, nil))
- // Start informer for CSIDrivers.
- factory := informers.NewSharedInformerFactory(fakeClient, csiResyncPeriod)
- csiDriverInformer := factory.Storage().V1beta1().CSIDrivers()
- csiDriverLister := csiDriverInformer.Lister()
- factory.Start(wait.NeverStop)
- host := volumetest.NewFakeVolumeHostWithCSINodeName(
- tmpDir,
- fakeClient,
- nil,
- "node",
- csiDriverLister,
- )
- plugMgr := &volume.VolumePluginMgr{}
- plugMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
- plug, err := plugMgr.FindPluginByName(CSIPluginName)
- if err != nil {
- t.Fatalf("can't find plugin %v", CSIPluginName)
- }
- csiPlug, ok := plug.(*csiPlugin)
- if !ok {
- t.Fatalf("cannot assert plugin to be type csiPlugin")
- }
- if utilfeature.DefaultFeatureGate.Enabled(features.CSIDriverRegistry) {
- // Wait until the informer in CSI volume plugin has all CSIDrivers.
- wait.PollImmediate(testInformerSyncPeriod, testInformerSyncTimeout, func() (bool, error) {
- return csiDriverInformer.Informer().HasSynced(), nil
- })
- }
- return csiPlug, fakeWatcher, tmpDir, fakeClient
- }
|