diff --git a/pkg/volume/csi/csi_block.go b/pkg/volume/csi/csi_block.go index a1daf21cfe..e27aa4ecb9 100644 --- a/pkg/volume/csi/csi_block.go +++ b/pkg/volume/csi/csi_block.go @@ -105,9 +105,10 @@ func (m *csiBlockMapper) stageVolumeForBlock( klog.Infof(log("blockMapper.stageVolumeForBlock STAGE_UNSTAGE_VOLUME capability not set. Skipping MountDevice...")) return "", nil } - - publishVolumeInfo := attachment.Status.AttachmentMetadata - + publishVolumeInfo := map[string]string{} + if attachment != nil { + publishVolumeInfo = attachment.Status.AttachmentMetadata + } nodeStageSecrets := map[string]string{} if csiSource.NodeStageSecretRef != nil { nodeStageSecrets, err = getCredentialsFromSecret(m.k8s, csiSource.NodeStageSecretRef) @@ -156,7 +157,10 @@ func (m *csiBlockMapper) publishVolumeForBlock( ) (string, error) { klog.V(4).Infof(log("blockMapper.publishVolumeForBlock called")) - publishVolumeInfo := attachment.Status.AttachmentMetadata + publishVolumeInfo := map[string]string{} + if attachment != nil { + publishVolumeInfo = attachment.Status.AttachmentMetadata + } nodePublishSecrets := map[string]string{} var err error @@ -224,18 +228,23 @@ func (m *csiBlockMapper) SetUpDevice() (string, error) { return "", err } - // Search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName - nodeName := string(m.plugin.host.GetNodeName()) - attachID := getAttachmentName(csiSource.VolumeHandle, csiSource.Driver, nodeName) - attachment, err := m.k8s.StorageV1().VolumeAttachments().Get(attachID, meta.GetOptions{}) + driverName := csiSource.Driver + skip, err := m.plugin.skipAttach(driverName) if err != nil { - klog.Error(log("blockMapper.SetupDevice failed to get volume attachment [id=%v]: %v", attachID, err)) + klog.Error(log("blockMapper.SetupDevice failed to check CSIDriver for %s: %v", driverName, err)) return "", err } - if attachment == nil { - klog.Error(log("blockMapper.SetupDevice unable to find VolumeAttachment [id=%s]", attachID)) - return "", errors.New("no existing VolumeAttachment found") + var attachment *storage.VolumeAttachment + if !skip { + // Search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName + nodeName := string(m.plugin.host.GetNodeName()) + attachID := getAttachmentName(csiSource.VolumeHandle, csiSource.Driver, nodeName) + attachment, err = m.k8s.StorageV1().VolumeAttachments().Get(attachID, meta.GetOptions{}) + if err != nil { + klog.Error(log("blockMapper.SetupDevice failed to get volume attachment [id=%v]: %v", attachID, err)) + return "", err + } } //TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI diff --git a/pkg/volume/csi/csi_block_test.go b/pkg/volume/csi/csi_block_test.go index f8090310e5..6d8cf41c0e 100644 --- a/pkg/volume/csi/csi_block_test.go +++ b/pkg/volume/csi/csi_block_test.go @@ -24,6 +24,7 @@ import ( "testing" api "k8s.io/api/core/v1" + "k8s.io/api/storage/v1beta1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" @@ -355,6 +356,88 @@ func TestBlockMapperMapDevice(t *testing.T) { } } +func TestBlockMapperMapDeviceNotSupportAttach(t *testing.T) { + defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)() + defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIDriverRegistry, true)() + plug, tmpDir := newTestPlugin(t, nil) + defer os.RemoveAll(tmpDir) + fakeClient := fakeclient.NewSimpleClientset() + attachRequired := false + fakeDriver := &v1beta1.CSIDriver{ + ObjectMeta: meta.ObjectMeta{ + Name: testDriver, + }, + Spec: v1beta1.CSIDriverSpec{ + AttachRequired: &attachRequired, + }, + } + _, err := plug.host.GetKubeClient().StorageV1beta1().CSIDrivers().Create(fakeDriver) + if err != nil { + t.Fatalf("Failed to create a fakeDriver: %v", err) + } + + host := volumetest.NewFakeVolumeHostWithCSINodeName( + tmpDir, + fakeClient, + nil, + "fakeNode", + nil, + ) + plug.host = host + csiMapper, _, _, err := prepareBlockMapperTest(plug, "test-pv", t) + if err != nil { + t.Fatalf("Failed to make a new Mapper: %v", err) + } + csiMapper.csiClient = setupClient(t, true) + devicePath, err := csiMapper.SetUpDevice() + if err != nil { + t.Fatalf("mapper failed to SetupDevice: %v", err) + } + globalMapPath, err := csiMapper.GetGlobalMapPath(csiMapper.spec) + if err != nil { + t.Fatalf("mapper failed to GetGlobalMapPath: %v", err) + } + + // Actual SetupDevice should create a symlink to or a bind mout of device in devicePath. + // Create dummy file there before calling MapDevice to test it properly. + fd, err := os.Create(devicePath) + if err != nil { + t.Fatalf("mapper failed to create dummy file in devicePath: %v", err) + } + if err := fd.Close(); err != nil { + t.Fatalf("mapper failed to close dummy file in devicePath: %v", err) + } + + // Map device to global and pod device map path + volumeMapPath, volName := csiMapper.GetPodDeviceMapPath() + err = csiMapper.MapDevice(devicePath, globalMapPath, volumeMapPath, volName, csiMapper.podUID) + if err != nil { + t.Fatalf("mapper failed to GetGlobalMapPath: %v", err) + } + + // Check if symlink {globalMapPath}/{podUID} exists + globalMapFilePath := filepath.Join(globalMapPath, string(csiMapper.podUID)) + if _, err := os.Stat(globalMapFilePath); err != nil { + if os.IsNotExist(err) { + t.Errorf("mapper.MapDevice failed, symlink in globalMapPath not created: %v", err) + t.Errorf("mapper.MapDevice devicePath:%v, globalMapPath: %v, globalMapFilePath: %v", + devicePath, globalMapPath, globalMapFilePath) + } else { + t.Errorf("mapper.MapDevice failed: %v", err) + } + } + + // Check if symlink {volumeMapPath}/{volName} exists + volumeMapFilePath := filepath.Join(volumeMapPath, volName) + if _, err := os.Stat(volumeMapFilePath); err != nil { + if os.IsNotExist(err) { + t.Errorf("mapper.MapDevice failed, symlink in volumeMapPath not created: %v", err) + } else { + t.Errorf("mapper.MapDevice failed: %v", err) + } + } +} + func TestBlockMapperTearDownDevice(t *testing.T) { defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIBlockVolume, true)()