diff --git a/README.md b/README.md index a1a6c63..a820a1e 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,27 @@ parameters: If the bucket is specified, it will still be created if it does not exist on the backend. Every volume will get its own prefix within the bucket which matches the volume ID. When deleting a volume, also just the prefix will be deleted. +#### Using an existing bucket with custom prefix + +If you have an existing bucket and with or without a prefix (subpath), you can specify to use a prefixed configuration by setting the parameters as: + +```yaml +kind: StorageClass +apiVersion: storage.k8s.io/v1 +metadata: + name: csi-s3-existing-bucket +provisioner: ch.ctrox.csi.s3-driver +reclaimPolicy: Retain +parameters: + mounter: rclone + bucket: some-existing-bucket-name + # 'usePrefix' must be true in order to enable the prefix feature and to avoid the removal of the prefix or bucket + usePrefix: "true" + # 'prefix' can be empty (it will mount on the root of the bucket), an existing prefix or a new one. + prefix: custom-prefix +``` +**Note:** all volumes created with this `StorageClass` will always be mounted to the same bucket and path, meaning they will be identical. + ### Mounter As S3 is not a real file system there are some limitations to consider here. Depending on what mounter you are using, you will have different levels of POSIX compability. Also depending on what S3 storage backend you are using there are not always [consistency guarantees](https://github.com/gaul/are-we-consistent-yet#observed-consistency). diff --git a/pkg/driver/controllerserver.go b/pkg/driver/controllerserver.go index 319c444..1a2d8ac 100644 --- a/pkg/driver/controllerserver.go +++ b/pkg/driver/controllerserver.go @@ -22,6 +22,7 @@ import ( "fmt" "io" "path" + "strconv" "strings" "github.com/ctrox/csi-s3/pkg/mounter" @@ -50,6 +51,8 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol volumeID := sanitizeVolumeID(req.GetName()) bucketName := volumeID prefix := "" + usePrefix, usePrefixError := strconv.ParseBool(params[mounter.UsePrefix]) + defaultFsPath := defaultFsPath // check if bucket name is overridden if nameOverride, ok := params[mounter.BucketKey]; ok { @@ -58,6 +61,16 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol volumeID = path.Join(bucketName, prefix) } + // check if volume prefix is overridden + if overridePrefix := usePrefix; usePrefixError == nil && overridePrefix { + prefix = "" + defaultFsPath = "" + if prefixOverride, ok := params[mounter.VolumePrefix]; ok && prefixOverride != "" { + prefix = prefixOverride + } + volumeID = path.Join(bucketName, prefix) + } + if err := cs.Driver.ValidateControllerServiceRequest(csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME); err != nil { glog.V(3).Infof("invalid create volume req: %v", req) return nil, err @@ -75,6 +88,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol meta := &s3.FSMeta{ BucketName: bucketName, + UsePrefix: usePrefix, Prefix: prefix, Mounter: mounterType, CapacityBytes: capacityBytes, @@ -108,7 +122,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol } } - if err = client.CreatePrefix(bucketName, path.Join(prefix, defaultFsPath)); err != nil { + if err = client.CreatePrefix(bucketName, path.Join(prefix, defaultFsPath)); err != nil && prefix != "" { return nil, fmt.Errorf("failed to create prefix %s: %v", path.Join(prefix, defaultFsPath), err) } @@ -153,7 +167,11 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol } var deleteErr error - if prefix == "" { + if meta.UsePrefix { + // UsePrefix is true, we do not delete anything + glog.V(4).Infof("Nothing to remove for %s", bucketName) + return &csi.DeleteVolumeResponse{}, nil + } else if prefix == "" { // prefix is empty, we delete the whole bucket if err := client.RemoveBucket(bucketName); err != nil { deleteErr = err diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go index e3c0f8d..c28079b 100644 --- a/pkg/driver/driver.go +++ b/pkg/driver/driver.go @@ -33,7 +33,7 @@ type driver struct { } var ( - vendorVersion = "v1.2.0-rc.1" + vendorVersion = "v1.2.0-rc.2" driverName = "ch.ctrox.csi.s3-driver" ) diff --git a/pkg/mounter/mounter.go b/pkg/mounter/mounter.go index dcfe021..d441fe1 100644 --- a/pkg/mounter/mounter.go +++ b/pkg/mounter/mounter.go @@ -31,6 +31,8 @@ const ( rcloneMounterType = "rclone" TypeKey = "mounter" BucketKey = "bucket" + VolumePrefix = "prefix" + UsePrefix = "usePrefix" ) // New returns a new mounter depending on the mounterType parameter diff --git a/pkg/s3/client.go b/pkg/s3/client.go index 87d5df1..85165f6 100644 --- a/pkg/s3/client.go +++ b/pkg/s3/client.go @@ -5,13 +5,12 @@ import ( "context" "encoding/json" "fmt" - "io" - "net/url" - "path" - "github.com/golang/glog" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" + "io" + "net/url" + "path" ) const ( @@ -36,6 +35,7 @@ type Config struct { type FSMeta struct { BucketName string `json:"Name"` Prefix string `json:"Prefix"` + UsePrefix bool `json:"UsePrefix"` Mounter string `json:"Mounter"` FSPath string `json:"FSPath"` CapacityBytes int64 `json:"CapacityBytes"`