From 234127439378c7450a069e8d536db29d0c6a3fa4 Mon Sep 17 00:00:00 2001
From: Cyrill Troxler <cyrilltroxler@gmail.com>
Date: Fri, 27 Jul 2018 21:37:32 +0200
Subject: [PATCH] Add fs prefix (directory)

This ensures the fs root is clean and does not mess with
the metadata. Also in the future this will allow for multiple
filesystems to be created in one bucket.
---
 pkg/s3/controllerserver.go | 11 +++++++----
 pkg/s3/mounter_goofys.go   |  3 ++-
 pkg/s3/mounter_s3backer.go |  9 ++++++++-
 pkg/s3/mounter_s3fs.go     |  2 +-
 pkg/s3/mounter_s3ql.go     |  4 +++-
 pkg/s3/s3-client.go        | 10 ++++++++++
 6 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/pkg/s3/controllerserver.go b/pkg/s3/controllerserver.go
index cb5bf08..6809115 100644
--- a/pkg/s3/controllerserver.go
+++ b/pkg/s3/controllerserver.go
@@ -61,7 +61,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
 		var b *bucket
 		b, err = cs.s3.client.getBucket(volumeID)
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("failed to get bucket: %v", err)
 		}
 		// Check if volume capacity requested is bigger than the already existing capacity
 		if capacityBytes > b.CapacityBytes {
@@ -69,16 +69,19 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
 		}
 	} else {
 		if err = cs.s3.client.createBucket(volumeID); err != nil {
-			glog.V(3).Infof("failed to create volume: %v", err)
-			return nil, err
+			return nil, fmt.Errorf("failed to create volume: %v", err)
+		}
+		if err = cs.s3.client.createPrefix(volumeID, fsPrefix); err != nil {
+			return nil, fmt.Errorf("failed to create prefix: %v", err)
 		}
 	}
 	b := &bucket{
 		Name:          volumeID,
 		CapacityBytes: capacityBytes,
+		FSPath:        fsPrefix,
 	}
 	if err := cs.s3.client.setBucket(b); err != nil {
-		return nil, err
+		return nil, fmt.Errorf("Error setting bucket metadata: %v", err)
 	}
 
 	glog.V(4).Infof("create volume %s", volumeID)
diff --git a/pkg/s3/mounter_goofys.go b/pkg/s3/mounter_goofys.go
index 326596e..6e93bb0 100644
--- a/pkg/s3/mounter_goofys.go
+++ b/pkg/s3/mounter_goofys.go
@@ -60,8 +60,9 @@ func (goofys *goofysMounter) Mount(source string, target string) error {
 
 	os.Setenv("AWS_ACCESS_KEY_ID", goofys.accessKeyID)
 	os.Setenv("AWS_SECRET_ACCESS_KEY", goofys.secretAccessKey)
+	fullPath := fmt.Sprintf("%s:%s", goofys.bucket.Name, goofys.bucket.FSPath)
 
-	_, _, err := goofysApi.Mount(context.Background(), goofys.bucket.Name, goofysCfg)
+	_, _, err := goofysApi.Mount(context.Background(), fullPath, goofysCfg)
 
 	if err != nil {
 		return fmt.Errorf("Error mounting via goofys: %s", err)
diff --git a/pkg/s3/mounter_s3backer.go b/pkg/s3/mounter_s3backer.go
index 3d43b4e..55d0379 100644
--- a/pkg/s3/mounter_s3backer.go
+++ b/pkg/s3/mounter_s3backer.go
@@ -2,6 +2,7 @@ package s3
 
 import (
 	"fmt"
+	"net/url"
 	"os"
 	"os/exec"
 	"path"
@@ -29,6 +30,11 @@ const (
 )
 
 func newS3backerMounter(bucket *bucket, cfg *Config) (Mounter, error) {
+	url, err := url.Parse(cfg.Endpoint)
+	if err != nil {
+		return nil, err
+	}
+	url.Path = path.Join(url.Path, bucket.Name, bucket.FSPath)
 	// s3backer cannot work with 0 size volumes
 	if bucket.CapacityBytes == 0 {
 		bucket.CapacityBytes = s3backerDefaultSize
@@ -88,8 +94,9 @@ func (s3backer *s3backerMounter) mountInit(path string) error {
 	args := []string{
 		// baseURL must end with /
 		fmt.Sprintf("--baseURL=%s/", s3backer.url),
-		fmt.Sprintf("--blockSize=%v", s3backerBlockSize),
+		fmt.Sprintf("--blockSize=%s", s3backerBlockSize),
 		fmt.Sprintf("--size=%v", s3backer.bucket.CapacityBytes),
+		fmt.Sprintf("--prefix=%s/", s3backer.bucket.FSPath),
 		"--listBlocks",
 		s3backer.bucket.Name,
 		path,
diff --git a/pkg/s3/mounter_s3fs.go b/pkg/s3/mounter_s3fs.go
index e6d6f42..e332557 100644
--- a/pkg/s3/mounter_s3fs.go
+++ b/pkg/s3/mounter_s3fs.go
@@ -39,7 +39,7 @@ func (s3fs *s3fsMounter) Mount(source string, target string) error {
 		return err
 	}
 	args := []string{
-		fmt.Sprintf("%s", s3fs.bucket.Name),
+		fmt.Sprintf("%s:/%s", s3fs.bucket.Name, s3fs.bucket.FSPath),
 		fmt.Sprintf("%s", target),
 		"-o", "sigv2",
 		"-o", "use_path_request_style",
diff --git a/pkg/s3/mounter_s3ql.go b/pkg/s3/mounter_s3ql.go
index 80facaa..1f90481 100644
--- a/pkg/s3/mounter_s3ql.go
+++ b/pkg/s3/mounter_s3ql.go
@@ -49,7 +49,9 @@ func newS3qlMounter(b *bucket, cfg *Config) (Mounter, error) {
 		ssl:        ssl,
 	}
 
-	url.Path = path.Join(url.Path, b.Name)
+	// s3ql requires a trailing slash or it will just
+	// prepend the fspath to the s3ql files
+	url.Path = path.Join(url.Path, b.Name, b.FSPath) + "/"
 	s3ql.bucketURL = url.String()
 
 	if !ssl {
diff --git a/pkg/s3/s3-client.go b/pkg/s3/s3-client.go
index 69dc676..e3c76bc 100644
--- a/pkg/s3/s3-client.go
+++ b/pkg/s3/s3-client.go
@@ -13,6 +13,7 @@ import (
 
 const (
 	metadataName = ".metadata.json"
+	fsPrefix     = "csi-fs"
 )
 
 type s3Client struct {
@@ -22,6 +23,7 @@ type s3Client struct {
 
 type bucket struct {
 	Name          string
+	FSPath        string
 	CapacityBytes int64
 }
 
@@ -54,6 +56,14 @@ func (client *s3Client) createBucket(bucketName string) error {
 	return client.minio.MakeBucket(bucketName, client.cfg.Region)
 }
 
+func (client *s3Client) createPrefix(bucketName string, prefix string) error {
+	_, err := client.minio.PutObject(bucketName, prefix+"/", bytes.NewReader([]byte("")), 0, minio.PutObjectOptions{})
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
 func (client *s3Client) removeBucket(bucketName string) error {
 	if err := client.emptyBucket(bucketName); err != nil {
 		return err