Skip to content

Commit

Permalink
Handle the force-delete scenario in the OS volume publisher (#3453) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
0sewa0 authored Jul 15, 2024
1 parent 064c700 commit 48436ea
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 14 deletions.
41 changes: 27 additions & 14 deletions pkg/controllers/csi/driver/volumes/host/publisher.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,23 @@ const failedToGetOsAgentVolumePrefix = "failed to get OSMount from database: "

func NewHostVolumePublisher(fs afero.Afero, mounter mount.Interface, db metadata.Access, path metadata.PathResolver) csivolumes.Publisher {
return &HostVolumePublisher{
fs: fs,
mounter: mounter,
db: db,
path: path,
fs: fs,
mounter: mounter,
db: db,
path: path,
isNotMounted: mount.IsNotMountPoint,
}
}

// necessary for mocking, as the MounterMock will use the os package
type mountChecker func(mounter mount.Interface, file string) (bool, error)

type HostVolumePublisher struct {
fs afero.Afero
mounter mount.Interface
db metadata.Access
path metadata.PathResolver
fs afero.Afero
mounter mount.Interface
db metadata.Access
isNotMounted mountChecker
path metadata.PathResolver
}

func (publisher *HostVolumePublisher) PublishVolume(ctx context.Context, volumeCfg csivolumes.VolumeConfig) (*csi.NodePublishVolumeResponse, error) {
Expand All @@ -62,7 +67,19 @@ func (publisher *HostVolumePublisher) PublishVolume(ctx context.Context, volumeC
return nil, status.Error(codes.Internal, failedToGetOsAgentVolumePrefix+err.Error())
}

if osMount != nil && osMount.DeletedAt.Valid {
if osMount != nil {
if !osMount.DeletedAt.Valid {
// If the OSAgents were removed forcefully, we might not get the unmount request, so we can't fully relay on the database, and have to directly check if its mounted or not
isNotMounted, err := publisher.isNotMounted(publisher.mounter, osMount.Location)
if err != nil {
return nil, err
}

if !isNotMounted {
return &csi.NodePublishVolumeResponse{}, goerrors.New("previous OSMount is yet to be unmounted, there can be only 1 OSMount per tenant per node, blocking until unmount") // don't want to have the stacktrace here, it just pollutes the logs
}
}

osMount.VolumeMeta = metadata.VolumeMeta{
ID: volumeCfg.VolumeID,
PodName: volumeCfg.PodName,
Expand All @@ -79,9 +96,7 @@ func (publisher *HostVolumePublisher) PublishVolume(ctx context.Context, volumeC
}

return &csi.NodePublishVolumeResponse{}, nil
}

if osMount == nil && errors.Is(err, gorm.ErrRecordNotFound) {
} else {
osMount := metadata.OSMount{
VolumeMeta: metadata.VolumeMeta{ID: volumeCfg.VolumeID, PodName: volumeCfg.PodName},
VolumeMetaID: volumeCfg.VolumeID,
Expand All @@ -98,8 +113,6 @@ func (publisher *HostVolumePublisher) PublishVolume(ctx context.Context, volumeC
if err := publisher.db.CreateOSMount(&osMount); err != nil {
return nil, status.Error(codes.Internal, fmt.Sprintf("failed to insert OSMount to database. info: %v err: %s", osMount, err.Error()))
}
} else {
return &csi.NodePublishVolumeResponse{}, goerrors.New("previous OSMount is yet to be unmounted, there can be only 1 OSMount per tenant per node, blocking until unmount") // don't want to have the stacktrace here, it just pollutes the logs
}

return &csi.NodePublishVolumeResponse{}, nil
Expand Down
25 changes: 25 additions & 0 deletions pkg/controllers/csi/driver/volumes/host/publisher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,28 @@ func TestPublishVolume(t *testing.T) {
require.Error(t, err)
assert.NotNil(t, response)
})

t.Run("publish volume when previous OSMount force deleted => do mount", func(t *testing.T) {
mounter := mount.NewFakeMounter([]mount.MountPoint{})
publisher := newPublisherForTesting(mounter)
mockDynakube(t, &publisher)

response, err := publisher.PublishVolume(ctx, createTestVolumeConfig())

require.NoError(t, err)
assert.NotNil(t, response)
assert.NotEmpty(t, mounter.MountPoints)
assertReferencesForPublishedVolume(t, &publisher, mounter)

publisher.isNotMounted = func(mounter mount.Interface, file string) (bool, error) {
return true, nil
}
response, err = publisher.PublishVolume(ctx, createTestVolumeConfig())
require.NoError(t, err)
assert.NotNil(t, response)
assert.NotEmpty(t, mounter.MountPoints)
assertReferencesForPublishedVolume(t, &publisher, mounter)
})
}

func TestUnpublishVolume(t *testing.T) {
Expand Down Expand Up @@ -134,6 +156,9 @@ func newPublisherForTesting(mounter *mount.FakeMounter) HostVolumePublisher {
mounter: mounter,
db: metadata.FakeMemoryDB(),
path: metadata.PathResolver{RootDir: csiOptions.RootDir},
isNotMounted: func(mounter mount.Interface, file string) (bool, error) {
return false, nil
},
}
}

Expand Down

0 comments on commit 48436ea

Please sign in to comment.