1/*
2 * MinIO Go Library for Amazon S3 Compatible Cloud Storage
3 * Copyright 2015-2020 MinIO, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package minio
19
20import (
21	"bytes"
22	"context"
23	"encoding/xml"
24	"net/http"
25
26	"github.com/minio/minio-go/v7/pkg/s3utils"
27)
28
29// Bucket operations
30func (c *Client) makeBucket(ctx context.Context, bucketName string, opts MakeBucketOptions) (err error) {
31	// Validate the input arguments.
32	if err := s3utils.CheckValidBucketNameStrict(bucketName); err != nil {
33		return err
34	}
35
36	err = c.doMakeBucket(ctx, bucketName, opts.Region, opts.ObjectLocking)
37	if err != nil && (opts.Region == "" || opts.Region == "us-east-1") {
38		if resp, ok := err.(ErrorResponse); ok && resp.Code == "AuthorizationHeaderMalformed" && resp.Region != "" {
39			err = c.doMakeBucket(ctx, bucketName, resp.Region, opts.ObjectLocking)
40		}
41	}
42	return err
43}
44
45func (c *Client) doMakeBucket(ctx context.Context, bucketName string, location string, objectLockEnabled bool) (err error) {
46	defer func() {
47		// Save the location into cache on a successful makeBucket response.
48		if err == nil {
49			c.bucketLocCache.Set(bucketName, location)
50		}
51	}()
52
53	// If location is empty, treat is a default region 'us-east-1'.
54	if location == "" {
55		location = "us-east-1"
56		// For custom region clients, default
57		// to custom region instead not 'us-east-1'.
58		if c.region != "" {
59			location = c.region
60		}
61	}
62	// PUT bucket request metadata.
63	reqMetadata := requestMetadata{
64		bucketName:     bucketName,
65		bucketLocation: location,
66	}
67
68	if objectLockEnabled {
69		headers := make(http.Header)
70		headers.Add("x-amz-bucket-object-lock-enabled", "true")
71		reqMetadata.customHeader = headers
72	}
73
74	// If location is not 'us-east-1' create bucket location config.
75	if location != "us-east-1" && location != "" {
76		createBucketConfig := createBucketConfiguration{}
77		createBucketConfig.Location = location
78		var createBucketConfigBytes []byte
79		createBucketConfigBytes, err = xml.Marshal(createBucketConfig)
80		if err != nil {
81			return err
82		}
83		reqMetadata.contentMD5Base64 = sumMD5Base64(createBucketConfigBytes)
84		reqMetadata.contentSHA256Hex = sum256Hex(createBucketConfigBytes)
85		reqMetadata.contentBody = bytes.NewReader(createBucketConfigBytes)
86		reqMetadata.contentLength = int64(len(createBucketConfigBytes))
87	}
88
89	// Execute PUT to create a new bucket.
90	resp, err := c.executeMethod(ctx, http.MethodPut, reqMetadata)
91	defer closeResponse(resp)
92	if err != nil {
93		return err
94	}
95
96	if resp != nil {
97		if resp.StatusCode != http.StatusOK {
98			return httpRespToErrorResponse(resp, bucketName, "")
99		}
100	}
101
102	// Success.
103	return nil
104}
105
106// MakeBucketOptions holds all options to tweak bucket creation
107type MakeBucketOptions struct {
108	// Bucket location
109	Region string
110	// Enable object locking
111	ObjectLocking bool
112}
113
114// MakeBucket creates a new bucket with bucketName with a context to control cancellations and timeouts.
115//
116// Location is an optional argument, by default all buckets are
117// created in US Standard Region.
118//
119// For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html
120// For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations
121func (c *Client) MakeBucket(ctx context.Context, bucketName string, opts MakeBucketOptions) (err error) {
122	return c.makeBucket(ctx, bucketName, opts)
123}
124