mirror of
https://github.com/minio/minio.git
synced 2025-11-09 05:34:56 -05:00
Service a new WebUI server at port 8081.
It is provided to avoid the router clutter on the Object API end. This is just a first cut sample we are going to build on top of this the new management console UI. General interest comes from subsequently writing a small UI to generate `AccessKey` and `SecretKey` to build authorization layer.
This commit is contained in:
82
pkg/api/minioapi/definitions.go
Normal file
82
pkg/api/minioapi/definitions.go
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Mini Object Storage, (C) 2014 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package minioapi
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
const (
|
||||
MAX_OBJECT_LIST = 1000
|
||||
)
|
||||
|
||||
type ObjectListResponse struct {
|
||||
XMLName xml.Name `xml:"ListBucketResult"`
|
||||
Name string
|
||||
Marker string
|
||||
MaxKeys int
|
||||
IsTruncated bool
|
||||
Contents []*Item `xml:"Contents",innerxml`
|
||||
}
|
||||
|
||||
type BucketListResponse struct {
|
||||
XMLName xml.Name `xml:"ListAllMyBucketsResult"`
|
||||
Owner Owner
|
||||
Buckets struct {
|
||||
Bucket []*Bucket
|
||||
} `xml:"Buckets",innerxml` // Buckets are nested
|
||||
}
|
||||
|
||||
type Bucket struct {
|
||||
Name string
|
||||
CreationDate string
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
Key string
|
||||
LastModified string
|
||||
ETag string
|
||||
Size int64
|
||||
StorageClass string
|
||||
Owner Owner
|
||||
}
|
||||
|
||||
type Owner struct {
|
||||
ID string
|
||||
DisplayName string
|
||||
}
|
||||
|
||||
var unimplementedBucketResourceNames = map[string]bool{
|
||||
"acl": true,
|
||||
"lifecycle": true,
|
||||
"policy": true,
|
||||
"location": true,
|
||||
"logging": true,
|
||||
"notification": true,
|
||||
"versions": true,
|
||||
"requestPayment": true,
|
||||
"versioning": true,
|
||||
"website": true,
|
||||
"uploads": true,
|
||||
}
|
||||
|
||||
var unimplementedObjectResourceNames = map[string]bool{
|
||||
"uploadId": true,
|
||||
"acl": true,
|
||||
"torrent": true,
|
||||
"uploads": true,
|
||||
}
|
||||
299
pkg/api/minioapi/minioapi.go
Normal file
299
pkg/api/minioapi/minioapi.go
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Mini Object Storage, (C) 2014 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package minioapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
mstorage "github.com/minio-io/minio/pkg/storage"
|
||||
)
|
||||
|
||||
type contentType int
|
||||
|
||||
const (
|
||||
xmlType contentType = iota
|
||||
jsonType
|
||||
)
|
||||
|
||||
const (
|
||||
dateFormat = "2006-01-02T15:04:05.000Z"
|
||||
)
|
||||
|
||||
type minioApi struct {
|
||||
storage mstorage.Storage
|
||||
}
|
||||
|
||||
// No encoder interface exists, so we create one.
|
||||
type encoder interface {
|
||||
Encode(v interface{}) error
|
||||
}
|
||||
|
||||
func HttpHandler(storage mstorage.Storage) http.Handler {
|
||||
mux := mux.NewRouter()
|
||||
var api = minioApi{}
|
||||
api.storage = storage
|
||||
|
||||
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
|
||||
mux.HandleFunc("/{bucket}/", api.listObjectsHandler).Methods("GET")
|
||||
mux.HandleFunc("/{bucket}/", api.putBucketHandler).Methods("PUT")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.getObjectHandler).Methods("GET")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.headObjectHandler).Methods("HEAD")
|
||||
mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectHandler).Methods("PUT")
|
||||
|
||||
return ignoreUnimplementedResources(mux)
|
||||
}
|
||||
|
||||
func ignoreUnimplementedResources(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if ignoreUnImplementedObjectResources(r) || ignoreUnImplementedBucketResources(r) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
} else {
|
||||
h.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
prefix, ok := vars["prefix"]
|
||||
if !ok {
|
||||
prefix = ""
|
||||
}
|
||||
|
||||
acceptsContentType := getContentType(req)
|
||||
buckets, err := server.storage.ListBuckets(prefix)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response := generateBucketsListResult(buckets)
|
||||
w.Write(writeObjectHeadersAndResponse(w, response, acceptsContentType))
|
||||
}
|
||||
|
||||
func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
bucket := vars["bucket"]
|
||||
prefix, ok := vars["prefix"]
|
||||
if !ok {
|
||||
prefix = ""
|
||||
}
|
||||
|
||||
acceptsContentType := getContentType(req)
|
||||
|
||||
objects, isTruncated, err := server.storage.ListObjects(bucket, prefix, 1000)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response := generateObjectsListResult(bucket, objects, isTruncated)
|
||||
w.Write(writeObjectHeadersAndResponse(w, response, acceptsContentType))
|
||||
}
|
||||
|
||||
func (server *minioApi) putBucketHandler(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
bucket := vars["bucket"]
|
||||
err := server.storage.StoreBucket(bucket)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Connection", "close")
|
||||
}
|
||||
|
||||
func (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
bucket := vars["bucket"]
|
||||
object := vars["object"]
|
||||
|
||||
metadata, err := server.storage.GetObjectMetadata(bucket, object)
|
||||
switch err := err.(type) {
|
||||
case nil: // success
|
||||
{
|
||||
log.Println("Found: " + bucket + "#" + object)
|
||||
writeObjectHeaders(w, metadata)
|
||||
if _, err := server.storage.CopyObjectToWriter(w, bucket, object); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
case mstorage.ObjectNotFound:
|
||||
{
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
default:
|
||||
{
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (server *minioApi) headObjectHandler(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
bucket := vars["bucket"]
|
||||
object := vars["object"]
|
||||
|
||||
metadata, err := server.storage.GetObjectMetadata(bucket, object)
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
writeObjectHeaders(w, metadata)
|
||||
case mstorage.ObjectNotFound:
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
default:
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func (server *minioApi) putObjectHandler(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
bucket := vars["bucket"]
|
||||
object := vars["object"]
|
||||
err := server.storage.StoreObject(bucket, object, req.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Connection", "close")
|
||||
}
|
||||
|
||||
func writeObjectHeadersAndResponse(w http.ResponseWriter, response interface{}, acceptsType contentType) []byte {
|
||||
var bytesBuffer bytes.Buffer
|
||||
var encoder encoder
|
||||
if acceptsType == xmlType {
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
encoder = xml.NewEncoder(&bytesBuffer)
|
||||
} else if acceptsType == jsonType {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
encoder = json.NewEncoder(&bytesBuffer)
|
||||
}
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Connection", "close")
|
||||
encoder.Encode(response)
|
||||
return bytesBuffer.Bytes()
|
||||
}
|
||||
|
||||
// Write Object Header helper
|
||||
func writeObjectHeaders(w http.ResponseWriter, metadata mstorage.ObjectMetadata) {
|
||||
lastModified := metadata.Created.Format(time.RFC1123)
|
||||
w.Header().Set("ETag", metadata.ETag)
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Last-Modified", lastModified)
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(metadata.Size, 10))
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Header().Set("Connection", "close")
|
||||
}
|
||||
|
||||
func generateBucketsListResult(buckets []mstorage.BucketMetadata) BucketListResponse {
|
||||
var listbuckets []*Bucket
|
||||
var data = BucketListResponse{}
|
||||
var owner = Owner{}
|
||||
|
||||
owner.ID = "minio"
|
||||
owner.DisplayName = "minio"
|
||||
|
||||
for _, bucket := range buckets {
|
||||
var listbucket = &Bucket{}
|
||||
listbucket.Name = bucket.Name
|
||||
listbucket.CreationDate = bucket.Created.Format(dateFormat)
|
||||
listbuckets = append(listbuckets, listbucket)
|
||||
}
|
||||
|
||||
data.Owner = owner
|
||||
data.Buckets.Bucket = listbuckets
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
//// helpers
|
||||
|
||||
// Checks requests for unimplemented resources
|
||||
func ignoreUnImplementedBucketResources(req *http.Request) bool {
|
||||
q := req.URL.Query()
|
||||
for name := range q {
|
||||
if unimplementedBucketResourceNames[name] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ignoreUnImplementedObjectResources(req *http.Request) bool {
|
||||
q := req.URL.Query()
|
||||
for name := range q {
|
||||
if unimplementedObjectResourceNames[name] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getContentType(req *http.Request) contentType {
|
||||
if _, ok := req.Header["Accept"]; ok {
|
||||
if req.Header["Accept"][0] == "application/json" {
|
||||
return jsonType
|
||||
}
|
||||
}
|
||||
return xmlType
|
||||
}
|
||||
|
||||
// takes a set of objects and prepares the objects for serialization
|
||||
// input:
|
||||
// bucket name
|
||||
// array of object metadata
|
||||
// results truncated flag
|
||||
//
|
||||
// output:
|
||||
// populated struct that can be serialized to match xml and json api spec output
|
||||
func generateObjectsListResult(bucket string, objects []mstorage.ObjectMetadata, isTruncated bool) ObjectListResponse {
|
||||
var contents []*Item
|
||||
var owner = Owner{}
|
||||
var data = ObjectListResponse{}
|
||||
|
||||
owner.ID = "minio"
|
||||
owner.DisplayName = "minio"
|
||||
|
||||
for _, object := range objects {
|
||||
var content = &Item{}
|
||||
content.Key = object.Key
|
||||
content.LastModified = object.Created.Format(dateFormat)
|
||||
content.ETag = object.ETag
|
||||
content.Size = object.Size
|
||||
content.StorageClass = "STANDARD"
|
||||
content.Owner = owner
|
||||
contents = append(contents, content)
|
||||
}
|
||||
data.Name = bucket
|
||||
data.Contents = contents
|
||||
data.MaxKeys = MAX_OBJECT_LIST
|
||||
data.IsTruncated = isTruncated
|
||||
return data
|
||||
}
|
||||
365
pkg/api/minioapi/minioapi_test.go
Normal file
365
pkg/api/minioapi/minioapi_test.go
Normal file
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* Mini Object Storage, (C) 2014 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package minioapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/minio-io/minio/pkg/storage/inmemory"
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) { TestingT(t) }
|
||||
|
||||
type MySuite struct{}
|
||||
|
||||
var _ = Suite(&MySuite{})
|
||||
|
||||
func (s *MySuite) TestNonExistantObject(c *C) {
|
||||
_, _, storage := inmemory.Start()
|
||||
httpHandler := HttpHandler(storage)
|
||||
testServer := httptest.NewServer(httpHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
response, err := http.Get(testServer.URL + "/bucket/object")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func (s *MySuite) TestEmptyObject(c *C) {
|
||||
_, _, storage := inmemory.Start()
|
||||
httpHandler := HttpHandler(storage)
|
||||
testServer := httptest.NewServer(httpHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
buffer := bytes.NewBufferString("")
|
||||
storage.StoreBucket("bucket")
|
||||
storage.StoreObject("bucket", "object", buffer)
|
||||
|
||||
response, err := http.Get(testServer.URL + "/bucket/object")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
responseBody, err := ioutil.ReadAll(response.Body)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(true, Equals, bytes.Equal(responseBody, buffer.Bytes()))
|
||||
|
||||
metadata, err := storage.GetObjectMetadata("bucket", "object")
|
||||
c.Assert(err, IsNil)
|
||||
verifyHeaders(c, response.Header, metadata.Created, 0, "text/plain", metadata.ETag)
|
||||
|
||||
// TODO Test Headers
|
||||
}
|
||||
|
||||
func (s *MySuite) TestObject(c *C) {
|
||||
_, _, storage := inmemory.Start()
|
||||
httpHandler := HttpHandler(storage)
|
||||
testServer := httptest.NewServer(httpHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
buffer := bytes.NewBufferString("hello world")
|
||||
storage.StoreBucket("bucket")
|
||||
storage.StoreObject("bucket", "object", buffer)
|
||||
|
||||
response, err := http.Get(testServer.URL + "/bucket/object")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
responseBody, err := ioutil.ReadAll(response.Body)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(true, Equals, bytes.Equal(responseBody, []byte("hello world")))
|
||||
|
||||
metadata, err := storage.GetObjectMetadata("bucket", "object")
|
||||
c.Assert(err, IsNil)
|
||||
verifyHeaders(c, response.Header, metadata.Created, len("hello world"), "text/plain", metadata.ETag)
|
||||
}
|
||||
|
||||
func (s *MySuite) TestMultipleObjects(c *C) {
|
||||
_, _, storage := inmemory.Start()
|
||||
httpHandler := HttpHandler(storage)
|
||||
testServer := httptest.NewServer(httpHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
buffer1 := bytes.NewBufferString("hello one")
|
||||
buffer2 := bytes.NewBufferString("hello two")
|
||||
buffer3 := bytes.NewBufferString("hello three")
|
||||
|
||||
storage.StoreBucket("bucket")
|
||||
storage.StoreObject("bucket", "object1", buffer1)
|
||||
storage.StoreObject("bucket", "object2", buffer2)
|
||||
storage.StoreObject("bucket", "object3", buffer3)
|
||||
|
||||
// test non-existant object
|
||||
response, err := http.Get(testServer.URL + "/bucket/object")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusNotFound)
|
||||
// TODO Test Headers
|
||||
|
||||
//// test object 1
|
||||
|
||||
// get object
|
||||
response, err = http.Get(testServer.URL + "/bucket/object1")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// get metadata
|
||||
metadata, err := storage.GetObjectMetadata("bucket", "object1")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
// verify headers
|
||||
verifyHeaders(c, response.Header, metadata.Created, len("hello one"), "text/plain", metadata.ETag)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// verify response data
|
||||
responseBody, err := ioutil.ReadAll(response.Body)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(true, Equals, bytes.Equal(responseBody, []byte("hello one")))
|
||||
|
||||
// test object 2
|
||||
// get object
|
||||
response, err = http.Get(testServer.URL + "/bucket/object2")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// get metadata
|
||||
metadata, err = storage.GetObjectMetadata("bucket", "object2")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
// verify headers
|
||||
verifyHeaders(c, response.Header, metadata.Created, len("hello two"), "text/plain", metadata.ETag)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// verify response data
|
||||
responseBody, err = ioutil.ReadAll(response.Body)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(true, Equals, bytes.Equal(responseBody, []byte("hello two")))
|
||||
|
||||
// test object 3
|
||||
// get object
|
||||
response, err = http.Get(testServer.URL + "/bucket/object3")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// get metadata
|
||||
metadata, err = storage.GetObjectMetadata("bucket", "object3")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
// verify headers
|
||||
verifyHeaders(c, response.Header, metadata.Created, len("hello three"), "text/plain", metadata.ETag)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// verify object
|
||||
responseBody, err = ioutil.ReadAll(response.Body)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(true, Equals, bytes.Equal(responseBody, []byte("hello three")))
|
||||
}
|
||||
|
||||
func (s *MySuite) TestNotImplemented(c *C) {
|
||||
_, _, storage := inmemory.Start()
|
||||
httpHandler := HttpHandler(storage)
|
||||
testServer := httptest.NewServer(httpHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
response, err := http.Get(testServer.URL + "/bucket/object?acl")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
func (s *MySuite) TestHeader(c *C) {
|
||||
_, _, storage := inmemory.Start()
|
||||
httpHandler := HttpHandler(storage)
|
||||
testServer := httptest.NewServer(httpHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
response, err := http.Get(testServer.URL + "/bucket/object")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusNotFound)
|
||||
|
||||
buffer := bytes.NewBufferString("hello world")
|
||||
storage.StoreBucket("bucket")
|
||||
storage.StoreObject("bucket", "object", buffer)
|
||||
|
||||
response, err = http.Get(testServer.URL + "/bucket/object")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
metadata, err := storage.GetObjectMetadata("bucket", "object")
|
||||
c.Assert(err, IsNil)
|
||||
verifyHeaders(c, response.Header, metadata.Created, len("hello world"), "text/plain", metadata.ETag)
|
||||
}
|
||||
|
||||
func (s *MySuite) TestPutBucket(c *C) {
|
||||
_, _, storage := inmemory.Start()
|
||||
httpHandler := HttpHandler(storage)
|
||||
testServer := httptest.NewServer(httpHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
buckets, err := storage.ListBuckets("bucket")
|
||||
c.Assert(len(buckets), Equals, 0)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
request, err := http.NewRequest("PUT", testServer.URL+"/bucket/", bytes.NewBufferString(""))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
client := http.Client{}
|
||||
response, err := client.Do(request)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
// check bucket exists
|
||||
buckets, err = storage.ListBuckets("bucket")
|
||||
c.Assert(len(buckets), Equals, 1)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(buckets[0].Name, Equals, "bucket")
|
||||
}
|
||||
|
||||
func (s *MySuite) TestPutObject(c *C) {
|
||||
_, _, storage := inmemory.Start()
|
||||
httpHandler := HttpHandler(storage)
|
||||
testServer := httptest.NewServer(httpHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
objects, isTruncated, err := storage.ListObjects("bucket", "", 1000)
|
||||
c.Assert(len(objects), Equals, 0)
|
||||
c.Assert(isTruncated, Equals, false)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
date1 := time.Now()
|
||||
|
||||
// Put Bucket before - Put Object into a bucket
|
||||
request, err := http.NewRequest("PUT", testServer.URL+"/bucket/", bytes.NewBufferString(""))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
client := http.Client{}
|
||||
response, err := client.Do(request)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
request, err = http.NewRequest("PUT", testServer.URL+"/bucket/two", bytes.NewBufferString("hello world"))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
response, err = client.Do(request)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
date2 := time.Now()
|
||||
|
||||
objects, isTruncated, err = storage.ListObjects("bucket", "", 1000)
|
||||
c.Assert(len(objects), Equals, 1)
|
||||
c.Assert(isTruncated, Equals, false)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
var writer bytes.Buffer
|
||||
|
||||
storage.CopyObjectToWriter(&writer, "bucket", "two")
|
||||
|
||||
c.Assert(bytes.Equal(writer.Bytes(), []byte("hello world")), Equals, true)
|
||||
|
||||
metadata, err := storage.GetObjectMetadata("bucket", "two")
|
||||
c.Assert(err, IsNil)
|
||||
lastModified := metadata.Created
|
||||
|
||||
c.Assert(date1.Before(lastModified), Equals, true)
|
||||
c.Assert(lastModified.Before(date2), Equals, true)
|
||||
}
|
||||
|
||||
func (s *MySuite) TestListBuckets(c *C) {
|
||||
_, _, storage := inmemory.Start()
|
||||
httpHandler := HttpHandler(storage)
|
||||
testServer := httptest.NewServer(httpHandler)
|
||||
defer testServer.Close()
|
||||
|
||||
response, err := http.Get(testServer.URL + "/")
|
||||
defer response.Body.Close()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
listResponse, err := readListBucket(response.Body)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(listResponse.Buckets.Bucket), Equals, 0)
|
||||
|
||||
storage.StoreBucket("foo")
|
||||
|
||||
response, err = http.Get(testServer.URL + "/")
|
||||
defer response.Body.Close()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
listResponse, err = readListBucket(response.Body)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(listResponse.Buckets.Bucket), Equals, 1)
|
||||
c.Assert(listResponse.Buckets.Bucket[0].Name, Equals, "foo")
|
||||
|
||||
storage.StoreBucket("bar")
|
||||
|
||||
response, err = http.Get(testServer.URL + "/")
|
||||
defer response.Body.Close()
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
listResponse, err = readListBucket(response.Body)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(len(listResponse.Buckets.Bucket), Equals, 2)
|
||||
|
||||
c.Assert(listResponse.Buckets.Bucket[0].Name, Equals, "bar")
|
||||
c.Assert(listResponse.Buckets.Bucket[1].Name, Equals, "foo")
|
||||
}
|
||||
|
||||
func readListBucket(reader io.Reader) (BucketListResponse, error) {
|
||||
var results BucketListResponse
|
||||
decoder := xml.NewDecoder(reader)
|
||||
err := decoder.Decode(&results)
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (s *MySuite) TestListObjects(c *C) {
|
||||
// TODO Implement
|
||||
}
|
||||
|
||||
func (s *MySuite) TestShouldNotBeAbleToCreateObjectInNonexistantBucket(c *C) {
|
||||
// TODO Implement
|
||||
}
|
||||
|
||||
func (s *MySuite) TestHeadOnObject(c *C) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func (s *MySuite) TestDateFormat(c *C) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
func verifyHeaders(c *C, header http.Header, date time.Time, size int, contentType string, etag string) {
|
||||
// Verify date
|
||||
c.Assert(header["Last-Modified"][0], Equals, date.Format(time.RFC1123))
|
||||
|
||||
// verify size
|
||||
c.Assert(header["Content-Length"][0], Equals, strconv.Itoa(size))
|
||||
|
||||
// verify content type
|
||||
c.Assert(header["Content-Type"][0], Equals, contentType)
|
||||
|
||||
// verify etag
|
||||
c.Assert(header["Etag"][0], Equals, etag)
|
||||
}
|
||||
Reference in New Issue
Block a user