* Implemented AmazonS3PathPrefix

* Remove unecessary method

* fix for test

* fix for test which are failing

* fix for test which are failing

* fix for test

Co-authored-by: Dusan Panic <dusan@salestrekker.com>
Этот коммит содержится в:
Dušan Panić
2020-07-07 00:20:13 +02:00
коммит произвёл GitHub
родитель 70084f107c
Коммит 83a80a2422
6 изменённых файлов: 52 добавлений и 18 удалений

Просмотреть файл

@@ -404,6 +404,7 @@ func TestS3TestConnection(t *testing.T) {
AmazonS3Bucket: model.NewString(""), AmazonS3Bucket: model.NewString(""),
AmazonS3Endpoint: model.NewString(s3Endpoint), AmazonS3Endpoint: model.NewString(s3Endpoint),
AmazonS3Region: model.NewString(""), AmazonS3Region: model.NewString(""),
AmazonS3PathPrefix: model.NewString(""),
AmazonS3SSL: model.NewBool(false), AmazonS3SSL: model.NewBool(false),
}, },
} }
@@ -420,6 +421,7 @@ func TestS3TestConnection(t *testing.T) {
// If this fails, check the test configuration to ensure minio is setup with the // If this fails, check the test configuration to ensure minio is setup with the
// `mattermost-test` bucket defined by model.MINIO_BUCKET. // `mattermost-test` bucket defined by model.MINIO_BUCKET.
*config.FileSettings.AmazonS3Bucket = model.MINIO_BUCKET *config.FileSettings.AmazonS3Bucket = model.MINIO_BUCKET
config.FileSettings.AmazonS3PathPrefix = model.NewString("")
*config.FileSettings.AmazonS3Region = "us-east-1" *config.FileSettings.AmazonS3Region = "us-east-1"
_, resp = th.SystemAdminClient.TestS3Connection(&config) _, resp = th.SystemAdminClient.TestS3Connection(&config)
CheckOKStatus(t, resp) CheckOKStatus(t, resp)

Просмотреть файл

@@ -498,6 +498,7 @@ func TestPluginSync(t *testing.T) {
cfg.FileSettings.AmazonS3AccessKeyId = model.NewString(model.MINIO_ACCESS_KEY) cfg.FileSettings.AmazonS3AccessKeyId = model.NewString(model.MINIO_ACCESS_KEY)
cfg.FileSettings.AmazonS3SecretAccessKey = model.NewString(model.MINIO_SECRET_KEY) cfg.FileSettings.AmazonS3SecretAccessKey = model.NewString(model.MINIO_SECRET_KEY)
cfg.FileSettings.AmazonS3Bucket = model.NewString(model.MINIO_BUCKET) cfg.FileSettings.AmazonS3Bucket = model.NewString(model.MINIO_BUCKET)
cfg.FileSettings.AmazonS3PathPrefix = model.NewString("")
cfg.FileSettings.AmazonS3Endpoint = model.NewString(s3Endpoint) cfg.FileSettings.AmazonS3Endpoint = model.NewString(s3Endpoint)
cfg.FileSettings.AmazonS3Region = model.NewString("") cfg.FileSettings.AmazonS3Region = model.NewString("")
cfg.FileSettings.AmazonS3SSL = model.NewBool(false) cfg.FileSettings.AmazonS3SSL = model.NewBool(false)

Просмотреть файл

@@ -1265,6 +1265,7 @@ type FileSettings struct {
AmazonS3AccessKeyId *string `restricted:"true"` AmazonS3AccessKeyId *string `restricted:"true"`
AmazonS3SecretAccessKey *string `restricted:"true"` AmazonS3SecretAccessKey *string `restricted:"true"`
AmazonS3Bucket *string `restricted:"true"` AmazonS3Bucket *string `restricted:"true"`
AmazonS3PathPrefix *string `restricted:"true"`
AmazonS3Region *string `restricted:"true"` AmazonS3Region *string `restricted:"true"`
AmazonS3Endpoint *string `restricted:"true"` AmazonS3Endpoint *string `restricted:"true"`
AmazonS3SSL *bool `restricted:"true"` AmazonS3SSL *bool `restricted:"true"`
@@ -1329,6 +1330,10 @@ func (s *FileSettings) SetDefaults(isUpdate bool) {
s.AmazonS3Bucket = NewString("") s.AmazonS3Bucket = NewString("")
} }
if s.AmazonS3PathPrefix == nil {
s.AmazonS3PathPrefix = NewString("")
}
if s.AmazonS3Region == nil { if s.AmazonS3Region == nil {
s.AmazonS3Region = NewString("") s.AmazonS3Region = NewString("")
} }

Просмотреть файл

@@ -34,15 +34,16 @@ func NewFileBackend(settings *model.FileSettings, enableComplianceFeatures bool)
switch *settings.DriverName { switch *settings.DriverName {
case model.IMAGE_DRIVER_S3: case model.IMAGE_DRIVER_S3:
return &S3FileBackend{ return &S3FileBackend{
endpoint: *settings.AmazonS3Endpoint, endpoint: *settings.AmazonS3Endpoint,
accessKey: *settings.AmazonS3AccessKeyId, accessKey: *settings.AmazonS3AccessKeyId,
secretKey: *settings.AmazonS3SecretAccessKey, secretKey: *settings.AmazonS3SecretAccessKey,
secure: settings.AmazonS3SSL == nil || *settings.AmazonS3SSL, secure: settings.AmazonS3SSL == nil || *settings.AmazonS3SSL,
signV2: settings.AmazonS3SignV2 != nil && *settings.AmazonS3SignV2, signV2: settings.AmazonS3SignV2 != nil && *settings.AmazonS3SignV2,
region: *settings.AmazonS3Region, region: *settings.AmazonS3Region,
bucket: *settings.AmazonS3Bucket, bucket: *settings.AmazonS3Bucket,
encrypt: settings.AmazonS3SSE != nil && *settings.AmazonS3SSE && enableComplianceFeatures, pathPrefix: *settings.AmazonS3PathPrefix,
trace: settings.AmazonS3Trace != nil && *settings.AmazonS3Trace, encrypt: settings.AmazonS3SSE != nil && *settings.AmazonS3SSE && enableComplianceFeatures,
trace: settings.AmazonS3Trace != nil && *settings.AmazonS3Trace,
}, nil }, nil
case model.IMAGE_DRIVER_LOCAL: case model.IMAGE_DRIVER_LOCAL:
return &LocalFileBackend{ return &LocalFileBackend{

Просмотреть файл

@@ -76,6 +76,7 @@ func runBackendTest(t *testing.T, encrypt bool) {
AmazonS3Bucket: model.NewString(model.MINIO_BUCKET), AmazonS3Bucket: model.NewString(model.MINIO_BUCKET),
AmazonS3Region: model.NewString(""), AmazonS3Region: model.NewString(""),
AmazonS3Endpoint: model.NewString(s3Endpoint), AmazonS3Endpoint: model.NewString(s3Endpoint),
AmazonS3PathPrefix: model.NewString(""),
AmazonS3SSL: model.NewBool(false), AmazonS3SSL: model.NewBool(false),
AmazonS3SSE: model.NewBool(encrypt), AmazonS3SSE: model.NewBool(encrypt),
}, },

Просмотреть файл

@@ -21,15 +21,16 @@ import (
) )
type S3FileBackend struct { type S3FileBackend struct {
endpoint string endpoint string
accessKey string accessKey string
secretKey string secretKey string
secure bool secure bool
signV2 bool signV2 bool
region string region string
bucket string bucket string
encrypt bool pathPrefix string
trace bool encrypt bool
trace bool
} }
// Similar to s3.New() but allows initialization of signature v2 or signature v4 client. // Similar to s3.New() but allows initialization of signature v2 or signature v4 client.
@@ -89,10 +90,13 @@ func (b *S3FileBackend) Reader(path string) (ReadCloseSeeker, *model.AppError) {
if err != nil { if err != nil {
return nil, model.NewAppError("Reader", "api.file.reader.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("Reader", "api.file.reader.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
path = filepath.Join(b.pathPrefix, path)
minioObject, err := s3Clnt.GetObject(b.bucket, path, s3.GetObjectOptions{}) minioObject, err := s3Clnt.GetObject(b.bucket, path, s3.GetObjectOptions{})
if err != nil { if err != nil {
return nil, model.NewAppError("Reader", "api.file.reader.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("Reader", "api.file.reader.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
return minioObject, nil return minioObject, nil
} }
@@ -101,10 +105,13 @@ func (b *S3FileBackend) ReadFile(path string) ([]byte, *model.AppError) {
if err != nil { if err != nil {
return nil, model.NewAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
path = filepath.Join(b.pathPrefix, path)
minioObject, err := s3Clnt.GetObject(b.bucket, path, s3.GetObjectOptions{}) minioObject, err := s3Clnt.GetObject(b.bucket, path, s3.GetObjectOptions{})
if err != nil { if err != nil {
return nil, model.NewAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
defer minioObject.Close() defer minioObject.Close()
if f, err := ioutil.ReadAll(minioObject); err != nil { if f, err := ioutil.ReadAll(minioObject); err != nil {
return nil, model.NewAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("ReadFile", "api.file.read_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -119,6 +126,8 @@ func (b *S3FileBackend) FileExists(path string) (bool, *model.AppError) {
if err != nil { if err != nil {
return false, model.NewAppError("FileExists", "api.file.file_exists.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return false, model.NewAppError("FileExists", "api.file.file_exists.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
path = filepath.Join(b.pathPrefix, path)
_, err = s3Clnt.StatObject(b.bucket, path, s3.StatObjectOptions{}) _, err = s3Clnt.StatObject(b.bucket, path, s3.StatObjectOptions{})
if err == nil { if err == nil {
@@ -138,11 +147,15 @@ func (b *S3FileBackend) CopyFile(oldPath, newPath string) *model.AppError {
return model.NewAppError("copyFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("copyFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
oldPath = filepath.Join(b.pathPrefix, oldPath)
newPath = filepath.Join(b.pathPrefix, newPath)
source := s3.NewSourceInfo(b.bucket, oldPath, nil) source := s3.NewSourceInfo(b.bucket, oldPath, nil)
destination, err := s3.NewDestinationInfo(b.bucket, newPath, encrypt.NewSSE(), nil) destination, err := s3.NewDestinationInfo(b.bucket, newPath, encrypt.NewSSE(), nil)
if err != nil { if err != nil {
return model.NewAppError("copyFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("copyFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
if err = s3Clnt.CopyObject(destination, source); err != nil { if err = s3Clnt.CopyObject(destination, source); err != nil {
return model.NewAppError("copyFile", "api.file.move_file.copy_within_s3.app_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("copyFile", "api.file.move_file.copy_within_s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
@@ -155,17 +168,23 @@ func (b *S3FileBackend) MoveFile(oldPath, newPath string) *model.AppError {
return model.NewAppError("moveFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("moveFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
oldPath = filepath.Join(b.pathPrefix, oldPath)
newPath = filepath.Join(b.pathPrefix, newPath)
source := s3.NewSourceInfo(b.bucket, oldPath, nil) source := s3.NewSourceInfo(b.bucket, oldPath, nil)
destination, err := s3.NewDestinationInfo(b.bucket, newPath, encrypt.NewSSE(), nil) destination, err := s3.NewDestinationInfo(b.bucket, newPath, encrypt.NewSSE(), nil)
if err != nil { if err != nil {
return model.NewAppError("moveFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("moveFile", "api.file.write_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
if err = s3Clnt.CopyObject(destination, source); err != nil { if err = s3Clnt.CopyObject(destination, source); err != nil {
return model.NewAppError("moveFile", "api.file.move_file.copy_within_s3.app_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("moveFile", "api.file.move_file.copy_within_s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
if err = s3Clnt.RemoveObject(b.bucket, oldPath); err != nil { if err = s3Clnt.RemoveObject(b.bucket, oldPath); err != nil {
return model.NewAppError("moveFile", "api.file.move_file.delete_from_s3.app_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("moveFile", "api.file.move_file.delete_from_s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
return nil return nil
} }
@@ -176,6 +195,7 @@ func (b *S3FileBackend) WriteFile(fr io.Reader, path string) (int64, *model.AppE
} }
var contentType string var contentType string
path = filepath.Join(b.pathPrefix, path)
if ext := filepath.Ext(path); model.IsFileExtImage(ext) { if ext := filepath.Ext(path); model.IsFileExtImage(ext) {
contentType = model.GetImageMimeType(ext) contentType = model.GetImageMimeType(ext)
} else { } else {
@@ -202,6 +222,7 @@ func (b *S3FileBackend) RemoveFile(path string) *model.AppError {
return model.NewAppError("RemoveFile", "utils.file.remove_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("RemoveFile", "utils.file.remove_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
path = filepath.Join(b.pathPrefix, path)
if err := s3Clnt.RemoveObject(b.bucket, path); err != nil { if err := s3Clnt.RemoveObject(b.bucket, path); err != nil {
return model.NewAppError("RemoveFile", "utils.file.remove_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError) return model.NewAppError("RemoveFile", "utils.file.remove_file.s3.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
@@ -240,11 +261,13 @@ func (b *S3FileBackend) ListDirectory(path string) (*[]string, *model.AppError)
doneCh := make(chan struct{}) doneCh := make(chan struct{})
defer close(doneCh) defer close(doneCh)
path = filepath.Join(b.pathPrefix, path)
if !strings.HasSuffix(path, "/") && len(path) > 0 { if !strings.HasSuffix(path, "/") && len(path) > 0 {
// s3Clnt returns only the path itself when "/" is not present // s3Clnt returns only the path itself when "/" is not present
// appending "/" to make it consistent across all filesstores // appending "/" to make it consistent across all filesstores
path = path + "/" path = path + "/"
} }
for object := range s3Clnt.ListObjects(b.bucket, path, false, doneCh) { for object := range s3Clnt.ListObjects(b.bucket, path, false, doneCh) {
if object.Err != nil { if object.Err != nil {
return nil, model.NewAppError("ListDirectory", "utils.file.list_directory.s3.app_error", nil, object.Err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("ListDirectory", "utils.file.list_directory.s3.app_error", nil, object.Err.Error(), http.StatusInternalServerError)
@@ -263,6 +286,7 @@ func (b *S3FileBackend) RemoveDirectory(path string) *model.AppError {
doneCh := make(chan struct{}) doneCh := make(chan struct{})
path = filepath.Join(b.pathPrefix, path)
for err := range s3Clnt.RemoveObjects(b.bucket, getPathsFromObjectInfos(s3Clnt.ListObjects(b.bucket, path, true, doneCh))) { for err := range s3Clnt.RemoveObjects(b.bucket, getPathsFromObjectInfos(s3Clnt.ListObjects(b.bucket, path, true, doneCh))) {
if err.Err != nil { if err.Err != nil {
doneCh <- struct{}{} doneCh <- struct{}{}