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  }