diff --git a/cmd/provisioner/main/main.go b/cmd/provisioner/main/main.go index 20a9f4ee3..b3e9a279f 100644 --- a/cmd/provisioner/main/main.go +++ b/cmd/provisioner/main/main.go @@ -30,6 +30,7 @@ import ( "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "os" + "flag" ) var ( @@ -39,6 +40,11 @@ var ( func main() { + /* this is fixing an existing issue with glog in kuberenetes in version 1.9 + if we ever move to a newer code version this can be removed. + */ + flag.CommandLine.Parse([]string{}) + ubiquityConfig, err := k8sutils.LoadConfig() if err != nil { panic(fmt.Errorf("Failed to load config %#v", err)) diff --git a/controller/controller.go b/controller/controller.go index 2c2254ec6..c9a4f6eee 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -39,6 +39,7 @@ const( k8sMountPointvolumeDirectoryName = "ibm~ubiquity-k8s-flex" FlexSuccessStr = "Success" FlexFailureStr = "Failure" + FlexNotSupportedStr = "Not supported" ) //Controller this is a structure that controls volume management @@ -126,9 +127,25 @@ func (c *Controller) Attach(attachRequest k8sresources.FlexVolumeAttachRequest) defer logs.GetDeleteFromMapFunc(go_id) defer c.logger.Trace(logs.DEBUG)() var response k8sresources.FlexVolumeResponse + var err error c.logger.Debug("", logs.Args{{"request", attachRequest}}) - err := c.doAttach(attachRequest) + getVolumeRequest := resources.GetVolumeRequest{Name:attachRequest.Name} + volume, err := c.Client.GetVolume(getVolumeRequest) + + if err != nil { + errormsg := fmt.Sprintf("Failed to get Volume details [%s]", attachRequest.Name) + response = c.failureFlexVolumeResponse(err, errormsg) + return response + } + + //TODO: later we should consider to get the volume backend by using the attachRequest.opts instead of calling to ubiquity server + if (volume.Backend == resources.SpectrumScale) { + response = c.notSupportedFlexVolumeResponse("Flex Attach API is not supported for spectrum-scale backend, because the backend assume that volume is already attached to all nodes") + return response + } + + err = c.doAttach(attachRequest) if err != nil { msg := fmt.Sprintf("Failed to attach volume [%s]", attachRequest.Name) response = c.failureFlexVolumeResponse(err, msg) @@ -178,8 +195,38 @@ func (c *Controller) IsAttached(isAttachedRequest k8sresources.FlexVolumeIsAttac defer logs.GetDeleteFromMapFunc(go_id) defer c.logger.Trace(logs.DEBUG)() var response k8sresources.FlexVolumeResponse + var volume resources.Volume + var err error c.logger.Debug("", logs.Args{{"request", isAttachedRequest}}) + volName, ok := isAttachedRequest.Opts["volumeName"] + + if !ok { + err := fmt.Errorf("volumeName not found in isAttachedRequest") + response = c.failureFlexVolumeResponse(err, "") + return response + } + + getVolumeRequest := resources.GetVolumeRequest{Name: volName} + if volume, err = c.Client.GetVolume(getVolumeRequest); err != nil { + if strings.Contains(err.Error(), resources.VolumeNotFoundErrorMsg) { + warningMsg := fmt.Sprintf("%s (backend error=%v)", IdempotentIsAttachedSkipOnVolumeNotExistWarnigMsg, err) + c.logger.Warning(warningMsg) + response = k8sresources.FlexVolumeResponse{ + Status: "Success", + Attached: false, + Message: warningMsg, + } + return response + } + return c.failureFlexVolumeResponse(err, "") + } + + if (volume.Backend == resources.SpectrumScale) { + response = c.notSupportedFlexVolumeResponse("Flex IsAttached API is not supported for spectrum-scale backend, because the backend assume that volume is always attached") + return response + } + isAttached, err := c.doIsAttached(isAttachedRequest) if err != nil { msg := fmt.Sprintf("Failed to check IsAttached volume [%s]", isAttachedRequest.Name) @@ -202,6 +249,8 @@ func (c *Controller) Detach(detachRequest k8sresources.FlexVolumeDetachRequest) defer logs.GetDeleteFromMapFunc(go_id) defer c.logger.Trace(logs.DEBUG)() var response k8sresources.FlexVolumeResponse + var volume resources.Volume + var err error c.logger.Debug("", logs.Args{{"request", detachRequest}}) if detachRequest.Version == k8sresources.KubernetesVersion_1_5 { @@ -210,7 +259,22 @@ func (c *Controller) Detach(detachRequest k8sresources.FlexVolumeDetachRequest) Status: "Success", } } else { - err := c.doDetach(detachRequest, true) + getVolumeRequest := resources.GetVolumeRequest{Name: detachRequest.Name} + if volume, err = c.Client.GetVolume(getVolumeRequest); err != nil { + if strings.Contains(err.Error(), resources.VolumeNotFoundErrorMsg) { + warningMsg := fmt.Sprintf("%s (backend error=%v)", IdempotentDetachSkipOnVolumeNotExistWarnigMsg, err) + c.logger.Warning(warningMsg) + return c.successFlexVolumeResponse(warningMsg) + } + return c.failureFlexVolumeResponse(err, "") + } + + if (volume.Backend == resources.SpectrumScale) { + response = c.notSupportedFlexVolumeResponse("Flex Detach API is not supported for spectrum-scale backend, because the backend assume that volume is always remain attached") + return response + } + + err = c.doDetach(detachRequest, true) if err != nil { msg := fmt.Sprintf( "Failed to detach volume [%s] from host [%s]. Error: %#v", @@ -330,6 +394,16 @@ func (c *Controller) failureFlexVolumeResponse(err error, additionalMsg string) return response } +func (c *Controller) notSupportedFlexVolumeResponse(msg string) k8sresources.FlexVolumeResponse { + defer c.logger.Trace(logs.DEBUG)() + response := k8sresources.FlexVolumeResponse{ + Status: FlexNotSupportedStr, + Message: msg, + } + c.logger.Info(fmt.Sprintf("%#v", response)) + return response +} + func (c *Controller) checkSlinkBeforeUmount(k8sPVDirectoryPath string, realMountedPath string) (bool, error) { /* Return @@ -383,7 +457,8 @@ func (c *Controller) getRealMountpointForPvByBackend(volumeBackend string, volum if volumeBackend == resources.SCBE { return fmt.Sprintf(resources.PathToMountUbiquityBlockDevices, volumeConfig["Wwn"].(string)), nil } else if volumeBackend == resources.SpectrumScale { - return "", &BackendNotImplementedGetRealMountpointError{Backend: volumeBackend} + return volumeConfig["mountpoint"].(string), nil + //TODO: scale should remove the mountpoint } else { return "", &PvBackendNotSupportedError{Backend: volumeBackend} } @@ -475,8 +550,16 @@ func (c *Controller) Unmount(unmountRequest k8sresources.FlexVolumeUnmountReques if err := c.doUnmount(k8sPVDirectoryPath, volume.Backend, volumeConfig, mounter); err != nil { return c.failureFlexVolumeResponse(err, "") } + + if (volume.Backend == resources.SpectrumScale) { + return c.successFlexVolumeResponse("") + } else if (volume.Backend == resources.SCBE) { // Do legacy detach (means trigger detach as part of the umount from the k8s node) - if err := c.doLegacyDetach(unmountRequest); err != nil { + if err := c.doLegacyDetach(unmountRequest); err != nil { + return c.failureFlexVolumeResponse(err, "") + } + } else { + err = c.logger.ErrorRet(&PvBackendNotSupportedError{Backend: volume.Backend}, "failed") return c.failureFlexVolumeResponse(err, "") } @@ -519,7 +602,33 @@ func (c *Controller) getMounterForBackend(backend string, requestContext resourc return c.mounterPerBackend[backend], nil } -func (c *Controller) prepareUbiquityMountRequest(mountRequest k8sresources.FlexVolumeMountRequest) (resources.MountRequest, error) { +func (c *Controller) getMountpointForVolume(mountRequest k8sresources.FlexVolumeMountRequest, volumeConfig map[string]interface{}, volumeBackend string) (string, error) { + + defer c.logger.Trace(logs.DEBUG)() + + var volumeMountPoint string + var ok bool + + if (volumeBackend == resources.SpectrumScale) { + volumeMountPoint, ok = volumeConfig["mountpoint"].(string) + if !ok { + return "", c.logger.ErrorRet(&SpectrumScaleMissingMntPtVolumeError{VolumeName: mountRequest.MountDevice}, "failed") + } + } else if (volumeBackend == resources.SCBE) { + wwn, ok := mountRequest.Opts["Wwn"] + if !ok { + err := fmt.Errorf(MissingWwnMountRequestErrorStr) + return "", c.logger.ErrorRet(err, "failed") + } + volumeMountPoint = fmt.Sprintf(resources.PathToMountUbiquityBlockDevices, wwn) + } else { + return "", c.logger.ErrorRet(&PvBackendNotSupportedError{Backend: volumeBackend}, "failed") + } + + return volumeMountPoint, nil +} + +func (c *Controller) prepareUbiquityMountRequest(mountRequest k8sresources.FlexVolumeMountRequest, volumeBackend string) (resources.MountRequest, error) { /* Prepare the mounter.Mount request */ @@ -532,32 +641,29 @@ func (c *Controller) prepareUbiquityMountRequest(mountRequest k8sresources.FlexV return resources.MountRequest{}, c.logger.ErrorRet(err, "Client.GetVolumeConfig failed") } - // Prepare request for mounter - step2 generate the designated mountpoint for this volume. - // TODO should be agnostic to the backend, currently its scbe oriented. - wwn, ok := mountRequest.Opts["Wwn"] - if !ok { - err = fmt.Errorf(MissingWwnMountRequestErrorStr) - return resources.MountRequest{}, c.logger.ErrorRet(err, "failed") + volumeMountpoint, err := c.getMountpointForVolume(mountRequest, volumeConfig, volumeBackend) + if err != nil { + return resources.MountRequest{}, err } - volumeMountpoint := fmt.Sprintf(resources.PathToMountUbiquityBlockDevices, wwn) + ubMountRequest := resources.MountRequest{Mountpoint: volumeMountpoint, VolumeConfig: volumeConfig, Context: mountRequest.Context} return ubMountRequest, nil } -func (c *Controller) getMounterByPV(mountRequest k8sresources.FlexVolumeMountRequest) (resources.Mounter, error) { +func (c *Controller) getMounterByPV(mountRequest k8sresources.FlexVolumeMountRequest) (resources.Mounter, string, error) { defer c.logger.Trace(logs.DEBUG)() getVolumeRequest := resources.GetVolumeRequest{Name: mountRequest.MountDevice, Context: mountRequest.Context} volume, err := c.Client.GetVolume(getVolumeRequest) if err != nil { - return nil, c.logger.ErrorRet(err, "GetVolume failed") + return nil, "", c.logger.ErrorRet(err, "GetVolume failed") } mounter, err := c.getMounterForBackend(volume.Backend, mountRequest.Context) if err != nil { - return nil, c.logger.ErrorRet(err, "getMounterForBackend failed") + return nil, "", c.logger.ErrorRet(err, "getMounterForBackend failed") } - return mounter, nil + return mounter, volume.Backend, nil } func getK8sPodsBaseDir(k8sMountPoint string) (string, error ){ @@ -653,16 +759,16 @@ func (c *Controller) doMount(mountRequest k8sresources.FlexVolumeMountRequest) ( return "", c.logger.ErrorRet(&k8sVersionNotSupported{mountRequest.Version}, "failed") } - mounter, err := c.getMounterByPV(mountRequest) + mounter, volumeBackend, err := c.getMounterByPV(mountRequest) if err != nil { return "", c.logger.ErrorRet(err, "getMounterByPV failed") } - ubMountRequest, err := c.prepareUbiquityMountRequest(mountRequest) + ubMountRequest, err := c.prepareUbiquityMountRequest(mountRequest, volumeBackend) if err != nil { return "", c.logger.ErrorRet(err, "prepareUbiquityMountRequest failed") } - + err = checkSlinkAlreadyExistsOnMountPoint(ubMountRequest.Mountpoint, mountRequest.MountPath, c.logger, c.exec) if err != nil { return "", c.logger.ErrorRet(err, "Failed to check if other links point to mountpoint") @@ -676,23 +782,6 @@ func (c *Controller) doMount(mountRequest k8sresources.FlexVolumeMountRequest) ( return mountpoint, nil } -func (c *Controller) getK8sPVDirectoryByBackend(mountedPath string, k8sPVDirectory string) string { - /* - mountedPath is the original device mountpoint (e.g /ubiquity/) - The function return the k8sPVDirectory based on the backend. - */ - - // TODO route between backend by using the volume backend instead of using /ubiquity hardcoded in the mountpoint - ubiquityMountPrefix := fmt.Sprintf(resources.PathToMountUbiquityBlockDevices, "") - var lnPath string - if strings.HasPrefix(mountedPath, ubiquityMountPrefix) { - lnPath = k8sPVDirectory - } else { - lnPath, _ = path.Split(k8sPVDirectory) // TODO verify why Scale backend use this split? - } - return lnPath -} - func (c *Controller) doAfterMount(mountRequest k8sresources.FlexVolumeMountRequest, mountedPath string) error { /* Create symbolic link instead of the k8s PV directory that will point to the ubiquity mountpoint. @@ -722,7 +811,7 @@ func (c *Controller) doAfterMount(mountRequest k8sresources.FlexVolumeMountReque var k8sPVDirectoryPath string var err error - k8sPVDirectoryPath = c.getK8sPVDirectoryByBackend(mountedPath, mountRequest.MountPath) + k8sPVDirectoryPath = mountRequest.MountPath // Identify the PV directory by using Lstat and then handle all idempotent cases (use Lstat to get the dir or slink detail and not the evaluation of it) fileInfo, err := c.exec.Lstat(k8sPVDirectoryPath) @@ -886,14 +975,23 @@ func (c *Controller) doDetach(detachRequest k8sresources.FlexVolumeDetachRequest if host == "" { // only when triggered during unmount var err error - host, err = c.getHostAttached(detachRequest.Name, detachRequest.Context) + + // TODO: if the automation will stop using loggin authenticatin and the WWN here is no longer needed we can + // we can go back to using regulat getHostAttached and remove getHostAttachUsingConfig. + getVolumeConfigRequest := resources.GetVolumeConfigRequest{Name: detachRequest.Name, Context: detachRequest.Context} + volumeConfig, err := c.Client.GetVolumeConfig(getVolumeConfigRequest) + if err != nil { + return c.logger.ErrorRet(err, "getVolumeConfig failed.", logs.Args{{"vol", detachRequest.Name}}) + } + + host, err = c.getHostAttachUsingConfig(volumeConfig) if err != nil { - return c.logger.ErrorRet(err, "getHostAttached failed") + return c.logger.ErrorRet(err, "getHostAttached failed.") } if host == "" { // this means that the host is not attached to anything so no reason to call detach - c.logger.Warning(fmt.Sprintf("Vol: %s is not attahced to any host. so no detach action is called.", detachRequest.Name)) + c.logger.Warning(fmt.Sprintf("Idempotent issue encoutered - vol is not attached to any host. so no detach action is called"), logs.Args{{"vol wwn", volumeConfig["Wwn"]}}) return nil } } @@ -926,6 +1024,16 @@ func (c *Controller) doIsAttached(isAttachedRequest k8sresources.FlexVolumeIsAtt return isAttached, nil } +func (c *Controller) getHostAttachUsingConfig(volumeConfig map[string]interface{}) (string, error){ + attachTo, ok := volumeConfig[resources.ScbeKeyVolAttachToHost].(string) + if !ok { + return "", c.logger.ErrorRet(fmt.Errorf("GetVolumeConfig is missing info"), "GetVolumeConfig missing info.", logs.Args{{"arg", resources.ScbeKeyVolAttachToHost}}) + } + c.logger.Debug("", logs.Args{{"volumeConfig", volumeConfig}, {"attachTo", attachTo}}) + + return attachTo, nil +} + func (c *Controller) getHostAttached(volName string, requestContext resources.RequestContext) (string, error) { defer c.logger.Trace(logs.DEBUG)() @@ -935,13 +1043,7 @@ func (c *Controller) getHostAttached(volName string, requestContext resources.Re return "", c.logger.ErrorRet(err, "Client.GetVolumeConfig failed") } - attachTo, ok := volumeConfig[resources.ScbeKeyVolAttachToHost].(string) - if !ok { - return "", c.logger.ErrorRet(err, "GetVolumeConfig missing info", logs.Args{{"arg", resources.ScbeKeyVolAttachToHost}}) - } - c.logger.Debug("", logs.Args{{"volumeConfig", volumeConfig}, {"attachTo", attachTo}}) - - return attachTo, nil + return c.getHostAttachUsingConfig(volumeConfig) } func getVolumeForMountpoint(mountpoint string, volumes []resources.Volume) (resources.Volume, error) { diff --git a/controller/controller_test.go b/controller/controller_test.go index 48123a185..64d0cfc59 100644 --- a/controller/controller_test.go +++ b/controller/controller_test.go @@ -65,6 +65,28 @@ var _ = Describe("Controller", func() { Expect(initResponse.Device).To(Equal("")) }) + Context(".Attach", func() { + It("fails when Attach fails to get volume details", func() { + fakeClient.GetVolumeReturns(resources.Volume{}, fmt.Errorf("GetVolume error")) + opts := map[string]string{} + host := "fakehost" + AttachRequest := k8sresources.FlexVolumeAttachRequest{"vol1", host, opts, "version", resources.RequestContext{}} + attachResponse := controller.Attach(AttachRequest) + Expect(attachResponse.Status).To(Equal("Failure")) + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + }) + + It("Attach should return Not supported for SpectrumScale backend", func() { + fakeClient.GetVolumeReturns(resources.Volume{Name: "pv1", Backend: "spectrum-scale", Mountpoint: "fake"}, nil) + opts := map[string]string{} + host := "fakehost" + AttachRequest := k8sresources.FlexVolumeAttachRequest{"vol1", host, opts, "version", resources.RequestContext{}} + attachResponse := controller.Attach(AttachRequest) + Expect(attachResponse.Status).To(Equal("Not supported")) + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + }) + }) + //Context(".Attach", func() { // // It("fails when attachRequest does not have volumeName", func() { @@ -203,6 +225,47 @@ var _ = Describe("Controller", func() { Expect(mountResponse.Status).To(Equal(ctl.FlexFailureStr)) }) + It("should fail to prepareUbiquityMountRequest if GetVolumeConfig does not contain mountpoint for spectrumscale backend", func() { + fakeClient.GetVolumeReturns(resources.Volume{Name: "pv1", Backend: "spectrum-scale", Mountpoint: "fake"}, nil) + byt := []byte(`{"fake":"fake"}`) + var dat map[string]interface{} + if err := json.Unmarshal(byt, &dat); err != nil { + panic(err) + } + fakeClient.GetVolumeConfigReturns(dat, nil) + mountRequest := k8sresources.FlexVolumeMountRequest{MountPath: "/pod/pv1", MountDevice: "pv1", Opts: map[string]string{}} + + mountResponse := controller.Mount(mountRequest) + + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + Expect(fakeMounterFactory.GetMounterPerBackendCallCount()).To(Equal(1)) + Expect(fakeClient.GetVolumeConfigCallCount()).To(Equal(1)) + Expect(fakeMounter.MountCallCount()).To(Equal(0)) + Expect(mountResponse.Message).To(Equal(ctl.SpectrumScaleMissingMntPtVolumeErrorStr+" volume=[pv1]")) + Expect(mountResponse.Status).To(Equal(ctl.FlexFailureStr)) + }) + + + It("should fail to prepareUbiquityMountRequest if GetVolumeConfig does not contain valid backend", func() { + fakeClient.GetVolumeReturns(resources.Volume{Name: "pv1", Backend: "fake", Mountpoint: "fake"}, nil) + byt := []byte(`{"fake":"fake"}`) + var dat map[string]interface{} + if err := json.Unmarshal(byt, &dat); err != nil { + panic(err) + } + fakeClient.GetVolumeConfigReturns(dat, nil) + mountRequest := k8sresources.FlexVolumeMountRequest{MountPath: "/pod/pv1", MountDevice: "pv1", Opts: map[string]string{}} + + mountResponse := controller.Mount(mountRequest) + + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + Expect(fakeMounterFactory.GetMounterPerBackendCallCount()).To(Equal(1)) + Expect(fakeClient.GetVolumeConfigCallCount()).To(Equal(1)) + Expect(fakeMounter.MountCallCount()).To(Equal(0)) + Expect(mountResponse.Message).To(Equal(ctl.PvBackendNotSupportedErrorStr+" backend=[fake]")) + Expect(mountResponse.Status).To(Equal(ctl.FlexFailureStr)) + }) + It("should fail if mounter.Mount failed (doMount)", func() { errstr := "TODO set error in mounter" fakeClient.GetVolumeReturns(resources.Volume{Name: "pv1", Backend: "scbe", Mountpoint: "fake"}, nil) @@ -504,6 +567,7 @@ var _ = Describe("Controller", func() { err := fmt.Errorf("an Error has occured") fakeExec.GetGlobFilesReturns(nil, err) controller = ctl.NewControllerWithClient(testLogger, ubiquityConfig, fakeClient, fakeExec, fakeMounterFactory) + fakeClient.GetVolumeReturns(resources.Volume{Name: "pv1", Backend: "scbe", Mountpoint: "fake"}, nil) mountRequest := k8sresources.FlexVolumeMountRequest{MountPath: mountPoint, MountDevice: "pv1", Opts: map[string]string{"Wwn": "fake"}, Version: k8sresources.KubernetesVersion_1_6OrLater} mountResponse := controller.Mount(mountRequest) @@ -955,10 +1019,90 @@ var _ = Describe("Controller", func() { }) }) */ + + Context(".IsAttached", func() { + var ( + host string + ) + BeforeEach(func() { + host = "fakehost" + }) + + It("IsAttached should return success with Attached as false when GetVolume Fails with VolumeNotFoundErrorMsg ", func() { + fakeClient.GetVolumeReturns(resources.Volume{}, &resources.VolumeNotFoundError{VolName: "pv1"}) + opts := make(map[string]string) + opts["volumeName"] = "pv1" + isAttachedRequest := k8sresources.FlexVolumeIsAttachedRequest{"", host, opts, resources.RequestContext{}} + isAttachResponse := controller.IsAttached(isAttachedRequest) + Expect(isAttachResponse.Status).To(Equal("Success")) + Expect(isAttachResponse.Attached).To(Equal(false)) + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + }) + + It("IsAttached should fail when GetVolume Fails with error other then VolumeNotFoundError.", func() { + fakeClient.GetVolumeReturns(resources.Volume{}, fmt.Errorf("GetVolume error")) + opts := make(map[string]string) + opts["volumeName"] = "pv1" + isAttachedRequest := k8sresources.FlexVolumeIsAttachedRequest{"", host, opts, resources.RequestContext{}} + isAttachResponse := controller.IsAttached(isAttachedRequest) + Expect(isAttachResponse.Status).To(Equal("Failure")) + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + }) + + + It("IsAttached should return Not supported for SpectrumScale backend", func() { + fakeClient.GetVolumeReturns(resources.Volume{Name: "pv1", Backend: "spectrum-scale", Mountpoint: "fake"}, nil) + opts := make(map[string]string) + opts["volumeName"] = "pv1" + isAttachedRequest := k8sresources.FlexVolumeIsAttachedRequest{"", host, opts, resources.RequestContext{}} + isAttachResponse := controller.IsAttached(isAttachedRequest) + Expect(isAttachResponse.Status).To(Equal("Not supported")) + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + }) + + It("IsAttached should fail since Request options does not contain volume name", func() { + fakeClient.GetVolumeReturns(resources.Volume{Name: "pv1", Backend: "spectrum-scale", Mountpoint: "fake"}, nil) + opts := make(map[string]string) + isAttachedRequest := k8sresources.FlexVolumeIsAttachedRequest{"", host, opts, resources.RequestContext{}} + isAttachResponse := controller.IsAttached(isAttachedRequest) + Expect(isAttachResponse.Status).To(Equal("Failure")) + Expect(fakeClient.GetVolumeCallCount()).To(Equal(0)) + }) + + }) + Context(".Detach", func() { BeforeEach(func() { }) + + It("Detach should return success when GetVolume Fails with VolumeNotFoundError ", func() { + fakeClient.GetVolumeReturns(resources.Volume{}, &resources.VolumeNotFoundError{VolName: "vol1"}) + host := "fakehost" + detachRequest := k8sresources.FlexVolumeDetachRequest{"vol1", host, "version", resources.RequestContext{}} + detachResponse := controller.Detach(detachRequest) + Expect(detachResponse.Status).To(Equal("Success")) + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + }) + + It("Detach should fail when GetVolume Fails with error other than VolumeNotFoundError ", func() { + fakeClient.GetVolumeReturns(resources.Volume{}, fmt.Errorf("GetVolume error")) + host := "fakehost" + detachRequest := k8sresources.FlexVolumeDetachRequest{"vol1", host, "version", resources.RequestContext{}} + detachResponse := controller.Detach(detachRequest) + Expect(detachResponse.Status).To(Equal("Failure")) + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + }) + + It("Detach should return Not supported for SpectrumScale backend", func() { + fakeClient.GetVolumeReturns(resources.Volume{Name: "pv1", Backend: "spectrum-scale", Mountpoint: "fake"}, nil) + host := "fakehost" + detachRequest := k8sresources.FlexVolumeDetachRequest{"vol1", host, "version", resources.RequestContext{}} + detachResponse := controller.Detach(detachRequest) + Expect(detachResponse.Status).To(Equal("Not supported")) + Expect(fakeClient.GetVolumeCallCount()).To(Equal(1)) + }) + It("calling detach works as expected when volume is attached to the host", func() { dat := make(map[string]interface{}) host := "host1" diff --git a/controller/errors.go b/controller/errors.go index 671884900..01f2081a0 100644 --- a/controller/errors.go +++ b/controller/errors.go @@ -79,6 +79,8 @@ func (e *k8sPVDirectoryIsNotDirNorSlinkError) Error() string { } const IdempotentUnmountSkipOnVolumeNotExistWarnigMsg = "Unmount operation requested to work on not exist volume. Assume its idempotent issue - so skip Unmount." +const IdempotentIsAttachedSkipOnVolumeNotExistWarnigMsg = "IsAttached operation requested to work on not exist volume. Assume its idempotent issue - so skip IsAttached." +const IdempotentDetachSkipOnVolumeNotExistWarnigMsg = "Detach operation requested to work on not exist volume. Assume its idempotent issue - so skip Detach." const K8sPVDirectoryIsNotSlinkErrorStr = "k8s PV directory, k8s-mountpoint, is not slink." @@ -134,3 +136,13 @@ func (e *WrongK8sDirectoryPathError) Error() string { return fmt.Sprintf(PVIsAlreadyUsedByAnotherPodMessage + "k8smountdir=[%s]", e.k8smountdir) } +const SpectrumScaleMissingMntPtVolumeErrorStr = "MountPoint Missing in Volume Config." + +type SpectrumScaleMissingMntPtVolumeError struct { + VolumeName string +} + +func (e *SpectrumScaleMissingMntPtVolumeError) Error() string { + return fmt.Sprintf(SpectrumScaleMissingMntPtVolumeErrorStr+" volume=[%s]", e.VolumeName) +} + diff --git a/glide.yaml b/glide.yaml index 1c934f803..1766b72fa 100644 --- a/glide.yaml +++ b/glide.yaml @@ -110,7 +110,7 @@ import: subpackages: - lib - package: github.com/IBM/ubiquity - version: a91d9984ae76f7a15cb18b5e0c318bc8d85a350e + version: 422904c4c777d359341f22687ab6e3c9b459c470 subpackages: - remote - resources