Merge pull request #453 from harshavardhana/pr_out_implement_headbuckethandler_and_its_related_tests

This commit is contained in:
Harshavardhana 2015-04-07 01:17:24 -07:00
commit 34d06101cc
29 changed files with 160 additions and 721 deletions

14
Godeps/Godeps.json generated
View File

@ -1,14 +1,10 @@
{
"ImportPath": "github.com/minio-io/minio",
"GoVersion": "go1.4.2",
"GoVersion": "go1.4",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/fkautz/testify/mock",
"Rev": "e4ec8152c15fc46bd5056ce65997a07c7d415325"
},
{
"ImportPath": "github.com/gorilla/context",
"Rev": "50c25fb3b2b3b3cc724e9b6ac75fb44b3bccd0da"
@ -28,7 +24,7 @@
},
{
"ImportPath": "github.com/minio-io/donut",
"Rev": "1adb050ccbc6b56caa8b29502adf43592d16adbd"
"Rev": "9d0c663a857103c780414e15d7e426c6fca9984e"
},
{
"ImportPath": "github.com/minio-io/erasure",
@ -40,7 +36,7 @@
},
{
"ImportPath": "github.com/minio-io/objectdriver",
"Rev": "144846812e29de66814a67b38c949f9875e0ee46"
"Rev": "3efb266b6a644b161d8a4d9a1f55bea95a2e391b"
},
{
"ImportPath": "github.com/stretchr/objx",
@ -49,6 +45,10 @@
{
"ImportPath": "github.com/stretchr/testify/assert",
"Rev": "e4ec8152c15fc46bd5056ce65997a07c7d415325"
},
{
"ImportPath": "github.com/stretchr/testify/mock",
"Rev": "e4ec8152c15fc46bd5056ce65997a07c7d415325"
}
]
}

View File

