diff --git a/.gitignore b/.gitignore index 8606ca9e..bfd112d4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ /vendor # test cluster config -kubeconfig +test/.env # v1.x files _legacy diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c8abf080..43ecc54d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,10 +8,9 @@ k8s sanity tests: before_script: - apk add --update git gcc musl-dev script: - - go test ./cmd/controller + - test/sanity except: - tags - allow_failure: true # TODO: remove me build dirty docker image: stage: test diff --git a/cmd/controller/controller_test.go b/cmd/controller/controller_test.go index 414e0c45..1f6c91ab 100644 --- a/cmd/controller/controller_test.go +++ b/cmd/controller/controller_test.go @@ -24,7 +24,9 @@ func Test(t *testing.T) { defer node.Stop() sanity.Test(t, &sanity.Config{ - Address: nodeSocketPath, - ControllerAddress: controllerSocketPath, + Address: nodeSocketPath, + ControllerAddress: controllerSocketPath, + SecretsFile: "../../test/secrets.yml", + TestVolumeParametersFile: "../../test/config.yml", }) } diff --git a/example/storageclass.yml b/example/storageclass.yml index 5389f305..36a5abfc 100644 --- a/example/storageclass.yml +++ b/example/storageclass.yml @@ -7,8 +7,6 @@ provisioner: dothill.csi.enix.io parameters: pool: B fsType: ext4 - initiatorName: iqn.2019-05.io.enix:arthurs-dev-cluster - uniqueInitiatorNameByPvc: "false" iqn: iqn.2015-11.com.hpe:storage.msa2050.18323cc9ed portals: 10.14.84.211,10.14.84.212 diff --git a/pkg/common/driver.go b/pkg/common/driver.go index 47f17ec5..66ea9a8b 100644 --- a/pkg/common/driver.go +++ b/pkg/common/driver.go @@ -18,16 +18,14 @@ const PluginName = "dothill.csi.enix.io" // Configuration constants const ( - FsTypeConfigKey = "fsType" - PoolConfigKey = "pool" - TargetIQNConfigKey = "iqn" - PortalsConfigKey = "portals" - InitiatorNameConfigKey = "initiatorName" - APIAddressConfigKey = "apiAddress" - UniqueInitiatorNameByPvcConfigKey = "uniqueInitiatorNameByPvc" - UsernameSecretKey = "username" - PasswordSecretKey = "password" - StorageClassAnnotationKey = "storageClass" + FsTypeConfigKey = "fsType" + PoolConfigKey = "pool" + TargetIQNConfigKey = "iqn" + PortalsConfigKey = "portals" + APIAddressConfigKey = "apiAddress" + UsernameSecretKey = "username" + PasswordSecretKey = "password" + StorageClassAnnotationKey = "storageClass" MaximumLUN = 255 VolumeNameMaxLength = 32 @@ -44,8 +42,8 @@ type Driver struct { // DriverCtx contains data common to most calls type DriverCtx struct { Credentials map[string]string - Parameters map[string]string - VolumeCaps []*csi.VolumeCapability + Parameters *map[string]string + VolumeCaps *[]*csi.VolumeCapability Req interface{} } diff --git a/pkg/controller/attacher.go b/pkg/controller/attacher.go index a63a67f2..3d390fdb 100644 --- a/pkg/controller/attacher.go +++ b/pkg/controller/attacher.go @@ -9,11 +9,23 @@ import ( "github.com/container-storage-interface/spec/lib/go/csi" "github.com/enix/dothill-storage-controller/pkg/common" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "k8s.io/klog" ) // ControllerPublishVolume attaches the given volume to the node func (driver *Driver) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) { + if len(req.GetVolumeId()) == 0 { + return nil, status.Error(codes.InvalidArgument, "cannot publish volume with empty ID") + } + if len(req.GetNodeId()) == 0 { + return nil, status.Error(codes.InvalidArgument, "cannot publish volume to a node with empty ID") + } + if req.GetVolumeCapability() == nil { + return nil, status.Error(codes.InvalidArgument, "cannot publish volume without capabilities") + } + err := driver.beginRoutine(&common.DriverCtx{ Req: req, Credentials: req.GetSecrets(), @@ -23,7 +35,7 @@ func (driver *Driver) ControllerPublishVolume(ctx context.Context, req *csi.Cont return nil, err } - initiatorName := getInitiatorName(req.GetVolumeContext()) + initiatorName := req.GetNodeId() klog.Infof("attach request for initiator %s, volume id : %s", initiatorName, req.GetVolumeId()) lun, err := driver.chooseLUN() @@ -47,6 +59,10 @@ func (driver *Driver) ControllerPublishVolume(ctx context.Context, req *csi.Cont // ControllerUnpublishVolume deattaches the given volume from the node func (driver *Driver) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) { + if len(req.GetVolumeId()) == 0 { + return nil, status.Error(codes.InvalidArgument, "cannot unpublish volume with empty ID") + } + err := driver.beginRoutine(&common.DriverCtx{ Req: req, Credentials: req.GetSecrets(), @@ -59,7 +75,7 @@ func (driver *Driver) ControllerUnpublishVolume(ctx context.Context, req *csi.Co klog.Infof("unmapping volume %s from all initiators", req.GetVolumeId()) _, status, err := driver.dothillClient.UnmapVolume(req.GetVolumeId(), "") if err != nil { - if status.ReturnCode == unmapFailedErrorCode { + if status != nil && status.ReturnCode == unmapFailedErrorCode { klog.Info("unmap failed, assuming volume is already unmapped") return &csi.ControllerUnpublishVolumeResponse{}, nil } @@ -102,12 +118,17 @@ func (driver *Driver) chooseLUN() (int, error) { func (driver *Driver) mapVolume(volumeName, initiatorName string, lun int) error { klog.Infof("trying to map volume %s for initiator %s on LUN %d", volumeName, initiatorName, lun) - _, status, err := driver.dothillClient.MapVolume(volumeName, initiatorName, "rw", lun) - if err != nil && status == nil { - return err + _, metadata, err := driver.dothillClient.MapVolume(volumeName, initiatorName, "rw", lun) + if err != nil && metadata == nil { + return status.Error(codes.Internal, err.Error()) } - if status.ReturnCode == hostDoesNotExistsErrorCode { - nodeName := strings.Split(initiatorName, ":")[1] + if metadata.ReturnCode == hostDoesNotExistsErrorCode { + nodeIDParts := strings.Split(initiatorName, ":") + if len(nodeIDParts) != 2 { + return status.Error(codes.InvalidArgument, "specified node ID is not a valid IQN") + } + + nodeName := nodeIDParts[1] klog.Infof("initiator does not exist, creating it with nickname %s", nodeName) _, _, err = driver.dothillClient.CreateHost(nodeName, initiatorName) if err != nil { @@ -118,25 +139,27 @@ func (driver *Driver) mapVolume(volumeName, initiatorName string, lun int) error if err != nil { return err } + } else if metadata.ReturnCode == volumeNotFoundErrorCode { + return status.Errorf(codes.NotFound, "volume %s not found", volumeName) } else if err != nil { - return err + return status.Error(codes.Internal, err.Error()) } return nil } -func getInitiatorName(volumeContext map[string]string) string { - initiatorName := volumeContext[common.InitiatorNameConfigKey] - // overrideInitiatorName, overrideExists := options.PVC.Annotations[initiatorNameConfigKey] - // if overrideExists { - // initiatorName = overrideInitiatorName - // klog.Infof("custom initiator name was specified in PVC annotation: %s", initiatorName) - // } else if options.Parameters[uniqueInitiatorNameByPvcConfigKey] == "true" { - // year, month, _ := time.Now().Date() - // uniquePart := fmt.Sprintf("%d", rand.Int())[:8] - // initiatorName = fmt.Sprintf("iqn.%d-%02d.local.cluster:%s", year, int(month), uniquePart) - // klog.Infof("generated initiator name: %s", initiatorName) - // } - - return initiatorName -} +// func getInitiatorName(volumeContext map[string]string) string { +// initiatorName := volumeContext[common.InitiatorNameConfigKey] +// overrideInitiatorName, overrideExists := options.PVC.Annotations[initiatorNameConfigKey] +// if overrideExists { +// initiatorName = overrideInitiatorName +// klog.Infof("custom initiator name was specified in PVC annotation: %s", initiatorName) +// } else if options.Parameters[uniqueInitiatorNameByPvcConfigKey] == "true" { +// year, month, _ := time.Now().Date() +// uniquePart := fmt.Sprintf("%d", rand.Int())[:8] +// initiatorName = fmt.Sprintf("iqn.%d-%02d.local.cluster:%s", year, int(month), uniquePart) +// klog.Infof("generated initiator name: %s", initiatorName) +// } + +// return initiatorName +// } diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 68c47bc0..c5535a99 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -17,6 +17,7 @@ const ( hostDoesNotExistsErrorCode = -10386 hostMapDoesNotExistsErrorCode = -10074 unmapFailedErrorCode = -10509 + volumeNotFoundErrorCode = -10075 ) // Driver is the implementation of csi.ControllerServer @@ -121,6 +122,11 @@ func (driver *Driver) configureClient(credentials map[string]string) error { username := string(credentials[common.UsernameSecretKey]) password := string(credentials[common.PasswordSecretKey]) apiAddr := string(credentials[common.APIAddressConfigKey]) + + if len(apiAddr) == 0 || len(username) == 0 || len(password) == 0 { + return status.Error(codes.InvalidArgument, "at least one field is missing in credentials secret") + } + klog.Infof("using dothill API at address %s", apiAddr) if driver.dothillClient.Addr == apiAddr && driver.dothillClient.Username == username { klog.Info("dothill client is already configured for this API, skipping login") @@ -133,9 +139,50 @@ func (driver *Driver) configureClient(credentials map[string]string) error { klog.Infof("login into %s as user %s", driver.dothillClient.Addr, driver.dothillClient.Username) err := driver.dothillClient.Login() if err != nil { - return err + return status.Error(codes.Unauthenticated, err.Error()) } klog.Info("login was successful") return nil } + +func runPreflightChecks(parameters *map[string]string, capabilities *[]*csi.VolumeCapability) error { + checkIfKeyExistsInConfig := func(key string) error { + if parameters == nil { + return nil + } + + klog.V(2).Infof("checking for %s in storage class parameters", key) + _, ok := (*parameters)[key] + if !ok { + return status.Errorf(codes.InvalidArgument, "'%s' is missing from configuration", key) + } + return nil + } + + if err := checkIfKeyExistsInConfig(common.FsTypeConfigKey); err != nil { + return err + } + if err := checkIfKeyExistsInConfig(common.PoolConfigKey); err != nil { + return err + } + if err := checkIfKeyExistsInConfig(common.TargetIQNConfigKey); err != nil { + return err + } + if err := checkIfKeyExistsInConfig(common.PortalsConfigKey); err != nil { + return err + } + + if capabilities != nil { + if len(*capabilities) == 0 { + return status.Error(codes.InvalidArgument, "missing volume capabilities") + } + for _, capability := range *capabilities { + if capability.GetAccessMode().GetMode() != csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER { + return status.Error(codes.FailedPrecondition, "dothill storage only supports ReadWriteOnce access mode") + } + } + } + + return nil +} diff --git a/pkg/controller/go.sum b/pkg/controller/go.sum index 33f4400e..482a0c30 100644 --- a/pkg/controller/go.sum +++ b/pkg/controller/go.sum @@ -6,6 +6,8 @@ github.com/container-storage-interface/spec v1.2.0 h1:bD9KIVgaVKKkQ/UbVUY9kCaH/C github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= github.com/enix/dothill-api-go v1.4.1 h1:ePb7tUef0WOnJl1LvEBiQxlQHcnJpEtMjjE47QRDYtI= github.com/enix/dothill-api-go v1.4.1/go.mod h1:LchQqj/tHiZ3AU23geA+0EY9ia9L0MuzNIvqX9reipI= +github.com/enix/dothill-api-go v1.4.2 h1:1heKdOUXFOa1Gsfdo3H6/bh79hTb/mvZbbmdKoaGBOQ= +github.com/enix/dothill-api-go v1.4.2/go.mod h1:LchQqj/tHiZ3AU23geA+0EY9ia9L0MuzNIvqX9reipI= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= diff --git a/pkg/controller/provisioner.go b/pkg/controller/provisioner.go index dd114e93..4601995b 100644 --- a/pkg/controller/provisioner.go +++ b/pkg/controller/provisioner.go @@ -6,8 +6,6 @@ import ( "github.com/container-storage-interface/spec/lib/go/csi" "github.com/enix/dothill-storage-controller/pkg/common" - "github.com/pborman/uuid" - "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog" @@ -16,11 +14,17 @@ import ( // CreateVolume creates a new volume from the given request. The function is // idempotent. func (driver *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) { + if len(req.GetName()) == 0 { + return nil, status.Error(codes.InvalidArgument, "cannot create volume with empty name") + } + + parameters := req.GetParameters() + caps := req.GetVolumeCapabilities() err := driver.beginRoutine(&common.DriverCtx{ Req: req, Credentials: req.GetSecrets(), - Parameters: req.GetParameters(), - VolumeCaps: req.GetVolumeCapabilities(), + Parameters: ¶meters, + VolumeCaps: &caps, }) defer driver.endRoutine() if err != nil { @@ -28,12 +32,20 @@ func (driver *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeReq } size := req.GetCapacityRange().GetRequiredBytes() + if size == 0 { + size = 4096 + } + sizeStr := fmt.Sprintf("%diB", size) klog.Infof("received %s volume request\n", sizeStr) - volumeID := uuid.NewUUID().String()[:common.VolumeNameMaxLength] - klog.Infof("creating volume %s (size %s) in pool %s", volumeID, sizeStr, req.GetParameters()[common.PoolConfigKey]) - _, _, err = driver.dothillClient.CreateVolume(volumeID, sizeStr, req.GetParameters()[common.PoolConfigKey]) + volumeID := req.GetName() + if len(volumeID) > common.VolumeNameMaxLength { + volumeID = volumeID[:common.VolumeNameMaxLength] + } + + klog.Infof("creating volume %s (size %s) in pool %s", volumeID, sizeStr, parameters[common.PoolConfigKey]) + _, _, err = driver.dothillClient.CreateVolume(volumeID, sizeStr, parameters[common.PoolConfigKey]) if err != nil { return nil, err } @@ -41,7 +53,7 @@ func (driver *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeReq volume := &csi.CreateVolumeResponse{ Volume: &csi.Volume{ VolumeId: volumeID, - VolumeContext: req.GetParameters(), + VolumeContext: parameters, CapacityBytes: req.GetCapacityRange().GetRequiredBytes(), ContentSource: req.GetVolumeContentSource(), }, @@ -54,6 +66,10 @@ func (driver *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeReq // DeleteVolume deletes the given volume. The function is idempotent. func (driver *Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) { + if len(req.GetVolumeId()) == 0 { + return nil, status.Error(codes.InvalidArgument, "cannot delete volume with empty ID") + } + err := driver.beginRoutine(&common.DriverCtx{ Req: req, Credentials: req.GetSecrets(), @@ -64,57 +80,15 @@ func (driver *Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeReq } klog.Infof("deleting volume %s", req.GetVolumeId()) - _, _, err = driver.dothillClient.DeleteVolume(req.GetVolumeId()) + _, status, err := driver.dothillClient.DeleteVolume(req.GetVolumeId()) if err != nil { + if status != nil && status.ReturnCode == volumeNotFoundErrorCode { + klog.Infof("volume %s does not exist, assuming it has already been deleted", req.GetVolumeId()) + return &csi.DeleteVolumeResponse{}, nil + } return nil, err } klog.Infof("successfully deleted volume %s", req.GetVolumeId()) return &csi.DeleteVolumeResponse{}, nil } - -func runPreflightChecks(parameters map[string]string, capabilities []*csi.VolumeCapability) error { - checkIfKeyExistsInConfig := func(key string) error { - if parameters == nil { - return nil - } - - klog.V(2).Infof("checking for %s in storage class parameters", key) - _, ok := parameters[key] - if !ok { - return status.Errorf(codes.FailedPrecondition, "'%s' is missing from configuration", key) - } - return nil - } - - if err := checkIfKeyExistsInConfig(common.FsTypeConfigKey); err != nil { - return err - } - if err := checkIfKeyExistsInConfig(common.PoolConfigKey); err != nil { - return err - } - if err := checkIfKeyExistsInConfig(common.TargetIQNConfigKey); err != nil { - return err - } - if err := checkIfKeyExistsInConfig(common.PortalsConfigKey); err != nil { - return err - } - if err := checkIfKeyExistsInConfig(common.InitiatorNameConfigKey); err != nil { - if err2 := checkIfKeyExistsInConfig(common.UniqueInitiatorNameByPvcConfigKey); err2 != nil { - return errors.Wrap(err, err2.Error()) - } - } - if err := checkIfKeyExistsInConfig(common.APIAddressConfigKey); err != nil { - return err - } - - if capabilities != nil { - for _, capability := range capabilities { - if capability.GetAccessMode().GetMode() != csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER { - return status.Error(codes.FailedPrecondition, "dothill storage only supports ReadWriteOnce access mode") - } - } - } - - return nil -} diff --git a/pkg/node/go.mod b/pkg/node/go.mod index 7ffb232f..e3352150 100644 --- a/pkg/node/go.mod +++ b/pkg/node/go.mod @@ -6,6 +6,7 @@ require ( github.com/container-storage-interface/spec v1.2.0 github.com/enix/dothill-storage-controller/pkg/common v0.0.0-00010101000000-000000000000 github.com/kubernetes-csi/csi-lib-iscsi v0.0.0-20200118015005-959f12c91ca8 + github.com/pkg/errors v0.9.1 google.golang.org/grpc v1.26.0 k8s.io/klog v1.0.0 ) diff --git a/pkg/node/go.sum b/pkg/node/go.sum index 744cd787..50a80e6d 100644 --- a/pkg/node/go.sum +++ b/pkg/node/go.sum @@ -32,6 +32,8 @@ github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= diff --git a/pkg/node/node.go b/pkg/node/node.go index 2c531311..a292ceac 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -3,7 +3,6 @@ package node import ( "bufio" "context" - "errors" "fmt" "os" "os/exec" @@ -14,6 +13,7 @@ import ( "github.com/container-storage-interface/spec/lib/go/csi" "github.com/enix/dothill-storage-controller/pkg/common" "github.com/kubernetes-csi/csi-lib-iscsi/iscsi" + "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog" @@ -69,6 +69,16 @@ func (driver *Driver) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetC // NodePublishVolume mounts the volume mounted to the staging path to the target path func (driver *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) { + if len(req.GetVolumeId()) == 0 { + return nil, status.Error(codes.InvalidArgument, "cannot publish volume with empty id") + } + if len(req.GetTargetPath()) == 0 { + return nil, status.Error(codes.InvalidArgument, "cannot publish volume at an empty path") + } + if req.GetVolumeCapability() == nil { + return nil, status.Error(codes.InvalidArgument, "cannot publish volume without capabilities") + } + driver.beginRoutine(&common.DriverCtx{Req: req}) defer driver.endRoutine() klog.Infof("publishing volume %s", req.GetVolumeId()) @@ -133,6 +143,13 @@ func (driver *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublis // NodeUnpublishVolume unmounts the volume from the target path func (driver *Driver) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) { + if len(req.GetVolumeId()) == 0 { + return nil, status.Error(codes.InvalidArgument, "cannot unpublish volume with empty id") + } + if len(req.GetTargetPath()) == 0 { + return nil, status.Error(codes.InvalidArgument, "cannot publish volume at an empty path") + } + driver.beginRoutine(&common.DriverCtx{Req: req}) defer driver.endRoutine() klog.Infof("unpublishing volume %s", req.GetVolumeId()) @@ -152,7 +169,8 @@ func (driver *Driver) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpu klog.Infof("loading ISCSI connection info from %s", iscsiInfoPath) connector, err := iscsi.GetConnectorFromFile(iscsiInfoPath) if err != nil { - return nil, err + klog.Error(errors.Wrap(err, "assuming ISCSI connection is already closed")) + return &csi.NodeUnpublishVolumeResponse{}, nil } klog.Info("detaching ISCSI device") @@ -162,6 +180,9 @@ func (driver *Driver) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpu return nil, err } + klog.Infof("deleting ISCSI connection info file %s", iscsiInfoPath) + os.Remove(iscsiInfoPath) + klog.Info("successfully detached ISCSI device") return &csi.NodeUnpublishVolumeResponse{}, nil } diff --git a/test/config.yml b/test/config.yml new file mode 100644 index 00000000..d3b09cf1 --- /dev/null +++ b/test/config.yml @@ -0,0 +1,4 @@ +pool: B +fsType: ext4 +iqn: iqn.2015-11.com.hpe:storage.msa2050.18323cc9ed +portals: 10.14.84.211,10.14.84.212 diff --git a/test/csc b/test/csc new file mode 100755 index 00000000..ed6e95d1 --- /dev/null +++ b/test/csc @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +csc=$(which csc) + +fsType="ext4" +pool="B" +iqn="iqn.2015-11.com.hpe:storage.msa2050.18323cc9ed" +portals="10.14.84.211,10.14.84.212" + +function setup { + cd $(dirname $0) + export CSI_ENDPOINT="tcp://localhost:10000" + export X_CSI_SECRETS="apiAddress=${DOTHILL_API_ADDR}, username=${DOTHILL_USERNAME}, password=${DOTHILL_PASSWORD}" +} + +setup +${csc} --params "fsType=${fsType},pool=${pool},iqn=${iqn},portals=${portals}" $@ diff --git a/test/sanity b/test/sanity new file mode 100755 index 00000000..8a957b56 --- /dev/null +++ b/test/sanity @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +secretsFileTmpl="secrets.template.yml" +secretsFile="secrets.yml" + +function setup { + cd $(dirname $0) + envsubst < ${secretsFileTmpl} > ${secretsFile} +} + +function teardown { + rm secrets.yml +} + +setup +go test ../cmd/controller $@ +teardown diff --git a/test/secrets.template.yml b/test/secrets.template.yml new file mode 100644 index 00000000..f2d29c68 --- /dev/null +++ b/test/secrets.template.yml @@ -0,0 +1,28 @@ +CreateVolumeSecret: + username: $DOTHILL_USERNAME + password: $DOTHILL_PASSWORD + apiAddress: $DOTHILL_API_ADDR +DeleteVolumeSecret: + username: $DOTHILL_USERNAME + password: $DOTHILL_PASSWORD + apiAddress: $DOTHILL_API_ADDR +ControllerPublishVolumeSecret: + username: $DOTHILL_USERNAME + password: $DOTHILL_PASSWORD + apiAddress: $DOTHILL_API_ADDR +ControllerUnpublishVolumeSecret: + username: $DOTHILL_USERNAME + password: $DOTHILL_PASSWORD + apiAddress: $DOTHILL_API_ADDR +NodeStageVolumeSecret: + username: $DOTHILL_USERNAME + password: $DOTHILL_PASSWORD + apiAddress: $DOTHILL_API_ADDR +NodePublishVolumeSecret: + username: $DOTHILL_USERNAME + password: $DOTHILL_PASSWORD + apiAddress: $DOTHILL_API_ADDR +ControllerValidateVolumeCapabilitiesSecret: + username: $DOTHILL_USERNAME + password: $DOTHILL_PASSWORD + apiAddress: $DOTHILL_API_ADDR