mirror of
https://github.com/minio/minio.git
synced 2025-05-22 18:11:50 -04:00
Merge pull request #257 from harshavardhana/pr_out_update_contributing_guide
This commit is contained in:
commit
02ccf123c9
@ -54,7 +54,8 @@ Building Libraries
|
|||||||
- Run `make save` from top-level directory (or `godep restore && godep save ./...`).
|
- Run `make save` from top-level directory (or `godep restore && godep save ./...`).
|
||||||
* When you're ready to create a pull request, be sure to:
|
* When you're ready to create a pull request, be sure to:
|
||||||
- Have test cases for the new code. If you have questions about how to do it, please ask in your pull request.
|
- Have test cases for the new code. If you have questions about how to do it, please ask in your pull request.
|
||||||
- Run `go fmt`
|
- Run `go fmt
|
||||||
|
- Run `golint` (`go get github.com/golang/lint/golint`)
|
||||||
- Squash your commits into a single commit. `git rebase -i`. It's okay to force update your pull request.
|
- Squash your commits into a single commit. `git rebase -i`. It's okay to force update your pull request.
|
||||||
- Make sure `go test -race ./...` and `go build` completes.
|
- Make sure `go test -race ./...` and `go build` completes.
|
||||||
* Read [Effective Go](https://github.com/golang/go/wiki/CodeReviewComments) article from Golang project
|
* Read [Effective Go](https://github.com/golang/go/wiki/CodeReviewComments) article from Golang project
|
||||||
|
20
main.go
20
main.go
@ -27,14 +27,14 @@ import (
|
|||||||
func getStorageType(input string) server.StorageType {
|
func getStorageType(input string) server.StorageType {
|
||||||
switch {
|
switch {
|
||||||
case input == "file":
|
case input == "file":
|
||||||
return server.FileStorage
|
return server.File
|
||||||
case input == "inmemory":
|
case input == "inmemory":
|
||||||
return server.InMemoryStorage
|
return server.InMemory
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
log.Println("Unknown storage type:", input)
|
log.Println("Unknown storage type:", input)
|
||||||
log.Println("Choosing default storage type as 'file'..")
|
log.Println("Choosing default storage type as 'file'..")
|
||||||
return server.FileStorage
|
return server.File
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,24 +51,24 @@ func runCmd(c *cli.Context) {
|
|||||||
}
|
}
|
||||||
tls := (certFile != "" && keyFile != "")
|
tls := (certFile != "" && keyFile != "")
|
||||||
storageType := getStorageType(storageTypeStr)
|
storageType := getStorageType(storageTypeStr)
|
||||||
var serverConfigs []server.ServerConfig
|
var serverConfigs []server.Config
|
||||||
apiServerConfig := server.ServerConfig{
|
apiServerConfig := server.Config{
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
Address: apiaddress,
|
Address: apiaddress,
|
||||||
Tls: tls,
|
TLS: tls,
|
||||||
CertFile: certFile,
|
CertFile: certFile,
|
||||||
KeyFile: keyFile,
|
KeyFile: keyFile,
|
||||||
ApiType: server.MinioApi{
|
APIType: server.MinioAPI{
|
||||||
StorageType: storageType,
|
StorageType: storageType,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
webUIServerConfig := server.ServerConfig{
|
webUIServerConfig := server.Config{
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
Address: webaddress,
|
Address: webaddress,
|
||||||
Tls: false,
|
TLS: false,
|
||||||
CertFile: "",
|
CertFile: "",
|
||||||
KeyFile: "",
|
KeyFile: "",
|
||||||
ApiType: server.WebUIApi{
|
APIType: server.WebAPI{
|
||||||
Websocket: false,
|
Websocket: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ var _ = Suite(&MySuite{})
|
|||||||
|
|
||||||
func (s *MySuite) TestNonExistantObject(c *C) {
|
func (s *MySuite) TestNonExistantObject(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ func (s *MySuite) TestNonExistantObject(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestEmptyObject(c *C) {
|
func (s *MySuite) TestEmptyObject(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ func (s *MySuite) TestEmptyObject(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestObject(c *C) {
|
func (s *MySuite) TestObject(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ func (s *MySuite) TestObject(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestMultipleObjects(c *C) {
|
func (s *MySuite) TestMultipleObjects(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ func (s *MySuite) TestMultipleObjects(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestNotImplemented(c *C) {
|
func (s *MySuite) TestNotImplemented(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ func (s *MySuite) TestNotImplemented(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestHeader(c *C) {
|
func (s *MySuite) TestHeader(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ func (s *MySuite) TestHeader(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestPutBucket(c *C) {
|
func (s *MySuite) TestPutBucket(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -240,7 +240,7 @@ func (s *MySuite) TestPutBucket(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestPutObject(c *C) {
|
func (s *MySuite) TestPutObject(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -297,7 +297,7 @@ func (s *MySuite) TestPutObject(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestListBuckets(c *C) {
|
func (s *MySuite) TestListBuckets(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -376,7 +376,7 @@ func verifyHeaders(c *C, header http.Header, date time.Time, size int, contentTy
|
|||||||
|
|
||||||
func (s *MySuite) TestXMLNameNotInBucketListJson(c *C) {
|
func (s *MySuite) TestXMLNameNotInBucketListJson(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -401,7 +401,7 @@ func (s *MySuite) TestXMLNameNotInBucketListJson(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestXMLNameNotInObjectListJson(c *C) {
|
func (s *MySuite) TestXMLNameNotInObjectListJson(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
@ -426,7 +426,7 @@ func (s *MySuite) TestXMLNameNotInObjectListJson(c *C) {
|
|||||||
|
|
||||||
func (s *MySuite) TestContentTypePersists(c *C) {
|
func (s *MySuite) TestContentTypePersists(c *C) {
|
||||||
_, _, storage := inmemory.Start()
|
_, _, storage := inmemory.Start()
|
||||||
httpHandler := HttpHandler("", storage)
|
httpHandler := HTTPHandler("", storage)
|
||||||
testServer := httptest.NewServer(httpHandler)
|
testServer := httptest.NewServer(httpHandler)
|
||||||
defer testServer.Close()
|
defer testServer.Close()
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ import (
|
|||||||
// 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 (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Request) {
|
func (server *minioAPI) listObjectsHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchBucket)
|
error := errorCodeError(NoSuchBucket)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.ImplementationError:
|
case mstorage.ImplementationError:
|
||||||
@ -65,21 +65,21 @@ func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BucketNameInvalid:
|
case mstorage.BucketNameInvalid:
|
||||||
{
|
{
|
||||||
error := errorCodeError(InvalidBucketName)
|
error := errorCodeError(InvalidBucketName)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.ObjectNameInvalid:
|
case mstorage.ObjectNameInvalid:
|
||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchKey)
|
error := errorCodeError(NoSuchKey)
|
||||||
errorResponse := getErrorResponse(error, resources.Prefix)
|
errorResponse := getErrorResponse(error, resources.Prefix)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
// -----------
|
// -----------
|
||||||
// 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 (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Request) {
|
func (server *minioAPI) listBucketsHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
acceptsContentType := getContentType(req)
|
acceptsContentType := getContentType(req)
|
||||||
buckets, err := server.storage.ListBuckets()
|
buckets, err := server.storage.ListBuckets()
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
@ -103,7 +103,7 @@ func (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, "")
|
errorResponse := getErrorResponse(error, "")
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BackendCorrupted:
|
case mstorage.BackendCorrupted:
|
||||||
@ -111,7 +111,7 @@ func (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, "")
|
errorResponse := getErrorResponse(error, "")
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ func (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Requ
|
|||||||
// PUT Bucket
|
// 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 (server *minioApi) putBucketHandler(w http.ResponseWriter, req *http.Request) {
|
func (server *minioAPI) putBucketHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
err := server.storage.StoreBucket(bucket)
|
err := server.storage.StoreBucket(bucket)
|
||||||
@ -142,14 +142,14 @@ func (server *minioApi) putBucketHandler(w http.ResponseWriter, req *http.Reques
|
|||||||
{
|
{
|
||||||
error := errorCodeError(InvalidBucketName)
|
error := errorCodeError(InvalidBucketName)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BucketExists:
|
case mstorage.BucketExists:
|
||||||
{
|
{
|
||||||
error := errorCodeError(BucketAlreadyExists)
|
error := errorCodeError(BucketAlreadyExists)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.ImplementationError:
|
case mstorage.ImplementationError:
|
||||||
@ -158,7 +158,7 @@ func (server *minioApi) putBucketHandler(w http.ResponseWriter, req *http.Reques
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ type ObjectListResponse struct {
|
|||||||
CommonPrefixes []*Prefix
|
CommonPrefixes []*Prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bucket list response format
|
// BucketListResponse - bucket list response format
|
||||||
type BucketListResponse struct {
|
type BucketListResponse struct {
|
||||||
XMLName xml.Name `xml:"ListAllMyBucketsResult" json:"-"`
|
XMLName xml.Name `xml:"ListAllMyBucketsResult" json:"-"`
|
||||||
Owner Owner
|
Owner Owner
|
||||||
@ -47,17 +47,18 @@ type BucketListResponse struct {
|
|||||||
} // Buckets are nested
|
} // Buckets are nested
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prefix - common prefix
|
||||||
type Prefix struct {
|
type Prefix struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bucket struct
|
// Bucket - bucket item
|
||||||
type Bucket struct {
|
type Bucket struct {
|
||||||
Name string
|
Name string
|
||||||
CreationDate string
|
CreationDate string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object struct
|
// Item - object item
|
||||||
type Item struct {
|
type Item struct {
|
||||||
Key string
|
Key string
|
||||||
LastModified string
|
LastModified string
|
||||||
@ -67,6 +68,7 @@ type Item struct {
|
|||||||
Owner Owner
|
Owner Owner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Owner - bucket owner/principal
|
||||||
type Owner struct {
|
type Owner struct {
|
||||||
ID string
|
ID string
|
||||||
DisplayName string
|
DisplayName string
|
||||||
|
@ -25,17 +25,17 @@ import (
|
|||||||
type Error struct {
|
type Error struct {
|
||||||
Code string
|
Code string
|
||||||
Description string
|
Description string
|
||||||
HttpStatusCode int
|
HTTPStatusCode int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error response format
|
// ErrorResponse - error response format
|
||||||
type ErrorResponse struct {
|
type ErrorResponse struct {
|
||||||
XMLName xml.Name `xml:"Error" json:"-"`
|
XMLName xml.Name `xml:"Error" json:"-"`
|
||||||
Code string
|
Code string
|
||||||
Message string
|
Message string
|
||||||
Resource string
|
Resource string
|
||||||
RequestId string
|
RequestID string
|
||||||
HostId string
|
HostID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error codes, non exhaustive list
|
// Error codes, non exhaustive list
|
||||||
@ -47,7 +47,7 @@ const (
|
|||||||
EntityTooLarge
|
EntityTooLarge
|
||||||
IncompleteBody
|
IncompleteBody
|
||||||
InternalError
|
InternalError
|
||||||
InvalidAccessKeyId
|
InvalidAccessKeyID
|
||||||
InvalidBucketName
|
InvalidBucketName
|
||||||
InvalidDigest
|
InvalidDigest
|
||||||
InvalidRange
|
InvalidRange
|
||||||
@ -70,117 +70,117 @@ var errorCodeResponse = map[int]Error{
|
|||||||
AccessDenied: {
|
AccessDenied: {
|
||||||
Code: "AccessDenied",
|
Code: "AccessDenied",
|
||||||
Description: "Access Denied",
|
Description: "Access Denied",
|
||||||
HttpStatusCode: http.StatusForbidden,
|
HTTPStatusCode: http.StatusForbidden,
|
||||||
},
|
},
|
||||||
BadDigest: {
|
BadDigest: {
|
||||||
Code: "BadDigest",
|
Code: "BadDigest",
|
||||||
Description: "The Content-MD5 you specified did not match what we received.",
|
Description: "The Content-MD5 you specified did not match what we received.",
|
||||||
HttpStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
BucketAlreadyExists: {
|
BucketAlreadyExists: {
|
||||||
Code: "BucketAlreadyExists",
|
Code: "BucketAlreadyExists",
|
||||||
Description: "The requested bucket name is not available.",
|
Description: "The requested bucket name is not available.",
|
||||||
HttpStatusCode: http.StatusConflict,
|
HTTPStatusCode: http.StatusConflict,
|
||||||
},
|
},
|
||||||
EntityTooSmall: {
|
EntityTooSmall: {
|
||||||
Code: "EntityTooSmall",
|
Code: "EntityTooSmall",
|
||||||
Description: "Your proposed upload is smaller than the minimum allowed object size.",
|
Description: "Your proposed upload is smaller than the minimum allowed object size.",
|
||||||
HttpStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
EntityTooLarge: {
|
EntityTooLarge: {
|
||||||
Code: "EntityTooLarge",
|
Code: "EntityTooLarge",
|
||||||
Description: "Your proposed upload exceeds the maximum allowed object size.",
|
Description: "Your proposed upload exceeds the maximum allowed object size.",
|
||||||
HttpStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
IncompleteBody: {
|
IncompleteBody: {
|
||||||
Code: "IncompleteBody",
|
Code: "IncompleteBody",
|
||||||
Description: "You did not provide the number of bytes specified by the Content-Length HTTP header",
|
Description: "You did not provide the number of bytes specified by the Content-Length HTTP header",
|
||||||
HttpStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
InternalError: {
|
InternalError: {
|
||||||
Code: "InternalError",
|
Code: "InternalError",
|
||||||
Description: "We encountered an internal error, please try again.",
|
Description: "We encountered an internal error, please try again.",
|
||||||
HttpStatusCode: http.StatusInternalServerError,
|
HTTPStatusCode: http.StatusInternalServerError,
|
||||||
},
|
},
|
||||||
InvalidAccessKeyId: {
|
InvalidAccessKeyID: {
|
||||||
Code: "InvalidAccessKeyId",
|
Code: "InvalidAccessKeyID",
|
||||||
Description: "The access key Id you provided does not exist in our records.",
|
Description: "The access key ID you provided does not exist in our records.",
|
||||||
HttpStatusCode: http.StatusForbidden,
|
HTTPStatusCode: http.StatusForbidden,
|
||||||
},
|
},
|
||||||
InvalidBucketName: {
|
InvalidBucketName: {
|
||||||
Code: "InvalidBucketName",
|
Code: "InvalidBucketName",
|
||||||
Description: "The specified bucket is not valid.",
|
Description: "The specified bucket is not valid.",
|
||||||
HttpStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
InvalidDigest: {
|
InvalidDigest: {
|
||||||
Code: "InvalidDigest",
|
Code: "InvalidDigest",
|
||||||
Description: "The Content-MD5 you specified is not valid.",
|
Description: "The Content-MD5 you specified is not valid.",
|
||||||
HttpStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
InvalidRange: {
|
InvalidRange: {
|
||||||
Code: "InvalidRange",
|
Code: "InvalidRange",
|
||||||
Description: "The requested range cannot be satisfied.",
|
Description: "The requested range cannot be satisfied.",
|
||||||
HttpStatusCode: http.StatusRequestedRangeNotSatisfiable,
|
HTTPStatusCode: http.StatusRequestedRangeNotSatisfiable,
|
||||||
},
|
},
|
||||||
MalformedXML: {
|
MalformedXML: {
|
||||||
Code: "MalformedXML",
|
Code: "MalformedXML",
|
||||||
Description: "The XML you provided was not well-formed or did not validate against our published schema.",
|
Description: "The XML you provided was not well-formed or did not validate against our published schema.",
|
||||||
HttpStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
MissingContentLength: {
|
MissingContentLength: {
|
||||||
Code: "MissingContentLength",
|
Code: "MissingContentLength",
|
||||||
Description: "You must provide the Content-Length HTTP header.",
|
Description: "You must provide the Content-Length HTTP header.",
|
||||||
HttpStatusCode: http.StatusLengthRequired,
|
HTTPStatusCode: http.StatusLengthRequired,
|
||||||
},
|
},
|
||||||
MissingRequestBodyError: {
|
MissingRequestBodyError: {
|
||||||
Code: "MissingRequestBodyError",
|
Code: "MissingRequestBodyError",
|
||||||
Description: "Request body is empty.",
|
Description: "Request body is empty.",
|
||||||
HttpStatusCode: http.StatusLengthRequired,
|
HTTPStatusCode: http.StatusLengthRequired,
|
||||||
},
|
},
|
||||||
NoSuchBucket: {
|
NoSuchBucket: {
|
||||||
Code: "NoSuchBucket",
|
Code: "NoSuchBucket",
|
||||||
Description: "The specified bucket does not exist.",
|
Description: "The specified bucket does not exist.",
|
||||||
HttpStatusCode: http.StatusNotFound,
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
},
|
},
|
||||||
NoSuchKey: {
|
NoSuchKey: {
|
||||||
Code: "NoSuchKey",
|
Code: "NoSuchKey",
|
||||||
Description: "The specified key does not exist.",
|
Description: "The specified key does not exist.",
|
||||||
HttpStatusCode: http.StatusNotFound,
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
},
|
},
|
||||||
NoSuchUpload: {
|
NoSuchUpload: {
|
||||||
Code: "NoSuchUpload",
|
Code: "NoSuchUpload",
|
||||||
Description: "The specified multipart upload does not exist.",
|
Description: "The specified multipart upload does not exist.",
|
||||||
HttpStatusCode: http.StatusNotFound,
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
},
|
},
|
||||||
NotImplemented: {
|
NotImplemented: {
|
||||||
Code: "NotImplemented",
|
Code: "NotImplemented",
|
||||||
Description: "A header you provided implies functionality that is not implemented.",
|
Description: "A header you provided implies functionality that is not implemented.",
|
||||||
HttpStatusCode: http.StatusNotImplemented,
|
HTTPStatusCode: http.StatusNotImplemented,
|
||||||
},
|
},
|
||||||
RequestTimeTooSkewed: {
|
RequestTimeTooSkewed: {
|
||||||
Code: "RequestTimeTooSkewed",
|
Code: "RequestTimeTooSkewed",
|
||||||
Description: "The difference between the request time and the server's time is too large.",
|
Description: "The difference between the request time and the server's time is too large.",
|
||||||
HttpStatusCode: http.StatusForbidden,
|
HTTPStatusCode: http.StatusForbidden,
|
||||||
},
|
},
|
||||||
SignatureDoesNotMatch: {
|
SignatureDoesNotMatch: {
|
||||||
Code: "SignatureDoesNotMatch",
|
Code: "SignatureDoesNotMatch",
|
||||||
Description: "The request signature we calculated does not match the signature you provided.",
|
Description: "The request signature we calculated does not match the signature you provided.",
|
||||||
HttpStatusCode: http.StatusForbidden,
|
HTTPStatusCode: http.StatusForbidden,
|
||||||
},
|
},
|
||||||
TooManyBuckets: {
|
TooManyBuckets: {
|
||||||
Code: "TooManyBuckets",
|
Code: "TooManyBuckets",
|
||||||
Description: "You have attempted to create more buckets than allowed.",
|
Description: "You have attempted to create more buckets than allowed.",
|
||||||
HttpStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
InvalidPolicyDocument: {
|
InvalidPolicyDocument: {
|
||||||
Code: "InvalidPolicyDocument",
|
Code: "InvalidPolicyDocument",
|
||||||
Description: "The content of the form does not meet the conditions specified in the policy document.",
|
Description: "The content of the form does not meet the conditions specified in the policy document.",
|
||||||
HttpStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
NoSuchBucketPolicy: {
|
NoSuchBucketPolicy: {
|
||||||
Code: "NoSuchBucketPolicy",
|
Code: "NoSuchBucketPolicy",
|
||||||
Description: "The specified bucket does not have a bucket policy.",
|
Description: "The specified bucket does not have a bucket policy.",
|
||||||
HttpStatusCode: http.StatusNotFound,
|
HTTPStatusCode: http.StatusNotFound,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,8 +199,8 @@ func getErrorResponse(err Error, resource string) ErrorResponse {
|
|||||||
data.Resource = resource
|
data.Resource = resource
|
||||||
}
|
}
|
||||||
// TODO implement this in future
|
// TODO implement this in future
|
||||||
data.RequestId = "3L137"
|
data.RequestID = "3L137"
|
||||||
data.HostId = "3L137"
|
data.HostID = "3L137"
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
@ -60,18 +60,25 @@ func (h vHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err := h.conf.ReadConfig(); err != nil {
|
if err := h.conf.ReadConfig(); err != nil {
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, "")
|
errorResponse := getErrorResponse(error, "")
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
} else {
|
} else {
|
||||||
user := h.conf.GetKey(accessKey)
|
user, ok := h.conf.Users[accessKey]
|
||||||
ok, _ := signers.ValidateRequest(user, r)
|
if ok == false {
|
||||||
if ok {
|
|
||||||
h.handler.ServeHTTP(w, r)
|
|
||||||
} else {
|
|
||||||
error := errorCodeError(AccessDenied)
|
error := errorCodeError(AccessDenied)
|
||||||
errorResponse := getErrorResponse(error, "")
|
errorResponse := getErrorResponse(error, "")
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
|
} else {
|
||||||
|
ok, _ = signers.ValidateRequest(user, r)
|
||||||
|
if ok {
|
||||||
|
h.handler.ServeHTTP(w, r)
|
||||||
|
} else {
|
||||||
|
error := errorCodeError(AccessDenied)
|
||||||
|
errorResponse := getErrorResponse(error, "")
|
||||||
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -84,7 +91,7 @@ func (h vHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
// ## Uncommented below links of code after disabling anonymous requests
|
// ## Uncommented below links of code after disabling anonymous requests
|
||||||
// error := errorCodeError(AccessDenied)
|
// error := errorCodeError(AccessDenied)
|
||||||
// errorResponse := getErrorResponse(error, "")
|
// errorResponse := getErrorResponse(error, "")
|
||||||
// w.WriteHeader(error.HttpStatusCode)
|
// w.WriteHeader(error.HTTPStatusCode)
|
||||||
// w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
// w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,7 +109,7 @@ func (h rHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
if ignoreUnImplementedObjectResources(r) || ignoreUnImplementedBucketResources(r) {
|
if ignoreUnImplementedObjectResources(r) || ignoreUnImplementedBucketResources(r) {
|
||||||
error := errorCodeError(NotImplemented)
|
error := errorCodeError(NotImplemented)
|
||||||
errorResponse := getErrorResponse(error, "")
|
errorResponse := getErrorResponse(error, "")
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
} else {
|
} else {
|
||||||
h.handler.ServeHTTP(w, r)
|
h.handler.ServeHTTP(w, r)
|
||||||
|
@ -26,13 +26,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// private use
|
// private use
|
||||||
type minioApi struct {
|
type minioAPI struct {
|
||||||
domain string
|
domain string
|
||||||
storage mstorage.Storage
|
storage mstorage.Storage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path based routing
|
// Path based routing
|
||||||
func pathMux(api minioApi, mux *x.Router) *x.Router {
|
func pathMux(api minioAPI, mux *x.Router) *x.Router {
|
||||||
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
|
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
|
||||||
mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET")
|
mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET")
|
||||||
mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT")
|
mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT")
|
||||||
@ -44,7 +44,7 @@ func pathMux(api minioApi, mux *x.Router) *x.Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Domain based routing
|
// Domain based routing
|
||||||
func domainMux(api minioApi, mux *x.Router) *x.Router {
|
func domainMux(api minioAPI, mux *x.Router) *x.Router {
|
||||||
mux.HandleFunc("/",
|
mux.HandleFunc("/",
|
||||||
api.listObjectsHandler).Host("{bucket}" + "." + api.domain).Methods("GET")
|
api.listObjectsHandler).Host("{bucket}" + "." + api.domain).Methods("GET")
|
||||||
mux.HandleFunc("/{object:.*}",
|
mux.HandleFunc("/{object:.*}",
|
||||||
@ -60,7 +60,7 @@ func domainMux(api minioApi, mux *x.Router) *x.Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get proper router based on domain availability
|
// Get proper router based on domain availability
|
||||||
func getMux(api minioApi, mux *x.Router) *x.Router {
|
func getMux(api minioAPI, mux *x.Router) *x.Router {
|
||||||
switch true {
|
switch true {
|
||||||
case api.domain == "":
|
case api.domain == "":
|
||||||
return pathMux(api, mux)
|
return pathMux(api, mux)
|
||||||
@ -71,10 +71,10 @@ func getMux(api minioApi, mux *x.Router) *x.Router {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Http wrapper handler
|
// HTTPHandler - http wrapper handler
|
||||||
func HttpHandler(domain string, storage mstorage.Storage) http.Handler {
|
func HTTPHandler(domain string, storage mstorage.Storage) http.Handler {
|
||||||
var mux *x.Router
|
var mux *x.Router
|
||||||
var api = minioApi{}
|
var api = minioAPI{}
|
||||||
api.storage = storage
|
api.storage = storage
|
||||||
api.domain = domain
|
api.domain = domain
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import (
|
|||||||
// ----------
|
// ----------
|
||||||
// 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 (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Request) {
|
func (server *minioAPI) getObjectHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
var object, bucket string
|
var object, bucket string
|
||||||
|
|
||||||
acceptsContentType := getContentType(req)
|
acceptsContentType := getContentType(req)
|
||||||
@ -50,21 +50,21 @@ func (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Reques
|
|||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchKey)
|
error := errorCodeError(NoSuchKey)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.ObjectNameInvalid:
|
case mstorage.ObjectNameInvalid:
|
||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchKey)
|
error := errorCodeError(NoSuchKey)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BucketNameInvalid:
|
case mstorage.BucketNameInvalid:
|
||||||
{
|
{
|
||||||
error := errorCodeError(InvalidBucketName)
|
error := errorCodeError(InvalidBucketName)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.ImplementationError:
|
case mstorage.ImplementationError:
|
||||||
@ -73,7 +73,7 @@ func (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Reques
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ func (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Reques
|
|||||||
// HEAD Object
|
// 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 (server *minioApi) headObjectHandler(w http.ResponseWriter, req *http.Request) {
|
func (server *minioAPI) headObjectHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
var object, bucket string
|
var object, bucket string
|
||||||
acceptsContentType := getContentType(req)
|
acceptsContentType := getContentType(req)
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
@ -97,14 +97,14 @@ func (server *minioApi) headObjectHandler(w http.ResponseWriter, req *http.Reque
|
|||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchKey)
|
error := errorCodeError(NoSuchKey)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.ObjectNameInvalid:
|
case mstorage.ObjectNameInvalid:
|
||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchKey)
|
error := errorCodeError(NoSuchKey)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.ImplementationError:
|
case mstorage.ImplementationError:
|
||||||
@ -113,7 +113,7 @@ func (server *minioApi) headObjectHandler(w http.ResponseWriter, req *http.Reque
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ func (server *minioApi) headObjectHandler(w http.ResponseWriter, req *http.Reque
|
|||||||
// PUT Object
|
// 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 (server *minioApi) putObjectHandler(w http.ResponseWriter, req *http.Request) {
|
func (server *minioAPI) putObjectHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
var object, bucket string
|
var object, bucket string
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
acceptsContentType := getContentType(req)
|
acceptsContentType := getContentType(req)
|
||||||
@ -146,28 +146,28 @@ func (server *minioApi) putObjectHandler(w http.ResponseWriter, req *http.Reques
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BucketNotFound:
|
case mstorage.BucketNotFound:
|
||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchBucket)
|
error := errorCodeError(NoSuchBucket)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BucketNameInvalid:
|
case mstorage.BucketNameInvalid:
|
||||||
{
|
{
|
||||||
error := errorCodeError(InvalidBucketName)
|
error := errorCodeError(InvalidBucketName)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.ObjectExists:
|
case mstorage.ObjectExists:
|
||||||
{
|
{
|
||||||
error := errorCodeError(NotImplemented)
|
error := errorCodeError(NotImplemented)
|
||||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import (
|
|||||||
// -----------------
|
// -----------------
|
||||||
// This implementation of the PUT operation uses the policy subresource
|
// This implementation of the PUT operation uses the policy subresource
|
||||||
// to add to or replace a policy on a bucket
|
// to add to or replace a policy on a bucket
|
||||||
func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
|
func (server *minioAPI) putBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
acceptsContentType := getContentType(req)
|
acceptsContentType := getContentType(req)
|
||||||
@ -39,7 +39,7 @@ func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.
|
|||||||
if ok == false {
|
if ok == false {
|
||||||
error := errorCodeError(InvalidPolicyDocument)
|
error := errorCodeError(InvalidPolicyDocument)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -56,14 +56,14 @@ func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.
|
|||||||
{
|
{
|
||||||
error := errorCodeError(InvalidBucketName)
|
error := errorCodeError(InvalidBucketName)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BucketNotFound:
|
case mstorage.BucketNotFound:
|
||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchBucket)
|
error := errorCodeError(NoSuchBucket)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BackendCorrupted:
|
case mstorage.BackendCorrupted:
|
||||||
@ -72,7 +72,7 @@ func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.
|
|||||||
// -----------------
|
// -----------------
|
||||||
// This implementation of the GET operation uses the policy subresource
|
// This implementation of the GET operation uses the policy subresource
|
||||||
// to return the policy of a specified bucket.
|
// to return the policy of a specified bucket.
|
||||||
func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
|
func (server *minioAPI) getBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
vars := mux.Vars(req)
|
vars := mux.Vars(req)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
acceptsContentType := getContentType(req)
|
acceptsContentType := getContentType(req)
|
||||||
@ -95,7 +95,7 @@ func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.
|
|||||||
if ret != nil {
|
if ret != nil {
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
writeCommonHeaders(w, getContentString(acceptsContentType))
|
writeCommonHeaders(w, getContentString(acceptsContentType))
|
||||||
@ -106,21 +106,21 @@ func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.
|
|||||||
{
|
{
|
||||||
error := errorCodeError(InvalidBucketName)
|
error := errorCodeError(InvalidBucketName)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BucketNotFound:
|
case mstorage.BucketNotFound:
|
||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchBucket)
|
error := errorCodeError(NoSuchBucket)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BucketPolicyNotFound:
|
case mstorage.BucketPolicyNotFound:
|
||||||
{
|
{
|
||||||
error := errorCodeError(NoSuchBucketPolicy)
|
error := errorCodeError(NoSuchBucketPolicy)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
case mstorage.BackendCorrupted:
|
case mstorage.BackendCorrupted:
|
||||||
@ -129,7 +129,7 @@ func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
error := errorCodeError(InternalError)
|
error := errorCodeError(InternalError)
|
||||||
errorResponse := getErrorResponse(error, bucket)
|
errorResponse := getErrorResponse(error, bucket)
|
||||||
w.WriteHeader(error.HttpStatusCode)
|
w.WriteHeader(error.HTTPStatusCode)
|
||||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,16 +54,16 @@ func generateBucketsListResult(buckets []mstorage.BucketMetadata) BucketListResp
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
type ItemKey []*Item
|
type itemKey []*Item
|
||||||
|
|
||||||
// Len
|
// Len
|
||||||
func (b ItemKey) Len() int { return len(b) }
|
func (b itemKey) Len() int { return len(b) }
|
||||||
|
|
||||||
// Swap
|
// Swap
|
||||||
func (b ItemKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
func (b itemKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||||
|
|
||||||
// Less
|
// Less
|
||||||
func (b ItemKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
|
func (b itemKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
|
||||||
|
|
||||||
// takes a set of objects and prepares the objects for serialization
|
// takes a set of objects and prepares the objects for serialization
|
||||||
// input:
|
// input:
|
||||||
@ -95,7 +95,7 @@ func generateObjectsListResult(bucket string, objects []mstorage.ObjectMetadata,
|
|||||||
content.Owner = owner
|
content.Owner = owner
|
||||||
contents = append(contents, content)
|
contents = append(contents, content)
|
||||||
}
|
}
|
||||||
sort.Sort(ItemKey(contents))
|
sort.Sort(itemKey(contents))
|
||||||
data.Name = bucket
|
data.Name = bucket
|
||||||
data.Contents = contents
|
data.Contents = contents
|
||||||
data.MaxKeys = bucketResources.Maxkeys
|
data.MaxKeys = bucketResources.Maxkeys
|
||||||
|
@ -29,10 +29,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DEFAULT_WEB = "polygon"
|
defaultWeb = "polygon"
|
||||||
)
|
)
|
||||||
|
|
||||||
type webUiApi struct {
|
type webAPI struct {
|
||||||
conf config.Config
|
conf config.Config
|
||||||
webPath string
|
webPath string
|
||||||
}
|
}
|
||||||
@ -42,16 +42,16 @@ type encoder interface {
|
|||||||
Encode(v interface{}) error
|
Encode(v interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Http wrapper handler
|
// HTTPHandler - http wrapper handler
|
||||||
func HttpHandler() http.Handler {
|
func HTTPHandler() http.Handler {
|
||||||
mux := mux.NewRouter()
|
mux := mux.NewRouter()
|
||||||
var api = webUiApi{}
|
var api = webAPI{}
|
||||||
|
|
||||||
if err := api.conf.SetupConfig(); err != nil {
|
if err := api.conf.SetupConfig(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
api.webPath = path.Join(api.conf.GetConfigPath(), DEFAULT_WEB)
|
api.webPath = path.Join(api.conf.GetConfigPath(), defaultWeb)
|
||||||
mux.Handle("/{polygon:.*}", http.FileServer(http.Dir(api.webPath))).Methods("GET")
|
mux.Handle("/{polygon:.*}", http.FileServer(http.Dir(api.webPath))).Methods("GET")
|
||||||
mux.HandleFunc("/access", api.accessHandler).Methods("POST")
|
mux.HandleFunc("/access", api.accessHandler).Methods("POST")
|
||||||
return mux
|
return mux
|
||||||
@ -68,7 +68,7 @@ func writeResponse(w http.ResponseWriter, response interface{}) []byte {
|
|||||||
return bytesBuffer.Bytes()
|
return bytesBuffer.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (web *webUiApi) accessHandler(w http.ResponseWriter, req *http.Request) {
|
func (web *webAPI) accessHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
var err error
|
var err error
|
||||||
var accesskey, secretkey []byte
|
var accesskey, secretkey []byte
|
||||||
username := req.FormValue("username")
|
username := req.FormValue("username")
|
||||||
@ -92,7 +92,7 @@ func (web *webUiApi) accessHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
var user = config.User{}
|
var user = config.User{}
|
||||||
user.Name = username
|
user.Name = username
|
||||||
|
|
||||||
accesskey, err = keys.GetRandomAlphaNumeric(keys.MINIO_ACCESS_ID)
|
accesskey, err = keys.GenerateRandomAlphaNumeric(keys.MinioAccessID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
@ -100,7 +100,7 @@ func (web *webUiApi) accessHandler(w http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
user.AccessKey = string(accesskey)
|
user.AccessKey = string(accesskey)
|
||||||
|
|
||||||
secretkey, err = keys.GetRandomBase64(keys.MINIO_SECRET_ID)
|
secretkey, err = keys.GenerateRandomBase64(keys.MinioSecretID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
@ -19,30 +19,31 @@ package httpserver
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
// "time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type HttpServerConfig struct {
|
// Config - http server config
|
||||||
|
type Config struct {
|
||||||
Address string
|
Address string
|
||||||
TLS bool
|
TLS bool
|
||||||
CertFile string
|
CertFile string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
Websocket bool // implement it - TODO
|
Websocket bool // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
type HttpServer struct{}
|
// Server - http server related
|
||||||
|
type Server struct{}
|
||||||
|
|
||||||
// Start http server
|
// Start http server
|
||||||
func Start(handler http.Handler, config HttpServerConfig) (chan<- string, <-chan error, *HttpServer) {
|
func Start(handler http.Handler, config Config) (chan<- string, <-chan error, *Server) {
|
||||||
ctrlChannel := make(chan string)
|
ctrlChannel := make(chan string)
|
||||||
errorChannel := make(chan error)
|
errorChannel := make(chan error)
|
||||||
server := HttpServer{}
|
server := Server{}
|
||||||
go start(ctrlChannel, errorChannel, handler, config, &server)
|
go start(ctrlChannel, errorChannel, handler, config, &server)
|
||||||
return ctrlChannel, errorChannel, &server
|
return ctrlChannel, errorChannel, &server
|
||||||
}
|
}
|
||||||
|
|
||||||
func start(ctrlChannel <-chan string, errorChannel chan<- error,
|
func start(ctrlChannel <-chan string, errorChannel chan<- error,
|
||||||
router http.Handler, config HttpServerConfig, server *HttpServer) {
|
router http.Handler, config Config, server *Server) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Minio server config
|
// Minio server config
|
||||||
|
@ -30,45 +30,51 @@ import (
|
|||||||
"github.com/minio-io/minio/pkg/storage/inmemory"
|
"github.com/minio-io/minio/pkg/storage/inmemory"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerConfig struct {
|
// Config - http server parameters
|
||||||
|
type Config struct {
|
||||||
Domain string
|
Domain string
|
||||||
Address string
|
Address string
|
||||||
Tls bool
|
TLS bool
|
||||||
CertFile string
|
CertFile string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
ApiType interface{}
|
APIType interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type MinioApi struct {
|
// MinioAPI - storage type donut, fs, inmemory
|
||||||
|
type MinioAPI struct {
|
||||||
StorageType StorageType
|
StorageType StorageType
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebUIApi struct {
|
// WebAPI - webui related
|
||||||
Websocket bool
|
type WebAPI struct {
|
||||||
|
Websocket bool // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StorageType - different storage types supported by minio
|
||||||
type StorageType int
|
type StorageType int
|
||||||
|
|
||||||
|
// Storage types
|
||||||
const (
|
const (
|
||||||
InMemoryStorage = iota
|
InMemory = iota
|
||||||
FileStorage
|
File
|
||||||
|
Donut
|
||||||
)
|
)
|
||||||
|
|
||||||
func getHttpChannels(configs []ServerConfig) (ctrlChans []chan<- string, statusChans []<-chan error) {
|
func getHTTPChannels(configs []Config) (ctrlChans []chan<- string, statusChans []<-chan error) {
|
||||||
// a pair of control channels, we use these primarily to add to the lists above
|
// a pair of control channels, we use these primarily to add to the lists above
|
||||||
var ctrlChan chan<- string
|
var ctrlChan chan<- string
|
||||||
var statusChan <-chan error
|
var statusChan <-chan error
|
||||||
|
|
||||||
for _, config := range configs {
|
for _, config := range configs {
|
||||||
switch k := config.ApiType.(type) {
|
switch k := config.APIType.(type) {
|
||||||
case MinioApi:
|
case MinioAPI:
|
||||||
{
|
{
|
||||||
// configure web server
|
// configure web server
|
||||||
var storage mstorage.Storage
|
var storage mstorage.Storage
|
||||||
var httpConfig = httpserver.HttpServerConfig{}
|
var httpConfig = httpserver.Config{}
|
||||||
httpConfig.Address = config.Address
|
httpConfig.Address = config.Address
|
||||||
httpConfig.Websocket = false
|
httpConfig.Websocket = false
|
||||||
httpConfig.TLS = config.Tls
|
httpConfig.TLS = config.TLS
|
||||||
|
|
||||||
if config.CertFile != "" {
|
if config.CertFile != "" {
|
||||||
httpConfig.CertFile = config.CertFile
|
httpConfig.CertFile = config.CertFile
|
||||||
@ -79,22 +85,22 @@ func getHttpChannels(configs []ServerConfig) (ctrlChans []chan<- string, statusC
|
|||||||
|
|
||||||
ctrlChans, statusChans, storage = getStorageChannels(k.StorageType)
|
ctrlChans, statusChans, storage = getStorageChannels(k.StorageType)
|
||||||
// start minio api in a web server, pass storage driver into it
|
// start minio api in a web server, pass storage driver into it
|
||||||
ctrlChan, statusChan, _ = httpserver.Start(minioapi.HttpHandler(config.Domain, storage), httpConfig)
|
ctrlChan, statusChan, _ = httpserver.Start(minioapi.HTTPHandler(config.Domain, storage), httpConfig)
|
||||||
|
|
||||||
ctrlChans = append(ctrlChans, ctrlChan)
|
ctrlChans = append(ctrlChans, ctrlChan)
|
||||||
statusChans = append(statusChans, statusChan)
|
statusChans = append(statusChans, statusChan)
|
||||||
|
|
||||||
}
|
}
|
||||||
case WebUIApi:
|
case WebAPI:
|
||||||
{
|
{
|
||||||
var httpConfig = httpserver.HttpServerConfig{}
|
var httpConfig = httpserver.Config{}
|
||||||
httpConfig.Address = config.Address
|
httpConfig.Address = config.Address
|
||||||
httpConfig.TLS = config.Tls
|
httpConfig.TLS = config.TLS
|
||||||
httpConfig.CertFile = config.CertFile
|
httpConfig.CertFile = config.CertFile
|
||||||
httpConfig.KeyFile = config.KeyFile
|
httpConfig.KeyFile = config.KeyFile
|
||||||
|
|
||||||
httpConfig.Websocket = k.Websocket
|
httpConfig.Websocket = k.Websocket
|
||||||
ctrlChan, statusChan, _ = httpserver.Start(webuiapi.HttpHandler(), httpConfig)
|
ctrlChan, statusChan, _ = httpserver.Start(webuiapi.HTTPHandler(), httpConfig)
|
||||||
|
|
||||||
ctrlChans = append(ctrlChans, ctrlChan)
|
ctrlChans = append(ctrlChans, ctrlChan)
|
||||||
statusChans = append(statusChans, statusChan)
|
statusChans = append(statusChans, statusChan)
|
||||||
@ -120,13 +126,13 @@ func getStorageChannels(storageType StorageType) (ctrlChans []chan<- string, sta
|
|||||||
// - ctrlChans has channel to communicate to storage
|
// - ctrlChans has channel to communicate to storage
|
||||||
// - statusChans has channel for messages coming from storage
|
// - statusChans has channel for messages coming from storage
|
||||||
switch {
|
switch {
|
||||||
case storageType == InMemoryStorage:
|
case storageType == InMemory:
|
||||||
{
|
{
|
||||||
ctrlChan, statusChan, storage = inmemory.Start()
|
ctrlChan, statusChan, storage = inmemory.Start()
|
||||||
ctrlChans = append(ctrlChans, ctrlChan)
|
ctrlChans = append(ctrlChans, ctrlChan)
|
||||||
statusChans = append(statusChans, statusChan)
|
statusChans = append(statusChans, statusChan)
|
||||||
}
|
}
|
||||||
case storageType == FileStorage:
|
case storageType == File:
|
||||||
{
|
{
|
||||||
u, err := user.Current()
|
u, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -143,10 +149,10 @@ func getStorageChannels(storageType StorageType) (ctrlChans []chan<- string, sta
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create channels
|
// Start - create channels
|
||||||
func Start(configs []ServerConfig) {
|
func Start(configs []Config) {
|
||||||
// reflected looping is necessary to remove dead channels from loop and not flood switch
|
// reflected looping is necessary to remove dead channels from loop and not flood switch
|
||||||
ctrlChans, statusChans := getHttpChannels(configs)
|
ctrlChans, statusChans := getHTTPChannels(configs)
|
||||||
cases := createSelectCases(statusChans)
|
cases := createSelectCases(statusChans)
|
||||||
for len(cases) > 0 {
|
for len(cases) > 0 {
|
||||||
chosen, value, recvOk := reflect.Select(cases)
|
chosen, value, recvOk := reflect.Select(cases)
|
||||||
|
@ -24,8 +24,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Metadata map
|
||||||
type Metadata map[string]string
|
type Metadata map[string]string
|
||||||
|
|
||||||
|
// DataHeader struct
|
||||||
type DataHeader struct {
|
type DataHeader struct {
|
||||||
Key string
|
Key string
|
||||||
Part uint8
|
Part uint8
|
||||||
@ -33,13 +35,16 @@ type DataHeader struct {
|
|||||||
EncoderParams EncoderParams
|
EncoderParams EncoderParams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncoderTechnique type
|
||||||
type EncoderTechnique int
|
type EncoderTechnique int
|
||||||
|
|
||||||
|
// EncoderTechniques
|
||||||
const (
|
const (
|
||||||
Vandermonde EncoderTechnique = iota
|
Vandermonde EncoderTechnique = iota
|
||||||
Cauchy
|
Cauchy
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// EncoderParams struct
|
||||||
type EncoderParams struct {
|
type EncoderParams struct {
|
||||||
Length uint32
|
Length uint32
|
||||||
K uint8
|
K uint8
|
||||||
@ -47,7 +52,7 @@ type EncoderParams struct {
|
|||||||
Technique EncoderTechnique
|
Technique EncoderTechnique
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate new header
|
// NewHeader populate new header
|
||||||
func NewHeader(key string, part uint8, metadata Metadata, encoderParams EncoderParams) DataHeader {
|
func NewHeader(key string, part uint8, metadata Metadata, encoderParams EncoderParams) DataHeader {
|
||||||
header := DataHeader{}
|
header := DataHeader{}
|
||||||
header.Key = key
|
header.Key = key
|
||||||
@ -62,7 +67,7 @@ func NewHeader(key string, part uint8, metadata Metadata, encoderParams EncoderP
|
|||||||
return header
|
return header
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate populated header
|
// ValidateHeader validate populated header
|
||||||
func ValidateHeader(header DataHeader) bool {
|
func ValidateHeader(header DataHeader) bool {
|
||||||
if header.Key == "" || header.Part < 0 || len(header.Metadata) < 2 {
|
if header.Key == "" || header.Part < 0 || len(header.Metadata) < 2 {
|
||||||
return false
|
return false
|
||||||
@ -75,7 +80,7 @@ func ValidateHeader(header DataHeader) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write data, returns error upon any failure
|
// WriteData write data, returns error upon any failure
|
||||||
func WriteData(target io.Writer, header DataHeader, data io.Reader) error {
|
func WriteData(target io.Writer, header DataHeader, data io.Reader) error {
|
||||||
if !ValidateHeader(header) {
|
if !ValidateHeader(header) {
|
||||||
return fmt.Errorf("Invalid header")
|
return fmt.Errorf("Invalid header")
|
||||||
|
@ -44,30 +44,49 @@ import (
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Magic list
|
||||||
var (
|
var (
|
||||||
MagicMINI = binary.LittleEndian.Uint32([]byte{'M', 'I', 'N', 'I'})
|
MagicMINI = binary.LittleEndian.Uint32([]byte{'M', 'I', 'N', 'I'})
|
||||||
MagicDATA = binary.LittleEndian.Uint32([]byte{'D', 'A', 'T', 'A'})
|
MagicDATA = binary.LittleEndian.Uint32([]byte{'D', 'A', 'T', 'A'})
|
||||||
MagicINIM = binary.LittleEndian.Uint32([]byte{'I', 'N', 'I', 'M'})
|
MagicINIM = binary.LittleEndian.Uint32([]byte{'I', 'N', 'I', 'M'})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DonutFrameHeader -
|
||||||
|
// --------------
|
||||||
|
// BlockStart uint32
|
||||||
|
// VersionMajor uint32
|
||||||
|
// Reserved uint64
|
||||||
|
// DataLen uint64
|
||||||
|
// --------------
|
||||||
type DonutFrameHeader struct {
|
type DonutFrameHeader struct {
|
||||||
MagicMINI uint32
|
MagicMINI uint32
|
||||||
Version uint32
|
Version uint32
|
||||||
Reserved uint64
|
Reserved uint64
|
||||||
DataLength uint64
|
DataLength uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Crc32c checksum
|
||||||
type Crc32c uint32
|
type Crc32c uint32
|
||||||
|
|
||||||
|
// Sha512 checksum
|
||||||
type Sha512 [sha512.Size]byte
|
type Sha512 [sha512.Size]byte
|
||||||
|
|
||||||
|
// DonutFrameFooter -
|
||||||
|
// --------------
|
||||||
|
// DataSha512 [64]byte
|
||||||
|
// BlockLen uint64
|
||||||
|
// BlockEnd uint32
|
||||||
|
// --------------
|
||||||
type DonutFrameFooter struct {
|
type DonutFrameFooter struct {
|
||||||
DataSha512 Sha512
|
DataSha512 Sha512
|
||||||
OffsetToMINI uint64
|
OffsetToMINI uint64
|
||||||
MagicINIM uint32
|
MagicINIM uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data buffer
|
||||||
type Data bytes.Buffer
|
type Data bytes.Buffer
|
||||||
|
|
||||||
// Write Donut format to input io.Writer, returns error upon any failure
|
// WriteFrame - write donut format to input io.Writer, returns error upon any failure
|
||||||
func WriteFrame(target io.Writer, reader io.Reader, length uint64) error {
|
func WriteFrame(target io.Writer, reader io.Reader, length uint64) error {
|
||||||
// write header
|
// write header
|
||||||
header := DonutFrameHeader{
|
header := DonutFrameHeader{
|
||||||
|
@ -43,10 +43,10 @@ func (s *MySuite) TestCauchyDecode(c *C) {
|
|||||||
chunks[9] = nil
|
chunks[9] = nil
|
||||||
chunks[13] = nil
|
chunks[13] = nil
|
||||||
|
|
||||||
recovered_data, err := e.Decode(chunks, length)
|
recoveredData, err := e.Decode(chunks, length)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
if !bytes.Equal(data, recovered_data) {
|
if !bytes.Equal(data, recoveredData) {
|
||||||
c.Fatalf("Recovered data mismatches with original data")
|
c.Fatalf("Recovered data mismatches with original data")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,10 @@ func (s *MySuite) TestVanderMondeDecode(c *C) {
|
|||||||
chunks[9] = nil
|
chunks[9] = nil
|
||||||
chunks[13] = nil
|
chunks[13] = nil
|
||||||
|
|
||||||
recovered_data, err := e.Decode(chunks, length)
|
recoveredData, err := e.Decode(chunks, length)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
if !bytes.Equal(recovered_data, data) {
|
if !bytes.Equal(recoveredData, data) {
|
||||||
c.Fatalf("Recovered data mismatches with original data")
|
c.Fatalf("Recovered data mismatches with original data")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ type storage struct {
|
|||||||
lock *sync.Mutex
|
lock *sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SerializedMetadata - carries content type
|
||||||
type SerializedMetadata struct {
|
type SerializedMetadata struct {
|
||||||
ContentType string
|
ContentType string
|
||||||
}
|
}
|
||||||
@ -237,9 +238,8 @@ func (storage *storage) CopyObjectToWriter(w io.Writer, bucket string, object st
|
|||||||
{
|
{
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return 0, mstorage.ObjectNotFound{Bucket: bucket, Object: object}
|
return 0, mstorage.ObjectNotFound{Bucket: bucket, Object: object}
|
||||||
} else {
|
|
||||||
return 0, mstorage.EmbedError(bucket, object, err)
|
|
||||||
}
|
}
|
||||||
|
return 0, mstorage.EmbedError(bucket, object, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file, err := os.Open(objectPath)
|
file, err := os.Open(objectPath)
|
||||||
@ -311,20 +311,20 @@ func (storage *storage) GetObjectMetadata(bucket string, object string) (mstorag
|
|||||||
return metadata, nil
|
return metadata, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Path struct {
|
type bucketDir struct {
|
||||||
files map[string]os.FileInfo
|
files map[string]os.FileInfo
|
||||||
root string
|
root string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Path) getAllFiles(path string, fl os.FileInfo, err error) error {
|
func (p *bucketDir) getAllFiles(object string, fl os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if fl.Mode().IsRegular() {
|
if fl.Mode().IsRegular() {
|
||||||
if strings.HasSuffix(path, "$metadata") {
|
if strings.HasSuffix(object, "$metadata") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
_p := strings.Split(path, p.root+"/")
|
_p := strings.Split(object, p.root+"/")
|
||||||
if len(_p) > 1 {
|
if len(_p) > 1 {
|
||||||
p.files[_p[1]] = fl
|
p.files[_p[1]] = fl
|
||||||
}
|
}
|
||||||
@ -332,8 +332,8 @@ func (p *Path) getAllFiles(path string, fl os.FileInfo, err error) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func delimiter(path, delimiter string) string {
|
func delimiter(object, delimiter string) string {
|
||||||
readBuffer := bytes.NewBufferString(path)
|
readBuffer := bytes.NewBufferString(object)
|
||||||
reader := bufio.NewReader(readBuffer)
|
reader := bufio.NewReader(readBuffer)
|
||||||
stringReader := strings.NewReader(delimiter)
|
stringReader := strings.NewReader(delimiter)
|
||||||
delimited, _ := stringReader.ReadByte()
|
delimited, _ := stringReader.ReadByte()
|
||||||
@ -341,20 +341,20 @@ func delimiter(path, delimiter string) string {
|
|||||||
return delimitedStr
|
return delimitedStr
|
||||||
}
|
}
|
||||||
|
|
||||||
type ByObjectKey []mstorage.ObjectMetadata
|
type byObjectKey []mstorage.ObjectMetadata
|
||||||
|
|
||||||
// Len
|
// Len
|
||||||
func (b ByObjectKey) Len() int { return len(b) }
|
func (b byObjectKey) Len() int { return len(b) }
|
||||||
|
|
||||||
// Swap
|
// Swap
|
||||||
func (b ByObjectKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
func (b byObjectKey) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||||
|
|
||||||
// Less
|
// Less
|
||||||
func (b ByObjectKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
|
func (b byObjectKey) Less(i, j int) bool { return b[i].Key < b[j].Key }
|
||||||
|
|
||||||
// GET bucket (list objects)
|
// GET bucket (list objects)
|
||||||
func (storage *storage) ListObjects(bucket string, resources mstorage.BucketResourcesMetadata) ([]mstorage.ObjectMetadata, mstorage.BucketResourcesMetadata, error) {
|
func (storage *storage) ListObjects(bucket string, resources mstorage.BucketResourcesMetadata) ([]mstorage.ObjectMetadata, mstorage.BucketResourcesMetadata, error) {
|
||||||
p := Path{}
|
p := bucketDir{}
|
||||||
p.files = make(map[string]os.FileInfo)
|
p.files = make(map[string]os.FileInfo)
|
||||||
|
|
||||||
if mstorage.IsValidBucket(bucket) == false {
|
if mstorage.IsValidBucket(bucket) == false {
|
||||||
@ -450,7 +450,7 @@ func (storage *storage) ListObjects(bucket string, resources mstorage.BucketReso
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret:
|
ret:
|
||||||
sort.Sort(ByObjectKey(metadataList))
|
sort.Sort(byObjectKey(metadataList))
|
||||||
return metadataList, resources, nil
|
return metadataList, resources, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,7 +495,7 @@ func (storage *storage) StoreObject(bucket, key, contentType string, data io.Rea
|
|||||||
if _, err := os.Stat(objectPath); !os.IsNotExist(err) {
|
if _, err := os.Stat(objectPath); !os.IsNotExist(err) {
|
||||||
return mstorage.ObjectExists{
|
return mstorage.ObjectExists{
|
||||||
Bucket: bucket,
|
Bucket: bucket,
|
||||||
Key: key,
|
Object: key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Mini Object Storage, (C) 2015 Minio, Inc.
|
* Minio Object Storage, (C) 2015 Minio, Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -72,19 +72,18 @@ func (storage *storage) CopyObjectToWriter(w io.Writer, bucket string, object st
|
|||||||
objectBuffer := bytes.NewBuffer(val.data)
|
objectBuffer := bytes.NewBuffer(val.data)
|
||||||
written, err := io.Copy(w, objectBuffer)
|
written, err := io.Copy(w, objectBuffer)
|
||||||
return written, err
|
return written, err
|
||||||
} else {
|
|
||||||
return 0, mstorage.ObjectNotFound{Bucket: bucket, Object: object}
|
|
||||||
}
|
}
|
||||||
|
return 0, mstorage.ObjectNotFound{Bucket: bucket, Object: object}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not implemented
|
// Not implemented
|
||||||
func (storage *storage) StoreBucketPolicy(bucket string, policy interface{}) error {
|
func (storage *storage) StoreBucketPolicy(bucket string, policy interface{}) error {
|
||||||
return mstorage.ApiNotImplemented{Api: "PutBucketPolicy"}
|
return mstorage.APINotImplemented{API: "PutBucketPolicy"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not implemented
|
// Not implemented
|
||||||
func (storage *storage) GetBucketPolicy(bucket string) (interface{}, error) {
|
func (storage *storage) GetBucketPolicy(bucket string) (interface{}, error) {
|
||||||
return policy.BucketPolicy{}, mstorage.ApiNotImplemented{Api: "GetBucketPolicy"}
|
return policy.BucketPolicy{}, mstorage.APINotImplemented{API: "GetBucketPolicy"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUT object to memory buffer
|
// PUT object to memory buffer
|
||||||
@ -99,7 +98,7 @@ func (storage *storage) StoreObject(bucket, key, contentType string, data io.Rea
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := storage.objectdata[objectKey]; ok == true {
|
if _, ok := storage.objectdata[objectKey]; ok == true {
|
||||||
return mstorage.ObjectExists{Bucket: bucket, Key: key}
|
return mstorage.ObjectExists{Bucket: bucket, Object: key}
|
||||||
}
|
}
|
||||||
|
|
||||||
if contentType == "" {
|
if contentType == "" {
|
||||||
@ -176,16 +175,16 @@ func (storage *storage) ListObjects(bucket string, resources mstorage.BucketReso
|
|||||||
return results, resources, nil
|
return results, resources, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ByBucketName []mstorage.BucketMetadata
|
type byBucketName []mstorage.BucketMetadata
|
||||||
|
|
||||||
// Len of bucket name
|
// Len of bucket name
|
||||||
func (b ByBucketName) Len() int { return len(b) }
|
func (b byBucketName) Len() int { return len(b) }
|
||||||
|
|
||||||
// Swap bucket i, j
|
// Swap bucket i, j
|
||||||
func (b ByBucketName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
func (b byBucketName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||||
|
|
||||||
// Less
|
// Less
|
||||||
func (b ByBucketName) Less(i, j int) bool { return b[i].Name < b[j].Name }
|
func (b byBucketName) Less(i, j int) bool { return b[i].Name < b[j].Name }
|
||||||
|
|
||||||
// List buckets
|
// List buckets
|
||||||
func (storage *storage) ListBuckets() ([]mstorage.BucketMetadata, error) {
|
func (storage *storage) ListBuckets() ([]mstorage.BucketMetadata, error) {
|
||||||
@ -193,7 +192,7 @@ func (storage *storage) ListBuckets() ([]mstorage.BucketMetadata, error) {
|
|||||||
for _, bucket := range storage.bucketdata {
|
for _, bucket := range storage.bucketdata {
|
||||||
results = append(results, bucket.metadata)
|
results = append(results, bucket.metadata)
|
||||||
}
|
}
|
||||||
sort.Sort(ByBucketName(results))
|
sort.Sort(byBucketName(results))
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +202,6 @@ func (storage *storage) GetObjectMetadata(bucket, key string) (mstorage.ObjectMe
|
|||||||
|
|
||||||
if object, ok := storage.objectdata[objectKey]; ok == true {
|
if object, ok := storage.objectdata[objectKey]; ok == true {
|
||||||
return object.metadata, nil
|
return object.metadata, nil
|
||||||
} else {
|
|
||||||
return mstorage.ObjectMetadata{}, mstorage.ObjectNotFound{Bucket: bucket, Object: key}
|
|
||||||
}
|
}
|
||||||
|
return mstorage.ObjectMetadata{}, mstorage.ObjectNotFound{Bucket: bucket, Object: key}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Storage - generic API interface
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
// Bucket Operations
|
// Bucket Operations
|
||||||
ListBuckets() ([]BucketMetadata, error)
|
ListBuckets() ([]BucketMetadata, error)
|
||||||
@ -37,11 +38,13 @@ type Storage interface {
|
|||||||
StoreObject(bucket string, key string, contentType string, data io.Reader) error
|
StoreObject(bucket string, key string, contentType string, data io.Reader) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BucketMetadata - name and create date
|
||||||
type BucketMetadata struct {
|
type BucketMetadata struct {
|
||||||
Name string
|
Name string
|
||||||
Created time.Time
|
Created time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ObjectMetadata - object key and its relevant metadata
|
||||||
type ObjectMetadata struct {
|
type ObjectMetadata struct {
|
||||||
Bucket string
|
Bucket string
|
||||||
Key string
|
Key string
|
||||||
@ -52,7 +55,7 @@ type ObjectMetadata struct {
|
|||||||
Size int64
|
Size int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Various types of bucket resources
|
// BucketResourcesMetadata - various types of bucket resources
|
||||||
type BucketResourcesMetadata struct {
|
type BucketResourcesMetadata struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
Marker string
|
Marker string
|
||||||
@ -67,7 +70,8 @@ type BucketResourcesMetadata struct {
|
|||||||
Notification string
|
Notification string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify Bucket name in accordance with http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html
|
// IsValidBucket - verify bucket name in accordance with
|
||||||
|
// - http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html
|
||||||
func IsValidBucket(bucket string) bool {
|
func IsValidBucket(bucket string) bool {
|
||||||
if len(bucket) < 3 || len(bucket) > 63 {
|
if len(bucket) < 3 || len(bucket) > 63 {
|
||||||
return false
|
return false
|
||||||
@ -83,7 +87,8 @@ func IsValidBucket(bucket string) bool {
|
|||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify Object name in accordance with http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html
|
// IsValidObject - verify object name in accordance with
|
||||||
|
// - http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html
|
||||||
func IsValidObject(object string) bool {
|
func IsValidObject(object string) bool {
|
||||||
if len(object) > 1024 || len(object) == 0 {
|
if len(object) > 1024 || len(object) == 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -24,7 +24,7 @@ import (
|
|||||||
. "gopkg.in/check.v1"
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// API test suite
|
// APITestSuite - collection of API tests
|
||||||
func APITestSuite(c *C, create func() Storage) {
|
func APITestSuite(c *C, create func() Storage) {
|
||||||
testCreateBucket(c, create)
|
testCreateBucket(c, create)
|
||||||
testMultipleObjectCreation(c, create)
|
testMultipleObjectCreation(c, create)
|
||||||
|
@ -16,62 +16,76 @@
|
|||||||
|
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
|
// BackendError - generic disk backend error
|
||||||
type BackendError struct {
|
type BackendError struct {
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GenericError struct {
|
// BackendCorrupted - path has corrupted data
|
||||||
Bucket string
|
type BackendCorrupted BackendError
|
||||||
Path string
|
|
||||||
|
// APINotImplemented - generic API not implemented error
|
||||||
|
type APINotImplemented struct {
|
||||||
|
API string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ObjectExists struct {
|
// GenericBucketError - generic bucket error
|
||||||
Bucket string
|
|
||||||
Key string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApiNotImplemented struct {
|
|
||||||
Api string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ObjectNotFound GenericObjectError
|
|
||||||
|
|
||||||
type GenericBucketError struct {
|
type GenericBucketError struct {
|
||||||
Bucket string
|
Bucket string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenericObjectError - generic object error
|
||||||
type GenericObjectError struct {
|
type GenericObjectError struct {
|
||||||
Bucket string
|
Bucket string
|
||||||
Object string
|
Object string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImplementationError - generic implementation error
|
||||||
type ImplementationError struct {
|
type ImplementationError struct {
|
||||||
Bucket string
|
Bucket string
|
||||||
Object string
|
Object string
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackendCorrupted BackendError
|
/// Bucket related errors
|
||||||
|
|
||||||
|
// BucketPolicyNotFound - missing bucket policy
|
||||||
type BucketPolicyNotFound GenericBucketError
|
type BucketPolicyNotFound GenericBucketError
|
||||||
|
|
||||||
|
// BucketNameInvalid - bucketname provided is invalid
|
||||||
type BucketNameInvalid GenericBucketError
|
type BucketNameInvalid GenericBucketError
|
||||||
|
|
||||||
|
// BucketExists - bucket already exists
|
||||||
type BucketExists GenericBucketError
|
type BucketExists GenericBucketError
|
||||||
|
|
||||||
|
// BucketNotFound - requested bucket not found
|
||||||
type BucketNotFound GenericBucketError
|
type BucketNotFound GenericBucketError
|
||||||
|
|
||||||
|
/// Object related errors
|
||||||
|
|
||||||
|
// ObjectNotFound - requested object not found
|
||||||
|
type ObjectNotFound GenericObjectError
|
||||||
|
|
||||||
|
// ObjectExists - object already exists
|
||||||
|
type ObjectExists GenericObjectError
|
||||||
|
|
||||||
|
// ObjectNameInvalid - object name provided is invalid
|
||||||
type ObjectNameInvalid GenericObjectError
|
type ObjectNameInvalid GenericObjectError
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self ImplementationError) Error() string {
|
func (e ImplementationError) Error() string {
|
||||||
error := ""
|
error := ""
|
||||||
if self.Bucket != "" {
|
if e.Bucket != "" {
|
||||||
error = error + "Bucket: " + self.Bucket + " "
|
error = error + "Bucket: " + e.Bucket + " "
|
||||||
}
|
}
|
||||||
if self.Object != "" {
|
if e.Object != "" {
|
||||||
error = error + "Object: " + self.Object + " "
|
error = error + "Object: " + e.Object + " "
|
||||||
}
|
}
|
||||||
error = error + "Error: " + self.Err.Error()
|
error = error + "Error: " + e.Err.Error()
|
||||||
return error
|
return error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper function for error object
|
// EmbedError - wrapper function for error object
|
||||||
func EmbedError(bucket, object string, err error) ImplementationError {
|
func EmbedError(bucket, object string, err error) ImplementationError {
|
||||||
return ImplementationError{
|
return ImplementationError{
|
||||||
Bucket: bucket,
|
Bucket: bucket,
|
||||||
@ -81,46 +95,46 @@ func EmbedError(bucket, object string, err error) ImplementationError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self BucketPolicyNotFound) Error() string {
|
func (e BucketPolicyNotFound) Error() string {
|
||||||
return "Bucket policy not found for: " + self.Bucket
|
return "Bucket policy not found for: " + e.Bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self ObjectNotFound) Error() string {
|
func (e ObjectNotFound) Error() string {
|
||||||
return "Object not Found: " + self.Bucket + "#" + self.Object
|
return "Object not Found: " + e.Bucket + "#" + e.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self ApiNotImplemented) Error() string {
|
func (e APINotImplemented) Error() string {
|
||||||
return "Api not implemented: " + self.Api
|
return "Api not implemented: " + e.API
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self ObjectExists) Error() string {
|
func (e ObjectExists) Error() string {
|
||||||
return "Object exists: " + self.Bucket + "#" + self.Key
|
return "Object exists: " + e.Bucket + "#" + e.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self BucketNameInvalid) Error() string {
|
func (e BucketNameInvalid) Error() string {
|
||||||
return "Bucket name invalid: " + self.Bucket
|
return "Bucket name invalid: " + e.Bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self BucketExists) Error() string {
|
func (e BucketExists) Error() string {
|
||||||
return "Bucket exists: " + self.Bucket
|
return "Bucket exists: " + e.Bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self BucketNotFound) Error() string {
|
func (e BucketNotFound) Error() string {
|
||||||
return "Bucket not Found: " + self.Bucket
|
return "Bucket not Found: " + e.Bucket
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self ObjectNameInvalid) Error() string {
|
func (e ObjectNameInvalid) Error() string {
|
||||||
return "Object name invalid: " + self.Bucket + "#" + self.Object
|
return "Object name invalid: " + e.Bucket + "#" + e.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return string an error formatted as the given text
|
// Return string an error formatted as the given text
|
||||||
func (self BackendCorrupted) Error() string {
|
func (e BackendCorrupted) Error() string {
|
||||||
return "Backend corrupted: " + self.Path
|
return "Backend corrupted: " + e.Path
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
// Package crc32 implements the 32-bit cyclic redundancy check, or CRC-32,
|
// Package crc32 implements the 32-bit cyclic redundancy check, or CRC-32,
|
||||||
// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check for
|
// checksum. See http://en.wikipedia.org/wiki/Cyclic_redundancy_check for
|
||||||
// information.
|
// information.
|
||||||
|
|
||||||
package crc32c
|
package crc32c
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -38,7 +39,7 @@ func (d *digest) Sum(in []byte) []byte {
|
|||||||
return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
|
return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return current crc in digest object
|
// Sum32 - return current crc in digest object
|
||||||
func (d *digest) Sum32() uint32 { return d.crc }
|
func (d *digest) Sum32() uint32 { return d.crc }
|
||||||
|
|
||||||
// Reset default crc
|
// Reset default crc
|
||||||
@ -57,7 +58,7 @@ func (d *digest) Write(p []byte) (n int, err error) {
|
|||||||
|
|
||||||
/// Convenience functions
|
/// Convenience functions
|
||||||
|
|
||||||
// Single caller crc helper
|
// Sum32 - single caller crc helper
|
||||||
func Sum32(data []byte) uint32 {
|
func Sum32(data []byte) uint32 {
|
||||||
crc32 := New()
|
crc32 := New()
|
||||||
crc32.Reset()
|
crc32.Reset()
|
||||||
@ -65,7 +66,7 @@ func Sum32(data []byte) uint32 {
|
|||||||
return crc32.Sum32()
|
return crc32.Sum32()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Low memory footprint io.Reader based crc helper
|
// Sum - low memory footprint io.Reader based crc helper
|
||||||
func Sum(reader io.Reader) (uint32, error) {
|
func Sum(reader io.Reader) (uint32, error) {
|
||||||
h := New()
|
h := New()
|
||||||
var err error
|
var err error
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Config context
|
||||||
type Config struct {
|
type Config struct {
|
||||||
configPath string
|
configPath string
|
||||||
configFile string
|
configFile string
|
||||||
@ -32,13 +33,14 @@ type Config struct {
|
|||||||
Users map[string]User
|
Users map[string]User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User context
|
||||||
type User struct {
|
type User struct {
|
||||||
Name string
|
Name string
|
||||||
AccessKey string
|
AccessKey string
|
||||||
SecretKey string
|
SecretKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize config directory and template config
|
// SetupConfig initialize config directory and template config
|
||||||
func (c *Config) SetupConfig() error {
|
func (c *Config) SetupConfig() error {
|
||||||
u, err := user.Current()
|
u, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -63,12 +65,12 @@ func (c *Config) SetupConfig() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get config file location
|
// GetConfigPath config file location
|
||||||
func (c *Config) GetConfigPath() string {
|
func (c *Config) GetConfigPath() string {
|
||||||
return c.configPath
|
return c.configPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if user exists
|
// IsUserExists verify if user exists
|
||||||
func (c *Config) IsUserExists(username string) bool {
|
func (c *Config) IsUserExists(username string) bool {
|
||||||
for _, user := range c.Users {
|
for _, user := range c.Users {
|
||||||
if user.Name == username {
|
if user.Name == username {
|
||||||
@ -78,16 +80,7 @@ func (c *Config) IsUserExists(username string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user based on accesskey
|
// GetUser - get user from username
|
||||||
func (c *Config) GetKey(accessKey string) User {
|
|
||||||
value, ok := c.Users[accessKey]
|
|
||||||
if !ok {
|
|
||||||
return User{}
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user based on username
|
|
||||||
func (c *Config) GetUser(username string) User {
|
func (c *Config) GetUser(username string) User {
|
||||||
for _, user := range c.Users {
|
for _, user := range c.Users {
|
||||||
if user.Name == username {
|
if user.Name == username {
|
||||||
@ -97,7 +90,7 @@ func (c *Config) GetUser(username string) User {
|
|||||||
return User{}
|
return User{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new user into existing User list
|
// AddUser - add a user into existing User list
|
||||||
func (c *Config) AddUser(user User) {
|
func (c *Config) AddUser(user User) {
|
||||||
var currentUsers map[string]User
|
var currentUsers map[string]User
|
||||||
if len(c.Users) == 0 {
|
if len(c.Users) == 0 {
|
||||||
@ -109,7 +102,7 @@ func (c *Config) AddUser(user User) {
|
|||||||
c.Users = currentUsers
|
c.Users = currentUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write encoded json in config file
|
// WriteConfig - write encoded json in config file
|
||||||
func (c *Config) WriteConfig() error {
|
func (c *Config) WriteConfig() error {
|
||||||
c.configLock.Lock()
|
c.configLock.Lock()
|
||||||
defer c.configLock.Unlock()
|
defer c.configLock.Unlock()
|
||||||
@ -128,7 +121,7 @@ func (c *Config) WriteConfig() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read json config file and decode
|
// ReadConfig - read json config file and decode
|
||||||
func (c *Config) ReadConfig() error {
|
func (c *Config) ReadConfig() error {
|
||||||
c.configLock.RLock()
|
c.configLock.RLock()
|
||||||
defer c.configLock.RUnlock()
|
defer c.configLock.RUnlock()
|
||||||
@ -155,29 +148,3 @@ func (c *Config) ReadConfig() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// helpers
|
|
||||||
|
|
||||||
// Load all users into memory
|
|
||||||
func Loadusers() map[string]User {
|
|
||||||
c := Config{}
|
|
||||||
c.SetupConfig()
|
|
||||||
c.ReadConfig()
|
|
||||||
return c.Users
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load a given user based on accessKey
|
|
||||||
func Loadkey(accessKeyId string) User {
|
|
||||||
c := Config{}
|
|
||||||
c.SetupConfig()
|
|
||||||
c.ReadConfig()
|
|
||||||
return c.GetKey(accessKeyId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load a given user based on username
|
|
||||||
func Loaduser(username string) User {
|
|
||||||
c := Config{}
|
|
||||||
c.SetupConfig()
|
|
||||||
c.ReadConfig()
|
|
||||||
return c.GetUser(username)
|
|
||||||
}
|
|
||||||
|
@ -46,8 +46,8 @@ func (s *MySuite) TestConfig(c *C) {
|
|||||||
}
|
}
|
||||||
conf.configLock = new(sync.RWMutex)
|
conf.configLock = new(sync.RWMutex)
|
||||||
|
|
||||||
accesskey, _ := keys.GetRandomAlphaNumeric(keys.MINIO_ACCESS_ID)
|
accesskey, _ := keys.GenerateRandomAlphaNumeric(keys.MinioAccessID)
|
||||||
secretkey, _ := keys.GetRandomBase64(keys.MINIO_SECRET_ID)
|
secretkey, _ := keys.GenerateRandomBase64(keys.MinioSecretID)
|
||||||
|
|
||||||
user := User{
|
user := User{
|
||||||
Name: "gnubot",
|
Name: "gnubot",
|
||||||
@ -62,8 +62,8 @@ func (s *MySuite) TestConfig(c *C) {
|
|||||||
err = conf.ReadConfig()
|
err = conf.ReadConfig()
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
accesskey, _ = keys.GetRandomAlphaNumeric(keys.MINIO_ACCESS_ID)
|
accesskey, _ = keys.GenerateRandomAlphaNumeric(keys.MinioAccessID)
|
||||||
secretkey, _ = keys.GetRandomBase64(keys.MINIO_SECRET_ID)
|
secretkey, _ = keys.GenerateRandomBase64(keys.MinioSecretID)
|
||||||
user = User{
|
user = User{
|
||||||
Name: "minio",
|
Name: "minio",
|
||||||
AccessKey: string(accesskey),
|
AccessKey: string(accesskey),
|
||||||
|
@ -32,7 +32,7 @@ type MySuite struct{}
|
|||||||
|
|
||||||
var _ = Suite(&MySuite{})
|
var _ = Suite(&MySuite{})
|
||||||
|
|
||||||
func hasCpuFeatureFromOS(feature string) (bool, error) {
|
func hasCPUFeatureFromOS(feature string) (bool, error) {
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
command := exec.Command("/bin/cat", "/proc/cpuinfo")
|
command := exec.Command("/bin/cat", "/proc/cpuinfo")
|
||||||
output, err := command.Output()
|
output, err := command.Output()
|
||||||
@ -41,19 +41,16 @@ func hasCpuFeatureFromOS(feature string) (bool, error) {
|
|||||||
}
|
}
|
||||||
if strings.Contains(string(output), feature) {
|
if strings.Contains(string(output), feature) {
|
||||||
return true, nil
|
return true, nil
|
||||||
} else {
|
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
} else {
|
return false, nil
|
||||||
// TODO find new way to test cpu flags on windows
|
|
||||||
return false, errors.New("Not Implemented on this platform")
|
|
||||||
}
|
}
|
||||||
|
return false, errors.New("Not Implemented on this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MySuite) TestHasSSE41(c *C) {
|
func (s *MySuite) TestHasSSE41(c *C) {
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
var flag = HasSSE41()
|
var flag = HasSSE41()
|
||||||
osCheck, err := hasCpuFeatureFromOS("sse4_1")
|
osCheck, err := hasCPUFeatureFromOS("sse4_1")
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Check(flag, Equals, osCheck)
|
c.Check(flag, Equals, osCheck)
|
||||||
}
|
}
|
||||||
@ -62,7 +59,7 @@ func (s *MySuite) TestHasSSE41(c *C) {
|
|||||||
func (s *MySuite) TestHasAVX(c *C) {
|
func (s *MySuite) TestHasAVX(c *C) {
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
var flag = HasAVX()
|
var flag = HasAVX()
|
||||||
osFlag, err := hasCpuFeatureFromOS("avx")
|
osFlag, err := hasCPUFeatureFromOS("avx")
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Check(osFlag, Equals, flag)
|
c.Check(osFlag, Equals, flag)
|
||||||
}
|
}
|
||||||
@ -71,7 +68,7 @@ func (s *MySuite) TestHasAVX(c *C) {
|
|||||||
func (s *MySuite) TestHasAVX2(c *C) {
|
func (s *MySuite) TestHasAVX2(c *C) {
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
var flag = HasAVX2()
|
var flag = HasAVX2()
|
||||||
osFlag, err := hasCpuFeatureFromOS("avx2")
|
osFlag, err := hasCPUFeatureFromOS("avx2")
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Check(osFlag, Equals, flag)
|
c.Check(osFlag, Equals, flag)
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,10 @@
|
|||||||
|
|
||||||
package keys
|
package keys
|
||||||
|
|
||||||
|
// AccessID and SecretID length in bytes
|
||||||
const (
|
const (
|
||||||
MINIO_ACCESS_ID = 20
|
MinioAccessID = 20
|
||||||
MINIO_SECRET_ID = 40
|
MinioSecretID = 40
|
||||||
)
|
)
|
||||||
|
|
||||||
/// helpers
|
/// helpers
|
||||||
@ -28,8 +29,8 @@ func isalnum(c byte) bool {
|
|||||||
return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
|
return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate access key for only alphanumeric characters
|
// IsValidAccessKey - validate access key for only alphanumeric characters
|
||||||
func ValidateAccessKey(key []byte) bool {
|
func IsValidAccessKey(key []byte) bool {
|
||||||
for _, char := range key {
|
for _, char := range key {
|
||||||
if isalnum(char) {
|
if isalnum(char) {
|
||||||
continue
|
continue
|
||||||
|
@ -23,13 +23,10 @@ import (
|
|||||||
|
|
||||||
// Static alphaNumeric table used for generating unique keys
|
// Static alphaNumeric table used for generating unique keys
|
||||||
var alphaNumericTable = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
var alphaNumericTable = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
var alphaNumericTableFull = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
|
||||||
|
|
||||||
/// helpers
|
// GenerateRandomAlphaNumeric - generate random alpha numeric value using only uppercase characters
|
||||||
|
|
||||||
// Generate random alpha numeric value using only uppercase characters
|
|
||||||
// takes input as size in integer
|
// takes input as size in integer
|
||||||
func GetRandomAlphaNumeric(size int) ([]byte, error) {
|
func GenerateRandomAlphaNumeric(size int) ([]byte, error) {
|
||||||
alpha := make([]byte, size)
|
alpha := make([]byte, size)
|
||||||
_, err := rand.Read(alpha)
|
_, err := rand.Read(alpha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -42,22 +39,8 @@ func GetRandomAlphaNumeric(size int) ([]byte, error) {
|
|||||||
return alpha, nil
|
return alpha, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate random alpha numeric value using all alphanumeric characters
|
// GenerateRandomBase64 - generate random base64 numeric value from a random seed.
|
||||||
// takes input as size in integer
|
func GenerateRandomBase64(size int) ([]byte, error) {
|
||||||
func GetRandomAlphaNumericFull(size int) ([]byte, error) {
|
|
||||||
alphaFull := make([]byte, size)
|
|
||||||
_, err := rand.Read(alphaFull)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
alphaFull[i] = alphaNumericTableFull[alphaFull[i]%byte(len(alphaNumericTableFull))]
|
|
||||||
}
|
|
||||||
return alphaFull, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate random base64 numeric value from a random seed.
|
|
||||||
func GetRandomBase64(size int) ([]byte, error) {
|
|
||||||
rb := make([]byte, size)
|
rb := make([]byte, size)
|
||||||
_, err := rand.Read(rb)
|
_, err := rand.Read(rb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -29,10 +29,10 @@ type MySuite struct{}
|
|||||||
var _ = Suite(&MySuite{})
|
var _ = Suite(&MySuite{})
|
||||||
|
|
||||||
func (s *MySuite) Testing(c *C) {
|
func (s *MySuite) Testing(c *C) {
|
||||||
value, err := GetRandomBase64(MINIO_SECRET_ID)
|
value, err := GenerateRandomBase64(MinioSecretID)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
alphanum, err := GetRandomAlphaNumeric(MINIO_ACCESS_ID)
|
alphanum, err := GenerateRandomAlphaNumeric(MinioAccessID)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
c.Log(string(value))
|
c.Log(string(value))
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Low memory footprint io.Reader based md5sum helper
|
// Sum - low memory footprint io.Reader based md5sum helper
|
||||||
func Sum(reader io.Reader) ([]byte, error) {
|
func Sum(reader io.Reader) ([]byte, error) {
|
||||||
hash := md5.New()
|
hash := md5.New()
|
||||||
var err error
|
var err error
|
||||||
|
@ -143,7 +143,7 @@ func (d *digest) checkSum() [Size]byte {
|
|||||||
|
|
||||||
/// Convenience functions
|
/// Convenience functions
|
||||||
|
|
||||||
// Single caller sha1 helper
|
// Sum1 - single caller sha1 helper
|
||||||
func Sum1(data []byte) [Size]byte {
|
func Sum1(data []byte) [Size]byte {
|
||||||
var d digest
|
var d digest
|
||||||
d.Reset()
|
d.Reset()
|
||||||
@ -151,7 +151,7 @@ func Sum1(data []byte) [Size]byte {
|
|||||||
return d.checkSum()
|
return d.checkSum()
|
||||||
}
|
}
|
||||||
|
|
||||||
// io.Reader based streaming sha1 helper
|
// Sum - io.Reader based streaming sha1 helper
|
||||||
func Sum(reader io.Reader) ([]byte, error) {
|
func Sum(reader io.Reader) ([]byte, error) {
|
||||||
h := New()
|
h := New()
|
||||||
var err error
|
var err error
|
||||||
|
@ -151,7 +151,7 @@ func (d *digest) checkSum() [Size]byte {
|
|||||||
|
|
||||||
/// Convenience functions
|
/// Convenience functions
|
||||||
|
|
||||||
// Single caller sha256 helper
|
// Sum256 - single caller sha256 helper
|
||||||
func Sum256(data []byte) [Size]byte {
|
func Sum256(data []byte) [Size]byte {
|
||||||
var d digest
|
var d digest
|
||||||
d.Reset()
|
d.Reset()
|
||||||
@ -159,7 +159,7 @@ func Sum256(data []byte) [Size]byte {
|
|||||||
return d.checkSum()
|
return d.checkSum()
|
||||||
}
|
}
|
||||||
|
|
||||||
// io.Reader based streaming sha256 helper
|
// Sum - io.Reader based streaming sha256 helper
|
||||||
func Sum(reader io.Reader) ([]byte, error) {
|
func Sum(reader io.Reader) ([]byte, error) {
|
||||||
h := New()
|
h := New()
|
||||||
var err error
|
var err error
|
||||||
|
@ -157,7 +157,7 @@ func (d *digest) checkSum() [Size]byte {
|
|||||||
|
|
||||||
/// Convenience functions
|
/// Convenience functions
|
||||||
|
|
||||||
// Single caller sha512 helper
|
// Sum512 - single caller sha512 helper
|
||||||
func Sum512(data []byte) [Size]byte {
|
func Sum512(data []byte) [Size]byte {
|
||||||
var d digest
|
var d digest
|
||||||
d.Reset()
|
d.Reset()
|
||||||
@ -165,7 +165,7 @@ func Sum512(data []byte) [Size]byte {
|
|||||||
return d.checkSum()
|
return d.checkSum()
|
||||||
}
|
}
|
||||||
|
|
||||||
// io.Reader based streaming sha512 helper
|
// Sum - io.Reader based streaming sha512 helper
|
||||||
func Sum(reader io.Reader) ([]byte, error) {
|
func Sum(reader io.Reader) ([]byte, error) {
|
||||||
h := New()
|
h := New()
|
||||||
var err error
|
var err error
|
||||||
@ -182,7 +182,7 @@ func Sum(reader io.Reader) ([]byte, error) {
|
|||||||
return h.Sum(nil), nil
|
return h.Sum(nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Similar to 'Sum()' but returns a [Size]byte
|
// SumStream - similar to 'Sum()' but returns a [Size]byte
|
||||||
func SumStream(reader io.Reader) ([Size]byte, error) {
|
func SumStream(reader io.Reader) ([Size]byte, error) {
|
||||||
var returnValue [Size]byte
|
var returnValue [Size]byte
|
||||||
sumSlice, err := Sum(reader)
|
sumSlice, err := Sum(reader)
|
||||||
|
@ -32,7 +32,7 @@ import (
|
|||||||
"github.com/minio-io/minio/pkg/utils/config"
|
"github.com/minio-io/minio/pkg/utils/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sign a given http request using HMAC style signatures
|
// SignRequest - a given http request using HMAC style signatures
|
||||||
func SignRequest(user config.User, req *http.Request) {
|
func SignRequest(user config.User, req *http.Request) {
|
||||||
if date := req.Header.Get("Date"); date == "" {
|
if date := req.Header.Get("Date"); date == "" {
|
||||||
req.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
|
req.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
|
||||||
@ -49,7 +49,7 @@ func SignRequest(user config.User, req *http.Request) {
|
|||||||
req.Header.Set("Authorization", authHeader.String())
|
req.Header.Set("Authorization", authHeader.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate an API request by validating its signature using HMAC signatures
|
// ValidateRequest - an API request by validating its signature using HMAC signatures
|
||||||
func ValidateRequest(user config.User, req *http.Request) (bool, error) {
|
func ValidateRequest(user config.User, req *http.Request) (bool, error) {
|
||||||
// Verify if date headers are set, if not reject the request
|
// Verify if date headers are set, if not reject the request
|
||||||
if req.Header.Get("x-amz-date") == "" {
|
if req.Header.Get("x-amz-date") == "" {
|
||||||
@ -117,7 +117,7 @@ func hasPrefixCaseInsensitive(s, pfx string) bool {
|
|||||||
|
|
||||||
// Canonicalize amazon special headers, headers starting with 'x-amz-'
|
// Canonicalize amazon special headers, headers starting with 'x-amz-'
|
||||||
func writeCanonicalizedAmzHeaders(buf *bytes.Buffer, req *http.Request) {
|
func writeCanonicalizedAmzHeaders(buf *bytes.Buffer, req *http.Request) {
|
||||||
amzHeaders := make([]string, 0)
|
var amzHeaders []string
|
||||||
vals := make(map[string][]string)
|
vals := make(map[string][]string)
|
||||||
for k, vv := range req.Header {
|
for k, vv := range req.Header {
|
||||||
if hasPrefixCaseInsensitive(k, "x-amz-") {
|
if hasPrefixCaseInsensitive(k, "x-amz-") {
|
||||||
|
@ -31,13 +31,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Based on http://golang.org/src/crypto/tls/generate_cert.go
|
// Certificates - based on http://golang.org/src/crypto/tls/generate_cert.go
|
||||||
type Certificates struct {
|
type Certificates struct {
|
||||||
CertPemBlock []byte
|
CertPemBlock []byte
|
||||||
CertKeyBlock []byte
|
CertKeyBlock []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type X509Params struct {
|
// Params - various x.509 parameters
|
||||||
|
type Params struct {
|
||||||
Hostname string
|
Hostname string
|
||||||
IsCA bool
|
IsCA bool
|
||||||
EcdsaCurve string
|
EcdsaCurve string
|
||||||
@ -72,9 +73,9 @@ func pemBlockForKey(priv interface{}) *pem.Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate certificates using custom parameters
|
// GenerateCertificates - generate certificates using custom x.509 parameters
|
||||||
func (tls *Certificates) GenerateCertificates(params X509Params) error {
|
func (tls *Certificates) GenerateCertificates(params Params) error {
|
||||||
var rsaBits int = 2048
|
var rsaBits = 2048
|
||||||
var priv interface{}
|
var priv interface{}
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ var _ = Suite(&MySuite{})
|
|||||||
|
|
||||||
func (s *MySuite) Testing(c *C) {
|
func (s *MySuite) Testing(c *C) {
|
||||||
certObj := Certificates{}
|
certObj := Certificates{}
|
||||||
params := X509Params{
|
params := Params{
|
||||||
Hostname: "example.com",
|
Hostname: "example.com",
|
||||||
IsCA: false,
|
IsCA: false,
|
||||||
EcdsaCurve: "P224",
|
EcdsaCurve: "P224",
|
||||||
|
@ -6,19 +6,19 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// For 0000-00-00 Date type
|
// Date - [0000-00-00]
|
||||||
type Date struct {
|
type Date struct {
|
||||||
Year int16
|
Year int16
|
||||||
Month byte
|
Month byte
|
||||||
Day byte
|
Day byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date to string output in yyyy-mm-dd format
|
// String output in yyyy-mm-dd format
|
||||||
func (d Date) String() string {
|
func (d Date) String() string {
|
||||||
return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
|
return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)
|
||||||
}
|
}
|
||||||
|
|
||||||
// True if date is 0000-00-00
|
// IsZero true if date is 0000-00-00
|
||||||
func (d Date) IsZero() bool {
|
func (d Date) IsZero() bool {
|
||||||
return d.Day == 0 && d.Month == 0 && d.Year == 0
|
return d.Day == 0 && d.Month == 0 && d.Year == 0
|
||||||
}
|
}
|
||||||
|
@ -6,35 +6,41 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserCred struct {
|
// User - AWS canonical
|
||||||
|
type User struct {
|
||||||
AWS string
|
AWS string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stmt struct {
|
// Statement - AWS policy statement
|
||||||
|
type Statement struct {
|
||||||
Sid string
|
Sid string
|
||||||
Effect string
|
Effect string
|
||||||
Principal UserCred
|
Principal User
|
||||||
Action []string
|
Action []string
|
||||||
Resource []string
|
Resource []string
|
||||||
// TODO fix it in future if necessary - Condition {}
|
// TODO fix it in future if necessary - Condition {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BucketPolicy - AWS policy collection
|
||||||
type BucketPolicy struct {
|
type BucketPolicy struct {
|
||||||
Version string // date in 0000-00-00 format
|
Version string // date in 0000-00-00 format
|
||||||
Statement []Stmt
|
Statement []Statement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resource delimiter
|
||||||
const (
|
const (
|
||||||
AwsResource = "arn:aws:s3:::"
|
AwsResource = "arn:aws:s3:::"
|
||||||
MinioResource = "minio:::"
|
MinioResource = "minio:::"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO support canonical user
|
// TODO support canonical user
|
||||||
|
// Principal delimiter
|
||||||
const (
|
const (
|
||||||
AwsPrincipal = "arn:aws:iam::"
|
AwsPrincipal = "arn:aws:iam::"
|
||||||
MinioPrincipal = "minio::"
|
MinioPrincipal = "minio::"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Action map
|
||||||
var SupportedActionMap = map[string]bool{
|
var SupportedActionMap = map[string]bool{
|
||||||
"*": true,
|
"*": true,
|
||||||
"s3:GetObject": true,
|
"s3:GetObject": true,
|
||||||
@ -47,22 +53,19 @@ var SupportedActionMap = map[string]bool{
|
|||||||
"s3:PutBucketPolicy": true,
|
"s3:PutBucketPolicy": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Effect map
|
||||||
var SupportedEffectMap = map[string]bool{
|
var SupportedEffectMap = map[string]bool{
|
||||||
"Allow": true,
|
"Allow": true,
|
||||||
"Deny": true,
|
"Deny": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValidAction(action []string) bool {
|
func isValidAction(action []string) bool {
|
||||||
var ok bool = false
|
|
||||||
for _, a := range action {
|
for _, a := range action {
|
||||||
if !SupportedActionMap[a] {
|
if !SupportedActionMap[a] {
|
||||||
goto error
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ok = true
|
return true
|
||||||
|
|
||||||
error:
|
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValidEffect(effect string) bool {
|
func isValidEffect(effect string) bool {
|
||||||
@ -73,7 +76,7 @@ func isValidEffect(effect string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isValidResource(resources []string) bool {
|
func isValidResource(resources []string) bool {
|
||||||
var ok bool = false
|
var ok bool
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
switch true {
|
switch true {
|
||||||
case strings.HasPrefix(resource, AwsResource):
|
case strings.HasPrefix(resource, AwsResource):
|
||||||
@ -96,7 +99,7 @@ func isValidResource(resources []string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isValidPrincipal(principal string) bool {
|
func isValidPrincipal(principal string) bool {
|
||||||
var ok bool = false
|
var ok bool
|
||||||
if principal == "*" {
|
if principal == "*" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -120,7 +123,7 @@ func isValidPrincipal(principal string) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate request body is proper JSON
|
// Parsepolicy - validate request body is proper JSON and in accordance with policy standards
|
||||||
func Parsepolicy(data io.Reader) (BucketPolicy, bool) {
|
func Parsepolicy(data io.Reader) (BucketPolicy, bool) {
|
||||||
var policy BucketPolicy
|
var policy BucketPolicy
|
||||||
decoder := json.NewDecoder(data)
|
decoder := json.NewDecoder(data)
|
||||||
|
@ -27,13 +27,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Message structure for results from the SplitStream goroutine
|
// Message - message structure for results from the Stream goroutine
|
||||||
type SplitMessage struct {
|
type Message struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitStream reads from io.Reader, splits the data into chunks, and sends
|
// Stream reads from io.Reader, splits the data into chunks, and sends
|
||||||
// each chunk to the channel. This method runs until an EOF or error occurs. If
|
// each chunk to the channel. This method runs until an EOF or error occurs. If
|
||||||
// an error occurs, the method sends the error over the channel and returns.
|
// an error occurs, the method sends the error over the channel and returns.
|
||||||
// Before returning, the channel is always closed.
|
// Before returning, the channel is always closed.
|
||||||
@ -41,18 +41,18 @@ type SplitMessage struct {
|
|||||||
// The user should run this as a gorountine and retrieve the data over the
|
// The user should run this as a gorountine and retrieve the data over the
|
||||||
// channel.
|
// channel.
|
||||||
//
|
//
|
||||||
// channel := make(chan SplitMessage)
|
// channel := make(chan Message)
|
||||||
// go SplitStream(reader, chunkSize, channel)
|
// go Stream(reader, chunkSize, channel)
|
||||||
// for chunk := range channel {
|
// for chunk := range channel {
|
||||||
// log.Println(chunk.Data)
|
// log.Println(chunk.Data)
|
||||||
// }
|
// }
|
||||||
func SplitStream(reader io.Reader, chunkSize uint64) <-chan SplitMessage {
|
func Stream(reader io.Reader, chunkSize uint64) <-chan Message {
|
||||||
ch := make(chan SplitMessage)
|
ch := make(chan Message)
|
||||||
go splitStreamGoRoutine(reader, chunkSize, ch)
|
go splitStreamGoRoutine(reader, chunkSize, ch)
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitStreamGoRoutine(reader io.Reader, chunkSize uint64, ch chan SplitMessage) {
|
func splitStreamGoRoutine(reader io.Reader, chunkSize uint64, ch chan Message) {
|
||||||
// we read until EOF or another error
|
// we read until EOF or another error
|
||||||
var readError error
|
var readError error
|
||||||
|
|
||||||
@ -81,12 +81,12 @@ func splitStreamGoRoutine(reader io.Reader, chunkSize uint64, ch chan SplitMessa
|
|||||||
bytesWriter.Flush()
|
bytesWriter.Flush()
|
||||||
// if we have data available, send it over the channel
|
// if we have data available, send it over the channel
|
||||||
if bytesBuffer.Len() != 0 {
|
if bytesBuffer.Len() != 0 {
|
||||||
ch <- SplitMessage{bytesBuffer.Bytes(), nil}
|
ch <- Message{bytesBuffer.Bytes(), nil}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if we have an error other than an EOF, send it over the channel
|
// if we have an error other than an EOF, send it over the channel
|
||||||
if readError != io.EOF {
|
if readError != io.EOF {
|
||||||
ch <- SplitMessage{nil, readError}
|
ch <- Message{nil, readError}
|
||||||
}
|
}
|
||||||
// close the channel, signaling the channel reader that the stream is complete
|
// close the channel, signaling the channel reader that the stream is complete
|
||||||
close(ch)
|
close(ch)
|
||||||
@ -142,9 +142,9 @@ func joinFilesGoRoutine(fileInfos []os.FileInfo, writer *io.PipeWriter) {
|
|||||||
writer.Close()
|
writer.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Takes a file and splits it into chunks with size chunkSize. The output
|
// FileWithPrefix - Takes a file and splits it into chunks with size chunkSize. The output
|
||||||
// filename is given with outputPrefix.
|
// filename is given with outputPrefix.
|
||||||
func SplitFileWithPrefix(filename string, chunkSize uint64, outputPrefix string) error {
|
func FileWithPrefix(filename string, chunkSize uint64, outputPrefix string) error {
|
||||||
// open file
|
// open file
|
||||||
file, err := os.Open(filename)
|
file, err := os.Open(filename)
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
@ -157,7 +157,7 @@ func SplitFileWithPrefix(filename string, chunkSize uint64, outputPrefix string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// start stream splitting goroutine
|
// start stream splitting goroutine
|
||||||
ch := SplitStream(file, chunkSize)
|
ch := Stream(file, chunkSize)
|
||||||
|
|
||||||
// used to write each chunk out as a separate file. {{outputPrefix}}.{{i}}
|
// used to write each chunk out as a separate file. {{outputPrefix}}.{{i}}
|
||||||
i := 0
|
i := 0
|
||||||
|
@ -41,7 +41,7 @@ func (s *MySuite) TestSplitStream(c *C) {
|
|||||||
}
|
}
|
||||||
bytesWriter.Flush()
|
bytesWriter.Flush()
|
||||||
reader := bytes.NewReader(bytesBuffer.Bytes())
|
reader := bytes.NewReader(bytesBuffer.Bytes())
|
||||||
ch := SplitStream(reader, 25)
|
ch := Stream(reader, 25)
|
||||||
var resultsBuffer bytes.Buffer
|
var resultsBuffer bytes.Buffer
|
||||||
resultsWriter := bufio.NewWriter(&resultsBuffer)
|
resultsWriter := bufio.NewWriter(&resultsBuffer)
|
||||||
for chunk := range ch {
|
for chunk := range ch {
|
||||||
@ -52,9 +52,9 @@ func (s *MySuite) TestSplitStream(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *MySuite) TestFileSplitJoin(c *C) {
|
func (s *MySuite) TestFileSplitJoin(c *C) {
|
||||||
err := SplitFileWithPrefix("test-data/TESTFILE", 1024, "TESTPREFIX")
|
err := FileWithPrefix("test-data/TESTFILE", 1024, "TESTPREFIX")
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
err = SplitFileWithPrefix("test-data/TESTFILE", 1024, "")
|
err = FileWithPrefix("test-data/TESTFILE", 1024, "")
|
||||||
c.Assert(err, Not(IsNil))
|
c.Assert(err, Not(IsNil))
|
||||||
|
|
||||||
devnull, err := os.OpenFile(os.DevNull, 2, os.ModeAppend)
|
devnull, err := os.OpenFile(os.DevNull, 2, os.ModeAppend)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user