Merge pull request #896 from harshavardhana/get-bucket-acl

Implement GetBucketACL - fixes #893
master
Harshavardhana 9 years ago
commit 312af12fd5
  1. 6
      pkg/tasker/commands.go
  2. 35
      pkg/tasker/task.go
  3. 23
      pkg/tasker/taskctl.go
  4. 45
      server-api-bucket-handlers.go
  5. 21
      server-api-definitions.go
  6. 46
      server-api-response.go
  7. 6
      server-api-signature-handler.go
  8. 1
      server-router.go

@ -23,7 +23,8 @@ type Command uint8
const (
// CmdNOOP does nothing. It is a default placeholder. Uninitialized variable of this type will point to NOOP command by default.
CmdNOOP Command = iota
// CmdSignalEnd gracefully ends current task. Never ending tasks (loop over) or Batched jobs will not take the next iteration, but may finish the current state to completion.
// CmdSignalEnd gracefully ends current task. Never ending tasks (loop over) or Batched jobs will not take the next iteration,
// but may finish the current state to completion.
CmdSignalEnd
// CmdSignalAbort ends the current task at hand immediately. It may still cleanup dangling issues quickly.
CmdSignalAbort
@ -37,6 +38,7 @@ const (
CmdPriorityMedium
// CmdPriorityHigh is optimized for speed. This option is ideal for short lived tasks (like meta-data related) that are latency sensitive. Use this option wisely.
CmdPriorityHigh
// CmdPrioritySuper is an exclusive priority. All tasks with priority lower than Super (including High) are paused temporarily until this task completes. Anytime you consider using this priority level, please seek for approval.
// CmdPrioritySuper is an exclusive priority. All tasks with priority lower than Super (including High) are paused
// temporarily until this task completes. Anytime you consider using this priority level, please seek for approval.
CmdPrioritySuper
)

@ -21,20 +21,18 @@ import (
"sync"
)
/* NOTE:
Task is a private entity. It is created and managed by TaskCtl
entirely. Only TaskCtl and Handle objects are exposed outside.
*/
// NOTE: Task is a private entity. It is created and managed by TaskCtl
// entirely. Only TaskCtl and Handle objects are exposed outside.
/* taskRef is a unique reference ID to a task. It is assigned by the
TaskCtl during the creation of a task. All tasfRef variables are
named "this". */
// taskRef is a unique reference ID to a task. It is assigned by the
// TaskCtl during the creation of a task. All tasfRef variables are
// named "this".
type taskRef *list.Element
/* Task is an abstract concept built on top of Go routines and
channels. Tasks themselves are expected to co-operate and comply with
the TaskCtl commands.
*/
// Task is an abstract concept built on top of Go routines and
// channels. Tasks themselves are expected to co-operate and comply with
// the TaskCtl commands.
type task struct {
mutex *sync.Mutex
@ -46,14 +44,13 @@ type task struct {
closeCh chan taskRef // Channel to notify the TaskCtl about ending this task.
}
/* NewTask creates a new task structure and returns a handle to
it. Only the task controller has access to the task structure. The
caller routine only receives a handle to its task structure. Task
handle is like a reference to task self. Caller is expected to listen
for commands from the task controller and comply with it
co-operatively.
this: Task reference is unique identifier assigned by the TaskCtl.
name: Free form name of the task. Eg. "Late Night Disk Scrubber". */
// NewTask creates a new task structure and returns a handle to
// it. Only the task controller has access to the task structure. The
// caller routine only receives a handle to its task structure. Task
// handle is like a reference to task self. Caller is expected to listen
// for commands from the task controller and comply with it co-operatively.
// this: Task reference is unique identifier assigned by the TaskCtl.
// name: Free form name of the task. Eg. "Late Night Disk Scrubber".
func newTask(name string) task {
return task{
// this: Is set by the TaskCtl's NewTask function.

@ -29,7 +29,8 @@ type TaskCtl struct {
tasks *list.List
}
// New creates a new TaskCtl to create and control a collection of tasks. Single application can create multiple task controllers to manage different set of tasks separately.
// New creates a new TaskCtl to create and control a collection of tasks.
// Single application can create multiple task controllers to manage different set of tasks separately.
func New(name string) *TaskCtl {
return &TaskCtl{
mutex: &sync.Mutex{},
@ -37,7 +38,10 @@ func New(name string) *TaskCtl {
}
}
// NewTask creates a new task structure and returns a handle to it. Only the task controller has access to the task structure. The caller routine only receives a handle to its task structure. Task handle is like a reference to task self. Caller is expected to listen for commands from the task controller and comply with it co-operatively.
// NewTask creates a new task structure and returns a handle to it. Only the task controller
// has access to the task structure. The caller routine only receives a handle to its task structure.
// Task handle is like a reference to task self. Caller is expected to listen for commands from
// the task controller and comply with it co-operatively.
func (tc *TaskCtl) NewTask(name string) Handle {
tc.mutex.Lock()
defer tc.mutex.Unlock()
@ -45,7 +49,7 @@ func (tc *TaskCtl) NewTask(name string) Handle {
// Create a new task.
tsk := newTask(name)
//Register this task in the TaskCtl's tasklist and save the reference.
// Register this task in the TaskCtl's tasklist and save the reference.
tsk.this = tc.tasks.PushBack(tsk)
// Free task from the tasklist upon close call.
@ -79,7 +83,10 @@ func (tc *TaskCtl) Shutdown() {
wg.Add(1)
thisTask := e.Value.(task) // Make a local copy for go routine.
// End tasks in background. Flow of events from here is as follows: thisTask.handle.Close() -> tc.NewTask() -> this.task.close().
go func() { thisTask.getHandle().Close(); wg.Done() }()
go func() {
thisTask.getHandle().Close()
wg.Done()
}()
}
wg.Wait() // Wait for all tasks to end gracefully.
@ -105,10 +112,10 @@ func (tc *TaskCtl) Suspend() bool {
locTask := e.Value.(task) // Make a local copy for go routine.
locI := i // local i
// Suspend a task in background.
go func() {
go func(locI int) {
defer wg.Done()
statusAll[locI] = locTask.command(CmdSignalSuspend)
}()
}(locI)
i++
}
@ -139,10 +146,10 @@ func (tc *TaskCtl) Resume() bool {
locTask := e.Value.(task) // Make a local copy for go routine.
locI := i // local i
// Resume a task in background.
go func() {
go func(locI int) {
defer wg.Done()
statusAll[locI] = locTask.command(CmdSignalResume)
}()
}(locI)
i++
}
wg.Wait() // Wait for all tasks to resume.

@ -403,8 +403,6 @@ func (api API) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) {
if err != nil {
errorIf(err.Trace(), "PutBucketACL failed.", nil)
switch err.ToGoError().(type) {
case signv4.DoesNotMatch:
writeErrorResponse(w, req, SignatureDoesNotMatch, req.URL.Path)
case donut.BucketNameInvalid:
writeErrorResponse(w, req, InvalidBucketName, req.URL.Path)
case donut.BucketNotFound:
@ -417,6 +415,47 @@ func (api API) PutBucketACLHandler(w http.ResponseWriter, req *http.Request) {
writeSuccessResponse(w)
}
// GetBucketACLHandler - GET ACL on a Bucket
// ----------
// This operation uses acl subresource to the return the ``acl``
// of a bucket. One must have permission to access the bucket to
// know its ``acl``. This operation willl return response of 404
// if bucket not found and 403 for invalid credentials.
func (api API) GetBucketACLHandler(w http.ResponseWriter, req *http.Request) {
// Ticket master block
{
op := APIOperation{}
op.ProceedCh = make(chan struct{})
api.OP <- op
// block until Ticket master gives us a go
<-op.ProceedCh
}
vars := mux.Vars(req)
bucket := vars["bucket"]
bucketMetadata, err := api.Donut.GetBucketMetadata(bucket)
if err != nil {
errorIf(err.Trace(), "GetBucketMetadata failed.", nil)
switch err.ToGoError().(type) {
case donut.BucketNotFound:
writeErrorResponse(w, req, NoSuchBucket, req.URL.Path)
case donut.BucketNameInvalid:
writeErrorResponse(w, req, InvalidBucketName, req.URL.Path)
default:
writeErrorResponse(w, req, InternalError, req.URL.Path)
}
return
}
// generate response
response := generateAccessControlPolicyResponse(bucketMetadata.ACL)
encodedSuccessResponse := encodeSuccessResponse(response)
// write headers
setCommonHeaders(w, len(encodedSuccessResponse))
// write body
w.Write(encodedSuccessResponse)
}
// HeadBucketHandler - HEAD Bucket
// ----------
// This operation is useful to determine if a bucket exists.
@ -440,8 +479,6 @@ func (api API) HeadBucketHandler(w http.ResponseWriter, req *http.Request) {
if err != nil {
errorIf(err.Trace(), "GetBucketMetadata failed.", nil)
switch err.ToGoError().(type) {
case signv4.DoesNotMatch:
writeErrorResponse(w, req, SignatureDoesNotMatch, req.URL.Path)
case donut.BucketNotFound:
writeErrorResponse(w, req, NoSuchBucket, req.URL.Path)
case donut.BucketNameInvalid:

@ -23,6 +23,26 @@ const (
maxObjectList = 1000
)
// AccessControlPolicyResponse - format for get bucket acl response
type AccessControlPolicyResponse struct {
AccessControlList struct {
Grant []Grant
}
Owner Owner
}
// Grant container for grantee and permission
type Grant struct {
Grantee struct {
ID string
DisplayName string
EmailAddress string
Type string
URI string
}
Permission string
}
// ListObjectsResponse - format for list objects response
type ListObjectsResponse struct {
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"`
@ -182,6 +202,7 @@ var notimplementedBucketResourceNames = map[string]bool{
"location": true,
"logging": true,
"notification": true,
"replication": true,
"tagging": true,
"versions": true,
"requestPayment": true,

@ -54,14 +54,44 @@ func generateListBucketsResponse(buckets []donut.BucketMetadata) ListBucketsResp
return data
}
// 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
// generates an AccessControlPolicy response for the said ACL.
func generateAccessControlPolicyResponse(acl donut.BucketACL) AccessControlPolicyResponse {
accessCtrlPolicyResponse := AccessControlPolicyResponse{}
accessCtrlPolicyResponse.Owner = Owner{
ID: "minio",
DisplayName: "minio",
}
defaultGrant := Grant{}
defaultGrant.Grantee.ID = "minio"
defaultGrant.Grantee.DisplayName = "minio"
defaultGrant.Permission = "FULL_CONTROL"
accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, defaultGrant)
switch {
case acl.IsPublicRead():
publicReadGrant := Grant{}
publicReadGrant.Grantee.ID = "minio"
publicReadGrant.Grantee.DisplayName = "minio"
publicReadGrant.Grantee.URI = "http://acs.amazonaws.com/groups/global/AllUsers"
publicReadGrant.Permission = "READ"
accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, publicReadGrant)
case acl.IsPublicReadWrite():
publicReadGrant := Grant{}
publicReadGrant.Grantee.ID = "minio"
publicReadGrant.Grantee.DisplayName = "minio"
publicReadGrant.Grantee.URI = "http://acs.amazonaws.com/groups/global/AllUsers"
publicReadGrant.Permission = "READ"
publicReadWriteGrant := Grant{}
publicReadWriteGrant.Grantee.ID = "minio"
publicReadWriteGrant.Grantee.DisplayName = "minio"
publicReadWriteGrant.Grantee.URI = "http://acs.amazonaws.com/groups/global/AllUsers"
publicReadWriteGrant.Permission = "WRITE"
accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, publicReadGrant)
accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, publicReadWriteGrant)
}
return accessCtrlPolicyResponse
}
// generates an ListObjects response for the said bucket with other enumerated options.
func generateListObjectsResponse(bucket string, objects []donut.ObjectMetadata, bucketResources donut.BucketResourcesMetadata) ListObjectsResponse {
var contents []*Object
var prefixes []*CommonPrefix

@ -51,9 +51,9 @@ func isRequestPresignedSignatureV4(req *http.Request) bool {
func (s signatureHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var signature *signv4.Signature
if isRequestSignatureV4(r) {
// If the request is not a PUT method handle the verification here.
// For PUT and POST requests with payload, send the call upwards for verification
if r.Method != "PUT" && r.Method != "POST" {
// For PUT and POST requests with payload, send the call upwards for verification.
// Or PUT and POST requests without payload, verify here.
if (r.Body == nil && (r.Method == "PUT" || r.Method == "POST")) || (r.Method != "PUT" && r.Method != "POST") {
// Init signature V4 verification
var err *probe.Error
signature, err = initSignatureV4(r)

@ -28,6 +28,7 @@ import (
// registerAPI - register all the object API handlers to their respective paths
func registerAPI(mux *router.Router, a API) {
mux.HandleFunc("/", a.ListBucketsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", a.GetBucketACLHandler).Queries("acl", "").Methods("GET")
mux.HandleFunc("/{bucket}", a.ListObjectsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", a.PutBucketACLHandler).Queries("acl", "").Methods("PUT")
mux.HandleFunc("/{bucket}", a.PutBucketHandler).Methods("PUT")

Loading…
Cancel
Save