Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

driver: handle non existing volumes and nodes #60

Merged
merged 3 commits into from
Aug 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
* Remove description to allow users to reuse volumes that were created by the
UI/API
[[GH-59]](https://github.com/digitalocean/csi-digitalocean/pull/59)
* Handle edge cases from external action, such as Volume deletion via UI more
gracefully. We're not very strict anymore in cases we don't need to be, but
we're also returning a better error in cases we need to be.
[[GH-60]](https://github.com/digitalocean/csi-digitalocean/pull/60)

## v0.1.3 - August 3rd 2018

Expand Down
46 changes: 46 additions & 0 deletions driver/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,24 @@ func (d *Driver) ControllerPublishVolume(ctx context.Context, req *csi.Controlle
})
ll.Info("controller publish volume called")

// check if volume exist before trying to attach it
_, resp, err := d.doClient.Storage.GetVolume(ctx, req.VolumeId)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, status.Errorf(codes.NotFound, "volume %q not found", req.VolumeId)
}
return nil, err
}

// check if droplet exist before trying to attach the volume to the droplet
_, resp, err = d.doClient.Droplets.Get(ctx, dropletID)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, status.Errorf(codes.NotFound, "droplet %q not found", dropletID)
}
return nil, err
}

action, resp, err := d.doClient.StorageActions.Attach(ctx, req.VolumeId, dropletID)
if err != nil {
// don't do anything if attached
Expand Down Expand Up @@ -224,6 +242,25 @@ func (d *Driver) ControllerUnpublishVolume(ctx context.Context, req *csi.Control
})
ll.Info("controller unpublish volume called")

// check if volume exist before trying to detach it
_, resp, err := d.doClient.Storage.GetVolume(ctx, req.VolumeId)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
// assume it's detached
return &csi.ControllerUnpublishVolumeResponse{}, nil
}
return nil, err
}

// check if droplet exist before trying to detach the volume from the droplet
_, resp, err = d.doClient.Droplets.Get(ctx, dropletID)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil, status.Errorf(codes.NotFound, "droplet %q not found", dropletID)
}
return nil, err
}

action, resp, err := d.doClient.StorageActions.DetachByDropletID(ctx, req.VolumeId, dropletID)
if err != nil {
if (resp != nil && resp.StatusCode == http.StatusUnprocessableEntity) || strings.Contains(err.Error(), "Attachment not found") {
Expand Down Expand Up @@ -275,6 +312,15 @@ func (d *Driver) ValidateVolumeCapabilities(ctx context.Context, req *csi.Valida
})
ll.Info("validate volume capabilities called")

// check if volume exist before trying to validate it it
_, volResp, err := d.doClient.Storage.GetVolume(ctx, req.VolumeId)
if err != nil {
if volResp != nil && volResp.StatusCode == http.StatusNotFound {
return nil, status.Errorf(codes.NotFound, "volume %q not found", req.VolumeId)
}
return nil, err
}

hasSupport := func(mode csi.VolumeCapability_AccessMode_Mode) bool {
for _, m := range vcaps {
if mode == m.Mode {
Expand Down
2 changes: 1 addition & 1 deletion driver/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,6 @@ func (f *fakeMounter) Unmount(target string) error {
func (f *fakeMounter) IsFormatted(source string) (bool, error) {
return true, nil
}
func (f *fakeMounter) IsMounted(source, target string) (bool, error) {
func (f *fakeMounter) IsMounted(target string) (bool, error) {
return true, nil
}
26 changes: 13 additions & 13 deletions driver/mounter.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,10 @@ type Mounter interface {
// returns true if the source device is already formatted.
IsFormatted(source string) (bool, error)

// IsMounted checks whether the source device is mounted to the target path
// in a correct way (i.e: propagated). It returns true if it's mounted. An
// error is returned in case of system errors or if it's mounted
// incorrectly.
IsMounted(source, target string) (bool, error)
// IsMounted checks whether the target path is a correct mount (i.e:
// propagated). It returns true if it's mounted. An error is returned in
// case of system errors or if it's mounted incorrectly.
IsMounted(target string) (bool, error)
}

// TODO(arslan): this is Linux only for now. Refactor this into a package with
Expand Down Expand Up @@ -215,11 +214,7 @@ func (m *mounter) IsFormatted(source string) (bool, error) {
return true, nil
}

func (m *mounter) IsMounted(source, target string) (bool, error) {
if source == "" {
return false, errors.New("source is not specified for checking the mount")
}

func (m *mounter) IsMounted(target string) (bool, error) {
if target == "" {
return false, errors.New("target is not specified for checking the mount")
}
Expand All @@ -233,12 +228,12 @@ func (m *mounter) IsMounted(source, target string) (bool, error) {
return false, err
}

findmntArgs := []string{"-o", "TARGET,PROPAGATION,FSTYPE,OPTIONS", source, "-J"}
findmntArgs := []string{"-o", "TARGET,PROPAGATION,FSTYPE,OPTIONS", "-M", target, "-J"}

m.log.WithFields(logrus.Fields{
"cmd": findmntCmd,
"args": findmntArgs,
}).Info("checking if source is mounted")
}).Info("checking if target is mounted")

out, err := exec.Command(findmntCmd, findmntArgs...).CombinedOutput()
if err != nil {
Expand All @@ -251,6 +246,11 @@ func (m *mounter) IsMounted(source, target string) (bool, error) {
err, findmntCmd, string(out))
}

// no response means there is no mount
if string(out) == "" {
return false, nil
}

var resp *findmntResponse
err = json.Unmarshal(out, &resp)
if err != nil {
Expand All @@ -261,7 +261,7 @@ func (m *mounter) IsMounted(source, target string) (bool, error) {
for _, fs := range resp.FileSystems {
// check if the mount is propagated correctly. It should be set to shared.
if fs.Propagation != "shared" {
return true, fmt.Errorf("mount propagation for target %q is not enabled or the block device %q does not exist anymore", target, source)
return true, fmt.Errorf("mount propagation for target %q is not enabled", target)
}

// the mountpoint should match as well
Expand Down
35 changes: 9 additions & 26 deletions driver/node.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.