@ -5,6 +5,10 @@
"./..."
],
"Deps": [
{
"ImportPath": "github.com/minio-io/check",
"Rev": "bc4e66da8cd7ff58a4b9b84301f906352b8f2c94"
},
{
"ImportPath": "github.com/minio-io/cli",
"Comment": "1.2.0-102-gecb385c",
@ -14,6 +18,10 @@
"ImportPath": "github.com/minio-io/erasure",
"Rev": "8a72b14991a6835b4d30403e7cb201f373b7cb3a"
},
{
"ImportPath": "github.com/minio-io/iodine",
"Rev": "55cc4d4256c68fbd6f0775f1a25e37e6a2f6457e"
},
{
"ImportPath": "github.com/minio-io/minio/pkg/utils/split",
"Rev": "936520e6e0fc5dd4ce8d04504ee991084555e57a"

View File

@ -60,8 +60,8 @@ docs-deploy:
@mkdocs gh-deploy --clean
install: test-all
@echo "Installing donut-cli:"
@godep go install -a github.com/minio-io/donut/cmd/donut-cli
@echo "Installing donut tool:"
@godep go install -a github.com/minio-io/donut/cmd/donut
@mkdir -p $(HOME)/.minio/donut
clean:

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
CONST_FILE=${PWD}/cmd/donut-cli/build-constants.go
CONST_FILE=${PWD}/cmd/donut/build-constants.go
cat > $CONST_FILE <<EOF
/*

View File

@ -1 +0,0 @@
donut-cli

View File

@ -1,58 +0,0 @@
/*
* Minimalist Object Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"log"
"github.com/minio-io/cli"
)
func doAttachDiskCmd(c *cli.Context) {
if !c.Args().Present() {
log.Fatalln("no args?")
}
disks := c.Args()
mcDonutConfigData, err := loadDonutConfig()
if err != nil {
log.Fatalln(err)
}
donutName := c.String("name")
if donutName == "" {
log.Fatalln("Invalid --donut <name> is needed for attach")
}
if _, ok := mcDonutConfigData.Donuts[donutName]; !ok {
log.Fatalf("Requested donut name %s does not exist, please use ``mc donut make`` first\n", donutName)
}
if _, ok := mcDonutConfigData.Donuts[donutName].Node["localhost"]; !ok {
log.Fatalln("Corrupted donut config, please consult donut experts")
}
activeDisks := mcDonutConfigData.Donuts[donutName].Node["localhost"].ActiveDisks
inactiveDisks := mcDonutConfigData.Donuts[donutName].Node["localhost"].InactiveDisks
for _, disk := range disks {
activeDisks = appendUniq(activeDisks, disk)
inactiveDisks = deleteFromSlice(inactiveDisks, disk)
}
mcDonutConfigData.Donuts[donutName].Node["localhost"] = nodeConfig{
ActiveDisks: activeDisks,
InactiveDisks: inactiveDisks,
}
if err := saveDonutConfig(mcDonutConfigData); err != nil {
log.Fatalln(err)
}
}

View File

@ -1,110 +0,0 @@
/*
* Minimalist Object Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"strings"
"net/url"
)
// url2Object converts URL to bucket and objectname
func url2Object(urlStr string) (bucketName, objectName string, err error) {
u, err := url.Parse(urlStr)
if u.Path == "" {
// No bucket name passed. It is a valid case
return "", "", nil
}
splits := strings.SplitN(u.Path, "/", 3)
switch len(splits) {
case 0, 1:
bucketName = ""
objectName = ""
case 2:
bucketName = splits[1]
objectName = ""
case 3:
bucketName = splits[1]
objectName = splits[2]
}
return bucketName, objectName, nil
}
func isStringInSlice(items []string, item string) bool {
for _, s := range items {
if s == item {
return true
}
}
return false
}
func deleteFromSlice(items []string, item string) []string {
var newitems []string
for _, s := range items {
if s == item {
continue
}
newitems = append(newitems, s)
}
return newitems
}
func appendUniq(slice []string, i string) []string {
for _, ele := range slice {
if ele == i {
return slice
}
}
return append(slice, i)
}
// Is alphanumeric?
func isalnum(c rune) bool {
return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
}
// isValidDonutName - verify donutName to be valid
func isValidDonutName(donutName string) bool {
if len(donutName) > 1024 || len(donutName) == 0 {
return false
}
for _, char := range donutName {
if isalnum(char) {
continue
}
switch char {
case '-':
case '.':
case '_':
case '~':
continue
default:
return false
}
}
return true
}
// getNodeMap - get a node and disk map through nodeConfig struct
func getNodeMap(node map[string]nodeConfig) map[string][]string {
nodes := make(map[string][]string)
for k, v := range node {
nodes[k] = v.ActiveDisks
}
return nodes
}

View File

@ -1,105 +0,0 @@
/*
* Minimalist Object Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"fmt"
"log"
"os"
"os/user"
"path"
"encoding/json"
"io/ioutil"
)
const (
donutConfigDir = ".minio/donut"
donutConfigFilename = "donuts.json"
)
type nodeConfig struct {
ActiveDisks []string
InactiveDisks []string
}
type donutConfig struct {
Node map[string]nodeConfig
}
type mcDonutConfig struct {
Donuts map[string]donutConfig
}
func getDonutConfigDir() string {
u, err := user.Current()
if err != nil {
msg := fmt.Sprintf("Unable to obtain user's home directory. \nError: %s", err)
log.Fatalln(msg)
}
return path.Join(u.HomeDir, donutConfigDir)
}
func getDonutConfigFilename() string {
return path.Join(getDonutConfigDir(), "donuts.json")
}
// saveDonutConfig writes configuration data in json format to donut config file.
func saveDonutConfig(donutConfigData *mcDonutConfig) error {
jsonConfig, err := json.MarshalIndent(donutConfigData, "", "\t")
if err != nil {
return err
}
err = os.MkdirAll(getDonutConfigDir(), 0755)
if !os.IsExist(err) && err != nil {
return err
}
configFile, err := os.OpenFile(getDonutConfigFilename(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer configFile.Close()
_, err = configFile.Write(jsonConfig)
if err != nil {
return err
}
return nil
}
func loadDonutConfig() (donutConfigData *mcDonutConfig, err error) {
configFile := getDonutConfigFilename()
_, err = os.Stat(configFile)
if err != nil {
return nil, err
}
configBytes, err := ioutil.ReadFile(configFile)
if err != nil {
return nil, err
}
err = json.Unmarshal(configBytes, &donutConfigData)
if err != nil {
return nil, err
}
return donutConfigData, nil
}

View File

@ -1,66 +0,0 @@
/*
* Minimalist Object Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"fmt"
"log"
"github.com/minio-io/cli"
)
func doDetachDiskCmd(c *cli.Context) {
if !c.Args().Present() {
log.Fatalln("no args?")
}
disks := c.Args()
mcDonutConfigData, err := loadDonutConfig()
if err != nil {
log.Fatalln(err.Error())
}
donutName := c.String("name")
if donutName == "" {
log.Fatalln("Invalid --donut <name> is needed for attach")
}
if _, ok := mcDonutConfigData.Donuts[donutName]; !ok {
msg := fmt.Sprintf("Requested donut name <%s> does not exist, please use ``mc donut make`` first", donutName)
log.Fatalln(msg)
}
if _, ok := mcDonutConfigData.Donuts[donutName].Node["localhost"]; !ok {
msg := fmt.Sprintf("Corrupted donut config, please consult donut experts")
log.Fatalln(msg)
}
inactiveDisks := mcDonutConfigData.Donuts[donutName].Node["localhost"].InactiveDisks
activeDisks := mcDonutConfigData.Donuts[donutName].Node["localhost"].ActiveDisks
for _, disk := range disks {
if isStringInSlice(activeDisks, disk) {
activeDisks = deleteFromSlice(activeDisks, disk)
inactiveDisks = appendUniq(inactiveDisks, disk)
} else {
msg := fmt.Sprintf("Cannot detach disk: <%s>, not part of donut <%s>", disk, donutName)
log.Println(msg)
}
}
mcDonutConfigData.Donuts[donutName].Node["localhost"] = nodeConfig{
ActiveDisks: activeDisks,
InactiveDisks: inactiveDisks,
}
if err := saveDonutConfig(mcDonutConfigData); err != nil {
log.Fatalln(err.Error())
}
}

View File

@ -1,84 +0,0 @@
/*
* Minimalist Object Storage, (C) 2014,2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"log"
"os"
"strings"
"text/tabwriter"
"text/template"
"github.com/minio-io/cli"
"github.com/minio-io/donut"
)
var infoTemplate = `
{{range $donutName, $nodes := .}}
DONUTNAME: {{$donutName}}
{{range $nodeName, $disks := $nodes}}
NODE: {{$nodeName}}
DISKS: {{$disks}}
{{end}}
{{end}}
`
var infoPrinter = func(templ string, data interface{}) {
funcMap := template.FuncMap{
"join": strings.Join,
}
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
err := t.Execute(w, data)
if err != nil {
panic(err)
}
w.Flush()
}
// doInfoDonutCmd
func doInfoDonutCmd(c *cli.Context) {
if !c.Args().Present() {
log.Fatalln("no args?")
}
if len(c.Args()) != 1 {
log.Fatalln("invalid number of args")
}
donutName := c.Args().First()
if !isValidDonutName(donutName) {
log.Fatalln("Invalid donutName")
}
mcDonutConfigData, err := loadDonutConfig()
if err != nil {
log.Fatalln(err)
}
if _, ok := mcDonutConfigData.Donuts[donutName]; !ok {
log.Fatalln("donut does not exist")
}
d, err := donut.NewDonut(donutName, getNodeMap(mcDonutConfigData.Donuts[donutName].Node))
if err != nil {
log.Fatalln(err)
}
donutNodes := make(map[string]map[string][]string)
donutNodes[donutName], err = d.Info()
if err != nil {
log.Fatalln(err)
}
infoPrinter(infoTemplate, donutNodes)
}

View File

@ -1,36 +0,0 @@
/*
* Minimalist Object Storage, (C) 2014,2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"fmt"
"log"
"github.com/minio-io/cli"
)
// doListDonutCmd creates a new bucket
func doListDonutCmd(c *cli.Context) {
mcDonutConfigData, err := loadDonutConfig()
if err != nil {
log.Fatalln(err)
}
for k := range mcDonutConfigData.Donuts {
fmt.Println(k)
}
}

View File

@ -1,80 +0,0 @@
/*
* Minimalist Object Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"fmt"
"log"
"os"
"github.com/minio-io/cli"
)
func newDonutConfig(donutName string) (*mcDonutConfig, error) {
mcDonutConfigData := new(mcDonutConfig)
mcDonutConfigData.Donuts = make(map[string]donutConfig)
mcDonutConfigData.Donuts[donutName] = donutConfig{
Node: make(map[string]nodeConfig),
}
mcDonutConfigData.Donuts[donutName].Node["localhost"] = nodeConfig{
ActiveDisks: make([]string, 0),
InactiveDisks: make([]string, 0),
}
return mcDonutConfigData, nil
}
// doMakeDonutCmd creates a new donut
func doMakeDonutCmd(c *cli.Context) {
if !c.Args().Present() {
log.Fatalln("no args?")
}
if len(c.Args()) != 1 {
log.Fatalln("invalid number of args")
}
donutName := c.Args().First()
if !isValidDonutName(donutName) {
log.Fatalln("Invalid donutName")
}
mcDonutConfigData, err := loadDonutConfig()
if os.IsNotExist(err) {
mcDonutConfigData, err = newDonutConfig(donutName)
if err != nil {
log.Fatalln(err)
}
if err := saveDonutConfig(mcDonutConfigData); err != nil {
log.Fatalln(err)
}
return
} else if err != nil {
log.Fatalln(err)
}
if _, ok := mcDonutConfigData.Donuts[donutName]; !ok {
mcDonutConfigData.Donuts[donutName] = donutConfig{
Node: make(map[string]nodeConfig),
}
mcDonutConfigData.Donuts[donutName].Node["localhost"] = nodeConfig{
ActiveDisks: make([]string, 0),
InactiveDisks: make([]string, 0),
}
if err := saveDonutConfig(mcDonutConfigData); err != nil {
log.Fatalln(err)
}
} else {
msg := fmt.Sprintf("donut: %s already exists", donutName)
log.Println(msg)
}
}

View File

@ -1,95 +0,0 @@
/*
* Minimalist Object Storage, (C) 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"github.com/minio-io/cli"
)
var makeDonutCmd = cli.Command{
Name: "make",
Usage: "make donut",
Description: "Make a new donut",
Action: doMakeDonutCmd,
}
var listDonutCmd = cli.Command{
Name: "list",
Usage: "list donuts",
Description: "list all donuts locally or remote",
Action: doListDonutCmd,
}
var attachDiskCmd = cli.Command{
Name: "attach",
Usage: "attach disk",
Description: "Attach disk to an existing donut",
Action: doAttachDiskCmd,
Flags: []cli.Flag{
cli.StringFlag{
Name: "name",
Usage: "Donut name",
},
},
}
var detachDiskCmd = cli.Command{
Name: "detach",
Usage: "detach disk",
Description: "Detach disk from an existing donut",
Action: doDetachDiskCmd,
Flags: []cli.Flag{
cli.StringFlag{
Name: "name",
Usage: "Donut name",
},
},
}
var healDonutCmd = cli.Command{
Name: "heal",
Usage: "heal donut",
Description: "Heal donut with any errors",
Action: doHealDonutCmd,
}
var rebalanceDonutCmd = cli.Command{
Name: "rebalance",
Usage: "rebalance donut",
Description: "Rebalance data on donut after adding disks",
Action: doRebalanceDonutCmd,
}
var infoDonutCmd = cli.Command{
Name: "info",
Usage: "information about donut",
Description: "Pretty print donut information",
Action: doInfoDonutCmd,
}
var donutOptions = []cli.Command{
makeDonutCmd,
listDonutCmd,
attachDiskCmd,
detachDiskCmd,
healDonutCmd,
rebalanceDonutCmd,
infoDonutCmd,
}
func doHealDonutCmd(c *cli.Context) {
}

View File

@ -1,32 +0,0 @@
package main
import (
"log"
"github.com/minio-io/cli"
"github.com/minio-io/donut"
)
func doRebalanceDonutCmd(c *cli.Context) {
if !c.Args().Present() {
log.Fatalln("no args?")
}
donutName := c.Args().First()
if !isValidDonutName(donutName) {
log.Fatalln("Invalid donutName")
}
mcDonutConfigData, err := loadDonutConfig()
if err != nil {
log.Fatalln(err)
}
if _, ok := mcDonutConfigData.Donuts[donutName]; !ok {
log.Fatalln("donut does not exist")
}
d, err := donut.NewDonut(donutName, getNodeMap(mcDonutConfigData.Donuts[donutName].Node))
if err != nil {
log.Fatalln(err)
}
if err := d.Rebalance(); err != nil {
log.Fatalln(err)
}
}

View File

@ -1,33 +0,0 @@
/*
* Minimalist Object Storage, (C) 2014,2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"os"
"github.com/minio-io/cli"
)
func main() {
app := cli.NewApp()
app.Usage = ""
app.Version = gitCommitHash
app.Commands = donutOptions
app.Author = "Minio.io"
app.EnableBashCompletion = true
app.Run(os.Args)
}

View File

@ -6,19 +6,30 @@ import (
"sort"
"strconv"
"strings"
"time"
"github.com/minio-io/iodine"
)
func (d donut) MakeBucket(bucket string) error {
if bucket == "" || strings.TrimSpace(bucket) == "" {
return errors.New("invalid argument")
return iodine.New(errors.New("invalid argument"), nil)
}
return d.makeBucket(bucket)
}
func (d donut) GetBucketMetadata(bucket string) (map[string]string, error) {
return nil, errors.New("Not implemented")
err := d.getAllBuckets()
if err != nil {
return nil, iodine.New(err, nil)
}
if _, ok := d.buckets[bucket]; !ok {
return nil, iodine.New(errors.New("bucket does not exist"), nil)
}
metadata := make(map[string]string)
metadata["name"] = bucket
metadata["created"] = time.Now().Format(time.RFC3339Nano) // TODO get this, from whatever is written from SetBucketMetadata
return metadata, nil
}
func (d donut) SetBucketMetadata(bucket string, metadata map[string]string) error {

View File

@ -11,7 +11,7 @@
},
{
"ImportPath": "github.com/minio-io/donut",
"Rev": "1adb050ccbc6b56caa8b29502adf43592d16adbd"
"Rev": "9d0c663a857103c780414e15d7e426c6fca9984e"
},
{
"ImportPath": "github.com/minio-io/erasure",

View File

@ -35,6 +35,7 @@ func APITestSuite(c *check.C, create func() Driver) {
testPaging(c, create)
testObjectOverwriteFails(c, create)
testNonExistantBucketOperations(c, create)
testBucketMetadata(c, create)
testBucketRecreateFails(c, create)
testPutObjectInSubdir(c, create)
testListBuckets(c, create)
@ -43,7 +44,6 @@ func APITestSuite(c *check.C, create func() Driver) {
testNonExistantObjectInBucket(c, create)
testGetDirectoryReturnsObjectNotFound(c, create)
testDefaultContentType(c, create)
//testContentMd5Set(c, create) TODO
}
func testCreateBucket(c *check.C, create func() Driver) {
@ -225,6 +225,16 @@ func testNonExistantBucketOperations(c *check.C, create func() Driver) {
c.Assert(err, check.Not(check.IsNil))
}
func testBucketMetadata(c *check.C, create func() Driver) {
drivers := create()
err := drivers.CreateBucket("string")
c.Assert(err, check.IsNil)
metadata, err := drivers.GetBucketMetadata("string")
c.Assert(err, check.IsNil)
c.Assert(metadata.Name, check.Equals, "string")
}
func testBucketRecreateFails(c *check.C, create func() Driver) {
drivers := create()
err := drivers.CreateBucket("string")

View File

@ -119,22 +119,37 @@ func (d donutDriver) CreateBucket(bucketName string) error {
if drivers.IsValidBucket(bucketName) && !strings.Contains(bucketName, ".") {
return d.donut.MakeBucket(bucketName)
}
return errors.New("Invalid bucket")
return iodine.New(errors.New("Invalid bucket"), map[string]string{"bucket": bucketName})
}
// GetBucketMetadata retrieves an bucket's metadata
func (d donutDriver) GetBucketMetadata(bucket string) (drivers.BucketMetadata, error) {
return drivers.BucketMetadata{}, errors.New("Not Implemented")
func (d donutDriver) GetBucketMetadata(bucketName string) (drivers.BucketMetadata, error) {
if !drivers.IsValidBucket(bucketName) || strings.Contains(bucketName, ".") {
return drivers.BucketMetadata{}, drivers.BucketNameInvalid{Bucket: bucketName}
}
metadata, err := d.donut.GetBucketMetadata(bucketName)
if err != nil {
return drivers.BucketMetadata{}, drivers.BucketNotFound{Bucket: bucketName}
}
created, err := time.Parse(time.RFC3339Nano, metadata["created"])
if err != nil {
return drivers.BucketMetadata{}, iodine.New(err, nil)
}
bucketMetadata := drivers.BucketMetadata{
Name: metadata["name"],
Created: created,
}
return bucketMetadata, nil
}
// CreateBucketPolicy sets a bucket's access policy
func (d donutDriver) CreateBucketPolicy(bucket string, p drivers.BucketPolicy) error {
return errors.New("Not Implemented")
return iodine.New(errors.New("Not Implemented"), nil)
}
// GetBucketPolicy returns a bucket's access policy
func (d donutDriver) GetBucketPolicy(bucket string) (drivers.BucketPolicy, error) {
return drivers.BucketPolicy{}, errors.New("Not Implemented")
return drivers.BucketPolicy{}, iodine.New(errors.New("Not Implemented"), nil)
}
// GetObject retrieves an object and writes it to a writer

View File

@ -28,6 +28,7 @@ type Driver interface {
// Bucket Operations
ListBuckets() ([]BucketMetadata, error)
CreateBucket(bucket string) error
GetBucketMetadata(bucket string) (BucketMetadata, error)
CreateBucketPolicy(bucket string, p BucketPolicy) error
GetBucketPolicy(bucket string) (BucketPolicy, error)

View File

@ -30,6 +30,19 @@ import (
/// Bucket Operations
// GetBucketMetadata - head
func (file *fileDriver) GetBucketMetadata(bucket string) (drivers.BucketMetadata, error) {
st, err := os.Stat(path.Join(file.root, bucket))
if err != nil {
return drivers.BucketMetadata{}, drivers.BucketNotFound{Bucket: bucket}
}
bucketMetadata := drivers.BucketMetadata{
Name: st.Name(),
Created: st.ModTime(),
}
return bucketMetadata, nil
}
// ListBuckets - Get service
func (file *fileDriver) ListBuckets() ([]drivers.BucketMetadata, error) {
files, err := ioutil.ReadDir(file.root)

View File

@ -96,6 +96,14 @@ func (memory memoryDriver) GetPartialObject(w io.Writer, bucket, object string,
return io.CopyN(w, &sourceBuffer, length)
}
// GetBucketMetadata -
func (memory memoryDriver) GetBucketMetadata(bucket string) (drivers.BucketMetadata, error) {
if _, ok := memory.bucketdata[bucket]; ok == false {
return drivers.BucketMetadata{}, drivers.BucketNotFound{Bucket: bucket}
}
return memory.bucketdata[bucket].metadata, nil
}
// CreateBucketPolicy - Not implemented
func (memory memoryDriver) CreateBucketPolicy(bucket string, policy drivers.BucketPolicy) error {
return drivers.APINotImplemented{API: "PutBucketPolicy"}

View File

@ -35,6 +35,15 @@ func (m *Driver) CreateBucket(bucket string) error {
return r0
}
// GetBucketMetadata is a mock
func (m *Driver) GetBucketMetadata(bucket string) (drivers.BucketMetadata, error) {
ret := m.Called(bucket)
r0 := ret.Get(0).(drivers.BucketMetadata)
r1 := ret.Error(1)
return r0, r1
}
// CreateBucketPolicy is a mock
func (m *Driver) CreateBucketPolicy(bucket string, p drivers.BucketPolicy) error {
ret := m.Called(bucket, p)
@ -71,7 +80,6 @@ func (m *Driver) GetObject(w io.Writer, bucket string, object string) (int64, er
r0 = n
}
}
return r0, r1
}

View File

@ -150,3 +150,39 @@ func (server *minioAPI) putBucketHandler(w http.ResponseWriter, req *http.Reques
}
}
}
// HEAD Bucket
// ----------
// This operation is useful to determine if a bucket exists.
// The operation returns a 200 OK if the bucket exists and you
// have permission to access it. Otherwise, the operation might
// return responses such as 404 Not Found and 403 Forbidden.
func (server *minioAPI) headBucketHandler(w http.ResponseWriter, req *http.Request) {
// TODO need to peek into bucketPolicy return appropriate checks here
vars := mux.Vars(req)
bucket := vars["bucket"]
acceptsContentType := getContentType(req)
_, err := server.driver.GetBucketMetadata(bucket)
switch err.(type) {
case nil:
{
w.Header().Set("Server", "Minio")
w.Header().Set("Connection", "close")
w.WriteHeader(http.StatusOK)
}
case drivers.BucketNameInvalid:
{
writeErrorResponse(w, req, InvalidBucketName, acceptsContentType, req.URL.Path)
}
case drivers.BucketNotFound:
{
writeErrorResponse(w, req, NoSuchBucket, acceptsContentType, req.URL.Path)
}
default:
{
log.Error.Println(iodine.New(err, nil))
writeErrorResponse(w, req, InternalError, acceptsContentType, req.URL.Path)
}
}
}

View File

@ -37,6 +37,7 @@ func pathMux(api minioAPI, mux *router.Router) *router.Router {
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT")
mux.HandleFunc("/{bucket}", api.headBucketHandler).Methods("HEAD")
mux.HandleFunc("/{bucket}/{object:.*}", api.getObjectHandler).Methods("GET")
mux.HandleFunc("/{bucket}/{object:.*}", api.headObjectHandler).Methods("HEAD")
mux.HandleFunc("/{bucket}/{object:.*}", api.putObjectHandler).Methods("PUT")
@ -56,6 +57,7 @@ func domainMux(api minioAPI, mux *router.Router) *router.Router {
api.putObjectHandler).Host("{bucket}" + "." + api.domain).Methods("PUT")
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", api.putBucketHandler).Methods("PUT")
mux.HandleFunc("/{bucket}", api.headBucketHandler).Methods("HEAD")
return mux
}

View File

@ -32,13 +32,13 @@ import (
"net/http"
"net/http/httptest"
"github.com/fkautz/testify/mock"
"github.com/minio-io/minio/pkg/api"
"github.com/minio-io/objectdriver"
"github.com/minio-io/objectdriver/donut"
"github.com/minio-io/objectdriver/file"
"github.com/minio-io/objectdriver/memory"
"github.com/minio-io/objectdriver/mocks"
"github.com/stretchr/testify/mock"
. "github.com/minio-io/check"
)
@ -177,6 +177,33 @@ func (s *MySuite) TestEmptyObject(c *C) {
verifyHeaders(c, response.Header, resMetadata.Created, 0, "application/octet-stream", resMetadata.Md5)
}
func (s *MySuite) TestBucket(c *C) {
switch driver := s.Driver.(type) {
case *mocks.Driver:
{
driver.AssertExpectations(c)
}
}
driver := s.Driver
typedDriver := s.MockDriver
metadata := drivers.BucketMetadata{
Name: "bucket",
Created: time.Now(),
}
typedDriver.On("CreateBucket", "bucket").Return(nil).Once()
typedDriver.On("GetBucketMetadata", "bucket").Return(metadata, nil).Twice()
httpHandler := api.HTTPHandler("", driver)
testServer := httptest.NewServer(httpHandler)
defer testServer.Close()
driver.CreateBucket("bucket")
response, err := http.Head(testServer.URL + "/bucket")
c.Assert(err, IsNil)
c.Assert(response.StatusCode, Equals, http.StatusOK)
}
func (s *MySuite) TestObject(c *C) {
switch driver := s.Driver.(type) {
case *mocks.Driver: