parent
01a439f95b
commit
30b0b4deba
@ -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 |
||||
} |
@ -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 |
||||
} |
@ -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 |
||||
} |
Loading…
Reference in new issue