diff --git a/models/bucket_replication_rule.go b/models/bucket_replication_rule.go index 7c41ca870..d493db909 100644 --- a/models/bucket_replication_rule.go +++ b/models/bucket_replication_rule.go @@ -49,6 +49,9 @@ type BucketReplicationRule struct { // destination Destination *BucketReplicationDestination `json:"destination,omitempty"` + // existing objects + ExistingObjects bool `json:"existingObjects,omitempty"` + // health check period HealthCheckPeriod int64 `json:"healthCheckPeriod,omitempty"` @@ -68,6 +71,9 @@ type BucketReplicationRule struct { // Enum: [Enabled Disabled] Status string `json:"status,omitempty"` + // storage class + StorageClass string `json:"storageClass,omitempty"` + // sync mode // Enum: [async sync] SyncMode *string `json:"syncMode,omitempty"` diff --git a/models/multi_bucket_replication.go b/models/multi_bucket_replication.go index 36d397262..05412c011 100644 --- a/models/multi_bucket_replication.go +++ b/models/multi_bucket_replication.go @@ -76,6 +76,9 @@ type MultiBucketReplication struct { // Min Length: 8 SecretKey *string `json:"secretKey"` + // storage class + StorageClass string `json:"storageClass,omitempty"` + // sync mode // Enum: [async sync] SyncMode *string `json:"syncMode,omitempty"` diff --git a/models/multi_bucket_replication_edit.go b/models/multi_bucket_replication_edit.go new file mode 100644 index 000000000..6a8c76a58 --- /dev/null +++ b/models/multi_bucket_replication_edit.go @@ -0,0 +1,94 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . +// + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// MultiBucketReplicationEdit multi bucket replication edit +// +// swagger:model multiBucketReplicationEdit +type MultiBucketReplicationEdit struct { + + // arn + Arn string `json:"arn,omitempty"` + + // prefix + Prefix string `json:"prefix,omitempty"` + + // priority + Priority int32 `json:"priority,omitempty"` + + // replicate delete markers + ReplicateDeleteMarkers bool `json:"replicateDeleteMarkers,omitempty"` + + // replicate deletes + ReplicateDeletes bool `json:"replicateDeletes,omitempty"` + + // replicate existing objects + ReplicateExistingObjects bool `json:"replicateExistingObjects,omitempty"` + + // replicate metadata + ReplicateMetadata bool `json:"replicateMetadata,omitempty"` + + // rule state + RuleState bool `json:"ruleState,omitempty"` + + // storage class + StorageClass string `json:"storageClass,omitempty"` + + // tags + Tags string `json:"tags,omitempty"` +} + +// Validate validates this multi bucket replication edit +func (m *MultiBucketReplicationEdit) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this multi bucket replication edit based on context it is used +func (m *MultiBucketReplicationEdit) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *MultiBucketReplicationEdit) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *MultiBucketReplicationEdit) UnmarshalBinary(b []byte) error { + var res MultiBucketReplicationEdit + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/AddReplicationModal.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/AddReplicationModal.tsx index 83391b898..096ecc05c 100644 --- a/portal-ui/src/screens/Console/Buckets/BucketDetails/AddReplicationModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/AddReplicationModal.tsx @@ -96,6 +96,7 @@ const AddReplicationModal = ({ const [useTLS, setUseTLS] = useState(true); const [repDeleteMarker, setRepDeleteMarker] = useState(true); const [repDelete, setRepDelete] = useState(true); + const [metadataSync, setMetadataSync] = useState(true); const [tags, setTags] = useState(""); const [replicationMode, setReplicationMode] = useState("async"); const [bandwidthScalar, setBandwidthScalar] = useState("100"); @@ -148,6 +149,8 @@ const AddReplicationModal = ({ replicateDeleteMarkers: repDeleteMarker, replicateDeletes: repDelete, priority: parseInt(priority), + storageClass: targetStorageClass, + replicateMetadata: metadataSync, }; api @@ -343,6 +346,22 @@ const AddReplicationModal = ({ value={healthCheck} /> + + ) => { + setTargetStorageClass(e.target.value); + }} + placeholder="STANDARD_IA,REDUCED_REDUNDANCY etc" + label="Storage Class" + value={targetStorageClass} + /> +
@@ -375,29 +394,23 @@ const AddReplicationModal = ({
- - - ) => { - setTargetStorageClass(e.target.value); - }} - placeholder="STANDARD_IA,REDUCED_REDUNDANCY etc" - label="Storage Class" - value={targetStorageClass} - /> -
Replication Options + { + setMetadataSync(e.target.checked); + }} + value={metadataSync} + description={"Metadata Sync"} + /> import("./AddReplicationModal")) @@ -86,6 +87,8 @@ const BucketReplicationPanel = ({ const [deleteReplicationModal, setDeleteReplicationModal] = useState(false); const [openSetReplication, setOpenSetReplication] = useState(false); + const [editReplicationModal, setEditReplicationModal] = + useState(false); const [selectedRRule, setSelectedRRule] = useState(""); const bucketName = match.params["bucketName"]; @@ -107,6 +110,9 @@ const BucketReplicationPanel = ({ .invoke("GET", `/api/v1/buckets/${bucketName}/replication`) .then((res: BucketReplication) => { const r = res.rules ? res.rules : []; + + r.sort((a, b) => a.priority - b.priority); + setReplicationRules(r); setLoadingReplication(false); }) @@ -142,11 +148,24 @@ const BucketReplicationPanel = ({ } }; + const closeEditReplication = (refresh: boolean) => { + setEditReplicationModal(false); + + if (refresh) { + setLoadingReplication(true); + } + }; + const confirmDeleteReplication = (replication: BucketReplicationRule) => { setSelectedRRule(replication.id); setDeleteReplicationModal(true); }; + const editReplicationRule = (replication: BucketReplicationRule) => { + setSelectedRRule(replication.id); + setEditReplicationModal(true); + }; + const ruleDestDisplay = (events: BucketReplicationDestination) => { return {events.bucket.replace("arn:aws:s3:::", "")}; }; @@ -161,6 +180,11 @@ const BucketReplicationPanel = ({ onClick: confirmDeleteReplication, disableButtonFunction: () => replicationRules.length === 1, }, + { + type: "view", + onClick: editReplicationRule, + disableButtonFunction: !hasPermission(bucketName, [IAM_SCOPES.S3_PUT_REPLICATION_CONFIGURATION], true), + }, ]; return ( @@ -182,6 +206,15 @@ const BucketReplicationPanel = ({ ruleToDelete={selectedRRule} /> )} + + {editReplicationModal && ( + + )} Replication @@ -215,6 +248,8 @@ const BucketReplicationPanel = ({ { label: "Priority", elementKey: "priority", + width: 55, + contentTextAlign: "center", }, { label: "Destination", @@ -224,19 +259,22 @@ const BucketReplicationPanel = ({ { label: "Prefix", elementKey: "prefix", + width: 200, }, { label: "Tags", elementKey: "tags", renderFunction: tagDisplay, + width: 60, }, - { label: "Status", elementKey: "status" }, + { label: "Status", elementKey: "status", width: 100 }, ]} isLoading={loadingReplication} records={replicationRules} entityName="Replication Rules" idField="id" customPaperHeight={classes.twHeight} + textSelectable /> diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/EditReplicationModal.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/EditReplicationModal.tsx new file mode 100644 index 000000000..7e19f1e67 --- /dev/null +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/EditReplicationModal.tsx @@ -0,0 +1,359 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . + +import React, { useEffect, useState } from "react"; +import Grid from "@mui/material/Grid"; +import { Theme } from "@mui/material/styles"; +import { Button } from "@mui/material"; +import createStyles from "@mui/styles/createStyles"; +import withStyles from "@mui/styles/withStyles"; +import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper"; +import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; +import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector"; +import { BucketReplicationIcon } from "../../../../icons"; +import { + createTenantCommon, + formFieldStyles, + modalStyleUtils, + spacingUtils, +} from "../../Common/FormComponents/common/styleLibrary"; +import { BucketReplicationRule } from "../types"; +import { connect } from "react-redux"; +import { setModalErrorSnackMessage } from "../../../../actions"; +import api from "../../../../common/api"; +import { ErrorResponseHandler } from "../../../../common/types"; +import PredefinedList from "../../Common/FormComponents/PredefinedList/PredefinedList"; +import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; + +interface IEditReplicationModal { + closeModalAndRefresh: (refresh: boolean) => void; + open: boolean; + classes: any; + bucketName: string; + ruleID: string; + setModalErrorSnackMessage: typeof setModalErrorSnackMessage; +} + +const styles = (theme: Theme) => + createStyles({ + buttonContainer: { + textAlign: "right", + }, + multiContainer: { + display: "flex", + alignItems: "center", + }, + sizeFactorContainer: { + "& label": { + display: "none", + }, + "& div:first-child": { + marginBottom: 0, + }, + }, + ...spacingUtils, + ...createTenantCommon, + ...formFieldStyles, + ...modalStyleUtils, + modalFormScrollable: { + ...modalStyleUtils.modalFormScrollable, + paddingRight: 10, + }, + }); + +const EditReplicationModal = ({ + closeModalAndRefresh, + open, + classes, + bucketName, + ruleID, + setModalErrorSnackMessage, +}: IEditReplicationModal) => { + const [editLoading, setEditLoading] = useState(true); + const [saveEdit, setSaveEdit] = useState(false); + const [priority, setPriority] = useState("1"); + const [destination, setDestination] = useState(""); + const [prefix, setPrefix] = useState(""); + const [repDeleteMarker, setRepDeleteMarker] = useState(false); + const [metadataSync, setMetadataSync] = useState(false); + const [initialTags, setInitialTags] = useState(""); + const [tags, setTags] = useState(""); + const [targetStorageClass, setTargetStorageClass] = useState(""); + const [repExisting, setRepExisting] = useState(false); + const [repDelete, setRepDelete] = useState(false); + const [ruleState, setRuleState] = useState(false); + + useEffect(() => { + if (editLoading) { + api + .invoke("GET", `/api/v1/buckets/${bucketName}/replication/${ruleID}`) + .then((res: BucketReplicationRule) => { + setPriority(res.priority.toString()); + const pref = res.prefix || ""; + const tag = res.tags || ""; + setPrefix(pref); + setInitialTags(tag); + setTags(tag); + setDestination(res.destination.bucket); + setRepDeleteMarker(res.delete_marker_replication); + setTargetStorageClass(res.storageClass || ""); + setRepExisting(!!res.existingObjects); + setRepDelete(!!res.deletes_replication); + setRuleState(res.status === "Enabled"); + setMetadataSync(!!res.metadata_replication); + + setEditLoading(false); + }) + .catch((err: ErrorResponseHandler) => { + setModalErrorSnackMessage(err); + setEditLoading(false); + }); + } + }, [editLoading, setModalErrorSnackMessage, bucketName, ruleID]); + + useEffect(() => { + if (saveEdit) { + const remoteBucketsInfo = { + arn: destination, + ruleState: ruleState, + prefix: prefix, + tags: tags, + replicateDeleteMarkers: repDeleteMarker, + replicateDeletes: repDelete, + replicateExistingObjects: repExisting, + replicateMetadata: metadataSync, + priority: parseInt(priority), + storageClass: targetStorageClass, + }; + + api + .invoke( + "PUT", + `/api/v1/buckets/${bucketName}/replication/${ruleID}`, + remoteBucketsInfo + ) + .then(() => { + setSaveEdit(false); + closeModalAndRefresh(true); + }) + .catch((err: ErrorResponseHandler) => { + setModalErrorSnackMessage(err); + setSaveEdit(false); + }); + } + }, [ + saveEdit, + bucketName, + ruleID, + destination, + prefix, + tags, + repDeleteMarker, + priority, + repDelete, + repExisting, + ruleState, + metadataSync, + targetStorageClass, + closeModalAndRefresh, + setModalErrorSnackMessage, + ]); + + return ( + { + closeModalAndRefresh(false); + }} + title="Edit Bucket Replication" + titleIcon={} + > +
) => { + e.preventDefault(); + setSaveEdit(true); + }} + > + + + + { + setRuleState(e.target.checked); + }} + value={ruleState} + /> + + + + + + ) => { + if (e.target.validity.valid) { + setPriority(e.target.value); + } + }} + label="Priority" + value={priority} + pattern={"[0-9]*"} + /> + + + ) => { + setTargetStorageClass(e.target.value); + }} + placeholder="STANDARD_IA,REDUCED_REDUNDANCY etc" + label="Storage Class" + value={targetStorageClass} + /> + + +
+ + Object Filters + + + ) => { + setPrefix(e.target.value); + }} + placeholder="prefix" + label="Prefix" + value={prefix} + /> + + + { + setTags(vl); + }} + keyPlaceholder="Tag Key" + valuePlaceholder="Tag Value" + withBorder + /> + +
+
+ +
+ + Replication Options + + + { + setRepExisting(e.target.checked); + }} + value={repExisting} + description={"Replicate existing objects"} + /> + + { + setMetadataSync(e.target.checked); + }} + value={metadataSync} + description={"Metadata Sync"} + /> + + { + setRepDeleteMarker(e.target.checked); + }} + value={repDeleteMarker} + description={"Replicate soft deletes"} + /> + + + { + setRepDelete(e.target.checked); + }} + value={repDelete} + description={"Replicate versioned deletes"} + /> + +
+
+
+ + + + +
+
+
+ ); +}; +const connector = connect(null, { + setModalErrorSnackMessage, +}); + +export default withStyles(styles)(connector(EditReplicationModal)); diff --git a/portal-ui/src/screens/Console/Buckets/types.tsx b/portal-ui/src/screens/Console/Buckets/types.tsx index 49452b3dd..83258f963 100644 --- a/portal-ui/src/screens/Console/Buckets/types.tsx +++ b/portal-ui/src/screens/Console/Buckets/types.tsx @@ -101,6 +101,8 @@ export interface BucketReplicationRule { tags?: string; destination: BucketReplicationDestination; syncMode: string; + storageClass?: string; + existingObjects?: boolean; } export interface BucketReplication { diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/AddPoolModal.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/AddPoolModal.tsx index 8f8e64f25..23c262a66 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/AddPoolModal.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/AddPoolModal.tsx @@ -168,7 +168,7 @@ const AddPoolModal = ({ onChange={(e: React.ChangeEvent) => { setNumberOfNodes(parseInt(e.target.value)); }} - label="Number o Nodes" + label="Number of Nodes" value={numberOfNodes.toString(10)} />
diff --git a/restapi/admin_remote_buckets.go b/restapi/admin_remote_buckets.go index 0d57fef96..4f8b0adc4 100644 --- a/restapi/admin_remote_buckets.go +++ b/restapi/admin_remote_buckets.go @@ -108,6 +108,15 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) { return user_api.NewDeleteBucketReplicationRuleNoContent() }) + + //update local bucket replication config item + api.UserAPIUpdateMultiBucketReplicationHandler = user_api.UpdateMultiBucketReplicationHandlerFunc(func(params user_api.UpdateMultiBucketReplicationParams, session *models.Principal) middleware.Responder { + err := updateBucketReplicationResponse(session, params) + if err != nil { + return user_api.NewUpdateMultiBucketReplicationDefault(500).WithPayload(err) + } + return user_api.NewUpdateMultiBucketReplicationCreated() + }) } func getListRemoteBucketsResponse(session *models.Principal) (*models.ListRemoteBucketsResponse, error) { @@ -269,7 +278,7 @@ func addRemoteBucket(ctx context.Context, client MinioAdmin, params models.Creat return bucketARN, err } -func addBucketReplicationItem(ctx context.Context, session *models.Principal, minClient minioClient, bucketName, prefix, destinationARN string, repDelMark, repDels, repMeta bool, tags string, priority int32) error { +func addBucketReplicationItem(ctx context.Context, session *models.Principal, minClient minioClient, bucketName, prefix, destinationARN string, repDelMark, repDels, repMeta bool, tags string, priority int32, storageClass string) error { // we will tolerate this call failing cfg, err := minClient.getBucketReplication(ctx, bucketName) if err != nil { @@ -324,6 +333,7 @@ func addBucketReplicationItem(ctx context.Context, session *models.Principal, mi ReplicateDeleteMarkers: repDelMarkStatus, ReplicateDeletes: repDelsStatus, ReplicaSync: repMetaStatus, + StorageClass: storageClass, } err2 := mcClient.setReplication(ctx, &cfg, opts) @@ -334,6 +344,73 @@ func addBucketReplicationItem(ctx context.Context, session *models.Principal, mi return nil } +func editBucketReplicationItem(ctx context.Context, session *models.Principal, minClient minioClient, ruleID, bucketName, prefix, destinationARN string, ruleStatus, repDelMark, repDels, repMeta, existingObjectRep bool, tags string, priority int32, storageClass string) error { + // we will tolerate this call failing + cfg, err := minClient.getBucketReplication(ctx, bucketName) + if err != nil { + LogError("error fetching replication configuration for bucket %s: %v", bucketName, err) + } + + maxPrio := int(priority) + + s3Client, err := newS3BucketClient(session, bucketName, prefix) + if err != nil { + LogError("error creating S3Client: %v", err) + return err + } + // create a mc S3Client interface implementation + // defining the client to be used + mcClient := mcClient{client: s3Client} + + ruleState := "disable" + if ruleStatus { + ruleState = "enable" + } + + repDelMarkStatus := "disable" + if repDelMark { + repDelMarkStatus = "enable" + } + + repDelsStatus := "disable" + if repDels { + repDelsStatus = "enable" + } + + repMetaStatus := "disable" + if repMeta { + repMetaStatus = "enable" + } + + existingRepStatus := "disable" + if existingObjectRep { + existingRepStatus = "enable" + } + + opts := replication.Options{ + ID: ruleID, + Priority: fmt.Sprintf("%d", maxPrio), + RuleStatus: ruleState, + DestBucket: destinationARN, + Op: replication.SetOption, + TagString: tags, + IsTagSet: true, + ExistingObjectReplicate: existingRepStatus, + ReplicateDeleteMarkers: repDelMarkStatus, + ReplicateDeletes: repDelsStatus, + ReplicaSync: repMetaStatus, + StorageClass: storageClass, + IsSCSet: true, + } + + err2 := mcClient.setReplication(ctx, &cfg, opts) + if err2 != nil { + LogError("error modifying replication for bucket:", err2.Cause) + return err2.Cause + } + return nil +} + func setMultiBucketReplication(ctx context.Context, session *models.Principal, client MinioAdmin, minClient minioClient, params user_api.SetMultiBucketReplicationParams) []RemoteBucketResult { bucketsRelation := params.Body.BucketsRelation @@ -372,7 +449,8 @@ func setMultiBucketReplication(ctx context.Context, session *models.Principal, c params.Body.ReplicateDeletes, params.Body.ReplicateMetadata, params.Body.Tags, - params.Body.Priority) + params.Body.Priority, + params.Body.StorageClass) } var errorReturn = "" @@ -523,3 +601,39 @@ func deleteReplicationRuleResponse(session *models.Principal, params user_api.De } return nil } + +func updateBucketReplicationResponse(session *models.Principal, params user_api.UpdateMultiBucketReplicationParams) *models.Error { + ctx := context.Background() + + mClient, err := newMinioClient(session) + if err != nil { + LogError("error creating MinIO Client:", err) + return prepareError(err) + } + // create a minioClient interface implementation + // defining the client to be used + minClient := minioClient{client: mClient} + + err = editBucketReplicationItem( + ctx, + session, + minClient, + params.RuleID, + params.BucketName, + params.Body.Prefix, + params.Body.Arn, + params.Body.RuleState, + params.Body.ReplicateDeleteMarkers, + params.Body.ReplicateDeletes, + params.Body.ReplicateMetadata, + params.Body.ReplicateExistingObjects, + params.Body.Tags, + params.Body.Priority, + params.Body.StorageClass) + + if err != nil { + return prepareError(err) + } + + return nil +} diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index 7cb4fe770..efec1d6a4 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -1626,6 +1626,81 @@ func init() { } }, "/buckets/{bucket_name}/replication/{rule_id}": { + "get": { + "tags": [ + "UserAPI" + ], + "summary": "Bucket Replication", + "operationId": "GetBucketReplicationRule", + "parameters": [ + { + "type": "string", + "name": "bucket_name", + "in": "path", + "required": true + }, + { + "type": "string", + "name": "rule_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/bucketReplicationRule" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + }, + "put": { + "tags": [ + "UserAPI" + ], + "summary": "Update Replication rule", + "operationId": "UpdateMultiBucketReplication", + "parameters": [ + { + "type": "string", + "name": "bucket_name", + "in": "path", + "required": true + }, + { + "type": "string", + "name": "rule_id", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/multiBucketReplicationEdit" + } + } + ], + "responses": { + "201": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + }, "delete": { "tags": [ "UserAPI" @@ -3956,6 +4031,9 @@ func init() { "destination": { "$ref": "#/definitions/bucketReplicationDestination" }, + "existingObjects": { + "type": "boolean" + }, "healthCheckPeriod": { "type": "integer" }, @@ -3979,6 +4057,9 @@ func init() { "Disabled" ] }, + "storageClass": { + "type": "string" + }, "syncMode": { "type": "string", "default": "async", @@ -4599,6 +4680,10 @@ func init() { "type": "string", "minLength": 8 }, + "storageClass": { + "type": "string", + "default": "" + }, "syncMode": { "type": "string", "default": "async", @@ -4615,6 +4700,44 @@ func init() { } } }, + "multiBucketReplicationEdit": { + "properties": { + "arn": { + "type": "string" + }, + "prefix": { + "type": "string" + }, + "priority": { + "type": "integer", + "format": "int32", + "default": 0 + }, + "replicateDeleteMarkers": { + "type": "boolean" + }, + "replicateDeletes": { + "type": "boolean" + }, + "replicateExistingObjects": { + "type": "boolean" + }, + "replicateMetadata": { + "type": "boolean" + }, + "ruleState": { + "type": "boolean" + }, + "storageClass": { + "type": "string", + "default": "" + }, + "tags": { + "type": "string", + "default": "" + } + } + }, "multiBucketResponseItem": { "type": "object", "properties": { @@ -7342,6 +7465,81 @@ func init() { } }, "/buckets/{bucket_name}/replication/{rule_id}": { + "get": { + "tags": [ + "UserAPI" + ], + "summary": "Bucket Replication", + "operationId": "GetBucketReplicationRule", + "parameters": [ + { + "type": "string", + "name": "bucket_name", + "in": "path", + "required": true + }, + { + "type": "string", + "name": "rule_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/bucketReplicationRule" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + }, + "put": { + "tags": [ + "UserAPI" + ], + "summary": "Update Replication rule", + "operationId": "UpdateMultiBucketReplication", + "parameters": [ + { + "type": "string", + "name": "bucket_name", + "in": "path", + "required": true + }, + { + "type": "string", + "name": "rule_id", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/multiBucketReplicationEdit" + } + } + ], + "responses": { + "201": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/error" + } + } + } + }, "delete": { "tags": [ "UserAPI" @@ -9792,6 +9990,9 @@ func init() { "destination": { "$ref": "#/definitions/bucketReplicationDestination" }, + "existingObjects": { + "type": "boolean" + }, "healthCheckPeriod": { "type": "integer" }, @@ -9815,6 +10016,9 @@ func init() { "Disabled" ] }, + "storageClass": { + "type": "string" + }, "syncMode": { "type": "string", "default": "async", @@ -10435,6 +10639,10 @@ func init() { "type": "string", "minLength": 8 }, + "storageClass": { + "type": "string", + "default": "" + }, "syncMode": { "type": "string", "default": "async", @@ -10451,6 +10659,44 @@ func init() { } } }, + "multiBucketReplicationEdit": { + "properties": { + "arn": { + "type": "string" + }, + "prefix": { + "type": "string" + }, + "priority": { + "type": "integer", + "format": "int32", + "default": 0 + }, + "replicateDeleteMarkers": { + "type": "boolean" + }, + "replicateDeletes": { + "type": "boolean" + }, + "replicateExistingObjects": { + "type": "boolean" + }, + "replicateMetadata": { + "type": "boolean" + }, + "ruleState": { + "type": "boolean" + }, + "storageClass": { + "type": "string", + "default": "" + }, + "tags": { + "type": "string", + "default": "" + } + } + }, "multiBucketResponseItem": { "type": "object", "properties": { diff --git a/restapi/operations/console_api.go b/restapi/operations/console_api.go index 042572006..5b1957492 100644 --- a/restapi/operations/console_api.go +++ b/restapi/operations/console_api.go @@ -182,6 +182,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI { UserAPIGetBucketReplicationHandler: user_api.GetBucketReplicationHandlerFunc(func(params user_api.GetBucketReplicationParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation user_api.GetBucketReplication has not yet been implemented") }), + UserAPIGetBucketReplicationRuleHandler: user_api.GetBucketReplicationRuleHandlerFunc(func(params user_api.GetBucketReplicationRuleParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation user_api.GetBucketReplicationRule has not yet been implemented") + }), UserAPIGetBucketRetentionConfigHandler: user_api.GetBucketRetentionConfigHandlerFunc(func(params user_api.GetBucketRetentionConfigParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation user_api.GetBucketRetentionConfig has not yet been implemented") }), @@ -359,6 +362,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI { AdminAPIUpdateGroupHandler: admin_api.UpdateGroupHandlerFunc(func(params admin_api.UpdateGroupParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation admin_api.UpdateGroup has not yet been implemented") }), + UserAPIUpdateMultiBucketReplicationHandler: user_api.UpdateMultiBucketReplicationHandlerFunc(func(params user_api.UpdateMultiBucketReplicationParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation user_api.UpdateMultiBucketReplication has not yet been implemented") + }), AdminAPIUpdateUserGroupsHandler: admin_api.UpdateUserGroupsHandlerFunc(func(params admin_api.UpdateUserGroupsParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation admin_api.UpdateUserGroups has not yet been implemented") }), @@ -498,6 +504,8 @@ type ConsoleAPI struct { UserAPIGetBucketQuotaHandler user_api.GetBucketQuotaHandler // UserAPIGetBucketReplicationHandler sets the operation handler for the get bucket replication operation UserAPIGetBucketReplicationHandler user_api.GetBucketReplicationHandler + // UserAPIGetBucketReplicationRuleHandler sets the operation handler for the get bucket replication rule operation + UserAPIGetBucketReplicationRuleHandler user_api.GetBucketReplicationRuleHandler // UserAPIGetBucketRetentionConfigHandler sets the operation handler for the get bucket retention config operation UserAPIGetBucketRetentionConfigHandler user_api.GetBucketRetentionConfigHandler // UserAPIGetBucketRewindHandler sets the operation handler for the get bucket rewind operation @@ -616,6 +624,8 @@ type ConsoleAPI struct { UserAPIUpdateBucketLifecycleHandler user_api.UpdateBucketLifecycleHandler // AdminAPIUpdateGroupHandler sets the operation handler for the update group operation AdminAPIUpdateGroupHandler admin_api.UpdateGroupHandler + // UserAPIUpdateMultiBucketReplicationHandler sets the operation handler for the update multi bucket replication operation + UserAPIUpdateMultiBucketReplicationHandler user_api.UpdateMultiBucketReplicationHandler // AdminAPIUpdateUserGroupsHandler sets the operation handler for the update user groups operation AdminAPIUpdateUserGroupsHandler admin_api.UpdateUserGroupsHandler // AdminAPIUpdateUserInfoHandler sets the operation handler for the update user info operation @@ -824,6 +834,9 @@ func (o *ConsoleAPI) Validate() error { if o.UserAPIGetBucketReplicationHandler == nil { unregistered = append(unregistered, "user_api.GetBucketReplicationHandler") } + if o.UserAPIGetBucketReplicationRuleHandler == nil { + unregistered = append(unregistered, "user_api.GetBucketReplicationRuleHandler") + } if o.UserAPIGetBucketRetentionConfigHandler == nil { unregistered = append(unregistered, "user_api.GetBucketRetentionConfigHandler") } @@ -1001,6 +1014,9 @@ func (o *ConsoleAPI) Validate() error { if o.AdminAPIUpdateGroupHandler == nil { unregistered = append(unregistered, "admin_api.UpdateGroupHandler") } + if o.UserAPIUpdateMultiBucketReplicationHandler == nil { + unregistered = append(unregistered, "user_api.UpdateMultiBucketReplicationHandler") + } if o.AdminAPIUpdateUserGroupsHandler == nil { unregistered = append(unregistered, "admin_api.UpdateUserGroupsHandler") } @@ -1268,6 +1284,10 @@ func (o *ConsoleAPI) initHandlerCache() { if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } + o.handlers["GET"]["/buckets/{bucket_name}/replication/{rule_id}"] = user_api.NewGetBucketReplicationRule(o.context, o.UserAPIGetBucketReplicationRuleHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } o.handlers["GET"]["/buckets/{bucket_name}/retention"] = user_api.NewGetBucketRetentionConfig(o.context, o.UserAPIGetBucketRetentionConfigHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) @@ -1504,6 +1524,10 @@ func (o *ConsoleAPI) initHandlerCache() { if o.handlers["PUT"] == nil { o.handlers["PUT"] = make(map[string]http.Handler) } + o.handlers["PUT"]["/buckets/{bucket_name}/replication/{rule_id}"] = user_api.NewUpdateMultiBucketReplication(o.context, o.UserAPIUpdateMultiBucketReplicationHandler) + if o.handlers["PUT"] == nil { + o.handlers["PUT"] = make(map[string]http.Handler) + } o.handlers["PUT"]["/user/groups"] = admin_api.NewUpdateUserGroups(o.context, o.AdminAPIUpdateUserGroupsHandler) if o.handlers["PUT"] == nil { o.handlers["PUT"] = make(map[string]http.Handler) diff --git a/restapi/operations/user_api/get_bucket_replication_rule.go b/restapi/operations/user_api/get_bucket_replication_rule.go new file mode 100644 index 000000000..ff90ad3fe --- /dev/null +++ b/restapi/operations/user_api/get_bucket_replication_rule.go @@ -0,0 +1,88 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . +// + +package user_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/minio/console/models" +) + +// GetBucketReplicationRuleHandlerFunc turns a function with the right signature into a get bucket replication rule handler +type GetBucketReplicationRuleHandlerFunc func(GetBucketReplicationRuleParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetBucketReplicationRuleHandlerFunc) Handle(params GetBucketReplicationRuleParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// GetBucketReplicationRuleHandler interface for that can handle valid get bucket replication rule params +type GetBucketReplicationRuleHandler interface { + Handle(GetBucketReplicationRuleParams, *models.Principal) middleware.Responder +} + +// NewGetBucketReplicationRule creates a new http.Handler for the get bucket replication rule operation +func NewGetBucketReplicationRule(ctx *middleware.Context, handler GetBucketReplicationRuleHandler) *GetBucketReplicationRule { + return &GetBucketReplicationRule{Context: ctx, Handler: handler} +} + +/* GetBucketReplicationRule swagger:route GET /buckets/{bucket_name}/replication/{rule_id} UserAPI getBucketReplicationRule + +Bucket Replication + +*/ +type GetBucketReplicationRule struct { + Context *middleware.Context + Handler GetBucketReplicationRuleHandler +} + +func (o *GetBucketReplicationRule) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetBucketReplicationRuleParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *models.Principal + if uprinc != nil { + principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/restapi/operations/user_api/get_bucket_replication_rule_parameters.go b/restapi/operations/user_api/get_bucket_replication_rule_parameters.go new file mode 100644 index 000000000..44555bcae --- /dev/null +++ b/restapi/operations/user_api/get_bucket_replication_rule_parameters.go @@ -0,0 +1,112 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . +// + +package user_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" +) + +// NewGetBucketReplicationRuleParams creates a new GetBucketReplicationRuleParams object +// +// There are no default values defined in the spec. +func NewGetBucketReplicationRuleParams() GetBucketReplicationRuleParams { + + return GetBucketReplicationRuleParams{} +} + +// GetBucketReplicationRuleParams contains all the bound params for the get bucket replication rule operation +// typically these are obtained from a http.Request +// +// swagger:parameters GetBucketReplicationRule +type GetBucketReplicationRuleParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + BucketName string + /* + Required: true + In: path + */ + RuleID string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetBucketReplicationRuleParams() beforehand. +func (o *GetBucketReplicationRuleParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name") + if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil { + res = append(res, err) + } + + rRuleID, rhkRuleID, _ := route.Params.GetOK("rule_id") + if err := o.bindRuleID(rRuleID, rhkRuleID, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindBucketName binds and validates parameter BucketName from path. +func (o *GetBucketReplicationRuleParams) bindBucketName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.BucketName = raw + + return nil +} + +// bindRuleID binds and validates parameter RuleID from path. +func (o *GetBucketReplicationRuleParams) bindRuleID(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.RuleID = raw + + return nil +} diff --git a/restapi/operations/user_api/get_bucket_replication_rule_responses.go b/restapi/operations/user_api/get_bucket_replication_rule_responses.go new file mode 100644 index 000000000..0d43f1567 --- /dev/null +++ b/restapi/operations/user_api/get_bucket_replication_rule_responses.go @@ -0,0 +1,133 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . +// + +package user_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/minio/console/models" +) + +// GetBucketReplicationRuleOKCode is the HTTP code returned for type GetBucketReplicationRuleOK +const GetBucketReplicationRuleOKCode int = 200 + +/*GetBucketReplicationRuleOK A successful response. + +swagger:response getBucketReplicationRuleOK +*/ +type GetBucketReplicationRuleOK struct { + + /* + In: Body + */ + Payload *models.BucketReplicationRule `json:"body,omitempty"` +} + +// NewGetBucketReplicationRuleOK creates GetBucketReplicationRuleOK with default headers values +func NewGetBucketReplicationRuleOK() *GetBucketReplicationRuleOK { + + return &GetBucketReplicationRuleOK{} +} + +// WithPayload adds the payload to the get bucket replication rule o k response +func (o *GetBucketReplicationRuleOK) WithPayload(payload *models.BucketReplicationRule) *GetBucketReplicationRuleOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get bucket replication rule o k response +func (o *GetBucketReplicationRuleOK) SetPayload(payload *models.BucketReplicationRule) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetBucketReplicationRuleOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +/*GetBucketReplicationRuleDefault Generic error response. + +swagger:response getBucketReplicationRuleDefault +*/ +type GetBucketReplicationRuleDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewGetBucketReplicationRuleDefault creates GetBucketReplicationRuleDefault with default headers values +func NewGetBucketReplicationRuleDefault(code int) *GetBucketReplicationRuleDefault { + if code <= 0 { + code = 500 + } + + return &GetBucketReplicationRuleDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the get bucket replication rule default response +func (o *GetBucketReplicationRuleDefault) WithStatusCode(code int) *GetBucketReplicationRuleDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the get bucket replication rule default response +func (o *GetBucketReplicationRuleDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the get bucket replication rule default response +func (o *GetBucketReplicationRuleDefault) WithPayload(payload *models.Error) *GetBucketReplicationRuleDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get bucket replication rule default response +func (o *GetBucketReplicationRuleDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetBucketReplicationRuleDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/restapi/operations/user_api/get_bucket_replication_rule_urlbuilder.go b/restapi/operations/user_api/get_bucket_replication_rule_urlbuilder.go new file mode 100644 index 000000000..c0488d1dd --- /dev/null +++ b/restapi/operations/user_api/get_bucket_replication_rule_urlbuilder.go @@ -0,0 +1,124 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . +// + +package user_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// GetBucketReplicationRuleURL generates an URL for the get bucket replication rule operation +type GetBucketReplicationRuleURL struct { + BucketName string + RuleID string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetBucketReplicationRuleURL) WithBasePath(bp string) *GetBucketReplicationRuleURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetBucketReplicationRuleURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetBucketReplicationRuleURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/buckets/{bucket_name}/replication/{rule_id}" + + bucketName := o.BucketName + if bucketName != "" { + _path = strings.Replace(_path, "{bucket_name}", bucketName, -1) + } else { + return nil, errors.New("bucketName is required on GetBucketReplicationRuleURL") + } + + ruleID := o.RuleID + if ruleID != "" { + _path = strings.Replace(_path, "{rule_id}", ruleID, -1) + } else { + return nil, errors.New("ruleId is required on GetBucketReplicationRuleURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetBucketReplicationRuleURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetBucketReplicationRuleURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetBucketReplicationRuleURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetBucketReplicationRuleURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetBucketReplicationRuleURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetBucketReplicationRuleURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/restapi/operations/user_api/update_multi_bucket_replication.go b/restapi/operations/user_api/update_multi_bucket_replication.go new file mode 100644 index 000000000..24acb2d6a --- /dev/null +++ b/restapi/operations/user_api/update_multi_bucket_replication.go @@ -0,0 +1,88 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . +// + +package user_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" + + "github.com/minio/console/models" +) + +// UpdateMultiBucketReplicationHandlerFunc turns a function with the right signature into a update multi bucket replication handler +type UpdateMultiBucketReplicationHandlerFunc func(UpdateMultiBucketReplicationParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn UpdateMultiBucketReplicationHandlerFunc) Handle(params UpdateMultiBucketReplicationParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// UpdateMultiBucketReplicationHandler interface for that can handle valid update multi bucket replication params +type UpdateMultiBucketReplicationHandler interface { + Handle(UpdateMultiBucketReplicationParams, *models.Principal) middleware.Responder +} + +// NewUpdateMultiBucketReplication creates a new http.Handler for the update multi bucket replication operation +func NewUpdateMultiBucketReplication(ctx *middleware.Context, handler UpdateMultiBucketReplicationHandler) *UpdateMultiBucketReplication { + return &UpdateMultiBucketReplication{Context: ctx, Handler: handler} +} + +/* UpdateMultiBucketReplication swagger:route PUT /buckets/{bucket_name}/replication/{rule_id} UserAPI updateMultiBucketReplication + +Update Replication rule + +*/ +type UpdateMultiBucketReplication struct { + Context *middleware.Context + Handler UpdateMultiBucketReplicationHandler +} + +func (o *UpdateMultiBucketReplication) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewUpdateMultiBucketReplicationParams() + uprinc, aCtx, err := o.Context.Authorize(r, route) + if err != nil { + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + if aCtx != nil { + *r = *aCtx + } + var principal *models.Principal + if uprinc != nil { + principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise + } + + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params, principal) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/restapi/operations/user_api/update_multi_bucket_replication_parameters.go b/restapi/operations/user_api/update_multi_bucket_replication_parameters.go new file mode 100644 index 000000000..89a7d2d3c --- /dev/null +++ b/restapi/operations/user_api/update_multi_bucket_replication_parameters.go @@ -0,0 +1,151 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . +// + +package user_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "io" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/validate" + + "github.com/minio/console/models" +) + +// NewUpdateMultiBucketReplicationParams creates a new UpdateMultiBucketReplicationParams object +// +// There are no default values defined in the spec. +func NewUpdateMultiBucketReplicationParams() UpdateMultiBucketReplicationParams { + + return UpdateMultiBucketReplicationParams{} +} + +// UpdateMultiBucketReplicationParams contains all the bound params for the update multi bucket replication operation +// typically these are obtained from a http.Request +// +// swagger:parameters UpdateMultiBucketReplication +type UpdateMultiBucketReplicationParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: body + */ + Body *models.MultiBucketReplicationEdit + /* + Required: true + In: path + */ + BucketName string + /* + Required: true + In: path + */ + RuleID string +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewUpdateMultiBucketReplicationParams() beforehand. +func (o *UpdateMultiBucketReplicationParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.MultiBucketReplicationEdit + if err := route.Consumer.Consume(r.Body, &body); err != nil { + if err == io.EOF { + res = append(res, errors.Required("body", "body", "")) + } else { + res = append(res, errors.NewParseError("body", "body", "", err)) + } + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(context.Background()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Body = &body + } + } + } else { + res = append(res, errors.Required("body", "body", "")) + } + + rBucketName, rhkBucketName, _ := route.Params.GetOK("bucket_name") + if err := o.bindBucketName(rBucketName, rhkBucketName, route.Formats); err != nil { + res = append(res, err) + } + + rRuleID, rhkRuleID, _ := route.Params.GetOK("rule_id") + if err := o.bindRuleID(rRuleID, rhkRuleID, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindBucketName binds and validates parameter BucketName from path. +func (o *UpdateMultiBucketReplicationParams) bindBucketName(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.BucketName = raw + + return nil +} + +// bindRuleID binds and validates parameter RuleID from path. +func (o *UpdateMultiBucketReplicationParams) bindRuleID(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: true + // Parameter is provided by construction from the route + o.RuleID = raw + + return nil +} diff --git a/restapi/operations/user_api/update_multi_bucket_replication_responses.go b/restapi/operations/user_api/update_multi_bucket_replication_responses.go new file mode 100644 index 000000000..a404986d9 --- /dev/null +++ b/restapi/operations/user_api/update_multi_bucket_replication_responses.go @@ -0,0 +1,113 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . +// + +package user_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/minio/console/models" +) + +// UpdateMultiBucketReplicationCreatedCode is the HTTP code returned for type UpdateMultiBucketReplicationCreated +const UpdateMultiBucketReplicationCreatedCode int = 201 + +/*UpdateMultiBucketReplicationCreated A successful response. + +swagger:response updateMultiBucketReplicationCreated +*/ +type UpdateMultiBucketReplicationCreated struct { +} + +// NewUpdateMultiBucketReplicationCreated creates UpdateMultiBucketReplicationCreated with default headers values +func NewUpdateMultiBucketReplicationCreated() *UpdateMultiBucketReplicationCreated { + + return &UpdateMultiBucketReplicationCreated{} +} + +// WriteResponse to the client +func (o *UpdateMultiBucketReplicationCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(201) +} + +/*UpdateMultiBucketReplicationDefault Generic error response. + +swagger:response updateMultiBucketReplicationDefault +*/ +type UpdateMultiBucketReplicationDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewUpdateMultiBucketReplicationDefault creates UpdateMultiBucketReplicationDefault with default headers values +func NewUpdateMultiBucketReplicationDefault(code int) *UpdateMultiBucketReplicationDefault { + if code <= 0 { + code = 500 + } + + return &UpdateMultiBucketReplicationDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the update multi bucket replication default response +func (o *UpdateMultiBucketReplicationDefault) WithStatusCode(code int) *UpdateMultiBucketReplicationDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the update multi bucket replication default response +func (o *UpdateMultiBucketReplicationDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the update multi bucket replication default response +func (o *UpdateMultiBucketReplicationDefault) WithPayload(payload *models.Error) *UpdateMultiBucketReplicationDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the update multi bucket replication default response +func (o *UpdateMultiBucketReplicationDefault) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *UpdateMultiBucketReplicationDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(o._statusCode) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/restapi/operations/user_api/update_multi_bucket_replication_urlbuilder.go b/restapi/operations/user_api/update_multi_bucket_replication_urlbuilder.go new file mode 100644 index 000000000..fa4a0bbee --- /dev/null +++ b/restapi/operations/user_api/update_multi_bucket_replication_urlbuilder.go @@ -0,0 +1,124 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// 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 . +// + +package user_api + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" + "strings" +) + +// UpdateMultiBucketReplicationURL generates an URL for the update multi bucket replication operation +type UpdateMultiBucketReplicationURL struct { + BucketName string + RuleID string + + _basePath string + // avoid unkeyed usage + _ struct{} +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *UpdateMultiBucketReplicationURL) WithBasePath(bp string) *UpdateMultiBucketReplicationURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *UpdateMultiBucketReplicationURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *UpdateMultiBucketReplicationURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/buckets/{bucket_name}/replication/{rule_id}" + + bucketName := o.BucketName + if bucketName != "" { + _path = strings.Replace(_path, "{bucket_name}", bucketName, -1) + } else { + return nil, errors.New("bucketName is required on UpdateMultiBucketReplicationURL") + } + + ruleID := o.RuleID + if ruleID != "" { + _path = strings.Replace(_path, "{rule_id}", ruleID, -1) + } else { + return nil, errors.New("ruleId is required on UpdateMultiBucketReplicationURL") + } + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *UpdateMultiBucketReplicationURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *UpdateMultiBucketReplicationURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *UpdateMultiBucketReplicationURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on UpdateMultiBucketReplicationURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on UpdateMultiBucketReplicationURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *UpdateMultiBucketReplicationURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/restapi/user_buckets.go b/restapi/user_buckets.go index 3d7f4a9c7..b131f6e33 100644 --- a/restapi/user_buckets.go +++ b/restapi/user_buckets.go @@ -38,6 +38,7 @@ import ( "github.com/minio/console/restapi/operations" "github.com/minio/console/restapi/operations/user_api" "github.com/minio/minio-go/v7/pkg/policy" + "github.com/minio/minio-go/v7/pkg/replication" minioIAMPolicy "github.com/minio/pkg/iam/policy" ) @@ -114,6 +115,15 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) { } return user_api.NewGetBucketReplicationOK().WithPayload(getBucketReplication) }) + // get single bucket replication rule + api.UserAPIGetBucketReplicationRuleHandler = user_api.GetBucketReplicationRuleHandlerFunc(func(params user_api.GetBucketReplicationRuleParams, session *models.Principal) middleware.Responder { + getBucketReplicationRule, err := getBucketReplicationRuleResponse(session, params.BucketName, params.RuleID) + if err != nil { + return user_api.NewGetBucketReplicationRuleDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())}) + } + return user_api.NewGetBucketReplicationRuleOK().WithPayload(getBucketReplicationRule) + }) + // enable bucket encryption api.UserAPIEnableBucketEncryptionHandler = user_api.EnableBucketEncryptionHandlerFunc(func(params user_api.EnableBucketEncryptionParams, session *models.Principal) middleware.Responder { if err := enableBucketEncryptionResponse(session, params); err != nil { @@ -248,6 +258,7 @@ func getBucketReplicationResponse(session *models.Principal, bucketName string) ID: rule.ID, Priority: int32(rule.Priority), Status: string(rule.Status), + StorageClass: rule.Destination.StorageClass, }) } @@ -258,6 +269,75 @@ func getBucketReplicationResponse(session *models.Principal, bucketName string) return bucketRResponse, nil } +func getBucketReplicationRuleResponse(session *models.Principal, bucketName, ruleID string) (*models.BucketReplicationRule, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) + defer cancel() + + mClient, err := newMinioClient(session) + if err != nil { + LogError("error creating MinIO Client: %v", err) + return nil, err + } + // create a minioClient interface implementation + + // defining the client to be used + minioClient := minioClient{client: mClient} + + replicationRules, err := minioClient.getBucketReplication(ctx, bucketName) + + if err != nil { + return nil, err + } + + var foundRule replication.Rule + found := false + + for i := range replicationRules.Rules { + if replicationRules.Rules[i].ID == ruleID { + foundRule = replicationRules.Rules[i] + found = true + break + } + } + + if !found { + return nil, errors.New("no rule is set with this ID") + } + + repDelMarkerStatus := false + if foundRule.DeleteMarkerReplication.Status == "Enabled" { + repDelMarkerStatus = true + } + repDelStatus := false + if foundRule.DeleteReplication.Status == "Enabled" { + repDelStatus = true + } + existingObjects := false + if foundRule.ExistingObjectReplication.Status == "Enabled" { + existingObjects = true + } + metadataModifications := false + if foundRule.SourceSelectionCriteria.ReplicaModifications.Status == "Enabled" { + metadataModifications = true + } + + returnRule := &models.BucketReplicationRule{ + DeleteMarkerReplication: repDelMarkerStatus, + DeletesReplication: repDelStatus, + Destination: &models.BucketReplicationDestination{Bucket: foundRule.Destination.Bucket}, + Tags: foundRule.Tags(), + Prefix: foundRule.Prefix(), + ID: foundRule.ID, + Priority: int32(foundRule.Priority), + Status: string(foundRule.Status), + StorageClass: foundRule.Destination.StorageClass, + ExistingObjects: existingObjects, + MetadataReplication: metadataModifications, + } + + return returnRule, nil +} + func getBucketVersionedResponse(session *models.Principal, bucketName string) (*models.BucketVersioningResponse, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() diff --git a/swagger-console.yml b/swagger-console.yml index 72c1c9208..5019d0eb9 100644 --- a/swagger-console.yml +++ b/swagger-console.yml @@ -878,6 +878,57 @@ paths: - UserAPI /buckets/{bucket_name}/replication/{rule_id}: + get: + summary: Bucket Replication + operationId: GetBucketReplicationRule + parameters: + - name: bucket_name + in: path + required: true + type: string + - name: rule_id + in: path + required: true + type: string + responses: + 200: + description: A successful response. + schema: + $ref: "#/definitions/bucketReplicationRule" + default: + description: Generic error response. + schema: + $ref: "#/definitions/error" + tags: + - UserAPI + + put: + summary: Update Replication rule + operationId: UpdateMultiBucketReplication + parameters: + - name: bucket_name + in: path + required: true + type: string + - name: rule_id + in: path + required: true + type: string + - name: body + in: body + required: true + schema: + $ref: "#/definitions/multiBucketReplicationEdit" + responses: + 201: + description: A successful response. + default: + description: Generic error response. + schema: + $ref: "#/definitions/error" + tags: + - UserAPI + delete: summary: Bucket Replication Rule Delete operationId: DeleteBucketReplicationRule @@ -2884,12 +2935,16 @@ definitions: type: boolean deletes_replication: type: boolean + existingObjects: + type: boolean metadata_replication: type: boolean prefix: type: string tags: type: string + storageClass: + type: string destination: $ref: "#/definitions/bucketReplicationDestination" @@ -2971,12 +3026,42 @@ definitions: type: integer format: int32 default: 0 + storageClass: + type: string + default: "" bucketsRelation: type: array minLength: 1 items: $ref: "#/definitions/multiBucketsRelation" + multiBucketReplicationEdit: + properties: + ruleState: + type: boolean + arn: + type: string + prefix: + type: string + tags: + type: string + default: "" + replicateDeleteMarkers: + type: boolean + replicateDeletes: + type: boolean + replicateMetadata: + type: boolean + replicateExistingObjects: + type: boolean + priority: + type: integer + format: int32 + default: 0 + storageClass: + type: string + default: "" + multiBucketsRelation: type: object properties: