github.com/minio/madmin-go/v2@v2.2.1/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/ioutil" 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 // SiteReplicationAdd - sends the SR add API call. 61 func (adm *AdminClient) SiteReplicationAdd(ctx context.Context, sites []PeerSite) (ReplicateAddStatus, error) { 62 sitesBytes, err := json.Marshal(sites) 63 if err != nil { 64 return ReplicateAddStatus{}, nil 65 } 66 encBytes, err := EncryptData(adm.getSecretKey(), sitesBytes) 67 if err != nil { 68 return ReplicateAddStatus{}, err 69 } 70 71 q := make(url.Values) 72 q.Set("api-version", SiteReplAPIVersion) 73 74 reqData := requestData{ 75 relPath: adminAPIPrefix + "/site-replication/add", 76 content: encBytes, 77 queryValues: q, 78 } 79 80 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 81 defer closeResponse(resp) 82 if err != nil { 83 return ReplicateAddStatus{}, err 84 } 85 86 if resp.StatusCode != http.StatusOK { 87 return ReplicateAddStatus{}, httpRespToErrorResponse(resp) 88 } 89 90 b, err := ioutil.ReadAll(resp.Body) 91 if err != nil { 92 return ReplicateAddStatus{}, err 93 } 94 95 var res ReplicateAddStatus 96 if err = json.Unmarshal(b, &res); err != nil { 97 return ReplicateAddStatus{}, err 98 } 99 100 return res, nil 101 } 102 103 // SiteReplicationInfo - contains cluster replication information. 104 type SiteReplicationInfo struct { 105 Enabled bool `json:"enabled"` 106 Name string `json:"name,omitempty"` 107 Sites []PeerInfo `json:"sites,omitempty"` 108 ServiceAccountAccessKey string `json:"serviceAccountAccessKey,omitempty"` 109 } 110 111 // SiteReplicationInfo - returns cluster replication information. 112 func (adm *AdminClient) SiteReplicationInfo(ctx context.Context) (info SiteReplicationInfo, err error) { 113 q := make(url.Values) 114 q.Set("api-version", SiteReplAPIVersion) 115 116 reqData := requestData{ 117 relPath: adminAPIPrefix + "/site-replication/info", 118 queryValues: q, 119 } 120 121 resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) 122 defer closeResponse(resp) 123 if err != nil { 124 return info, err 125 } 126 127 if resp.StatusCode != http.StatusOK { 128 return info, httpRespToErrorResponse(resp) 129 } 130 131 b, err := ioutil.ReadAll(resp.Body) 132 if err != nil { 133 return info, err 134 } 135 136 err = json.Unmarshal(b, &info) 137 return info, err 138 } 139 140 // SRPeerJoinReq - arg body for SRPeerJoin 141 type SRPeerJoinReq struct { 142 SvcAcctAccessKey string `json:"svcAcctAccessKey"` 143 SvcAcctSecretKey string `json:"svcAcctSecretKey"` 144 SvcAcctParent string `json:"svcAcctParent"` 145 Peers map[string]PeerInfo `json:"peers"` 146 } 147 148 // PeerInfo - contains some properties of a cluster peer. 149 type PeerInfo struct { 150 Endpoint string `json:"endpoint"` 151 Name string `json:"name"` 152 // Deployment ID is useful as it is immutable - though endpoint may 153 // change. 154 DeploymentID string `json:"deploymentID"` 155 SyncState SyncStatus `json:"sync"` // whether to enable| disable synchronous replication 156 } 157 158 type SyncStatus string // change in sync state 159 const ( 160 SyncEnabled SyncStatus = "enable" 161 SyncDisabled SyncStatus = "disable" 162 ) 163 164 func (s SyncStatus) Empty() bool { 165 return s != SyncDisabled && s != SyncEnabled 166 } 167 168 // SRPeerJoin - used only by minio server to send SR join requests to peer 169 // servers. 170 func (adm *AdminClient) SRPeerJoin(ctx context.Context, r SRPeerJoinReq) error { 171 b, err := json.Marshal(r) 172 if err != nil { 173 return err 174 } 175 encBuf, err := EncryptData(adm.getSecretKey(), b) 176 if err != nil { 177 return err 178 } 179 180 q := make(url.Values) 181 q.Set("api-version", SiteReplAPIVersion) 182 183 reqData := requestData{ 184 relPath: adminAPIPrefix + "/site-replication/peer/join", 185 content: encBuf, 186 queryValues: q, 187 } 188 189 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 190 defer closeResponse(resp) 191 if err != nil { 192 return err 193 } 194 195 if resp.StatusCode != http.StatusOK { 196 return httpRespToErrorResponse(resp) 197 } 198 199 return nil 200 } 201 202 // BktOp represents the bucket operation being requested. 203 type BktOp string 204 205 // BktOp value constants. 206 const ( 207 // make bucket and enable versioning 208 MakeWithVersioningBktOp BktOp = "make-with-versioning" 209 // add replication configuration 210 ConfigureReplBktOp BktOp = "configure-replication" 211 // delete bucket (forceDelete = off) 212 DeleteBucketBktOp BktOp = "delete-bucket" 213 // delete bucket (forceDelete = on) 214 ForceDeleteBucketBktOp BktOp = "force-delete-bucket" 215 // purge bucket 216 PurgeDeletedBucketOp BktOp = "purge-deleted-bucket" 217 ) 218 219 // SRPeerBucketOps - tells peers to create bucket and setup replication. 220 func (adm *AdminClient) SRPeerBucketOps(ctx context.Context, bucket string, op BktOp, opts map[string]string) error { 221 v := url.Values{} 222 v.Add("bucket", bucket) 223 v.Add("operation", string(op)) 224 225 // For make-bucket, bucket options may be sent via `opts` 226 if op == MakeWithVersioningBktOp || op == DeleteBucketBktOp { 227 for k, val := range opts { 228 v.Add(k, val) 229 } 230 } 231 232 v.Set("api-version", SiteReplAPIVersion) 233 234 reqData := requestData{ 235 queryValues: v, 236 relPath: adminAPIPrefix + "/site-replication/peer/bucket-ops", 237 } 238 239 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 240 defer closeResponse(resp) 241 if err != nil { 242 return err 243 } 244 245 if resp.StatusCode != http.StatusOK { 246 return httpRespToErrorResponse(resp) 247 } 248 249 return nil 250 } 251 252 // SRIAMItem.Type constants. 253 const ( 254 SRIAMItemPolicy = "policy" 255 SRIAMItemSvcAcc = "service-account" 256 SRIAMItemSTSAcc = "sts-account" 257 SRIAMItemPolicyMapping = "policy-mapping" 258 SRIAMItemIAMUser = "iam-user" 259 SRIAMItemGroupInfo = "group-info" 260 ) 261 262 // SRSvcAccCreate - create operation 263 type SRSvcAccCreate struct { 264 Parent string `json:"parent"` 265 AccessKey string `json:"accessKey"` 266 SecretKey string `json:"secretKey"` 267 Groups []string `json:"groups"` 268 Claims map[string]interface{} `json:"claims"` 269 SessionPolicy json.RawMessage `json:"sessionPolicy"` 270 Status string `json:"status"` 271 Name string `json:"name"` 272 Description string `json:"description"` 273 Expiration *time.Time `json:"expiration,omitempty"` 274 } 275 276 // SRSvcAccUpdate - update operation 277 type SRSvcAccUpdate struct { 278 AccessKey string `json:"accessKey"` 279 SecretKey string `json:"secretKey"` 280 Status string `json:"status"` 281 Name string `json:"name"` 282 Description string `json:"description"` 283 SessionPolicy json.RawMessage `json:"sessionPolicy"` 284 Expiration *time.Time `json:"expiration,omitempty"` 285 } 286 287 // SRSvcAccDelete - delete operation 288 type SRSvcAccDelete struct { 289 AccessKey string `json:"accessKey"` 290 } 291 292 // SRSvcAccChange - sum-type to represent an svc account change. 293 type SRSvcAccChange struct { 294 Create *SRSvcAccCreate `json:"crSvcAccCreate"` 295 Update *SRSvcAccUpdate `json:"crSvcAccUpdate"` 296 Delete *SRSvcAccDelete `json:"crSvcAccDelete"` 297 } 298 299 // SRPolicyMapping - represents mapping of a policy to a user or group. 300 type SRPolicyMapping struct { 301 UserOrGroup string `json:"userOrGroup"` 302 UserType int `json:"userType"` 303 IsGroup bool `json:"isGroup"` 304 Policy string `json:"policy"` 305 CreatedAt time.Time `json:"createdAt,omitempty"` 306 UpdatedAt time.Time `json:"updatedAt,omitempty"` 307 } 308 309 // SRSTSCredential - represents an STS credential to be replicated. 310 type SRSTSCredential struct { 311 AccessKey string `json:"accessKey"` 312 SecretKey string `json:"secretKey"` 313 SessionToken string `json:"sessionToken"` 314 ParentUser string `json:"parentUser"` 315 ParentPolicyMapping string `json:"parentPolicyMapping,omitempty"` 316 } 317 318 // SRIAMUser - represents a regular (IAM) user to be replicated. A nil UserReq 319 // implies that a user delete operation should be replicated on the peer cluster. 320 type SRIAMUser struct { 321 AccessKey string `json:"accessKey"` 322 IsDeleteReq bool `json:"isDeleteReq"` 323 UserReq *AddOrUpdateUserReq `json:"userReq"` 324 } 325 326 // SRGroupInfo - represents a regular (IAM) user to be replicated. 327 type SRGroupInfo struct { 328 UpdateReq GroupAddRemove `json:"updateReq"` 329 } 330 331 // SRIAMItem - represents an IAM object that will be copied to a peer. 332 type SRIAMItem struct { 333 Type string `json:"type"` 334 335 // Name and Policy below are used when Type == SRIAMItemPolicy 336 Name string `json:"name"` 337 Policy json.RawMessage `json:"policy"` 338 339 // Used when Type == SRIAMItemPolicyMapping 340 PolicyMapping *SRPolicyMapping `json:"policyMapping"` 341 342 // Used when Type == SRIAMItemSvcAcc 343 SvcAccChange *SRSvcAccChange `json:"serviceAccountChange"` 344 345 // Used when Type = SRIAMItemSTSAcc 346 STSCredential *SRSTSCredential `json:"stsCredential"` 347 348 // Used when Type = SRIAMItemIAMUser 349 IAMUser *SRIAMUser `json:"iamUser"` 350 351 // Used when Type = SRIAMItemGroupInfo 352 GroupInfo *SRGroupInfo `json:"groupInfo"` 353 354 // UpdatedAt - timestamp of last update 355 UpdatedAt time.Time `json:"updatedAt,omitempty"` 356 } 357 358 // SRPeerReplicateIAMItem - copies an IAM object to a peer cluster. 359 func (adm *AdminClient) SRPeerReplicateIAMItem(ctx context.Context, item SRIAMItem) error { 360 b, err := json.Marshal(item) 361 if err != nil { 362 return err 363 } 364 365 q := make(url.Values) 366 q.Add("api-version", SiteReplAPIVersion) 367 368 reqData := requestData{ 369 relPath: adminAPIPrefix + "/site-replication/peer/iam-item", 370 content: b, 371 queryValues: q, 372 } 373 374 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 375 defer closeResponse(resp) 376 if err != nil { 377 return err 378 } 379 380 if resp.StatusCode != http.StatusOK { 381 return httpRespToErrorResponse(resp) 382 } 383 384 return nil 385 } 386 387 // SRBucketMeta.Type constants 388 const ( 389 SRBucketMetaTypePolicy = "policy" 390 SRBucketMetaTypeTags = "tags" 391 SRBucketMetaTypeVersionConfig = "version-config" 392 SRBucketMetaTypeObjectLockConfig = "object-lock-config" 393 SRBucketMetaTypeSSEConfig = "sse-config" 394 SRBucketMetaTypeQuotaConfig = "quota-config" 395 ) 396 397 // SRBucketMeta - represents a bucket metadata change that will be copied to a peer. 398 type SRBucketMeta struct { 399 Type string `json:"type"` 400 Bucket string `json:"bucket"` 401 Policy json.RawMessage `json:"policy,omitempty"` 402 403 // Since Versioning config does not have a json representation, we use 404 // xml byte presentation directly. 405 Versioning *string `json:"versioningConfig,omitempty"` 406 407 // Since tags does not have a json representation, we use its xml byte 408 // representation directly. 409 Tags *string `json:"tags,omitempty"` 410 411 // Since object lock does not have a json representation, we use its xml 412 // byte representation. 413 ObjectLockConfig *string `json:"objectLockConfig,omitempty"` 414 415 // Since SSE config does not have a json representation, we use its xml 416 // byte respresentation. 417 SSEConfig *string `json:"sseConfig,omitempty"` 418 419 // Quota has a json representation use it as is. 420 Quota json.RawMessage `json:"quota,omitempty"` 421 422 // UpdatedAt - timestamp of last update 423 UpdatedAt time.Time `json:"updatedAt,omitempty"` 424 } 425 426 // SRPeerReplicateBucketMeta - copies a bucket metadata change to a peer cluster. 427 func (adm *AdminClient) SRPeerReplicateBucketMeta(ctx context.Context, item SRBucketMeta) error { 428 b, err := json.Marshal(item) 429 if err != nil { 430 return err 431 } 432 433 q := make(url.Values) 434 q.Set("api-version", SiteReplAPIVersion) 435 436 reqData := requestData{ 437 relPath: adminAPIPrefix + "/site-replication/peer/bucket-meta", 438 content: b, 439 queryValues: q, 440 } 441 442 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 443 defer closeResponse(resp) 444 if err != nil { 445 return err 446 } 447 448 if resp.StatusCode != http.StatusOK { 449 return httpRespToErrorResponse(resp) 450 } 451 452 return nil 453 } 454 455 // SRBucketInfo - returns all the bucket metadata available for bucket 456 type SRBucketInfo struct { 457 Bucket string `json:"bucket"` 458 Policy json.RawMessage `json:"policy,omitempty"` 459 460 // Since Versioning config does not have a json representation, we use 461 // xml byte presentation directly. 462 Versioning *string `json:"versioningConfig,omitempty"` 463 464 // Since tags does not have a json representation, we use its xml byte 465 // representation directly. 466 Tags *string `json:"tags,omitempty"` 467 468 // Since object lock does not have a json representation, we use its xml 469 // byte representation. 470 ObjectLockConfig *string `json:"objectLockConfig,omitempty"` 471 472 // Since SSE config does not have a json representation, we use its xml 473 // byte respresentation. 474 SSEConfig *string `json:"sseConfig,omitempty"` 475 // replication config in json representation 476 ReplicationConfig *string `json:"replicationConfig,omitempty"` 477 // quota config in json representation 478 QuotaConfig *string `json:"quotaConfig,omitempty"` 479 480 // time stamps of bucket metadata updates 481 PolicyUpdatedAt time.Time `json:"policyTimestamp,omitempty"` 482 TagConfigUpdatedAt time.Time `json:"tagTimestamp,omitempty"` 483 ObjectLockConfigUpdatedAt time.Time `json:"olockTimestamp,omitempty"` 484 SSEConfigUpdatedAt time.Time `json:"sseTimestamp,omitempty"` 485 VersioningConfigUpdatedAt time.Time `json:"versioningTimestamp,omitempty"` 486 ReplicationConfigUpdatedAt time.Time `json:"replicationConfigTimestamp,omitempty"` 487 QuotaConfigUpdatedAt time.Time `json:"quotaTimestamp,omitempty"` 488 CreatedAt time.Time `json:"bucketTimestamp,omitempty"` 489 DeletedAt time.Time `json:"bucketDeletedTimestamp,omitempty"` 490 Location string `json:"location,omitempty"` 491 } 492 493 // OpenIDProviderSettings contains info on a particular OIDC based provider. 494 type OpenIDProviderSettings struct { 495 ClaimName string 496 ClaimUserinfoEnabled bool 497 RolePolicy string 498 ClientID string 499 HashedClientSecret string 500 } 501 502 // OpenIDSettings contains OpenID configuration info of a cluster. 503 type OpenIDSettings struct { 504 // Enabled is true iff there is at least one OpenID provider configured. 505 Enabled bool 506 Region string 507 // Map of role ARN to provider info 508 Roles map[string]OpenIDProviderSettings 509 // Info on the claim based provider (all fields are empty if not 510 // present) 511 ClaimProvider OpenIDProviderSettings 512 } 513 514 // IDPSettings contains key IDentity Provider settings to validate that all 515 // peers have the same configuration. 516 type IDPSettings struct { 517 LDAP LDAPSettings 518 OpenID OpenIDSettings 519 } 520 521 // LDAPSettings contains LDAP configuration info of a cluster. 522 type LDAPSettings struct { 523 IsLDAPEnabled bool 524 LDAPUserDNSearchBase string 525 LDAPUserDNSearchFilter string 526 LDAPGroupSearchBase string 527 LDAPGroupSearchFilter string 528 } 529 530 // SRPeerGetIDPSettings - fetches IDP settings from the server. 531 func (adm *AdminClient) SRPeerGetIDPSettings(ctx context.Context) (info IDPSettings, err error) { 532 q := make(url.Values) 533 q.Set("api-version", SiteReplAPIVersion) 534 535 reqData := requestData{ 536 relPath: adminAPIPrefix + "/site-replication/peer/idp-settings", 537 queryValues: q, 538 } 539 540 resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) 541 defer closeResponse(resp) 542 if err != nil { 543 return info, err 544 } 545 546 if resp.StatusCode != http.StatusOK { 547 return info, httpRespToErrorResponse(resp) 548 } 549 550 b, err := ioutil.ReadAll(resp.Body) 551 if err != nil { 552 return info, err 553 } 554 555 err = json.Unmarshal(b, &info) 556 if err != nil { 557 // If the server is older version, the IDPSettings was = 558 // LDAPSettings, so we try that. 559 err2 := json.Unmarshal(b, &info.LDAP) 560 if err2 == nil { 561 err = nil 562 } 563 } 564 return info, err 565 } 566 567 // SRIAMPolicy - represents an IAM policy. 568 type SRIAMPolicy struct { 569 Policy json.RawMessage `json:"policy"` 570 UpdatedAt time.Time `json:"updatedAt,omitempty"` 571 } 572 573 // SRInfo gets replication metadata for a site 574 type SRInfo struct { 575 Enabled bool 576 Name string 577 DeploymentID string 578 Buckets map[string]SRBucketInfo // map of bucket metadata info 579 Policies map[string]SRIAMPolicy // map of IAM policy name to content 580 UserPolicies map[string]SRPolicyMapping // map of username -> user policy mapping 581 UserInfoMap map[string]UserInfo // map of user name to UserInfo 582 GroupDescMap map[string]GroupDesc // map of group name to GroupDesc 583 GroupPolicies map[string]SRPolicyMapping // map of groupname -> group policy mapping 584 ReplicationCfg map[string]replication.Config // map of bucket -> replication config 585 } 586 587 // SRMetaInfo - returns replication metadata info for a site. 588 func (adm *AdminClient) SRMetaInfo(ctx context.Context, opts SRStatusOptions) (info SRInfo, err error) { 589 q := opts.getURLValues() 590 q.Set("api-version", SiteReplAPIVersion) 591 592 reqData := requestData{ 593 relPath: adminAPIPrefix + "/site-replication/metainfo", 594 queryValues: q, 595 } 596 597 resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) 598 defer closeResponse(resp) 599 if err != nil { 600 return info, err 601 } 602 603 if resp.StatusCode != http.StatusOK { 604 return info, httpRespToErrorResponse(resp) 605 } 606 607 err = json.NewDecoder(resp.Body).Decode(&info) 608 return info, err 609 } 610 611 // SRStatusInfo returns detailed status on site replication status 612 type SRStatusInfo struct { 613 Enabled bool 614 MaxBuckets int // maximum buckets seen across sites 615 MaxUsers int // maximum users seen across sites 616 MaxGroups int // maximum groups seen across sites 617 MaxPolicies int // maximum policies across sites 618 Sites map[string]PeerInfo // deployment->sitename 619 StatsSummary map[string]SRSiteSummary // map of deployment id -> site stat 620 // BucketStats map of bucket to slice of deployment IDs with stats. This is populated only if there are 621 // mismatches or if a specific bucket's stats are requested 622 BucketStats map[string]map[string]SRBucketStatsSummary 623 // PolicyStats map of policy to slice of deployment IDs with stats. This is populated only if there are 624 // mismatches or if a specific bucket's stats are requested 625 PolicyStats map[string]map[string]SRPolicyStatsSummary 626 // UserStats map of user to slice of deployment IDs with stats. This is populated only if there are 627 // mismatches or if a specific bucket's stats are requested 628 UserStats map[string]map[string]SRUserStatsSummary 629 // GroupStats map of group to slice of deployment IDs with stats. This is populated only if there are 630 // mismatches or if a specific bucket's stats are requested 631 GroupStats map[string]map[string]SRGroupStatsSummary 632 } 633 634 // SRPolicyStatsSummary has status of policy replication misses 635 type SRPolicyStatsSummary struct { 636 DeploymentID string 637 PolicyMismatch bool 638 HasPolicy bool 639 } 640 641 // SRUserStatsSummary has status of user replication misses 642 type SRUserStatsSummary struct { 643 DeploymentID string 644 PolicyMismatch bool 645 UserInfoMismatch bool 646 HasUser bool 647 HasPolicyMapping bool 648 } 649 650 // SRGroupStatsSummary has status of group replication misses 651 type SRGroupStatsSummary struct { 652 DeploymentID string 653 PolicyMismatch bool 654 HasGroup bool 655 GroupDescMismatch bool 656 HasPolicyMapping bool 657 } 658 659 // SRBucketStatsSummary has status of bucket metadata replication misses 660 type SRBucketStatsSummary struct { 661 DeploymentID string 662 HasBucket bool 663 BucketMarkedDeleted bool 664 TagMismatch bool 665 VersioningConfigMismatch bool 666 OLockConfigMismatch bool 667 PolicyMismatch bool 668 SSEConfigMismatch bool 669 ReplicationCfgMismatch bool 670 QuotaCfgMismatch bool 671 HasTagsSet bool 672 HasOLockConfigSet bool 673 HasPolicySet bool 674 HasSSECfgSet bool 675 HasReplicationCfg bool 676 HasQuotaCfgSet bool 677 } 678 679 // SRSiteSummary holds the count of replicated items in site replication 680 type SRSiteSummary struct { 681 ReplicatedBuckets int // count of buckets replicated across sites 682 ReplicatedTags int // count of buckets with tags replicated across sites 683 ReplicatedBucketPolicies int // count of policies replicated across sites 684 ReplicatedIAMPolicies int // count of IAM policies replicated across sites 685 ReplicatedUsers int // count of users replicated across sites 686 ReplicatedGroups int // count of groups replicated across sites 687 ReplicatedLockConfig int // count of object lock config replicated across sites 688 ReplicatedSSEConfig int // count of SSE config replicated across sites 689 ReplicatedVersioningConfig int // count of versioning config replicated across sites 690 ReplicatedQuotaConfig int // count of bucket with quota config replicated across sites 691 ReplicatedUserPolicyMappings int // count of user policy mappings replicated across sites 692 ReplicatedGroupPolicyMappings int // count of group policy mappings replicated across sites 693 694 TotalBucketsCount int // total buckets on this site 695 TotalTagsCount int // total count of buckets with tags on this site 696 TotalBucketPoliciesCount int // total count of buckets with bucket policies for this site 697 TotalIAMPoliciesCount int // total count of IAM policies for this site 698 TotalLockConfigCount int // total count of buckets with object lock config for this site 699 TotalSSEConfigCount int // total count of buckets with SSE config 700 TotalVersioningConfigCount int // total count of bucekts with versioning config 701 TotalQuotaConfigCount int // total count of buckets with quota config 702 TotalUsersCount int // total number of users seen on this site 703 TotalGroupsCount int // total number of groups seen on this site 704 TotalUserPolicyMappingCount int // total number of user policy mappings seen on this site 705 TotalGroupPolicyMappingCount int // total number of group policy mappings seen on this site 706 } 707 708 // SREntityType specifies type of entity 709 type SREntityType int 710 711 const ( 712 // Unspecified entity 713 Unspecified SREntityType = iota 714 715 // SRBucketEntity Bucket entity type 716 SRBucketEntity 717 718 // SRPolicyEntity Policy entity type 719 SRPolicyEntity 720 721 // SRUserEntity User entity type 722 SRUserEntity 723 724 // SRGroupEntity Group entity type 725 SRGroupEntity 726 ) 727 728 // SRStatusOptions holds SR status options 729 type SRStatusOptions struct { 730 Buckets bool 731 Policies bool 732 Users bool 733 Groups bool 734 Entity SREntityType 735 EntityValue string 736 ShowDeleted bool 737 } 738 739 // IsEntitySet returns true if entity option is set 740 func (o *SRStatusOptions) IsEntitySet() bool { 741 switch o.Entity { 742 case SRBucketEntity, SRPolicyEntity, SRUserEntity, SRGroupEntity: 743 return true 744 default: 745 return false 746 } 747 } 748 749 // GetSREntityType returns the SREntityType for a key 750 func GetSREntityType(name string) SREntityType { 751 switch name { 752 case "bucket": 753 return SRBucketEntity 754 case "user": 755 return SRUserEntity 756 case "group": 757 return SRGroupEntity 758 case "policy": 759 return SRPolicyEntity 760 default: 761 return Unspecified 762 } 763 } 764 765 func (o *SRStatusOptions) getURLValues() url.Values { 766 urlValues := make(url.Values) 767 urlValues.Set("buckets", strconv.FormatBool(o.Buckets)) 768 urlValues.Set("policies", strconv.FormatBool(o.Policies)) 769 urlValues.Set("users", strconv.FormatBool(o.Users)) 770 urlValues.Set("groups", strconv.FormatBool(o.Groups)) 771 urlValues.Set("showDeleted", strconv.FormatBool(o.ShowDeleted)) 772 773 if o.IsEntitySet() { 774 urlValues.Set("entityvalue", o.EntityValue) 775 switch o.Entity { 776 case SRBucketEntity: 777 urlValues.Set("entity", "bucket") 778 case SRPolicyEntity: 779 urlValues.Set("entity", "policy") 780 case SRUserEntity: 781 urlValues.Set("entity", "user") 782 case SRGroupEntity: 783 urlValues.Set("entity", "group") 784 } 785 } 786 return urlValues 787 } 788 789 // SRStatusInfo - returns site replication status 790 func (adm *AdminClient) SRStatusInfo(ctx context.Context, opts SRStatusOptions) (info SRStatusInfo, err error) { 791 q := opts.getURLValues() 792 q.Set("api-version", SiteReplAPIVersion) 793 794 reqData := requestData{ 795 relPath: adminAPIPrefix + "/site-replication/status", 796 queryValues: q, 797 } 798 799 resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) 800 defer closeResponse(resp) 801 if err != nil { 802 return info, err 803 } 804 805 if resp.StatusCode != http.StatusOK { 806 return info, httpRespToErrorResponse(resp) 807 } 808 809 err = json.NewDecoder(resp.Body).Decode(&info) 810 return info, err 811 } 812 813 // ReplicateEditStatus - returns status of edit request. 814 type ReplicateEditStatus struct { 815 Success bool `json:"success"` 816 Status string `json:"status"` 817 ErrDetail string `json:"errorDetail,omitempty"` 818 } 819 820 // SiteReplicationEdit - sends the SR edit API call. 821 func (adm *AdminClient) SiteReplicationEdit(ctx context.Context, site PeerInfo) (ReplicateEditStatus, error) { 822 sitesBytes, err := json.Marshal(site) 823 if err != nil { 824 return ReplicateEditStatus{}, nil 825 } 826 encBytes, err := EncryptData(adm.getSecretKey(), sitesBytes) 827 if err != nil { 828 return ReplicateEditStatus{}, err 829 } 830 831 q := make(url.Values) 832 q.Set("api-version", SiteReplAPIVersion) 833 834 reqData := requestData{ 835 relPath: adminAPIPrefix + "/site-replication/edit", 836 content: encBytes, 837 queryValues: q, 838 } 839 840 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 841 defer closeResponse(resp) 842 if err != nil { 843 return ReplicateEditStatus{}, err 844 } 845 846 if resp.StatusCode != http.StatusOK { 847 return ReplicateEditStatus{}, httpRespToErrorResponse(resp) 848 } 849 850 var res ReplicateEditStatus 851 err = json.NewDecoder(resp.Body).Decode(&res) 852 return res, err 853 } 854 855 // SRPeerEdit - used only by minio server to update peer endpoint 856 // for a server already in the site replication setup 857 func (adm *AdminClient) SRPeerEdit(ctx context.Context, pi PeerInfo) error { 858 b, err := json.Marshal(pi) 859 if err != nil { 860 return err 861 } 862 863 q := make(url.Values) 864 q.Set("api-version", SiteReplAPIVersion) 865 866 reqData := requestData{ 867 relPath: adminAPIPrefix + "/site-replication/peer/edit", 868 content: b, 869 queryValues: q, 870 } 871 872 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 873 defer closeResponse(resp) 874 if err != nil { 875 return err 876 } 877 878 if resp.StatusCode != http.StatusOK { 879 return httpRespToErrorResponse(resp) 880 } 881 882 return nil 883 } 884 885 // SiteReplicationRemove - unlinks a site from site replication 886 func (adm *AdminClient) SiteReplicationRemove(ctx context.Context, removeReq SRRemoveReq) (st ReplicateRemoveStatus, err error) { 887 rmvBytes, err := json.Marshal(removeReq) 888 if err != nil { 889 return st, nil 890 } 891 q := make(url.Values) 892 q.Set("api-version", SiteReplAPIVersion) 893 894 reqData := requestData{ 895 relPath: adminAPIPrefix + "/site-replication/remove", 896 content: rmvBytes, 897 queryValues: q, 898 } 899 900 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 901 defer closeResponse(resp) 902 if err != nil { 903 return st, err 904 } 905 906 if resp.StatusCode != http.StatusOK { 907 return st, httpRespToErrorResponse(resp) 908 } 909 var res ReplicateRemoveStatus 910 err = json.NewDecoder(resp.Body).Decode(&res) 911 return res, err 912 } 913 914 // SRPeerRemove - used only by minio server to unlink cluster replication 915 // for a server already in the site replication setup 916 func (adm *AdminClient) SRPeerRemove(ctx context.Context, removeReq SRRemoveReq) (st ReplicateRemoveStatus, err error) { 917 reqBytes, err := json.Marshal(removeReq) 918 if err != nil { 919 return st, err 920 } 921 q := make(url.Values) 922 q.Set("api-version", SiteReplAPIVersion) 923 924 reqData := requestData{ 925 relPath: adminAPIPrefix + "/site-replication/peer/remove", 926 content: reqBytes, 927 queryValues: q, 928 } 929 930 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 931 defer closeResponse(resp) 932 if err != nil { 933 return st, err 934 } 935 936 if resp.StatusCode != http.StatusOK { 937 return st, httpRespToErrorResponse(resp) 938 } 939 return ReplicateRemoveStatus{}, nil 940 } 941 942 // ReplicateRemoveStatus - returns status of unlink request. 943 type ReplicateRemoveStatus struct { 944 Status string `json:"status"` 945 ErrDetail string `json:"errorDetail,omitempty"` 946 } 947 948 // SRRemoveReq - arg body for SRRemoveReq 949 type SRRemoveReq struct { 950 RequestingDepID string `json:"requestingDepID"` 951 SiteNames []string `json:"sites"` 952 RemoveAll bool `json:"all"` // true if all sites are to be removed. 953 } 954 955 const ( 956 ReplicateRemoveStatusSuccess = "Requested site(s) were removed from cluster replication successfully." 957 ReplicateRemoveStatusPartial = "Some site(s) could not be removed from cluster replication configuration." 958 ) 959 960 type ResyncBucketStatus struct { 961 Bucket string `json:"bucket"` 962 Status string `json:"status"` 963 ErrDetail string `json:"errorDetail,omitempty"` 964 } 965 966 // SRResyncOpStatus - returns status of resync start request. 967 type SRResyncOpStatus struct { 968 OpType string `json:"op"` // one of "start" or "cancel" 969 ResyncID string `json:"id"` 970 Status string `json:"status"` 971 Buckets []ResyncBucketStatus `json:"buckets"` 972 ErrDetail string `json:"errorDetail,omitempty"` 973 } 974 975 // SiteResyncOp type of resync operation 976 type SiteResyncOp string 977 978 const ( 979 // SiteResyncStart starts a site resync operation 980 SiteResyncStart SiteResyncOp = "start" 981 // SiteResyncCancel cancels ongoing site resync 982 SiteResyncCancel SiteResyncOp = "cancel" 983 ) 984 985 // SiteReplicationResyncOp - perform a site replication resync operation 986 func (adm *AdminClient) SiteReplicationResyncOp(ctx context.Context, site PeerInfo, op SiteResyncOp) (SRResyncOpStatus, error) { 987 reqBytes, err := json.Marshal(site) 988 if err != nil { 989 return SRResyncOpStatus{}, nil 990 } 991 992 v := url.Values{} 993 v.Set("operation", string(op)) 994 v.Set("api-version", SiteReplAPIVersion) 995 996 reqData := requestData{ 997 relPath: adminAPIPrefix + "/site-replication/resync/op", 998 content: reqBytes, 999 queryValues: v, 1000 } 1001 1002 resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) 1003 defer closeResponse(resp) 1004 if err != nil { 1005 return SRResyncOpStatus{}, err 1006 } 1007 1008 if resp.StatusCode != http.StatusOK { 1009 return SRResyncOpStatus{}, httpRespToErrorResponse(resp) 1010 } 1011 1012 var res SRResyncOpStatus 1013 err = json.NewDecoder(resp.Body).Decode(&res) 1014 return res, err 1015 }