github.com/minio/console@v1.3.0/api/admin_remote_buckets.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "net/url" 24 "strconv" 25 "time" 26 27 "github.com/minio/console/pkg/utils" 28 29 "github.com/minio/madmin-go/v3" 30 31 "github.com/go-openapi/runtime/middleware" 32 "github.com/go-openapi/swag" 33 "github.com/minio/console/api/operations" 34 bucketApi "github.com/minio/console/api/operations/bucket" 35 "github.com/minio/console/models" 36 "github.com/minio/minio-go/v7/pkg/replication" 37 ) 38 39 type RemoteBucketResult struct { 40 OriginBucket string 41 TargetBucket string 42 Error string 43 } 44 45 func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) { 46 // return list of remote buckets 47 api.BucketListRemoteBucketsHandler = bucketApi.ListRemoteBucketsHandlerFunc(func(params bucketApi.ListRemoteBucketsParams, session *models.Principal) middleware.Responder { 48 listResp, err := getListRemoteBucketsResponse(session, params) 49 if err != nil { 50 return bucketApi.NewListRemoteBucketsDefault(err.Code).WithPayload(err.APIError) 51 } 52 return bucketApi.NewListRemoteBucketsOK().WithPayload(listResp) 53 }) 54 55 // return information about a specific bucket 56 api.BucketRemoteBucketDetailsHandler = bucketApi.RemoteBucketDetailsHandlerFunc(func(params bucketApi.RemoteBucketDetailsParams, session *models.Principal) middleware.Responder { 57 response, err := getRemoteBucketDetailsResponse(session, params) 58 if err != nil { 59 return bucketApi.NewRemoteBucketDetailsDefault(err.Code).WithPayload(err.APIError) 60 } 61 return bucketApi.NewRemoteBucketDetailsOK().WithPayload(response) 62 }) 63 64 // delete remote bucket 65 api.BucketDeleteRemoteBucketHandler = bucketApi.DeleteRemoteBucketHandlerFunc(func(params bucketApi.DeleteRemoteBucketParams, session *models.Principal) middleware.Responder { 66 err := getDeleteRemoteBucketResponse(session, params) 67 if err != nil { 68 return bucketApi.NewDeleteRemoteBucketDefault(err.Code).WithPayload(err.APIError) 69 } 70 return bucketApi.NewDeleteRemoteBucketNoContent() 71 }) 72 73 // set remote bucket 74 api.BucketAddRemoteBucketHandler = bucketApi.AddRemoteBucketHandlerFunc(func(params bucketApi.AddRemoteBucketParams, session *models.Principal) middleware.Responder { 75 err := getAddRemoteBucketResponse(session, params) 76 if err != nil { 77 return bucketApi.NewAddRemoteBucketDefault(err.Code).WithPayload(err.APIError) 78 } 79 return bucketApi.NewAddRemoteBucketCreated() 80 }) 81 82 // set multi-bucket replication 83 api.BucketSetMultiBucketReplicationHandler = bucketApi.SetMultiBucketReplicationHandlerFunc(func(params bucketApi.SetMultiBucketReplicationParams, session *models.Principal) middleware.Responder { 84 response, err := setMultiBucketReplicationResponse(session, params) 85 if err != nil { 86 return bucketApi.NewSetMultiBucketReplicationDefault(err.Code).WithPayload(err.APIError) 87 } 88 89 return bucketApi.NewSetMultiBucketReplicationOK().WithPayload(response) 90 }) 91 92 // list external buckets 93 api.BucketListExternalBucketsHandler = bucketApi.ListExternalBucketsHandlerFunc(func(params bucketApi.ListExternalBucketsParams, _ *models.Principal) middleware.Responder { 94 response, err := listExternalBucketsResponse(params) 95 if err != nil { 96 return bucketApi.NewListExternalBucketsDefault(err.Code).WithPayload(err.APIError) 97 } 98 99 return bucketApi.NewListExternalBucketsOK().WithPayload(response) 100 }) 101 102 // delete replication rule 103 api.BucketDeleteBucketReplicationRuleHandler = bucketApi.DeleteBucketReplicationRuleHandlerFunc(func(params bucketApi.DeleteBucketReplicationRuleParams, session *models.Principal) middleware.Responder { 104 err := deleteReplicationRuleResponse(session, params) 105 if err != nil { 106 return bucketApi.NewDeleteBucketReplicationRuleDefault(err.Code).WithPayload(err.APIError) 107 } 108 109 return bucketApi.NewDeleteBucketReplicationRuleNoContent() 110 }) 111 112 // delete all replication rules for a bucket 113 api.BucketDeleteAllReplicationRulesHandler = bucketApi.DeleteAllReplicationRulesHandlerFunc(func(params bucketApi.DeleteAllReplicationRulesParams, session *models.Principal) middleware.Responder { 114 err := deleteBucketReplicationRulesResponse(session, params) 115 if err != nil { 116 if err.Code == 500 && err.APIError.DetailedMessage == "The remote target does not exist" { 117 // We should ignore this MinIO error when deleting all replication rules 118 return bucketApi.NewDeleteAllReplicationRulesNoContent() // This will return 204 as per swagger spec 119 } 120 // If there is a different error, then we should handle it 121 // This will return a generic error with err.Code (likely a 500 or 404) and its *err.DetailedMessage 122 return bucketApi.NewDeleteAllReplicationRulesDefault(err.Code).WithPayload(err.APIError) 123 } 124 return bucketApi.NewDeleteAllReplicationRulesNoContent() 125 }) 126 127 // delete selected replication rules for a bucket 128 api.BucketDeleteSelectedReplicationRulesHandler = bucketApi.DeleteSelectedReplicationRulesHandlerFunc(func(params bucketApi.DeleteSelectedReplicationRulesParams, session *models.Principal) middleware.Responder { 129 err := deleteSelectedReplicationRulesResponse(session, params) 130 if err != nil { 131 return bucketApi.NewDeleteSelectedReplicationRulesDefault(err.Code).WithPayload(err.APIError) 132 } 133 134 return bucketApi.NewDeleteSelectedReplicationRulesNoContent() 135 }) 136 137 // update local bucket replication config item 138 api.BucketUpdateMultiBucketReplicationHandler = bucketApi.UpdateMultiBucketReplicationHandlerFunc(func(params bucketApi.UpdateMultiBucketReplicationParams, session *models.Principal) middleware.Responder { 139 err := updateBucketReplicationResponse(session, params) 140 if err != nil { 141 return bucketApi.NewUpdateMultiBucketReplicationDefault(err.Code).WithPayload(err.APIError) 142 } 143 return bucketApi.NewUpdateMultiBucketReplicationCreated() 144 }) 145 } 146 147 func getListRemoteBucketsResponse(session *models.Principal, params bucketApi.ListRemoteBucketsParams) (*models.ListRemoteBucketsResponse, *CodedAPIError) { 148 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 149 defer cancel() 150 mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) 151 if err != nil { 152 return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err)) 153 } 154 adminClient := AdminClient{Client: mAdmin} 155 return listRemoteBuckets(ctx, adminClient) 156 } 157 158 func getRemoteBucketDetailsResponse(session *models.Principal, params bucketApi.RemoteBucketDetailsParams) (*models.RemoteBucket, *CodedAPIError) { 159 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 160 defer cancel() 161 mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) 162 if err != nil { 163 return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err)) 164 } 165 adminClient := AdminClient{Client: mAdmin} 166 return getRemoteBucket(ctx, adminClient, params.Name) 167 } 168 169 func getDeleteRemoteBucketResponse(session *models.Principal, params bucketApi.DeleteRemoteBucketParams) *CodedAPIError { 170 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 171 defer cancel() 172 mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) 173 if err != nil { 174 return ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err)) 175 } 176 adminClient := AdminClient{Client: mAdmin} 177 err = deleteRemoteBucket(ctx, adminClient, params.SourceBucketName, params.Arn) 178 if err != nil { 179 return ErrorWithContext(ctx, fmt.Errorf("error deleting remote bucket: %v", err)) 180 } 181 return nil 182 } 183 184 func getAddRemoteBucketResponse(session *models.Principal, params bucketApi.AddRemoteBucketParams) *CodedAPIError { 185 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 186 defer cancel() 187 mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) 188 if err != nil { 189 return ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err)) 190 } 191 adminClient := AdminClient{Client: mAdmin} 192 _, err = addRemoteBucket(ctx, adminClient, *params.Body) 193 if err != nil { 194 return ErrorWithContext(ctx, fmt.Errorf("error adding remote bucket: %v", err)) 195 } 196 return nil 197 } 198 199 func listRemoteBuckets(ctx context.Context, client MinioAdmin) (*models.ListRemoteBucketsResponse, *CodedAPIError) { 200 var remoteBuckets []*models.RemoteBucket 201 buckets, err := client.listRemoteBuckets(ctx, "", "") 202 if err != nil { 203 return nil, ErrorWithContext(ctx, fmt.Errorf("error listing remote buckets: %v", err)) 204 } 205 for _, bucket := range buckets { 206 remoteBucket := &models.RemoteBucket{ 207 AccessKey: swag.String(bucket.Credentials.AccessKey), 208 RemoteARN: swag.String(bucket.Arn), 209 SecretKey: bucket.Credentials.SecretKey, 210 Service: "replication", 211 SourceBucket: swag.String(bucket.SourceBucket), 212 Status: "", 213 TargetBucket: bucket.TargetBucket, 214 TargetURL: bucket.Endpoint, 215 SyncMode: "async", 216 Bandwidth: bucket.BandwidthLimit, 217 HealthCheckPeriod: int64(bucket.HealthCheckDuration.Seconds()), 218 } 219 if bucket.ReplicationSync { 220 remoteBucket.SyncMode = "sync" 221 } 222 remoteBuckets = append(remoteBuckets, remoteBucket) 223 } 224 225 return &models.ListRemoteBucketsResponse{ 226 Buckets: remoteBuckets, 227 Total: int64(len(remoteBuckets)), 228 }, nil 229 } 230 231 func getRemoteBucket(ctx context.Context, client MinioAdmin, name string) (*models.RemoteBucket, *CodedAPIError) { 232 remoteBucket, err := client.getRemoteBucket(ctx, name, "") 233 if err != nil { 234 return nil, ErrorWithContext(ctx, fmt.Errorf("error getting remote bucket details: %v", err)) 235 } 236 if remoteBucket == nil { 237 return nil, ErrorWithContext(ctx, "error getting remote bucket details: bucket not found") 238 } 239 return &models.RemoteBucket{ 240 AccessKey: &remoteBucket.Credentials.AccessKey, 241 RemoteARN: &remoteBucket.Arn, 242 SecretKey: remoteBucket.Credentials.SecretKey, 243 Service: "replication", 244 SourceBucket: &remoteBucket.SourceBucket, 245 Status: "", 246 TargetBucket: remoteBucket.TargetBucket, 247 TargetURL: remoteBucket.Endpoint, 248 }, nil 249 } 250 251 func deleteRemoteBucket(ctx context.Context, client MinioAdmin, sourceBucketName, arn string) error { 252 return client.removeRemoteBucket(ctx, sourceBucketName, arn) 253 } 254 255 func addRemoteBucket(ctx context.Context, client MinioAdmin, params models.CreateRemoteBucket) (string, error) { 256 TargetURL := *params.TargetURL 257 accessKey := *params.AccessKey 258 secretKey := *params.SecretKey 259 u, err := url.Parse(TargetURL) 260 if err != nil { 261 return "", errors.New("malformed Remote target URL") 262 } 263 secure := u.Scheme == "https" 264 host := u.Host 265 if u.Port() == "" { 266 port := 80 267 if secure { 268 port = 443 269 } 270 host = host + ":" + strconv.Itoa(port) 271 } 272 creds := &madmin.Credentials{AccessKey: accessKey, SecretKey: secretKey} 273 remoteBucket := &madmin.BucketTarget{ 274 TargetBucket: *params.TargetBucket, 275 Secure: secure, 276 Credentials: creds, 277 Endpoint: host, 278 Path: "", 279 API: "s3v4", 280 Type: "replication", 281 Region: params.Region, 282 ReplicationSync: *params.SyncMode == "sync", 283 } 284 if *params.SyncMode == "async" { 285 remoteBucket.BandwidthLimit = params.Bandwidth 286 } 287 if params.HealthCheckPeriod > 0 { 288 remoteBucket.HealthCheckDuration = time.Duration(params.HealthCheckPeriod) * time.Second 289 } 290 bucketARN, err := client.addRemoteBucket(ctx, *params.SourceBucket, remoteBucket) 291 292 return bucketARN, err 293 } 294 295 func addBucketReplicationItem(ctx context.Context, session *models.Principal, minClient minioClient, bucketName, prefix, destinationARN string, repDelMark, repDels, repMeta bool, tags string, priority int32, storageClass string) error { 296 // we will tolerate this call failing 297 cfg, err := minClient.getBucketReplication(ctx, bucketName) 298 if err != nil { 299 ErrorWithContext(ctx, fmt.Errorf("error fetching replication configuration for bucket %s: %v", bucketName, err)) 300 } 301 302 // add rule 303 maxPrio := 0 304 305 if priority <= 0 { // We pick next priority by default 306 for _, r := range cfg.Rules { 307 if r.Priority > maxPrio { 308 maxPrio = r.Priority 309 } 310 } 311 maxPrio++ 312 } else { // User picked priority, we try to set this manually 313 maxPrio = int(priority) 314 } 315 clientIP := utils.ClientIPFromContext(ctx) 316 s3Client, err := newS3BucketClient(session, bucketName, prefix, clientIP) 317 if err != nil { 318 ErrorWithContext(ctx, fmt.Errorf("error creating S3Client: %v", err)) 319 return err 320 } 321 // create a mc S3Client interface implementation 322 // defining the client to be used 323 mcClient := mcClient{client: s3Client} 324 325 repDelMarkStatus := "disable" 326 if repDelMark { 327 repDelMarkStatus = "enable" 328 } 329 330 repDelsStatus := "disable" 331 if repDels { 332 repDelsStatus = "enable" 333 } 334 335 repMetaStatus := "disable" 336 if repMeta { 337 repMetaStatus = "enable" 338 } 339 340 opts := replication.Options{ 341 Priority: fmt.Sprintf("%d", maxPrio), 342 RuleStatus: "enable", 343 DestBucket: destinationARN, 344 Op: replication.AddOption, 345 TagString: tags, 346 ExistingObjectReplicate: "enable", // enabled by default 347 ReplicateDeleteMarkers: repDelMarkStatus, 348 ReplicateDeletes: repDelsStatus, 349 ReplicaSync: repMetaStatus, 350 StorageClass: storageClass, 351 } 352 353 err2 := mcClient.setReplication(ctx, &cfg, opts) 354 if err2 != nil { 355 ErrorWithContext(ctx, fmt.Errorf("error creating replication for bucket: %v", err2.Cause)) 356 return err2.Cause 357 } 358 return nil 359 } 360 361 func editBucketReplicationItem(ctx context.Context, session *models.Principal, minClient minioClient, ruleID, bucketName, prefix, destinationARN string, ruleStatus, repDelMark, repDels, repMeta, existingObjectRep bool, tags string, priority int32, storageClass string) error { 362 // we will tolerate this call failing 363 cfg, err := minClient.getBucketReplication(ctx, bucketName) 364 if err != nil { 365 ErrorWithContext(ctx, fmt.Errorf("error fetching replication configuration for bucket %s: %v", bucketName, err)) 366 } 367 368 maxPrio := int(priority) 369 370 clientIP := utils.ClientIPFromContext(ctx) 371 s3Client, err := newS3BucketClient(session, bucketName, prefix, clientIP) 372 if err != nil { 373 return fmt.Errorf("error creating S3Client: %v", err) 374 } 375 // create a mc S3Client interface implementation 376 // defining the client to be used 377 mcClient := mcClient{client: s3Client} 378 379 ruleState := "disable" 380 if ruleStatus { 381 ruleState = "enable" 382 } 383 384 repDelMarkStatus := "disable" 385 if repDelMark { 386 repDelMarkStatus = "enable" 387 } 388 389 repDelsStatus := "disable" 390 if repDels { 391 repDelsStatus = "enable" 392 } 393 394 repMetaStatus := "disable" 395 if repMeta { 396 repMetaStatus = "enable" 397 } 398 399 existingRepStatus := "disable" 400 if existingObjectRep { 401 existingRepStatus = "enable" 402 } 403 404 opts := replication.Options{ 405 ID: ruleID, 406 Priority: fmt.Sprintf("%d", maxPrio), 407 RuleStatus: ruleState, 408 DestBucket: destinationARN, 409 Op: replication.SetOption, 410 TagString: tags, 411 IsTagSet: true, 412 ExistingObjectReplicate: existingRepStatus, 413 ReplicateDeleteMarkers: repDelMarkStatus, 414 ReplicateDeletes: repDelsStatus, 415 ReplicaSync: repMetaStatus, 416 StorageClass: storageClass, 417 IsSCSet: true, 418 } 419 420 err2 := mcClient.setReplication(ctx, &cfg, opts) 421 if err2 != nil { 422 return fmt.Errorf("error modifying replication for bucket: %v", err2.Cause) 423 } 424 return nil 425 } 426 427 func setMultiBucketReplication(ctx context.Context, session *models.Principal, client MinioAdmin, minClient minioClient, params bucketApi.SetMultiBucketReplicationParams) []RemoteBucketResult { 428 bucketsRelation := params.Body.BucketsRelation 429 430 // Parallel remote bucket adding 431 parallelRemoteBucket := func(bucketRelationData *models.MultiBucketsRelation) chan RemoteBucketResult { 432 remoteProc := make(chan RemoteBucketResult) 433 sourceBucket := bucketRelationData.OriginBucket 434 targetBucket := bucketRelationData.DestinationBucket 435 436 go func() { 437 defer close(remoteProc) 438 439 createRemoteBucketParams := models.CreateRemoteBucket{ 440 AccessKey: params.Body.AccessKey, 441 SecretKey: params.Body.SecretKey, 442 SourceBucket: &sourceBucket, 443 TargetBucket: &targetBucket, 444 Region: params.Body.Region, 445 TargetURL: params.Body.TargetURL, 446 SyncMode: params.Body.SyncMode, 447 Bandwidth: params.Body.Bandwidth, 448 HealthCheckPeriod: params.Body.HealthCheckPeriod, 449 } 450 451 // We add the remote bucket reference & store the arn or errors returned 452 arn, err := addRemoteBucket(ctx, client, createRemoteBucketParams) 453 454 if err == nil { 455 err = addBucketReplicationItem( 456 ctx, 457 session, 458 minClient, 459 sourceBucket, 460 params.Body.Prefix, 461 arn, 462 params.Body.ReplicateDeleteMarkers, 463 params.Body.ReplicateDeletes, 464 params.Body.ReplicateMetadata, 465 params.Body.Tags, 466 params.Body.Priority, 467 params.Body.StorageClass) 468 } 469 470 errorReturn := "" 471 472 if err != nil { 473 deleteRemoteBucket(ctx, client, sourceBucket, arn) 474 errorReturn = err.Error() 475 } 476 477 retParams := RemoteBucketResult{ 478 OriginBucket: sourceBucket, 479 TargetBucket: targetBucket, 480 Error: errorReturn, 481 } 482 483 remoteProc <- retParams 484 }() 485 return remoteProc 486 } 487 488 var bucketsManagement []chan RemoteBucketResult 489 490 for _, bucketName := range bucketsRelation { 491 // We generate the ARNs for each bucket 492 rBucket := parallelRemoteBucket(bucketName) 493 bucketsManagement = append(bucketsManagement, rBucket) 494 } 495 496 resultsList := []RemoteBucketResult{} 497 for _, result := range bucketsManagement { 498 res := <-result 499 resultsList = append(resultsList, res) 500 } 501 502 return resultsList 503 } 504 505 func setMultiBucketReplicationResponse(session *models.Principal, params bucketApi.SetMultiBucketReplicationParams) (*models.MultiBucketResponseState, *CodedAPIError) { 506 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 507 defer cancel() 508 509 mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) 510 if err != nil { 511 return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err)) 512 } 513 adminClient := AdminClient{Client: mAdmin} 514 515 mClient, err := newMinioClient(session, getClientIP(params.HTTPRequest)) 516 if err != nil { 517 return nil, ErrorWithContext(ctx, fmt.Errorf("error creating MinIO Client: %v", err)) 518 } 519 // create a minioClient interface implementation 520 // defining the client to be used 521 mnClient := minioClient{client: mClient} 522 523 replicationResults := setMultiBucketReplication(ctx, session, adminClient, mnClient, params) 524 525 if replicationResults == nil { 526 return nil, ErrorWithContext(ctx, errors.New("error setting buckets replication")) 527 } 528 529 resParsed := []*models.MultiBucketResponseItem{} 530 531 for _, repResult := range replicationResults { 532 responseItem := models.MultiBucketResponseItem{ 533 ErrorString: repResult.Error, 534 OriginBucket: repResult.OriginBucket, 535 TargetBucket: repResult.TargetBucket, 536 } 537 538 resParsed = append(resParsed, &responseItem) 539 } 540 541 resultsParsed := models.MultiBucketResponseState{ 542 ReplicationState: resParsed, 543 } 544 545 return &resultsParsed, nil 546 } 547 548 func listExternalBucketsResponse(params bucketApi.ListExternalBucketsParams) (*models.ListBucketsResponse, *CodedAPIError) { 549 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 550 defer cancel() 551 remoteAdmin, err := newAdminFromCreds(*params.Body.AccessKey, *params.Body.SecretKey, *params.Body.TargetURL, *params.Body.UseTLS) 552 if err != nil { 553 return nil, ErrorWithContext(ctx, err) 554 } 555 return listExternalBuckets(ctx, AdminClient{Client: remoteAdmin}) 556 } 557 558 func listExternalBuckets(ctx context.Context, client MinioAdmin) (*models.ListBucketsResponse, *CodedAPIError) { 559 buckets, err := getAccountBuckets(ctx, client) 560 if err != nil { 561 return nil, ErrorWithContext(ctx, err) 562 } 563 564 return &models.ListBucketsResponse{ 565 Buckets: buckets, 566 Total: int64(len(buckets)), 567 }, nil 568 } 569 570 func getARNFromID(conf *replication.Config, rule string) string { 571 for i := range conf.Rules { 572 if conf.Rules[i].ID == rule { 573 return conf.Rules[i].Destination.Bucket 574 } 575 } 576 return "" 577 } 578 579 func getARNsFromIDs(conf *replication.Config, rules []string) []string { 580 temp := make(map[string]string) 581 for i := range conf.Rules { 582 temp[conf.Rules[i].ID] = conf.Rules[i].Destination.Bucket 583 } 584 var retval []string 585 for i := range rules { 586 if val, ok := temp[rules[i]]; ok { 587 retval = append(retval, val) 588 } 589 } 590 return retval 591 } 592 593 func deleteReplicationRule(ctx context.Context, session *models.Principal, bucketName, ruleID string) error { 594 clientIP := utils.ClientIPFromContext(ctx) 595 mClient, err := newMinioClient(session, clientIP) 596 if err != nil { 597 return fmt.Errorf("error creating MinIO Client: %v", err) 598 } 599 // create a minioClient interface implementation 600 // defining the client to be used 601 minClient := minioClient{client: mClient} 602 603 cfg, err := minClient.getBucketReplication(ctx, bucketName) 604 if err != nil { 605 ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err)) 606 } 607 608 s3Client, err := newS3BucketClient(session, bucketName, "", clientIP) 609 if err != nil { 610 return fmt.Errorf("error creating S3Client: %v", err) 611 } 612 mAdmin, err := NewMinioAdminClient(ctx, session) 613 if err != nil { 614 return fmt.Errorf("error creating Admin Client: %v", err) 615 } 616 admClient := AdminClient{Client: mAdmin} 617 618 // create a mc S3Client interface implementation 619 // defining the client to be used 620 mcClient := mcClient{client: s3Client} 621 622 opts := replication.Options{ 623 ID: ruleID, 624 Op: replication.RemoveOption, 625 } 626 627 err2 := mcClient.setReplication(ctx, &cfg, opts) 628 if err2 != nil { 629 return err2.Cause 630 } 631 632 // Replication rule was successfully deleted. We remove remote bucket 633 err3 := deleteRemoteBucket(ctx, admClient, bucketName, getARNFromID(&cfg, ruleID)) 634 if err3 != nil { 635 return err3 636 } 637 638 return nil 639 } 640 641 func deleteAllReplicationRules(ctx context.Context, session *models.Principal, bucketName string) error { 642 clientIP := utils.ClientIPFromContext(ctx) 643 644 s3Client, err := newS3BucketClient(session, bucketName, "", clientIP) 645 if err != nil { 646 return fmt.Errorf("error creating S3Client: %v", err) 647 } 648 // create a mc S3Client interface implementation 649 // defining the client to be used 650 mcClient := mcClient{client: s3Client} 651 mClient, err := newMinioClient(session, clientIP) 652 if err != nil { 653 return fmt.Errorf("error creating MinIO Client: %v", err) 654 } 655 // create a minioClient interface implementation 656 // defining the client to be used 657 minClient := minioClient{client: mClient} 658 659 cfg, err := minClient.getBucketReplication(ctx, bucketName) 660 if err != nil { 661 ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err)) 662 } 663 664 mAdmin, err := NewMinioAdminClient(ctx, session) 665 if err != nil { 666 return fmt.Errorf("error creating Admin Client: %v", err) 667 } 668 admClient := AdminClient{Client: mAdmin} 669 670 err2 := mcClient.deleteAllReplicationRules(ctx) 671 672 if err2 != nil { 673 return err2.ToGoError() 674 } 675 676 for i := range cfg.Rules { 677 err3 := deleteRemoteBucket(ctx, admClient, bucketName, cfg.Rules[i].Destination.Bucket) 678 if err3 != nil { 679 return err3 680 } 681 } 682 683 return nil 684 } 685 686 func deleteSelectedReplicationRules(ctx context.Context, session *models.Principal, bucketName string, rules []string) error { 687 clientIP := utils.ClientIPFromContext(ctx) 688 mClient, err := newMinioClient(session, clientIP) 689 if err != nil { 690 return fmt.Errorf("error creating MinIO Client: %v", err) 691 } 692 // create a minioClient interface implementation 693 // defining the client to be used 694 minClient := minioClient{client: mClient} 695 696 cfg, err := minClient.getBucketReplication(ctx, bucketName) 697 if err != nil { 698 ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err)) 699 } 700 701 s3Client, err := newS3BucketClient(session, bucketName, "", clientIP) 702 if err != nil { 703 return fmt.Errorf("error creating S3Client: %v", err) 704 } 705 // create a mc S3Client interface implementation 706 // defining the client to be used 707 mcClient := mcClient{client: s3Client} 708 709 mAdmin, err := NewMinioAdminClient(ctx, session) 710 if err != nil { 711 return fmt.Errorf("error creating Admin Client: %v", err) 712 } 713 admClient := AdminClient{Client: mAdmin} 714 715 ARNs := getARNsFromIDs(&cfg, rules) 716 717 for i := range rules { 718 opts := replication.Options{ 719 ID: rules[i], 720 Op: replication.RemoveOption, 721 } 722 err2 := mcClient.setReplication(ctx, &cfg, opts) 723 if err2 != nil { 724 return err2.Cause 725 } 726 727 // In case replication rule was deleted successfully, we remove the remote bucket ARN 728 err3 := deleteRemoteBucket(ctx, admClient, bucketName, ARNs[i]) 729 if err3 != nil { 730 return err3 731 } 732 } 733 return nil 734 } 735 736 func deleteReplicationRuleResponse(session *models.Principal, params bucketApi.DeleteBucketReplicationRuleParams) *CodedAPIError { 737 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 738 defer cancel() 739 ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest)) 740 err := deleteReplicationRule(ctx, session, params.BucketName, params.RuleID) 741 if err != nil { 742 return ErrorWithContext(ctx, err) 743 } 744 return nil 745 } 746 747 func deleteBucketReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteAllReplicationRulesParams) *CodedAPIError { 748 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 749 defer cancel() 750 ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest)) 751 err := deleteAllReplicationRules(ctx, session, params.BucketName) 752 if err != nil { 753 return ErrorWithContext(ctx, err) 754 } 755 return nil 756 } 757 758 func deleteSelectedReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteSelectedReplicationRulesParams) *CodedAPIError { 759 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 760 defer cancel() 761 762 ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest)) 763 764 err := deleteSelectedReplicationRules(ctx, session, params.BucketName, params.Rules.Rules) 765 if err != nil { 766 return ErrorWithContext(ctx, err) 767 } 768 return nil 769 } 770 771 func updateBucketReplicationResponse(session *models.Principal, params bucketApi.UpdateMultiBucketReplicationParams) *CodedAPIError { 772 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 773 defer cancel() 774 775 mClient, err := newMinioClient(session, getClientIP(params.HTTPRequest)) 776 if err != nil { 777 return ErrorWithContext(ctx, err) 778 } 779 // create a minioClient interface implementation 780 // defining the client to be used 781 minClient := minioClient{client: mClient} 782 783 err = editBucketReplicationItem( 784 ctx, 785 session, 786 minClient, 787 params.RuleID, 788 params.BucketName, 789 params.Body.Prefix, 790 params.Body.Arn, 791 params.Body.RuleState, 792 params.Body.ReplicateDeleteMarkers, 793 params.Body.ReplicateDeletes, 794 params.Body.ReplicateMetadata, 795 params.Body.ReplicateExistingObjects, 796 params.Body.Tags, 797 params.Body.Priority, 798 params.Body.StorageClass) 799 if err != nil { 800 return ErrorWithContext(ctx, err) 801 } 802 803 return nil 804 }