storage/server/client: Enable storage server, enable client storage.

This commit is contained in:
Harshavardhana 2016-04-12 12:45:15 -07:00
parent 01a439f95b
commit 30b0b4deba
17 changed files with 476 additions and 483 deletions

View File

@ -18,13 +18,13 @@ package main
import router "github.com/gorilla/mux" import router "github.com/gorilla/mux"
// objectStorageAPI container for S3 compatible API. // objectAPIHandler implements and provides http handlers for S3 API.
type objectStorageAPI struct { type objectAPIHandlers struct {
ObjectAPI ObjectAPI ObjectAPI *objectAPI
} }
// registerAPIRouter - registers S3 compatible APIs. // registerAPIRouter - registers S3 compatible APIs.
func registerAPIRouter(mux *router.Router, api objectStorageAPI) { func registerAPIRouter(mux *router.Router, api objectAPIHandlers) {
// API Router // API Router
apiRouter := mux.NewRoute().PathPrefix("/").Subrouter() apiRouter := mux.NewRoute().PathPrefix("/").Subrouter()

View File

@ -74,7 +74,7 @@ func enforceBucketPolicy(action string, bucket string, reqURL *url.URL) (s3Error
// GetBucketLocationHandler - GET Bucket location. // GetBucketLocationHandler - GET Bucket location.
// ------------------------- // -------------------------
// This operation returns bucket location. // This operation returns bucket location.
func (api objectStorageAPI) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -152,7 +152,7 @@ func (api objectStorageAPI) GetBucketLocationHandler(w http.ResponseWriter, r *h
// completed or aborted. This operation returns at most 1,000 multipart // completed or aborted. This operation returns at most 1,000 multipart
// uploads in the response. // uploads in the response.
// //
func (api objectStorageAPI) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -223,7 +223,7 @@ func (api objectStorageAPI) ListMultipartUploadsHandler(w http.ResponseWriter, r
// of the objects in a bucket. You can use the request parameters as selection // of the objects in a bucket. You can use the request parameters as selection
// criteria to return a subset of the objects in a bucket. // criteria to return a subset of the objects in a bucket.
// //
func (api objectStorageAPI) ListObjectsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListObjectsHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -301,7 +301,7 @@ func (api objectStorageAPI) ListObjectsHandler(w http.ResponseWriter, r *http.Re
// ----------- // -----------
// This implementation of the GET operation returns a list of all buckets // This implementation of the GET operation returns a list of all buckets
// owned by the authenticated sender of the request. // owned by the authenticated sender of the request.
func (api objectStorageAPI) ListBucketsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
// List buckets does not support bucket policies. // List buckets does not support bucket policies.
switch getRequestAuthType(r) { switch getRequestAuthType(r) {
default: default:
@ -352,7 +352,7 @@ func (api objectStorageAPI) ListBucketsHandler(w http.ResponseWriter, r *http.Re
} }
// DeleteMultipleObjectsHandler - deletes multiple objects. // DeleteMultipleObjectsHandler - deletes multiple objects.
func (api objectStorageAPI) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -463,7 +463,7 @@ func (api objectStorageAPI) DeleteMultipleObjectsHandler(w http.ResponseWriter,
// PutBucketHandler - PUT Bucket // PutBucketHandler - PUT Bucket
// ---------- // ----------
// This implementation of the PUT operation creates a new bucket for authenticated request // This implementation of the PUT operation creates a new bucket for authenticated request
func (api objectStorageAPI) PutBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -528,7 +528,7 @@ func extractHTTPFormValues(reader *multipart.Reader) (io.Reader, map[string]stri
// ---------- // ----------
// This implementation of the POST operation handles object creation with a specified // This implementation of the POST operation handles object creation with a specified
// signature policy in multipart/form-data // signature policy in multipart/form-data
func (api objectStorageAPI) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
// Here the parameter is the size of the form data that should // Here the parameter is the size of the form data that should
// be loaded in memory, the remaining being put in temporary files. // be loaded in memory, the remaining being put in temporary files.
reader, e := r.MultipartReader() reader, e := r.MultipartReader()
@ -589,7 +589,7 @@ func (api objectStorageAPI) PostPolicyBucketHandler(w http.ResponseWriter, r *ht
// The operation returns a 200 OK if the bucket exists and you // The operation returns a 200 OK if the bucket exists and you
// have permission to access it. Otherwise, the operation might // have permission to access it. Otherwise, the operation might
// return responses such as 404 Not Found and 403 Forbidden. // return responses such as 404 Not Found and 403 Forbidden.
func (api objectStorageAPI) HeadBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -628,7 +628,7 @@ func (api objectStorageAPI) HeadBucketHandler(w http.ResponseWriter, r *http.Req
} }
// DeleteBucketHandler - Delete bucket // DeleteBucketHandler - Delete bucket
func (api objectStorageAPI) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]

View File

@ -127,7 +127,7 @@ func bucketPolicyConditionMatch(conditions map[string]string, statement policySt
// ----------------- // -----------------
// This implementation of the PUT operation uses the policy // This implementation of the PUT operation uses the policy
// subresource to add to or replace a policy on a bucket // subresource to add to or replace a policy on a bucket
func (api objectStorageAPI) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -201,7 +201,7 @@ func (api objectStorageAPI) PutBucketPolicyHandler(w http.ResponseWriter, r *htt
// ----------------- // -----------------
// This implementation of the DELETE operation uses the policy // This implementation of the DELETE operation uses the policy
// subresource to add to remove a policy on a bucket. // subresource to add to remove a policy on a bucket.
func (api objectStorageAPI) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -238,7 +238,7 @@ func (api objectStorageAPI) DeleteBucketPolicyHandler(w http.ResponseWriter, r *
// ----------------- // -----------------
// This operation uses the policy // This operation uses the policy
// subresource to return the policy of a specified bucket. // subresource to return the policy of a specified bucket.
func (api objectStorageAPI) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]

7
fs.go
View File

@ -66,7 +66,7 @@ func isDirEmpty(dirname string) (status bool, err error) {
// isDirExist - returns whether given directory exists or not. // isDirExist - returns whether given directory exists or not.
func isDirExist(dirname string) (bool, error) { func isDirExist(dirname string) (bool, error) {
fi, e := os.Lstat(dirname) fi, e := os.Stat(dirname)
if e != nil { if e != nil {
if os.IsNotExist(e) { if os.IsNotExist(e) {
return false, nil return false, nil
@ -322,6 +322,8 @@ func (s fsStorage) ListFiles(volume, prefix, marker string, recursive bool, coun
if err == nil { if err == nil {
// Prefix does not exist, not an error just respond empty list response. // Prefix does not exist, not an error just respond empty list response.
return nil, true, nil return nil, true, nil
} else if strings.Contains(err.Error(), "not a directory") {
return nil, true, nil
} }
// Rest errors should be treated as failure. // Rest errors should be treated as failure.
return nil, true, err return nil, true, err
@ -465,6 +467,9 @@ func (s fsStorage) StatFile(volume, path string) (file FileInfo, err error) {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return FileInfo{}, errFileNotFound return FileInfo{}, errFileNotFound
} }
if strings.Contains(err.Error(), "not a directory") {
return FileInfo{}, errIsNotRegular
}
return FileInfo{}, err return FileInfo{}, err
} }
if st.Mode().IsDir() { if st.Mode().IsDir() {

View File

@ -58,10 +58,10 @@ func (h redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Re-direction handled specifically for browsers. // Re-direction handled specifically for browsers.
if strings.Contains(r.Header.Get("User-Agent"), "Mozilla") { if strings.Contains(r.Header.Get("User-Agent"), "Mozilla") {
// '/' is redirected to 'locationPrefix/' // '/' is redirected to 'locationPrefix/'
// '/rpc' is redirected to 'locationPrefix/rpc' // '/webrpc' is redirected to 'locationPrefix/webrpc'
// '/login' is redirected to 'locationPrefix/login' // '/login' is redirected to 'locationPrefix/login'
switch r.URL.Path { switch r.URL.Path {
case "/", "/rpc", "/login", "/favicon.ico": case "/", "/webrpc", "/login", "/favicon.ico":
location := h.locationPrefix + r.URL.Path location := h.locationPrefix + r.URL.Path
// Redirect to new location. // Redirect to new location.
http.Redirect(w, r, location, http.StatusTemporaryRedirect) http.Redirect(w, r, location, http.StatusTemporaryRedirect)

239
network-fs.go Normal file
View File

@ -0,0 +1,239 @@
/*
* Minio Cloud Storage, (C) 2016 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 main
import (
"errors"
"fmt"
"io"
"net/http"
"net/rpc"
"net/url"
urlpath "path"
"strconv"
"strings"
"time"
)
type networkFS struct {
netAddr string
netPath string
rpcClient *rpc.Client
httpClient *http.Client
}
const (
connected = "200 Connected to Go RPC"
dialTimeoutSecs = 30 // 30 seconds.
)
// splits network path into its components Address and Path.
func splitNetPath(networkPath string) (netAddr, netPath string) {
index := strings.LastIndex(networkPath, ":")
netAddr = networkPath[:index]
netPath = networkPath[index+1:]
return netAddr, netPath
}
// Initialize new network file system.
func newNetworkFS(networkPath string) (StorageAPI, error) {
// Input validation.
if networkPath == "" && strings.LastIndex(networkPath, ":") != -1 {
return nil, errInvalidArgument
}
// TODO validate netAddr and netPath.
netAddr, netPath := splitNetPath(networkPath)
// Dial minio rpc storage http path.
rpcClient, err := rpc.DialHTTPPath("tcp", netAddr, "/minio/rpc/storage")
if err != nil {
return nil, err
}
// Initialize http client.
httpClient := &http.Client{
// Setting a sensible time out of 6minutes to wait for
// response headers. Request is pro-actively cancelled
// after 6minutes if no response was received from server.
Timeout: 6 * time.Minute,
Transport: http.DefaultTransport,
}
// Initialize network storage.
ndisk := &networkFS{
netAddr: netAddr,
netPath: netPath,
rpcClient: rpcClient,
httpClient: httpClient,
}
// Returns successfully here.
return ndisk, nil
}
// MakeVol - make a volume.
func (n networkFS) MakeVol(volume string) error {
reply := GenericReply{}
if err := n.rpcClient.Call("Storage.MakeVolHandler", volume, &reply); err != nil {
if err.Error() == errVolumeExists.Error() {
return errVolumeExists
}
return err
}
return nil
}
// ListVols - List all volumes.
func (n networkFS) ListVols() (vols []VolInfo, err error) {
ListVols := ListVolsReply{}
err = n.rpcClient.Call("Storage.ListVolsHandler", "", &ListVols)
if err != nil {
return nil, err
}
return ListVols.Vols, nil
}
// StatVol - get current Stat volume info.
func (n networkFS) StatVol(volume string) (volInfo VolInfo, err error) {
if err = n.rpcClient.Call("Storage.StatVolHandler", volume, &volInfo); err != nil {
if err.Error() == errVolumeNotFound.Error() {
return VolInfo{}, errVolumeNotFound
}
return VolInfo{}, err
}
return volInfo, nil
}
// DeleteVol - Delete a volume.
func (n networkFS) DeleteVol(volume string) error {
reply := GenericReply{}
if err := n.rpcClient.Call("Storage.DeleteVolHandler", volume, &reply); err != nil {
if err.Error() == errVolumeNotFound.Error() {
return errVolumeNotFound
}
return err
}
return nil
}
// File operations.
// CreateFile - create file.
func (n networkFS) CreateFile(volume, path string) (writeCloser io.WriteCloser, err error) {
writeURL := new(url.URL)
writeURL.Scheme = "http" // TODO fix this.
writeURL.Host = n.netAddr
writeURL.Path = fmt.Sprintf("/minio/rpc/storage/upload/%s", urlpath.Join(volume, path))
contentType := "application/octet-stream"
readCloser, writeCloser := io.Pipe()
go func() {
resp, err := n.httpClient.Post(writeURL.String(), contentType, readCloser)
if err != nil {
readCloser.CloseWithError(err)
return
}
if resp != nil {
if resp.StatusCode != http.StatusNotFound {
readCloser.CloseWithError(errFileNotFound)
return
}
readCloser.CloseWithError(errors.New("Invalid response."))
}
}()
return writeCloser, nil
}
// StatFile - get latest Stat information for a file at path.
func (n networkFS) StatFile(volume, path string) (fileInfo FileInfo, err error) {
if err = n.rpcClient.Call("Storage.StatFileHandler", StatFileArgs{
Vol: volume,
Path: path,
}, &fileInfo); err != nil {
if err.Error() == errVolumeNotFound.Error() {
return FileInfo{}, errVolumeNotFound
} else if err.Error() == errFileNotFound.Error() {
return FileInfo{}, errFileNotFound
} else if err.Error() == errIsNotRegular.Error() {
return FileInfo{}, errFileNotFound
}
return FileInfo{}, err
}
return fileInfo, nil
}
// ReadFile - reads a file.
func (n networkFS) ReadFile(volume string, path string, offset int64) (reader io.ReadCloser, err error) {
readURL := new(url.URL)
readURL.Scheme = "http" // TODO fix this.
readURL.Host = n.netAddr
readURL.Path = fmt.Sprintf("/minio/rpc/storage/download/%s", urlpath.Join(volume, path))
readQuery := make(url.Values)
readQuery.Set("offset", strconv.FormatInt(offset, 10))
readURL.RawQuery = readQuery.Encode()
resp, err := n.httpClient.Get(readURL.String())
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
if resp.StatusCode == http.StatusNotFound {
return nil, errFileNotFound
}
return nil, errors.New("Invalid response")
}
return resp.Body, nil
}
// ListFiles - List all files in a volume.
func (n networkFS) ListFiles(volume, prefix, marker string, recursive bool, count int) (files []FileInfo, eof bool, err error) {
listFilesReply := ListFilesReply{}
if err = n.rpcClient.Call("Storage.ListFilesHandler", ListFilesArgs{
Vol: volume,
Prefix: prefix,
Marker: marker,
Recursive: recursive,
Count: count,
}, &listFilesReply); err != nil {
if err.Error() == errVolumeNotFound.Error() {
return nil, true, errVolumeNotFound
}
return nil, true, err
}
// List of files.
files = listFilesReply.Files
// EOF.
eof = listFilesReply.EOF
return files, eof, nil
}
// DeleteFile - Delete a file at path.
func (n networkFS) DeleteFile(volume, path string) (err error) {
reply := GenericReply{}
if err = n.rpcClient.Call("Storage.DeleteFileHandler", DeleteFileArgs{
Vol: volume,
Path: path,
}, &reply); err != nil {
if err.Error() == errVolumeNotFound.Error() {
return errVolumeNotFound
} else if err.Error() == errFileNotFound.Error() {
return errFileNotFound
}
return err
}
return nil
}

View File

@ -1,33 +0,0 @@
package main
import (
"io"
"github.com/minio/minio/pkg/probe"
)
// ObjectAPI interface.
type ObjectAPI interface {
// Bucket resource API.
DeleteBucket(bucket string) *probe.Error
ListBuckets() ([]BucketInfo, *probe.Error)
MakeBucket(bucket string) *probe.Error
GetBucketInfo(bucket string) (BucketInfo, *probe.Error)
// Bucket query API.
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
// Object resource API.
GetObject(bucket, object string, startOffset int64) (io.ReadCloser, *probe.Error)
GetObjectInfo(bucket, object string) (ObjectInfo, *probe.Error)
PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (ObjectInfo, *probe.Error)
DeleteObject(bucket, object string) *probe.Error
// Object query API.
NewMultipartUpload(bucket, object string) (string, *probe.Error)
PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
AbortMultipartUpload(bucket, object, uploadID string) *probe.Error
}

View File

@ -74,7 +74,7 @@ func errAllowableObjectNotFound(bucket string, r *http.Request) APIErrorCode {
// ---------- // ----------
// This implementation of the GET operation retrieves object. To use GET, // This implementation of the GET operation retrieves object. To use GET,
// you must have READ access to the object. // you must have READ access to the object.
func (api objectStorageAPI) GetObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
var object, bucket string var object, bucket string
vars := mux.Vars(r) vars := mux.Vars(r)
bucket = vars["bucket"] bucket = vars["bucket"]
@ -268,7 +268,7 @@ func checkETag(w http.ResponseWriter, r *http.Request) bool {
// HeadObjectHandler - HEAD Object // HeadObjectHandler - HEAD Object
// ----------- // -----------
// The HEAD operation retrieves metadata from an object without returning the object itself. // The HEAD operation retrieves metadata from an object without returning the object itself.
func (api objectStorageAPI) HeadObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
var object, bucket string var object, bucket string
vars := mux.Vars(r) vars := mux.Vars(r)
bucket = vars["bucket"] bucket = vars["bucket"]
@ -332,7 +332,7 @@ func (api objectStorageAPI) HeadObjectHandler(w http.ResponseWriter, r *http.Req
// ---------- // ----------
// This implementation of the PUT operation adds an object to a bucket // This implementation of the PUT operation adds an object to a bucket
// while reading the object from another source. // while reading the object from another source.
func (api objectStorageAPI) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]
@ -583,7 +583,7 @@ func checkCopySourceETag(w http.ResponseWriter, r *http.Request) bool {
// PutObjectHandler - PUT Object // PutObjectHandler - PUT Object
// ---------- // ----------
// This implementation of the PUT operation adds an object to a bucket. // This implementation of the PUT operation adds an object to a bucket.
func (api objectStorageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
// If the matching failed, it means that the X-Amz-Copy-Source was // If the matching failed, it means that the X-Amz-Copy-Source was
// wrong, fail right here. // wrong, fail right here.
if _, ok := r.Header["X-Amz-Copy-Source"]; ok { if _, ok := r.Header["X-Amz-Copy-Source"]; ok {
@ -699,10 +699,10 @@ func (api objectStorageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Requ
writeSuccessResponse(w, nil) writeSuccessResponse(w, nil)
} }
/// Multipart objectStorageAPI /// Multipart objectAPIHandlers
// NewMultipartUploadHandler - New multipart upload // NewMultipartUploadHandler - New multipart upload
func (api objectStorageAPI) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
var object, bucket string var object, bucket string
vars := mux.Vars(r) vars := mux.Vars(r)
bucket = vars["bucket"] bucket = vars["bucket"]
@ -755,7 +755,7 @@ func (api objectStorageAPI) NewMultipartUploadHandler(w http.ResponseWriter, r *
} }
// PutObjectPartHandler - Upload part // PutObjectPartHandler - Upload part
func (api objectStorageAPI) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]
@ -868,7 +868,7 @@ func (api objectStorageAPI) PutObjectPartHandler(w http.ResponseWriter, r *http.
} }
// AbortMultipartUploadHandler - Abort multipart upload // AbortMultipartUploadHandler - Abort multipart upload
func (api objectStorageAPI) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]
@ -915,7 +915,7 @@ func (api objectStorageAPI) AbortMultipartUploadHandler(w http.ResponseWriter, r
} }
// ListObjectPartsHandler - List object parts // ListObjectPartsHandler - List object parts
func (api objectStorageAPI) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]
@ -979,7 +979,7 @@ func (api objectStorageAPI) ListObjectPartsHandler(w http.ResponseWriter, r *htt
} }
// CompleteMultipartUploadHandler - Complete multipart upload // CompleteMultipartUploadHandler - Complete multipart upload
func (api objectStorageAPI) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]
@ -1066,10 +1066,10 @@ func (api objectStorageAPI) CompleteMultipartUploadHandler(w http.ResponseWriter
writeSuccessResponse(w, encodedSuccessResponse) writeSuccessResponse(w, encodedSuccessResponse)
} }
/// Delete objectStorageAPI /// Delete objectAPIHandlers
// DeleteObjectHandler - delete an object // DeleteObjectHandler - delete an object
func (api objectStorageAPI) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]

View File

@ -28,7 +28,7 @@ import (
) )
// APITestSuite - collection of API tests // APITestSuite - collection of API tests
func APITestSuite(c *check.C, create func() ObjectAPI) { func APITestSuite(c *check.C, create func() *objectAPI) {
testMakeBucket(c, create) testMakeBucket(c, create)
testMultipleObjectCreation(c, create) testMultipleObjectCreation(c, create)
testPaging(c, create) testPaging(c, create)
@ -46,17 +46,17 @@ func APITestSuite(c *check.C, create func() ObjectAPI) {
testMultipartObjectAbort(c, create) testMultipartObjectAbort(c, create)
} }
func testMakeBucket(c *check.C, create func() ObjectAPI) { func testMakeBucket(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
err := fs.MakeBucket("bucket") err := obj.MakeBucket("bucket")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
} }
func testMultipartObjectCreation(c *check.C, create func() ObjectAPI) { func testMultipartObjectCreation(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
err := fs.MakeBucket("bucket") err := obj.MakeBucket("bucket")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
uploadID, err := fs.NewMultipartUpload("bucket", "key") uploadID, err := obj.NewMultipartUpload("bucket", "key")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
completedParts := completeMultipartUpload{} completedParts := completeMultipartUpload{}
@ -72,21 +72,21 @@ func testMultipartObjectCreation(c *check.C, create func() ObjectAPI) {
expectedMD5Sumhex := hex.EncodeToString(hasher.Sum(nil)) expectedMD5Sumhex := hex.EncodeToString(hasher.Sum(nil))
var calculatedMD5sum string var calculatedMD5sum string
calculatedMD5sum, err = fs.PutObjectPart("bucket", "key", uploadID, i, int64(len(randomString)), bytes.NewBufferString(randomString), expectedMD5Sumhex) calculatedMD5sum, err = obj.PutObjectPart("bucket", "key", uploadID, i, int64(len(randomString)), bytes.NewBufferString(randomString), expectedMD5Sumhex)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(calculatedMD5sum, check.Equals, expectedMD5Sumhex) c.Assert(calculatedMD5sum, check.Equals, expectedMD5Sumhex)
completedParts.Parts = append(completedParts.Parts, completePart{PartNumber: i, ETag: calculatedMD5sum}) completedParts.Parts = append(completedParts.Parts, completePart{PartNumber: i, ETag: calculatedMD5sum})
} }
objInfo, err := fs.CompleteMultipartUpload("bucket", "key", uploadID, completedParts.Parts) objInfo, err := obj.CompleteMultipartUpload("bucket", "key", uploadID, completedParts.Parts)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objInfo.MD5Sum, check.Equals, "3605d84b1c43b1a664aa7c0d5082d271-10") c.Assert(objInfo.MD5Sum, check.Equals, "3605d84b1c43b1a664aa7c0d5082d271-10")
} }
func testMultipartObjectAbort(c *check.C, create func() ObjectAPI) { func testMultipartObjectAbort(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
err := fs.MakeBucket("bucket") err := obj.MakeBucket("bucket")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
uploadID, err := fs.NewMultipartUpload("bucket", "key") uploadID, err := obj.NewMultipartUpload("bucket", "key")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
parts := make(map[int]string) parts := make(map[int]string)
@ -104,19 +104,19 @@ func testMultipartObjectAbort(c *check.C, create func() ObjectAPI) {
metadata["md5"] = expectedMD5Sumhex metadata["md5"] = expectedMD5Sumhex
var calculatedMD5sum string var calculatedMD5sum string
calculatedMD5sum, err = fs.PutObjectPart("bucket", "key", uploadID, i, int64(len(randomString)), bytes.NewBufferString(randomString), expectedMD5Sumhex) calculatedMD5sum, err = obj.PutObjectPart("bucket", "key", uploadID, i, int64(len(randomString)), bytes.NewBufferString(randomString), expectedMD5Sumhex)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(calculatedMD5sum, check.Equals, expectedMD5Sumhex) c.Assert(calculatedMD5sum, check.Equals, expectedMD5Sumhex)
parts[i] = expectedMD5Sumhex parts[i] = expectedMD5Sumhex
} }
err = fs.AbortMultipartUpload("bucket", "key", uploadID) err = obj.AbortMultipartUpload("bucket", "key", uploadID)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
} }
func testMultipleObjectCreation(c *check.C, create func() ObjectAPI) { func testMultipleObjectCreation(c *check.C, create func() *objectAPI) {
objects := make(map[string][]byte) objects := make(map[string][]byte)
fs := create() obj := create()
err := fs.MakeBucket("bucket") err := obj.MakeBucket("bucket")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
randomPerm := rand.Perm(10) randomPerm := rand.Perm(10)
@ -133,40 +133,40 @@ func testMultipleObjectCreation(c *check.C, create func() ObjectAPI) {
objects[key] = []byte(randomString) objects[key] = []byte(randomString)
metadata := make(map[string]string) metadata := make(map[string]string)
metadata["md5Sum"] = expectedMD5Sumhex metadata["md5Sum"] = expectedMD5Sumhex
objInfo, err := fs.PutObject("bucket", key, int64(len(randomString)), bytes.NewBufferString(randomString), metadata) objInfo, err := obj.PutObject("bucket", key, int64(len(randomString)), bytes.NewBufferString(randomString), metadata)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objInfo.MD5Sum, check.Equals, expectedMD5Sumhex) c.Assert(objInfo.MD5Sum, check.Equals, expectedMD5Sumhex)
} }
for key, value := range objects { for key, value := range objects {
var byteBuffer bytes.Buffer var byteBuffer bytes.Buffer
r, err := fs.GetObject("bucket", key, 0) r, err := obj.GetObject("bucket", key, 0)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, e := io.Copy(&byteBuffer, r) _, e := io.Copy(&byteBuffer, r)
c.Assert(e, check.IsNil) c.Assert(e, check.IsNil)
c.Assert(byteBuffer.Bytes(), check.DeepEquals, value) c.Assert(byteBuffer.Bytes(), check.DeepEquals, value)
c.Assert(r.Close(), check.IsNil) c.Assert(r.Close(), check.IsNil)
objInfo, err := fs.GetObjectInfo("bucket", key) objInfo, err := obj.GetObjectInfo("bucket", key)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objInfo.Size, check.Equals, int64(len(value))) c.Assert(objInfo.Size, check.Equals, int64(len(value)))
r.Close() r.Close()
} }
} }
func testPaging(c *check.C, create func() ObjectAPI) { func testPaging(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
fs.MakeBucket("bucket") obj.MakeBucket("bucket")
result, err := fs.ListObjects("bucket", "", "", "", 0) result, err := obj.ListObjects("bucket", "", "", "", 0)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(result.Objects), check.Equals, 0) c.Assert(len(result.Objects), check.Equals, 0)
c.Assert(result.IsTruncated, check.Equals, false) c.Assert(result.IsTruncated, check.Equals, false)
// check before paging occurs // check before paging occurs
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
key := "obj" + strconv.Itoa(i) key := "obj" + strconv.Itoa(i)
_, err = fs.PutObject("bucket", key, int64(len(key)), bytes.NewBufferString(key), nil) _, err = obj.PutObject("bucket", key, int64(len(key)), bytes.NewBufferString(key), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
result, err = fs.ListObjects("bucket", "", "", "", 5) result, err = obj.ListObjects("bucket", "", "", "", 5)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(result.Objects), check.Equals, i+1) c.Assert(len(result.Objects), check.Equals, i+1)
c.Assert(result.IsTruncated, check.Equals, false) c.Assert(result.IsTruncated, check.Equals, false)
@ -174,27 +174,27 @@ func testPaging(c *check.C, create func() ObjectAPI) {
// check after paging occurs pages work // check after paging occurs pages work
for i := 6; i <= 10; i++ { for i := 6; i <= 10; i++ {
key := "obj" + strconv.Itoa(i) key := "obj" + strconv.Itoa(i)
_, err = fs.PutObject("bucket", key, int64(len(key)), bytes.NewBufferString(key), nil) _, err = obj.PutObject("bucket", key, int64(len(key)), bytes.NewBufferString(key), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
result, err = fs.ListObjects("bucket", "obj", "", "", 5) result, err = obj.ListObjects("bucket", "obj", "", "", 5)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(result.Objects), check.Equals, 5) c.Assert(len(result.Objects), check.Equals, 5)
c.Assert(result.IsTruncated, check.Equals, true) c.Assert(result.IsTruncated, check.Equals, true)
} }
// check paging with prefix at end returns less objects // check paging with prefix at end returns less objects
{ {
_, err = fs.PutObject("bucket", "newPrefix", int64(len("prefix1")), bytes.NewBufferString("prefix1"), nil) _, err = obj.PutObject("bucket", "newPrefix", int64(len("prefix1")), bytes.NewBufferString("prefix1"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = fs.PutObject("bucket", "newPrefix2", int64(len("prefix2")), bytes.NewBufferString("prefix2"), nil) _, err = obj.PutObject("bucket", "newPrefix2", int64(len("prefix2")), bytes.NewBufferString("prefix2"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
result, err = fs.ListObjects("bucket", "new", "", "", 5) result, err = obj.ListObjects("bucket", "new", "", "", 5)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(result.Objects), check.Equals, 2) c.Assert(len(result.Objects), check.Equals, 2)
} }
// check ordering of pages // check ordering of pages
{ {
result, err = fs.ListObjects("bucket", "", "", "", 1000) result, err = obj.ListObjects("bucket", "", "", "", 1000)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Name, check.Equals, "newPrefix") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix")
c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2") c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2")
@ -205,11 +205,11 @@ func testPaging(c *check.C, create func() ObjectAPI) {
// check delimited results with delimiter and prefix // check delimited results with delimiter and prefix
{ {
_, err = fs.PutObject("bucket", "this/is/delimited", int64(len("prefix1")), bytes.NewBufferString("prefix1"), nil) _, err = obj.PutObject("bucket", "this/is/delimited", int64(len("prefix1")), bytes.NewBufferString("prefix1"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = fs.PutObject("bucket", "this/is/also/a/delimited/file", int64(len("prefix2")), bytes.NewBufferString("prefix2"), nil) _, err = obj.PutObject("bucket", "this/is/also/a/delimited/file", int64(len("prefix2")), bytes.NewBufferString("prefix2"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
result, err = fs.ListObjects("bucket", "this/is/", "", "/", 10) result, err = obj.ListObjects("bucket", "this/is/", "", "/", 10)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(result.Objects), check.Equals, 1) c.Assert(len(result.Objects), check.Equals, 1)
c.Assert(result.Prefixes[0], check.Equals, "this/is/also/") c.Assert(result.Prefixes[0], check.Equals, "this/is/also/")
@ -217,7 +217,7 @@ func testPaging(c *check.C, create func() ObjectAPI) {
// check delimited results with delimiter without prefix // check delimited results with delimiter without prefix
{ {
result, err = fs.ListObjects("bucket", "", "", "/", 1000) result, err = obj.ListObjects("bucket", "", "", "/", 1000)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Name, check.Equals, "newPrefix") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix")
c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2") c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2")
@ -229,7 +229,7 @@ func testPaging(c *check.C, create func() ObjectAPI) {
// check results with Marker // check results with Marker
{ {
result, err = fs.ListObjects("bucket", "", "newPrefix", "", 3) result, err = obj.ListObjects("bucket", "", "newPrefix", "", 3)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Name, check.Equals, "newPrefix2") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix2")
c.Assert(result.Objects[1].Name, check.Equals, "obj0") c.Assert(result.Objects[1].Name, check.Equals, "obj0")
@ -237,7 +237,7 @@ func testPaging(c *check.C, create func() ObjectAPI) {
} }
// check ordering of results with prefix // check ordering of results with prefix
{ {
result, err = fs.ListObjects("bucket", "obj", "", "", 1000) result, err = obj.ListObjects("bucket", "obj", "", "", 1000)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Name, check.Equals, "obj0") c.Assert(result.Objects[0].Name, check.Equals, "obj0")
c.Assert(result.Objects[1].Name, check.Equals, "obj1") c.Assert(result.Objects[1].Name, check.Equals, "obj1")
@ -247,27 +247,27 @@ func testPaging(c *check.C, create func() ObjectAPI) {
} }
// check ordering of results with prefix and no paging // check ordering of results with prefix and no paging
{ {
result, err = fs.ListObjects("bucket", "new", "", "", 5) result, err = obj.ListObjects("bucket", "new", "", "", 5)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(result.Objects[0].Name, check.Equals, "newPrefix") c.Assert(result.Objects[0].Name, check.Equals, "newPrefix")
c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2") c.Assert(result.Objects[1].Name, check.Equals, "newPrefix2")
} }
} }
func testObjectOverwriteWorks(c *check.C, create func() ObjectAPI) { func testObjectOverwriteWorks(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
err := fs.MakeBucket("bucket") err := obj.MakeBucket("bucket")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = fs.PutObject("bucket", "object", int64(len("one")), bytes.NewBufferString("one"), nil) _, err = obj.PutObject("bucket", "object", int64(len("one")), bytes.NewBufferString("one"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
// c.Assert(md5Sum1hex, check.Equals, objInfo.MD5Sum) // c.Assert(md5Sum1hex, check.Equals, objInfo.MD5Sum)
_, err = fs.PutObject("bucket", "object", int64(len("three")), bytes.NewBufferString("three"), nil) _, err = obj.PutObject("bucket", "object", int64(len("three")), bytes.NewBufferString("three"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
var bytesBuffer bytes.Buffer var bytesBuffer bytes.Buffer
r, err := fs.GetObject("bucket", "object", 0) r, err := obj.GetObject("bucket", "object", 0)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, e := io.Copy(&bytesBuffer, r) _, e := io.Copy(&bytesBuffer, r)
c.Assert(e, check.IsNil) c.Assert(e, check.IsNil)
@ -275,30 +275,30 @@ func testObjectOverwriteWorks(c *check.C, create func() ObjectAPI) {
c.Assert(r.Close(), check.IsNil) c.Assert(r.Close(), check.IsNil)
} }
func testNonExistantBucketOperations(c *check.C, create func() ObjectAPI) { func testNonExistantBucketOperations(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
_, err := fs.PutObject("bucket", "object", int64(len("one")), bytes.NewBufferString("one"), nil) _, err := obj.PutObject("bucket", "object", int64(len("one")), bytes.NewBufferString("one"), nil)
c.Assert(err, check.Not(check.IsNil)) c.Assert(err, check.Not(check.IsNil))
} }
func testBucketRecreateFails(c *check.C, create func() ObjectAPI) { func testBucketRecreateFails(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
err := fs.MakeBucket("string") err := obj.MakeBucket("string")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
err = fs.MakeBucket("string") err = obj.MakeBucket("string")
c.Assert(err, check.Not(check.IsNil)) c.Assert(err, check.Not(check.IsNil))
} }
func testPutObjectInSubdir(c *check.C, create func() ObjectAPI) { func testPutObjectInSubdir(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
err := fs.MakeBucket("bucket") err := obj.MakeBucket("bucket")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = fs.PutObject("bucket", "dir1/dir2/object", int64(len("hello world")), bytes.NewBufferString("hello world"), nil) _, err = obj.PutObject("bucket", "dir1/dir2/object", int64(len("hello world")), bytes.NewBufferString("hello world"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
var bytesBuffer bytes.Buffer var bytesBuffer bytes.Buffer
r, err := fs.GetObject("bucket", "dir1/dir2/object", 0) r, err := obj.GetObject("bucket", "dir1/dir2/object", 0)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
n, e := io.Copy(&bytesBuffer, r) n, e := io.Copy(&bytesBuffer, r)
c.Assert(e, check.IsNil) c.Assert(e, check.IsNil)
@ -307,49 +307,49 @@ func testPutObjectInSubdir(c *check.C, create func() ObjectAPI) {
c.Assert(r.Close(), check.IsNil) c.Assert(r.Close(), check.IsNil)
} }
func testListBuckets(c *check.C, create func() ObjectAPI) { func testListBuckets(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
// test empty list // test empty list
buckets, err := fs.ListBuckets() buckets, err := obj.ListBuckets()
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(buckets), check.Equals, 0) c.Assert(len(buckets), check.Equals, 0)
// add one and test exists // add one and test exists
err = fs.MakeBucket("bucket1") err = obj.MakeBucket("bucket1")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
buckets, err = fs.ListBuckets() buckets, err = obj.ListBuckets()
c.Assert(len(buckets), check.Equals, 1) c.Assert(len(buckets), check.Equals, 1)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
// add two and test exists // add two and test exists
err = fs.MakeBucket("bucket2") err = obj.MakeBucket("bucket2")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
buckets, err = fs.ListBuckets() buckets, err = obj.ListBuckets()
c.Assert(len(buckets), check.Equals, 2) c.Assert(len(buckets), check.Equals, 2)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
// add three and test exists + prefix // add three and test exists + prefix
err = fs.MakeBucket("bucket22") err = obj.MakeBucket("bucket22")
buckets, err = fs.ListBuckets() buckets, err = obj.ListBuckets()
c.Assert(len(buckets), check.Equals, 3) c.Assert(len(buckets), check.Equals, 3)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
} }
func testListBucketsOrder(c *check.C, create func() ObjectAPI) { func testListBucketsOrder(c *check.C, create func() *objectAPI) {
// if implementation contains a map, order of map keys will vary. // if implementation contains a map, order of map keys will vary.
// this ensures they return in the same order each time // this ensures they return in the same order each time
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
fs := create() obj := create()
// add one and test exists // add one and test exists
err := fs.MakeBucket("bucket1") err := obj.MakeBucket("bucket1")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
err = fs.MakeBucket("bucket2") err = obj.MakeBucket("bucket2")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
buckets, err := fs.ListBuckets() buckets, err := obj.ListBuckets()
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(len(buckets), check.Equals, 2) c.Assert(len(buckets), check.Equals, 2)
c.Assert(buckets[0].Name, check.Equals, "bucket1") c.Assert(buckets[0].Name, check.Equals, "bucket1")
@ -357,20 +357,20 @@ func testListBucketsOrder(c *check.C, create func() ObjectAPI) {
} }
} }
func testListObjectsTestsForNonExistantBucket(c *check.C, create func() ObjectAPI) { func testListObjectsTestsForNonExistantBucket(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
result, err := fs.ListObjects("bucket", "", "", "", 1000) result, err := obj.ListObjects("bucket", "", "", "", 1000)
c.Assert(err, check.Not(check.IsNil)) c.Assert(err, check.Not(check.IsNil))
c.Assert(result.IsTruncated, check.Equals, false) c.Assert(result.IsTruncated, check.Equals, false)
c.Assert(len(result.Objects), check.Equals, 0) c.Assert(len(result.Objects), check.Equals, 0)
} }
func testNonExistantObjectInBucket(c *check.C, create func() ObjectAPI) { func testNonExistantObjectInBucket(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
err := fs.MakeBucket("bucket") err := obj.MakeBucket("bucket")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = fs.GetObject("bucket", "dir1", 0) _, err = obj.GetObject("bucket", "dir1", 0)
c.Assert(err, check.Not(check.IsNil)) c.Assert(err, check.Not(check.IsNil))
switch err := err.ToGoError().(type) { switch err := err.ToGoError().(type) {
case ObjectNotFound: case ObjectNotFound:
@ -380,15 +380,15 @@ func testNonExistantObjectInBucket(c *check.C, create func() ObjectAPI) {
} }
} }
func testGetDirectoryReturnsObjectNotFound(c *check.C, create func() ObjectAPI) { func testGetDirectoryReturnsObjectNotFound(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
err := fs.MakeBucket("bucket") err := obj.MakeBucket("bucket")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = fs.PutObject("bucket", "dir1/dir2/object", int64(len("hello world")), bytes.NewBufferString("hello world"), nil) _, err = obj.PutObject("bucket", "dir1/dir2/object", int64(len("hello world")), bytes.NewBufferString("hello world"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
_, err = fs.GetObject("bucket", "dir1", 0) _, err = obj.GetObject("bucket", "dir1", 0)
switch err := err.ToGoError().(type) { switch err := err.ToGoError().(type) {
case ObjectNotFound: case ObjectNotFound:
c.Assert(err.Bucket, check.Equals, "bucket") c.Assert(err.Bucket, check.Equals, "bucket")
@ -398,7 +398,7 @@ func testGetDirectoryReturnsObjectNotFound(c *check.C, create func() ObjectAPI)
c.Assert(err, check.Equals, "ObjectNotFound") c.Assert(err, check.Equals, "ObjectNotFound")
} }
_, err = fs.GetObject("bucket", "dir1/", 0) _, err = obj.GetObject("bucket", "dir1/", 0)
switch err := err.ToGoError().(type) { switch err := err.ToGoError().(type) {
case ObjectNotFound: case ObjectNotFound:
c.Assert(err.Bucket, check.Equals, "bucket") c.Assert(err.Bucket, check.Equals, "bucket")
@ -409,15 +409,15 @@ func testGetDirectoryReturnsObjectNotFound(c *check.C, create func() ObjectAPI)
} }
} }
func testDefaultContentType(c *check.C, create func() ObjectAPI) { func testDefaultContentType(c *check.C, create func() *objectAPI) {
fs := create() obj := create()
err := fs.MakeBucket("bucket") err := obj.MakeBucket("bucket")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
// Test empty // Test empty
_, err = fs.PutObject("bucket", "one", int64(len("one")), bytes.NewBufferString("one"), nil) _, err = obj.PutObject("bucket", "one", int64(len("one")), bytes.NewBufferString("one"), nil)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
objInfo, err := fs.GetObjectInfo("bucket", "one") objInfo, err := obj.GetObjectInfo("bucket", "one")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(objInfo.ContentType, check.Equals, "application/octet-stream") c.Assert(objInfo.ContentType, check.Equals, "application/octet-stream")
} }

View File

@ -18,19 +18,39 @@ package main
import ( import (
"net/http" "net/http"
"os"
router "github.com/gorilla/mux" router "github.com/gorilla/mux"
"github.com/minio/minio/pkg/probe"
) )
// configureServer handler returns final handler for the http server. // configureServer handler returns final handler for the http server.
func configureServerHandler(objectAPI ObjectAPI) http.Handler { func configureServerHandler(srvCmdConfig serverCmdConfig) http.Handler {
var storageHandlers StorageAPI
if len(srvCmdConfig.exportPaths) == 1 {
// Verify if export path is a local file system path.
st, e := os.Stat(srvCmdConfig.exportPaths[0])
if e == nil && st.Mode().IsDir() {
// Initialize storage API.
storageHandlers, e = newFS(srvCmdConfig.exportPaths[0])
fatalIf(probe.NewError(e), "Initializing fs failed.", nil)
} else {
// Initialize storage API.
storageHandlers, e = newNetworkFS(srvCmdConfig.exportPaths[0])
fatalIf(probe.NewError(e), "Initializing network fs failed.", nil)
}
} // else if - XL part.
// Initialize object layer.
objectAPI := newObjectLayer(storageHandlers)
// Initialize API. // Initialize API.
api := objectStorageAPI{ apiHandlers := objectAPIHandlers{
ObjectAPI: objectAPI, ObjectAPI: objectAPI,
} }
// Initialize Web. // Initialize Web.
web := &webAPI{ webHandlers := &webAPIHandlers{
ObjectAPI: objectAPI, ObjectAPI: objectAPI,
} }
@ -38,8 +58,9 @@ func configureServerHandler(objectAPI ObjectAPI) http.Handler {
mux := router.NewRouter() mux := router.NewRouter()
// Register all routers. // Register all routers.
registerWebRouter(mux, web) registerStorageRPCRouter(mux, storageHandlers)
registerAPIRouter(mux, api) registerWebRouter(mux, webHandlers)
registerAPIRouter(mux, apiHandlers)
// Add new routers here. // Add new routers here.
// List of some generic handlers which are applied for all // List of some generic handlers which are applied for all

View File

@ -69,12 +69,17 @@ EXAMPLES:
`, `,
} }
type serverCmdConfig struct {
serverAddr string
exportPaths []string
}
// configureServer configure a new server instance // configureServer configure a new server instance
func configureServer(serverAddr string, objectAPI ObjectAPI) *http.Server { func configureServer(srvCmdConfig serverCmdConfig) *http.Server {
// Minio server config // Minio server config
apiServer := &http.Server{ apiServer := &http.Server{
Addr: serverAddr, Addr: srvCmdConfig.serverAddr,
Handler: configureServerHandler(objectAPI), Handler: configureServerHandler(srvCmdConfig),
MaxHeaderBytes: 1 << 20, MaxHeaderBytes: 1 << 20,
} }
@ -148,7 +153,7 @@ func initServerConfig(c *cli.Context) {
// Check server arguments. // Check server arguments.
func checkServerSyntax(c *cli.Context) { func checkServerSyntax(c *cli.Context) {
if c.Args().First() == "help" { if !c.Args().Present() && c.Args().First() == "help" {
cli.ShowCommandHelpAndExit(c, "server", 1) cli.ShowCommandHelpAndExit(c, "server", 1)
} }
if len(c.Args()) > 2 { if len(c.Args()) > 2 {
@ -255,26 +260,17 @@ func serverMain(c *cli.Context) {
} }
} }
// Check configured ports. // Check if requested port is available.
checkPortAvailability(getPort(net.JoinHostPort(host, port))) checkPortAvailability(getPort(net.JoinHostPort(host, port)))
var objectAPI ObjectAPI // Save all command line args as export paths.
var err *probe.Error exportPaths := c.Args()
// Set backend FS type.
fsPath := strings.TrimSpace(c.Args().Get(0))
if fsPath != "" {
// Last argument is always a file system path, verify if it exists and is accessible.
_, e := os.Stat(fsPath)
fatalIf(probe.NewError(e), "Unable to validate the path", nil)
// Initialize filesystem storage layer.
storage, e := newFS(fsPath)
fatalIf(probe.NewError(e), "Initializing filesystem failed.", nil)
objectAPI = newObjectLayer(storage)
}
// Configure server. // Configure server.
apiServer := configureServer(serverAddress, objectAPI) apiServer := configureServer(serverCmdConfig{
serverAddr: serverAddress,
exportPaths: exportPaths,
})
// Credential. // Credential.
cred := serverConfig.GetCredential() cred := serverConfig.GetCredential()
@ -305,6 +301,6 @@ func serverMain(c *cli.Context) {
} }
// Start server. // Start server.
err = minhttp.ListenAndServe(apiServer) err := minhttp.ListenAndServe(apiServer)
errorIf(err.Trace(), "Failed to start the minio server.", nil) errorIf(err.Trace(), "Failed to start the minio server.", nil)
} }

View File

@ -96,12 +96,10 @@ func (s *MyAPISuite) SetUpSuite(c *C) {
// Save config. // Save config.
c.Assert(serverConfig.Save(), IsNil) c.Assert(serverConfig.Save(), IsNil)
fs, err := newFS(fsroot) apiServer := configureServer(serverCmdConfig{
c.Assert(err, IsNil) serverAddr: addr,
exportPaths: []string{fsroot},
obj := newObjectLayer(fs) })
apiServer := configureServer(addr, obj)
testAPIFSCacheServer = httptest.NewServer(apiServer.Handler) testAPIFSCacheServer = httptest.NewServer(apiServer.Handler)
} }

View File

@ -1,178 +0,0 @@
/*
* Minio Cloud Storage, (C) 2016 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 main
import (
"errors"
"io"
"net"
"net/http"
"net/rpc"
"time"
)
type networkStorage struct {
address string
connection *rpc.Client
httpClient *http.Client
}
const (
connected = "200 Connected to Go RPC"
dialTimeoutSecs = 30 // 30 seconds.
)
// Initialize new network storage.
func newNetworkStorage(address string) (StorageAPI, error) {
// Dial to the address with timeout of 30secs, this includes DNS resolution.
conn, err := net.DialTimeout("tcp", address, dialTimeoutSecs*time.Second)
if err != nil {
return nil, err
}
// Initialize rpc client with dialed connection.
rpcClient := rpc.NewClient(conn)
// Initialize http client.
httpClient := &http.Client{
// Setting a sensible time out of 2minutes to wait for
// response headers. Request is pro-actively cancelled
// after 2minutes if no response was received from server.
Timeout: 2 * time.Minute,
Transport: http.DefaultTransport,
}
// Initialize network storage.
ndisk := &networkStorage{
address: address,
connection: rpcClient,
httpClient: httpClient,
}
// Returns successfully here.
return ndisk, nil
}
// MakeVol - make a volume.
func (n networkStorage) MakeVol(volume string) error {
reply := GenericReply{}
return n.connection.Call("Storage.MakeVolHandler", volume, &reply)
}
// ListVols - List all volumes.
func (n networkStorage) ListVols() (vols []VolInfo, err error) {
ListVols := ListVolsReply{}
err = n.connection.Call("Storage.ListVolsHandler", "", &ListVols)
if err != nil {
return nil, err
}
return ListVols.Vols, nil
}
// StatVol - get current Stat volume info.
func (n networkStorage) StatVol(volume string) (volInfo VolInfo, err error) {
if err = n.connection.Call("Storage.StatVolHandler", volume, &volInfo); err != nil {
return VolInfo{}, err
}
return volInfo, nil
}
// DeleteVol - Delete a volume.
func (n networkStorage) DeleteVol(volume string) error {
reply := GenericReply{}
return n.connection.Call("Storage.DeleteVolHandler", volume, &reply)
}
// File operations.
// CreateFile - create file.
func (n networkStorage) CreateFile(volume, path string) (writeCloser io.WriteCloser, err error) {
createFileReply := CreateFileReply{}
if err = n.connection.Call("Storage.CreateFileHandler", CreateFileArgs{
Vol: volume,
Path: path,
}, &createFileReply); err != nil {
return nil, err
}
contentType := "application/octet-stream"
readCloser, writeCloser := io.Pipe()
defer readCloser.Close()
go n.httpClient.Post(createFileReply.URL, contentType, readCloser)
return writeCloser, nil
}
// StatFile - get latest Stat information for a file at path.
func (n networkStorage) StatFile(volume, path string) (fileInfo FileInfo, err error) {
if err = n.connection.Call("Storage.StatFileHandler", StatFileArgs{
Vol: volume,
Path: path,
}, &fileInfo); err != nil {
return FileInfo{}, err
}
return fileInfo, nil
}
// ReadFile - reads a file.
func (n networkStorage) ReadFile(volume string, path string, offset int64) (reader io.ReadCloser, err error) {
readFileReply := ReadFileReply{}
if err = n.connection.Call("Storage.ReadFileHandler", ReadFileArgs{
Vol: volume,
Path: path,
Offset: offset,
}, &readFileReply); err != nil {
return nil, err
}
resp, err := n.httpClient.Get(readFileReply.URL)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New("Invalid response")
}
return resp.Body, nil
}
// ListFiles - List all files in a volume.
func (n networkStorage) ListFiles(volume, prefix, marker string, recursive bool, count int) (files []FileInfo, eof bool, err error) {
listFilesReply := ListFilesReply{}
if err = n.connection.Call("Storage.ListFilesHandler", ListFilesArgs{
Vol: volume,
Prefix: prefix,
Marker: marker,
Recursive: recursive,
Count: count,
}, &listFilesReply); err != nil {
return nil, true, err
}
// List of files.
files = listFilesReply.Files
// EOF.
eof = listFilesReply.EOF
return files, eof, nil
}
// DeleteFile - Delete a file at path.
func (n networkStorage) DeleteFile(volume, path string) (err error) {
reply := GenericReply{}
if err = n.connection.Call("Storage.DeleteFileHandler", DeleteFileArgs{
Vol: volume,
Path: path,
}, &reply); err != nil {
return err
}
return nil
}

View File

@ -42,29 +42,6 @@ type ListFilesReply struct {
EOF bool EOF bool
} }
// ReadFileArgs read file args.
type ReadFileArgs struct {
Vol string
Path string
Offset int64
}
// ReadFileReply read file reply.
type ReadFileReply struct {
URL string
}
// CreateFileArgs create file args.
type CreateFileArgs struct {
Vol string
Path string
}
// CreateFileReply create file reply.
type CreateFileReply struct {
URL string
}
// StatFileArgs stat file args. // StatFileArgs stat file args.
type StatFileArgs struct { type StatFileArgs struct {
Vol string Vol string

View File

@ -1,17 +1,13 @@
package main package main
import ( import (
"fmt"
"io" "io"
"net/http" "net/http"
"net/rpc" "net/rpc"
"net/url"
"os" "os"
"path"
"strconv" "strconv"
router "github.com/gorilla/mux" router "github.com/gorilla/mux"
"github.com/minio/minio/pkg/probe"
"github.com/minio/minio/pkg/safe" "github.com/minio/minio/pkg/safe"
) )
@ -67,32 +63,6 @@ func (s *storageServer) ListFilesHandler(arg *ListFilesArgs, reply *ListFilesRep
return nil return nil
} }
// ReadFileHandler - read file handler is a wrapper to provide
// destination URL for reading files.
func (s *storageServer) ReadFileHandler(arg *ReadFileArgs, reply *ReadFileReply) error {
endpoint := "http://localhost:9000/minio/rpc/storage" // TODO fix this.
newURL, err := url.Parse(fmt.Sprintf("%s/%s", endpoint, path.Join(arg.Vol, arg.Path)))
if err != nil {
return err
}
q := newURL.Query()
q.Set("offset", fmt.Sprintf("%d", arg.Offset))
newURL.RawQuery = q.Encode()
reply.URL = newURL.String()
return nil
}
// CreateFileHandler - create file handler is rpc wrapper to create file.
func (s *storageServer) CreateFileHandler(arg *CreateFileArgs, reply *CreateFileReply) error {
endpoint := "http://localhost:9000/minio/rpc/storage" // TODO fix this.
newURL, err := url.Parse(fmt.Sprintf("%s/%s", endpoint, path.Join(arg.Vol, arg.Path)))
if err != nil {
return err
}
reply.URL = newURL.String()
return nil
}
// StatFileHandler - stat file handler is rpc wrapper to stat file. // StatFileHandler - stat file handler is rpc wrapper to stat file.
func (s *storageServer) StatFileHandler(arg *StatFileArgs, reply *FileInfo) error { func (s *storageServer) StatFileHandler(arg *StatFileArgs, reply *FileInfo) error {
fileInfo, err := s.storage.StatFile(arg.Vol, arg.Path) fileInfo, err := s.storage.StatFile(arg.Vol, arg.Path)
@ -108,58 +78,56 @@ func (s *storageServer) DeleteFileHandler(arg *DeleteFileArgs, reply *GenericRep
return s.storage.DeleteFile(arg.Vol, arg.Path) return s.storage.DeleteFile(arg.Vol, arg.Path)
} }
// StreamUpload - stream upload handler. // registerStorageRPCRouter - register storage rpc router.
func (s *storageServer) StreamUploadHandler(w http.ResponseWriter, r *http.Request) { func registerStorageRPCRouter(mux *router.Router, storageAPI StorageAPI) {
vars := router.Vars(r)
volume := vars["volume"]
path := vars["path"]
writeCloser, err := s.storage.CreateFile(volume, path)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
reader := r.Body
if _, err = io.Copy(writeCloser, reader); err != nil {
writeCloser.(*safe.File).CloseAndRemove()
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
writeCloser.Close()
}
// StreamDownloadHandler - stream download handler.
func (s *storageServer) StreamDownloadHandler(w http.ResponseWriter, r *http.Request) {
vars := router.Vars(r)
volume := vars["volume"]
path := vars["path"]
offset, err := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
readCloser, err := s.storage.ReadFile(volume, path, offset)
if err != nil {
httpErr := http.StatusBadRequest
if os.IsNotExist(err) {
httpErr = http.StatusNotFound
}
http.Error(w, err.Error(), httpErr)
return
}
io.Copy(w, readCloser)
}
func registerStorageServer(mux *router.Router, diskPath string) {
// Minio storage routes.
fs, e := newFS(diskPath)
fatalIf(probe.NewError(e), "Unable to initialize storage disk.", nil)
storageRPCServer := rpc.NewServer()
stServer := &storageServer{ stServer := &storageServer{
storage: fs, storage: storageAPI,
} }
storageRPCServer := rpc.NewServer()
storageRPCServer.RegisterName("Storage", stServer) storageRPCServer.RegisterName("Storage", stServer)
storageRouter := mux.NewRoute().PathPrefix(reservedBucket).Subrouter() storageRouter := mux.NewRoute().PathPrefix(reservedBucket).Subrouter()
// Add minio storage routes.
storageRouter.Path("/rpc/storage").Handler(storageRPCServer) storageRouter.Path("/rpc/storage").Handler(storageRPCServer)
storageRouter.Methods("POST").Path("/rpc/storage/upload/{volume}/{path:.+}").HandlerFunc(stServer.StreamUploadHandler) // StreamUpload - stream upload handler.
storageRouter.Methods("GET").Path("/rpc/storage/download/{volume}/{path:.+}").Queries("offset", "").HandlerFunc(stServer.StreamDownloadHandler) storageRouter.Methods("POST").Path("/rpc/storage/upload/{volume}/{path:.+}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := router.Vars(r)
volume := vars["volume"]
path := vars["path"]
writeCloser, err := stServer.storage.CreateFile(volume, path)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
reader := r.Body
if _, err = io.Copy(writeCloser, reader); err != nil {
writeCloser.(*safe.File).CloseAndRemove()
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
writeCloser.Close()
reader.Close()
})
// StreamDownloadHandler - stream download handler.
storageRouter.Methods("GET").Path("/rpc/storage/download/{volume}/{path:.+}").Queries("offset", "{offset:.*}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
vars := router.Vars(r)
volume := vars["volume"]
path := vars["path"]
offset, err := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
readCloser, err := stServer.storage.ReadFile(volume, path, offset)
if err != nil {
httpErr := http.StatusBadRequest
if os.IsNotExist(err) {
httpErr = http.StatusNotFound
}
http.Error(w, err.Error(), httpErr)
return
}
io.Copy(w, readCloser)
w.(http.Flusher).Flush()
readCloser.Close()
})
} }

View File

@ -71,7 +71,7 @@ type ServerInfoRep struct {
} }
// ServerInfo - get server info. // ServerInfo - get server info.
func (web *webAPI) ServerInfo(r *http.Request, args *WebGenericArgs, reply *ServerInfoRep) error { func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, reply *ServerInfoRep) error {
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"} return &json2.Error{Message: "Unauthorized request"}
} }
@ -106,7 +106,7 @@ type DiskInfoRep struct {
} }
// DiskInfo - get disk statistics. // DiskInfo - get disk statistics.
func (web *webAPI) DiskInfo(r *http.Request, args *WebGenericArgs, reply *DiskInfoRep) error { func (web *webAPIHandlers) DiskInfo(r *http.Request, args *WebGenericArgs, reply *DiskInfoRep) error {
// FIXME: bring in StatFS in StorageAPI interface and uncomment the below lines. // FIXME: bring in StatFS in StorageAPI interface and uncomment the below lines.
// if !isJWTReqAuthenticated(r) { // if !isJWTReqAuthenticated(r) {
// return &json2.Error{Message: "Unauthorized request"} // return &json2.Error{Message: "Unauthorized request"}
@ -126,7 +126,7 @@ type MakeBucketArgs struct {
} }
// MakeBucket - make a bucket. // MakeBucket - make a bucket.
func (web *webAPI) MakeBucket(r *http.Request, args *MakeBucketArgs, reply *WebGenericRep) error { func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, reply *WebGenericRep) error {
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"} return &json2.Error{Message: "Unauthorized request"}
} }
@ -153,7 +153,7 @@ type WebBucketInfo struct {
} }
// ListBuckets - list buckets api. // ListBuckets - list buckets api.
func (web *webAPI) ListBuckets(r *http.Request, args *WebGenericArgs, reply *ListBucketsRep) error { func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, reply *ListBucketsRep) error {
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"} return &json2.Error{Message: "Unauthorized request"}
} }
@ -199,7 +199,7 @@ type WebObjectInfo struct {
} }
// ListObjects - list objects api. // ListObjects - list objects api.
func (web *webAPI) ListObjects(r *http.Request, args *ListObjectsArgs, reply *ListObjectsRep) error { func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, reply *ListObjectsRep) error {
marker := "" marker := ""
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"} return &json2.Error{Message: "Unauthorized request"}
@ -238,7 +238,7 @@ type RemoveObjectArgs struct {
} }
// RemoveObject - removes an object. // RemoveObject - removes an object.
func (web *webAPI) RemoveObject(r *http.Request, args *RemoveObjectArgs, reply *WebGenericRep) error { func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs, reply *WebGenericRep) error {
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"} return &json2.Error{Message: "Unauthorized request"}
} }
@ -263,7 +263,7 @@ type LoginRep struct {
} }
// Login - user login handler. // Login - user login handler.
func (web *webAPI) Login(r *http.Request, args *LoginArgs, reply *LoginRep) error { func (web *webAPIHandlers) Login(r *http.Request, args *LoginArgs, reply *LoginRep) error {
jwt := initJWT() jwt := initJWT()
if jwt.Authenticate(args.Username, args.Password) { if jwt.Authenticate(args.Username, args.Password) {
token, err := jwt.GenerateToken(args.Username) token, err := jwt.GenerateToken(args.Username)
@ -284,7 +284,7 @@ type GenerateAuthReply struct {
UIVersion string `json:"uiVersion"` UIVersion string `json:"uiVersion"`
} }
func (web webAPI) GenerateAuth(r *http.Request, args *WebGenericArgs, reply *GenerateAuthReply) error { func (web webAPIHandlers) GenerateAuth(r *http.Request, args *WebGenericArgs, reply *GenerateAuthReply) error {
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"} return &json2.Error{Message: "Unauthorized request"}
} }
@ -308,7 +308,7 @@ type SetAuthReply struct {
} }
// SetAuth - Set accessKey and secretKey credentials. // SetAuth - Set accessKey and secretKey credentials.
func (web *webAPI) SetAuth(r *http.Request, args *SetAuthArgs, reply *SetAuthReply) error { func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *SetAuthReply) error {
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"} return &json2.Error{Message: "Unauthorized request"}
} }
@ -345,7 +345,7 @@ type GetAuthReply struct {
} }
// GetAuth - return accessKey and secretKey credentials. // GetAuth - return accessKey and secretKey credentials.
func (web *webAPI) GetAuth(r *http.Request, args *WebGenericArgs, reply *GetAuthReply) error { func (web *webAPIHandlers) GetAuth(r *http.Request, args *WebGenericArgs, reply *GetAuthReply) error {
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"} return &json2.Error{Message: "Unauthorized request"}
} }
@ -357,7 +357,7 @@ func (web *webAPI) GetAuth(r *http.Request, args *WebGenericArgs, reply *GetAuth
} }
// Upload - file upload handler. // Upload - file upload handler.
func (web *webAPI) Upload(w http.ResponseWriter, r *http.Request) { func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
writeWebErrorResponse(w, errInvalidToken) writeWebErrorResponse(w, errInvalidToken)
return return
@ -371,7 +371,7 @@ func (web *webAPI) Upload(w http.ResponseWriter, r *http.Request) {
} }
// Download - file download handler. // Download - file download handler.
func (web *webAPI) Download(w http.ResponseWriter, r *http.Request) { func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
bucket := vars["bucket"] bucket := vars["bucket"]
object := vars["object"] object := vars["object"]

View File

@ -29,8 +29,8 @@ import (
) )
// webAPI container for Web API. // webAPI container for Web API.
type webAPI struct { type webAPIHandlers struct {
ObjectAPI ObjectAPI ObjectAPI *objectAPI
} }
// indexHandler - Handler to serve index.html // indexHandler - Handler to serve index.html
@ -58,7 +58,7 @@ func assetFS() *assetfs.AssetFS {
const specialAssets = "loader.css|logo.svg|firefox.png|safari.png|chrome.png|favicon.ico" const specialAssets = "loader.css|logo.svg|firefox.png|safari.png|chrome.png|favicon.ico"
// registerWebRouter - registers web router for serving minio browser. // registerWebRouter - registers web router for serving minio browser.
func registerWebRouter(mux *router.Router, web *webAPI) { func registerWebRouter(mux *router.Router, web *webAPIHandlers) {
// Initialize a new json2 codec. // Initialize a new json2 codec.
codec := json2.NewCodec() codec := json2.NewCodec()
@ -74,7 +74,7 @@ func registerWebRouter(mux *router.Router, web *webAPI) {
// RPC handler at URI - /minio/webrpc // RPC handler at URI - /minio/webrpc
webBrowserRouter.Methods("POST").Path("/webrpc").Handler(webRPC) webBrowserRouter.Methods("POST").Path("/webrpc").Handler(webRPC)
webBrowserRouter.Methods("PUT").Path("/upload/{bucket}/{object:.+}").HandlerFunc(web.Upload) webBrowserRouter.Methods("PUT").Path("/upload/{bucket}/{object:.+}").HandlerFunc(web.Upload)
webBrowserRouter.Methods("GET").Path("/download/{bucket}/{object:.+}").Queries("token", "").HandlerFunc(web.Download) webBrowserRouter.Methods("GET").Path("/download/{bucket}/{object:.+}").Queries("token", "{token:.*}").HandlerFunc(web.Download)
// Add compression for assets. // Add compression for assets.
compressedAssets := handlers.CompressHandler(http.StripPrefix(reservedBucket, http.FileServer(assetFS()))) compressedAssets := handlers.CompressHandler(http.StripPrefix(reservedBucket, http.FileServer(assetFS())))