Skip to content

Commit

Permalink
cleanup: Major rewrite of most of the components
Browse files Browse the repository at this point in the history
- Handles region automatically.
- providew New() (automatically detects signature),
  NewV2() defaults to signature v2, NewV4() defaults to signature v4.
- Add SetCustomTransport() to be able to debug transport.
- Simplify request handling.
- Add more documentation.
- Add unit tests on endpoint verification and validation.
- Add functional tests.
- Further documentation improvements.
- Cleanup and remove any remaining temp files after completion.
  • Loading branch information
Harshavardhana committed Dec 11, 2015
1 parent efabece commit ba6eb86
Show file tree
Hide file tree
Showing 54 changed files with 2,676 additions and 2,067 deletions.
1,187 changes: 213 additions & 974 deletions api.go

Large diffs are not rendered by default.

87 changes: 87 additions & 0 deletions api_functional_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package minio_test

import (
"log"
"math/rand"
"testing"
"time"

"github.com/minio/minio-go"
)

const letterBytes = "abcdefghijklmnopqrstuvwxyz01234569-"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)

func randString(n int, src rand.Source) string {
b := make([]byte, n)
// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b[0:30])
}

func TestFunctional(t *testing.T) {
a, err := minio.New("play.minio.io:9002",
"Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", false)
if err != nil {
log.Fatalln(err)
}

bucketName := randString(60, rand.NewSource(time.Now().UnixNano()))
err = a.MakeBucket(bucketName, "private", "us-east-1")
if err != nil {
t.Fatal("Error", err)
}

err = a.BucketExists(bucketName)
if err != nil {
t.Fatal("Error", err)
}

err = a.SetBucketACL(bucketName, "public-read-write")
if err != nil {
t.Fatal("Error", err)
}

acl, err := a.GetBucketACL(bucketName)
if err != nil {
t.Fatal("Error", err)
}
if acl != minio.BucketACL("public-read-write") {
t.Fatal("Error", acl)
}

for b := range a.ListBuckets() {
if b.Err != nil {
t.Fatal("Error", b.Err)
}
}

err = a.RemoveBucket(bucketName)
if err != nil {
t.Fatal("Error", err)
}

err = a.RemoveBucket("bucket1")
if err == nil {
t.Fatal("Error")
}

if err.Error() != "The specified bucket does not exist." {
t.Fatal("Error", err)
}

}
13 changes: 7 additions & 6 deletions api_handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,15 @@ type bucketHandler struct {
}

