diff --git a/api/admin_client_mock.go b/api/admin_client_mock.go index 91e1b98dc2..0036b7a633 100644 --- a/api/admin_client_mock.go +++ b/api/admin_client_mock.go @@ -66,11 +66,12 @@ 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 - minioRemoveTierMock func(ctx context.Context, tierName string) 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 + minioVerifyTierStatusMock func(ctx context.Context, tierName string) error minioServiceTraceMock func(ctx context.Context, threshold int64, s3, internal, storage, os, errTrace bool) <-chan madmin.ServiceTraceInfo @@ -121,8 +122,8 @@ func (ac AdminClientMock) speedtest(_ context.Context, _ madmin.SpeedtestOpts) ( return nil, nil } -func (ac AdminClientMock) verifyTierStatus(_ context.Context, _ string) error { - return nil +func (ac AdminClientMock) verifyTierStatus(ctx context.Context, tier string) error { + return minioVerifyTierStatusMock(ctx, tier) } // mock function helpConfigKV() diff --git a/api/admin_tiers.go b/api/admin_tiers.go index 5c13cb36cf..b662d2b35f 100644 --- a/api/admin_tiers.go +++ b/api/admin_tiers.go @@ -24,6 +24,7 @@ import ( "github.com/dustin/go-humanize" "github.com/go-openapi/runtime/middleware" "github.com/minio/console/api/operations" + "github.com/minio/console/api/operations/tiering" tieringApi "github.com/minio/console/api/operations/tiering" "github.com/minio/console/models" "github.com/minio/madmin-go/v3" @@ -38,6 +39,13 @@ func registerAdminTiersHandlers(api *operations.ConsoleAPI) { } return tieringApi.NewTiersListOK().WithPayload(tierList) }) + api.TieringTiersListNamesHandler = tiering.TiersListNamesHandlerFunc(func(params tiering.TiersListNamesParams, session *models.Principal) middleware.Responder { + tierList, err := getTiersNameResponse(session, params) + if err != nil { + return tieringApi.NewTiersListDefault(err.Code).WithPayload(err.APIError) + } + return tieringApi.NewTiersListNamesOK().WithPayload(tierList) + }) // add a new tiers api.TieringAddTierHandler = tieringApi.AddTierHandlerFunc(func(params tieringApi.AddTierParams, session *models.Principal) middleware.Responder { err := getAddTierResponse(session, params) @@ -72,33 +80,36 @@ func registerAdminTiersHandlers(api *operations.ConsoleAPI) { }) } -// getNotificationEndpoints invokes admin info and returns a list of notification endpoints +// getTiers returns a list of tiers with their stats func getTiers(ctx context.Context, client MinioAdmin) (*models.TierListResponse, error) { tiers, err := client.listTiers(ctx) if err != nil { return nil, err } - tiersInfo, err := client.tierStats(ctx) + + tierStatsInfo, err := client.tierStats(ctx) if err != nil { return nil, err } + tiersStatsMap := make(map[string]madmin.TierStats, len(tierStatsInfo)) + for _, stat := range tierStatsInfo { + tiersStatsMap[stat.Name] = stat.Stats + } + var tiersList []*models.Tier for _, tierData := range tiers { - // Default Tier Stats - stats := madmin.TierStats{ + tierStats := madmin.TierStats{ NumObjects: 0, NumVersions: 0, TotalSize: 0, } - - // We look for the correct tier stats & set the values. - for _, stat := range tiersInfo { - if stat.Name == tierData.Name { - stats = stat.Stats - break - } + if stats, ok := tiersStatsMap[tierData.Name]; ok { + tierStats = stats } + + status := client.verifyTierStatus(ctx, tierData.Name) == nil + switch tierData.Type { case madmin.S3: tiersList = append(tiersList, &models.Tier{ @@ -112,11 +123,11 @@ func getTiers(ctx context.Context, client MinioAdmin) (*models.TierListResponse, Region: tierData.S3.Region, Secretkey: tierData.S3.SecretKey, Storageclass: tierData.S3.StorageClass, - Usage: humanize.IBytes(stats.TotalSize), - Objects: strconv.Itoa(stats.NumObjects), - Versions: strconv.Itoa(stats.NumVersions), + Usage: humanize.IBytes(tierStats.TotalSize), + Objects: strconv.Itoa(tierStats.NumObjects), + Versions: strconv.Itoa(tierStats.NumVersions), }, - Status: client.verifyTierStatus(ctx, tierData.Name) == nil, + Status: status, }) case madmin.MinIO: tiersList = append(tiersList, &models.Tier{ @@ -129,11 +140,11 @@ func getTiers(ctx context.Context, client MinioAdmin) (*models.TierListResponse, Prefix: tierData.MinIO.Prefix, Region: tierData.MinIO.Region, Secretkey: tierData.MinIO.SecretKey, - Usage: humanize.IBytes(stats.TotalSize), - Objects: strconv.Itoa(stats.NumObjects), - Versions: strconv.Itoa(stats.NumVersions), + Usage: humanize.IBytes(tierStats.TotalSize), + Objects: strconv.Itoa(tierStats.NumObjects), + Versions: strconv.Itoa(tierStats.NumVersions), }, - Status: client.verifyTierStatus(ctx, tierData.Name) == nil, + Status: status, }) case madmin.GCS: tiersList = append(tiersList, &models.Tier{ @@ -145,11 +156,11 @@ func getTiers(ctx context.Context, client MinioAdmin) (*models.TierListResponse, Name: tierData.Name, Prefix: tierData.GCS.Prefix, Region: tierData.GCS.Region, - Usage: humanize.IBytes(stats.TotalSize), - Objects: strconv.Itoa(stats.NumObjects), - Versions: strconv.Itoa(stats.NumVersions), + Usage: humanize.IBytes(tierStats.TotalSize), + Objects: strconv.Itoa(tierStats.NumObjects), + Versions: strconv.Itoa(tierStats.NumVersions), }, - Status: client.verifyTierStatus(ctx, tierData.Name) == nil, + Status: status, }) case madmin.Azure: tiersList = append(tiersList, &models.Tier{ @@ -162,16 +173,16 @@ func getTiers(ctx context.Context, client MinioAdmin) (*models.TierListResponse, Name: tierData.Name, Prefix: tierData.Azure.Prefix, Region: tierData.Azure.Region, - Usage: humanize.IBytes(stats.TotalSize), - Objects: strconv.Itoa(stats.NumObjects), - Versions: strconv.Itoa(stats.NumVersions), + Usage: humanize.IBytes(tierStats.TotalSize), + Objects: strconv.Itoa(tierStats.NumObjects), + Versions: strconv.Itoa(tierStats.NumVersions), }, - Status: client.verifyTierStatus(ctx, tierData.Name) == nil, + Status: status, }) case madmin.Unsupported: tiersList = append(tiersList, &models.Tier{ Type: models.TierTypeUnsupported, - Status: client.verifyTierStatus(ctx, tierData.Name) == nil, + Status: status, }) } } @@ -200,6 +211,42 @@ func getTiersResponse(session *models.Principal, params tieringApi.TiersListPara return tiersResp, nil } +// getTiersNameResponse returns a response with a list of tiers' names +func getTiersNameResponse(session *models.Principal, params tieringApi.TiersListNamesParams) (*models.TiersNameListResponse, *CodedAPIError) { + ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) + defer cancel() + mAdmin, err := NewMinioAdminClient(params.HTTPRequest.Context(), session) + if err != nil { + return nil, ErrorWithContext(ctx, err) + } + // create a minioClient interface implementation + // defining the client to be used + adminClient := AdminClient{Client: mAdmin} + // serialize output + tiersResp, err := getTiersName(ctx, adminClient) + if err != nil { + return nil, ErrorWithContext(ctx, err) + } + return tiersResp, nil +} + +// getTiersName fetches listTiers and returns a list of the tiers' names +func getTiersName(ctx context.Context, client MinioAdmin) (*models.TiersNameListResponse, error) { + tiers, err := client.listTiers(ctx) + if err != nil { + return nil, err + } + + tiersNameList := make([]string, len(tiers)) + for i, tierData := range tiers { + tiersNameList[i] = tierData.Name + } + + return &models.TiersNameListResponse{ + Items: tiersNameList, + }, nil +} + func addTier(ctx context.Context, client MinioAdmin, params *tieringApi.AddTierParams) error { var cfg *madmin.TierConfig var err error diff --git a/api/admin_tiers_test.go b/api/admin_tiers_test.go index 12cdb7c283..791a4e35dd 100644 --- a/api/admin_tiers_test.go +++ b/api/admin_tiers_test.go @@ -36,12 +36,12 @@ func TestGetTiers(t *testing.T) { function := "getTiers()" ctx, cancel := context.WithCancel(context.Background()) defer cancel() - // Test-1 : getBucketLifecycle() get list of tiers + // Test-1 : getTiers() get list of tiers // mock lifecycle response from MinIO returnListMock := []*madmin.TierConfig{ { Version: "V1", - Type: madmin.TierType(0), + Type: madmin.S3, Name: "S3 Tier", S3: &madmin.TierS3{ Endpoint: "https://s3tier.test.com/", @@ -53,6 +53,19 @@ func TestGetTiers(t *testing.T) { StorageClass: "TT1", }, }, + { + Version: "V1", + Type: madmin.MinIO, + Name: "MinIO Tier", + MinIO: &madmin.TierMinIO{ + Endpoint: "https://minio-endpoint.test.com/", + AccessKey: "access", + SecretKey: "secret", + Bucket: "somebucket", + Prefix: "p1", + Region: "us-east-2", + }, + }, } returnStatsMock := []madmin.TierInfo{ @@ -61,6 +74,11 @@ func TestGetTiers(t *testing.T) { Type: "internal", Stats: madmin.TierStats{NumObjects: 2, NumVersions: 2, TotalSize: 228915}, }, + { + Name: "MinIO Tier", + Type: "internal", + Stats: madmin.TierStats{NumObjects: 10, NumVersions: 3, TotalSize: 132788}, + }, { Name: "S3 Tier", Type: "s3", @@ -71,7 +89,7 @@ func TestGetTiers(t *testing.T) { expectedOutput := &models.TierListResponse{ Items: []*models.Tier{ { - Type: "S3", + Type: models.TierTypeS3, S3: &models.TierS3{ Accesskey: "Access Key", Secretkey: "Secret Key", @@ -85,6 +103,23 @@ func TestGetTiers(t *testing.T) { Objects: "0", Versions: "0", }, + Status: false, + }, + { + Type: models.TierTypeMinio, + Minio: &models.TierMinio{ + Accesskey: "access", + Secretkey: "secret", + Bucket: "somebucket", + Endpoint: "https://minio-endpoint.test.com/", + Name: "MinIO Tier", + Prefix: "p1", + Region: "us-east-2", + Usage: "130 KiB", + Objects: "10", + Versions: "3", + }, + Status: false, }, }, } @@ -97,47 +132,20 @@ func TestGetTiers(t *testing.T) { return returnStatsMock, nil } + minioVerifyTierStatusMock = func(_ context.Context, _ string) error { + return fmt.Errorf("someerror") + } + tiersList, err := getTiers(ctx, adminClient) if err != nil { t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) } // verify length of tiers list is correct assert.Equal(len(tiersList.Items), len(returnListMock), fmt.Sprintf("Failed on %s: length of lists is not the same", function)) - for i, conf := range returnListMock { - switch conf.Type { - case madmin.TierType(0): - // S3 - assert.Equal(expectedOutput.Items[i].S3.Name, conf.Name) - assert.Equal(expectedOutput.Items[i].S3.Bucket, conf.S3.Bucket) - assert.Equal(expectedOutput.Items[i].S3.Prefix, conf.S3.Prefix) - assert.Equal(expectedOutput.Items[i].S3.Accesskey, conf.S3.AccessKey) - assert.Equal(expectedOutput.Items[i].S3.Secretkey, conf.S3.SecretKey) - assert.Equal(expectedOutput.Items[i].S3.Endpoint, conf.S3.Endpoint) - assert.Equal(expectedOutput.Items[i].S3.Region, conf.S3.Region) - assert.Equal(expectedOutput.Items[i].S3.Storageclass, conf.S3.StorageClass) - case madmin.TierType(1): - // Azure - assert.Equal(expectedOutput.Items[i].Azure.Name, conf.Name) - assert.Equal(expectedOutput.Items[i].Azure.Bucket, conf.Azure.Bucket) - assert.Equal(expectedOutput.Items[i].Azure.Prefix, conf.Azure.Prefix) - assert.Equal(expectedOutput.Items[i].Azure.Accountkey, conf.Azure.AccountKey) - assert.Equal(expectedOutput.Items[i].Azure.Accountname, conf.Azure.AccountName) - assert.Equal(expectedOutput.Items[i].Azure.Endpoint, conf.Azure.Endpoint) - assert.Equal(expectedOutput.Items[i].Azure.Region, conf.Azure.Region) - case madmin.TierType(2): - // GCS - assert.Equal(expectedOutput.Items[i].Gcs.Name, conf.Name) - assert.Equal(expectedOutput.Items[i].Gcs.Bucket, conf.GCS.Bucket) - assert.Equal(expectedOutput.Items[i].Gcs.Prefix, conf.GCS.Prefix) - assert.Equal(expectedOutput.Items[i].Gcs.Creds, conf.GCS.Creds) - assert.Equal(expectedOutput.Items[i].Gcs.Endpoint, conf.GCS.Endpoint) - assert.Equal(expectedOutput.Items[i].Gcs.Region, conf.GCS.Region) - } - } - - // Test-2 : getBucketLifecycle() list is empty - returnListMockT2 := []*madmin.TierConfig{} + assert.Equal(expectedOutput, tiersList) + // Test-2 : getTiers() list is empty + returnListMockT2 := []*madmin.TierConfig{} minioListTiersMock = func(_ context.Context) ([]*madmin.TierConfig, error) { return returnListMockT2, nil } @@ -152,6 +160,78 @@ func TestGetTiers(t *testing.T) { } } +func TestGetTiersName(t *testing.T) { + assert := assert.New(t) + // mock minIO client + adminClient := AdminClientMock{} + + function := "getTiersName()" + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // Test-1 : getTiersName() get list tiers' names + // mock lifecycle response from MinIO + returnListMock := []*madmin.TierConfig{ + { + Version: "V1", + Type: madmin.S3, + Name: "S3 Tier", + S3: &madmin.TierS3{ + Endpoint: "https://s3tier.test.com/", + AccessKey: "Access Key", + SecretKey: "Secret Key", + Bucket: "buckets3", + Prefix: "pref1", + Region: "us-west-1", + StorageClass: "TT1", + }, + }, + { + Version: "V1", + Type: madmin.MinIO, + Name: "MinIO Tier", + MinIO: &madmin.TierMinIO{ + Endpoint: "https://minio-endpoint.test.com/", + AccessKey: "access", + SecretKey: "secret", + Bucket: "somebucket", + Prefix: "p1", + Region: "us-east-2", + }, + }, + } + + expectedOutput := &models.TiersNameListResponse{ + Items: []string{"S3 Tier", "MinIO Tier"}, + } + + minioListTiersMock = func(_ context.Context) ([]*madmin.TierConfig, error) { + return returnListMock, nil + } + + tiersList, err := getTiersName(ctx, adminClient) + if err != nil { + t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) + } + // verify length of tiers list is correct + assert.Equal(len(tiersList.Items), len(returnListMock), fmt.Sprintf("Failed on %s: length of lists is not the same", function)) + assert.Equal(expectedOutput, tiersList) + + // Test-2 : getTiersName() list is empty + returnListMockT2 := []*madmin.TierConfig{} + minioListTiersMock = func(_ context.Context) ([]*madmin.TierConfig, error) { + return returnListMockT2, nil + } + + emptyTierList, err := getTiersName(ctx, adminClient) + if err != nil { + t.Errorf("Failed on %s:, error occurred: %s", function, err.Error()) + } + + if len(emptyTierList.Items) != 0 { + t.Errorf("Failed on %s:, returned list was not empty", function) + } +} + func TestAddTier(t *testing.T) { assert := assert.New(t) // mock minIO client diff --git a/api/embedded_spec.go b/api/embedded_spec.go index 2a9083d84b..de48e606a0 100644 --- a/api/embedded_spec.go +++ b/api/embedded_spec.go @@ -545,6 +545,29 @@ func init() { } } }, + "/admin/tiers/names": { + "get": { + "tags": [ + "Tiering" + ], + "summary": "Returns a list of tiers' names for ilm", + "operationId": "TiersListNames", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/tiersNameListResponse" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/ApiError" + } + } + } + } + }, "/admin/tiers/{name}/remove": { "delete": { "tags": [ @@ -8888,6 +8911,17 @@ func init() { } } }, + "tiersNameListResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "transitionResponse": { "type": "object", "properties": { @@ -9757,6 +9791,29 @@ func init() { } } }, + "/admin/tiers/names": { + "get": { + "tags": [ + "Tiering" + ], + "summary": "Returns a list of tiers' names for ilm", + "operationId": "TiersListNames", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/tiersNameListResponse" + } + }, + "default": { + "description": "Generic error response.", + "schema": { + "$ref": "#/definitions/ApiError" + } + } + } + } + }, "/admin/tiers/{name}/remove": { "delete": { "tags": [ @@ -18308,6 +18365,17 @@ func init() { } } }, + "tiersNameListResponse": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "transitionResponse": { "type": "object", "properties": { diff --git a/api/operations/console_api.go b/api/operations/console_api.go index b5ed8f7b52..f4bab978e9 100644 --- a/api/operations/console_api.go +++ b/api/operations/console_api.go @@ -542,6 +542,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI { TieringTiersListHandler: tiering.TiersListHandlerFunc(func(params tiering.TiersListParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation tiering.TiersList has not yet been implemented") }), + TieringTiersListNamesHandler: tiering.TiersListNamesHandlerFunc(func(params tiering.TiersListNamesParams, principal *models.Principal) middleware.Responder { + return middleware.NotImplemented("operation tiering.TiersListNames has not yet been implemented") + }), BucketUpdateBucketLifecycleHandler: bucket.UpdateBucketLifecycleHandlerFunc(func(params bucket.UpdateBucketLifecycleParams, principal *models.Principal) middleware.Responder { return middleware.NotImplemented("operation bucket.UpdateBucketLifecycle has not yet been implemented") }), @@ -931,6 +934,8 @@ type ConsoleAPI struct { SubnetSubnetRegisterHandler subnet.SubnetRegisterHandler // TieringTiersListHandler sets the operation handler for the tiers list operation TieringTiersListHandler tiering.TiersListHandler + // TieringTiersListNamesHandler sets the operation handler for the tiers list names operation + TieringTiersListNamesHandler tiering.TiersListNamesHandler // BucketUpdateBucketLifecycleHandler sets the operation handler for the update bucket lifecycle operation BucketUpdateBucketLifecycleHandler bucket.UpdateBucketLifecycleHandler // IdpUpdateConfigurationHandler sets the operation handler for the update configuration operation @@ -1491,6 +1496,9 @@ func (o *ConsoleAPI) Validate() error { if o.TieringTiersListHandler == nil { unregistered = append(unregistered, "tiering.TiersListHandler") } + if o.TieringTiersListNamesHandler == nil { + unregistered = append(unregistered, "tiering.TiersListNamesHandler") + } if o.BucketUpdateBucketLifecycleHandler == nil { unregistered = append(unregistered, "bucket.UpdateBucketLifecycleHandler") } @@ -2226,6 +2234,10 @@ func (o *ConsoleAPI) initHandlerCache() { o.handlers["GET"] = make(map[string]http.Handler) } o.handlers["GET"]["/admin/tiers"] = tiering.NewTiersList(o.context, o.TieringTiersListHandler) + if o.handlers["GET"] == nil { + o.handlers["GET"] = make(map[string]http.Handler) + } + o.handlers["GET"]["/admin/tiers/names"] = tiering.NewTiersListNames(o.context, o.TieringTiersListNamesHandler) if o.handlers["PUT"] == nil { o.handlers["PUT"] = make(map[string]http.Handler) } diff --git a/api/operations/tiering/tiers_list_names.go b/api/operations/tiering/tiers_list_names.go new file mode 100644 index 0000000000..2e4203949d --- /dev/null +++ b/api/operations/tiering/tiers_list_names.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" +) + +// TiersListNamesHandlerFunc turns a function with the right signature into a tiers list names handler +type TiersListNamesHandlerFunc func(TiersListNamesParams, *models.Principal) middleware.Responder + +// Handle executing the request and returning a response +func (fn TiersListNamesHandlerFunc) Handle(params TiersListNamesParams, principal *models.Principal) middleware.Responder { + return fn(params, principal) +} + +// TiersListNamesHandler interface for that can handle valid tiers list names params +type TiersListNamesHandler interface { + Handle(TiersListNamesParams, *models.Principal) middleware.Responder +} + +// NewTiersListNames creates a new http.Handler for the tiers list names operation +func NewTiersListNames(ctx *middleware.Context, handler TiersListNamesHandler) *TiersListNames { + return &TiersListNames{Context: ctx, Handler: handler} +} + +/* + TiersListNames swagger:route GET /admin/tiers/names Tiering tiersListNames + +Returns a list of tiers' names for ilm +*/ +type TiersListNames struct { + Context *middleware.Context + Handler TiersListNamesHandler +} + +func (o *TiersListNames) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewTiersListNamesParams() + 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/tiers_list_names_parameters.go b/api/operations/tiering/tiers_list_names_parameters.go new file mode 100644 index 0000000000..7ab9445b85 --- /dev/null +++ b/api/operations/tiering/tiers_list_names_parameters.go @@ -0,0 +1,63 @@ +// 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" +) + +// NewTiersListNamesParams creates a new TiersListNamesParams object +// +// There are no default values defined in the spec. +func NewTiersListNamesParams() TiersListNamesParams { + + return TiersListNamesParams{} +} + +// TiersListNamesParams contains all the bound params for the tiers list names operation +// typically these are obtained from a http.Request +// +// swagger:parameters TiersListNames +type TiersListNamesParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` +} + +// 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 NewTiersListNamesParams() beforehand. +func (o *TiersListNamesParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/api/operations/tiering/tiers_list_names_responses.go b/api/operations/tiering/tiers_list_names_responses.go new file mode 100644 index 0000000000..0f30b084c1 --- /dev/null +++ b/api/operations/tiering/tiers_list_names_responses.go @@ -0,0 +1,135 @@ +// 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" +) + +// TiersListNamesOKCode is the HTTP code returned for type TiersListNamesOK +const TiersListNamesOKCode int = 200 + +/* +TiersListNamesOK A successful response. + +swagger:response tiersListNamesOK +*/ +type TiersListNamesOK struct { + + /* + In: Body + */ + Payload *models.TiersNameListResponse `json:"body,omitempty"` +} + +// NewTiersListNamesOK creates TiersListNamesOK with default headers values +func NewTiersListNamesOK() *TiersListNamesOK { + + return &TiersListNamesOK{} +} + +// WithPayload adds the payload to the tiers list names o k response +func (o *TiersListNamesOK) WithPayload(payload *models.TiersNameListResponse) *TiersListNamesOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the tiers list names o k response +func (o *TiersListNamesOK) SetPayload(payload *models.TiersNameListResponse) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *TiersListNamesOK) 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 + } + } +} + +/* +TiersListNamesDefault Generic error response. + +swagger:response tiersListNamesDefault +*/ +type TiersListNamesDefault struct { + _statusCode int + + /* + In: Body + */ + Payload *models.APIError `json:"body,omitempty"` +} + +// NewTiersListNamesDefault creates TiersListNamesDefault with default headers values +func NewTiersListNamesDefault(code int) *TiersListNamesDefault { + if code <= 0 { + code = 500 + } + + return &TiersListNamesDefault{ + _statusCode: code, + } +} + +// WithStatusCode adds the status to the tiers list names default response +func (o *TiersListNamesDefault) WithStatusCode(code int) *TiersListNamesDefault { + o._statusCode = code + return o +} + +// SetStatusCode sets the status to the tiers list names default response +func (o *TiersListNamesDefault) SetStatusCode(code int) { + o._statusCode = code +} + +// WithPayload adds the payload to the tiers list names default response +func (o *TiersListNamesDefault) WithPayload(payload *models.APIError) *TiersListNamesDefault { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the tiers list names default response +func (o *TiersListNamesDefault) SetPayload(payload *models.APIError) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *TiersListNamesDefault) 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/tiers_list_names_urlbuilder.go b/api/operations/tiering/tiers_list_names_urlbuilder.go new file mode 100644 index 0000000000..287badaf75 --- /dev/null +++ b/api/operations/tiering/tiers_list_names_urlbuilder.go @@ -0,0 +1,104 @@ +// 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" +) + +// TiersListNamesURL generates an URL for the tiers list names operation +type TiersListNamesURL struct { + _basePath string +} + +// 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 *TiersListNamesURL) WithBasePath(bp string) *TiersListNamesURL { + 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 *TiersListNamesURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *TiersListNamesURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/admin/tiers/names" + + _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 *TiersListNamesURL) 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 *TiersListNamesURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *TiersListNamesURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on TiersListNamesURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on TiersListNamesURL") + } + + 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 *TiersListNamesURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/models/tiers_name_list_response.go b/models/tiers_name_list_response.go new file mode 100644 index 0000000000..8ff1359a07 --- /dev/null +++ b/models/tiers_name_list_response.go @@ -0,0 +1,67 @@ +// 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 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" +) + +// TiersNameListResponse tiers name list response +// +// swagger:model tiersNameListResponse +type TiersNameListResponse struct { + + // items + Items []string `json:"items"` +} + +// Validate validates this tiers name list response +func (m *TiersNameListResponse) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this tiers name list response based on context it is used +func (m *TiersNameListResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *TiersNameListResponse) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *TiersNameListResponse) UnmarshalBinary(b []byte) error { + var res TiersNameListResponse + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/swagger.yml b/swagger.yml index 457944ce61..bec05df4b7 100644 --- a/swagger.yml +++ b/swagger.yml @@ -2705,6 +2705,22 @@ paths: tags: - Tiering + /admin/tiers/names: + get: + summary: Returns a list of tiers' names for ilm + operationId: TiersListNames + responses: + 200: + description: A successful response. + schema: + $ref: "#/definitions/tiersNameListResponse" + default: + description: Generic error response. + schema: + $ref: "#/definitions/ApiError" + tags: + - Tiering + /admin/tiers/{type}/{name}: get: summary: Get Tier @@ -2769,7 +2785,7 @@ paths: - Tiering /admin/tiers/{name}/remove: delete: - summary: Remove Tier + summary: Remove Tier operationId: RemoveTier parameters: - name: name @@ -2784,7 +2800,7 @@ paths: schema: $ref: "#/definitions/ApiError" tags: - - Tiering + - Tiering /nodes: get: @@ -5598,6 +5614,14 @@ definitions: items: $ref: "#/definitions/tier" + tiersNameListResponse: + type: object + properties: + items: + type: array + items: + type: string + tierCredentialsRequest: type: object properties: diff --git a/web-app/src/api/consoleApi.ts b/web-app/src/api/consoleApi.ts index 93d3c345d2..4abfd23ed1 100644 --- a/web-app/src/api/consoleApi.ts +++ b/web-app/src/api/consoleApi.ts @@ -1195,6 +1195,10 @@ export interface TierListResponse { items?: Tier[]; } +export interface TiersNameListResponse { + items?: string[]; +} + export interface TierCredentialsRequest { access_key?: string; secret_key?: string; @@ -4496,6 +4500,24 @@ export class Api< ...params, }), + /** + * No description + * + * @tags Tiering + * @name TiersListNames + * @summary Returns a list of tiers' names for ilm + * @request GET:/admin/tiers/names + * @secure + */ + tiersListNames: (params: RequestParams = {}) => + this.request({ + path: `/admin/tiers/names`, + method: "GET", + secure: true, + format: "json", + ...params, + }), + /** * No description * diff --git a/web-app/src/screens/Console/Buckets/BucketDetails/AddLifecycleModal.tsx b/web-app/src/screens/Console/Buckets/BucketDetails/AddLifecycleModal.tsx index 83955f6840..b4c17b8d12 100644 --- a/web-app/src/screens/Console/Buckets/BucketDetails/AddLifecycleModal.tsx +++ b/web-app/src/screens/Console/Buckets/BucketDetails/AddLifecycleModal.tsx @@ -33,7 +33,7 @@ import { } from "mds"; import { useSelector } from "react-redux"; import { api } from "api"; -import { BucketVersioningResponse, Tier } from "api/consoleApi"; +import { BucketVersioningResponse } from "api/consoleApi"; import { errorToHandler } from "api/errors"; import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary"; import { selDistSet, setModalErrorSnackMessage } from "../../../../systemSlice"; @@ -88,16 +88,13 @@ const AddLifecycleModal = ({ useEffect(() => { if (loadingTiers) { api.admin - .tiersList() + .tiersListNames() .then((res) => { - const tiersList: Tier[] | null = get(res.data, "items", []); + const tiersList: string[] | null = get(res.data, "items", []); if (tiersList !== null && tiersList.length >= 1) { - const objList = tiersList.map((tier: Tier) => { - const tierType = tier.type; - const value = get(tier, `${tierType}.name`, ""); - - return { label: value, value: value }; + const objList = tiersList.map((tierName: string) => { + return { label: tierName, value: tierName }; }); setTiersList(objList); @@ -107,11 +104,12 @@ const AddLifecycleModal = ({ } setLoadingTiers(false); }) - .catch(() => { + .catch((err) => { setLoadingTiers(false); + dispatch(setModalErrorSnackMessage(errorToHandler(err.error))); }); } - }, [loadingTiers]); + }, [dispatch, loadingTiers]); useEffect(() => { let valid = true; diff --git a/web-app/src/screens/Console/Buckets/BucketDetails/EditLifecycleConfiguration.tsx b/web-app/src/screens/Console/Buckets/BucketDetails/EditLifecycleConfiguration.tsx index 62cfbf662c..3046e12f95 100644 --- a/web-app/src/screens/Console/Buckets/BucketDetails/EditLifecycleConfiguration.tsx +++ b/web-app/src/screens/Console/Buckets/BucketDetails/EditLifecycleConfiguration.tsx @@ -30,10 +30,13 @@ import { Switch, } from "mds"; import { api } from "api"; -import { ApiError, Tier } from "api/consoleApi"; +import { ApiError } from "api/consoleApi"; import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary"; import { ITiersDropDown, LifeCycleItem } from "../types"; -import { setErrorSnackMessage } from "../../../../systemSlice"; +import { + setErrorSnackMessage, + setModalErrorSnackMessage, +} from "../../../../systemSlice"; import { useAppDispatch } from "../../../../store"; import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper"; import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector"; @@ -81,16 +84,13 @@ const EditLifecycleConfiguration = ({ useEffect(() => { if (loadingTiers) { api.admin - .tiersList() + .tiersListNames() .then((res) => { - const tiersList: Tier[] | null = get(res.data, "items", []); + const tiersList: string[] | null = get(res.data, "items", []); if (tiersList !== null && tiersList.length >= 1) { - const objList = tiersList.map((tier: Tier) => { - const tierType = tier.type; - const value = get(tier, `${tierType}.name`, ""); - - return { label: value, value: value }; + const objList = tiersList.map((tierName: string) => { + return { label: tierName, value: tierName }; }); setTiersList(objList); if (objList.length > 0) { @@ -99,11 +99,12 @@ const EditLifecycleConfiguration = ({ } setLoadingTiers(false); }) - .catch(() => { + .catch((err) => { setLoadingTiers(false); + dispatch(setModalErrorSnackMessage(errorToHandler(err.error))); }); } - }, [loadingTiers, lifecycleRule.transition?.storage_class]); + }, [dispatch, loadingTiers, lifecycleRule.transition?.storage_class]); useEffect(() => { let valid = true; diff --git a/web-app/src/screens/Console/Buckets/ListBuckets/BulkLifecycleModal.tsx b/web-app/src/screens/Console/Buckets/ListBuckets/BulkLifecycleModal.tsx index f7c739185d..cad0893911 100644 --- a/web-app/src/screens/Console/Buckets/ListBuckets/BulkLifecycleModal.tsx +++ b/web-app/src/screens/Console/Buckets/ListBuckets/BulkLifecycleModal.tsx @@ -36,7 +36,7 @@ import { ITiersDropDown } from "../types"; import { setModalErrorSnackMessage } from "../../../../systemSlice"; import { useAppDispatch } from "../../../../store"; import { api } from "api"; -import { MultiLifecycleResult, Tier } from "api/consoleApi"; +import { MultiLifecycleResult } from "api/consoleApi"; import { errorToHandler } from "api/errors"; interface IBulkReplicationModal { @@ -72,16 +72,13 @@ const AddBulkReplicationModal = ({ useEffect(() => { if (loadingTiers) { api.admin - .tiersList() + .tiersListNames() .then((res) => { - const tiersList: Tier[] | null = get(res.data, "items", []); + const tiersList: string[] | null = get(res.data, "items", []); if (tiersList !== null && tiersList.length >= 1) { - const objList = tiersList.map((tier: Tier) => { - const tierType = tier.type; - const value = get(tier, `${tierType}.name`, ""); - - return { label: value, value: value }; + const objList = tiersList.map((tierName: string) => { + return { label: tierName, value: tierName }; }); setTiersList(objList); @@ -96,7 +93,7 @@ const AddBulkReplicationModal = ({ dispatch(setModalErrorSnackMessage(errorToHandler(err.error))); }); } - }, [loadingTiers, dispatch]); + }, [dispatch, loadingTiers]); useEffect(() => { let valid = true;