github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/peer-rest-client.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"encoding/gob"
    23  	"encoding/hex"
    24  	"encoding/json"
    25  	"errors"
    26  	"fmt"
    27  	"io"
    28  	"net/url"
    29  	"strconv"
    30  	"sync/atomic"
    31  	"time"
    32  
    33  	"github.com/minio/madmin-go/v3"
    34  	"github.com/minio/minio/internal/bucket/bandwidth"
    35  	"github.com/minio/minio/internal/grid"
    36  	xhttp "github.com/minio/minio/internal/http"
    37  	"github.com/minio/minio/internal/logger"
    38  	"github.com/minio/minio/internal/rest"
    39  	xnet "github.com/minio/pkg/v2/net"
    40  )
    41  
    42  // client to talk to peer Nodes.
    43  type peerRESTClient struct {
    44  	host       *xnet.Host
    45  	restClient *rest.Client
    46  	gridHost   string
    47  	// Function that returns the grid connection for this peer when initialized.
    48  	// Will return nil if the grid connection is not initialized yet.
    49  	gridConn func() *grid.Connection
    50  }
    51  
    52  // Returns a peer rest client.
    53  func newPeerRESTClient(peer *xnet.Host, gridHost string) *peerRESTClient {
    54  	scheme := "http"
    55  	if globalIsTLS {
    56  		scheme = "https"
    57  	}
    58  
    59  	serverURL := &url.URL{
    60  		Scheme: scheme,
    61  		Host:   peer.String(),
    62  		Path:   peerRESTPath,
    63  	}
    64  
    65  	restClient := rest.NewClient(serverURL, globalInternodeTransport, newCachedAuthToken())
    66  	// Use a separate client to avoid recursive calls.
    67  	healthClient := rest.NewClient(serverURL, globalInternodeTransport, newCachedAuthToken())
    68  	healthClient.NoMetrics = true
    69  
    70  	// Construct a new health function.
    71  	restClient.HealthCheckFn = func() bool {
    72  		ctx, cancel := context.WithTimeout(context.Background(), restClient.HealthCheckTimeout)
    73  		defer cancel()
    74  		respBody, err := healthClient.Call(ctx, peerRESTMethodHealth, nil, nil, -1)
    75  		xhttp.DrainBody(respBody)
    76  		return !isNetworkError(err)
    77  	}
    78  	var gridConn atomic.Pointer[grid.Connection]
    79  
    80  	return &peerRESTClient{
    81  		host: peer, restClient: restClient, gridHost: gridHost,
    82  		gridConn: func() *grid.Connection {
    83  			// Lazy initialization of grid connection.
    84  			// When we create this peer client, the grid connection is likely not yet initialized.
    85  			if gridHost == "" {
    86  				logger.LogOnceIf(context.Background(), fmt.Errorf("gridHost is empty for peer %s", peer.String()), peer.String()+":gridHost")
    87  				return nil
    88  			}
    89  			gc := gridConn.Load()
    90  			if gc != nil {
    91  				return gc
    92  			}
    93  			gm := globalGrid.Load()
    94  			if gm == nil {
    95  				return nil
    96  			}
    97  			gc = gm.Connection(gridHost)
    98  			if gc == nil {
    99  				logger.LogOnceIf(context.Background(), fmt.Errorf("gridHost %q not found for peer %s", gridHost, peer.String()), peer.String()+":gridHost")
   100  				return nil
   101  			}
   102  			gridConn.Store(gc)
   103  			return gc
   104  		},
   105  	}
   106  }
   107  
   108  // Wrapper to restClient.Call to handle network errors, in case of network error the connection is marked disconnected
   109  // permanently. The only way to restore the connection is at the xl-sets layer by xlsets.monitorAndConnectEndpoints()
   110  // after verifying format.json
   111  func (client *peerRESTClient) call(method string, values url.Values, body io.Reader, length int64) (respBody io.ReadCloser, err error) {
   112  	return client.callWithContext(GlobalContext, method, values, body, length)
   113  }
   114  
   115  // Wrapper to restClient.Call to handle network errors, in case of network error the connection is marked disconnected
   116  // permanently. The only way to restore the connection is at the xl-sets layer by xlsets.monitorAndConnectEndpoints()
   117  // after verifying format.json
   118  func (client *peerRESTClient) callWithContext(ctx context.Context, method string, values url.Values, body io.Reader, length int64) (respBody io.ReadCloser, err error) {
   119  	if client == nil || !client.IsOnline() {
   120  		return nil, errPeerNotReachable
   121  	}
   122  
   123  	if values == nil {
   124  		values = make(url.Values)
   125  	}
   126  
   127  	respBody, err = client.restClient.Call(ctx, method, values, body, length)
   128  	if err == nil {
   129  		return respBody, nil
   130  	}
   131  
   132  	return nil, err
   133  }
   134  
   135  // Stringer provides a canonicalized representation of node.
   136  func (client *peerRESTClient) String() string {
   137  	return client.host.String()
   138  }
   139  
   140  // IsOnline returns true if the peer client is online.
   141  func (client *peerRESTClient) IsOnline() bool {
   142  	return client.restClient.IsOnline()
   143  }
   144  
   145  // Close - marks the client as closed.
   146  func (client *peerRESTClient) Close() error {
   147  	client.restClient.Close()
   148  	return nil
   149  }
   150  
   151  // GetLocks - fetch older locks for a remote node.
   152  func (client *peerRESTClient) GetLocks() (lockMap map[string][]lockRequesterInfo, err error) {
   153  	resp, err := getLocksRPC.Call(context.Background(), client.gridConn(), grid.NewMSS())
   154  	if err != nil || resp == nil {
   155  		return nil, err
   156  	}
   157  	return *resp, nil
   158  }
   159  
   160  // LocalStorageInfo - fetch server information for a remote node.
   161  func (client *peerRESTClient) LocalStorageInfo(metrics bool) (info StorageInfo, err error) {
   162  	resp, err := localStorageInfoRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   163  		peerRESTMetrics: strconv.FormatBool(metrics),
   164  	}))
   165  	return resp.ValueOrZero(), err
   166  }
   167  
   168  // ServerInfo - fetch server information for a remote node.
   169  func (client *peerRESTClient) ServerInfo(metrics bool) (info madmin.ServerProperties, err error) {
   170  	resp, err := serverInfoRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{peerRESTMetrics: strconv.FormatBool(metrics)}))
   171  	return resp.ValueOrZero(), err
   172  }
   173  
   174  // GetCPUs - fetch CPU information for a remote node.
   175  func (client *peerRESTClient) GetCPUs(ctx context.Context) (info madmin.CPUs, err error) {
   176  	resp, err := getCPUsHandler.Call(ctx, client.gridConn(), grid.NewMSS())
   177  	return resp.ValueOrZero(), err
   178  }
   179  
   180  // GetNetInfo - fetch network information for a remote node.
   181  func (client *peerRESTClient) GetNetInfo(ctx context.Context) (info madmin.NetInfo, err error) {
   182  	resp, err := getNetInfoRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   183  	return resp.ValueOrZero(), err
   184  }
   185  
   186  // GetPartitions - fetch disk partition information for a remote node.
   187  func (client *peerRESTClient) GetPartitions(ctx context.Context) (info madmin.Partitions, err error) {
   188  	resp, err := getPartitionsRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   189  	return resp.ValueOrZero(), err
   190  }
   191  
   192  // GetOSInfo - fetch OS information for a remote node.
   193  func (client *peerRESTClient) GetOSInfo(ctx context.Context) (info madmin.OSInfo, err error) {
   194  	resp, err := getOSInfoRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   195  	return resp.ValueOrZero(), err
   196  }
   197  
   198  // GetSELinuxInfo - fetch SELinux information for a remote node.
   199  func (client *peerRESTClient) GetSELinuxInfo(ctx context.Context) (info madmin.SysServices, err error) {
   200  	resp, err := getSysServicesRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   201  	return resp.ValueOrZero(), err
   202  }
   203  
   204  // GetSysConfig - fetch sys config for a remote node.
   205  func (client *peerRESTClient) GetSysConfig(ctx context.Context) (info madmin.SysConfig, err error) {
   206  	sent := time.Now()
   207  	resp, err := getSysConfigRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   208  	info = resp.ValueOrZero()
   209  	if ti, ok := info.Config["time-info"].(madmin.TimeInfo); ok {
   210  		rt := int32(time.Since(sent).Milliseconds())
   211  		ti.RoundtripDuration = rt
   212  		info.Config["time-info"] = ti
   213  	}
   214  	return info, err
   215  }
   216  
   217  // GetSysErrors - fetch sys errors for a remote node.
   218  func (client *peerRESTClient) GetSysErrors(ctx context.Context) (info madmin.SysErrors, err error) {
   219  	resp, err := getSysErrorsRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   220  	return resp.ValueOrZero(), err
   221  }
   222  
   223  // GetMemInfo - fetch memory information for a remote node.
   224  func (client *peerRESTClient) GetMemInfo(ctx context.Context) (info madmin.MemInfo, err error) {
   225  	resp, err := getMemInfoRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   226  	return resp.ValueOrZero(), err
   227  }
   228  
   229  // GetMetrics - fetch metrics from a remote node.
   230  func (client *peerRESTClient) GetMetrics(ctx context.Context, t madmin.MetricType, opts collectMetricsOpts) (info madmin.RealtimeMetrics, err error) {
   231  	values := make(url.Values)
   232  	values.Set(peerRESTMetricsTypes, strconv.FormatUint(uint64(t), 10))
   233  	for disk := range opts.disks {
   234  		values.Add(peerRESTDisk, disk)
   235  	}
   236  	for host := range opts.hosts {
   237  		values.Add(peerRESTHost, host)
   238  	}
   239  	values.Set(peerRESTJobID, opts.jobID)
   240  	values.Set(peerRESTDepID, opts.depID)
   241  	v, err := getMetricsRPC.Call(ctx, client.gridConn(), grid.NewURLValuesWith(values))
   242  	return v.ValueOrZero(), err
   243  }
   244  
   245  // GetProcInfo - fetch MinIO process information for a remote node.
   246  func (client *peerRESTClient) GetProcInfo(ctx context.Context) (info madmin.ProcInfo, err error) {
   247  	resp, err := getProcInfoRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   248  	return resp.ValueOrZero(), err
   249  }
   250  
   251  // StartProfiling - Issues profiling command on the peer node.
   252  func (client *peerRESTClient) StartProfiling(profiler string) error {
   253  	values := make(url.Values)
   254  	values.Set(peerRESTProfiler, profiler)
   255  	respBody, err := client.call(peerRESTMethodStartProfiling, values, nil, -1)
   256  	if err != nil {
   257  		return err
   258  	}
   259  	defer xhttp.DrainBody(respBody)
   260  	return nil
   261  }
   262  
   263  // DownloadProfileData - download profiled data from a remote node.
   264  func (client *peerRESTClient) DownloadProfileData() (data map[string][]byte, err error) {
   265  	respBody, err := client.call(peerRESTMethodDownloadProfilingData, nil, nil, -1)
   266  	if err != nil {
   267  		return
   268  	}
   269  	defer xhttp.DrainBody(respBody)
   270  	err = gob.NewDecoder(respBody).Decode(&data)
   271  	return data, err
   272  }
   273  
   274  // GetBucketStats - load bucket statistics
   275  func (client *peerRESTClient) GetBucketStats(bucket string) (BucketStats, error) {
   276  	resp, err := getBucketStatsRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   277  		peerRESTBucket: bucket,
   278  	}))
   279  	if err != nil || resp == nil {
   280  		return BucketStats{}, err
   281  	}
   282  	return *resp, nil
   283  }
   284  
   285  // GetSRMetrics loads site replication metrics, optionally for a specific bucket
   286  func (client *peerRESTClient) GetSRMetrics() (SRMetricsSummary, error) {
   287  	resp, err := getSRMetricsRPC.Call(context.Background(), client.gridConn(), grid.NewMSS())
   288  	if err != nil || resp == nil {
   289  		return SRMetricsSummary{}, err
   290  	}
   291  	return *resp, nil
   292  }
   293  
   294  // GetAllBucketStats - load replication stats for all buckets
   295  func (client *peerRESTClient) GetAllBucketStats() (BucketStatsMap, error) {
   296  	resp, err := getAllBucketStatsRPC.Call(context.Background(), client.gridConn(), grid.NewMSS())
   297  	if err != nil || resp == nil {
   298  		return BucketStatsMap{}, err
   299  	}
   300  	return *resp, nil
   301  }
   302  
   303  // LoadBucketMetadata - load bucket metadata
   304  func (client *peerRESTClient) LoadBucketMetadata(bucket string) error {
   305  	_, err := loadBucketMetadataRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   306  		peerRESTBucket: bucket,
   307  	}))
   308  	return err
   309  }
   310  
   311  // DeleteBucketMetadata - Delete bucket metadata
   312  func (client *peerRESTClient) DeleteBucketMetadata(bucket string) error {
   313  	_, err := deleteBucketMetadataRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   314  		peerRESTBucket: bucket,
   315  	}))
   316  	return err
   317  }
   318  
   319  // DeletePolicy - delete a specific canned policy.
   320  func (client *peerRESTClient) DeletePolicy(policyName string) (err error) {
   321  	_, err = deletePolicyRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   322  		peerRESTPolicy: policyName,
   323  	}))
   324  	return err
   325  }
   326  
   327  // LoadPolicy - reload a specific canned policy.
   328  func (client *peerRESTClient) LoadPolicy(policyName string) (err error) {
   329  	_, err = loadPolicyRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   330  		peerRESTPolicy: policyName,
   331  	}))
   332  	return err
   333  }
   334  
   335  // LoadPolicyMapping - reload a specific policy mapping
   336  func (client *peerRESTClient) LoadPolicyMapping(userOrGroup string, userType IAMUserType, isGroup bool) error {
   337  	_, err := loadPolicyMappingRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   338  		peerRESTUserOrGroup: userOrGroup,
   339  		peerRESTUserType:    strconv.Itoa(int(userType)),
   340  		peerRESTIsGroup:     strconv.FormatBool(isGroup),
   341  	}))
   342  	return err
   343  }
   344  
   345  // DeleteUser - delete a specific user.
   346  func (client *peerRESTClient) DeleteUser(accessKey string) (err error) {
   347  	_, err = deleteUserRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   348  		peerRESTUser: accessKey,
   349  	}))
   350  	return err
   351  }
   352  
   353  // DeleteServiceAccount - delete a specific service account.
   354  func (client *peerRESTClient) DeleteServiceAccount(accessKey string) (err error) {
   355  	_, err = deleteSvcActRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   356  		peerRESTUser: accessKey,
   357  	}))
   358  	return err
   359  }
   360  
   361  // LoadUser - reload a specific user.
   362  func (client *peerRESTClient) LoadUser(accessKey string, temp bool) (err error) {
   363  	_, err = loadUserRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   364  		peerRESTUser:     accessKey,
   365  		peerRESTUserTemp: strconv.FormatBool(temp),
   366  	}))
   367  	return err
   368  }
   369  
   370  // LoadServiceAccount - reload a specific service account.
   371  func (client *peerRESTClient) LoadServiceAccount(accessKey string) (err error) {
   372  	_, err = loadSvcActRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   373  		peerRESTUser: accessKey,
   374  	}))
   375  	return err
   376  }
   377  
   378  // LoadGroup - send load group command to peers.
   379  func (client *peerRESTClient) LoadGroup(group string) error {
   380  	_, err := loadGroupRPC.Call(context.Background(), client.gridConn(), grid.NewMSSWith(map[string]string{
   381  		peerRESTGroup: group,
   382  	}))
   383  	return err
   384  }
   385  
   386  func (client *peerRESTClient) ReloadSiteReplicationConfig(ctx context.Context) error {
   387  	conn := client.gridConn()
   388  	if conn == nil {
   389  		return nil
   390  	}
   391  
   392  	_, err := reloadSiteReplicationConfigRPC.Call(ctx, conn, grid.NewMSS())
   393  	return err
   394  }
   395  
   396  // VerifyBinary - sends verify binary message to remote peers.
   397  func (client *peerRESTClient) VerifyBinary(ctx context.Context, u *url.URL, sha256Sum []byte, releaseInfo string, reader io.Reader) error {
   398  	values := make(url.Values)
   399  	values.Set(peerRESTURL, u.String())
   400  	values.Set(peerRESTSha256Sum, hex.EncodeToString(sha256Sum))
   401  	values.Set(peerRESTReleaseInfo, releaseInfo)
   402  
   403  	respBody, err := client.callWithContext(ctx, peerRESTMethodVerifyBinary, values, reader, -1)
   404  	if err != nil {
   405  		return err
   406  	}
   407  	defer xhttp.DrainBody(respBody)
   408  	return nil
   409  }
   410  
   411  // CommitBinary - sends commit binary message to remote peers.
   412  func (client *peerRESTClient) CommitBinary(ctx context.Context) error {
   413  	respBody, err := client.callWithContext(ctx, peerRESTMethodCommitBinary, nil, nil, -1)
   414  	if err != nil {
   415  		return err
   416  	}
   417  	defer xhttp.DrainBody(respBody)
   418  	return nil
   419  }
   420  
   421  // SignalService - sends signal to peer nodes.
   422  func (client *peerRESTClient) SignalService(sig serviceSignal, subSys string, dryRun bool) error {
   423  	values := grid.NewMSS()
   424  	values.Set(peerRESTSignal, strconv.Itoa(int(sig)))
   425  	values.Set(peerRESTDryRun, strconv.FormatBool(dryRun))
   426  	values.Set(peerRESTSubSys, subSys)
   427  	_, err := signalServiceRPC.Call(context.Background(), client.gridConn(), values)
   428  	return err
   429  }
   430  
   431  func (client *peerRESTClient) BackgroundHealStatus() (madmin.BgHealState, error) {
   432  	resp, err := getBackgroundHealStatusRPC.Call(context.Background(), client.gridConn(), grid.NewMSS())
   433  	return resp.ValueOrZero(), err
   434  }
   435  
   436  // GetMetacacheListing - get a new or existing metacache.
   437  func (client *peerRESTClient) GetMetacacheListing(ctx context.Context, o listPathOptions) (*metacache, error) {
   438  	if client == nil {
   439  		resp := localMetacacheMgr.getBucket(ctx, o.Bucket).findCache(o)
   440  		return &resp, nil
   441  	}
   442  	return getMetacacheListingRPC.Call(ctx, client.gridConn(), &o)
   443  }
   444  
   445  // UpdateMetacacheListing - update an existing metacache it will unconditionally be updated to the new state.
   446  func (client *peerRESTClient) UpdateMetacacheListing(ctx context.Context, m metacache) (metacache, error) {
   447  	if client == nil {
   448  		return localMetacacheMgr.updateCacheEntry(m)
   449  	}
   450  	resp, err := updateMetacacheListingRPC.Call(ctx, client.gridConn(), &m)
   451  	if err != nil || resp == nil {
   452  		return metacache{}, err
   453  	}
   454  	return *resp, nil
   455  }
   456  
   457  func (client *peerRESTClient) ReloadPoolMeta(ctx context.Context) error {
   458  	conn := client.gridConn()
   459  	if conn == nil {
   460  		return nil
   461  	}
   462  	_, err := reloadPoolMetaRPC.Call(ctx, conn, grid.NewMSSWith(map[string]string{}))
   463  	return err
   464  }
   465  
   466  func (client *peerRESTClient) StopRebalance(ctx context.Context) error {
   467  	conn := client.gridConn()
   468  	if conn == nil {
   469  		return nil
   470  	}
   471  	_, err := stopRebalanceRPC.Call(ctx, conn, grid.NewMSSWith(map[string]string{}))
   472  	return err
   473  }
   474  
   475  func (client *peerRESTClient) LoadRebalanceMeta(ctx context.Context, startRebalance bool) error {
   476  	conn := client.gridConn()
   477  	if conn == nil {
   478  		return nil
   479  	}
   480  	_, err := loadRebalanceMetaRPC.Call(ctx, conn, grid.NewMSSWith(map[string]string{
   481  		peerRESTStartRebalance: strconv.FormatBool(startRebalance),
   482  	}))
   483  	return err
   484  }
   485  
   486  func (client *peerRESTClient) LoadTransitionTierConfig(ctx context.Context) error {
   487  	conn := client.gridConn()
   488  	if conn == nil {
   489  		return nil
   490  	}
   491  	_, err := loadTransitionTierConfigRPC.Call(ctx, conn, grid.NewMSSWith(map[string]string{}))
   492  	return err
   493  }
   494  
   495  func (client *peerRESTClient) doTrace(ctx context.Context, traceCh chan<- []byte, traceOpts madmin.ServiceTraceOpts) {
   496  	gridConn := client.gridConn()
   497  	if gridConn == nil {
   498  		return
   499  	}
   500  
   501  	payload, err := json.Marshal(traceOpts)
   502  	if err != nil {
   503  		logger.LogIf(ctx, err)
   504  		return
   505  	}
   506  
   507  	st, err := gridConn.NewStream(ctx, grid.HandlerTrace, payload)
   508  	if err != nil {
   509  		return
   510  	}
   511  	st.Results(func(b []byte) error {
   512  		select {
   513  		case traceCh <- b:
   514  		default:
   515  			// Do not block on slow receivers.
   516  			// Just recycle the buffer.
   517  			grid.PutByteBuffer(b)
   518  		}
   519  		return nil
   520  	})
   521  }
   522  
   523  func (client *peerRESTClient) doListen(ctx context.Context, listenCh chan<- []byte, v url.Values) {
   524  	conn := client.gridConn()
   525  	if conn == nil {
   526  		return
   527  	}
   528  	st, err := listenRPC.Call(ctx, conn, grid.NewURLValuesWith(v))
   529  	if err != nil {
   530  		return
   531  	}
   532  	st.Results(func(b *grid.Bytes) error {
   533  		select {
   534  		case listenCh <- *b:
   535  		default:
   536  			// Do not block on slow receivers.
   537  			b.Recycle()
   538  		}
   539  		return nil
   540  	})
   541  }
   542  
   543  // Listen - listen on peers.
   544  func (client *peerRESTClient) Listen(ctx context.Context, listenCh chan<- []byte, v url.Values) {
   545  	go func() {
   546  		for {
   547  			client.doListen(ctx, listenCh, v)
   548  			select {
   549  			case <-ctx.Done():
   550  				return
   551  			default:
   552  				// There was error in the REST request, retry after sometime as probably the peer is down.
   553  				time.Sleep(5 * time.Second)
   554  			}
   555  		}
   556  	}()
   557  }
   558  
   559  // Trace - send http trace request to peer nodes
   560  func (client *peerRESTClient) Trace(ctx context.Context, traceCh chan<- []byte, traceOpts madmin.ServiceTraceOpts) {
   561  	go func() {
   562  		for {
   563  			// Blocks until context is canceled or an error occurs.
   564  			client.doTrace(ctx, traceCh, traceOpts)
   565  			select {
   566  			case <-ctx.Done():
   567  				return
   568  			default:
   569  				// There was error in the REST request, retry after sometime as probably the peer is down.
   570  				time.Sleep(5 * time.Second)
   571  			}
   572  		}
   573  	}()
   574  }
   575  
   576  func (client *peerRESTClient) doConsoleLog(ctx context.Context, kind madmin.LogMask, logCh chan<- []byte) {
   577  	st, err := consoleLogRPC.Call(ctx, client.gridConn(), grid.NewMSSWith(map[string]string{
   578  		peerRESTLogMask: strconv.Itoa(int(kind)),
   579  	}))
   580  	if err != nil {
   581  		return
   582  	}
   583  	st.Results(func(b *grid.Bytes) error {
   584  		select {
   585  		case logCh <- *b:
   586  		default:
   587  			consoleLogRPC.PutResponse(b)
   588  			// Do not block on slow receivers.
   589  		}
   590  		return nil
   591  	})
   592  }
   593  
   594  // ConsoleLog - sends request to peer nodes to get console logs
   595  func (client *peerRESTClient) ConsoleLog(ctx context.Context, kind madmin.LogMask, logCh chan<- []byte) {
   596  	go func() {
   597  		for {
   598  			client.doConsoleLog(ctx, kind, logCh)
   599  			select {
   600  			case <-ctx.Done():
   601  				return
   602  			default:
   603  				// There was error in the REST request, retry after sometime as probably the peer is down.
   604  				time.Sleep(5 * time.Second)
   605  			}
   606  		}
   607  	}()
   608  }
   609  
   610  // newPeerRestClients creates new peer clients.
   611  // The two slices will point to the same clients,
   612  // but 'all' will contain nil entry for local client.
   613  // The 'all' slice will be in the same order across the cluster.
   614  func newPeerRestClients(endpoints EndpointServerPools) (remote, all []*peerRESTClient) {
   615  	if !globalIsDistErasure {
   616  		// Only useful in distributed setups
   617  		return nil, nil
   618  	}
   619  
   620  	hosts := endpoints.hostsSorted()
   621  	remote = make([]*peerRESTClient, 0, len(hosts))
   622  	all = make([]*peerRESTClient, len(hosts))
   623  	for i, host := range hosts {
   624  		if host == nil {
   625  			continue
   626  		}
   627  		all[i] = newPeerRESTClient(host, endpoints.FindGridHostsFromPeer(host))
   628  		remote = append(remote, all[i])
   629  	}
   630  	if len(all) != len(remote)+1 {
   631  		logger.LogIf(context.Background(), fmt.Errorf("WARNING: Expected number of all hosts (%v) to be remote +1 (%v)", len(all), len(remote)))
   632  	}
   633  	return remote, all
   634  }
   635  
   636  // MonitorBandwidth - send http trace request to peer nodes
   637  func (client *peerRESTClient) MonitorBandwidth(ctx context.Context, buckets []string) (*bandwidth.BucketBandwidthReport, error) {
   638  	values := grid.NewURLValuesWith(map[string][]string{
   639  		peerRESTBuckets: buckets,
   640  	})
   641  	return getBandwidthRPC.Call(ctx, client.gridConn(), values)
   642  }
   643  
   644  func (client *peerRESTClient) GetResourceMetrics(ctx context.Context) (<-chan MetricV2, error) {
   645  	resp, err := getResourceMetricsRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   646  	if err != nil {
   647  		return nil, err
   648  	}
   649  	ch := make(chan MetricV2)
   650  	go func(ch chan<- MetricV2) {
   651  		defer close(ch)
   652  		for _, m := range resp.Value() {
   653  			if m == nil {
   654  				continue
   655  			}
   656  			select {
   657  			case <-ctx.Done():
   658  				return
   659  			case ch <- *m:
   660  			}
   661  		}
   662  	}(ch)
   663  	return ch, nil
   664  }
   665  
   666  func (client *peerRESTClient) GetPeerMetrics(ctx context.Context) (<-chan MetricV2, error) {
   667  	resp, err := getPeerMetricsRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   668  	if err != nil {
   669  		return nil, err
   670  	}
   671  	ch := make(chan MetricV2)
   672  	go func() {
   673  		defer close(ch)
   674  		for _, m := range resp.Value() {
   675  			if m == nil {
   676  				continue
   677  			}
   678  			select {
   679  			case <-ctx.Done():
   680  				return
   681  			case ch <- *m:
   682  			}
   683  		}
   684  	}()
   685  	return ch, nil
   686  }
   687  
   688  func (client *peerRESTClient) GetPeerBucketMetrics(ctx context.Context) (<-chan MetricV2, error) {
   689  	resp, err := getPeerBucketMetricsRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   690  	if err != nil {
   691  		return nil, err
   692  	}
   693  	ch := make(chan MetricV2)
   694  	go func() {
   695  		defer close(ch)
   696  		for _, m := range resp.Value() {
   697  			if m == nil {
   698  				continue
   699  			}
   700  			select {
   701  			case <-ctx.Done():
   702  				return
   703  			case ch <- *m:
   704  			}
   705  		}
   706  	}()
   707  	return ch, nil
   708  }
   709  
   710  func (client *peerRESTClient) SpeedTest(ctx context.Context, opts speedTestOpts) (SpeedTestResult, error) {
   711  	values := make(url.Values)
   712  	values.Set(peerRESTSize, strconv.Itoa(opts.objectSize))
   713  	values.Set(peerRESTConcurrent, strconv.Itoa(opts.concurrency))
   714  	values.Set(peerRESTDuration, opts.duration.String())
   715  	values.Set(peerRESTStorageClass, opts.storageClass)
   716  	values.Set(peerRESTBucket, opts.bucketName)
   717  	values.Set(peerRESTEnableSha256, strconv.FormatBool(opts.enableSha256))
   718  
   719  	respBody, err := client.callWithContext(context.Background(), peerRESTMethodSpeedTest, values, nil, -1)
   720  	if err != nil {
   721  		return SpeedTestResult{}, err
   722  	}
   723  	defer xhttp.DrainBody(respBody)
   724  	waitReader, err := waitForHTTPResponse(respBody)
   725  	if err != nil {
   726  		return SpeedTestResult{}, err
   727  	}
   728  
   729  	var result SpeedTestResult
   730  	err = gob.NewDecoder(waitReader).Decode(&result)
   731  	if err != nil {
   732  		return result, err
   733  	}
   734  	if result.Error != "" {
   735  		return result, errors.New(result.Error)
   736  	}
   737  	return result, nil
   738  }
   739  
   740  func (client *peerRESTClient) DriveSpeedTest(ctx context.Context, opts madmin.DriveSpeedTestOpts) (madmin.DriveSpeedTestResult, error) {
   741  	queryVals := make(url.Values)
   742  	if opts.Serial {
   743  		queryVals.Set("serial", "true")
   744  	}
   745  	queryVals.Set("blocksize", strconv.FormatUint(opts.BlockSize, 10))
   746  	queryVals.Set("filesize", strconv.FormatUint(opts.FileSize, 10))
   747  
   748  	respBody, err := client.callWithContext(ctx, peerRESTMethodDriveSpeedTest, queryVals, nil, -1)
   749  	if err != nil {
   750  		return madmin.DriveSpeedTestResult{}, err
   751  	}
   752  	defer xhttp.DrainBody(respBody)
   753  	waitReader, err := waitForHTTPResponse(respBody)
   754  	if err != nil {
   755  		return madmin.DriveSpeedTestResult{}, err
   756  	}
   757  
   758  	var result madmin.DriveSpeedTestResult
   759  	err = gob.NewDecoder(waitReader).Decode(&result)
   760  	if err != nil {
   761  		return result, err
   762  	}
   763  	if result.Error != "" {
   764  		return result, errors.New(result.Error)
   765  	}
   766  	return result, nil
   767  }
   768  
   769  func (client *peerRESTClient) GetLastDayTierStats(ctx context.Context) (DailyAllTierStats, error) {
   770  	resp, err := getLastDayTierStatsRPC.Call(ctx, client.gridConn(), grid.NewMSS())
   771  	if err != nil || resp == nil {
   772  		return DailyAllTierStats{}, err
   773  	}
   774  	return *resp, nil
   775  }
   776  
   777  // DevNull - Used by netperf to pump data to peer
   778  func (client *peerRESTClient) DevNull(ctx context.Context, r io.Reader) error {
   779  	respBody, err := client.callWithContext(ctx, peerRESTMethodDevNull, nil, r, -1)
   780  	if err != nil {
   781  		return err
   782  	}
   783  	defer xhttp.DrainBody(respBody)
   784  	return err
   785  }
   786  
   787  // Netperf - To initiate netperf on peer
   788  func (client *peerRESTClient) Netperf(ctx context.Context, duration time.Duration) (madmin.NetperfNodeResult, error) {
   789  	var result madmin.NetperfNodeResult
   790  	values := make(url.Values)
   791  	values.Set(peerRESTDuration, duration.String())
   792  	respBody, err := client.callWithContext(context.Background(), peerRESTMethodNetperf, values, nil, -1)
   793  	if err != nil {
   794  		return result, err
   795  	}
   796  	defer xhttp.DrainBody(respBody)
   797  	err = gob.NewDecoder(respBody).Decode(&result)
   798  	return result, err
   799  }
   800  
   801  // GetReplicationMRF - get replication MRF for bucket
   802  func (client *peerRESTClient) GetReplicationMRF(ctx context.Context, bucket string) (chan madmin.ReplicationMRF, error) {
   803  	values := make(url.Values)
   804  	values.Set(peerRESTBucket, bucket)
   805  
   806  	respBody, err := client.callWithContext(ctx, peerRESTMethodGetReplicationMRF, values, nil, -1)
   807  	if err != nil {
   808  		return nil, err
   809  	}
   810  	dec := gob.NewDecoder(respBody)
   811  	ch := make(chan madmin.ReplicationMRF)
   812  	go func(ch chan madmin.ReplicationMRF) {
   813  		defer func() {
   814  			xhttp.DrainBody(respBody)
   815  			close(ch)
   816  		}()
   817  		for {
   818  			var entry madmin.ReplicationMRF
   819  			if err := dec.Decode(&entry); err != nil {
   820  				return
   821  			}
   822  			select {
   823  			case <-ctx.Done():
   824  				return
   825  			case ch <- entry:
   826  			}
   827  		}
   828  	}(ch)
   829  	return ch, nil
   830  }