Skip to content

Commit db447dd

Browse files
committed
DeviceManager: Return no error if the device not found while deleting
Discussion on issue intel#413, raised a requirement that PMEM-CSI should handle the DeleteVolume() call for volumes that the underlined device already deleted manually by the admin. So for such a volume we just ignore and return no error in DeviceManager.DeleteVolume(). This also makes DeleteVolume() idempotent.
1 parent 1d93db7 commit db447dd

File tree

6 files changed

+58
-12
lines changed

6 files changed

+58
-12
lines changed

pkg/pmem-csi-driver/controllerserver-master.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ func (cs *masterController) DeleteVolume(ctx context.Context, req *csi.DeleteVol
305305
defer conn.Close() // nolint:errcheck
306306
klog.V(4).Infof("Asking node %s to delete volume name:%s id:%s", node, vol.name, vol.id)
307307
if _, err := csi.NewControllerClient(conn).DeleteVolume(ctx, req); err != nil {
308-
return nil, status.Error(codes.Internal, "Failed to delete volume name:"+vol.name+" id:"+vol.id+" on "+node+": "+err.Error())
308+
return nil, err
309309
}
310310
}
311311
cs.mutex.Lock()

pkg/pmem-csi-driver/controllerserver-node.go

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package pmemcsidriver
99
import (
1010
"crypto/sha1"
1111
"encoding/hex"
12+
"errors"
1213
"fmt"
1314
"strconv"
1415
"sync"
@@ -273,6 +274,9 @@ func (cs *nodeControllerServer) DeleteVolume(ctx context.Context, req *csi.Delet
273274
}
274275

275276
if err := cs.dm.DeleteDevice(req.VolumeId, eraseafter); err != nil {
277+
if errors.Is(err, pmdmanager.ErrDeviceInUse) {
278+
return nil, status.Errorf(codes.FailedPrecondition, err.Error())
279+
}
276280
return nil, status.Errorf(codes.Internal, "Failed to delete volume: %s", err.Error())
277281
}
278282
if cs.sm != nil {

pkg/pmem-device-manager/pmd-lvm.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package pmdmanager
22

33
import (
4+
"errors"
45
"fmt"
56
"strconv"
67
"strings"
@@ -148,11 +149,21 @@ func (lvm *pmemLvm) DeleteDevice(volumeId string, flush bool) error {
148149
lvmMutex.Lock()
149150
defer lvmMutex.Unlock()
150151

151-
device, err := lvm.getDevice(volumeId)
152-
if err != nil {
152+
var err error
153+
var device *PmemDeviceInfo
154+
155+
if device, err = lvm.getDevice(volumeId); err != nil {
156+
if errors.Is(err, ErrDeviceNotFound) {
157+
return nil
158+
}
153159
return err
154160
}
155161
if err := clearDevice(device, flush); err != nil {
162+
if errors.Is(err, ErrDeviceNotFound) {
163+
// Remove device from cache
164+
delete(lvm.devices, volumeId)
165+
return nil
166+
}
156167
return err
157168
}
158169

pkg/pmem-device-manager/pmd-ndctl.go

+6
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,15 @@ func (pmem *pmemNdctl) DeleteDevice(volumeId string, flush bool) error {
137137

138138
device, err := pmem.getDevice(volumeId)
139139
if err != nil {
140+
if errors.Is(err, ErrDeviceNotFound) {
141+
return nil
142+
}
140143
return err
141144
}
142145
if err := clearDevice(device, flush); err != nil {
146+
if errors.Is(err, ErrDeviceNotFound) {
147+
return nil
148+
}
143149
return err
144150
}
145151
return pmem.ctx.DestroyNamespaceByName(volumeId)

pkg/pmem-device-manager/pmd-util.go

+10-7
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
"time"
88

99
pmemexec "github.com/intel/pmem-csi/pkg/pmem-exec"
10+
"golang.org/x/sys/unix"
1011
"k8s.io/klog"
11-
"k8s.io/kubernetes/pkg/volume/util/hostutil"
1212
)
1313

1414
const (
@@ -33,17 +33,20 @@ func clearDevice(dev *PmemDeviceInfo, flush bool) error {
3333
klog.Errorf("clearDevice: %s does not exist", dev.Path)
3434
return err
3535
}
36+
37+
// Check if device
3638
if (fileinfo.Mode() & os.ModeDevice) == 0 {
3739
klog.Errorf("clearDevice: %s is not device", dev.Path)
3840
return fmt.Errorf("%s is not device", dev.Path)
3941
}
40-
devOpen, err := hostutil.NewHostUtil().DeviceOpened(dev.Path)
42+
43+
fd, err := unix.Open(dev.Path, unix.O_RDONLY|unix.O_EXCL|unix.O_CLOEXEC, 0)
44+
defer unix.Close(fd)
45+
4146
if err != nil {
42-
return err
43-
}
44-
if devOpen {
45-
return fmt.Errorf("%s is in use", dev.Path)
47+
return fmt.Errorf("failed to clear device %q as %w", dev.Path, ErrDeviceInUse)
4648
}
49+
4750
if blocks == 0 {
4851
klog.V(5).Infof("Wiping entire device: %s", dev.Path)
4952
// use one iteration instead of shred's default=3 for speed
@@ -75,5 +78,5 @@ func waitDeviceAppears(dev *PmemDeviceInfo) error {
7578
i, dev.Path, retryStatTimeout)
7679
time.Sleep(retryStatTimeout)
7780
}
78-
return fmt.Errorf("device %s did not appear after multiple retries", dev.Path)
81+
return fmt.Errorf("%w(%s)", ErrDeviceNotReady, dev.Path)
7982
}

test/e2e/storage/sanity.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import (
3434
"github.com/kubernetes-csi/csi-test/pkg/sanity"
3535
sanityutils "github.com/kubernetes-csi/csi-test/utils"
3636
"google.golang.org/grpc"
37+
"google.golang.org/grpc/codes"
38+
"google.golang.org/grpc/status"
3739
appsv1 "k8s.io/api/apps/v1"
3840
v1 "k8s.io/api/core/v1"
3941
"k8s.io/apimachinery/pkg/api/resource"
@@ -43,9 +45,9 @@ import (
4345
clientset "k8s.io/client-go/kubernetes"
4446
clientexec "k8s.io/client-go/util/exec"
4547
"k8s.io/kubernetes/test/e2e/framework"
46-
testutils "k8s.io/kubernetes/test/utils"
4748
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
4849
e2essh "k8s.io/kubernetes/test/e2e/framework/ssh"
50+
testutils "k8s.io/kubernetes/test/utils"
4951

5052
. "github.com/onsi/ginkgo"
5153
. "github.com/onsi/gomega"
@@ -67,7 +69,7 @@ var _ = Describe("sanity", func() {
6769
// and deletes all extra entries that it does not know about.
6870
TargetPath: "/var/lib/kubelet/plugins/kubernetes.io/csi/pv/pmem-sanity-target.XXXXXX",
6971
StagingPath: "/var/lib/kubelet/plugins/kubernetes.io/csi/pv/pmem-sanity-staging.XXXXXX",
70-
IDGen: &sanity.DefaultIDGenerator{},
72+
IDGen: &sanity.DefaultIDGenerator{},
7173
}
7274

7375
f := framework.NewDefaultFramework("pmem")
@@ -352,6 +354,26 @@ var _ = Describe("sanity", func() {
352354
Expect(resp.AvailableCapacity).To(Equal(nodeCapacity), "capacity mismatch")
353355
})
354356

357+
It("delete volume should fail with appropriate error", func() {
358+
v.namePrefix = "delete-volume"
359+
360+
name, vol := v.create(2*1024*1024, nodeID)
361+
// Publish for the second time.
362+
nodeID := v.publish(name, vol)
363+
364+
_, err := v.cc.DeleteVolume(v.ctx, &csi.DeleteVolumeRequest{
365+
VolumeId: vol.GetVolumeId(),
366+
})
367+
Expect(err).ShouldNot(BeNil(), fmt.Sprintf("Volume(%s) in use cannot be deleted", name))
368+
s, ok := status.FromError(err)
369+
Expect(ok).Should(BeTrue(), "Expected a status error")
370+
Expect(s.Code()).Should(BeEquivalentTo(codes.FailedPrecondition), "Expected device busy error")
371+
372+
v.unpublish(vol, nodeID)
373+
374+
v.remove(vol, name)
375+
})
376+
355377
var (
356378
numWorkers = flag.Int("pmem.sanity.workers", 10, "number of worker creating volumes in parallel and thus also the maximum number of volumes at any time")
357379
numVolumes = flag.Int("pmem.sanity.volumes",

0 commit comments

Comments
 (0)