func (h bucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
switch {
case r.Method == "GET":
switch {
case r.URL.Path == "/":
case path == "/":
response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListAllMyBucketsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Buckets><Bucket><Name>bucket</Name><CreationDate>2015-05-20T23:05:09.230Z</CreationDate></Bucket></Buckets><Owner><ID>minio</ID><DisplayName>minio</DisplayName></Owner></ListAllMyBucketsResult>")
w.Header().Set("Content-Length", strconv.Itoa(len(response)))
w.Write(response)
case r.URL.Path == "/bucket":
case path == h.resource:
_, ok := r.URL.Query()["acl"]
if ok {
response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><AccessControlPolicy><Owner><ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID><DisplayName>CustomersName@amazon.com</DisplayName></Owner><AccessControlList><Grant><Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID><DisplayName>CustomersName@amazon.com</DisplayName></Grantee><Permission>FULL_CONTROL</Permission></Grant></AccessControlList></AccessControlPolicy>")
Expand All @@ -59,14 +60,14 @@ func (h bucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
fallthrough
case r.URL.Path == "/bucket":
case path == h.resource:
response := []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Contents><ETag>\"259d04a13802ae09c7e41be50ccc6baa\"</ETag><Key>object</Key><LastModified>2015-05-21T18:24:21.097Z</LastModified><Size>22061</Size><Owner><ID>minio</ID><DisplayName>minio</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Delimiter></Delimiter><EncodingType></EncodingType><IsTruncated>false</IsTruncated><Marker></Marker><MaxKeys>1000</MaxKeys><Name>testbucket</Name><NextMarker></NextMarker><Prefix></Prefix></ListBucketResult>")
w.Header().Set("Content-Length", strconv.Itoa(len(response)))
w.Write(response)
}
case r.Method == "PUT":
switch {
case r.URL.Path == h.resource:
case path == h.resource:
_, ok := r.URL.Query()["acl"]
if ok {
switch r.Header.Get("x-amz-acl") {
Expand All @@ -90,14 +91,14 @@ func (h bucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
case r.Method == "HEAD":
switch {
case r.URL.Path == h.resource:
case path == h.resource:
w.WriteHeader(http.StatusOK)
default:
w.WriteHeader(http.StatusForbidden)
}
case r.Method == "DELETE":
switch {
case r.URL.Path != h.resource:
case path != h.resource:
w.WriteHeader(http.StatusNotFound)
default:
h.resource = ""
Expand Down
152 changes: 132 additions & 20 deletions api_private_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,25 @@

package minio

import "testing"
import (
"net/url"
"testing"
)

func TestSignature(t *testing.T) {
conf := new(Config)
if !conf.Signature.isLatest() {
credentials := clientCredentials{}
if !credentials.Signature.isLatest() {
t.Fatalf("Error")
}
conf.Signature = SignatureV2
if !conf.Signature.isV2() {
credentials.Signature = SignatureV2
if !credentials.Signature.isV2() {
t.Fatalf("Error")
}
if conf.Signature.isV4() {
if credentials.Signature.isV4() {
t.Fatalf("Error")
}
conf.Signature = SignatureV4
if !conf.Signature.isV4() {
credentials.Signature = SignatureV4
if !credentials.Signature.isV4() {
t.Fatalf("Error")
}
}
Expand All @@ -51,17 +54,6 @@ func TestACLTypes(t *testing.T) {
}
}

func TestGetRegion(t *testing.T) {
region := getRegion("s3.amazonaws.com")
if region != "us-east-1" {
t.Fatalf("Error")
}
region = getRegion("localhost:9000")
if region != "us-east-1" {
t.Fatalf("Error")
}
}

func TestPartSize(t *testing.T) {
var maxPartSize int64 = 1024 * 1024 * 1024 * 5
partSize := calculatePartSize(5000000000000000000)
Expand Down Expand Up @@ -110,8 +102,128 @@ func TestURLEncoding(t *testing.T) {
}

for _, u := range want {
if u.encodedName != getURLEncodedPath(u.name) {
if u.encodedName != urlEncodePath(u.name) {
t.Errorf("Error")
}
}
}

func TestGetEndpointURL(t *testing.T) {
if _, err := getEndpointURL("s3.amazonaws.com", false); err != nil {
t.Fatal(err)
}
if _, err := getEndpointURL("192.168.1.1", false); err != nil {
t.Fatal(err)
}
if _, err := getEndpointURL("13333.123123.", false); err == nil {
t.Fatal("Error")
}
if _, err := getEndpointURL("s3.aamzza.", false); err == nil {
t.Fatal("Error")
}
if _, err := getEndpointURL("s3.amazonaws.com:443", false); err == nil {
t.Fatal("Error")
}
}

func TestValidIP(t *testing.T) {
type validIP struct {
ip string
valid bool
}

want := []validIP{
{
ip: "192.168.1.1",
valid: true,
},
{
ip: "192.1.8",
valid: false,
},
{
ip: "..192.",
valid: false,
},
{
ip: "192.168.1.1.1",
valid: false,
},
}
for _, w := range want {
valid := validIPAddress.MatchString(w.ip)
if valid != w.valid {
t.Fatal("Error")
}
}
}

func TestValidEndpointDomain(t *testing.T) {
type validEndpoint struct {
endpointDomain string
valid bool
}

want := []validEndpoint{
{
endpointDomain: "s3.amazonaws.com",
valid: true,
},
{
endpointDomain: "s3.amazonaws.com_",
valid: false,
},
{
endpointDomain: "%$$$",
valid: false,
},
{
endpointDomain: "s3.amz.test.com",
valid: false,
},
{
endpointDomain: "s3.",
valid: false,
},
}
for _, w := range want {
valid := validEndpointDomain.MatchString(w.endpointDomain)
if valid != w.valid {
t.Fatal("Error")
}
}
}

func TestValidEndpointURL(t *testing.T) {
type validURL struct {
url string
valid bool
}
want := []validURL{
{
url: "https://s3.amazonaws.com",
valid: true,
},
{
url: "https://s3.amazonaws.com/bucket/object",
valid: false,
},
{
url: "192.168.1.1",
valid: false,
},
}
for _, w := range want {
u, err := url.Parse(w.url)
if err != nil {
t.Fatal("Error")
}
valid := false
if err := isValidEndpointURL(u); err == nil {
valid = true
}
if valid != w.valid {
t.Fatal("Error")
}
}
}
Loading

0 comments on commit ba6eb86

Please sign in to comment.