github.com/minio/madmin-go/v3@v3.0.51/cluster-commands.go (about)

     1  //
     2  // Copyright (c) 2015-2022 MinIO, Inc.
     3  //
     4  // This file is part of MinIO Object Storage stack
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU Affero General Public License as
     8  // published by the Free Software Foundation, either version 3 of the
     9  // License, or (at your option) any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU Affero General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU Affero General Public License
    17  // along with this program. If not, see <http://www.gnu.org/licenses/>.
    18  //
    19  
    20  package madmin
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"io"
    26  	"net/http"
    27  	"net/url"
    28  	"strconv"
    29  	"time"
    30  
    31  	"github.com/minio/minio-go/v7/pkg/replication"
    32  )
    33  
    34  // SiteReplAPIVersion holds the supported version of the server Replication API
    35  const SiteReplAPIVersion = "1"
    36  
    37  // PeerSite - represents a cluster/site to be added to the set of replicated
    38  // sites.
    39  type PeerSite struct {
    40  	Name      string `json:"name"`
    41  	Endpoint  string `json:"endpoints"`
    42  	AccessKey string `json:"accessKey"`
    43  	SecretKey string `json:"secretKey"`
    44  }
    45  
    46  // Meaningful values for ReplicateAddStatus.Status
    47  const (
    48  	ReplicateAddStatusSuccess = "Requested sites were configured for replication successfully."
    49  	ReplicateAddStatusPartial = "Some sites could not be configured for replication."
    50  )
    51  
    52  // ReplicateAddStatus - returns status of add request.
    53  type ReplicateAddStatus struct {
    54  	Success                 bool   `json:"success"`
    55  	Status                  string `json:"status"`
    56  	ErrDetail               string `json:"errorDetail,omitempty"`
    57  	InitialSyncErrorMessage string `json:"initialSyncErrorMessage,omitempty"`
    58  }
    59  
    60  // SRAddOptions holds SR Add options
    61  type SRAddOptions struct {
    62  	ReplicateILMExpiry bool
    63  }
    64  
    65  func (o *SRAddOptions) getURLValues() url.Values {
    66  	urlValues := make(url.Values)
    67  	urlValues.Set("replicateILMExpiry", strconv.FormatBool(o.ReplicateILMExpiry))
    68  	return urlValues
    69  }
    70  
    71  // SiteReplicationAdd - sends the SR add API call.
    72  func (adm *AdminClient) SiteReplicationAdd(ctx context.Context, sites []PeerSite, opts SRAddOptions) (ReplicateAddStatus, error) {
    73  	sitesBytes, err := json.Marshal(sites)
    74  	if err != nil {
    75  		return ReplicateAddStatus{}, nil
    76  	}
    77  	encBytes, err := EncryptData(adm.getSecretKey(), sitesBytes)
    78  	if err != nil {
    79  		return ReplicateAddStatus{}, err
    80  	}
    81  
    82  	q := opts.getURLValues()
    83  	q.Set("api-version", SiteReplAPIVersion)
    84  
    85  	reqData := requestData{
    86  		relPath:     adminAPIPrefix + "/site-replication/add",
    87  		content:     encBytes,
    88  		queryValues: q,
    89  	}
    90  
    91  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
    92  	defer closeResponse(resp)
    93  	if err != nil {
    94  		return ReplicateAddStatus{}, err
    95  	}
    96  
    97  	if resp.StatusCode != http.StatusOK {
    98  		return ReplicateAddStatus{}, httpRespToErrorResponse(resp)
    99  	}
   100  
   101  	b, err := io.ReadAll(resp.Body)
   102  	if err != nil {
   103  		return ReplicateAddStatus{}, err
   104  	}
   105  
   106  	var res ReplicateAddStatus
   107  	if err = json.Unmarshal(b, &res); err != nil {
   108  		return ReplicateAddStatus{}, err
   109  	}
   110  
   111  	return res, nil
   112  }
   113  
   114  // SiteReplicationInfo - contains cluster replication information.
   115  type SiteReplicationInfo struct {
   116  	Enabled                 bool       `json:"enabled"`
   117  	Name                    string     `json:"name,omitempty"`
   118  	Sites                   []PeerInfo `json:"sites,omitempty"`
   119  	ServiceAccountAccessKey string     `json:"serviceAccountAccessKey,omitempty"`
   120  }
   121  
   122  // SiteReplicationInfo - returns cluster replication information.
   123  func (adm *AdminClient) SiteReplicationInfo(ctx context.Context) (info SiteReplicationInfo, err error) {
   124  	q := make(url.Values)
   125  	q.Set("api-version", SiteReplAPIVersion)
   126  
   127  	reqData := requestData{
   128  		relPath:     adminAPIPrefix + "/site-replication/info",
   129  		queryValues: q,
   130  	}
   131  
   132  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   133  	defer closeResponse(resp)
   134  	if err != nil {
   135  		return info, err
   136  	}
   137  
   138  	if resp.StatusCode != http.StatusOK {
   139  		return info, httpRespToErrorResponse(resp)
   140  	}
   141  
   142  	b, err := io.ReadAll(resp.Body)
   143  	if err != nil {
   144  		return info, err
   145  	}
   146  
   147  	err = json.Unmarshal(b, &info)
   148  	return info, err
   149  }
   150  
   151  // SRPeerJoinReq - arg body for SRPeerJoin
   152  type SRPeerJoinReq struct {
   153  	SvcAcctAccessKey string              `json:"svcAcctAccessKey"`
   154  	SvcAcctSecretKey string              `json:"svcAcctSecretKey"`
   155  	SvcAcctParent    string              `json:"svcAcctParent"`
   156  	Peers            map[string]PeerInfo `json:"peers"`
   157  	UpdatedAt        time.Time           `json:"updatedAt"`
   158  }
   159  
   160  // PeerInfo - contains some properties of a cluster peer.
   161  type PeerInfo struct {
   162  	Endpoint string `json:"endpoint"`
   163  	Name     string `json:"name"`
   164  	// Deployment ID is useful as it is immutable - though endpoint may
   165  	// change.
   166  	DeploymentID       string          `json:"deploymentID"`
   167  	SyncState          SyncStatus      `json:"sync"`             // whether to enable| disable synchronous replication
   168  	DefaultBandwidth   BucketBandwidth `json:"defaultbandwidth"` // bandwidth limit per bucket in bytes/sec
   169  	ReplicateILMExpiry bool            `json:"replicate-ilm-expiry"`
   170  }
   171  
   172  // BucketBandwidth has default bandwidth limit per bucket in bytes/sec
   173  type BucketBandwidth struct {
   174  	Limit     uint64    `json:"bandwidthLimitPerBucket"`
   175  	IsSet     bool      `json:"set"`
   176  	UpdatedAt time.Time `json:"updatedAt,omitempty"`
   177  }
   178  
   179  type SyncStatus string // change in sync state
   180  const (
   181  	SyncEnabled  SyncStatus = "enable"
   182  	SyncDisabled SyncStatus = "disable"
   183  )
   184  
   185  func (s SyncStatus) Empty() bool {
   186  	return s != SyncDisabled && s != SyncEnabled
   187  }
   188  
   189  // SRPeerJoin - used only by minio server to send SR join requests to peer
   190  // servers.
   191  func (adm *AdminClient) SRPeerJoin(ctx context.Context, r SRPeerJoinReq) error {
   192  	b, err := json.Marshal(r)
   193  	if err != nil {
   194  		return err
   195  	}
   196  	encBuf, err := EncryptData(adm.getSecretKey(), b)
   197  	if err != nil {
   198  		return err
   199  	}
   200  
   201  	q := make(url.Values)
   202  	q.Set("api-version", SiteReplAPIVersion)
   203  
   204  	reqData := requestData{
   205  		relPath:     adminAPIPrefix + "/site-replication/peer/join",
   206  		content:     encBuf,
   207  		queryValues: q,
   208  	}
   209  
   210  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   211  	defer closeResponse(resp)
   212  	if err != nil {
   213  		return err
   214  	}
   215  
   216  	if resp.StatusCode != http.StatusOK {
   217  		return httpRespToErrorResponse(resp)
   218  	}
   219  
   220  	return nil
   221  }
   222  
   223  // BktOp represents the bucket operation being requested.
   224  type BktOp string
   225  
   226  // BktOp value constants.
   227  const (
   228  	// make bucket and enable versioning
   229  	MakeWithVersioningBktOp BktOp = "make-with-versioning"
   230  	// add replication configuration
   231  	ConfigureReplBktOp BktOp = "configure-replication"
   232  	// delete bucket (forceDelete = off)
   233  	DeleteBucketBktOp BktOp = "delete-bucket"
   234  	// delete bucket (forceDelete = on)
   235  	ForceDeleteBucketBktOp BktOp = "force-delete-bucket"
   236  	// purge bucket
   237  	PurgeDeletedBucketOp BktOp = "purge-deleted-bucket"
   238  )
   239  
   240  // SRPeerBucketOps - tells peers to create bucket and setup replication.
   241  func (adm *AdminClient) SRPeerBucketOps(ctx context.Context, bucket string, op BktOp, opts map[string]string) error {
   242  	v := url.Values{}
   243  	v.Add("bucket", bucket)
   244  	v.Add("operation", string(op))
   245  
   246  	// For make-bucket, bucket options may be sent via `opts`
   247  	if op == MakeWithVersioningBktOp || op == DeleteBucketBktOp {
   248  		for k, val := range opts {
   249  			v.Add(k, val)
   250  		}
   251  	}
   252  
   253  	v.Set("api-version", SiteReplAPIVersion)
   254  
   255  	reqData := requestData{
   256  		queryValues: v,
   257  		relPath:     adminAPIPrefix + "/site-replication/peer/bucket-ops",
   258  	}
   259  
   260  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   261  	defer closeResponse(resp)
   262  	if err != nil {
   263  		return err
   264  	}
   265  
   266  	if resp.StatusCode != http.StatusOK {
   267  		return httpRespToErrorResponse(resp)
   268  	}
   269  
   270  	return nil
   271  }
   272  
   273  // SRIAMItem.Type constants.
   274  const (
   275  	SRIAMItemPolicy        = "policy"
   276  	SRIAMItemPolicyMapping = "policy-mapping"
   277  	SRIAMItemGroupInfo     = "group-info"
   278  	SRIAMItemCredential    = "credential"
   279  	SRIAMItemSvcAcc        = "service-account"
   280  	SRIAMItemSTSAcc        = "sts-account"
   281  	SRIAMItemIAMUser       = "iam-user"
   282  )
   283  
   284  // SRSvcAccCreate - create operation
   285  type SRSvcAccCreate struct {
   286  	Parent        string                 `json:"parent"`
   287  	AccessKey     string                 `json:"accessKey"`
   288  	SecretKey     string                 `json:"secretKey"`
   289  	Groups        []string               `json:"groups"`
   290  	Claims        map[string]interface{} `json:"claims"`
   291  	SessionPolicy json.RawMessage        `json:"sessionPolicy"`
   292  	Status        string                 `json:"status"`
   293  	Name          string                 `json:"name"`
   294  	Description   string                 `json:"description"`
   295  	Expiration    *time.Time             `json:"expiration,omitempty"`
   296  }
   297  
   298  // SRSvcAccUpdate - update operation
   299  type SRSvcAccUpdate struct {
   300  	AccessKey     string          `json:"accessKey"`
   301  	SecretKey     string          `json:"secretKey"`
   302  	Status        string          `json:"status"`
   303  	Name          string          `json:"name"`
   304  	Description   string          `json:"description"`
   305  	SessionPolicy json.RawMessage `json:"sessionPolicy"`
   306  	Expiration    *time.Time      `json:"expiration,omitempty"`
   307  }
   308  
   309  // SRSvcAccDelete - delete operation
   310  type SRSvcAccDelete struct {
   311  	AccessKey string `json:"accessKey"`
   312  }
   313  
   314  // SRSvcAccChange - sum-type to represent an svc account change.
   315  type SRSvcAccChange struct {
   316  	Create *SRSvcAccCreate `json:"crSvcAccCreate"`
   317  	Update *SRSvcAccUpdate `json:"crSvcAccUpdate"`
   318  	Delete *SRSvcAccDelete `json:"crSvcAccDelete"`
   319  }
   320  
   321  // SRPolicyMapping - represents mapping of a policy to a user or group.
   322  type SRPolicyMapping struct {
   323  	UserOrGroup string    `json:"userOrGroup"`
   324  	UserType    int       `json:"userType"`
   325  	IsGroup     bool      `json:"isGroup"`
   326  	Policy      string    `json:"policy"`
   327  	CreatedAt   time.Time `json:"createdAt,omitempty"`
   328  	UpdatedAt   time.Time `json:"updatedAt,omitempty"`
   329  }
   330  
   331  // SRSTSCredential - represents an STS credential to be replicated.
   332  type SRSTSCredential struct {
   333  	AccessKey           string `json:"accessKey"`
   334  	SecretKey           string `json:"secretKey"`
   335  	SessionToken        string `json:"sessionToken"`
   336  	ParentUser          string `json:"parentUser"`
   337  	ParentPolicyMapping string `json:"parentPolicyMapping,omitempty"`
   338  }
   339  
   340  // SRIAMUser - represents a regular (IAM) user to be replicated. A nil UserReq
   341  // implies that a user delete operation should be replicated on the peer cluster.
   342  type SRIAMUser struct {
   343  	AccessKey   string              `json:"accessKey"`
   344  	IsDeleteReq bool                `json:"isDeleteReq"`
   345  	UserReq     *AddOrUpdateUserReq `json:"userReq"`
   346  }
   347  
   348  // SRGroupInfo - represents a regular (IAM) user to be replicated.
   349  type SRGroupInfo struct {
   350  	UpdateReq GroupAddRemove `json:"updateReq"`
   351  }
   352  
   353  // SRCredInfo - represents a credential change (create/update/delete) to be
   354  // replicated. This replaces `SvcAccChange`, `STSCredential` and `IAMUser` and
   355  // will DEPRECATE them.
   356  type SRCredInfo struct {
   357  	AccessKey string `json:"accessKey"`
   358  
   359  	// This type corresponds to github.com/minio/minio/cmd.IAMUserType
   360  	IAMUserType int `json:"iamUserType"`
   361  
   362  	IsDeleteReq bool `json:"isDeleteReq,omitempty"`
   363  
   364  	// This is the JSON encoded value of github.com/minio/minio/cmd.UserIdentity
   365  	UserIdentityJSON json.RawMessage `json:"userIdentityJSON"`
   366  }
   367  
   368  // SRIAMItem - represents an IAM object that will be copied to a peer.
   369  type SRIAMItem struct {
   370  	Type string `json:"type"`
   371  
   372  	// Name and Policy below are used when Type == SRIAMItemPolicy
   373  	Name   string          `json:"name"`
   374  	Policy json.RawMessage `json:"policy"`
   375  
   376  	// Used when Type == SRIAMItemPolicyMapping
   377  	PolicyMapping *SRPolicyMapping `json:"policyMapping"`
   378  
   379  	// Used when Type = SRIAMItemGroupInfo
   380  	GroupInfo *SRGroupInfo `json:"groupInfo"`
   381  
   382  	// Used when Type = SRIAMItemCredential
   383  	CredentialInfo *SRCredInfo `json:"credentialChange"`
   384  
   385  	// Used when Type == SRIAMItemSvcAcc
   386  	SvcAccChange *SRSvcAccChange `json:"serviceAccountChange"`
   387  
   388  	// Used when Type = SRIAMItemSTSAcc
   389  	STSCredential *SRSTSCredential `json:"stsCredential"`
   390  
   391  	// Used when Type = SRIAMItemIAMUser
   392  	IAMUser *SRIAMUser `json:"iamUser"`
   393  
   394  	// UpdatedAt - timestamp of last update
   395  	UpdatedAt time.Time `json:"updatedAt,omitempty"`
   396  }
   397  
   398  // SRPeerReplicateIAMItem - copies an IAM object to a peer cluster.
   399  func (adm *AdminClient) SRPeerReplicateIAMItem(ctx context.Context, item SRIAMItem) error {
   400  	b, err := json.Marshal(item)
   401  	if err != nil {
   402  		return err
   403  	}
   404  
   405  	q := make(url.Values)
   406  	q.Add("api-version", SiteReplAPIVersion)
   407  
   408  	reqData := requestData{
   409  		relPath:     adminAPIPrefix + "/site-replication/peer/iam-item",
   410  		content:     b,
   411  		queryValues: q,
   412  	}
   413  
   414  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   415  	defer closeResponse(resp)
   416  	if err != nil {
   417  		return err
   418  	}
   419  
   420  	if resp.StatusCode != http.StatusOK {
   421  		return httpRespToErrorResponse(resp)
   422  	}
   423  
   424  	return nil
   425  }
   426  
   427  // SRBucketMeta.Type constants
   428  const (
   429  	SRBucketMetaTypePolicy           = "policy"
   430  	SRBucketMetaTypeTags             = "tags"
   431  	SRBucketMetaTypeVersionConfig    = "version-config"
   432  	SRBucketMetaTypeObjectLockConfig = "object-lock-config"
   433  	SRBucketMetaTypeSSEConfig        = "sse-config"
   434  	SRBucketMetaTypeQuotaConfig      = "quota-config"
   435  	SRBucketMetaLCConfig             = "lc-config"
   436  )
   437  
   438  // SRBucketMeta - represents a bucket metadata change that will be copied to a peer.
   439  type SRBucketMeta struct {
   440  	Type   string          `json:"type"`
   441  	Bucket string          `json:"bucket"`
   442  	Policy json.RawMessage `json:"policy,omitempty"`
   443  
   444  	// Since Versioning config does not have a json representation, we use
   445  	// xml byte presentation directly.
   446  	Versioning *string `json:"versioningConfig,omitempty"`
   447  
   448  	// Since tags does not have a json representation, we use its xml byte
   449  	// representation directly.
   450  	Tags *string `json:"tags,omitempty"`
   451  
   452  	// Since object lock does not have a json representation, we use its xml
   453  	// byte representation.
   454  	ObjectLockConfig *string `json:"objectLockConfig,omitempty"`
   455  
   456  	// Since SSE config does not have a json representation, we use its xml
   457  	// byte respresentation.
   458  	SSEConfig *string `json:"sseConfig,omitempty"`
   459  
   460  	// Quota has a json representation use it as is.
   461  	Quota json.RawMessage `json:"quota,omitempty"`
   462  
   463  	// Since Expiry Lifecycle config does not have a json representation, we use its xml
   464  	// byte respresentation.
   465  	ExpiryLCConfig *string `json:"expLCConfig,omitempty"`
   466  
   467  	// UpdatedAt - timestamp of last update
   468  	UpdatedAt time.Time `json:"updatedAt,omitempty"`
   469  
   470  	// ExpiryUpdatedAt - timestamp of last update of expiry rule
   471  	ExpiryUpdatedAt time.Time `json:"expiryUpdatedAt,omitempty"`
   472  }
   473  
   474  // SRPeerReplicateBucketMeta - copies a bucket metadata change to a peer cluster.
   475  func (adm *AdminClient) SRPeerReplicateBucketMeta(ctx context.Context, item SRBucketMeta) error {
   476  	b, err := json.Marshal(item)
   477  	if err != nil {
   478  		return err
   479  	}
   480  
   481  	q := make(url.Values)
   482  	q.Set("api-version", SiteReplAPIVersion)
   483  
   484  	reqData := requestData{
   485  		relPath:     adminAPIPrefix + "/site-replication/peer/bucket-meta",
   486  		content:     b,
   487  		queryValues: q,
   488  	}
   489  
   490  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   491  	defer closeResponse(resp)
   492  	if err != nil {
   493  		return err
   494  	}
   495  
   496  	if resp.StatusCode != http.StatusOK {
   497  		return httpRespToErrorResponse(resp)
   498  	}
   499  
   500  	return nil
   501  }
   502  
   503  // SRBucketInfo - returns all the bucket metadata available for bucket
   504  type SRBucketInfo struct {
   505  	Bucket string          `json:"bucket"`
   506  	Policy json.RawMessage `json:"policy,omitempty"`
   507  
   508  	// Since Versioning config does not have a json representation, we use
   509  	// xml byte presentation directly.
   510  	Versioning *string `json:"versioningConfig,omitempty"`
   511  
   512  	// Since tags does not have a json representation, we use its xml byte
   513  	// representation directly.
   514  	Tags *string `json:"tags,omitempty"`
   515  
   516  	// Since object lock does not have a json representation, we use its xml
   517  	// byte representation.
   518  	ObjectLockConfig *string `json:"objectLockConfig,omitempty"`
   519  
   520  	// Since SSE config does not have a json representation, we use its xml
   521  	// byte respresentation.
   522  	SSEConfig *string `json:"sseConfig,omitempty"`
   523  	// replication config in json representation
   524  	ReplicationConfig *string `json:"replicationConfig,omitempty"`
   525  	// quota config in json representation
   526  	QuotaConfig *string `json:"quotaConfig,omitempty"`
   527  
   528  	// Since Expiry Licfecycle config does not have a json representation, we use its xml
   529  	// byte representation
   530  	ExpiryLCConfig *string `json:"expLCConfig,omitempty"`
   531  
   532  	// time stamps of bucket metadata updates
   533  	PolicyUpdatedAt            time.Time `json:"policyTimestamp,omitempty"`
   534  	TagConfigUpdatedAt         time.Time `json:"tagTimestamp,omitempty"`
   535  	ObjectLockConfigUpdatedAt  time.Time `json:"olockTimestamp,omitempty"`
   536  	SSEConfigUpdatedAt         time.Time `json:"sseTimestamp,omitempty"`
   537  	VersioningConfigUpdatedAt  time.Time `json:"versioningTimestamp,omitempty"`
   538  	ReplicationConfigUpdatedAt time.Time `json:"replicationConfigTimestamp,omitempty"`
   539  	QuotaConfigUpdatedAt       time.Time `json:"quotaTimestamp,omitempty"`
   540  	ExpiryLCConfigUpdatedAt    time.Time `json:"expLCTimestamp,omitempty"`
   541  	CreatedAt                  time.Time `json:"bucketTimestamp,omitempty"`
   542  	DeletedAt                  time.Time `json:"bucketDeletedTimestamp,omitempty"`
   543  	Location                   string    `json:"location,omitempty"`
   544  }
   545  
   546  // OpenIDProviderSettings contains info on a particular OIDC based provider.
   547  type OpenIDProviderSettings struct {
   548  	ClaimName            string
   549  	ClaimUserinfoEnabled bool
   550  	RolePolicy           string
   551  	ClientID             string
   552  	HashedClientSecret   string
   553  }
   554  
   555  // OpenIDSettings contains OpenID configuration info of a cluster.
   556  type OpenIDSettings struct {
   557  	// Enabled is true iff there is at least one OpenID provider configured.
   558  	Enabled bool
   559  	Region  string
   560  	// Map of role ARN to provider info
   561  	Roles map[string]OpenIDProviderSettings
   562  	// Info on the claim based provider (all fields are empty if not
   563  	// present)
   564  	ClaimProvider OpenIDProviderSettings
   565  }
   566  
   567  // IDPSettings contains key IDentity Provider settings to validate that all
   568  // peers have the same configuration.
   569  type IDPSettings struct {
   570  	LDAP   LDAPSettings
   571  	OpenID OpenIDSettings
   572  }
   573  
   574  // LDAPSettings contains LDAP configuration info of a cluster.
   575  type LDAPSettings struct {
   576  	IsLDAPEnabled          bool
   577  	LDAPUserDNSearchBase   string
   578  	LDAPUserDNSearchFilter string
   579  	LDAPGroupSearchBase    string
   580  	LDAPGroupSearchFilter  string
   581  }
   582  
   583  // SRPeerGetIDPSettings - fetches IDP settings from the server.
   584  func (adm *AdminClient) SRPeerGetIDPSettings(ctx context.Context) (info IDPSettings, err error) {
   585  	q := make(url.Values)
   586  	q.Set("api-version", SiteReplAPIVersion)
   587  
   588  	reqData := requestData{
   589  		relPath:     adminAPIPrefix + "/site-replication/peer/idp-settings",
   590  		queryValues: q,
   591  	}
   592  
   593  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   594  	defer closeResponse(resp)
   595  	if err != nil {
   596  		return info, err
   597  	}
   598  
   599  	if resp.StatusCode != http.StatusOK {
   600  		return info, httpRespToErrorResponse(resp)
   601  	}
   602  
   603  	b, err := io.ReadAll(resp.Body)
   604  	if err != nil {
   605  		return info, err
   606  	}
   607  
   608  	err = json.Unmarshal(b, &info)
   609  	if err != nil {
   610  		// If the server is older version, the IDPSettings was =
   611  		// LDAPSettings, so we try that.
   612  		err2 := json.Unmarshal(b, &info.LDAP)
   613  		if err2 == nil {
   614  			err = nil
   615  		}
   616  	}
   617  	return info, err
   618  }
   619  
   620  // SRIAMPolicy - represents an IAM policy.
   621  type SRIAMPolicy struct {
   622  	Policy    json.RawMessage `json:"policy"`
   623  	UpdatedAt time.Time       `json:"updatedAt,omitempty"`
   624  }
   625  
   626  // ILMExpiryRule - represents an ILM expiry rule
   627  type ILMExpiryRule struct {
   628  	ILMRule   string    `json:"ilm-rule"`
   629  	Bucket    string    `json:"bucket"`
   630  	UpdatedAt time.Time `json:"updatedAt,omitempty"`
   631  }
   632  
   633  // SRInfo gets replication metadata for a site
   634  type SRInfo struct {
   635  	Enabled        bool
   636  	Name           string
   637  	DeploymentID   string
   638  	Buckets        map[string]SRBucketInfo       // map of bucket metadata info
   639  	Policies       map[string]SRIAMPolicy        // map of IAM policy name to content
   640  	UserPolicies   map[string]SRPolicyMapping    // map of username -> user policy mapping
   641  	UserInfoMap    map[string]UserInfo           // map of user name to UserInfo
   642  	GroupDescMap   map[string]GroupDesc          // map of group name to GroupDesc
   643  	GroupPolicies  map[string]SRPolicyMapping    // map of groupname -> group policy mapping
   644  	ReplicationCfg map[string]replication.Config // map of bucket -> replication config
   645  	ILMExpiryRules map[string]ILMExpiryRule      // map of ILM Expiry rule to content
   646  	State          SRStateInfo                   // peer state
   647  }
   648  
   649  // SRMetaInfo - returns replication metadata info for a site.
   650  func (adm *AdminClient) SRMetaInfo(ctx context.Context, opts SRStatusOptions) (info SRInfo, err error) {
   651  	q := opts.getURLValues()
   652  	q.Set("api-version", SiteReplAPIVersion)
   653  
   654  	reqData := requestData{
   655  		relPath:     adminAPIPrefix + "/site-replication/metainfo",
   656  		queryValues: q,
   657  	}
   658  
   659  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   660  	defer closeResponse(resp)
   661  	if err != nil {
   662  		return info, err
   663  	}
   664  
   665  	if resp.StatusCode != http.StatusOK {
   666  		return info, httpRespToErrorResponse(resp)
   667  	}
   668  
   669  	err = json.NewDecoder(resp.Body).Decode(&info)
   670  	return info, err
   671  }
   672  
   673  // SRStatusInfo returns detailed status on site replication status
   674  type SRStatusInfo struct {
   675  	Enabled           bool
   676  	MaxBuckets        int                      // maximum buckets seen across sites
   677  	MaxUsers          int                      // maximum users seen across sites
   678  	MaxGroups         int                      // maximum groups seen across sites
   679  	MaxPolicies       int                      // maximum policies across sites
   680  	MaxILMExpiryRules int                      // maxmimum ILM Expiry rules across sites
   681  	Sites             map[string]PeerInfo      // deployment->sitename
   682  	StatsSummary      map[string]SRSiteSummary // map of deployment id -> site stat
   683  	// BucketStats map of bucket to slice of deployment IDs with stats. This is populated only if there are
   684  	// mismatches or if a specific bucket's stats are requested
   685  	BucketStats map[string]map[string]SRBucketStatsSummary
   686  	// PolicyStats map of policy to slice of deployment IDs with stats. This is populated only if there are
   687  	// mismatches or if a specific bucket's stats are requested
   688  	PolicyStats map[string]map[string]SRPolicyStatsSummary
   689  	// UserStats map of user to slice of deployment IDs with stats. This is populated only if there are
   690  	// mismatches or if a specific bucket's stats are requested
   691  	UserStats map[string]map[string]SRUserStatsSummary
   692  	// GroupStats map of group to slice of deployment IDs with stats. This is populated only if there are
   693  	// mismatches or if a specific bucket's stats are requested
   694  	GroupStats map[string]map[string]SRGroupStatsSummary
   695  	// Metrics summary of SRMetrics
   696  	Metrics SRMetricsSummary // metrics summary. This is populated if buckets/bucket entity requested
   697  	// ILMExpiryStats map of ILM Expiry rules to slice of deployment IDs with stats. This is populated if there
   698  	// are mismatches or if a specific ILM expiry rule's stats are requested
   699  	ILMExpiryStats map[string]map[string]SRILMExpiryStatsSummary
   700  }
   701  
   702  // SRPolicyStatsSummary has status of policy replication misses
   703  type SRPolicyStatsSummary struct {
   704  	DeploymentID   string
   705  	PolicyMismatch bool
   706  	HasPolicy      bool
   707  }
   708  
   709  // SRUserStatsSummary has status of user replication misses
   710  type SRUserStatsSummary struct {
   711  	DeploymentID     string
   712  	PolicyMismatch   bool
   713  	UserInfoMismatch bool
   714  	HasUser          bool
   715  	HasPolicyMapping bool
   716  }
   717  
   718  // SRGroupStatsSummary has status of group replication misses
   719  type SRGroupStatsSummary struct {
   720  	DeploymentID      string
   721  	PolicyMismatch    bool
   722  	HasGroup          bool
   723  	GroupDescMismatch bool
   724  	HasPolicyMapping  bool
   725  }
   726  
   727  // SRBucketStatsSummary has status of bucket metadata replication misses
   728  type SRBucketStatsSummary struct {
   729  	DeploymentID             string
   730  	HasBucket                bool
   731  	BucketMarkedDeleted      bool
   732  	TagMismatch              bool
   733  	VersioningConfigMismatch bool
   734  	OLockConfigMismatch      bool
   735  	PolicyMismatch           bool
   736  	SSEConfigMismatch        bool
   737  	ReplicationCfgMismatch   bool
   738  	QuotaCfgMismatch         bool
   739  	HasTagsSet               bool
   740  	HasOLockConfigSet        bool
   741  	HasPolicySet             bool
   742  	HasSSECfgSet             bool
   743  	HasReplicationCfg        bool
   744  	HasQuotaCfgSet           bool
   745  }
   746  
   747  // SRILMExpiryStatsSummary has status of ILM Expiry rules metadata replication misses
   748  type SRILMExpiryStatsSummary struct {
   749  	DeploymentID          string
   750  	ILMExpiryRuleMismatch bool
   751  	HasILMExpiryRules     bool
   752  }
   753  
   754  // SRSiteSummary holds the count of replicated items in site replication
   755  type SRSiteSummary struct {
   756  	ReplicatedBuckets             int // count of buckets replicated across sites
   757  	ReplicatedTags                int // count of buckets with tags replicated across sites
   758  	ReplicatedBucketPolicies      int // count of policies replicated across sites
   759  	ReplicatedIAMPolicies         int // count of IAM policies replicated across sites
   760  	ReplicatedUsers               int // count of users replicated across sites
   761  	ReplicatedGroups              int // count of groups replicated across sites
   762  	ReplicatedLockConfig          int // count of object lock config replicated across sites
   763  	ReplicatedSSEConfig           int // count of SSE config replicated across sites
   764  	ReplicatedVersioningConfig    int // count of versioning config replicated across sites
   765  	ReplicatedQuotaConfig         int // count of bucket with quota config replicated across sites
   766  	ReplicatedUserPolicyMappings  int // count of user policy mappings replicated across sites
   767  	ReplicatedGroupPolicyMappings int // count of group policy mappings replicated across sites
   768  	ReplicatedILMExpiryRules      int // count of ILM expiry rules replicated across sites
   769  
   770  	TotalBucketsCount            int // total buckets on this site
   771  	TotalTagsCount               int // total count of buckets with tags on this site
   772  	TotalBucketPoliciesCount     int // total count of buckets with bucket policies for this site
   773  	TotalIAMPoliciesCount        int // total count of IAM policies for this site
   774  	TotalLockConfigCount         int // total count of buckets with object lock config for this site
   775  	TotalSSEConfigCount          int // total count of buckets with SSE config
   776  	TotalVersioningConfigCount   int // total count of bucekts with versioning config
   777  	TotalQuotaConfigCount        int // total count of buckets with quota config
   778  	TotalUsersCount              int // total number of users seen on this site
   779  	TotalGroupsCount             int // total number of groups seen on this site
   780  	TotalUserPolicyMappingCount  int // total number of user policy mappings seen on this site
   781  	TotalGroupPolicyMappingCount int // total number of group policy mappings seen on this site
   782  	TotalILMExpiryRulesCount     int // total number of ILM expiry rules seen on the site
   783  }
   784  
   785  // SREntityType specifies type of entity
   786  type SREntityType int
   787  
   788  const (
   789  	// Unspecified entity
   790  	Unspecified SREntityType = iota
   791  
   792  	// SRBucketEntity Bucket entity type
   793  	SRBucketEntity
   794  
   795  	// SRPolicyEntity Policy entity type
   796  	SRPolicyEntity
   797  
   798  	// SRUserEntity User entity type
   799  	SRUserEntity
   800  
   801  	// SRGroupEntity Group entity type
   802  	SRGroupEntity
   803  
   804  	// SRILMExpiryRuleEntity ILM expiry rule entity type
   805  	SRILMExpiryRuleEntity
   806  )
   807  
   808  // SRStatusOptions holds SR status options
   809  type SRStatusOptions struct {
   810  	Buckets        bool
   811  	Policies       bool
   812  	Users          bool
   813  	Groups         bool
   814  	Metrics        bool
   815  	ILMExpiryRules bool
   816  	PeerState      bool
   817  	Entity         SREntityType
   818  	EntityValue    string
   819  	ShowDeleted    bool
   820  }
   821  
   822  // IsEntitySet returns true if entity option is set
   823  func (o *SRStatusOptions) IsEntitySet() bool {
   824  	switch o.Entity {
   825  	case SRBucketEntity, SRPolicyEntity, SRUserEntity, SRGroupEntity, SRILMExpiryRuleEntity:
   826  		return true
   827  	default:
   828  		return false
   829  	}
   830  }
   831  
   832  // GetSREntityType returns the SREntityType for a key
   833  func GetSREntityType(name string) SREntityType {
   834  	switch name {
   835  	case "bucket":
   836  		return SRBucketEntity
   837  	case "user":
   838  		return SRUserEntity
   839  	case "group":
   840  		return SRGroupEntity
   841  	case "policy":
   842  		return SRPolicyEntity
   843  	case "ilm-expiry-rule":
   844  		return SRILMExpiryRuleEntity
   845  	default:
   846  		return Unspecified
   847  	}
   848  }
   849  
   850  func (o *SRStatusOptions) getURLValues() url.Values {
   851  	urlValues := make(url.Values)
   852  	urlValues.Set("buckets", strconv.FormatBool(o.Buckets))
   853  	urlValues.Set("policies", strconv.FormatBool(o.Policies))
   854  	urlValues.Set("users", strconv.FormatBool(o.Users))
   855  	urlValues.Set("groups", strconv.FormatBool(o.Groups))
   856  	urlValues.Set("showDeleted", strconv.FormatBool(o.ShowDeleted))
   857  	urlValues.Set("metrics", strconv.FormatBool(o.Metrics))
   858  	urlValues.Set("ilm-expiry-rules", strconv.FormatBool(o.ILMExpiryRules))
   859  	urlValues.Set("peer-state", strconv.FormatBool(o.PeerState))
   860  
   861  	if o.IsEntitySet() {
   862  		urlValues.Set("entityvalue", o.EntityValue)
   863  		switch o.Entity {
   864  		case SRBucketEntity:
   865  			urlValues.Set("entity", "bucket")
   866  		case SRPolicyEntity:
   867  			urlValues.Set("entity", "policy")
   868  		case SRUserEntity:
   869  			urlValues.Set("entity", "user")
   870  		case SRGroupEntity:
   871  			urlValues.Set("entity", "group")
   872  		case SRILMExpiryRuleEntity:
   873  			urlValues.Set("entity", "ilm-expiry-rule")
   874  		}
   875  	}
   876  	return urlValues
   877  }
   878  
   879  // SRStatusInfo - returns site replication status
   880  func (adm *AdminClient) SRStatusInfo(ctx context.Context, opts SRStatusOptions) (info SRStatusInfo, err error) {
   881  	q := opts.getURLValues()
   882  	q.Set("api-version", SiteReplAPIVersion)
   883  
   884  	reqData := requestData{
   885  		relPath:     adminAPIPrefix + "/site-replication/status",
   886  		queryValues: q,
   887  	}
   888  
   889  	resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   890  	defer closeResponse(resp)
   891  	if err != nil {
   892  		return info, err
   893  	}
   894  
   895  	if resp.StatusCode != http.StatusOK {
   896  		return info, httpRespToErrorResponse(resp)
   897  	}
   898  
   899  	err = json.NewDecoder(resp.Body).Decode(&info)
   900  	return info, err
   901  }
   902  
   903  // ReplicateEditStatus - returns status of edit request.
   904  type ReplicateEditStatus struct {
   905  	Success   bool   `json:"success"`
   906  	Status    string `json:"status"`
   907  	ErrDetail string `json:"errorDetail,omitempty"`
   908  }
   909  
   910  // SREditOptions holds SR Edit options
   911  type SREditOptions struct {
   912  	DisableILMExpiryReplication bool
   913  	EnableILMExpiryReplication  bool
   914  }
   915  
   916  func (o *SREditOptions) getURLValues() url.Values {
   917  	urlValues := make(url.Values)
   918  	urlValues.Set("disableILMExpiryReplication", strconv.FormatBool(o.DisableILMExpiryReplication))
   919  	urlValues.Set("enableILMExpiryReplication", strconv.FormatBool(o.EnableILMExpiryReplication))
   920  	return urlValues
   921  }
   922  
   923  // SiteReplicationEdit - sends the SR edit API call.
   924  func (adm *AdminClient) SiteReplicationEdit(ctx context.Context, site PeerInfo, opts SREditOptions) (ReplicateEditStatus, error) {
   925  	sitesBytes, err := json.Marshal(site)
   926  	if err != nil {
   927  		return ReplicateEditStatus{}, nil
   928  	}
   929  	encBytes, err := EncryptData(adm.getSecretKey(), sitesBytes)
   930  	if err != nil {
   931  		return ReplicateEditStatus{}, err
   932  	}
   933  
   934  	q := opts.getURLValues()
   935  	q.Set("api-version", SiteReplAPIVersion)
   936  
   937  	reqData := requestData{
   938  		relPath:     adminAPIPrefix + "/site-replication/edit",
   939  		content:     encBytes,
   940  		queryValues: q,
   941  	}
   942  
   943  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   944  	defer closeResponse(resp)
   945  	if err != nil {
   946  		return ReplicateEditStatus{}, err
   947  	}
   948  
   949  	if resp.StatusCode != http.StatusOK {
   950  		return ReplicateEditStatus{}, httpRespToErrorResponse(resp)
   951  	}
   952  
   953  	var res ReplicateEditStatus
   954  	err = json.NewDecoder(resp.Body).Decode(&res)
   955  	return res, err
   956  }
   957  
   958  // SRPeerEdit - used only by minio server to update peer endpoint
   959  // for a server already in the site replication setup
   960  func (adm *AdminClient) SRPeerEdit(ctx context.Context, pi PeerInfo) error {
   961  	b, err := json.Marshal(pi)
   962  	if err != nil {
   963  		return err
   964  	}
   965  
   966  	q := make(url.Values)
   967  	q.Set("api-version", SiteReplAPIVersion)
   968  
   969  	reqData := requestData{
   970  		relPath:     adminAPIPrefix + "/site-replication/peer/edit",
   971  		content:     b,
   972  		queryValues: q,
   973  	}
   974  
   975  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
   976  	defer closeResponse(resp)
   977  	if err != nil {
   978  		return err
   979  	}
   980  
   981  	if resp.StatusCode != http.StatusOK {
   982  		return httpRespToErrorResponse(resp)
   983  	}
   984  
   985  	return nil
   986  }
   987  
   988  // SRStateEdit - used only by minio server to update peer state
   989  // for a server already in the site replication setup
   990  func (adm *AdminClient) SRStateEdit(ctx context.Context, state SRStateEditReq) error {
   991  	b, err := json.Marshal(state)
   992  	if err != nil {
   993  		return err
   994  	}
   995  
   996  	q := make(url.Values)
   997  	q.Set("api-version", SiteReplAPIVersion)
   998  
   999  	reqData := requestData{
  1000  		relPath:     adminAPIPrefix + "/site-replication/state/edit",
  1001  		content:     b,
  1002  		queryValues: q,
  1003  	}
  1004  
  1005  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
  1006  	defer closeResponse(resp)
  1007  	if err != nil {
  1008  		return err
  1009  	}
  1010  
  1011  	if resp.StatusCode != http.StatusOK {
  1012  		return httpRespToErrorResponse(resp)
  1013  	}
  1014  
  1015  	return nil
  1016  }
  1017  
  1018  // SiteReplicationRemove - unlinks a site from site replication
  1019  func (adm *AdminClient) SiteReplicationRemove(ctx context.Context, removeReq SRRemoveReq) (st ReplicateRemoveStatus, err error) {
  1020  	rmvBytes, err := json.Marshal(removeReq)
  1021  	if err != nil {
  1022  		return st, nil
  1023  	}
  1024  	q := make(url.Values)
  1025  	q.Set("api-version", SiteReplAPIVersion)
  1026  
  1027  	reqData := requestData{
  1028  		relPath:     adminAPIPrefix + "/site-replication/remove",
  1029  		content:     rmvBytes,
  1030  		queryValues: q,
  1031  	}
  1032  
  1033  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
  1034  	defer closeResponse(resp)
  1035  	if err != nil {
  1036  		return st, err
  1037  	}
  1038  
  1039  	if resp.StatusCode != http.StatusOK {
  1040  		return st, httpRespToErrorResponse(resp)
  1041  	}
  1042  	var res ReplicateRemoveStatus
  1043  	err = json.NewDecoder(resp.Body).Decode(&res)
  1044  	return res, err
  1045  }
  1046  
  1047  // SRPeerRemove - used only by minio server to unlink cluster replication
  1048  // for a server already in the site replication setup
  1049  func (adm *AdminClient) SRPeerRemove(ctx context.Context, removeReq SRRemoveReq) (st ReplicateRemoveStatus, err error) {
  1050  	reqBytes, err := json.Marshal(removeReq)
  1051  	if err != nil {
  1052  		return st, err
  1053  	}
  1054  	q := make(url.Values)
  1055  	q.Set("api-version", SiteReplAPIVersion)
  1056  
  1057  	reqData := requestData{
  1058  		relPath:     adminAPIPrefix + "/site-replication/peer/remove",
  1059  		content:     reqBytes,
  1060  		queryValues: q,
  1061  	}
  1062  
  1063  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
  1064  	defer closeResponse(resp)
  1065  	if err != nil {
  1066  		return st, err
  1067  	}
  1068  
  1069  	if resp.StatusCode != http.StatusOK {
  1070  		return st, httpRespToErrorResponse(resp)
  1071  	}
  1072  	return ReplicateRemoveStatus{}, nil
  1073  }
  1074  
  1075  // ReplicateRemoveStatus - returns status of unlink request.
  1076  type ReplicateRemoveStatus struct {
  1077  	Status    string `json:"status"`
  1078  	ErrDetail string `json:"errorDetail,omitempty"`
  1079  }
  1080  
  1081  // SRRemoveReq - arg body for SRRemoveReq
  1082  type SRRemoveReq struct {
  1083  	RequestingDepID string   `json:"requestingDepID"`
  1084  	SiteNames       []string `json:"sites"`
  1085  	RemoveAll       bool     `json:"all"` // true if all sites are to be removed.
  1086  }
  1087  
  1088  // SRStateEditReq - arg body for SRStateEditReq
  1089  type SRStateEditReq struct {
  1090  	Peers     map[string]PeerInfo `json:"peers"`
  1091  	UpdatedAt time.Time           `json:"updatedAt"`
  1092  }
  1093  
  1094  // SRStateInfo - site replication state information
  1095  type SRStateInfo struct {
  1096  	Name      string              `json:"name"`
  1097  	Peers     map[string]PeerInfo `json:"peers"`
  1098  	UpdatedAt time.Time           `json:"updatedAt"`
  1099  }
  1100  
  1101  const (
  1102  	ReplicateRemoveStatusSuccess = "Requested site(s) were removed from cluster replication successfully."
  1103  	ReplicateRemoveStatusPartial = "Some site(s) could not be removed from cluster replication configuration."
  1104  )
  1105  
  1106  type ResyncBucketStatus struct {
  1107  	Bucket    string `json:"bucket"`
  1108  	Status    string `json:"status"`
  1109  	ErrDetail string `json:"errorDetail,omitempty"`
  1110  }
  1111  
  1112  // SRResyncOpStatus - returns status of resync start request.
  1113  type SRResyncOpStatus struct {
  1114  	OpType    string               `json:"op"` // one of "start" or "cancel"
  1115  	ResyncID  string               `json:"id"`
  1116  	Status    string               `json:"status"`
  1117  	Buckets   []ResyncBucketStatus `json:"buckets"`
  1118  	ErrDetail string               `json:"errorDetail,omitempty"`
  1119  }
  1120  
  1121  // SiteResyncOp type of resync operation
  1122  type SiteResyncOp string
  1123  
  1124  const (
  1125  	// SiteResyncStart starts a site resync operation
  1126  	SiteResyncStart SiteResyncOp = "start"
  1127  	// SiteResyncCancel cancels ongoing site resync
  1128  	SiteResyncCancel SiteResyncOp = "cancel"
  1129  )
  1130  
  1131  // SiteReplicationResyncOp - perform a site replication resync operation
  1132  func (adm *AdminClient) SiteReplicationResyncOp(ctx context.Context, site PeerInfo, op SiteResyncOp) (SRResyncOpStatus, error) {
  1133  	reqBytes, err := json.Marshal(site)
  1134  	if err != nil {
  1135  		return SRResyncOpStatus{}, nil
  1136  	}
  1137  
  1138  	v := url.Values{}
  1139  	v.Set("operation", string(op))
  1140  	v.Set("api-version", SiteReplAPIVersion)
  1141  
  1142  	reqData := requestData{
  1143  		relPath:     adminAPIPrefix + "/site-replication/resync/op",
  1144  		content:     reqBytes,
  1145  		queryValues: v,
  1146  	}
  1147  
  1148  	resp, err := adm.executeMethod(ctx, http.MethodPut, reqData)
  1149  	defer closeResponse(resp)
  1150  	if err != nil {
  1151  		return SRResyncOpStatus{}, err
  1152  	}
  1153  
  1154  	if resp.StatusCode != http.StatusOK {
  1155  		return SRResyncOpStatus{}, httpRespToErrorResponse(resp)
  1156  	}
  1157  
  1158  	var res SRResyncOpStatus
  1159  	err = json.NewDecoder(resp.Body).Decode(&res)
  1160  	return res, err
  1161  }
  1162  
  1163  // SRMetric - captures replication metrics for a site replication peer
  1164  type SRMetric struct {
  1165  	DeploymentID  string        `json:"deploymentID"`
  1166  	Endpoint      string        `json:"endpoint"`
  1167  	TotalDowntime time.Duration `json:"totalDowntime"`
  1168  	LastOnline    time.Time     `json:"lastOnline"`
  1169  	Online        bool          `json:"isOnline"`
  1170  	Latency       LatencyStat   `json:"latency"`
  1171  
  1172  	// replication metrics across buckets roll up
  1173  	ReplicatedSize int64 `json:"replicatedSize"`
  1174  	// Total number of completed operations
  1175  	ReplicatedCount int64 `json:"replicatedCount"`
  1176  	// ReplicationErrorStats captures replication errors
  1177  	Failed TimedErrStats `json:"failed,omitempty"`
  1178  	// XferStats captures transfer stats
  1179  	XferStats map[replication.MetricName]replication.XferStats `json:"transferSummary"`
  1180  	// MRFStats captures current backlog entries in the last 5 minutes
  1181  	MRFStats replication.ReplMRFStats `json:"mrfStats"`
  1182  }
  1183  
  1184  // WorkerStat captures number of replication workers
  1185  type WorkerStat struct {
  1186  	Curr int     `json:"curr"`
  1187  	Avg  float32 `json:"avg"`
  1188  	Max  int     `json:"max"`
  1189  }
  1190  
  1191  // InQueueMetric holds stats for objects in replication queue
  1192  type InQueueMetric struct {
  1193  	Curr QStat `json:"curr" msg:"cq"`
  1194  	Avg  QStat `json:"avg" msg:"aq"`
  1195  	Max  QStat `json:"max" msg:"pq"`
  1196  }
  1197  
  1198  // QStat represents number of objects and bytes in queue
  1199  type QStat struct {
  1200  	Count float64 `json:"count"`
  1201  	Bytes float64 `json:"bytes"`
  1202  }
  1203  
  1204  // Add two QStat
  1205  func (q *QStat) Add(o QStat) QStat {
  1206  	return QStat{Bytes: q.Bytes + o.Bytes, Count: q.Count + o.Count}
  1207  }
  1208  
  1209  // SRMetricsSummary captures summary of replication counts across buckets on site
  1210  // along with op metrics rollup.
  1211  type SRMetricsSummary struct {
  1212  	// op metrics roll up
  1213  	ActiveWorkers WorkerStat `json:"activeWorkers"`
  1214  	// Total Replica size in bytes
  1215  	ReplicaSize int64 `json:"replicaSize"`
  1216  	// Total count of replica received
  1217  	ReplicaCount int64 `json:"replicaCount"`
  1218  	// queue metrics
  1219  	Queued InQueueMetric `json:"queued"`
  1220  	// proxied metrics
  1221  	Proxied ReplProxyMetric `json:"proxied"`
  1222  	// replication metrics summary for each site replication peer
  1223  	Metrics map[string]SRMetric `json:"replMetrics"`
  1224  	// uptime of node being queried for site replication metrics
  1225  	Uptime int64 `json:"uptime"`
  1226  }
  1227  
  1228  // ReplProxyMetric holds stats for replication proxying
  1229  type ReplProxyMetric struct {
  1230  	PutTagTotal       uint64 `json:"putTaggingProxyTotal" msg:"ptc"`
  1231  	GetTagTotal       uint64 `json:"getTaggingProxyTotal" msg:"gtc"`
  1232  	RmvTagTotal       uint64 `json:"removeTaggingProxyTotal" msg:"rtc"`
  1233  	GetTotal          uint64 `json:"getProxyTotal" msg:"gc"`
  1234  	HeadTotal         uint64 `json:"headProxyTotal" msg:"hc"`
  1235  	PutTagFailedTotal uint64 `json:"putTaggingProxyFailed" msg:"ptc"`
  1236  	GetTagFailedTotal uint64 `json:"getTaggingProxyFailed" msg:"gtc"`
  1237  	RmvTagFailedTotal uint64 `json:"removeTaggingProxyFailed" msg:"rtc"`
  1238  	GetFailedTotal    uint64 `json:"getProxyFailed" msg:"gc"`
  1239  	HeadFailedTotal   uint64 `json:"headProxyFailed" msg:"hc"`
  1240  }
  1241  
  1242  // Add updates proxy metrics
  1243  func (p *ReplProxyMetric) Add(p2 ReplProxyMetric) {
  1244  	p.GetTagTotal += p2.GetTagTotal
  1245  	p.PutTagTotal += p2.PutTagTotal
  1246  	p.RmvTagTotal += p2.RmvTagTotal
  1247  	p.GetTotal += p2.GetTotal
  1248  	p.HeadTotal += p2.HeadTotal
  1249  	p.PutTagFailedTotal += p2.PutTagFailedTotal
  1250  	p.GetTagFailedTotal += p2.GetTagFailedTotal
  1251  	p.RmvTagFailedTotal += p2.RmvTagFailedTotal
  1252  	p.GetFailedTotal += p2.GetFailedTotal
  1253  	p.HeadFailedTotal += p2.HeadFailedTotal
  1254  }