From 5f7b563a012481ea73e566273d41fcd12ba26841 Mon Sep 17 00:00:00 2001 From: jinapurapu <65002498+jinapurapu@users.noreply.github.com> Date: Fri, 7 Jun 2024 10:01:10 -0700 Subject: [PATCH] Add Remove Tier feature (#3371) --- api/admin_client_mock.go | 13 +- api/admin_tiers.go | 30 +++++ api/client-admin.go | 7 ++ api/embedded_spec.go | 56 +++++++++ api/operations/console_api.go | 12 ++ api/operations/tiering/remove_tier.go | 88 +++++++++++++ .../tiering/remove_tier_parameters.go | 88 +++++++++++++ .../tiering/remove_tier_responses.go | 115 +++++++++++++++++ .../tiering/remove_tier_urlbuilder.go | 116 ++++++++++++++++++ swagger.yml | 18 +++ web-app/src/api/consoleApi.ts | 19 ++- .../DeleteTierConfirmModal.tsx | 79 ++++++++++++ .../ListTiersConfiguration.tsx | 29 +++++ 13 files changed, 665 insertions(+), 5 deletions(-) create mode 100644 api/operations/tiering/remove_tier.go create mode 100644 api/operations/tiering/remove_tier_parameters.go create mode 100644 api/operations/tiering/remove_tier_responses.go create mode 100644 api/operations/tiering/remove_tier_urlbuilder.go create mode 100644 web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx diff --git a/api/admin_client_mock.go b/api/admin_client_mock.go index 2165a1fc1..91e1b98dc 100644 --- a/api/admin_client_mock.go +++ b/api/admin_client_mock.go @@ -66,10 +66,11 @@ var ( deleteSiteReplicationInfoMock func(ctx context.Context, removeReq madmin.SRRemoveReq) (*madmin.ReplicateRemoveStatus, error) getSiteReplicationStatus func(ctx context.Context, params madmin.SRStatusOptions) (*madmin.SRStatusInfo, error) - minioListTiersMock func(ctx context.Context) ([]*madmin.TierConfig, error) - minioTierStatsMock func(ctx context.Context) ([]madmin.TierInfo, error) - minioAddTiersMock func(ctx context.Context, tier *madmin.TierConfig) error - minioEditTiersMock func(ctx context.Context, tierName string, creds madmin.TierCreds) error + minioListTiersMock func(ctx context.Context) ([]*madmin.TierConfig, error) + minioTierStatsMock func(ctx context.Context) ([]madmin.TierInfo, error) + minioAddTiersMock func(ctx context.Context, tier *madmin.TierConfig) error + minioRemoveTierMock func(ctx context.Context, tierName string) error + minioEditTiersMock func(ctx context.Context, tierName string, creds madmin.TierCreds) error minioServiceTraceMock func(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo @@ -345,6 +346,10 @@ func (ac AdminClientMock) addTier(ctx context.Context, tier *madmin.TierConfig) return minioAddTiersMock(ctx, tier) } +func (ac AdminClientMock) removeTier(ctx context.Context, tierName string) error { + return minioRemoveTierMock(ctx, tierName) +} + func (ac AdminClientMock) editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error { return minioEditTiersMock(ctx, tierName, creds) } diff --git a/api/admin_tiers.go b/api/admin_tiers.go index 122bd3bc1..5c13cb36c 100644 --- a/api/admin_tiers.go +++ b/api/admin_tiers.go @@ -62,6 +62,14 @@ func registerAdminTiersHandlers(api *operations.ConsoleAPI) { } return tieringApi.NewEditTierCredentialsOK() }) + // remove an empty tier + api.TieringRemoveTierHandler = tieringApi.RemoveTierHandlerFunc(func(params tieringApi.RemoveTierParams, session *models.Principal) middleware.Responder { + err := getRemoveTierResponse(session, params) + if err != nil { + return tieringApi.NewRemoveTierDefault(err.Code).WithPayload(err.APIError) + } + return tieringApi.NewRemoveTierNoContent() + }) } // getNotificationEndpoints invokes admin info and returns a list of notification endpoints @@ -409,3 +417,25 @@ func getEditTierCredentialsResponse(session *models.Principal, params tieringApi } return nil } + +func removeTier(ctx context.Context, client MinioAdmin, params *tieringApi.RemoveTierParams) error { + return client.removeTier(ctx, params.Name) +} + +func getRemoveTierResponse(session *models.Principal, params tieringApi.RemoveTierParams) *CodedAPIError { + ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) + defer cancel() + mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) + if err != nil { + return ErrorWithContext(ctx, err) + } + // create a minioClient interface implementation + // defining the client to be used + adminClient := AdminClient{Client: mAdmin} + // serialize output + err = removeTier(ctx, adminClient, ¶ms) + if err != nil { + return ErrorWithContext(ctx, err) + } + return nil +} diff --git a/api/client-admin.go b/api/client-admin.go index ba11a81c6..4c8016e71 100644 --- a/api/client-admin.go +++ b/api/client-admin.go @@ -96,6 +96,8 @@ type MinioAdmin interface { editTierCreds(ctx context.Context, tierName string, creds madmin.TierCreds) error // verify Tier status verifyTierStatus(ctx context.Context, tierName string) error + // remove empty Tier + removeTier(ctx context.Context, tierName string) error // Speedtest speedtest(ctx context.Context, opts madmin.SpeedtestOpts) (chan madmin.SpeedTestResult, error) // Site Relication @@ -448,6 +450,11 @@ func (ac AdminClient) verifyTierStatus(ctx context.Context, tierName string) err return ac.Client.VerifyTier(ctx, tierName) } +// implements madmin.RemoveTier() +func (ac AdminClient) removeTier(ctx context.Context, tierName string) error { + return ac.Client.RemoveTier(ctx, tierName) +} + func NewMinioAdminClient(ctx context.Context, sessionClaims *models.Principal) (*madmin.AdminClient, error) { clientIP := utils.ClientIPFromContext(ctx) adminClient, err := newAdminFromClaims(sessionClaims, clientIP) diff --git a/api/embedded_spec.go b/api/embedded_spec.go index 0f74a1ecf..2a9083d84 100644 --- a/api/embedded_spec.go +++ b/api/embedded_spec.go @@ -545,6 +545,34 @@ func init() { } } }, + "/admin/tiers/{name}/remove": { + "delete": { + "tags": [ + "Tiering" + ], + "summary": "Remove Tier", + "operationId": "RemoveTier", + "parameters": [ + { + "type": "string", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/ApiError" + } + } + } + } + }, "/admin/tiers/{type}/{name}": { "get": { "tags": [ @@ -9729,6 +9757,34 @@ func init() { } } }, + "/admin/tiers/{name}/remove": { + "delete": { + "tags": [ + "Tiering" + ], + "summary": "Remove Tier", + "operationId": "RemoveTier", + "parameters": [ + { + "type": "string", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "A successful response." + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/ApiError" + } + } + } + } + }, "/admin/tiers/{type}/{name}": { "get": { "tags": [ diff --git a/api/operations/console_api.go b/api/operations/console_api.go index 62ec5eba1..b5ed8f7b5 100644 --- a/api/operations/console_api.go +++ b/api/operations/console_api.go @@ -467,6 +467,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI { PolicyRemovePolicyHandler: policy.RemovePolicyHandlerFunc(func(params policy.RemovePolicyParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation policy.RemovePolicy has not yet been implemented") }), + TieringRemoveTierHandler: tiering.RemoveTierHandlerFunc(func(params tiering.RemoveTierParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation tiering.RemoveTier has not yet been implemented") + }), UserRemoveUserHandler: user.RemoveUserHandlerFunc(func(params user.RemoveUserParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation user.RemoveUser has not yet been implemented") }), @@ -878,6 +881,8 @@ type ConsoleAPI struct { GroupRemoveGroupHandler group.RemoveGroupHandler // PolicyRemovePolicyHandler sets the operation handler for the remove policy operation PolicyRemovePolicyHandler policy.RemovePolicyHandler + // TieringRemoveTierHandler sets the operation handler for the remove tier operation + TieringRemoveTierHandler tiering.RemoveTierHandler // UserRemoveUserHandler sets the operation handler for the remove user operation UserRemoveUserHandler user.RemoveUserHandler // ConfigurationResetConfigHandler sets the operation handler for the reset config operation @@ -1411,6 +1416,9 @@ func (o *ConsoleAPI) Validate() error { if o.PolicyRemovePolicyHandler == nil { unregistered = append(unregistered, "policy.RemovePolicyHandler") } + if o.TieringRemoveTierHandler == nil { + unregistered = append(unregistered, "tiering.RemoveTierHandler") + } if o.UserRemoveUserHandler == nil { unregistered = append(unregistered, "user.RemoveUserHandler") } @@ -2121,6 +2129,10 @@ func (o *ConsoleAPI) initHandlerCache() { if o.handlers["DELETE"] == nil { o.handlers["DELETE"] = make(map[string]http.Handler) } + o.handlers["DELETE"]["/admin/tiers/{name}/remove"] = tiering.NewRemoveTier(o.context, o.TieringRemoveTierHandler) + if o.handlers["DELETE"] == nil { + o.handlers["DELETE"] = make(map[string]http.Handler) + } o.handlers["DELETE"]["/user/{name}"] = user.NewRemoveUser(o.context, o.UserRemoveUserHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) diff --git a/api/operations/tiering/remove_tier.go b/api/operations/tiering/remove_tier.go new file mode 100644 index 000000000..7dbbe12d0 --- /dev/null +++ b/api/operations/tiering/remove_tier.go @@ -0,0 +1,88 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2023 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 tiering + +// 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" +) + +// RemoveTierHandlerFunc turns a function with the right signature into a remove tier handler +type RemoveTierHandlerFunc func(RemoveTierParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn RemoveTierHandlerFunc) Handle(params RemoveTierParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// RemoveTierHandler interface for that can handle valid remove tier params +type RemoveTierHandler interface { + Handle(RemoveTierParams, *models.Principal) middleware.Responder +} + +// NewRemoveTier creates a new http.Handler for the remove tier operation +func NewRemoveTier(ctx *middleware.Context, handler RemoveTierHandler) *RemoveTier { + return &RemoveTier{Context: ctx, Handler: handler} +} + +/* + RemoveTier swagger:route DELETE /admin/tiers/{name}/remove Tiering removeTier + +Remove Tier +*/ +type RemoveTier struct { + Context *middleware.Context + Handler RemoveTierHandler +} + +func (o *RemoveTier) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewRemoveTierParams() + 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/api/operations/tiering/remove_tier_parameters.go b/api/operations/tiering/remove_tier_parameters.go new file mode 100644 index 000000000..6eeb31770 --- /dev/null +++ b/api/operations/tiering/remove_tier_parameters.go @@ -0,0 +1,88 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2023 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 tiering + +// 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" +) + +// NewRemoveTierParams creates a new RemoveTierParams object +// +// There are no default values defined in the spec. +func NewRemoveTierParams() RemoveTierParams { + + return RemoveTierParams{} +} + +// RemoveTierParams contains all the bound params for the remove tier operation +// typically these are obtained from a http.Request +// +// swagger:parameters RemoveTier +type RemoveTierParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: path + */ + Name 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 NewRemoveTierParams() beforehand. +func (o *RemoveTierParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + rName, rhkName, _ := route.Params.GetOK("name") + if err := o.bindName(rName, rhkName, route.Formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +// bindName binds and validates parameter Name from path. +func (o *RemoveTierParams) bindName(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.Name = raw + + return nil +} diff --git a/api/operations/tiering/remove_tier_responses.go b/api/operations/tiering/remove_tier_responses.go new file mode 100644 index 000000000..a594274f4 --- /dev/null +++ b/api/operations/tiering/remove_tier_responses.go @@ -0,0 +1,115 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2023 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 tiering + +// 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" +) + +// RemoveTierNoContentCode is the HTTP code returned for type RemoveTierNoContent +const RemoveTierNoContentCode int = 204 + +/* +RemoveTierNoContent A successful response. + +swagger:response removeTierNoContent +*/ +type RemoveTierNoContent struct { +} + +// NewRemoveTierNoContent creates RemoveTierNoContent with default headers values +func NewRemoveTierNoContent() *RemoveTierNoContent { + + return &RemoveTierNoContent{} +} + +// WriteResponse to the client +func (o *RemoveTierNoContent) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(204) +} + +/* +RemoveTierDefault Generic error response. + +swagger:response removeTierDefault +*/ +type RemoveTierDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.APIError `json:"body,omitempty"` +} + +// NewRemoveTierDefault creates RemoveTierDefault with default headers values +func NewRemoveTierDefault(code int) *RemoveTierDefault { + if code <= 0 { + code = 500 + } + + return &RemoveTierDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the remove tier default response +func (o *RemoveTierDefault) WithStatusCode(code int) *RemoveTierDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the remove tier default response +func (o *RemoveTierDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the remove tier default response +func (o *RemoveTierDefault) WithPayload(payload *models.APIError) *RemoveTierDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the remove tier default response +func (o *RemoveTierDefault) SetPayload(payload *models.APIError) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *RemoveTierDefault) 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/api/operations/tiering/remove_tier_urlbuilder.go b/api/operations/tiering/remove_tier_urlbuilder.go new file mode 100644 index 000000000..cabe36758 --- /dev/null +++ b/api/operations/tiering/remove_tier_urlbuilder.go @@ -0,0 +1,116 @@ +// Code generated by go-swagger; DO NOT EDIT. + +// This file is part of MinIO Console Server +// Copyright (c) 2023 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 tiering + +// 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" +) + +// RemoveTierURL generates an URL for the remove tier operation +type RemoveTierURL struct { + Name 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 *RemoveTierURL) WithBasePath(bp string) *RemoveTierURL { + 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 *RemoveTierURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *RemoveTierURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/admin/tiers/{name}/remove" + + name := o.Name + if name != "" { + _path = strings.Replace(_path, "{name}", name, -1) + } else { + return nil, errors.New("name is required on RemoveTierURL") + } + + _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 *RemoveTierURL) 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 *RemoveTierURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *RemoveTierURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on RemoveTierURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on RemoveTierURL") + } + + 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 *RemoveTierURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/swagger.yml b/swagger.yml index 11182ef53..457944ce6 100644 --- a/swagger.yml +++ b/swagger.yml @@ -2767,6 +2767,24 @@ paths: $ref: "#/definitions/ApiError" tags: - Tiering + /admin/tiers/{name}/remove: + delete: + summary: Remove Tier + operationId: RemoveTier + parameters: + - name: name + in: path + required: true + type: string + responses: + 204: + description: A successful response. + default: + description: Generic error response. + schema: + $ref: "#/definitions/ApiError" + tags: + - Tiering /nodes: get: diff --git a/web-app/src/api/consoleApi.ts b/web-app/src/api/consoleApi.ts index 5c9c5d63b..9ab2f2b99 100644 --- a/web-app/src/api/consoleApi.ts +++ b/web-app/src/api/consoleApi.ts @@ -1761,7 +1761,7 @@ export class HttpClient { : payloadFormatter(body), }, ).then(async (response) => { - const r = response as HttpResponse; + const r = response.clone() as HttpResponse; r.data = null as unknown as T; r.error = null as unknown as E; @@ -4542,6 +4542,23 @@ export class Api< ...params, }), + /** + * No description + * + * @tags Tiering + * @name RemoveTier + * @summary Remove Tier + * @request DELETE:/admin/tiers/{name}/remove + * @secure + */ + removeTier: (name: string, params: RequestParams = {}) => + this.request({ + path: `/admin/tiers/${name}/remove`, + method: "DELETE", + secure: true, + ...params, + }), + /** * No description * diff --git a/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx new file mode 100644 index 000000000..0e534e766 --- /dev/null +++ b/web-app/src/screens/Console/Configurations/TiersConfiguration/DeleteTierConfirmModal.tsx @@ -0,0 +1,79 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2024 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 from "react"; +import { ConfirmModalIcon } from "mds"; +import { api } from "api"; +import { errorToHandler } from "api/errors"; +import { setModalErrorSnackMessage } from "../../../../systemSlice"; +import { useAppDispatch } from "../../../../store"; +import ConfirmDialog from "screens/Console/Common/ModalWrapper/ConfirmDialog"; + +interface ITierDeleteModal { + open: boolean; + closeModalAndRefresh: (refresh: boolean) => any; + tierName: string; +} + +const DeleteTierConfirmModal = ({ + open, + closeModalAndRefresh, + tierName, +}: ITierDeleteModal) => { + const dispatch = useAppDispatch(); + + const deleteTier = () => { + if (tierName !== "") { + api.admin + .removeTier(tierName) + .then(() => { + closeModalAndRefresh(true); + }) + .catch((err) => { + dispatch(setModalErrorSnackMessage(errorToHandler(err.error))); + }); + } else { + setModalErrorSnackMessage({ + errorMessage: "There was an error deleting the tier", + detailedError: "", + }); + } + }; + + return ( + } + isLoading={false} + onConfirm={() => deleteTier()} + onClose={() => closeModalAndRefresh(false)} + confirmationContent={ + + Are you sure you want to delete the tier {tierName}? +
+
+ Please note +
Only empty tiers can be deleted. If the tier has had objects + transitioned into it, it cannot be removed. +
+ } + /> + ); +}; + +export default DeleteTierConfirmModal; diff --git a/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx b/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx index 3620d994f..9ba9b12aa 100644 --- a/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx +++ b/web-app/src/screens/Console/Configurations/TiersConfiguration/ListTiersConfiguration.tsx @@ -41,6 +41,8 @@ import { actionsTray } from "../../Common/FormComponents/common/styleLibrary"; import { CONSOLE_UI_RESOURCE, IAM_PAGES, + IAM_PERMISSIONS, + IAM_ROLES, IAM_SCOPES, } from "../../../../common/SecureComponent/permissions"; import { @@ -61,6 +63,7 @@ import DistributedOnly from "../../Common/DistributedOnly/DistributedOnly"; import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper"; import PageHeaderWrapper from "../../Common/PageHeaderWrapper/PageHeaderWrapper"; import HelpMenu from "../../HelpMenu"; +import DeleteTierConfirmModal from "./DeleteTierConfirmModal"; const UpdateTierCredentialsModal = withSuspense( React.lazy(() => import("./UpdateTierCredentialsModal")), @@ -76,6 +79,9 @@ const ListTiersConfiguration = () => { const [isLoading, setIsLoading] = useState(true); const [updateCredentialsOpen, setUpdateCredentialsOpen] = useState(false); + + const [deleteTierModalOpen, setDeleteTierModalOpen] = + useState(false); const [selectedTier, setSelectedTier] = useState({ type: "unsupported", status: false, @@ -261,6 +267,10 @@ const ListTiersConfiguration = () => { const closeTierCredentials = () => { setUpdateCredentialsOpen(false); }; + const closeDeleteTier = () => { + setDeleteTierModalOpen(false); + setIsLoading(true); + }; useEffect(() => { dispatch(setHelpName("list-tiers-configuration")); @@ -276,6 +286,13 @@ const ListTiersConfiguration = () => { closeModalAndRefresh={closeTierCredentials} /> )} + {deleteTierModalOpen && ( + + )} } /> @@ -357,6 +374,18 @@ const ListTiersConfiguration = () => { setUpdateCredentialsOpen(true); }, }, + { + type: "delete", + isDisabled: !hasPermission( + "*", + IAM_PERMISSIONS[IAM_ROLES.BUCKET_LIFECYCLE], + true, + ), + onClick: (tierData: Tier) => { + setSelectedTier(tierData); + setDeleteTierModalOpen(true); + }, + }, ]} columns={[ {