mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
Add NAS gateway support (#5516)
This commit is contained in:
parent
926e480156
commit
25107c2e11
1
.gitignore
vendored
1
.gitignore
vendored
@ -24,3 +24,4 @@ prime/
|
|||||||
snap/.snapcraft/
|
snap/.snapcraft/
|
||||||
stage/
|
stage/
|
||||||
.sia_temp/
|
.sia_temp/
|
||||||
|
buildcoveragecoverage.txt
|
@ -90,7 +90,7 @@ func initBucketPolicies(objAPI ObjectLayer) (*bucketPolicies, error) {
|
|||||||
policies := make(map[string]policy.BucketAccessPolicy)
|
policies := make(map[string]policy.BucketAccessPolicy)
|
||||||
// Loads bucket policy.
|
// Loads bucket policy.
|
||||||
for _, bucket := range buckets {
|
for _, bucket := range buckets {
|
||||||
bp, pErr := readBucketPolicy(bucket.Name, objAPI)
|
bp, pErr := ReadBucketPolicy(bucket.Name, objAPI)
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
// net.Dial fails for rpc client or any
|
// net.Dial fails for rpc client or any
|
||||||
// other unexpected errors during net.Dial.
|
// other unexpected errors during net.Dial.
|
||||||
@ -130,9 +130,9 @@ func readBucketPolicyJSON(bucket string, objAPI ObjectLayer) (bucketPolicyReader
|
|||||||
return &buffer, nil
|
return &buffer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readBucketPolicy - reads bucket policy for an input bucket, returns BucketPolicyNotFound
|
// ReadBucketPolicy - reads bucket policy for an input bucket, returns BucketPolicyNotFound
|
||||||
// if bucket policy is not found. This function also parses the bucket policy into an object.
|
// if bucket policy is not found. This function also parses the bucket policy into an object.
|
||||||
func readBucketPolicy(bucket string, objAPI ObjectLayer) (policy.BucketAccessPolicy, error) {
|
func ReadBucketPolicy(bucket string, objAPI ObjectLayer) (policy.BucketAccessPolicy, error) {
|
||||||
// Read bucket policy JSON.
|
// Read bucket policy JSON.
|
||||||
bucketPolicyReader, err := readBucketPolicyJSON(bucket, objAPI)
|
bucketPolicyReader, err := readBucketPolicyJSON(bucket, objAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -44,7 +44,7 @@ func TestReadFSMetadata(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
|
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
|
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
@ -79,7 +79,7 @@ func TestWriteFSMetadata(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
|
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
|
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
|
@ -36,22 +36,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Returns EXPORT/.minio.sys/multipart/SHA256/UPLOADID
|
// Returns EXPORT/.minio.sys/multipart/SHA256/UPLOADID
|
||||||
func (fs *fsObjects) getUploadIDDir(bucket, object, uploadID string) string {
|
func (fs *FSObjects) getUploadIDDir(bucket, object, uploadID string) string {
|
||||||
return pathJoin(fs.fsPath, minioMetaMultipartBucket, getSHA256Hash([]byte(pathJoin(bucket, object))), uploadID)
|
return pathJoin(fs.fsPath, minioMetaMultipartBucket, getSHA256Hash([]byte(pathJoin(bucket, object))), uploadID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns EXPORT/.minio.sys/multipart/SHA256
|
// Returns EXPORT/.minio.sys/multipart/SHA256
|
||||||
func (fs *fsObjects) getMultipartSHADir(bucket, object string) string {
|
func (fs *FSObjects) getMultipartSHADir(bucket, object string) string {
|
||||||
return pathJoin(fs.fsPath, minioMetaMultipartBucket, getSHA256Hash([]byte(pathJoin(bucket, object))))
|
return pathJoin(fs.fsPath, minioMetaMultipartBucket, getSHA256Hash([]byte(pathJoin(bucket, object))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns partNumber.etag
|
// Returns partNumber.etag
|
||||||
func (fs *fsObjects) encodePartFile(partNumber int, etag string) string {
|
func (fs *FSObjects) encodePartFile(partNumber int, etag string) string {
|
||||||
return fmt.Sprintf("%.5d.%s", partNumber, etag)
|
return fmt.Sprintf("%.5d.%s", partNumber, etag)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns partNumber and etag
|
// Returns partNumber and etag
|
||||||
func (fs *fsObjects) decodePartFile(name string) (partNumber int, etag string, err error) {
|
func (fs *FSObjects) decodePartFile(name string) (partNumber int, etag string, err error) {
|
||||||
result := strings.Split(name, ".")
|
result := strings.Split(name, ".")
|
||||||
if len(result) != 2 {
|
if len(result) != 2 {
|
||||||
return 0, "", errUnexpected
|
return 0, "", errUnexpected
|
||||||
@ -64,7 +64,7 @@ func (fs *fsObjects) decodePartFile(name string) (partNumber int, etag string, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Appends parts to an appendFile sequentially.
|
// Appends parts to an appendFile sequentially.
|
||||||
func (fs *fsObjects) backgroundAppend(bucket, object, uploadID string) {
|
func (fs *FSObjects) backgroundAppend(bucket, object, uploadID string) {
|
||||||
fs.appendFileMapMu.Lock()
|
fs.appendFileMapMu.Lock()
|
||||||
file := fs.appendFileMap[uploadID]
|
file := fs.appendFileMap[uploadID]
|
||||||
if file == nil {
|
if file == nil {
|
||||||
@ -121,7 +121,7 @@ func (fs *fsObjects) backgroundAppend(bucket, object, uploadID string) {
|
|||||||
|
|
||||||
// ListMultipartUploads - lists all the uploadIDs for the specified object.
|
// ListMultipartUploads - lists all the uploadIDs for the specified object.
|
||||||
// We do not support prefix based listing.
|
// We do not support prefix based listing.
|
||||||
func (fs *fsObjects) ListMultipartUploads(bucket, object, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (result ListMultipartsInfo, e error) {
|
func (fs *FSObjects) ListMultipartUploads(bucket, object, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (result ListMultipartsInfo, e error) {
|
||||||
if err := checkListMultipartArgs(bucket, object, keyMarker, uploadIDMarker, delimiter, fs); err != nil {
|
if err := checkListMultipartArgs(bucket, object, keyMarker, uploadIDMarker, delimiter, fs); err != nil {
|
||||||
return result, toObjectErr(errors.Trace(err))
|
return result, toObjectErr(errors.Trace(err))
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ func (fs *fsObjects) ListMultipartUploads(bucket, object, keyMarker, uploadIDMar
|
|||||||
// subsequent request each UUID is unique.
|
// subsequent request each UUID is unique.
|
||||||
//
|
//
|
||||||
// Implements S3 compatible initiate multipart API.
|
// Implements S3 compatible initiate multipart API.
|
||||||
func (fs *fsObjects) NewMultipartUpload(bucket, object string, meta map[string]string) (string, error) {
|
func (fs *FSObjects) NewMultipartUpload(bucket, object string, meta map[string]string) (string, error) {
|
||||||
if err := checkNewMultipartArgs(bucket, object, fs); err != nil {
|
if err := checkNewMultipartArgs(bucket, object, fs); err != nil {
|
||||||
return "", toObjectErr(err, bucket)
|
return "", toObjectErr(err, bucket)
|
||||||
}
|
}
|
||||||
@ -238,7 +238,7 @@ func (fs *fsObjects) NewMultipartUpload(bucket, object string, meta map[string]s
|
|||||||
// CopyObjectPart - similar to PutObjectPart but reads data from an existing
|
// CopyObjectPart - similar to PutObjectPart but reads data from an existing
|
||||||
// object. Internally incoming data is written to '.minio.sys/tmp' location
|
// object. Internally incoming data is written to '.minio.sys/tmp' location
|
||||||
// and safely renamed to '.minio.sys/multipart' for reach parts.
|
// and safely renamed to '.minio.sys/multipart' for reach parts.
|
||||||
func (fs *fsObjects) CopyObjectPart(srcBucket, srcObject, dstBucket, dstObject, uploadID string, partID int,
|
func (fs *FSObjects) CopyObjectPart(srcBucket, srcObject, dstBucket, dstObject, uploadID string, partID int,
|
||||||
startOffset int64, length int64, metadata map[string]string, srcEtag string) (pi PartInfo, e error) {
|
startOffset int64, length int64, metadata map[string]string, srcEtag string) (pi PartInfo, e error) {
|
||||||
|
|
||||||
if err := checkNewMultipartArgs(srcBucket, srcObject, fs); err != nil {
|
if err := checkNewMultipartArgs(srcBucket, srcObject, fs); err != nil {
|
||||||
@ -277,7 +277,7 @@ func (fs *fsObjects) CopyObjectPart(srcBucket, srcObject, dstBucket, dstObject,
|
|||||||
// an ongoing multipart transaction. Internally incoming data is
|
// an ongoing multipart transaction. Internally incoming data is
|
||||||
// written to '.minio.sys/tmp' location and safely renamed to
|
// written to '.minio.sys/tmp' location and safely renamed to
|
||||||
// '.minio.sys/multipart' for reach parts.
|
// '.minio.sys/multipart' for reach parts.
|
||||||
func (fs *fsObjects) PutObjectPart(bucket, object, uploadID string, partID int, data *hash.Reader) (pi PartInfo, e error) {
|
func (fs *FSObjects) PutObjectPart(bucket, object, uploadID string, partID int, data *hash.Reader) (pi PartInfo, e error) {
|
||||||
if err := checkPutObjectPartArgs(bucket, object, fs); err != nil {
|
if err := checkPutObjectPartArgs(bucket, object, fs); err != nil {
|
||||||
return pi, toObjectErr(errors.Trace(err), bucket)
|
return pi, toObjectErr(errors.Trace(err), bucket)
|
||||||
}
|
}
|
||||||
@ -358,7 +358,7 @@ func (fs *fsObjects) PutObjectPart(bucket, object, uploadID string, partID int,
|
|||||||
// Implements S3 compatible ListObjectParts API. The resulting
|
// Implements S3 compatible ListObjectParts API. The resulting
|
||||||
// ListPartsInfo structure is unmarshalled directly into XML and
|
// ListPartsInfo structure is unmarshalled directly into XML and
|
||||||
// replied back to the client.
|
// replied back to the client.
|
||||||
func (fs *fsObjects) ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (result ListPartsInfo, e error) {
|
func (fs *FSObjects) ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (result ListPartsInfo, e error) {
|
||||||
if err := checkListPartsArgs(bucket, object, fs); err != nil {
|
if err := checkListPartsArgs(bucket, object, fs); err != nil {
|
||||||
return result, toObjectErr(errors.Trace(err))
|
return result, toObjectErr(errors.Trace(err))
|
||||||
}
|
}
|
||||||
@ -460,7 +460,7 @@ func (fs *fsObjects) ListObjectParts(bucket, object, uploadID string, partNumber
|
|||||||
// md5sums of all the parts.
|
// md5sums of all the parts.
|
||||||
//
|
//
|
||||||
// Implements S3 compatible Complete multipart API.
|
// Implements S3 compatible Complete multipart API.
|
||||||
func (fs *fsObjects) CompleteMultipartUpload(bucket string, object string, uploadID string, parts []CompletePart) (oi ObjectInfo, e error) {
|
func (fs *FSObjects) CompleteMultipartUpload(bucket string, object string, uploadID string, parts []CompletePart) (oi ObjectInfo, e error) {
|
||||||
if err := checkCompleteMultipartArgs(bucket, object, fs); err != nil {
|
if err := checkCompleteMultipartArgs(bucket, object, fs); err != nil {
|
||||||
return oi, toObjectErr(err)
|
return oi, toObjectErr(err)
|
||||||
}
|
}
|
||||||
@ -634,7 +634,7 @@ func (fs *fsObjects) CompleteMultipartUpload(bucket string, object string, uploa
|
|||||||
// that this is an atomic idempotent operation. Subsequent calls have
|
// that this is an atomic idempotent operation. Subsequent calls have
|
||||||
// no affect and further requests to the same uploadID would not be
|
// no affect and further requests to the same uploadID would not be
|
||||||
// honored.
|
// honored.
|
||||||
func (fs *fsObjects) AbortMultipartUpload(bucket, object, uploadID string) error {
|
func (fs *FSObjects) AbortMultipartUpload(bucket, object, uploadID string) error {
|
||||||
if err := checkAbortMultipartArgs(bucket, object, fs); err != nil {
|
if err := checkAbortMultipartArgs(bucket, object, fs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -666,7 +666,7 @@ func (fs *fsObjects) AbortMultipartUpload(bucket, object, uploadID string) error
|
|||||||
// Removes multipart uploads if any older than `expiry` duration
|
// Removes multipart uploads if any older than `expiry` duration
|
||||||
// on all buckets for every `cleanupInterval`, this function is
|
// on all buckets for every `cleanupInterval`, this function is
|
||||||
// blocking and should be run in a go-routine.
|
// blocking and should be run in a go-routine.
|
||||||
func (fs *fsObjects) cleanupStaleMultipartUploads(cleanupInterval, expiry time.Duration, doneCh chan struct{}) {
|
func (fs *FSObjects) cleanupStaleMultipartUploads(cleanupInterval, expiry time.Duration, doneCh chan struct{}) {
|
||||||
ticker := time.NewTicker(cleanupInterval)
|
ticker := time.NewTicker(cleanupInterval)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -33,7 +33,7 @@ func TestFSCleanupMultipartUploadsInRoutine(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
|
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
|
|
||||||
// Close the go-routine, we are going to
|
// Close the go-routine, we are going to
|
||||||
// manually start it and test in this test case.
|
// manually start it and test in this test case.
|
||||||
@ -73,7 +73,7 @@ func TestNewMultipartUploadFaultyDisk(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
|
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ func TestPutObjectPartFaultyDisk(t *testing.T) {
|
|||||||
disk := filepath.Join(globalTestTmpDir, "minio-"+nextSuffix())
|
disk := filepath.Join(globalTestTmpDir, "minio-"+nextSuffix())
|
||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
data := []byte("12345")
|
data := []byte("12345")
|
||||||
@ -134,7 +134,7 @@ func TestCompleteMultipartUploadFaultyDisk(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
|
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
data := []byte("12345")
|
data := []byte("12345")
|
||||||
@ -166,7 +166,7 @@ func TestCompleteMultipartUpload(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
|
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
data := []byte("12345")
|
data := []byte("12345")
|
||||||
@ -200,7 +200,7 @@ func TestAbortMultipartUpload(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
|
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
data := []byte("12345")
|
data := []byte("12345")
|
||||||
@ -233,7 +233,7 @@ func TestListMultipartUploadsFaultyDisk(t *testing.T) {
|
|||||||
|
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
|
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
|
|
||||||
|
90
cmd/fs-v1.go
90
cmd/fs-v1.go
@ -37,8 +37,8 @@ import (
|
|||||||
"github.com/minio/minio/pkg/madmin"
|
"github.com/minio/minio/pkg/madmin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fsObjects - Implements fs object layer.
|
// FSObjects - Implements fs object layer.
|
||||||
type fsObjects struct {
|
type FSObjects struct {
|
||||||
// Path to be exported over S3 API.
|
// Path to be exported over S3 API.
|
||||||
fsPath string
|
fsPath string
|
||||||
|
|
||||||
@ -93,8 +93,8 @@ func initMetaVolumeFS(fsPath, fsUUID string) error {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFSObjectLayer - initialize new fs object layer.
|
// NewFSObjectLayer - initialize new fs object layer.
|
||||||
func newFSObjectLayer(fsPath string) (ObjectLayer, error) {
|
func NewFSObjectLayer(fsPath string) (ObjectLayer, error) {
|
||||||
if fsPath == "" {
|
if fsPath == "" {
|
||||||
return nil, errInvalidArgument
|
return nil, errInvalidArgument
|
||||||
}
|
}
|
||||||
@ -146,7 +146,7 @@ func newFSObjectLayer(fsPath string) (ObjectLayer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize fs objects.
|
// Initialize fs objects.
|
||||||
fs := &fsObjects{
|
fs := &FSObjects{
|
||||||
fsPath: fsPath,
|
fsPath: fsPath,
|
||||||
fsUUID: fsUUID,
|
fsUUID: fsUUID,
|
||||||
rwPool: &fsIOPool{
|
rwPool: &fsIOPool{
|
||||||
@ -180,8 +180,8 @@ func newFSObjectLayer(fsPath string) (ObjectLayer, error) {
|
|||||||
return fs, nil
|
return fs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be called when process shuts down.
|
// Shutdown - should be called when process shuts down.
|
||||||
func (fs *fsObjects) Shutdown() error {
|
func (fs *FSObjects) Shutdown() error {
|
||||||
fs.fsFormatRlk.Close()
|
fs.fsFormatRlk.Close()
|
||||||
|
|
||||||
// Cleanup and delete tmp uuid.
|
// Cleanup and delete tmp uuid.
|
||||||
@ -189,7 +189,7 @@ func (fs *fsObjects) Shutdown() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StorageInfo - returns underlying storage statistics.
|
// StorageInfo - returns underlying storage statistics.
|
||||||
func (fs *fsObjects) StorageInfo() StorageInfo {
|
func (fs *FSObjects) StorageInfo() StorageInfo {
|
||||||
info, err := getDiskInfo((fs.fsPath))
|
info, err := getDiskInfo((fs.fsPath))
|
||||||
errorIf(err, "Unable to get disk info %#v", fs.fsPath)
|
errorIf(err, "Unable to get disk info %#v", fs.fsPath)
|
||||||
storageInfo := StorageInfo{
|
storageInfo := StorageInfo{
|
||||||
@ -202,13 +202,13 @@ func (fs *fsObjects) StorageInfo() StorageInfo {
|
|||||||
|
|
||||||
// Locking operations
|
// Locking operations
|
||||||
|
|
||||||
// List namespace locks held in object layer
|
// ListLocks - List namespace locks held in object layer
|
||||||
func (fs *fsObjects) ListLocks(bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error) {
|
func (fs *FSObjects) ListLocks(bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error) {
|
||||||
return []VolumeLockInfo{}, NotImplemented{}
|
return []VolumeLockInfo{}, NotImplemented{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear namespace locks held in object layer
|
// ClearLocks - Clear namespace locks held in object layer
|
||||||
func (fs *fsObjects) ClearLocks([]VolumeLockInfo) error {
|
func (fs *FSObjects) ClearLocks([]VolumeLockInfo) error {
|
||||||
return NotImplemented{}
|
return NotImplemented{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ func (fs *fsObjects) ClearLocks([]VolumeLockInfo) error {
|
|||||||
// getBucketDir - will convert incoming bucket names to
|
// getBucketDir - will convert incoming bucket names to
|
||||||
// corresponding valid bucket names on the backend in a platform
|
// corresponding valid bucket names on the backend in a platform
|
||||||
// compatible way for all operating systems.
|
// compatible way for all operating systems.
|
||||||
func (fs *fsObjects) getBucketDir(bucket string) (string, error) {
|
func (fs *FSObjects) getBucketDir(bucket string) (string, error) {
|
||||||
// Verify if bucket is valid.
|
// Verify if bucket is valid.
|
||||||
if !IsValidBucketName(bucket) {
|
if !IsValidBucketName(bucket) {
|
||||||
return "", errors.Trace(BucketNameInvalid{Bucket: bucket})
|
return "", errors.Trace(BucketNameInvalid{Bucket: bucket})
|
||||||
@ -227,7 +227,7 @@ func (fs *fsObjects) getBucketDir(bucket string) (string, error) {
|
|||||||
return bucketDir, nil
|
return bucketDir, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *fsObjects) statBucketDir(bucket string) (os.FileInfo, error) {
|
func (fs *FSObjects) statBucketDir(bucket string) (os.FileInfo, error) {
|
||||||
bucketDir, err := fs.getBucketDir(bucket)
|
bucketDir, err := fs.getBucketDir(bucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -239,9 +239,9 @@ func (fs *fsObjects) statBucketDir(bucket string) (os.FileInfo, error) {
|
|||||||
return st, nil
|
return st, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeBucket - create a new bucket, returns if it
|
// MakeBucketWithLocation - create a new bucket, returns if it
|
||||||
// already exists.
|
// already exists.
|
||||||
func (fs *fsObjects) MakeBucketWithLocation(bucket, location string) error {
|
func (fs *FSObjects) MakeBucketWithLocation(bucket, location string) error {
|
||||||
bucketLock := fs.nsMutex.NewNSLock(bucket, "")
|
bucketLock := fs.nsMutex.NewNSLock(bucket, "")
|
||||||
if err := bucketLock.GetLock(globalObjectTimeout); err != nil {
|
if err := bucketLock.GetLock(globalObjectTimeout); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -260,7 +260,7 @@ func (fs *fsObjects) MakeBucketWithLocation(bucket, location string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetBucketInfo - fetch bucket metadata info.
|
// GetBucketInfo - fetch bucket metadata info.
|
||||||
func (fs *fsObjects) GetBucketInfo(bucket string) (bi BucketInfo, e error) {
|
func (fs *FSObjects) GetBucketInfo(bucket string) (bi BucketInfo, e error) {
|
||||||
bucketLock := fs.nsMutex.NewNSLock(bucket, "")
|
bucketLock := fs.nsMutex.NewNSLock(bucket, "")
|
||||||
if e := bucketLock.GetRLock(globalObjectTimeout); e != nil {
|
if e := bucketLock.GetRLock(globalObjectTimeout); e != nil {
|
||||||
return bi, e
|
return bi, e
|
||||||
@ -280,7 +280,7 @@ func (fs *fsObjects) GetBucketInfo(bucket string) (bi BucketInfo, e error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ListBuckets - list all s3 compatible buckets (directories) at fsPath.
|
// ListBuckets - list all s3 compatible buckets (directories) at fsPath.
|
||||||
func (fs *fsObjects) ListBuckets() ([]BucketInfo, error) {
|
func (fs *FSObjects) ListBuckets() ([]BucketInfo, error) {
|
||||||
if err := checkPathLength(fs.fsPath); err != nil {
|
if err := checkPathLength(fs.fsPath); err != nil {
|
||||||
return nil, errors.Trace(err)
|
return nil, errors.Trace(err)
|
||||||
}
|
}
|
||||||
@ -321,7 +321,7 @@ func (fs *fsObjects) ListBuckets() ([]BucketInfo, error) {
|
|||||||
|
|
||||||
// DeleteBucket - delete a bucket and all the metadata associated
|
// DeleteBucket - delete a bucket and all the metadata associated
|
||||||
// with the bucket including pending multipart, object metadata.
|
// with the bucket including pending multipart, object metadata.
|
||||||
func (fs *fsObjects) DeleteBucket(bucket string) error {
|
func (fs *FSObjects) DeleteBucket(bucket string) error {
|
||||||
bucketLock := fs.nsMutex.NewNSLock(bucket, "")
|
bucketLock := fs.nsMutex.NewNSLock(bucket, "")
|
||||||
if err := bucketLock.GetLock(globalObjectTimeout); err != nil {
|
if err := bucketLock.GetLock(globalObjectTimeout); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -360,7 +360,7 @@ func (fs *fsObjects) DeleteBucket(bucket string) error {
|
|||||||
// CopyObject - copy object source object to destination object.
|
// CopyObject - copy object source object to destination object.
|
||||||
// if source object and destination object are same we only
|
// if source object and destination object are same we only
|
||||||
// update metadata.
|
// update metadata.
|
||||||
func (fs *fsObjects) CopyObject(srcBucket, srcObject, dstBucket, dstObject string, metadata map[string]string, srcEtag string) (oi ObjectInfo, e error) {
|
func (fs *FSObjects) CopyObject(srcBucket, srcObject, dstBucket, dstObject string, metadata map[string]string, srcEtag string) (oi ObjectInfo, e error) {
|
||||||
cpSrcDstSame := srcBucket == dstBucket && srcObject == dstObject
|
cpSrcDstSame := srcBucket == dstBucket && srcObject == dstObject
|
||||||
// Hold write lock on destination since in both cases
|
// Hold write lock on destination since in both cases
|
||||||
// - if source and destination are same
|
// - if source and destination are same
|
||||||
@ -463,7 +463,7 @@ func (fs *fsObjects) CopyObject(srcBucket, srcObject, dstBucket, dstObject strin
|
|||||||
//
|
//
|
||||||
// startOffset indicates the starting read location of the object.
|
// startOffset indicates the starting read location of the object.
|
||||||
// length indicates the total length of the object.
|
// length indicates the total length of the object.
|
||||||
func (fs *fsObjects) GetObject(bucket, object string, offset int64, length int64, writer io.Writer, etag string) (err error) {
|
func (fs *FSObjects) GetObject(bucket, object string, offset int64, length int64, writer io.Writer, etag string) (err error) {
|
||||||
if err = checkGetObjArgs(bucket, object); err != nil {
|
if err = checkGetObjArgs(bucket, object); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -478,7 +478,7 @@ func (fs *fsObjects) GetObject(bucket, object string, offset int64, length int64
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getObject - wrapper for GetObject
|
// getObject - wrapper for GetObject
|
||||||
func (fs *fsObjects) getObject(bucket, object string, offset int64, length int64, writer io.Writer, etag string) (err error) {
|
func (fs *FSObjects) getObject(bucket, object string, offset int64, length int64, writer io.Writer, etag string) (err error) {
|
||||||
if _, err = fs.statBucketDir(bucket); err != nil {
|
if _, err = fs.statBucketDir(bucket); err != nil {
|
||||||
return toObjectErr(err, bucket)
|
return toObjectErr(err, bucket)
|
||||||
}
|
}
|
||||||
@ -549,7 +549,7 @@ func (fs *fsObjects) getObject(bucket, object string, offset int64, length int64
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getObjectInfo - wrapper for reading object metadata and constructs ObjectInfo.
|
// getObjectInfo - wrapper for reading object metadata and constructs ObjectInfo.
|
||||||
func (fs *fsObjects) getObjectInfo(bucket, object string) (oi ObjectInfo, e error) {
|
func (fs *FSObjects) getObjectInfo(bucket, object string) (oi ObjectInfo, e error) {
|
||||||
fsMeta := fsMetaV1{}
|
fsMeta := fsMetaV1{}
|
||||||
fi, err := fsStatDir(pathJoin(fs.fsPath, bucket, object))
|
fi, err := fsStatDir(pathJoin(fs.fsPath, bucket, object))
|
||||||
if err != nil && errors.Cause(err) != errFileAccessDenied {
|
if err != nil && errors.Cause(err) != errFileAccessDenied {
|
||||||
@ -597,7 +597,7 @@ func (fs *fsObjects) getObjectInfo(bucket, object string) (oi ObjectInfo, e erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetObjectInfo - reads object metadata and replies back ObjectInfo.
|
// GetObjectInfo - reads object metadata and replies back ObjectInfo.
|
||||||
func (fs *fsObjects) GetObjectInfo(bucket, object string) (oi ObjectInfo, e error) {
|
func (fs *FSObjects) GetObjectInfo(bucket, object string) (oi ObjectInfo, e error) {
|
||||||
// Lock the object before reading.
|
// Lock the object before reading.
|
||||||
objectLock := fs.nsMutex.NewNSLock(bucket, object)
|
objectLock := fs.nsMutex.NewNSLock(bucket, object)
|
||||||
if err := objectLock.GetRLock(globalObjectTimeout); err != nil {
|
if err := objectLock.GetRLock(globalObjectTimeout); err != nil {
|
||||||
@ -619,7 +619,7 @@ func (fs *fsObjects) GetObjectInfo(bucket, object string) (oi ObjectInfo, e erro
|
|||||||
// This function does the following check, suppose
|
// This function does the following check, suppose
|
||||||
// object is "a/b/c/d", stat makes sure that objects ""a/b/c""
|
// object is "a/b/c/d", stat makes sure that objects ""a/b/c""
|
||||||
// "a/b" and "a" do not exist.
|
// "a/b" and "a" do not exist.
|
||||||
func (fs *fsObjects) parentDirIsObject(bucket, parent string) bool {
|
func (fs *FSObjects) parentDirIsObject(bucket, parent string) bool {
|
||||||
var isParentDirObject func(string) bool
|
var isParentDirObject func(string) bool
|
||||||
isParentDirObject = func(p string) bool {
|
isParentDirObject = func(p string) bool {
|
||||||
if p == "." || p == "/" {
|
if p == "." || p == "/" {
|
||||||
@ -640,7 +640,7 @@ func (fs *fsObjects) parentDirIsObject(bucket, parent string) bool {
|
|||||||
// until EOF, writes data directly to configured filesystem path.
|
// until EOF, writes data directly to configured filesystem path.
|
||||||
// Additionally writes `fs.json` which carries the necessary metadata
|
// Additionally writes `fs.json` which carries the necessary metadata
|
||||||
// for future object operations.
|
// for future object operations.
|
||||||
func (fs *fsObjects) PutObject(bucket string, object string, data *hash.Reader, metadata map[string]string) (objInfo ObjectInfo, retErr error) {
|
func (fs *FSObjects) PutObject(bucket string, object string, data *hash.Reader, metadata map[string]string) (objInfo ObjectInfo, retErr error) {
|
||||||
if err := checkPutObjectArgs(bucket, object, fs, data.Size()); err != nil {
|
if err := checkPutObjectArgs(bucket, object, fs, data.Size()); err != nil {
|
||||||
return ObjectInfo{}, err
|
return ObjectInfo{}, err
|
||||||
}
|
}
|
||||||
@ -654,7 +654,7 @@ func (fs *fsObjects) PutObject(bucket string, object string, data *hash.Reader,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// putObject - wrapper for PutObject
|
// putObject - wrapper for PutObject
|
||||||
func (fs *fsObjects) putObject(bucket string, object string, data *hash.Reader, metadata map[string]string) (objInfo ObjectInfo, retErr error) {
|
func (fs *FSObjects) putObject(bucket string, object string, data *hash.Reader, metadata map[string]string) (objInfo ObjectInfo, retErr error) {
|
||||||
// No metadata is set, allocate a new one.
|
// No metadata is set, allocate a new one.
|
||||||
if metadata == nil {
|
if metadata == nil {
|
||||||
metadata = make(map[string]string)
|
metadata = make(map[string]string)
|
||||||
@ -778,7 +778,7 @@ func (fs *fsObjects) putObject(bucket string, object string, data *hash.Reader,
|
|||||||
|
|
||||||
// DeleteObject - deletes an object from a bucket, this operation is destructive
|
// DeleteObject - deletes an object from a bucket, this operation is destructive
|
||||||
// and there are no rollbacks supported.
|
// and there are no rollbacks supported.
|
||||||
func (fs *fsObjects) DeleteObject(bucket, object string) error {
|
func (fs *FSObjects) DeleteObject(bucket, object string) error {
|
||||||
// Acquire a write lock before deleting the object.
|
// Acquire a write lock before deleting the object.
|
||||||
objectLock := fs.nsMutex.NewNSLock(bucket, object)
|
objectLock := fs.nsMutex.NewNSLock(bucket, object)
|
||||||
if err := objectLock.GetLock(globalOperationTimeout); err != nil {
|
if err := objectLock.GetLock(globalOperationTimeout); err != nil {
|
||||||
@ -825,7 +825,7 @@ func (fs *fsObjects) DeleteObject(bucket, object string) error {
|
|||||||
// Returns function "listDir" of the type listDirFunc.
|
// Returns function "listDir" of the type listDirFunc.
|
||||||
// isLeaf - is used by listDir function to check if an entry
|
// isLeaf - is used by listDir function to check if an entry
|
||||||
// is a leaf or non-leaf entry.
|
// is a leaf or non-leaf entry.
|
||||||
func (fs *fsObjects) listDirFactory(isLeaf isLeafFunc) listDirFunc {
|
func (fs *FSObjects) listDirFactory(isLeaf isLeafFunc) listDirFunc {
|
||||||
// listDir - lists all the entries at a given prefix and given entry in the prefix.
|
// listDir - lists all the entries at a given prefix and given entry in the prefix.
|
||||||
listDir := func(bucket, prefixDir, prefixEntry string) (entries []string, delayIsLeaf bool, err error) {
|
listDir := func(bucket, prefixDir, prefixEntry string) (entries []string, delayIsLeaf bool, err error) {
|
||||||
entries, err = readDir(pathJoin(fs.fsPath, bucket, prefixDir))
|
entries, err = readDir(pathJoin(fs.fsPath, bucket, prefixDir))
|
||||||
@ -842,7 +842,7 @@ func (fs *fsObjects) listDirFactory(isLeaf isLeafFunc) listDirFunc {
|
|||||||
|
|
||||||
// getObjectETag is a helper function, which returns only the md5sum
|
// getObjectETag is a helper function, which returns only the md5sum
|
||||||
// of the file on the disk.
|
// of the file on the disk.
|
||||||
func (fs *fsObjects) getObjectETag(bucket, entry string) (string, error) {
|
func (fs *FSObjects) getObjectETag(bucket, entry string) (string, error) {
|
||||||
fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, entry, fsMetaJSONFile)
|
fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, entry, fsMetaJSONFile)
|
||||||
|
|
||||||
// Read `fs.json` to perhaps contend with
|
// Read `fs.json` to perhaps contend with
|
||||||
@ -891,7 +891,7 @@ func (fs *fsObjects) getObjectETag(bucket, entry string) (string, error) {
|
|||||||
|
|
||||||
// ListObjects - list all objects at prefix upto maxKeys., optionally delimited by '/'. Maintains the list pool
|
// ListObjects - list all objects at prefix upto maxKeys., optionally delimited by '/'. Maintains the list pool
|
||||||
// state for future re-entrant list requests.
|
// state for future re-entrant list requests.
|
||||||
func (fs *fsObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (loi ListObjectsInfo, e error) {
|
func (fs *FSObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (loi ListObjectsInfo, e error) {
|
||||||
if err := checkListObjsArgs(bucket, prefix, marker, delimiter, fs); err != nil {
|
if err := checkListObjsArgs(bucket, prefix, marker, delimiter, fs); err != nil {
|
||||||
return loi, err
|
return loi, err
|
||||||
}
|
}
|
||||||
@ -1051,53 +1051,53 @@ func (fs *fsObjects) ListObjects(bucket, prefix, marker, delimiter string, maxKe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HealFormat - no-op for fs, Valid only for XL.
|
// HealFormat - no-op for fs, Valid only for XL.
|
||||||
func (fs *fsObjects) HealFormat(dryRun bool) (madmin.HealResultItem, error) {
|
func (fs *FSObjects) HealFormat(dryRun bool) (madmin.HealResultItem, error) {
|
||||||
return madmin.HealResultItem{}, errors.Trace(NotImplemented{})
|
return madmin.HealResultItem{}, errors.Trace(NotImplemented{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// HealObject - no-op for fs. Valid only for XL.
|
// HealObject - no-op for fs. Valid only for XL.
|
||||||
func (fs *fsObjects) HealObject(bucket, object string, dryRun bool) (
|
func (fs *FSObjects) HealObject(bucket, object string, dryRun bool) (
|
||||||
res madmin.HealResultItem, err error) {
|
res madmin.HealResultItem, err error) {
|
||||||
return res, errors.Trace(NotImplemented{})
|
return res, errors.Trace(NotImplemented{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// HealBucket - no-op for fs, Valid only for XL.
|
// HealBucket - no-op for fs, Valid only for XL.
|
||||||
func (fs *fsObjects) HealBucket(bucket string, dryRun bool) ([]madmin.HealResultItem,
|
func (fs *FSObjects) HealBucket(bucket string, dryRun bool) ([]madmin.HealResultItem,
|
||||||
error) {
|
error) {
|
||||||
return nil, errors.Trace(NotImplemented{})
|
return nil, errors.Trace(NotImplemented{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListObjectsHeal - list all objects to be healed. Valid only for XL
|
// ListObjectsHeal - list all objects to be healed. Valid only for XL
|
||||||
func (fs *fsObjects) ListObjectsHeal(bucket, prefix, marker, delimiter string, maxKeys int) (loi ListObjectsInfo, e error) {
|
func (fs *FSObjects) ListObjectsHeal(bucket, prefix, marker, delimiter string, maxKeys int) (loi ListObjectsInfo, e error) {
|
||||||
return loi, errors.Trace(NotImplemented{})
|
return loi, errors.Trace(NotImplemented{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListBucketsHeal - list all buckets to be healed. Valid only for XL
|
// ListBucketsHeal - list all buckets to be healed. Valid only for XL
|
||||||
func (fs *fsObjects) ListBucketsHeal() ([]BucketInfo, error) {
|
func (fs *FSObjects) ListBucketsHeal() ([]BucketInfo, error) {
|
||||||
return []BucketInfo{}, errors.Trace(NotImplemented{})
|
return []BucketInfo{}, errors.Trace(NotImplemented{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBucketPolicy sets policy on bucket
|
// SetBucketPolicy sets policy on bucket
|
||||||
func (fs *fsObjects) SetBucketPolicy(bucket string, policy policy.BucketAccessPolicy) error {
|
func (fs *FSObjects) SetBucketPolicy(bucket string, policy policy.BucketAccessPolicy) error {
|
||||||
return persistAndNotifyBucketPolicyChange(bucket, false, policy, fs)
|
return persistAndNotifyBucketPolicyChange(bucket, false, policy, fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBucketPolicy will get policy on bucket
|
// GetBucketPolicy will get policy on bucket
|
||||||
func (fs *fsObjects) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, error) {
|
func (fs *FSObjects) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, error) {
|
||||||
policy := fs.bucketPolicies.GetBucketPolicy(bucket)
|
policy := fs.bucketPolicies.GetBucketPolicy(bucket)
|
||||||
if reflect.DeepEqual(policy, emptyBucketPolicy) {
|
if reflect.DeepEqual(policy, emptyBucketPolicy) {
|
||||||
return readBucketPolicy(bucket, fs)
|
return ReadBucketPolicy(bucket, fs)
|
||||||
}
|
}
|
||||||
return policy, nil
|
return policy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteBucketPolicy deletes all policies on bucket
|
// DeleteBucketPolicy deletes all policies on bucket
|
||||||
func (fs *fsObjects) DeleteBucketPolicy(bucket string) error {
|
func (fs *FSObjects) DeleteBucketPolicy(bucket string) error {
|
||||||
return persistAndNotifyBucketPolicyChange(bucket, true, emptyBucketPolicy, fs)
|
return persistAndNotifyBucketPolicyChange(bucket, true, emptyBucketPolicy, fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListObjectsV2 lists all blobs in bucket filtered by prefix
|
// ListObjectsV2 lists all blobs in bucket filtered by prefix
|
||||||
func (fs *fsObjects) ListObjectsV2(bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (result ListObjectsV2Info, err error) {
|
func (fs *FSObjects) ListObjectsV2(bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (result ListObjectsV2Info, err error) {
|
||||||
loi, err := fs.ListObjects(bucket, prefix, continuationToken, delimiter, maxKeys)
|
loi, err := fs.ListObjects(bucket, prefix, continuationToken, delimiter, maxKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
@ -1114,8 +1114,8 @@ func (fs *fsObjects) ListObjectsV2(bucket, prefix, continuationToken, delimiter
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RefreshBucketPolicy refreshes cache policy with what's on disk.
|
// RefreshBucketPolicy refreshes cache policy with what's on disk.
|
||||||
func (fs *fsObjects) RefreshBucketPolicy(bucket string) error {
|
func (fs *FSObjects) RefreshBucketPolicy(bucket string) error {
|
||||||
policy, err := readBucketPolicy(bucket, fs)
|
policy, err := ReadBucketPolicy(bucket, fs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if reflect.DeepEqual(policy, emptyBucketPolicy) {
|
if reflect.DeepEqual(policy, emptyBucketPolicy) {
|
||||||
@ -1127,11 +1127,11 @@ func (fs *fsObjects) RefreshBucketPolicy(bucket string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
|
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
|
||||||
func (fs *fsObjects) IsNotificationSupported() bool {
|
func (fs *FSObjects) IsNotificationSupported() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEncryptionSupported returns whether server side encryption is applicable for this layer.
|
// IsEncryptionSupported returns whether server side encryption is applicable for this layer.
|
||||||
func (fs *fsObjects) IsEncryptionSupported() bool {
|
func (fs *FSObjects) IsEncryptionSupported() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ func TestFSParentDirIsObject(t *testing.T) {
|
|||||||
t.Fatalf("Unexpected object name returned got %s, expected %s", objInfo.Name, objectName)
|
t.Fatalf("Unexpected object name returned got %s, expected %s", objInfo.Name, objectName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
parentIsObject bool
|
parentIsObject bool
|
||||||
objectName string
|
objectName string
|
||||||
@ -101,16 +101,16 @@ func TestFSParentDirIsObject(t *testing.T) {
|
|||||||
// and constructs a valid `FS` object layer.
|
// and constructs a valid `FS` object layer.
|
||||||
func TestNewFS(t *testing.T) {
|
func TestNewFS(t *testing.T) {
|
||||||
// Do not attempt to create this path, the test validates
|
// Do not attempt to create this path, the test validates
|
||||||
// so that newFSObjectLayer initializes non existing paths
|
// so that NewFSObjectLayer initializes non existing paths
|
||||||
// and successfully returns initialized object layer.
|
// and successfully returns initialized object layer.
|
||||||
disk := filepath.Join(globalTestTmpDir, "minio-"+nextSuffix())
|
disk := filepath.Join(globalTestTmpDir, "minio-"+nextSuffix())
|
||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
|
|
||||||
_, err := newFSObjectLayer("")
|
_, err := NewFSObjectLayer("")
|
||||||
if err != errInvalidArgument {
|
if err != errInvalidArgument {
|
||||||
t.Errorf("Expecting error invalid argument, got %s", err)
|
t.Errorf("Expecting error invalid argument, got %s", err)
|
||||||
}
|
}
|
||||||
_, err = newFSObjectLayer(disk)
|
_, err = NewFSObjectLayer(disk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errMsg := "Unable to recognize backend format, Disk is not in FS format."
|
errMsg := "Unable to recognize backend format, Disk is not in FS format."
|
||||||
if err.Error() == errMsg {
|
if err.Error() == errMsg {
|
||||||
@ -131,10 +131,10 @@ func TestFSShutdown(t *testing.T) {
|
|||||||
bucketName := "testbucket"
|
bucketName := "testbucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
// Create and return an fsObject with its path in the disk
|
// Create and return an fsObject with its path in the disk
|
||||||
prepareTest := func() (*fsObjects, string) {
|
prepareTest := func() (*FSObjects, string) {
|
||||||
disk := filepath.Join(globalTestTmpDir, "minio-"+nextSuffix())
|
disk := filepath.Join(globalTestTmpDir, "minio-"+nextSuffix())
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
objectContent := "12345"
|
objectContent := "12345"
|
||||||
obj.MakeBucketWithLocation(bucketName, "")
|
obj.MakeBucketWithLocation(bucketName, "")
|
||||||
obj.PutObject(bucketName, objectName, mustGetHashReader(t, bytes.NewReader([]byte(objectContent)), int64(len(objectContent)), "", ""), nil)
|
obj.PutObject(bucketName, objectName, mustGetHashReader(t, bytes.NewReader([]byte(objectContent)), int64(len(objectContent)), "", ""), nil)
|
||||||
@ -164,7 +164,7 @@ func TestFSGetBucketInfo(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
|
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
|
|
||||||
obj.MakeBucketWithLocation(bucketName, "")
|
obj.MakeBucketWithLocation(bucketName, "")
|
||||||
@ -266,7 +266,7 @@ func TestFSDeleteObject(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
|
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
objectName := "object"
|
objectName := "object"
|
||||||
|
|
||||||
@ -311,7 +311,7 @@ func TestFSDeleteBucket(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
|
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
|
|
||||||
err := obj.MakeBucketWithLocation(bucketName, "")
|
err := obj.MakeBucketWithLocation(bucketName, "")
|
||||||
@ -350,7 +350,7 @@ func TestFSListBuckets(t *testing.T) {
|
|||||||
defer os.RemoveAll(disk)
|
defer os.RemoveAll(disk)
|
||||||
|
|
||||||
obj := initFSObjects(disk, t)
|
obj := initFSObjects(disk, t)
|
||||||
fs := obj.(*fsObjects)
|
fs := obj.(*FSObjects)
|
||||||
|
|
||||||
bucketName := "bucket"
|
bucketName := "bucket"
|
||||||
if err := obj.MakeBucketWithLocation(bucketName, ""); err != nil {
|
if err := obj.MakeBucketWithLocation(bucketName, ""); err != nil {
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
_ "github.com/minio/minio/cmd/gateway/b2"
|
_ "github.com/minio/minio/cmd/gateway/b2"
|
||||||
_ "github.com/minio/minio/cmd/gateway/gcs"
|
_ "github.com/minio/minio/cmd/gateway/gcs"
|
||||||
_ "github.com/minio/minio/cmd/gateway/manta"
|
_ "github.com/minio/minio/cmd/gateway/manta"
|
||||||
|
_ "github.com/minio/minio/cmd/gateway/nas"
|
||||||
_ "github.com/minio/minio/cmd/gateway/oss"
|
_ "github.com/minio/minio/cmd/gateway/oss"
|
||||||
_ "github.com/minio/minio/cmd/gateway/s3"
|
_ "github.com/minio/minio/cmd/gateway/s3"
|
||||||
_ "github.com/minio/minio/cmd/gateway/sia"
|
_ "github.com/minio/minio/cmd/gateway/sia"
|
||||||
|
119
cmd/gateway/nas/gateway-nas.go
Normal file
119
cmd/gateway/nas/gateway-nas.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package nas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/minio/cli"
|
||||||
|
"github.com/minio/minio-go/pkg/policy"
|
||||||
|
minio "github.com/minio/minio/cmd"
|
||||||
|
"github.com/minio/minio/pkg/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nasBackend = "nas"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
const nasGatewayTemplate = `NAME:
|
||||||
|
{{.HelpName}} - {{.Usage}}
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
{{.HelpName}} {{if .VisibleFlags}}[FLAGS]{{end}} PATH
|
||||||
|
{{if .VisibleFlags}}
|
||||||
|
FLAGS:
|
||||||
|
{{range .VisibleFlags}}{{.}}
|
||||||
|
{{end}}{{end}}
|
||||||
|
PATH:
|
||||||
|
path to NAS mount point.
|
||||||
|
|
||||||
|
ENVIRONMENT VARIABLES:
|
||||||
|
ACCESS:
|
||||||
|
MINIO_ACCESS_KEY: Username or access key of minimum 3 characters in length.
|
||||||
|
MINIO_SECRET_KEY: Password or secret key of minimum 8 characters in length.
|
||||||
|
|
||||||
|
BROWSER:
|
||||||
|
MINIO_BROWSER: To disable web browser access, set this value to "off".
|
||||||
|
|
||||||
|
UPDATE:
|
||||||
|
MINIO_UPDATE: To turn off in-place upgrades, set this value to "off".
|
||||||
|
|
||||||
|
EXAMPLES:
|
||||||
|
1. Start minio gateway server for NAS backend.
|
||||||
|
$ export MINIO_ACCESS_KEY=accesskey
|
||||||
|
$ export MINIO_SECRET_KEY=secretkey
|
||||||
|
$ {{.HelpName}} /shared/nasvol
|
||||||
|
`
|
||||||
|
|
||||||
|
minio.RegisterGatewayCommand(cli.Command{
|
||||||
|
Name: nasBackend,
|
||||||
|
Usage: "Network-attached storage (NAS).",
|
||||||
|
Action: nasGatewayMain,
|
||||||
|
CustomHelpTemplate: nasGatewayTemplate,
|
||||||
|
HideHelpCommand: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler for 'minio gateway nas' command line.
|
||||||
|
func nasGatewayMain(ctx *cli.Context) {
|
||||||
|
// Validate gateway arguments.
|
||||||
|
host := ctx.Args().First()
|
||||||
|
if host == "" {
|
||||||
|
cli.ShowCommandHelpAndExit(ctx, "nas", 1)
|
||||||
|
}
|
||||||
|
// Validate gateway arguments.
|
||||||
|
minio.StartGateway(ctx, &NAS{host})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NAS implements Gateway.
|
||||||
|
type NAS struct {
|
||||||
|
host string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name implements Gateway interface.
|
||||||
|
func (g *NAS) Name() string {
|
||||||
|
return nasBackend
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGatewayLayer returns nas gatewaylayer.
|
||||||
|
func (g *NAS) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) {
|
||||||
|
var err error
|
||||||
|
newObject, err := minio.NewFSObjectLayer(g.host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &nasObjects{newObject.(*minio.FSObjects)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Production - nas gateway is production ready.
|
||||||
|
func (g *NAS) Production() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// nasObjects implements gateway for Minio and S3 compatible object storage servers.
|
||||||
|
type nasObjects struct {
|
||||||
|
*minio.FSObjects
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotificationSupported returns whether notifications are applicable for this layer.
|
||||||
|
func (l *nasObjects) IsNotificationSupported() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBucketPolicy will get policy on bucket
|
||||||
|
func (l *nasObjects) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, error) {
|
||||||
|
return minio.ReadBucketPolicy(bucket, l)
|
||||||
|
}
|
@ -576,7 +576,7 @@ func testListObjects(obj ObjectLayer, instanceType string, t TestErrHandler) {
|
|||||||
// Initialize FS backend for the benchmark.
|
// Initialize FS backend for the benchmark.
|
||||||
func initFSObjectsB(disk string, t *testing.B) (obj ObjectLayer) {
|
func initFSObjectsB(disk string, t *testing.B) (obj ObjectLayer) {
|
||||||
var err error
|
var err error
|
||||||
obj, err = newFSObjectLayer(disk)
|
obj, err = NewFSObjectLayer(disk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Unexpected err: ", err)
|
t.Fatal("Unexpected err: ", err)
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,7 @@ func newObjectLayer(endpoints EndpointList) (newObject ObjectLayer, err error) {
|
|||||||
isFS := len(endpoints) == 1
|
isFS := len(endpoints) == 1
|
||||||
if isFS {
|
if isFS {
|
||||||
// Initialize new FS object layer.
|
// Initialize new FS object layer.
|
||||||
return newFSObjectLayer(endpoints[0].Path)
|
return NewFSObjectLayer(endpoints[0].Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := waitForFormatXL(endpoints[0].IsLocal, endpoints, globalXLSetCount, globalXLSetDriveCount)
|
format, err := waitForFormatXL(endpoints[0].IsLocal, endpoints, globalXLSetCount, globalXLSetDriveCount)
|
||||||
|
@ -36,7 +36,7 @@ func TestNewObjectLayer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Unexpected object layer initialization error", err)
|
t.Fatal("Unexpected object layer initialization error", err)
|
||||||
}
|
}
|
||||||
_, ok := obj.(*fsObjects)
|
_, ok := obj.(*FSObjects)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("Unexpected object layer detected", reflect.TypeOf(obj))
|
t.Fatal("Unexpected object layer detected", reflect.TypeOf(obj))
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ func prepareFS() (ObjectLayer, string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
obj, err := newFSObjectLayer(fsDirs[0])
|
obj, err := NewFSObjectLayer(fsDirs[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
@ -221,7 +221,7 @@ func prepareXL16() (ObjectLayer, []string, error) {
|
|||||||
func initFSObjects(disk string, t *testing.T) (obj ObjectLayer) {
|
func initFSObjects(disk string, t *testing.T) (obj ObjectLayer) {
|
||||||
newTestConfig(globalMinioDefaultRegion)
|
newTestConfig(globalMinioDefaultRegion)
|
||||||
var err error
|
var err error
|
||||||
obj, err = newFSObjectLayer(disk)
|
obj, err = NewFSObjectLayer(disk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1685,7 +1685,7 @@ func newTestObjectLayer(endpoints EndpointList) (newObject ObjectLayer, err erro
|
|||||||
isFS := len(endpoints) == 1
|
isFS := len(endpoints) == 1
|
||||||
if isFS {
|
if isFS {
|
||||||
// Initialize new FS object layer.
|
// Initialize new FS object layer.
|
||||||
return newFSObjectLayer(endpoints[0].Path)
|
return NewFSObjectLayer(endpoints[0].Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = waitForFormatXL(endpoints[0].IsLocal, endpoints, 1, 16)
|
_, err = waitForFormatXL(endpoints[0].IsLocal, endpoints, 1, 16)
|
||||||
|
@ -433,7 +433,7 @@ func (s *xlSets) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, erro
|
|||||||
// fetch bucket policy from cache.
|
// fetch bucket policy from cache.
|
||||||
bpolicy := s.bucketPolicies.GetBucketPolicy(bucket)
|
bpolicy := s.bucketPolicies.GetBucketPolicy(bucket)
|
||||||
if reflect.DeepEqual(bpolicy, emptyBucketPolicy) {
|
if reflect.DeepEqual(bpolicy, emptyBucketPolicy) {
|
||||||
return readBucketPolicy(bucket, s)
|
return ReadBucketPolicy(bucket, s)
|
||||||
}
|
}
|
||||||
return bpolicy, nil
|
return bpolicy, nil
|
||||||
}
|
}
|
||||||
@ -445,7 +445,7 @@ func (s *xlSets) DeleteBucketPolicy(bucket string) error {
|
|||||||
|
|
||||||
// RefreshBucketPolicy refreshes policy cache from disk
|
// RefreshBucketPolicy refreshes policy cache from disk
|
||||||
func (s *xlSets) RefreshBucketPolicy(bucket string) error {
|
func (s *xlSets) RefreshBucketPolicy(bucket string) error {
|
||||||
policy, err := readBucketPolicy(bucket, s)
|
policy, err := ReadBucketPolicy(bucket, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if reflect.DeepEqual(policy, emptyBucketPolicy) {
|
if reflect.DeepEqual(policy, emptyBucketPolicy) {
|
||||||
return s.bucketPolicies.DeleteBucketPolicy(bucket)
|
return s.bucketPolicies.DeleteBucketPolicy(bucket)
|
||||||
|
@ -291,7 +291,7 @@ func (xl xlObjects) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, e
|
|||||||
// fetch bucket policy from cache.
|
// fetch bucket policy from cache.
|
||||||
bpolicy := xl.bucketPolicies.GetBucketPolicy(bucket)
|
bpolicy := xl.bucketPolicies.GetBucketPolicy(bucket)
|
||||||
if reflect.DeepEqual(bpolicy, emptyBucketPolicy) {
|
if reflect.DeepEqual(bpolicy, emptyBucketPolicy) {
|
||||||
return readBucketPolicy(bucket, xl)
|
return ReadBucketPolicy(bucket, xl)
|
||||||
}
|
}
|
||||||
return bpolicy, nil
|
return bpolicy, nil
|
||||||
}
|
}
|
||||||
@ -303,7 +303,7 @@ func (xl xlObjects) DeleteBucketPolicy(bucket string) error {
|
|||||||
|
|
||||||
// RefreshBucketPolicy refreshes policy cache from disk
|
// RefreshBucketPolicy refreshes policy cache from disk
|
||||||
func (xl xlObjects) RefreshBucketPolicy(bucket string) error {
|
func (xl xlObjects) RefreshBucketPolicy(bucket string) error {
|
||||||
policy, err := readBucketPolicy(bucket, xl)
|
policy, err := ReadBucketPolicy(bucket, xl)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if reflect.DeepEqual(policy, emptyBucketPolicy) {
|
if reflect.DeepEqual(policy, emptyBucketPolicy) {
|
||||||
|
@ -17,7 +17,7 @@ export MINIO_SECRET_KEY=azureaccountkey
|
|||||||
minio gateway azure
|
minio gateway azure
|
||||||
```
|
```
|
||||||
## Test using Minio Browser
|
## Test using Minio Browser
|
||||||
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 ensure your server has started successfully.
|
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 to ensure that your server has started successfully.
|
||||||
|
|
||||||
![Screenshot](https://github.com/minio/minio/blob/master/docs/screenshots/minio-browser-gateway.png?raw=true)
|
![Screenshot](https://github.com/minio/minio/blob/master/docs/screenshots/minio-browser-gateway.png?raw=true)
|
||||||
## Test using Minio Client `mc`
|
## Test using Minio Client `mc`
|
||||||
|
@ -13,7 +13,7 @@ docker run -p 9000:9000 --name b2-s3 \
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Test using Minio Browser
|
## Test using Minio Browser
|
||||||
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 ensure your server has started successfully.
|
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 to ensure that your server has started successfully.
|
||||||
|
|
||||||
![Screenshot](https://raw.githubusercontent.com/minio/minio/master/docs/screenshots/minio-browser-gateway.png)
|
![Screenshot](https://raw.githubusercontent.com/minio/minio/master/docs/screenshots/minio-browser-gateway.png)
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ minio gateway gcs yourprojectid
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Test using Minio Browser
|
## Test using Minio Browser
|
||||||
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 ensure your server has started successfully.
|
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 to ensure that your server has started successfully.
|
||||||
|
|
||||||
![Screenshot](https://github.com/minio/minio/blob/master/docs/screenshots/minio-browser-gateway.png?raw=true)
|
![Screenshot](https://github.com/minio/minio/blob/master/docs/screenshots/minio-browser-gateway.png?raw=true)
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ export MANTA_SUBUSER=devuser
|
|||||||
minio gateway manta
|
minio gateway manta
|
||||||
```
|
```
|
||||||
## Test using Minio Browser
|
## Test using Minio Browser
|
||||||
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 ensure your server has started successfully.
|
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 to ensure that your server has started successfully.
|
||||||
|
|
||||||
![Screenshot](https://github.com/minio/minio/blob/master/docs/screenshots/minio-browser-gateway.png?raw=true)
|
![Screenshot](https://github.com/minio/minio/blob/master/docs/screenshots/minio-browser-gateway.png?raw=true)
|
||||||
## Test using Minio Client `mc`
|
## Test using Minio Client `mc`
|
||||||
|
43
docs/gateway/nas.md
Normal file
43
docs/gateway/nas.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Minio NAS Gateway [![Slack](https://slack.minio.io/slack?type=svg)](https://slack.minio.io)
|
||||||
|
Minio Gateway adds Amazon S3 compatibility to NAS storage. You may run multiple minio instances on the same shared NAS volume as a distributed object gateway.
|
||||||
|
|
||||||
|
## Run Minio Gateway for NAS Storage
|
||||||
|
### Using Docker
|
||||||
|
```
|
||||||
|
docker run -p 9000:9000 --name nas-s3 \
|
||||||
|
-e "MINIO_ACCESS_KEY=minio" \
|
||||||
|
-e "MINIO_SECRET_KEY=minio123" \
|
||||||
|
minio/minio:edge gateway nas /shared/nasvol
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Binary
|
||||||
|
```
|
||||||
|
export MINIO_ACCESS_KEY=minioaccesskey
|
||||||
|
export MINIO_SECRET_KEY=miniosecretkey
|
||||||
|
minio gateway nas /shared/nasvol
|
||||||
|
```
|
||||||
|
## Test using Minio Browser
|
||||||
|
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 to ensure that your server has started successfully.
|
||||||
|
|
||||||
|
![Screenshot](https://raw.githubusercontent.com/minio/minio/master/docs/screenshots/minio-browser-gateway.png)
|
||||||
|
|
||||||
|
## Test using Minio Client `mc`
|
||||||
|
`mc` provides a modern alternative to UNIX commands such as ls, cat, cp, mirror, diff etc. It supports filesystems and Amazon S3 compatible cloud storage services.
|
||||||
|
|
||||||
|
### Configure `mc`
|
||||||
|
```
|
||||||
|
mc config host add mynas http://gateway-ip:9000 access_key secret_key
|
||||||
|
```
|
||||||
|
|
||||||
|
### List buckets on nas
|
||||||
|
```
|
||||||
|
mc ls mynas
|
||||||
|
[2017-02-22 01:50:43 PST] 0B ferenginar/
|
||||||
|
[2017-02-26 21:43:51 PST] 0B my-bucket/
|
||||||
|
[2017-02-26 22:10:11 PST] 0B test-bucket1/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explore Further
|
||||||
|
- [`mc` command-line interface](https://docs.minio.io/docs/minio-client-quickstart-guide)
|
||||||
|
- [`aws` command-line interface](https://docs.minio.io/docs/aws-cli-with-minio)
|
||||||
|
- [`minio-go` Go SDK](https://docs.minio.io/docs/golang-client-quickstart-guide)
|
@ -19,7 +19,7 @@ minio gateway azure
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Test using Minio Browser
|
## Test using Minio Browser
|
||||||
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 ensure your server has started successfully.
|
Minio Gateway comes with an embedded web based object browser. Point your web browser to http://127.0.0.1:9000 to ensure that your server has started successfully.
|
||||||
|
|
||||||
![Screenshot](https://raw.githubusercontent.com/minio/minio/master/docs/screenshots/minio-browser-gateway.png)
|
![Screenshot](https://raw.githubusercontent.com/minio/minio/master/docs/screenshots/minio-browser-gateway.png)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user