|
|
|
@ -25,6 +25,7 @@ import ( |
|
|
|
|
"io" |
|
|
|
|
"io/ioutil" |
|
|
|
|
"log" |
|
|
|
|
"reflect" |
|
|
|
|
"runtime/debug" |
|
|
|
|
"sort" |
|
|
|
|
"strconv" |
|
|
|
@ -71,11 +72,22 @@ type storedBucket struct { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// New instantiate a new donut
|
|
|
|
|
func New(c *Config) (Interface, error) { |
|
|
|
|
if err := quick.CheckData(c); err != nil { |
|
|
|
|
func New() (Interface, error) { |
|
|
|
|
var conf *Config |
|
|
|
|
var err error |
|
|
|
|
conf, err = LoadConfig() |
|
|
|
|
if err != nil { |
|
|
|
|
conf = &Config{ |
|
|
|
|
Version: "0.0.1", |
|
|
|
|
MaxSize: 512000000, |
|
|
|
|
NodeDiskMap: nil, |
|
|
|
|
DonutName: "", |
|
|
|
|
} |
|
|
|
|
if err := quick.CheckData(conf); err != nil { |
|
|
|
|
return nil, iodine.New(err, nil) |
|
|
|
|
} |
|
|
|
|
a := API{config: c} |
|
|
|
|
} |
|
|
|
|
a := API{config: conf} |
|
|
|
|
a.storedBuckets = metadata.NewCache() |
|
|
|
|
a.nodes = make(map[string]node) |
|
|
|
|
a.buckets = make(map[string]bucket) |
|
|
|
@ -107,10 +119,30 @@ func New(c *Config) (Interface, error) { |
|
|
|
|
return a, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// updateConfig loads new config everytime
|
|
|
|
|
func (donut API) updateConfig() { |
|
|
|
|
// on error loading config's just return do not modify
|
|
|
|
|
conf, err := LoadConfig() |
|
|
|
|
if err != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
if reflect.DeepEqual(donut.config, conf) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
if conf.MaxSize == donut.config.MaxSize { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
donut.config = conf |
|
|
|
|
donut.objects.SetMaxSize(conf.MaxSize) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// GetObject - GET object from cache buffer
|
|
|
|
|
func (donut API) GetObject(w io.Writer, bucket string, object string) (int64, error) { |
|
|
|
|
donut.lock.Lock() |
|
|
|
|
defer donut.lock.Unlock() |
|
|
|
|
// update Config if possible
|
|
|
|
|
donut.updateConfig() |
|
|
|
|
|
|
|
|
|
if !IsValidBucket(bucket) { |
|
|
|
|
return 0, iodine.New(BucketNameInvalid{Bucket: bucket}, nil) |
|
|
|
|
} |
|
|
|
@ -156,6 +188,9 @@ func (donut API) GetObject(w io.Writer, bucket string, object string) (int64, er |
|
|
|
|
func (donut API) GetPartialObject(w io.Writer, bucket, object string, start, length int64) (int64, error) { |
|
|
|
|
donut.lock.Lock() |
|
|
|
|
defer donut.lock.Unlock() |
|
|
|
|
// update Config if possible
|
|
|
|
|
donut.updateConfig() |
|
|
|
|
|
|
|
|
|
errParams := map[string]string{ |
|
|
|
|
"bucket": bucket, |
|
|
|
|
"object": object, |
|
|
|
@ -211,6 +246,9 @@ func (donut API) GetPartialObject(w io.Writer, bucket, object string, start, len |
|
|
|
|
func (donut API) GetBucketMetadata(bucket string) (BucketMetadata, error) { |
|
|
|
|
donut.lock.Lock() |
|
|
|
|
defer donut.lock.Unlock() |
|
|
|
|
// update Config if possible
|
|
|
|
|
donut.updateConfig() |
|
|
|
|
|
|
|
|
|
if !IsValidBucket(bucket) { |
|
|
|
|
return BucketMetadata{}, iodine.New(BucketNameInvalid{Bucket: bucket}, nil) |
|
|
|
|
} |
|
|
|
@ -233,6 +271,9 @@ func (donut API) GetBucketMetadata(bucket string) (BucketMetadata, error) { |
|
|
|
|
func (donut API) SetBucketMetadata(bucket string, metadata map[string]string) error { |
|
|
|
|
donut.lock.Lock() |
|
|
|
|
defer donut.lock.Unlock() |
|
|
|
|
// update Config if possible
|
|
|
|
|
donut.updateConfig() |
|
|
|
|
|
|
|
|
|
if !IsValidBucket(bucket) { |
|
|
|
|
return iodine.New(BucketNameInvalid{Bucket: bucket}, nil) |
|
|
|
|
} |
|
|
|
@ -271,25 +312,29 @@ func isMD5SumEqual(expectedMD5Sum, actualMD5Sum string) error { |
|
|
|
|
|
|
|
|
|
// CreateObject - create an object
|
|
|
|
|
func (donut API) CreateObject(bucket, key, expectedMD5Sum string, size int64, data io.Reader, metadata map[string]string) (ObjectMetadata, error) { |
|
|
|
|
if size > int64(donut.config.MaxSize) { |
|
|
|
|
generic := GenericObjectError{Bucket: bucket, Object: key} |
|
|
|
|
return ObjectMetadata{}, iodine.New(EntityTooLarge{ |
|
|
|
|
GenericObjectError: generic, |
|
|
|
|
Size: strconv.FormatInt(size, 10), |
|
|
|
|
MaxSize: strconv.FormatUint(donut.config.MaxSize, 10), |
|
|
|
|
}, nil) |
|
|
|
|
} |
|
|
|
|
contentType := metadata["contentType"] |
|
|
|
|
donut.lock.Lock() |
|
|
|
|
defer donut.lock.Unlock() |
|
|
|
|
// update Config if possible
|
|
|
|
|
donut.updateConfig() |
|
|
|
|
|
|
|
|
|
contentType := metadata["contentType"] |
|
|
|
|
objectMetadata, err := donut.createObject(bucket, key, contentType, expectedMD5Sum, size, data) |
|
|
|
|
donut.lock.Unlock() |
|
|
|
|
// free
|
|
|
|
|
debug.FreeOSMemory() |
|
|
|
|
|
|
|
|
|
return objectMetadata, iodine.New(err, nil) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// createObject - PUT object to cache buffer
|
|
|
|
|
func (donut API) createObject(bucket, key, contentType, expectedMD5Sum string, size int64, data io.Reader) (ObjectMetadata, error) { |
|
|
|
|
if size > int64(donut.config.MaxSize) { |
|
|
|
|
generic := GenericObjectError{Bucket: bucket, Object: key} |
|
|
|
|
return ObjectMetadata{}, iodine.New(EntityTooLarge{ |
|
|
|
|
GenericObjectError: generic, |
|
|
|
|
Size: strconv.FormatInt(size, 10), |
|
|
|
|
MaxSize: strconv.FormatUint(donut.config.MaxSize, 10), |
|
|
|
|
}, nil) |
|
|
|
|
} |
|
|
|
|
if !IsValidBucket(bucket) { |
|
|
|
|
return ObjectMetadata{}, iodine.New(BucketNameInvalid{Bucket: bucket}, nil) |
|
|
|
|
} |
|
|
|
@ -384,6 +429,9 @@ func (donut API) createObject(bucket, key, contentType, expectedMD5Sum string, s |
|
|
|
|
func (donut API) MakeBucket(bucketName, acl string) error { |
|
|
|
|
donut.lock.Lock() |
|
|
|
|
defer donut.lock.Unlock() |
|
|
|
|
// update Config if possible
|
|
|
|
|
donut.updateConfig() |
|
|
|
|
|
|
|
|
|
if donut.storedBuckets.Stats().Items == totalBuckets { |
|
|
|
|
return iodine.New(TooManyBuckets{Bucket: bucketName}, nil) |
|
|
|
|
} |
|
|
|
@ -422,6 +470,9 @@ func (donut API) MakeBucket(bucketName, acl string) error { |
|
|
|
|
func (donut API) ListObjects(bucket string, resources BucketResourcesMetadata) ([]ObjectMetadata, BucketResourcesMetadata, error) { |
|
|
|
|
donut.lock.Lock() |
|
|
|
|
defer donut.lock.Unlock() |
|
|
|
|
// update Config if possible
|
|
|
|
|
donut.updateConfig() |
|
|
|
|
|
|
|
|
|
if !IsValidBucket(bucket) { |
|
|
|
|
return nil, BucketResourcesMetadata{IsTruncated: false}, iodine.New(BucketNameInvalid{Bucket: bucket}, nil) |
|
|
|
|
} |
|
|
|
@ -515,6 +566,9 @@ func (b byBucketName) Less(i, j int) bool { return b[i].Name < b[j].Name } |
|
|
|
|
func (donut API) ListBuckets() ([]BucketMetadata, error) { |
|
|
|
|
donut.lock.Lock() |
|
|
|
|
defer donut.lock.Unlock() |
|
|
|
|
// update Config if possible
|
|
|
|
|
donut.updateConfig() |
|
|
|
|
|
|
|
|
|
var results []BucketMetadata |
|
|
|
|
if len(donut.config.NodeDiskMap) > 0 { |
|
|
|
|
buckets, err := donut.listBuckets() |
|
|
|
@ -538,6 +592,9 @@ func (donut API) ListBuckets() ([]BucketMetadata, error) { |
|
|
|
|
func (donut API) GetObjectMetadata(bucket, key string) (ObjectMetadata, error) { |
|
|
|
|
donut.lock.Lock() |
|
|
|
|
defer donut.lock.Unlock() |
|
|
|
|
// update Config if possible
|
|
|
|
|
donut.updateConfig() |
|
|
|
|
|
|
|
|
|
// check if bucket exists
|
|
|
|
|
if !IsValidBucket(bucket) { |
|
|
|
|
return ObjectMetadata{}, iodine.New(BucketNameInvalid{Bucket: bucket}, nil) |
|
|
|
|