github.com/minio/minio-go/v6@v6.0.57/api-put-bucket.go (about) 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 18 package minio 19 20 import ( 21 "bytes" 22 "context" 23 "encoding/xml" 24 "io/ioutil" 25 "net/http" 26 "net/url" 27 "strings" 28 29 "github.com/minio/minio-go/v6/pkg/s3utils" 30 ) 31 32 // ApplyServerSideEncryptionByDefault defines default encryption configuration, KMS or SSE. To activate 33 // KMS, SSEAlgoritm needs to be set to "aws:kms" 34 // Minio currently does not support Kms. 35 type ApplyServerSideEncryptionByDefault struct { 36 KmsMasterKeyID string `xml:"KMSMasterKeyID,omitempty"` 37 SSEAlgorithm string `xml:"SSEAlgorithm"` 38 } 39 40 // Rule layer encapsulates default encryption configuration 41 type Rule struct { 42 Apply ApplyServerSideEncryptionByDefault `xml:"ApplyServerSideEncryptionByDefault"` 43 } 44 45 // ServerSideEncryptionConfiguration is the default encryption configuration structure 46 type ServerSideEncryptionConfiguration struct { 47 XMLName xml.Name `xml:"ServerSideEncryptionConfiguration"` 48 Rules []Rule `xml:"Rule"` 49 } 50 51 /// Bucket operations 52 53 func (c Client) makeBucket(ctx context.Context, bucketName string, location string, objectLockEnabled bool) (err error) { 54 // Validate the input arguments. 55 if err := s3utils.CheckValidBucketNameStrict(bucketName); err != nil { 56 return err 57 } 58 59 err = c.doMakeBucket(ctx, bucketName, location, objectLockEnabled) 60 if err != nil && (location == "" || location == "us-east-1") { 61 if resp, ok := err.(ErrorResponse); ok && resp.Code == "AuthorizationHeaderMalformed" && resp.Region != "" { 62 err = c.doMakeBucket(ctx, bucketName, resp.Region, objectLockEnabled) 63 } 64 } 65 return err 66 } 67 68 func (c Client) doMakeBucket(ctx context.Context, bucketName string, location string, objectLockEnabled bool) (err error) { 69 defer func() { 70 // Save the location into cache on a successful makeBucket response. 71 if err == nil { 72 c.bucketLocCache.Set(bucketName, location) 73 } 74 }() 75 76 // If location is empty, treat is a default region 'us-east-1'. 77 if location == "" { 78 location = "us-east-1" 79 // For custom region clients, default 80 // to custom region instead not 'us-east-1'. 81 if c.region != "" { 82 location = c.region 83 } 84 } 85 // PUT bucket request metadata. 86 reqMetadata := requestMetadata{ 87 bucketName: bucketName, 88 bucketLocation: location, 89 } 90 91 if objectLockEnabled { 92 headers := make(http.Header) 93 headers.Add("x-amz-bucket-object-lock-enabled", "true") 94 reqMetadata.customHeader = headers 95 } 96 97 // If location is not 'us-east-1' create bucket location config. 98 if location != "us-east-1" && location != "" { 99 createBucketConfig := createBucketConfiguration{} 100 createBucketConfig.Location = location 101 var createBucketConfigBytes []byte 102 createBucketConfigBytes, err = xml.Marshal(createBucketConfig) 103 if err != nil { 104 return err 105 } 106 reqMetadata.contentMD5Base64 = sumMD5Base64(createBucketConfigBytes) 107 reqMetadata.contentSHA256Hex = sum256Hex(createBucketConfigBytes) 108 reqMetadata.contentBody = bytes.NewReader(createBucketConfigBytes) 109 reqMetadata.contentLength = int64(len(createBucketConfigBytes)) 110 } 111 112 // Execute PUT to create a new bucket. 113 resp, err := c.executeMethod(ctx, "PUT", reqMetadata) 114 defer closeResponse(resp) 115 if err != nil { 116 return err 117 } 118 119 if resp != nil { 120 if resp.StatusCode != http.StatusOK { 121 return httpRespToErrorResponse(resp, bucketName, "") 122 } 123 } 124 125 // Success. 126 return nil 127 } 128 129 // MakeBucket creates a new bucket with bucketName. 130 // 131 // Location is an optional argument, by default all buckets are 132 // created in US Standard Region. 133 // 134 // For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html 135 // For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations 136 func (c Client) MakeBucket(bucketName string, location string) (err error) { 137 return c.MakeBucketWithContext(context.Background(), bucketName, location) 138 } 139 140 // MakeBucketWithContext creates a new bucket with bucketName with a context to control cancellations and timeouts. 141 // 142 // Location is an optional argument, by default all buckets are 143 // created in US Standard Region. 144 // 145 // For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html 146 // For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations 147 func (c Client) MakeBucketWithContext(ctx context.Context, bucketName string, location string) (err error) { 148 return c.makeBucket(ctx, bucketName, location, false) 149 } 150 151 // MakeBucketWithObjectLock creates a object lock enabled new bucket with bucketName. 152 // 153 // Location is an optional argument, by default all buckets are 154 // created in US Standard Region. 155 // 156 // For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html 157 // For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations 158 func (c Client) MakeBucketWithObjectLock(bucketName string, location string) (err error) { 159 return c.MakeBucketWithObjectLockWithContext(context.Background(), bucketName, location) 160 } 161 162 // MakeBucketWithObjectLockWithContext creates a object lock enabled new bucket with bucketName with a context to 163 // control cancellations and timeouts. 164 // 165 // Location is an optional argument, by default all buckets are 166 // created in US Standard Region. 167 // 168 // For Amazon S3 for more supported regions - http://docs.aws.amazon.com/general/latest/gr/rande.html 169 // For Google Cloud Storage for more supported regions - https://cloud.google.com/storage/docs/bucket-locations 170 func (c Client) MakeBucketWithObjectLockWithContext(ctx context.Context, bucketName string, location string) (err error) { 171 return c.makeBucket(ctx, bucketName, location, true) 172 } 173 174 // SetBucketPolicy set the access permissions on an existing bucket. 175 func (c Client) SetBucketPolicy(bucketName, policy string) error { 176 return c.SetBucketPolicyWithContext(context.Background(), bucketName, policy) 177 } 178 179 // SetBucketPolicyWithContext set the access permissions on an existing bucket. 180 func (c Client) SetBucketPolicyWithContext(ctx context.Context, bucketName, policy string) error { 181 // Input validation. 182 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 183 return err 184 } 185 186 // If policy is empty then delete the bucket policy. 187 if policy == "" { 188 return c.removeBucketPolicy(ctx, bucketName) 189 } 190 191 // Save the updated policies. 192 return c.putBucketPolicy(ctx, bucketName, policy) 193 } 194 195 // Saves a new bucket policy. 196 func (c Client) putBucketPolicy(ctx context.Context, bucketName, policy string) error { 197 // Input validation. 198 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 199 return err 200 } 201 202 // Get resources properly escaped and lined up before 203 // using them in http request. 204 urlValues := make(url.Values) 205 urlValues.Set("policy", "") 206 207 // Content-length is mandatory for put policy request 208 policyReader := strings.NewReader(policy) 209 b, err := ioutil.ReadAll(policyReader) 210 if err != nil { 211 return err 212 } 213 214 reqMetadata := requestMetadata{ 215 bucketName: bucketName, 216 queryValues: urlValues, 217 contentBody: policyReader, 218 contentLength: int64(len(b)), 219 } 220 221 // Execute PUT to upload a new bucket policy. 222 resp, err := c.executeMethod(ctx, "PUT", reqMetadata) 223 defer closeResponse(resp) 224 if err != nil { 225 return err 226 } 227 if resp != nil { 228 if resp.StatusCode != http.StatusNoContent { 229 return httpRespToErrorResponse(resp, bucketName, "") 230 } 231 } 232 return nil 233 } 234 235 // Removes all policies on a bucket. 236 func (c Client) removeBucketPolicy(ctx context.Context, bucketName string) error { 237 // Input validation. 238 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 239 return err 240 } 241 // Get resources properly escaped and lined up before 242 // using them in http request. 243 urlValues := make(url.Values) 244 urlValues.Set("policy", "") 245 246 // Execute DELETE on objectName. 247 resp, err := c.executeMethod(ctx, "DELETE", requestMetadata{ 248 bucketName: bucketName, 249 queryValues: urlValues, 250 contentSHA256Hex: emptySHA256Hex, 251 }) 252 defer closeResponse(resp) 253 if err != nil { 254 return err 255 } 256 return nil 257 } 258 259 // SetBucketLifecycle set the lifecycle on an existing bucket. 260 func (c Client) SetBucketLifecycle(bucketName, lifecycle string) error { 261 return c.SetBucketLifecycleWithContext(context.Background(), bucketName, lifecycle) 262 } 263 264 // SetBucketLifecycleWithContext set the lifecycle on an existing bucket with a context to control cancellations and timeouts. 265 func (c Client) SetBucketLifecycleWithContext(ctx context.Context, bucketName, lifecycle string) error { 266 // Input validation. 267 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 268 return err 269 } 270 271 // If lifecycle is empty then delete it. 272 if lifecycle == "" { 273 return c.removeBucketLifecycle(ctx, bucketName) 274 } 275 276 // Save the updated lifecycle. 277 return c.putBucketLifecycle(ctx, bucketName, lifecycle) 278 } 279 280 // Saves a new bucket lifecycle. 281 func (c Client) putBucketLifecycle(ctx context.Context, bucketName, lifecycle string) error { 282 // Input validation. 283 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 284 return err 285 } 286 287 // Get resources properly escaped and lined up before 288 // using them in http request. 289 urlValues := make(url.Values) 290 urlValues.Set("lifecycle", "") 291 292 // Content-length is mandatory for put lifecycle request 293 lifecycleReader := strings.NewReader(lifecycle) 294 b, err := ioutil.ReadAll(lifecycleReader) 295 if err != nil { 296 return err 297 } 298 299 reqMetadata := requestMetadata{ 300 bucketName: bucketName, 301 queryValues: urlValues, 302 contentBody: lifecycleReader, 303 contentLength: int64(len(b)), 304 contentMD5Base64: sumMD5Base64(b), 305 } 306 307 // Execute PUT to upload a new bucket lifecycle. 308 resp, err := c.executeMethod(ctx, "PUT", reqMetadata) 309 defer closeResponse(resp) 310 if err != nil { 311 return err 312 } 313 if resp != nil { 314 if resp.StatusCode != http.StatusOK { 315 return httpRespToErrorResponse(resp, bucketName, "") 316 } 317 } 318 return nil 319 } 320 321 // Remove lifecycle from a bucket. 322 func (c Client) removeBucketLifecycle(ctx context.Context, bucketName string) error { 323 // Input validation. 324 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 325 return err 326 } 327 // Get resources properly escaped and lined up before 328 // using them in http request. 329 urlValues := make(url.Values) 330 urlValues.Set("lifecycle", "") 331 332 // Execute DELETE on objectName. 333 resp, err := c.executeMethod(ctx, "DELETE", requestMetadata{ 334 bucketName: bucketName, 335 queryValues: urlValues, 336 contentSHA256Hex: emptySHA256Hex, 337 }) 338 defer closeResponse(resp) 339 if err != nil { 340 return err 341 } 342 return nil 343 } 344 345 // SetBucketEncryption sets the default encryption configuration on an existing bucket. 346 func (c Client) SetBucketEncryption(bucketName string, configuration ServerSideEncryptionConfiguration) error { 347 return c.SetBucketEncryptionWithContext(context.Background(), bucketName, configuration) 348 } 349 350 // SetBucketEncryptionWithContext sets the default encryption configuration on an existing bucket with a context to control cancellations and timeouts. 351 func (c Client) SetBucketEncryptionWithContext(ctx context.Context, bucketName string, configuration ServerSideEncryptionConfiguration) error { 352 // Input validation. 353 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 354 return err 355 } 356 357 buf, err := xml.Marshal(&configuration) 358 if err != nil { 359 return err 360 } 361 362 // Get resources properly escaped and lined up before 363 // using them in http request. 364 urlValues := make(url.Values) 365 urlValues.Set("encryption", "") 366 367 // Content-length is mandatory to set a default encryption configuration 368 reqMetadata := requestMetadata{ 369 bucketName: bucketName, 370 queryValues: urlValues, 371 contentBody: bytes.NewReader(buf), 372 contentLength: int64(len(buf)), 373 contentMD5Base64: sumMD5Base64(buf), 374 } 375 376 // Execute PUT to upload a new bucket default encryption configuration. 377 resp, err := c.executeMethod(ctx, http.MethodPut, reqMetadata) 378 defer closeResponse(resp) 379 if err != nil { 380 return err 381 } 382 if resp.StatusCode != http.StatusOK { 383 return httpRespToErrorResponse(resp, bucketName, "") 384 } 385 return nil 386 } 387 388 // DeleteBucketEncryption removes the default encryption configuration on a bucket. 389 func (c Client) DeleteBucketEncryption(bucketName string) error { 390 return c.DeleteBucketEncryptionWithContext(context.Background(), bucketName) 391 } 392 393 // DeleteBucketEncryptionWithContext removes the default encryption configuration on a bucket with a context to control cancellations and timeouts. 394 func (c Client) DeleteBucketEncryptionWithContext(ctx context.Context, bucketName string) error { 395 // Input validation. 396 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 397 return err 398 } 399 400 // Get resources properly escaped and lined up before 401 // using them in http request. 402 urlValues := make(url.Values) 403 urlValues.Set("encryption", "") 404 405 // DELETE default encryption configuration on a bucket. 406 resp, err := c.executeMethod(ctx, http.MethodDelete, requestMetadata{ 407 bucketName: bucketName, 408 queryValues: urlValues, 409 contentSHA256Hex: emptySHA256Hex, 410 }) 411 defer closeResponse(resp) 412 if err != nil { 413 return err 414 } 415 if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { 416 return httpRespToErrorResponse(resp, bucketName, "") 417 } 418 return nil 419 } 420 421 // SetBucketNotification saves a new bucket notification. 422 func (c Client) SetBucketNotification(bucketName string, bucketNotification BucketNotification) error { 423 return c.SetBucketNotificationWithContext(context.Background(), bucketName, bucketNotification) 424 } 425 426 // SetBucketNotificationWithContext saves a new bucket notification with a context to control cancellations 427 // and timeouts. 428 func (c Client) SetBucketNotificationWithContext(ctx context.Context, bucketName string, bucketNotification BucketNotification) error { 429 // Input validation. 430 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 431 return err 432 } 433 434 // Get resources properly escaped and lined up before 435 // using them in http request. 436 urlValues := make(url.Values) 437 urlValues.Set("notification", "") 438 439 notifBytes, err := xml.Marshal(bucketNotification) 440 if err != nil { 441 return err 442 } 443 444 notifBuffer := bytes.NewReader(notifBytes) 445 reqMetadata := requestMetadata{ 446 bucketName: bucketName, 447 queryValues: urlValues, 448 contentBody: notifBuffer, 449 contentLength: int64(len(notifBytes)), 450 contentMD5Base64: sumMD5Base64(notifBytes), 451 contentSHA256Hex: sum256Hex(notifBytes), 452 } 453 454 // Execute PUT to upload a new bucket notification. 455 resp, err := c.executeMethod(ctx, "PUT", reqMetadata) 456 defer closeResponse(resp) 457 if err != nil { 458 return err 459 } 460 if resp != nil { 461 if resp.StatusCode != http.StatusOK { 462 return httpRespToErrorResponse(resp, bucketName, "") 463 } 464 } 465 return nil 466 } 467 468 // RemoveAllBucketNotification - Remove bucket notification clears all previously specified config 469 func (c Client) RemoveAllBucketNotification(bucketName string) error { 470 return c.SetBucketNotification(bucketName, BucketNotification{}) 471 } 472 473 var ( 474 versionEnableConfig = []byte("<VersioningConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Status>Enabled</Status></VersioningConfiguration>") 475 versionEnableConfigLen = int64(len(versionEnableConfig)) 476 versionEnableConfigMD5Sum = sumMD5Base64(versionEnableConfig) 477 versionEnableConfigSHA256 = sum256Hex(versionEnableConfig) 478 479 versionDisableConfig = []byte("<VersioningConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Status>Suspended</Status></VersioningConfiguration>") 480 versionDisableConfigLen = int64(len(versionDisableConfig)) 481 versionDisableConfigMD5Sum = sumMD5Base64(versionDisableConfig) 482 versionDisableConfigSHA256 = sum256Hex(versionDisableConfig) 483 ) 484 485 func (c Client) setVersioning(ctx context.Context, bucketName string, config []byte, length int64, md5sum, sha256sum string) error { 486 // Input validation. 487 if err := s3utils.CheckValidBucketName(bucketName); err != nil { 488 return err 489 } 490 491 // Get resources properly escaped and lined up before 492 // using them in http request. 493 urlValues := make(url.Values) 494 urlValues.Set("versioning", "") 495 496 reqMetadata := requestMetadata{ 497 bucketName: bucketName, 498 queryValues: urlValues, 499 contentBody: bytes.NewReader(config), 500 contentLength: length, 501 contentMD5Base64: md5sum, 502 contentSHA256Hex: sha256sum, 503 } 504 505 // Execute PUT to set a bucket versioning. 506 resp, err := c.executeMethod(ctx, "PUT", reqMetadata) 507 defer closeResponse(resp) 508 if err != nil { 509 return err 510 } 511 if resp != nil { 512 if resp.StatusCode != http.StatusOK { 513 return httpRespToErrorResponse(resp, bucketName, "") 514 } 515 } 516 return nil 517 } 518 519 // EnableVersioning - Enable object versioning in given bucket. 520 func (c Client) EnableVersioning(bucketName string) error { 521 return c.EnableVersioningWithContext(context.Background(), bucketName) 522 } 523 524 // EnableVersioningWithContext - Enable object versioning in given bucket with a context to control cancellations and timeouts. 525 func (c Client) EnableVersioningWithContext(ctx context.Context, bucketName string) error { 526 return c.setVersioning(ctx, bucketName, versionEnableConfig, versionEnableConfigLen, versionEnableConfigMD5Sum, versionEnableConfigSHA256) 527 } 528 529 // DisableVersioning - Disable object versioning in given bucket. 530 func (c Client) DisableVersioning(bucketName string) error { 531 return c.DisableVersioningWithContext(context.Background(), bucketName) 532 } 533 534 // DisableVersioningWithContext - Disable object versioning in given bucket with a context to control cancellations and timeouts. 535 func (c Client) DisableVersioningWithContext(ctx context.Context, bucketName string) error { 536 return c.setVersioning(ctx, bucketName, versionDisableConfig, versionDisableConfigLen, versionDisableConfigMD5Sum, versionDisableConfigSHA256) 537 }