2021-10-06 19:36:31 -04:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
2023-07-06 01:28:26 -04:00
|
|
|
"encoding/gob"
|
2021-10-06 19:36:31 -04:00
|
|
|
"encoding/json"
|
2023-07-06 01:28:26 -04:00
|
|
|
"errors"
|
2021-10-06 19:36:31 -04:00
|
|
|
"io"
|
|
|
|
"net/http"
|
2022-07-25 20:51:32 -04:00
|
|
|
"strings"
|
2023-07-06 01:28:26 -04:00
|
|
|
"sync/atomic"
|
2022-07-25 20:51:32 -04:00
|
|
|
"time"
|
2021-10-06 19:36:31 -04:00
|
|
|
|
2023-07-06 01:28:26 -04:00
|
|
|
"github.com/dustin/go-humanize"
|
2023-06-19 20:53:08 -04:00
|
|
|
"github.com/minio/madmin-go/v3"
|
2021-10-06 19:36:31 -04:00
|
|
|
"github.com/minio/minio/internal/logger"
|
2023-07-06 01:28:26 -04:00
|
|
|
"github.com/minio/mux"
|
2021-10-06 19:36:31 -04:00
|
|
|
"github.com/minio/pkg/bucket/policy"
|
|
|
|
iampolicy "github.com/minio/pkg/iam/policy"
|
|
|
|
)
|
|
|
|
|
|
|
|
// SiteReplicationAdd - PUT /minio/admin/v3/site-replication/add
|
|
|
|
func (a adminAPIHandlers) SiteReplicationAdd(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2021-10-06 19:36:31 -04:00
|
|
|
|
|
|
|
objectAPI, cred := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationAddAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var sites []madmin.PeerSite
|
2022-01-19 23:02:24 -05:00
|
|
|
if err := parseJSONBody(ctx, r.Body, &sites, cred.SecretKey); err != nil {
|
2021-12-14 17:09:57 -05:00
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-14 17:09:57 -05:00
|
|
|
status, err := globalSiteReplicationSys.AddPeerClusters(ctx, sites)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
body, err := json.Marshal(status)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
writeSuccessResponseJSON(w, body)
|
|
|
|
}
|
|
|
|
|
2021-12-27 02:10:34 -05:00
|
|
|
// SRPeerJoin - PUT /minio/admin/v3/site-replication/join
|
2021-10-06 19:36:31 -04:00
|
|
|
//
|
|
|
|
// used internally to tell current cluster to enable SR with
|
|
|
|
// the provided peer clusters and service account.
|
2021-12-27 02:10:34 -05:00
|
|
|
func (a adminAPIHandlers) SRPeerJoin(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2021-10-06 19:36:31 -04:00
|
|
|
|
|
|
|
objectAPI, cred := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationAddAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-12-27 02:10:34 -05:00
|
|
|
var joinArg madmin.SRPeerJoinReq
|
2021-12-14 17:09:57 -05:00
|
|
|
if err := parseJSONBody(ctx, r.Body, &joinArg, cred.SecretKey); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-05 05:44:08 -05:00
|
|
|
if err := globalSiteReplicationSys.PeerJoinReq(ctx, joinArg); err != nil {
|
2021-12-14 17:09:57 -05:00
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-27 02:10:34 -05:00
|
|
|
// SRPeerBucketOps - PUT /minio/admin/v3/site-replication/bucket-ops?bucket=x&operation=y
|
|
|
|
func (a adminAPIHandlers) SRPeerBucketOps(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2021-10-06 19:36:31 -04:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationOperationAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
bucket := vars["bucket"]
|
|
|
|
operation := madmin.BktOp(vars["operation"])
|
|
|
|
|
|
|
|
var err error
|
|
|
|
switch operation {
|
2021-12-08 14:50:15 -05:00
|
|
|
default:
|
2021-12-14 17:09:57 -05:00
|
|
|
err = errSRInvalidRequest(errInvalidArgument)
|
2021-10-06 19:36:31 -04:00
|
|
|
case madmin.MakeWithVersioningBktOp:
|
2023-01-03 11:16:39 -05:00
|
|
|
createdAt, cerr := time.Parse(time.RFC3339Nano, strings.TrimSpace(r.Form.Get("createdAt")))
|
2022-07-25 20:51:32 -04:00
|
|
|
if cerr != nil {
|
|
|
|
createdAt = timeSentinel
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := MakeBucketOptions{
|
2022-12-23 10:46:00 -05:00
|
|
|
LockEnabled: r.Form.Get("lockEnabled") == "true",
|
|
|
|
VersioningEnabled: r.Form.Get("versioningEnabled") == "true",
|
|
|
|
ForceCreate: r.Form.Get("forceCreate") == "true",
|
2022-07-25 20:51:32 -04:00
|
|
|
CreatedAt: createdAt,
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
|
|
|
err = globalSiteReplicationSys.PeerBucketMakeWithVersioningHandler(ctx, bucket, opts)
|
|
|
|
case madmin.ConfigureReplBktOp:
|
|
|
|
err = globalSiteReplicationSys.PeerBucketConfigureReplHandler(ctx, bucket)
|
2022-12-23 10:46:00 -05:00
|
|
|
case madmin.DeleteBucketBktOp, madmin.ForceDeleteBucketBktOp:
|
2022-07-25 20:51:32 -04:00
|
|
|
err = globalSiteReplicationSys.PeerBucketDeleteHandler(ctx, bucket, DeleteBucketOptions{
|
2022-12-23 10:46:00 -05:00
|
|
|
Force: operation == madmin.ForceDeleteBucketBktOp,
|
2022-07-25 20:51:32 -04:00
|
|
|
SRDeleteOp: getSRBucketDeleteOp(true),
|
|
|
|
})
|
|
|
|
case madmin.PurgeDeletedBucketOp:
|
|
|
|
globalSiteReplicationSys.purgeDeletedBucket(ctx, objectAPI, bucket)
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
2021-12-08 14:50:15 -05:00
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-27 02:10:34 -05:00
|
|
|
// SRPeerReplicateIAMItem - PUT /minio/admin/v3/site-replication/iam-item
|
|
|
|
func (a adminAPIHandlers) SRPeerReplicateIAMItem(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2021-10-06 19:36:31 -04:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationOperationAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var item madmin.SRIAMItem
|
2021-12-14 17:09:57 -05:00
|
|
|
if err := parseJSONBody(ctx, r.Body, &item, ""); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
switch item.Type {
|
2021-12-08 14:50:15 -05:00
|
|
|
default:
|
2021-12-14 17:09:57 -05:00
|
|
|
err = errSRInvalidRequest(errInvalidArgument)
|
2021-10-06 19:36:31 -04:00
|
|
|
case madmin.SRIAMItemPolicy:
|
2021-12-08 14:50:15 -05:00
|
|
|
if item.Policy == nil {
|
2022-07-01 16:19:13 -04:00
|
|
|
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil, item.UpdatedAt)
|
2021-12-08 14:50:15 -05:00
|
|
|
} else {
|
|
|
|
policy, perr := iampolicy.ParseConfig(bytes.NewReader(item.Policy))
|
|
|
|
if perr != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, perr), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
2021-12-08 14:50:15 -05:00
|
|
|
if policy.IsEmpty() {
|
2022-07-01 16:19:13 -04:00
|
|
|
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil, item.UpdatedAt)
|
2021-12-08 14:50:15 -05:00
|
|
|
} else {
|
2022-07-01 16:19:13 -04:00
|
|
|
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, policy, item.UpdatedAt)
|
2021-12-08 14:50:15 -05:00
|
|
|
}
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
|
|
|
case madmin.SRIAMItemSvcAcc:
|
2022-07-01 16:19:13 -04:00
|
|
|
err = globalSiteReplicationSys.PeerSvcAccChangeHandler(ctx, item.SvcAccChange, item.UpdatedAt)
|
2021-10-06 19:36:31 -04:00
|
|
|
case madmin.SRIAMItemPolicyMapping:
|
2022-07-01 16:19:13 -04:00
|
|
|
err = globalSiteReplicationSys.PeerPolicyMappingHandler(ctx, item.PolicyMapping, item.UpdatedAt)
|
2021-10-06 19:36:31 -04:00
|
|
|
case madmin.SRIAMItemSTSAcc:
|
2022-07-01 16:19:13 -04:00
|
|
|
err = globalSiteReplicationSys.PeerSTSAccHandler(ctx, item.STSCredential, item.UpdatedAt)
|
2022-01-06 18:52:43 -05:00
|
|
|
case madmin.SRIAMItemIAMUser:
|
2022-07-01 16:19:13 -04:00
|
|
|
err = globalSiteReplicationSys.PeerIAMUserChangeHandler(ctx, item.IAMUser, item.UpdatedAt)
|
2022-01-06 18:52:43 -05:00
|
|
|
case madmin.SRIAMItemGroupInfo:
|
2022-07-01 16:19:13 -04:00
|
|
|
err = globalSiteReplicationSys.PeerGroupInfoChangeHandler(ctx, item.GroupInfo, item.UpdatedAt)
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
2021-12-08 14:50:15 -05:00
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-27 02:10:34 -05:00
|
|
|
// SRPeerReplicateBucketItem - PUT /minio/admin/v3/site-replication/bucket-meta
|
|
|
|
func (a adminAPIHandlers) SRPeerReplicateBucketItem(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2021-10-06 19:36:31 -04:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationOperationAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var item madmin.SRBucketMeta
|
2021-12-14 17:09:57 -05:00
|
|
|
if err := parseJSONBody(ctx, r.Body, &item, ""); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
switch item.Type {
|
2021-12-08 14:50:15 -05:00
|
|
|
default:
|
2021-12-14 17:09:57 -05:00
|
|
|
err = errSRInvalidRequest(errInvalidArgument)
|
2021-10-06 19:36:31 -04:00
|
|
|
case madmin.SRBucketMetaTypePolicy:
|
2021-12-08 14:50:15 -05:00
|
|
|
if item.Policy == nil {
|
2022-06-28 21:09:20 -04:00
|
|
|
err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, nil, item.UpdatedAt)
|
2021-12-08 14:50:15 -05:00
|
|
|
} else {
|
|
|
|
bktPolicy, berr := policy.ParseConfig(bytes.NewReader(item.Policy), item.Bucket)
|
|
|
|
if berr != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, berr), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
2021-12-08 14:50:15 -05:00
|
|
|
if bktPolicy.IsEmpty() {
|
2022-06-28 21:09:20 -04:00
|
|
|
err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, nil, item.UpdatedAt)
|
2021-12-08 14:50:15 -05:00
|
|
|
} else {
|
2022-06-28 21:09:20 -04:00
|
|
|
err = globalSiteReplicationSys.PeerBucketPolicyHandler(ctx, item.Bucket, bktPolicy, item.UpdatedAt)
|
2021-12-08 14:50:15 -05:00
|
|
|
}
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
2022-01-19 23:02:24 -05:00
|
|
|
case madmin.SRBucketMetaTypeQuotaConfig:
|
|
|
|
if item.Quota == nil {
|
2022-06-28 21:09:20 -04:00
|
|
|
err = globalSiteReplicationSys.PeerBucketQuotaConfigHandler(ctx, item.Bucket, nil, item.UpdatedAt)
|
2022-01-19 23:02:24 -05:00
|
|
|
} else {
|
|
|
|
quotaConfig, err := parseBucketQuota(item.Bucket, item.Quota)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
2022-06-28 21:09:20 -04:00
|
|
|
if err = globalSiteReplicationSys.PeerBucketQuotaConfigHandler(ctx, item.Bucket, quotaConfig, item.UpdatedAt); err != nil {
|
2022-01-19 23:02:24 -05:00
|
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2022-05-07 21:39:40 -04:00
|
|
|
case madmin.SRBucketMetaTypeVersionConfig:
|
2022-06-28 21:09:20 -04:00
|
|
|
err = globalSiteReplicationSys.PeerBucketVersioningHandler(ctx, item.Bucket, item.Versioning, item.UpdatedAt)
|
2021-10-06 19:36:31 -04:00
|
|
|
case madmin.SRBucketMetaTypeTags:
|
2022-06-28 21:09:20 -04:00
|
|
|
err = globalSiteReplicationSys.PeerBucketTaggingHandler(ctx, item.Bucket, item.Tags, item.UpdatedAt)
|
2021-10-06 19:36:31 -04:00
|
|
|
case madmin.SRBucketMetaTypeObjectLockConfig:
|
2022-06-28 21:09:20 -04:00
|
|
|
err = globalSiteReplicationSys.PeerBucketObjectLockConfigHandler(ctx, item.Bucket, item.ObjectLockConfig, item.UpdatedAt)
|
2021-10-06 19:36:31 -04:00
|
|
|
case madmin.SRBucketMetaTypeSSEConfig:
|
2022-06-28 21:09:20 -04:00
|
|
|
err = globalSiteReplicationSys.PeerBucketSSEConfigHandler(ctx, item.Bucket, item.SSEConfig, item.UpdatedAt)
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
2021-12-08 14:50:15 -05:00
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
2021-10-06 19:36:31 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SiteReplicationInfo - GET /minio/admin/v3/site-replication/info
|
|
|
|
func (a adminAPIHandlers) SiteReplicationInfo(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2021-10-06 19:36:31 -04:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationInfoAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
info, err := globalSiteReplicationSys.GetClusterInfo(ctx)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = json.NewEncoder(w).Encode(info); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-27 02:10:34 -05:00
|
|
|
func (a adminAPIHandlers) SRPeerGetIDPSettings(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2021-10-06 19:36:31 -04:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationAddAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
idpSettings := globalSiteReplicationSys.GetIDPSettings(ctx)
|
|
|
|
if err := json.NewEncoder(w).Encode(idpSettings); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-14 17:09:57 -05:00
|
|
|
func parseJSONBody(ctx context.Context, body io.Reader, v interface{}, encryptionKey string) error {
|
2022-09-19 14:05:16 -04:00
|
|
|
data, err := io.ReadAll(body)
|
2021-10-06 19:36:31 -04:00
|
|
|
if err != nil {
|
2021-12-14 17:09:57 -05:00
|
|
|
return SRError{
|
|
|
|
Cause: err,
|
|
|
|
Code: ErrSiteReplicationInvalidRequest,
|
|
|
|
}
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
|
|
|
if encryptionKey != "" {
|
|
|
|
data, err = madmin.DecryptData(encryptionKey, bytes.NewReader(data))
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
2021-12-14 17:09:57 -05:00
|
|
|
return SRError{
|
|
|
|
Cause: err,
|
|
|
|
Code: ErrSiteReplicationInvalidRequest,
|
|
|
|
}
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
|
|
|
}
|
2021-12-14 17:09:57 -05:00
|
|
|
return json.Unmarshal(data, v)
|
2021-10-06 19:36:31 -04:00
|
|
|
}
|
2022-01-05 05:44:08 -05:00
|
|
|
|
|
|
|
// SiteReplicationStatus - GET /minio/admin/v3/site-replication/status
|
|
|
|
func (a adminAPIHandlers) SiteReplicationStatus(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2022-01-05 05:44:08 -05:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationInfoAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
2022-01-28 18:37:55 -05:00
|
|
|
opts := getSRStatusOptions(r)
|
2022-01-29 00:19:38 -05:00
|
|
|
// default options to all if status options are unset for backward compatibility
|
|
|
|
var dfltOpts madmin.SRStatusOptions
|
|
|
|
if opts == dfltOpts {
|
|
|
|
opts.Buckets = true
|
|
|
|
opts.Users = true
|
|
|
|
opts.Policies = true
|
|
|
|
opts.Groups = true
|
|
|
|
}
|
2022-01-28 18:37:55 -05:00
|
|
|
info, err := globalSiteReplicationSys.SiteReplicationStatus(ctx, objectAPI, opts)
|
2022-01-05 05:44:08 -05:00
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = json.NewEncoder(w).Encode(info); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SiteReplicationMetaInfo - GET /minio/admin/v3/site-replication/metainfo
|
|
|
|
func (a adminAPIHandlers) SiteReplicationMetaInfo(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2022-01-05 05:44:08 -05:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationInfoAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-01-28 18:37:55 -05:00
|
|
|
opts := getSRStatusOptions(r)
|
|
|
|
info, err := globalSiteReplicationSys.SiteReplicationMetaInfo(ctx, objectAPI, opts)
|
2022-01-05 05:44:08 -05:00
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = json.NewEncoder(w).Encode(info); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2022-01-21 11:48:21 -05:00
|
|
|
|
|
|
|
// SiteReplicationEdit - PUT /minio/admin/v3/site-replication/edit
|
|
|
|
func (a adminAPIHandlers) SiteReplicationEdit(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2022-01-21 11:48:21 -05:00
|
|
|
|
|
|
|
objectAPI, cred := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationAddAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var site madmin.PeerInfo
|
|
|
|
err := parseJSONBody(ctx, r.Body, &site, cred.SecretKey)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
status, err := globalSiteReplicationSys.EditPeerCluster(ctx, site)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
body, err := json.Marshal(status)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
writeSuccessResponseJSON(w, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SRPeerEdit - PUT /minio/admin/v3/site-replication/peer/edit
|
|
|
|
//
|
|
|
|
// used internally to tell current cluster to update endpoint for peer
|
|
|
|
func (a adminAPIHandlers) SRPeerEdit(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2022-01-21 11:48:21 -05:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationAddAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var pi madmin.PeerInfo
|
|
|
|
if err := parseJSONBody(ctx, r.Body, &pi, ""); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := globalSiteReplicationSys.PeerEditReq(ctx, pi); err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2022-01-28 18:37:55 -05:00
|
|
|
|
|
|
|
func getSRStatusOptions(r *http.Request) (opts madmin.SRStatusOptions) {
|
|
|
|
q := r.Form
|
|
|
|
opts.Buckets = q.Get("buckets") == "true"
|
|
|
|
opts.Policies = q.Get("policies") == "true"
|
|
|
|
opts.Groups = q.Get("groups") == "true"
|
|
|
|
opts.Users = q.Get("users") == "true"
|
|
|
|
opts.Entity = madmin.GetSREntityType(q.Get("entity"))
|
|
|
|
opts.EntityValue = q.Get("entityvalue")
|
2022-07-25 20:51:32 -04:00
|
|
|
opts.ShowDeleted = q.Get("showDeleted") == "true"
|
2022-01-28 18:37:55 -05:00
|
|
|
return
|
|
|
|
}
|
2022-02-01 20:26:09 -05:00
|
|
|
|
|
|
|
// SiteReplicationRemove - PUT /minio/admin/v3/site-replication/remove
|
|
|
|
func (a adminAPIHandlers) SiteReplicationRemove(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2022-02-01 20:26:09 -05:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationRemoveAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var rreq madmin.SRRemoveReq
|
|
|
|
err := parseJSONBody(ctx, r.Body, &rreq, "")
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
status, err := globalSiteReplicationSys.RemovePeerCluster(ctx, objectAPI, rreq)
|
|
|
|
if err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
body, err := json.Marshal(status)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
writeSuccessResponseJSON(w, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SRPeerRemove - PUT /minio/admin/v3/site-replication/peer/remove
|
|
|
|
//
|
|
|
|
// used internally to tell current cluster to update endpoint for peer
|
|
|
|
func (a adminAPIHandlers) SRPeerRemove(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2022-02-01 20:26:09 -05:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationRemoveAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var req madmin.SRRemoveReq
|
|
|
|
if err := parseJSONBody(ctx, r.Body, &req, ""); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := globalSiteReplicationSys.InternalRemoveReq(ctx, objectAPI, req); err != nil {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2022-11-14 10:16:40 -05:00
|
|
|
|
|
|
|
// SiteReplicationResyncOp - PUT /minio/admin/v3/site-replication/resync/op
|
|
|
|
func (a adminAPIHandlers) SiteReplicationResyncOp(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2022-11-14 10:16:40 -05:00
|
|
|
|
|
|
|
objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SiteReplicationResyncAction)
|
|
|
|
if objectAPI == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var peerSite madmin.PeerInfo
|
|
|
|
if err := parseJSONBody(ctx, r.Body, &peerSite, ""); err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
op := madmin.SiteResyncOp(vars["operation"])
|
|
|
|
var (
|
|
|
|
status madmin.SRResyncOpStatus
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
switch op {
|
|
|
|
case madmin.SiteResyncStart:
|
|
|
|
status, err = globalSiteReplicationSys.startResync(ctx, objectAPI, peerSite)
|
|
|
|
case madmin.SiteResyncCancel:
|
|
|
|
status, err = globalSiteReplicationSys.cancelResync(ctx, objectAPI, peerSite)
|
|
|
|
default:
|
|
|
|
err = errSRInvalidRequest(errInvalidArgument)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
body, err := json.Marshal(status)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
writeSuccessResponseJSON(w, body)
|
|
|
|
}
|
2023-07-06 01:28:26 -04:00
|
|
|
|
|
|
|
// SiteReplicationDevNull - everything goes to io.Discard
|
|
|
|
// [POST] /minio/admin/v3/site-replication/devnull
|
2023-07-10 19:59:44 -04:00
|
|
|
func (a adminAPIHandlers) SiteReplicationDevNull(w http.ResponseWriter, r *http.Request) {
|
2023-07-13 17:52:21 -04:00
|
|
|
ctx := r.Context()
|
2023-07-10 19:59:44 -04:00
|
|
|
|
2023-07-06 01:28:26 -04:00
|
|
|
globalSiteNetPerfRX.Connect()
|
|
|
|
defer globalSiteNetPerfRX.Disconnect()
|
|
|
|
|
|
|
|
connectTime := time.Now()
|
|
|
|
for {
|
|
|
|
n, err := io.CopyN(io.Discard, r.Body, 128*humanize.KiByte)
|
|
|
|
atomic.AddUint64(&globalSiteNetPerfRX.RX, uint64(n))
|
|
|
|
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
|
|
|
// If there is a disconnection before globalNetPerfMinDuration (we give a margin of error of 1 sec)
|
|
|
|
// would mean the network is not stable. Logging here will help in debugging network issues.
|
|
|
|
if time.Since(connectTime) < (globalNetPerfMinDuration - time.Second) {
|
|
|
|
logger.LogIf(ctx, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, io.EOF) {
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
} else {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SiteReplicationNetPerf - everything goes to io.Discard
|
|
|
|
// [POST] /minio/admin/v3/site-replication/netperf
|
2023-07-10 19:59:44 -04:00
|
|
|
func (a adminAPIHandlers) SiteReplicationNetPerf(w http.ResponseWriter, r *http.Request) {
|
2023-07-06 01:28:26 -04:00
|
|
|
durationStr := r.Form.Get(peerRESTDuration)
|
|
|
|
duration, _ := time.ParseDuration(durationStr)
|
|
|
|
if duration < globalNetPerfMinDuration {
|
|
|
|
duration = globalNetPerfMinDuration
|
|
|
|
}
|
|
|
|
result := siteNetperf(r.Context(), duration)
|
|
|
|
logger.LogIf(r.Context(), gob.NewEncoder(w).Encode(result))
|
|
|
|
}
|