Add NAS gateway support (#5516)

master
poornas 7 years ago committed by kannappanr
parent 926e480156
commit 25107c2e11
  1. 1
      .gitignore
  2. 6
      cmd/bucket-policy.go
  3. 4
      cmd/fs-v1-metadata_test.go
  4. 26
      cmd/fs-v1-multipart.go
  5. 14
      cmd/fs-v1-multipart_test.go
  6. 90
      cmd/fs-v1.go
  7. 20
      cmd/fs-v1_test.go
  8. 1
      cmd/gateway/gateway.go
  9. 119
      cmd/gateway/nas/gateway-nas.go
  10. 2
      cmd/object-api-listobjects_test.go
  11. 2
      cmd/server-main.go
  12. 2
      cmd/server-main_test.go
  13. 6
      cmd/test-utils_test.go
  14. 4
      cmd/xl-sets.go
  15. 4
      cmd/xl-v1-bucket.go
  16. 2
      docs/gateway/azure.md
  17. 2
      docs/gateway/b2.md
  18. 2
      docs/gateway/gcs.md
  19. 2
      docs/gateway/manta.md
  20. 43
      docs/gateway/nas.md
  21. 2
      docs/gateway/oss.md

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"

@ -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"

@ -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`

@ -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…
Cancel
Save