github.com/minio/console@v1.4.1/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, repExistingObj, 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 existingRepStatus := "disable" 341 if repExistingObj { 342 existingRepStatus = "enable" 343 } 344 345 opts := replication.Options{ 346 Priority: fmt.Sprintf("%d", maxPrio), 347 RuleStatus: "enable", 348 DestBucket: destinationARN, 349 Op: replication.AddOption, 350 TagString: tags, 351 ExistingObjectReplicate: existingRepStatus, 352 ReplicateDeleteMarkers: repDelMarkStatus, 353 ReplicateDeletes: repDelsStatus, 354 ReplicaSync: repMetaStatus, 355 StorageClass: storageClass, 356 } 357 358 err2 := mcClient.setReplication(ctx, &cfg, opts) 359 if err2 != nil { 360 ErrorWithContext(ctx, fmt.Errorf("error creating replication for bucket: %v", err2.Cause)) 361 return err2.Cause 362 } 363 return nil 364 } 365 366 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 { 367 // we will tolerate this call failing 368 cfg, err := minClient.getBucketReplication(ctx, bucketName) 369 if err != nil { 370 ErrorWithContext(ctx, fmt.Errorf("error fetching replication configuration for bucket %s: %v", bucketName, err)) 371 } 372 373 maxPrio := int(priority) 374 375 clientIP := utils.ClientIPFromContext(ctx) 376 s3Client, err := newS3BucketClient(session, bucketName, prefix, clientIP) 377 if err != nil { 378 return fmt.Errorf("error creating S3Client: %v", err) 379 } 380 // create a mc S3Client interface implementation 381 // defining the client to be used 382 mcClient := mcClient{client: s3Client} 383 384 ruleState := "disable" 385 if ruleStatus { 386 ruleState = "enable" 387 } 388 389 repDelMarkStatus := "disable" 390 if repDelMark { 391 repDelMarkStatus = "enable" 392 } 393 394 repDelsStatus := "disable" 395 if repDels { 396 repDelsStatus = "enable" 397 } 398 399 repMetaStatus := "disable" 400 if repMeta { 401 repMetaStatus = "enable" 402 } 403 404 existingRepStatus := "disable" 405 if existingObjectRep { 406 existingRepStatus = "enable" 407 } 408 409 opts := replication.Options{ 410 ID: ruleID, 411 Priority: fmt.Sprintf("%d", maxPrio), 412 RuleStatus: ruleState, 413 DestBucket: destinationARN, 414 Op: replication.SetOption, 415 TagString: tags, 416 IsTagSet: true, 417 ExistingObjectReplicate: existingRepStatus, 418 ReplicateDeleteMarkers: repDelMarkStatus, 419 ReplicateDeletes: repDelsStatus, 420 ReplicaSync: repMetaStatus, 421 StorageClass: storageClass, 422 IsSCSet: true, 423 } 424 425 err2 := mcClient.setReplication(ctx, &cfg, opts) 426 if err2 != nil { 427 return fmt.Errorf("error modifying replication for bucket: %v", err2.Cause) 428 } 429 return nil 430 } 431 432 func setMultiBucketReplication(ctx context.Context, session *models.Principal, client MinioAdmin, minClient minioClient, params bucketApi.SetMultiBucketReplicationParams) []RemoteBucketResult { 433 bucketsRelation := params.Body.BucketsRelation 434 435 // Parallel remote bucket adding 436 parallelRemoteBucket := func(bucketRelationData *models.MultiBucketsRelation) chan RemoteBucketResult { 437 remoteProc := make(chan RemoteBucketResult) 438 sourceBucket := bucketRelationData.OriginBucket 439 targetBucket := bucketRelationData.DestinationBucket 440 441 go func() { 442 defer close(remoteProc) 443 444 createRemoteBucketParams := models.CreateRemoteBucket{ 445 AccessKey: params.Body.AccessKey, 446 SecretKey: params.Body.SecretKey, 447 SourceBucket: &sourceBucket, 448 TargetBucket: &targetBucket, 449 Region: params.Body.Region, 450 TargetURL: params.Body.TargetURL, 451 SyncMode: params.Body.SyncMode, 452 Bandwidth: params.Body.Bandwidth, 453 HealthCheckPeriod: params.Body.HealthCheckPeriod, 454 } 455 456 // We add the remote bucket reference & store the arn or errors returned 457 arn, err := addRemoteBucket(ctx, client, createRemoteBucketParams) 458 459 if err == nil { 460 err = addBucketReplicationItem( 461 ctx, 462 session, 463 minClient, 464 sourceBucket, 465 params.Body.Prefix, 466 arn, 467 params.Body.ReplicateExistingObjects, 468 params.Body.ReplicateDeleteMarkers, 469 params.Body.ReplicateDeletes, 470 params.Body.ReplicateMetadata, 471 params.Body.Tags, 472 params.Body.Priority, 473 params.Body.StorageClass) 474 } 475 476 errorReturn := "" 477 478 if err != nil { 479 deleteRemoteBucket(ctx, client, sourceBucket, arn) 480 errorReturn = err.Error() 481 } 482 483 retParams := RemoteBucketResult{ 484 OriginBucket: sourceBucket, 485 TargetBucket: targetBucket, 486 Error: errorReturn, 487 } 488 489 remoteProc <- retParams 490 }() 491 return remoteProc 492 } 493 494 var bucketsManagement []chan RemoteBucketResult 495 496 for _, bucketName := range bucketsRelation { 497 // We generate the ARNs for each bucket 498 rBucket := parallelRemoteBucket(bucketName) 499 bucketsManagement = append(bucketsManagement, rBucket) 500 } 501 502 resultsList := []RemoteBucketResult{} 503 for _, result := range bucketsManagement { 504 res := <-result 505 resultsList = append(resultsList, res) 506 } 507 508 return resultsList 509 } 510 511 func setMultiBucketReplicationResponse(session *models.Principal, params bucketApi.SetMultiBucketReplicationParams) (*models.MultiBucketResponseState, *CodedAPIError) { 512 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 513 defer cancel() 514 515 mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) 516 if err != nil { 517 return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err)) 518 } 519 adminClient := AdminClient{Client: mAdmin} 520 521 mClient, err := newMinioClient(session, getClientIP(params.HTTPRequest)) 522 if err != nil { 523 return nil, ErrorWithContext(ctx, fmt.Errorf("error creating MinIO Client: %v", err)) 524 } 525 // create a minioClient interface implementation 526 // defining the client to be used 527 mnClient := minioClient{client: mClient} 528 529 replicationResults := setMultiBucketReplication(ctx, session, adminClient, mnClient, params) 530 531 if replicationResults == nil { 532 return nil, ErrorWithContext(ctx, errors.New("error setting buckets replication")) 533 } 534 535 resParsed := []*models.MultiBucketResponseItem{} 536 537 for _, repResult := range replicationResults { 538 responseItem := models.MultiBucketResponseItem{ 539 ErrorString: repResult.Error, 540 OriginBucket: repResult.OriginBucket, 541 TargetBucket: repResult.TargetBucket, 542 } 543 544 resParsed = append(resParsed, &responseItem) 545 } 546 547 resultsParsed := models.MultiBucketResponseState{ 548 ReplicationState: resParsed, 549 } 550 551 return &resultsParsed, nil 552 } 553 554 func listExternalBucketsResponse(params bucketApi.ListExternalBucketsParams) (*models.ListBucketsResponse, *CodedAPIError) { 555 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 556 defer cancel() 557 remoteAdmin, err := newAdminFromCreds(*params.Body.AccessKey, *params.Body.SecretKey, *params.Body.TargetURL, *params.Body.UseTLS) 558 if err != nil { 559 return nil, ErrorWithContext(ctx, err) 560 } 561 return listExternalBuckets(ctx, AdminClient{Client: remoteAdmin}) 562 } 563 564 func listExternalBuckets(ctx context.Context, client MinioAdmin) (*models.ListBucketsResponse, *CodedAPIError) { 565 buckets, err := getAccountBuckets(ctx, client) 566 if err != nil { 567 return nil, ErrorWithContext(ctx, err) 568 } 569 570 return &models.ListBucketsResponse{ 571 Buckets: buckets, 572 Total: int64(len(buckets)), 573 }, nil 574 } 575 576 func getARNFromID(conf *replication.Config, rule string) string { 577 for i := range conf.Rules { 578 if conf.Rules[i].ID == rule { 579 return conf.Rules[i].Destination.Bucket 580 } 581 } 582 return "" 583 } 584 585 func getARNsFromIDs(conf *replication.Config, rules []string) []string { 586 temp := make(map[string]string) 587 for i := range conf.Rules { 588 temp[conf.Rules[i].ID] = conf.Rules[i].Destination.Bucket 589 } 590 var retval []string 591 for i := range rules { 592 if val, ok := temp[rules[i]]; ok { 593 retval = append(retval, val) 594 } 595 } 596 return retval 597 } 598 599 func deleteReplicationRule(ctx context.Context, session *models.Principal, bucketName, ruleID string) error { 600 clientIP := utils.ClientIPFromContext(ctx) 601 mClient, err := newMinioClient(session, clientIP) 602 if err != nil { 603 return fmt.Errorf("error creating MinIO Client: %v", err) 604 } 605 // create a minioClient interface implementation 606 // defining the client to be used 607 minClient := minioClient{client: mClient} 608 609 cfg, err := minClient.getBucketReplication(ctx, bucketName) 610 if err != nil { 611 ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err)) 612 } 613 614 s3Client, err := newS3BucketClient(session, bucketName, "", clientIP) 615 if err != nil { 616 return fmt.Errorf("error creating S3Client: %v", err) 617 } 618 mAdmin, err := NewMinioAdminClient(ctx, session) 619 if err != nil { 620 return fmt.Errorf("error creating Admin Client: %v", err) 621 } 622 admClient := AdminClient{Client: mAdmin} 623 624 // create a mc S3Client interface implementation 625 // defining the client to be used 626 mcClient := mcClient{client: s3Client} 627 628 opts := replication.Options{ 629 ID: ruleID, 630 Op: replication.RemoveOption, 631 } 632 633 err2 := mcClient.setReplication(ctx, &cfg, opts) 634 if err2 != nil { 635 return err2.Cause 636 } 637 638 // Replication rule was successfully deleted. We remove remote bucket 639 err3 := deleteRemoteBucket(ctx, admClient, bucketName, getARNFromID(&cfg, ruleID)) 640 if err3 != nil { 641 return err3 642 } 643 644 return nil 645 } 646 647 func deleteAllReplicationRules(ctx context.Context, session *models.Principal, bucketName string) error { 648 clientIP := utils.ClientIPFromContext(ctx) 649 650 s3Client, err := newS3BucketClient(session, bucketName, "", clientIP) 651 if err != nil { 652 return fmt.Errorf("error creating S3Client: %v", err) 653 } 654 // create a mc S3Client interface implementation 655 // defining the client to be used 656 mcClient := mcClient{client: s3Client} 657 mClient, err := newMinioClient(session, clientIP) 658 if err != nil { 659 return fmt.Errorf("error creating MinIO Client: %v", err) 660 } 661 // create a minioClient interface implementation 662 // defining the client to be used 663 minClient := minioClient{client: mClient} 664 665 cfg, err := minClient.getBucketReplication(ctx, bucketName) 666 if err != nil { 667 ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err)) 668 } 669 670 mAdmin, err := NewMinioAdminClient(ctx, session) 671 if err != nil { 672 return fmt.Errorf("error creating Admin Client: %v", err) 673 } 674 admClient := AdminClient{Client: mAdmin} 675 676 err2 := mcClient.deleteAllReplicationRules(ctx) 677 678 if err2 != nil { 679 return err2.ToGoError() 680 } 681 682 for i := range cfg.Rules { 683 err3 := deleteRemoteBucket(ctx, admClient, bucketName, cfg.Rules[i].Destination.Bucket) 684 if err3 != nil { 685 return err3 686 } 687 } 688 689 return nil 690 } 691 692 func deleteSelectedReplicationRules(ctx context.Context, session *models.Principal, bucketName string, rules []string) error { 693 clientIP := utils.ClientIPFromContext(ctx) 694 mClient, err := newMinioClient(session, clientIP) 695 if err != nil { 696 return fmt.Errorf("error creating MinIO Client: %v", err) 697 } 698 // create a minioClient interface implementation 699 // defining the client to be used 700 minClient := minioClient{client: mClient} 701 702 cfg, err := minClient.getBucketReplication(ctx, bucketName) 703 if err != nil { 704 ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err)) 705 } 706 707 s3Client, err := newS3BucketClient(session, bucketName, "", clientIP) 708 if err != nil { 709 return fmt.Errorf("error creating S3Client: %v", err) 710 } 711 // create a mc S3Client interface implementation 712 // defining the client to be used 713 mcClient := mcClient{client: s3Client} 714 715 mAdmin, err := NewMinioAdminClient(ctx, session) 716 if err != nil { 717 return fmt.Errorf("error creating Admin Client: %v", err) 718 } 719 admClient := AdminClient{Client: mAdmin} 720 721 ARNs := getARNsFromIDs(&cfg, rules) 722 723 for i := range rules { 724 opts := replication.Options{ 725 ID: rules[i], 726 Op: replication.RemoveOption, 727 } 728 err2 := mcClient.setReplication(ctx, &cfg, opts) 729 if err2 != nil { 730 return err2.Cause 731 } 732 733 // In case replication rule was deleted successfully, we remove the remote bucket ARN 734 err3 := deleteRemoteBucket(ctx, admClient, bucketName, ARNs[i]) 735 if err3 != nil { 736 return err3 737 } 738 } 739 return nil 740 } 741 742 func deleteReplicationRuleResponse(session *models.Principal, params bucketApi.DeleteBucketReplicationRuleParams) *CodedAPIError { 743 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 744 defer cancel() 745 ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest)) 746 err := deleteReplicationRule(ctx, session, params.BucketName, params.RuleID) 747 if err != nil { 748 return ErrorWithContext(ctx, err) 749 } 750 return nil 751 } 752 753 func deleteBucketReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteAllReplicationRulesParams) *CodedAPIError { 754 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 755 defer cancel() 756 ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest)) 757 err := deleteAllReplicationRules(ctx, session, params.BucketName) 758 if err != nil { 759 return ErrorWithContext(ctx, err) 760 } 761 return nil 762 } 763 764 func deleteSelectedReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteSelectedReplicationRulesParams) *CodedAPIError { 765 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 766 defer cancel() 767 768 ctx = context.WithValue(ctx, utils.ContextClientIP, getClientIP(params.HTTPRequest)) 769 770 err := deleteSelectedReplicationRules(ctx, session, params.BucketName, params.Rules.Rules) 771 if err != nil { 772 return ErrorWithContext(ctx, err) 773 } 774 return nil 775 } 776 777 func updateBucketReplicationResponse(session *models.Principal, params bucketApi.UpdateMultiBucketReplicationParams) *CodedAPIError { 778 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 779 defer cancel() 780 781 mClient, err := newMinioClient(session, getClientIP(params.HTTPRequest)) 782 if err != nil { 783 return ErrorWithContext(ctx, err) 784 } 785 // create a minioClient interface implementation 786 // defining the client to be used 787 minClient := minioClient{client: mClient} 788 789 err = editBucketReplicationItem( 790 ctx, 791 session, 792 minClient, 793 params.RuleID, 794 params.BucketName, 795 params.Body.Prefix, 796 params.Body.Arn, 797 params.Body.RuleState, 798 params.Body.ReplicateDeleteMarkers, 799 params.Body.ReplicateDeletes, 800 params.Body.ReplicateMetadata, 801 params.Body.ReplicateExistingObjects, 802 params.Body.Tags, 803 params.Body.Priority, 804 params.Body.StorageClass) 805 if err != nil { 806 return ErrorWithContext(ctx, err) 807 } 808 809 return nil 810 }