mirror of
https://github.com/minio/minio.git
synced 2025-04-26 13:02:24 -04:00
event: Enhance event message struct to provide origin server. (#3557)
`principalId` i.e user identity is kept as AccessKey in accordance with S3 spec. Additionally responseElements{} are added starting with `x-amz-request-id` is a hexadecimal of the event time itself in nanosecs. `x-minio-origin-server` - points to the server generating the event. Fixes #3556
This commit is contained in:
parent
0563a9235a
commit
b0cfceb211
@ -20,6 +20,11 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Response request id.
|
||||||
|
responseRequestIDKey = "x-amz-request-id"
|
||||||
|
)
|
||||||
|
|
||||||
// ObjectIdentifier carries key name for the object to delete.
|
// ObjectIdentifier carries key name for the object to delete.
|
||||||
type ObjectIdentifier struct {
|
type ObjectIdentifier struct {
|
||||||
ObjectName string `xml:"Key"`
|
ObjectName string `xml:"Key"`
|
||||||
|
@ -18,33 +18,24 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const requestIDLen = 16
|
// Returns a hexadecimal representation of time at the
|
||||||
|
// time response is sent to the client.
|
||||||
// mustGetRequestID generates and returns request ID string.
|
func mustGetRequestID(t time.Time) string {
|
||||||
func mustGetRequestID() string {
|
return fmt.Sprintf("%X", t.UnixNano())
|
||||||
reqBytes := make([]byte, requestIDLen)
|
|
||||||
if _, err := rand.Read(reqBytes); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < requestIDLen; i++ {
|
|
||||||
reqBytes[i] = alphaNumericTable[reqBytes[i]%alphaNumericTableLen]
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(reqBytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write http common headers
|
// Write http common headers
|
||||||
func setCommonHeaders(w http.ResponseWriter) {
|
func setCommonHeaders(w http.ResponseWriter) {
|
||||||
// Set unique request ID for each reply.
|
// Set unique request ID for each reply.
|
||||||
w.Header().Set("X-Amz-Request-Id", mustGetRequestID())
|
w.Header().Set(responseRequestIDKey, mustGetRequestID(time.Now().UTC()))
|
||||||
w.Header().Set("Server", ("Minio/" + ReleaseTag + " (" + runtime.GOOS + "; " + runtime.GOARCH + ")"))
|
w.Header().Set("Server", ("Minio/" + ReleaseTag + " (" + runtime.GOOS + "; " + runtime.GOARCH + ")"))
|
||||||
w.Header().Set("Accept-Ranges", "bytes")
|
w.Header().Set("Accept-Ranges", "bytes")
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,12 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewRequestID(t *testing.T) {
|
func TestNewRequestID(t *testing.T) {
|
||||||
// Ensure that it returns an alphanumeric result of length 16.
|
// Ensure that it returns an alphanumeric result of length 16.
|
||||||
var id = mustGetRequestID()
|
var id = mustGetRequestID(time.Now().UTC())
|
||||||
|
|
||||||
if len(id) != 16 {
|
if len(id) != 16 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
@ -56,7 +56,7 @@ func enforceBucketPolicy(bucket string, action string, reqURL *url.URL) (s3Error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct resource in 'arn:aws:s3:::examplebucket/object' format.
|
// Construct resource in 'arn:aws:s3:::examplebucket/object' format.
|
||||||
resource := AWSResourcePrefix + strings.TrimSuffix(strings.TrimPrefix(reqURL.Path, "/"), "/")
|
resource := bucketARNPrefix + strings.TrimSuffix(strings.TrimPrefix(reqURL.Path, "/"), "/")
|
||||||
|
|
||||||
// Get conditions for policy verification.
|
// Get conditions for policy verification.
|
||||||
conditionKeyMap := make(map[string]set.StringSet)
|
conditionKeyMap := make(map[string]set.StringSet)
|
||||||
|
@ -114,15 +114,11 @@ func (eventName EventName) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indentity represents the user id, this is a compliance field.
|
// Indentity represents the accessKey who caused the event.
|
||||||
type identity struct {
|
type identity struct {
|
||||||
PrincipalID string `json:"principalId"`
|
PrincipalID string `json:"principalId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultIdentity() identity {
|
|
||||||
return identity{"minio"}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notification event bucket metadata.
|
// Notification event bucket metadata.
|
||||||
type bucketMeta struct {
|
type bucketMeta struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -139,6 +135,21 @@ type objectMeta struct {
|
|||||||
Sequencer string `json:"sequencer"`
|
Sequencer string `json:"sequencer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Event schema version number defaulting to the value in S3 spec.
|
||||||
|
// ref: http://docs.aws.amazon.com/AmazonS3/latest/dev/notification-content-structure.html
|
||||||
|
eventSchemaVersion = "1.0"
|
||||||
|
|
||||||
|
// Default ID found in bucket notification configuration.
|
||||||
|
// ref: http://docs.aws.amazon.com/AmazonS3/latest/dev/notification-content-structure.html
|
||||||
|
eventConfigID = "Config"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Response element origin endpoint key.
|
||||||
|
responseOriginEndpointKey = "x-minio-origin-endpoint"
|
||||||
|
)
|
||||||
|
|
||||||
// Notification event server specific metadata.
|
// Notification event server specific metadata.
|
||||||
type eventMeta struct {
|
type eventMeta struct {
|
||||||
SchemaVersion string `json:"s3SchemaVersion"`
|
SchemaVersion string `json:"s3SchemaVersion"`
|
||||||
@ -147,6 +158,16 @@ type eventMeta struct {
|
|||||||
Object objectMeta `json:"object"`
|
Object objectMeta `json:"object"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Event source static value defaulting to the value in S3 spec.
|
||||||
|
// ref: http://docs.aws.amazon.com/AmazonS3/latest/dev/notification-content-structure.html
|
||||||
|
eventSource = "aws:s3"
|
||||||
|
|
||||||
|
// Event version number defaulting to the value in S3 spec.
|
||||||
|
// ref: http://docs.aws.amazon.com/AmazonS3/latest/dev/notification-content-structure.html
|
||||||
|
eventVersion = "2.0"
|
||||||
|
)
|
||||||
|
|
||||||
// NotificationEvent represents an Amazon an S3 bucket notification event.
|
// NotificationEvent represents an Amazon an S3 bucket notification event.
|
||||||
type NotificationEvent struct {
|
type NotificationEvent struct {
|
||||||
EventVersion string `json:"eventVersion"`
|
EventVersion string `json:"eventVersion"`
|
||||||
|
@ -40,7 +40,7 @@ func TestBucketPolicyResourceMatch(t *testing.T) {
|
|||||||
|
|
||||||
// generates resource prefix.
|
// generates resource prefix.
|
||||||
generateResource := func(bucketName, objectName string) string {
|
generateResource := func(bucketName, objectName string) string {
|
||||||
return AWSResourcePrefix + bucketName + "/" + objectName
|
return bucketARNPrefix + bucketName + "/" + objectName
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
@ -50,30 +50,30 @@ func TestBucketPolicyResourceMatch(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
// Test case 1-4.
|
// Test case 1-4.
|
||||||
// Policy with resource ending with bucket/* allows access to all objects inside the given bucket.
|
// Policy with resource ending with bucket/* allows access to all objects inside the given bucket.
|
||||||
{generateResource("minio-bucket", ""), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix, "minio-bucket"+"/*")), true},
|
{generateResource("minio-bucket", ""), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix, "minio-bucket"+"/*")), true},
|
||||||
{generateResource("minio-bucket", ""), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix, "minio-bucket"+"/*")), true},
|
{generateResource("minio-bucket", ""), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix, "minio-bucket"+"/*")), true},
|
||||||
{generateResource("minio-bucket", ""), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix, "minio-bucket"+"/*")), true},
|
{generateResource("minio-bucket", ""), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix, "minio-bucket"+"/*")), true},
|
||||||
{generateResource("minio-bucket", ""), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix, "minio-bucket"+"/*")), true},
|
{generateResource("minio-bucket", ""), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix, "minio-bucket"+"/*")), true},
|
||||||
// Test case - 5.
|
// Test case - 5.
|
||||||
// Policy with resource ending with bucket/oo* should not allow access to bucket/output.txt.
|
// Policy with resource ending with bucket/oo* should not allow access to bucket/output.txt.
|
||||||
{generateResource("minio-bucket", "output.txt"), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix, "minio-bucket"+"/oo*")), false},
|
{generateResource("minio-bucket", "output.txt"), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix, "minio-bucket"+"/oo*")), false},
|
||||||
// Test case - 6.
|
// Test case - 6.
|
||||||
// Policy with resource ending with bucket/oo* should allow access to bucket/ootput.txt.
|
// Policy with resource ending with bucket/oo* should allow access to bucket/ootput.txt.
|
||||||
{generateResource("minio-bucket", "ootput.txt"), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix, "minio-bucket"+"/oo*")), true},
|
{generateResource("minio-bucket", "ootput.txt"), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix, "minio-bucket"+"/oo*")), true},
|
||||||
// Test case - 7.
|
// Test case - 7.
|
||||||
// Policy with resource ending with bucket/oo* allows access to all sub-dirs starting with "oo" inside given bucket.
|
// Policy with resource ending with bucket/oo* allows access to all sub-dirs starting with "oo" inside given bucket.
|
||||||
{generateResource("minio-bucket", "oop-bucket/my-file"), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix, "minio-bucket"+"/oo*")), true},
|
{generateResource("minio-bucket", "oop-bucket/my-file"), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix, "minio-bucket"+"/oo*")), true},
|
||||||
// Test case - 8.
|
// Test case - 8.
|
||||||
{generateResource("minio-bucket", "Asia/India/1.pjg"), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix, "minio-bucket"+"/Asia/Japan/*")), false},
|
{generateResource("minio-bucket", "Asia/India/1.pjg"), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix, "minio-bucket"+"/Asia/Japan/*")), false},
|
||||||
// Test case - 9.
|
// Test case - 9.
|
||||||
{generateResource("minio-bucket", "Asia/India/1.pjg"), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix, "minio-bucket"+"/Asia/Japan/*")), false},
|
{generateResource("minio-bucket", "Asia/India/1.pjg"), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix, "minio-bucket"+"/Asia/Japan/*")), false},
|
||||||
// Test case - 10.
|
// Test case - 10.
|
||||||
// Proves that the name space is flat.
|
// Proves that the name space is flat.
|
||||||
{generateResource("minio-bucket", "Africa/Bihar/India/design_info.doc/Bihar"), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix,
|
{generateResource("minio-bucket", "Africa/Bihar/India/design_info.doc/Bihar"), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix,
|
||||||
"minio-bucket"+"/*/India/*/Bihar")), true},
|
"minio-bucket"+"/*/India/*/Bihar")), true},
|
||||||
// Test case - 11.
|
// Test case - 11.
|
||||||
// Proves that the name space is flat.
|
// Proves that the name space is flat.
|
||||||
{generateResource("minio-bucket", "Asia/China/India/States/Bihar/output.txt"), generateStatement(fmt.Sprintf("%s%s", AWSResourcePrefix,
|
{generateResource("minio-bucket", "Asia/China/India/States/Bihar/output.txt"), generateStatement(fmt.Sprintf("%s%s", bucketARNPrefix,
|
||||||
"minio-bucket"+"/*/India/*/Bihar/*")), true},
|
"minio-bucket"+"/*/India/*/Bihar/*")), true},
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
|
@ -29,11 +29,6 @@ import (
|
|||||||
"github.com/minio/minio-go/pkg/set"
|
"github.com/minio/minio-go/pkg/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// AWSResourcePrefix - bucket policy resource prefix.
|
|
||||||
AWSResourcePrefix = "arn:aws:s3:::"
|
|
||||||
)
|
|
||||||
|
|
||||||
// supportedActionMap - lists all the actions supported by minio.
|
// supportedActionMap - lists all the actions supported by minio.
|
||||||
var supportedActionMap = set.CreateStringSet("*", "s3:*", "s3:GetObject",
|
var supportedActionMap = set.CreateStringSet("*", "s3:*", "s3:GetObject",
|
||||||
"s3:ListBucket", "s3:PutObject", "s3:GetBucketLocation", "s3:DeleteObject",
|
"s3:ListBucket", "s3:PutObject", "s3:GetBucketLocation", "s3:DeleteObject",
|
||||||
@ -111,11 +106,11 @@ func isValidResources(resources set.StringSet) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for resource := range resources {
|
for resource := range resources {
|
||||||
if !strings.HasPrefix(resource, AWSResourcePrefix) {
|
if !strings.HasPrefix(resource, bucketARNPrefix) {
|
||||||
err = errors.New("Unsupported resource style found: ‘" + resource + "’, please validate your policy document")
|
err = errors.New("Unsupported resource style found: ‘" + resource + "’, please validate your policy document")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resourceSuffix := strings.SplitAfter(resource, AWSResourcePrefix)[1]
|
resourceSuffix := strings.SplitAfter(resource, bucketARNPrefix)[1]
|
||||||
if len(resourceSuffix) == 0 || strings.HasPrefix(resourceSuffix, "/") {
|
if len(resourceSuffix) == 0 || strings.HasPrefix(resourceSuffix, "/") {
|
||||||
err = errors.New("Invalid resource style found: ‘" + resource + "’, please validate your policy document")
|
err = errors.New("Invalid resource style found: ‘" + resource + "’, please validate your policy document")
|
||||||
return err
|
return err
|
||||||
@ -236,7 +231,7 @@ func checkBucketPolicyResources(bucket string, bucketPolicy *bucketPolicy) APIEr
|
|||||||
for _, statement := range bucketPolicy.Statements {
|
for _, statement := range bucketPolicy.Statements {
|
||||||
for action := range statement.Actions {
|
for action := range statement.Actions {
|
||||||
for resource := range statement.Resources {
|
for resource := range statement.Resources {
|
||||||
resourcePrefix := strings.SplitAfter(resource, AWSResourcePrefix)[1]
|
resourcePrefix := strings.SplitAfter(resource, bucketARNPrefix)[1]
|
||||||
if _, ok := invalidPrefixActions[action]; ok {
|
if _, ok := invalidPrefixActions[action]; ok {
|
||||||
// Resource prefix is not equal to bucket for
|
// Resource prefix is not equal to bucket for
|
||||||
// prefix invalid actions, reject them.
|
// prefix invalid actions, reject them.
|
||||||
|
@ -79,7 +79,7 @@ func getReadWriteObjectStatement(bucketName, objectPrefix string) policyStatemen
|
|||||||
objectResourceStatement.Principal = map[string]interface{}{
|
objectResourceStatement.Principal = map[string]interface{}{
|
||||||
"AWS": "*",
|
"AWS": "*",
|
||||||
}
|
}
|
||||||
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...)
|
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName+"/"+objectPrefix+"*")}...)
|
||||||
objectResourceStatement.Actions = set.CreateStringSet(readWriteObjectActions...)
|
objectResourceStatement.Actions = set.CreateStringSet(readWriteObjectActions...)
|
||||||
return objectResourceStatement
|
return objectResourceStatement
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ func getReadWriteBucketStatement(bucketName, objectPrefix string) policyStatemen
|
|||||||
bucketResourceStatement.Principal = map[string]interface{}{
|
bucketResourceStatement.Principal = map[string]interface{}{
|
||||||
"AWS": "*",
|
"AWS": "*",
|
||||||
}
|
}
|
||||||
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...)
|
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName)}...)
|
||||||
bucketResourceStatement.Actions = set.CreateStringSet(readWriteBucketActions...)
|
bucketResourceStatement.Actions = set.CreateStringSet(readWriteBucketActions...)
|
||||||
return bucketResourceStatement
|
return bucketResourceStatement
|
||||||
}
|
}
|
||||||
@ -111,7 +111,7 @@ func getReadOnlyBucketStatement(bucketName, objectPrefix string) policyStatement
|
|||||||
bucketResourceStatement.Principal = map[string]interface{}{
|
bucketResourceStatement.Principal = map[string]interface{}{
|
||||||
"AWS": "*",
|
"AWS": "*",
|
||||||
}
|
}
|
||||||
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...)
|
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName)}...)
|
||||||
bucketResourceStatement.Actions = set.CreateStringSet(readOnlyBucketActions...)
|
bucketResourceStatement.Actions = set.CreateStringSet(readOnlyBucketActions...)
|
||||||
return bucketResourceStatement
|
return bucketResourceStatement
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ func getReadOnlyObjectStatement(bucketName, objectPrefix string) policyStatement
|
|||||||
objectResourceStatement.Principal = map[string]interface{}{
|
objectResourceStatement.Principal = map[string]interface{}{
|
||||||
"AWS": "*",
|
"AWS": "*",
|
||||||
}
|
}
|
||||||
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...)
|
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName+"/"+objectPrefix+"*")}...)
|
||||||
objectResourceStatement.Actions = set.CreateStringSet(readOnlyObjectActions...)
|
objectResourceStatement.Actions = set.CreateStringSet(readOnlyObjectActions...)
|
||||||
return objectResourceStatement
|
return objectResourceStatement
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ func getWriteOnlyBucketStatement(bucketName, objectPrefix string) policyStatemen
|
|||||||
bucketResourceStatement.Principal = map[string]interface{}{
|
bucketResourceStatement.Principal = map[string]interface{}{
|
||||||
"AWS": "*",
|
"AWS": "*",
|
||||||
}
|
}
|
||||||
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName)}...)
|
bucketResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName)}...)
|
||||||
bucketResourceStatement.Actions = set.CreateStringSet(writeOnlyBucketActions...)
|
bucketResourceStatement.Actions = set.CreateStringSet(writeOnlyBucketActions...)
|
||||||
return bucketResourceStatement
|
return bucketResourceStatement
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ func getWriteOnlyObjectStatement(bucketName, objectPrefix string) policyStatemen
|
|||||||
objectResourceStatement.Principal = map[string]interface{}{
|
objectResourceStatement.Principal = map[string]interface{}{
|
||||||
"AWS": "*",
|
"AWS": "*",
|
||||||
}
|
}
|
||||||
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", AWSResourcePrefix, bucketName+"/"+objectPrefix+"*")}...)
|
objectResourceStatement.Resources = set.CreateStringSet([]string{fmt.Sprintf("%s%s", bucketARNPrefix, bucketName+"/"+objectPrefix+"*")}...)
|
||||||
objectResourceStatement.Actions = set.CreateStringSet(writeOnlyObjectActions...)
|
objectResourceStatement.Actions = set.CreateStringSet(writeOnlyObjectActions...)
|
||||||
return objectResourceStatement
|
return objectResourceStatement
|
||||||
}
|
}
|
||||||
@ -269,19 +269,19 @@ func TestIsValidResources(t *testing.T) {
|
|||||||
// Empty Resources.
|
// Empty Resources.
|
||||||
{[]string{}, errors.New("Resource list cannot be empty"), false},
|
{[]string{}, errors.New("Resource list cannot be empty"), false},
|
||||||
// Test case - 2.
|
// Test case - 2.
|
||||||
// A valid resource should have prefix "arn:aws:s3:::".
|
// A valid resource should have prefix bucketARNPrefix.
|
||||||
{[]string{"my-resource"}, errors.New("Unsupported resource style found: ‘my-resource’, please validate your policy document"), false},
|
{[]string{"my-resource"}, errors.New("Unsupported resource style found: ‘my-resource’, please validate your policy document"), false},
|
||||||
// Test case - 3.
|
// Test case - 3.
|
||||||
// A Valid resource should have bucket name followed by "arn:aws:s3:::".
|
// A Valid resource should have bucket name followed by bucketARNPrefix.
|
||||||
{[]string{"arn:aws:s3:::"}, errors.New("Invalid resource style found: ‘arn:aws:s3:::’, please validate your policy document"), false},
|
{[]string{bucketARNPrefix}, errors.New("Invalid resource style found: ‘arn:aws:s3:::’, please validate your policy document"), false},
|
||||||
// Test Case - 4.
|
// Test Case - 4.
|
||||||
// Valid resource shouldn't have slash('/') followed by "arn:aws:s3:::".
|
// Valid resource shouldn't have slash('/') followed by bucketARNPrefix.
|
||||||
{[]string{"arn:aws:s3:::/"}, errors.New("Invalid resource style found: ‘arn:aws:s3:::/’, please validate your policy document"), false},
|
{[]string{bucketARNPrefix + "/"}, errors.New("Invalid resource style found: ‘arn:aws:s3:::/’, please validate your policy document"), false},
|
||||||
|
|
||||||
// Test cases with valid Resources.
|
// Test cases with valid Resources.
|
||||||
{[]string{"arn:aws:s3:::my-bucket"}, nil, true},
|
{[]string{bucketARNPrefix + "my-bucket"}, nil, true},
|
||||||
{[]string{"arn:aws:s3:::my-bucket/Asia/*"}, nil, true},
|
{[]string{bucketARNPrefix + "my-bucket/Asia/*"}, nil, true},
|
||||||
{[]string{"arn:aws:s3:::my-bucket/Asia/India/*"}, nil, true},
|
{[]string{bucketARNPrefix + "my-bucket/Asia/India/*"}, nil, true},
|
||||||
}
|
}
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
err := isValidResources(set.CreateStringSet(testCase.resources...))
|
err := isValidResources(set.CreateStringSet(testCase.resources...))
|
||||||
|
@ -24,6 +24,12 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Static prefix to be used while constructing bucket ARN.
|
||||||
|
// refer to S3 docs for more info.
|
||||||
|
bucketARNPrefix = "arn:" + eventSource + ":::"
|
||||||
|
)
|
||||||
|
|
||||||
// Variable represents bucket policies in memory.
|
// Variable represents bucket policies in memory.
|
||||||
var globalBucketPolicies *bucketPolicies
|
var globalBucketPolicies *bucketPolicies
|
||||||
|
|
||||||
|
19
cmd/certs.go
19
cmd/certs.go
@ -145,3 +145,22 @@ func parseCertificateChain(bytes []byte) ([]*x509.Certificate, error) {
|
|||||||
}
|
}
|
||||||
return certs, nil
|
return certs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadRootCAs fetches CA files provided in minio config and adds them to globalRootCAs
|
||||||
|
// Currently under Windows, there is no way to load system + user CAs at the same time
|
||||||
|
func loadRootCAs() {
|
||||||
|
caFiles := mustGetCAFiles()
|
||||||
|
if len(caFiles) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Get system cert pool, and empty cert pool under Windows because it is not supported
|
||||||
|
globalRootCAs = mustGetSystemCertPool()
|
||||||
|
// Load custom root CAs for client requests
|
||||||
|
for _, caFile := range caFiles {
|
||||||
|
caCert, err := ioutil.ReadFile(caFile)
|
||||||
|
if err != nil {
|
||||||
|
fatalIf(err, "Unable to load a CA file")
|
||||||
|
}
|
||||||
|
globalRootCAs.AppendCertsFromPEM(caCert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -88,49 +88,76 @@ type eventData struct {
|
|||||||
// New notification event constructs a new notification event message from
|
// New notification event constructs a new notification event message from
|
||||||
// input request metadata which completed successfully.
|
// input request metadata which completed successfully.
|
||||||
func newNotificationEvent(event eventData) NotificationEvent {
|
func newNotificationEvent(event eventData) NotificationEvent {
|
||||||
/// Construct a new object created event.
|
// Fetch the region.
|
||||||
region := serverConfig.GetRegion()
|
region := serverConfig.GetRegion()
|
||||||
tnow := time.Now().UTC()
|
|
||||||
sequencer := fmt.Sprintf("%X", tnow.UnixNano())
|
// Fetch the credentials.
|
||||||
|
creds := serverConfig.GetCredential()
|
||||||
|
|
||||||
|
// Time when Minio finished processing the request.
|
||||||
|
eventTime := time.Now().UTC()
|
||||||
|
|
||||||
|
// API endpoint is captured here to be returned back
|
||||||
|
// to the client for it to differentiate from which
|
||||||
|
// server the request came from.
|
||||||
|
var apiEndpoint string
|
||||||
|
if len(globalAPIEndpoints) >= 1 {
|
||||||
|
apiEndpoint = globalAPIEndpoints[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch a hexadecimal representation of event time in nano seconds.
|
||||||
|
uniqueID := mustGetRequestID(eventTime)
|
||||||
|
|
||||||
|
/// Construct a new object created event.
|
||||||
|
|
||||||
// Following blocks fills in all the necessary details of s3
|
// Following blocks fills in all the necessary details of s3
|
||||||
// event message structure.
|
// event message structure.
|
||||||
// http://docs.aws.amazon.com/AmazonS3/latest/dev/notification-content-structure.html
|
// http://docs.aws.amazon.com/AmazonS3/latest/dev/notification-content-structure.html
|
||||||
nEvent := NotificationEvent{
|
nEvent := NotificationEvent{
|
||||||
EventVersion: "2.0",
|
EventVersion: eventVersion,
|
||||||
EventSource: "aws:s3",
|
EventSource: eventSource,
|
||||||
AwsRegion: region,
|
AwsRegion: region,
|
||||||
EventTime: tnow.Format(timeFormatAMZ),
|
EventTime: eventTime.Format(timeFormatAMZ),
|
||||||
EventName: event.Type.String(),
|
EventName: event.Type.String(),
|
||||||
UserIdentity: defaultIdentity(),
|
UserIdentity: identity{creds.AccessKey},
|
||||||
RequestParameters: event.ReqParams,
|
RequestParameters: event.ReqParams,
|
||||||
ResponseElements: map[string]string{},
|
ResponseElements: map[string]string{
|
||||||
|
responseRequestIDKey: uniqueID,
|
||||||
|
// Following is a custom response element to indicate
|
||||||
|
// event origin server endpoint.
|
||||||
|
responseOriginEndpointKey: apiEndpoint,
|
||||||
|
},
|
||||||
S3: eventMeta{
|
S3: eventMeta{
|
||||||
SchemaVersion: "1.0",
|
SchemaVersion: eventSchemaVersion,
|
||||||
ConfigurationID: "Config",
|
ConfigurationID: eventConfigID,
|
||||||
Bucket: bucketMeta{
|
Bucket: bucketMeta{
|
||||||
Name: event.Bucket,
|
Name: event.Bucket,
|
||||||
OwnerIdentity: defaultIdentity(),
|
OwnerIdentity: identity{creds.AccessKey},
|
||||||
ARN: "arn:aws:s3:::" + event.Bucket,
|
ARN: bucketARNPrefix + event.Bucket,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Escape the object name. For example "red flower.jpg" becomes "red+flower.jpg".
|
||||||
escapedObj := url.QueryEscape(event.ObjInfo.Name)
|
escapedObj := url.QueryEscape(event.ObjInfo.Name)
|
||||||
|
|
||||||
// For delete object event type, we do not need to set ETag and Size.
|
// For delete object event type, we do not need to set ETag and Size.
|
||||||
if event.Type == ObjectRemovedDelete {
|
if event.Type == ObjectRemovedDelete {
|
||||||
nEvent.S3.Object = objectMeta{
|
nEvent.S3.Object = objectMeta{
|
||||||
Key: escapedObj,
|
Key: escapedObj,
|
||||||
Sequencer: sequencer,
|
Sequencer: uniqueID,
|
||||||
}
|
}
|
||||||
return nEvent
|
return nEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all other events we should set ETag and Size.
|
// For all other events we should set ETag and Size.
|
||||||
nEvent.S3.Object = objectMeta{
|
nEvent.S3.Object = objectMeta{
|
||||||
Key: escapedObj,
|
Key: escapedObj,
|
||||||
ETag: event.ObjInfo.MD5Sum,
|
ETag: event.ObjInfo.MD5Sum,
|
||||||
Size: event.ObjInfo.Size,
|
Size: event.ObjInfo.Size,
|
||||||
Sequencer: sequencer,
|
Sequencer: uniqueID,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success.
|
// Success.
|
||||||
return nEvent
|
return nEvent
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@ var (
|
|||||||
|
|
||||||
// Cache expiry.
|
// Cache expiry.
|
||||||
globalCacheExpiry = objcache.DefaultExpiry
|
globalCacheExpiry = objcache.DefaultExpiry
|
||||||
|
|
||||||
// Minio local server address (in `host:port` format)
|
// Minio local server address (in `host:port` format)
|
||||||
globalMinioAddr = ""
|
globalMinioAddr = ""
|
||||||
// Minio default port, can be changed through command line.
|
// Minio default port, can be changed through command line.
|
||||||
@ -80,6 +81,9 @@ var (
|
|||||||
// Holds the host that was passed using --address
|
// Holds the host that was passed using --address
|
||||||
globalMinioHost = ""
|
globalMinioHost = ""
|
||||||
|
|
||||||
|
// Holds the list of API endpoints for a given server.
|
||||||
|
globalAPIEndpoints = []string{}
|
||||||
|
|
||||||
// Peer communication struct
|
// Peer communication struct
|
||||||
globalS3Peers = s3Peers{}
|
globalS3Peers = s3Peers{}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -225,5 +226,13 @@ func (n *nsLockMap) deleteLockInfoEntryForOps(param nsParam, opsID string) error
|
|||||||
|
|
||||||
// Return randomly generated string ID
|
// Return randomly generated string ID
|
||||||
func getOpsID() string {
|
func getOpsID() string {
|
||||||
return mustGetRequestID()
|
const opsIDLen = 16
|
||||||
|
opsIDBytes := make([]byte, opsIDLen)
|
||||||
|
if _, err := rand.Read(opsIDBytes); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for i := 0; i < opsIDLen; i++ {
|
||||||
|
opsIDBytes[i] = alphaNumericTable[opsIDBytes[i]%alphaNumericTableLen]
|
||||||
|
}
|
||||||
|
return string(opsIDBytes)
|
||||||
}
|
}
|
||||||
|
@ -228,6 +228,25 @@ func verifyLockState(l lockStateCase, t *testing.T, testNum int) {
|
|||||||
verifyLockStats(l, t, testNum)
|
verifyLockStats(l, t, testNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetOpsID(t *testing.T) {
|
||||||
|
// Ensure that it returns an alphanumeric result of length 16.
|
||||||
|
var id = getOpsID()
|
||||||
|
|
||||||
|
if len(id) != 16 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
var e rune
|
||||||
|
for _, char := range id {
|
||||||
|
e = rune(char)
|
||||||
|
|
||||||
|
// Ensure that it is alphanumeric, in this case, between 0-9 and A-Z.
|
||||||
|
if !(('0' <= e && e <= '9') || ('A' <= e && e <= 'Z')) {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestNewDebugLockInfoPerVolumePath - Validates the values initialized by newDebugLockInfoPerVolumePath().
|
// TestNewDebugLockInfoPerVolumePath - Validates the values initialized by newDebugLockInfoPerVolumePath().
|
||||||
func TestNewDebugLockInfoPerVolumePath(t *testing.T) {
|
func TestNewDebugLockInfoPerVolumePath(t *testing.T) {
|
||||||
lockInfo := &debugLockInfoPerVolumePath{
|
lockInfo := &debugLockInfoPerVolumePath{
|
||||||
|
@ -18,9 +18,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@ -129,69 +127,6 @@ func parseStorageEndpoints(eps []string) (endpoints []*url.URL, err error) {
|
|||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getListenIPs - gets all the ips to listen on.
|
|
||||||
func getListenIPs(serverAddr string) (hosts []string, port string, err error) {
|
|
||||||
var host string
|
|
||||||
host, port, err = net.SplitHostPort(serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, port, fmt.Errorf("Unable to parse host address %s", err)
|
|
||||||
}
|
|
||||||
if host == "" {
|
|
||||||
var ipv4s []net.IP
|
|
||||||
ipv4s, err = getInterfaceIPv4s()
|
|
||||||
if err != nil {
|
|
||||||
return nil, port, fmt.Errorf("Unable reverse sorted ips from hosts %s", err)
|
|
||||||
}
|
|
||||||
for _, ip := range ipv4s {
|
|
||||||
hosts = append(hosts, ip.String())
|
|
||||||
}
|
|
||||||
return hosts, port, nil
|
|
||||||
} // if host != "" {
|
|
||||||
// Proceed to append itself, since user requested a specific endpoint.
|
|
||||||
hosts = append(hosts, host)
|
|
||||||
return hosts, port, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finalizes the endpoints based on the host list and port.
|
|
||||||
func finalizeEndpoints(tls bool, apiServer *http.Server) (endPoints []string) {
|
|
||||||
// Verify current scheme.
|
|
||||||
scheme := "http"
|
|
||||||
if tls {
|
|
||||||
scheme = "https"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get list of listen ips and port.
|
|
||||||
hosts, port, err := getListenIPs(apiServer.Addr)
|
|
||||||
fatalIf(err, "Unable to get list of ips to listen on")
|
|
||||||
|
|
||||||
// Construct proper endpoints.
|
|
||||||
for _, host := range hosts {
|
|
||||||
endPoints = append(endPoints, fmt.Sprintf("%s://%s:%s", scheme, host, port))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Success.
|
|
||||||
return endPoints
|
|
||||||
}
|
|
||||||
|
|
||||||
// loadRootCAs fetches CA files provided in minio config and adds them to globalRootCAs
|
|
||||||
// Currently under Windows, there is no way to load system + user CAs at the same time
|
|
||||||
func loadRootCAs() {
|
|
||||||
caFiles := mustGetCAFiles()
|
|
||||||
if len(caFiles) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Get system cert pool, and empty cert pool under Windows because it is not supported
|
|
||||||
globalRootCAs = mustGetSystemCertPool()
|
|
||||||
// Load custom root CAs for client requests
|
|
||||||
for _, caFile := range mustGetCAFiles() {
|
|
||||||
caCert, err := ioutil.ReadFile(caFile)
|
|
||||||
if err != nil {
|
|
||||||
fatalIf(err, "Unable to load a CA file")
|
|
||||||
}
|
|
||||||
globalRootCAs.AppendCertsFromPEM(caCert)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// initServerConfig initialize server config.
|
// initServerConfig initialize server config.
|
||||||
func initServerConfig(c *cli.Context) {
|
func initServerConfig(c *cli.Context) {
|
||||||
// Create certs path.
|
// Create certs path.
|
||||||
@ -430,6 +365,9 @@ func serverMain(c *cli.Context) {
|
|||||||
fatalIf(errInvalidArgument, "None of the disks passed as command line args are local to this server.")
|
fatalIf(errInvalidArgument, "None of the disks passed as command line args are local to this server.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is TLS configured?.
|
||||||
|
tls := isSSL()
|
||||||
|
|
||||||
// Sort endpoints for consistent ordering across multiple
|
// Sort endpoints for consistent ordering across multiple
|
||||||
// nodes in a distributed setup. This is to avoid format.json
|
// nodes in a distributed setup. This is to avoid format.json
|
||||||
// corruption if the disks aren't supplied in the same order
|
// corruption if the disks aren't supplied in the same order
|
||||||
@ -473,15 +411,15 @@ func serverMain(c *cli.Context) {
|
|||||||
// Initialize a new HTTP server.
|
// Initialize a new HTTP server.
|
||||||
apiServer := NewServerMux(serverAddr, handler)
|
apiServer := NewServerMux(serverAddr, handler)
|
||||||
|
|
||||||
// If https.
|
// Set the global minio addr for this server.
|
||||||
tls := isSSL()
|
|
||||||
|
|
||||||
// Fetch endpoints which we are going to serve from.
|
|
||||||
endPoints := finalizeEndpoints(tls, apiServer.Server)
|
|
||||||
|
|
||||||
// Initialize local server address
|
|
||||||
globalMinioAddr = getLocalAddress(srvConfig)
|
globalMinioAddr = getLocalAddress(srvConfig)
|
||||||
|
|
||||||
|
// Determine API endpoints where we are going to serve the S3 API from.
|
||||||
|
apiEndPoints := finalizeAPIEndpoints(tls, apiServer.Server)
|
||||||
|
|
||||||
|
// Set the global API endpoints value.
|
||||||
|
globalAPIEndpoints = apiEndPoints
|
||||||
|
|
||||||
// Initialize S3 Peers inter-node communication
|
// Initialize S3 Peers inter-node communication
|
||||||
initGlobalS3Peers(endpoints)
|
initGlobalS3Peers(endpoints)
|
||||||
|
|
||||||
@ -512,7 +450,7 @@ func serverMain(c *cli.Context) {
|
|||||||
globalObjLayerMutex.Unlock()
|
globalObjLayerMutex.Unlock()
|
||||||
|
|
||||||
// Prints the formatted startup message once object layer is initialized.
|
// Prints the formatted startup message once object layer is initialized.
|
||||||
printStartupMessage(endPoints)
|
printStartupMessage(apiEndPoints)
|
||||||
|
|
||||||
// Waits on the server.
|
// Waits on the server.
|
||||||
<-globalServiceDoneCh
|
<-globalServiceDoneCh
|
||||||
|
@ -63,7 +63,7 @@ func TestGetListenIPs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFinalizeEndpoints(t *testing.T) {
|
func TestFinalizeAPIEndpoints(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
tls bool
|
tls bool
|
||||||
addr string
|
addr string
|
||||||
@ -75,7 +75,7 @@ func TestFinalizeEndpoints(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
endPoints := finalizeEndpoints(test.tls, &http.Server{Addr: test.addr})
|
endPoints := finalizeAPIEndpoints(test.tls, &http.Server{Addr: test.addr})
|
||||||
if len(endPoints) <= 0 {
|
if len(endPoints) <= 0 {
|
||||||
t.Errorf("Test case %d returned with no API end points for %s",
|
t.Errorf("Test case %d returned with no API end points for %s",
|
||||||
i+1, test.addr)
|
i+1, test.addr)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
* Minio Cloud Storage, (C) 2016, 2017 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.
|
||||||
@ -43,20 +43,30 @@ func getFormatStr(strLen int, padding int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prints the formatted startup message.
|
// Prints the formatted startup message.
|
||||||
func printStartupMessage(endPoints []string) {
|
func printStartupMessage(apiEndPoints []string) {
|
||||||
// If quiet flag is set do not print startup message.
|
// If quiet flag is set do not print startup message.
|
||||||
if globalQuiet {
|
if globalQuiet {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
printServerCommonMsg(endPoints)
|
|
||||||
printCLIAccessMsg(endPoints[0])
|
// Prints credential, region and browser access.
|
||||||
|
printServerCommonMsg(apiEndPoints)
|
||||||
|
|
||||||
|
// Prints `mc` cli configuration message chooses
|
||||||
|
// first endpoint as default.
|
||||||
|
printCLIAccessMsg(apiEndPoints[0])
|
||||||
|
|
||||||
|
// Prints documentation message.
|
||||||
printObjectAPIMsg()
|
printObjectAPIMsg()
|
||||||
|
|
||||||
|
// Object layer is initialized then print StorageInfo.
|
||||||
objAPI := newObjectLayerFn()
|
objAPI := newObjectLayerFn()
|
||||||
if objAPI != nil {
|
if objAPI != nil {
|
||||||
printStorageInfo(objAPI.StorageInfo())
|
printStorageInfo(objAPI.StorageInfo())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSL is configured reads certification chain, prints
|
||||||
|
// authority and expiry.
|
||||||
if isSSL() {
|
if isSSL() {
|
||||||
certs, err := readCertificateChain()
|
certs, err := readCertificateChain()
|
||||||
fatalIf(err, "Unable to read certificate chain.")
|
fatalIf(err, "Unable to read certificate chain.")
|
||||||
@ -65,23 +75,23 @@ func printStartupMessage(endPoints []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prints common server startup message. Prints credential, region and browser access.
|
// Prints common server startup message. Prints credential, region and browser access.
|
||||||
func printServerCommonMsg(endPoints []string) {
|
func printServerCommonMsg(apiEndpoints []string) {
|
||||||
// Get saved credentials.
|
// Get saved credentials.
|
||||||
cred := serverConfig.GetCredential()
|
cred := serverConfig.GetCredential()
|
||||||
|
|
||||||
// Get saved region.
|
// Get saved region.
|
||||||
region := serverConfig.GetRegion()
|
region := serverConfig.GetRegion()
|
||||||
|
|
||||||
endPointStr := strings.Join(endPoints, " ")
|
apiEndpointStr := strings.Join(apiEndpoints, " ")
|
||||||
// Colorize the message and print.
|
// Colorize the message and print.
|
||||||
console.Println(colorBlue("\nEndpoint: ") + colorBold(fmt.Sprintf(getFormatStr(len(endPointStr), 1), endPointStr)))
|
console.Println(colorBlue("\nEndpoint: ") + colorBold(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 1), apiEndpointStr)))
|
||||||
console.Println(colorBlue("AccessKey: ") + colorBold(fmt.Sprintf("%s ", cred.AccessKey)))
|
console.Println(colorBlue("AccessKey: ") + colorBold(fmt.Sprintf("%s ", cred.AccessKey)))
|
||||||
console.Println(colorBlue("SecretKey: ") + colorBold(fmt.Sprintf("%s ", cred.SecretKey)))
|
console.Println(colorBlue("SecretKey: ") + colorBold(fmt.Sprintf("%s ", cred.SecretKey)))
|
||||||
console.Println(colorBlue("Region: ") + colorBold(fmt.Sprintf(getFormatStr(len(region), 3), region)))
|
console.Println(colorBlue("Region: ") + colorBold(fmt.Sprintf(getFormatStr(len(region), 3), region)))
|
||||||
printEventNotifiers()
|
printEventNotifiers()
|
||||||
|
|
||||||
console.Println(colorBlue("\nBrowser Access:"))
|
console.Println(colorBlue("\nBrowser Access:"))
|
||||||
console.Println(fmt.Sprintf(getFormatStr(len(endPointStr), 3), endPointStr))
|
console.Println(fmt.Sprintf(getFormatStr(len(apiEndpointStr), 3), apiEndpointStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints bucket notification configurations.
|
// Prints bucket notification configurations.
|
||||||
|
@ -94,3 +94,39 @@ func TestCertificateNotExpired(t *testing.T) {
|
|||||||
t.Fatalf("Expected empty message was: %s", msg)
|
t.Fatalf("Expected empty message was: %s", msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test printing server common message.
|
||||||
|
func TestPrintServerCommonMessage(t *testing.T) {
|
||||||
|
root, err := newTestConfig("us-east-1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer removeAll(root)
|
||||||
|
|
||||||
|
apiEndpoints := []string{"127.0.0.1:9000"}
|
||||||
|
printServerCommonMsg(apiEndpoints)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests print cli access message.
|
||||||
|
func TestPrintCLIAccessMsg(t *testing.T) {
|
||||||
|
root, err := newTestConfig("us-east-1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer removeAll(root)
|
||||||
|
|
||||||
|
apiEndpoints := []string{"127.0.0.1:9000"}
|
||||||
|
printCLIAccessMsg(apiEndpoints[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test print startup message.
|
||||||
|
func TestPrintStartupMessage(t *testing.T) {
|
||||||
|
root, err := newTestConfig("us-east-1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer removeAll(root)
|
||||||
|
|
||||||
|
apiEndpoints := []string{"127.0.0.1:9000"}
|
||||||
|
printStartupMessage(apiEndpoints)
|
||||||
|
}
|
||||||
|
67
cmd/server-startup-utils.go
Normal file
67
cmd/server-startup-utils.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2016, 2017 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 cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getListenIPs - gets all the ips to listen on.
|
||||||
|
func getListenIPs(serverAddr string) (hosts []string, port string, err error) {
|
||||||
|
var host string
|
||||||
|
host, port, err = net.SplitHostPort(serverAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, port, fmt.Errorf("Unable to parse host address %s", err)
|
||||||
|
}
|
||||||
|
if host == "" {
|
||||||
|
var ipv4s []net.IP
|
||||||
|
ipv4s, err = getInterfaceIPv4s()
|
||||||
|
if err != nil {
|
||||||
|
return nil, port, fmt.Errorf("Unable reverse sort ips from hosts %s", err)
|
||||||
|
}
|
||||||
|
for _, ip := range ipv4s {
|
||||||
|
hosts = append(hosts, ip.String())
|
||||||
|
}
|
||||||
|
return hosts, port, nil
|
||||||
|
} // if host != "" {
|
||||||
|
// Proceed to append itself, since user requested a specific endpoint.
|
||||||
|
hosts = append(hosts, host)
|
||||||
|
return hosts, port, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finalizes the API endpoints based on the host list and port.
|
||||||
|
func finalizeAPIEndpoints(tls bool, apiServer *http.Server) (endPoints []string) {
|
||||||
|
// Verify current scheme.
|
||||||
|
scheme := "http"
|
||||||
|
if tls {
|
||||||
|
scheme = "https"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get list of listen ips and port.
|
||||||
|
hosts, port, err := getListenIPs(apiServer.Addr)
|
||||||
|
fatalIf(err, "Unable to get list of ips to listen on")
|
||||||
|
|
||||||
|
// Construct proper endpoints.
|
||||||
|
for _, host := range hosts {
|
||||||
|
endPoints = append(endPoints, fmt.Sprintf("%s://%s:%s", scheme, host, port))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success.
|
||||||
|
return endPoints
|
||||||
|
}
|
@ -948,14 +948,14 @@ func testWebGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
|
|||||||
Actions: set.CreateStringSet("s3:GetBucketLocation", "s3:ListBucket"),
|
Actions: set.CreateStringSet("s3:GetBucketLocation", "s3:ListBucket"),
|
||||||
Effect: "Allow",
|
Effect: "Allow",
|
||||||
Principal: map[string][]string{"AWS": {"*"}},
|
Principal: map[string][]string{"AWS": {"*"}},
|
||||||
Resources: set.CreateStringSet("arn:aws:s3:::" + bucketName),
|
Resources: set.CreateStringSet(bucketARNPrefix + bucketName),
|
||||||
Sid: "",
|
Sid: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Actions: set.CreateStringSet("s3:GetObject"),
|
Actions: set.CreateStringSet("s3:GetObject"),
|
||||||
Effect: "Allow",
|
Effect: "Allow",
|
||||||
Principal: map[string][]string{"AWS": {"*"}},
|
Principal: map[string][]string{"AWS": {"*"}},
|
||||||
Resources: set.CreateStringSet("arn:aws:s3:::" + bucketName + "/*"),
|
Resources: set.CreateStringSet(bucketARNPrefix + bucketName + "/*"),
|
||||||
Sid: "",
|
Sid: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1031,7 +1031,7 @@ func testWebListAllBucketPoliciesHandler(obj ObjectLayer, instanceType string, t
|
|||||||
Actions: set.CreateStringSet("s3:GetBucketLocation"),
|
Actions: set.CreateStringSet("s3:GetBucketLocation"),
|
||||||
Effect: "Allow",
|
Effect: "Allow",
|
||||||
Principal: map[string][]string{"AWS": {"*"}},
|
Principal: map[string][]string{"AWS": {"*"}},
|
||||||
Resources: set.CreateStringSet("arn:aws:s3:::" + bucketName),
|
Resources: set.CreateStringSet(bucketARNPrefix + bucketName),
|
||||||
Sid: "",
|
Sid: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1043,14 +1043,14 @@ func testWebListAllBucketPoliciesHandler(obj ObjectLayer, instanceType string, t
|
|||||||
},
|
},
|
||||||
Effect: "Allow",
|
Effect: "Allow",
|
||||||
Principal: map[string][]string{"AWS": {"*"}},
|
Principal: map[string][]string{"AWS": {"*"}},
|
||||||
Resources: set.CreateStringSet("arn:aws:s3:::" + bucketName),
|
Resources: set.CreateStringSet(bucketARNPrefix + bucketName),
|
||||||
Sid: "",
|
Sid: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Actions: set.CreateStringSet("s3:ListBucketMultipartUploads"),
|
Actions: set.CreateStringSet("s3:ListBucketMultipartUploads"),
|
||||||
Effect: "Allow",
|
Effect: "Allow",
|
||||||
Principal: map[string][]string{"AWS": {"*"}},
|
Principal: map[string][]string{"AWS": {"*"}},
|
||||||
Resources: set.CreateStringSet("arn:aws:s3:::" + bucketName),
|
Resources: set.CreateStringSet(bucketARNPrefix + bucketName),
|
||||||
Sid: "",
|
Sid: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1058,7 +1058,7 @@ func testWebListAllBucketPoliciesHandler(obj ObjectLayer, instanceType string, t
|
|||||||
"s3:GetObject", "s3:ListMultipartUploadParts", "s3:PutObject"),
|
"s3:GetObject", "s3:ListMultipartUploadParts", "s3:PutObject"),
|
||||||
Effect: "Allow",
|
Effect: "Allow",
|
||||||
Principal: map[string][]string{"AWS": {"*"}},
|
Principal: map[string][]string{"AWS": {"*"}},
|
||||||
Resources: set.CreateStringSet("arn:aws:s3:::" + bucketName + "/hello*"),
|
Resources: set.CreateStringSet(bucketARNPrefix + bucketName + "/hello*"),
|
||||||
Sid: "",
|
Sid: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user