Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
858d363e97 | ||
|
|
47704189d1 | ||
|
|
b72d424ec9 | ||
|
|
86426e95f7 | ||
|
|
e5f7870f5e | ||
|
|
c0ee739624 | ||
|
|
1dc99498d9 | ||
|
|
319d96c725 | ||
|
|
6d58290a89 | ||
|
|
666904f902 | ||
|
|
064533d8aa | ||
|
|
1768af9026 | ||
|
|
cb7513e9f0 | ||
|
|
645b45cf35 | ||
|
|
9f6d965ba2 | ||
|
|
5348400665 | ||
|
|
812fd5f253 | ||
|
|
da9b393e1b | ||
|
|
aeaa1a23ce | ||
|
|
a8d403a216 | ||
|
|
7bd898b2c7 | ||
|
|
dad66db49a | ||
|
|
adf3f929a4 | ||
|
|
3b23e877b5 | ||
|
|
af4bebb6eb | ||
|
|
8530eb5368 | ||
|
|
0ba1e76400 | ||
|
|
94096ee657 |
4
go.mod
4
go.mod
@@ -19,14 +19,14 @@ require (
|
||||
github.com/minio/mc v0.0.0-20200808005614-7e52c104bee1
|
||||
github.com/minio/minio v0.0.0-20200808024306-2a9819aff876
|
||||
github.com/minio/minio-go/v7 v7.0.5-0.20200807085956-d7db33ea7618
|
||||
github.com/minio/operator v0.0.0-20200806194125-c2ff646f4af1
|
||||
github.com/minio/operator v0.0.0-20200922064400-af3315add727
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/unrolled/secure v1.0.7
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
k8s.io/api v0.18.6
|
||||
k8s.io/apimachinery v0.18.6
|
||||
|
||||
@@ -7,7 +7,6 @@ rules:
|
||||
- ""
|
||||
resources:
|
||||
- namespaces
|
||||
- secrets
|
||||
- pods
|
||||
- services
|
||||
- events
|
||||
@@ -18,6 +17,18 @@ rules:
|
||||
- create
|
||||
- list
|
||||
- patch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- watch
|
||||
- create
|
||||
- list
|
||||
- patch
|
||||
- deletecollection
|
||||
- delete
|
||||
- apiGroups:
|
||||
- "storage.k8s.io"
|
||||
resources:
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.3.19
|
||||
image: minio/console:v0.3.26
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
args:
|
||||
- server
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.3.19
|
||||
image: minio/console:v0.3.26
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_OPERATOR_MODE
|
||||
|
||||
@@ -8,4 +8,4 @@ resources:
|
||||
- console-configmap.yaml
|
||||
- console-service.yaml
|
||||
- console-deployment.yaml
|
||||
- https://github.com/minio/operator/?ref=v3.0.10
|
||||
- https://github.com/minio/operator/?ref=v3.0.19
|
||||
|
||||
117
models/console_configuration.go
Normal file
117
models/console_configuration.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
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 (
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// ConsoleConfiguration console configuration
|
||||
//
|
||||
// swagger:model consoleConfiguration
|
||||
type ConsoleConfiguration struct {
|
||||
MetadataFields
|
||||
|
||||
// image
|
||||
Image string `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals this object from a JSON structure
|
||||
func (m *ConsoleConfiguration) UnmarshalJSON(raw []byte) error {
|
||||
// AO0
|
||||
var aO0 MetadataFields
|
||||
if err := swag.ReadJSON(raw, &aO0); err != nil {
|
||||
return err
|
||||
}
|
||||
m.MetadataFields = aO0
|
||||
|
||||
// AO1
|
||||
var dataAO1 struct {
|
||||
Image string `json:"image,omitempty"`
|
||||
}
|
||||
if err := swag.ReadJSON(raw, &dataAO1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Image = dataAO1.Image
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON marshals this object to a JSON structure
|
||||
func (m ConsoleConfiguration) MarshalJSON() ([]byte, error) {
|
||||
_parts := make([][]byte, 0, 2)
|
||||
|
||||
aO0, err := swag.WriteJSON(m.MetadataFields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_parts = append(_parts, aO0)
|
||||
var dataAO1 struct {
|
||||
Image string `json:"image,omitempty"`
|
||||
}
|
||||
|
||||
dataAO1.Image = m.Image
|
||||
|
||||
jsonDataAO1, errAO1 := swag.WriteJSON(dataAO1)
|
||||
if errAO1 != nil {
|
||||
return nil, errAO1
|
||||
}
|
||||
_parts = append(_parts, jsonDataAO1)
|
||||
return swag.ConcatJSON(_parts...), nil
|
||||
}
|
||||
|
||||
// Validate validates this console configuration
|
||||
func (m *ConsoleConfiguration) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
// validation for a type composition with MetadataFields
|
||||
if err := m.MetadataFields.Validate(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *ConsoleConfiguration) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *ConsoleConfiguration) UnmarshalBinary(b []byte) error {
|
||||
var res ConsoleConfiguration
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -42,6 +42,9 @@ type CreateTenantRequest struct {
|
||||
// annotations
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// console
|
||||
Console *ConsoleConfiguration `json:"console,omitempty"`
|
||||
|
||||
// console image
|
||||
ConsoleImage string `json:"console_image,omitempty"`
|
||||
|
||||
@@ -72,6 +75,9 @@ type CreateTenantRequest struct {
|
||||
// image registry
|
||||
ImageRegistry *ImageRegistry `json:"image_registry,omitempty"`
|
||||
|
||||
// labels
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// mounth path
|
||||
MounthPath string `json:"mounth_path,omitempty"`
|
||||
|
||||
@@ -102,6 +108,10 @@ type CreateTenantRequest struct {
|
||||
func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateConsole(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateEncryption(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -136,6 +146,24 @@ func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CreateTenantRequest) validateConsole(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Console) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Console != nil {
|
||||
if err := m.Console.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("console")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *CreateTenantRequest) validateEncryption(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Encryption) { // not required
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
//
|
||||
// swagger:model encryptionConfiguration
|
||||
type EncryptionConfiguration struct {
|
||||
MetadataFields
|
||||
|
||||
// aws
|
||||
Aws *AwsConfiguration `json:"aws,omitempty"`
|
||||
@@ -52,10 +53,100 @@ type EncryptionConfiguration struct {
|
||||
Vault *VaultConfiguration `json:"vault,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals this object from a JSON structure
|
||||
func (m *EncryptionConfiguration) UnmarshalJSON(raw []byte) error {
|
||||
// AO0
|
||||
var aO0 MetadataFields
|
||||
if err := swag.ReadJSON(raw, &aO0); err != nil {
|
||||
return err
|
||||
}
|
||||
m.MetadataFields = aO0
|
||||
|
||||
// AO1
|
||||
var dataAO1 struct {
|
||||
Aws *AwsConfiguration `json:"aws,omitempty"`
|
||||
|
||||
Client *KeyPairConfiguration `json:"client,omitempty"`
|
||||
|
||||
Gemalto *GemaltoConfiguration `json:"gemalto,omitempty"`
|
||||
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
Server *KeyPairConfiguration `json:"server,omitempty"`
|
||||
|
||||
Vault *VaultConfiguration `json:"vault,omitempty"`
|
||||
}
|
||||
if err := swag.ReadJSON(raw, &dataAO1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Aws = dataAO1.Aws
|
||||
|
||||
m.Client = dataAO1.Client
|
||||
|
||||
m.Gemalto = dataAO1.Gemalto
|
||||
|
||||
m.Image = dataAO1.Image
|
||||
|
||||
m.Server = dataAO1.Server
|
||||
|
||||
m.Vault = dataAO1.Vault
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON marshals this object to a JSON structure
|
||||
func (m EncryptionConfiguration) MarshalJSON() ([]byte, error) {
|
||||
_parts := make([][]byte, 0, 2)
|
||||
|
||||
aO0, err := swag.WriteJSON(m.MetadataFields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_parts = append(_parts, aO0)
|
||||
var dataAO1 struct {
|
||||
Aws *AwsConfiguration `json:"aws,omitempty"`
|
||||
|
||||
Client *KeyPairConfiguration `json:"client,omitempty"`
|
||||
|
||||
Gemalto *GemaltoConfiguration `json:"gemalto,omitempty"`
|
||||
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
Server *KeyPairConfiguration `json:"server,omitempty"`
|
||||
|
||||
Vault *VaultConfiguration `json:"vault,omitempty"`
|
||||
}
|
||||
|
||||
dataAO1.Aws = m.Aws
|
||||
|
||||
dataAO1.Client = m.Client
|
||||
|
||||
dataAO1.Gemalto = m.Gemalto
|
||||
|
||||
dataAO1.Image = m.Image
|
||||
|
||||
dataAO1.Server = m.Server
|
||||
|
||||
dataAO1.Vault = m.Vault
|
||||
|
||||
jsonDataAO1, errAO1 := swag.WriteJSON(dataAO1)
|
||||
if errAO1 != nil {
|
||||
return nil, errAO1
|
||||
}
|
||||
_parts = append(_parts, jsonDataAO1)
|
||||
return swag.ConcatJSON(_parts...), nil
|
||||
}
|
||||
|
||||
// Validate validates this encryption configuration
|
||||
func (m *EncryptionConfiguration) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
// validation for a type composition with MetadataFields
|
||||
if err := m.MetadataFields.Validate(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateAws(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
type Error struct {
|
||||
|
||||
// code
|
||||
Code int64 `json:"code,omitempty"`
|
||||
Code int32 `json:"code,omitempty"`
|
||||
|
||||
// message
|
||||
// Required: true
|
||||
|
||||
@@ -28,26 +28,33 @@ import (
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// UpdateCertificatesRequest update certificates request
|
||||
// MetadataConfiguration metadata configuration
|
||||
//
|
||||
// swagger:model updateCertificatesRequest
|
||||
type UpdateCertificatesRequest struct {
|
||||
// swagger:model metadataConfiguration
|
||||
type MetadataConfiguration struct {
|
||||
|
||||
// console
|
||||
Console *KeyPairConfiguration `json:"console,omitempty"`
|
||||
Console *MetadataFields `json:"console,omitempty"`
|
||||
|
||||
// kes
|
||||
Kes *MetadataFields `json:"kes,omitempty"`
|
||||
|
||||
// minio
|
||||
Minio *KeyPairConfiguration `json:"minio,omitempty"`
|
||||
Minio *MetadataFields `json:"minio,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this update certificates request
|
||||
func (m *UpdateCertificatesRequest) Validate(formats strfmt.Registry) error {
|
||||
// Validate validates this metadata configuration
|
||||
func (m *MetadataConfiguration) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateConsole(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateKes(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateMinio(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@@ -58,7 +65,7 @@ func (m *UpdateCertificatesRequest) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UpdateCertificatesRequest) validateConsole(formats strfmt.Registry) error {
|
||||
func (m *MetadataConfiguration) validateConsole(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Console) { // not required
|
||||
return nil
|
||||
@@ -76,7 +83,25 @@ func (m *UpdateCertificatesRequest) validateConsole(formats strfmt.Registry) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UpdateCertificatesRequest) validateMinio(formats strfmt.Registry) error {
|
||||
func (m *MetadataConfiguration) validateKes(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Kes) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
if m.Kes != nil {
|
||||
if err := m.Kes.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("kes")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MetadataConfiguration) validateMinio(formats strfmt.Registry) error {
|
||||
|
||||
if swag.IsZero(m.Minio) { // not required
|
||||
return nil
|
||||
@@ -95,7 +120,7 @@ func (m *UpdateCertificatesRequest) validateMinio(formats strfmt.Registry) error
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *UpdateCertificatesRequest) MarshalBinary() ([]byte, error) {
|
||||
func (m *MetadataConfiguration) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -103,8 +128,8 @@ func (m *UpdateCertificatesRequest) MarshalBinary() ([]byte, error) {
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *UpdateCertificatesRequest) UnmarshalBinary(b []byte) error {
|
||||
var res UpdateCertificatesRequest
|
||||
func (m *MetadataConfiguration) UnmarshalBinary(b []byte) error {
|
||||
var res MetadataConfiguration
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
66
models/metadata_fields.go
Normal file
66
models/metadata_fields.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
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 (
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// MetadataFields metadata fields
|
||||
//
|
||||
// swagger:model metadataFields
|
||||
type MetadataFields struct {
|
||||
|
||||
// annotations
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// labels
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
// node selector
|
||||
NodeSelector map[string]string `json:"node_selector,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this metadata fields
|
||||
func (m *MetadataFields) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *MetadataFields) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *MetadataFields) UnmarshalBinary(b []byte) error {
|
||||
var res MetadataFields
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
37
models/parity_response.go
Normal file
37
models/parity_response.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
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 (
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
// ParityResponse parity response
|
||||
//
|
||||
// swagger:model parityResponse
|
||||
type ParityResponse []string
|
||||
|
||||
// Validate validates this parity response
|
||||
func (m ParityResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
@@ -35,12 +35,21 @@ import (
|
||||
// swagger:model tenant
|
||||
type Tenant struct {
|
||||
|
||||
// console image
|
||||
ConsoleImage string `json:"console_image,omitempty"`
|
||||
|
||||
// creation date
|
||||
CreationDate string `json:"creation_date,omitempty"`
|
||||
|
||||
// current state
|
||||
CurrentState string `json:"currentState,omitempty"`
|
||||
|
||||
// deletion date
|
||||
DeletionDate string `json:"deletion_date,omitempty"`
|
||||
|
||||
// enable prometheus
|
||||
EnablePrometheus bool `json:"enable_prometheus,omitempty"`
|
||||
|
||||
// image
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ type TenantList struct {
|
||||
// current state
|
||||
CurrentState string `json:"currentState,omitempty"`
|
||||
|
||||
// deletion date
|
||||
DeletionDate string `json:"deletion_date,omitempty"`
|
||||
|
||||
// instance count
|
||||
InstanceCount int64 `json:"instance_count,omitempty"`
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ type UpdateTenantRequest struct {
|
||||
// Pattern: ^((.*?)/(.*?):(.+))$
|
||||
ConsoleImage string `json:"console_image,omitempty"`
|
||||
|
||||
// enable prometheus
|
||||
EnablePrometheus bool `json:"enable_prometheus,omitempty"`
|
||||
|
||||
// image
|
||||
// Pattern: ^((.*?)/(.*?):(.+))$
|
||||
Image string `json:"image,omitempty"`
|
||||
|
||||
@@ -207,6 +207,9 @@ func (m *Zone) UnmarshalBinary(b []byte) error {
|
||||
// swagger:model ZoneVolumeConfiguration
|
||||
type ZoneVolumeConfiguration struct {
|
||||
|
||||
// annotations
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
|
||||
// labels
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ var (
|
||||
bucketsDetail = "/buckets/:bucketName"
|
||||
serviceAccounts = "/service-accounts"
|
||||
tenants = "/tenants"
|
||||
tenantsDetail = "/tenants/:tenantName"
|
||||
tenantsDetail = "/namespaces/:tenantNamespace/tenants/:tenantName"
|
||||
heal = "/heal"
|
||||
)
|
||||
|
||||
|
||||
@@ -155,10 +155,10 @@ const (
|
||||
// or data key provided as plaintext.
|
||||
//
|
||||
// The returned ciphertext data consists of:
|
||||
// iv | AEAD ID | nonce | encrypted data
|
||||
// 32 1 12 ~ len(data)
|
||||
// AEAD ID | iv | nonce | encrypted data
|
||||
// 1 16 12 ~ len(data)
|
||||
func encrypt(plaintext, associatedData []byte) ([]byte, error) {
|
||||
iv, err := sioutil.Random(32) // 32 bit IV
|
||||
iv, err := sioutil.Random(16) // 16 bytes IV
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func encrypt(plaintext, associatedData []byte) ([]byte, error) {
|
||||
}
|
||||
case c20p1305:
|
||||
var sealingKey []byte
|
||||
sealingKey, err = chacha20.HChaCha20(derivedKey, iv)
|
||||
sealingKey, err = chacha20.HChaCha20(derivedKey, iv) // HChaCha20 expects nonce of 16 bytes
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -202,11 +202,11 @@ func encrypt(plaintext, associatedData []byte) ([]byte, error) {
|
||||
|
||||
sealedBytes := aead.Seal(nil, nonce, plaintext, associatedData)
|
||||
|
||||
// ciphertext = iv | AEAD ID | nonce | sealed bytes
|
||||
// ciphertext = AEAD ID | iv | nonce | sealed bytes
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write(iv)
|
||||
buf.WriteByte(algorithm)
|
||||
buf.Write(iv)
|
||||
buf.Write(nonce)
|
||||
buf.Write(sealedBytes)
|
||||
|
||||
@@ -218,16 +218,16 @@ func encrypt(plaintext, associatedData []byte) ([]byte, error) {
|
||||
// and a pbkdf2 derived key
|
||||
func decrypt(ciphertext []byte, associatedData []byte) ([]byte, error) {
|
||||
var (
|
||||
iv [32]byte
|
||||
algorithm [1]byte
|
||||
iv [16]byte
|
||||
nonce [12]byte // This depends on the AEAD but both used ciphers have the same nonce length.
|
||||
)
|
||||
|
||||
r := bytes.NewReader(ciphertext)
|
||||
if _, err := io.ReadFull(r, iv[:]); err != nil {
|
||||
if _, err := io.ReadFull(r, algorithm[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, algorithm[:]); err != nil {
|
||||
if _, err := io.ReadFull(r, iv[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, nonce[:]); err != nil {
|
||||
@@ -249,7 +249,7 @@ func decrypt(ciphertext []byte, associatedData []byte) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
case c20p1305:
|
||||
sealingKey, err := chacha20.HChaCha20(derivedKey, iv[:])
|
||||
sealingKey, err := chacha20.HChaCha20(derivedKey, iv[:]) // HChaCha20 expects nonce of 16 bytes
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
219
pkg/utils/parity.go
Normal file
219
pkg/utils/parity.go
Normal file
@@ -0,0 +1,219 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/minio/pkg/ellipses"
|
||||
)
|
||||
|
||||
// This file implements and supports ellipses pattern for
|
||||
// `minio server` command line arguments.
|
||||
|
||||
// Supported set sizes this is used to find the optimal
|
||||
// single set size.
|
||||
var setSizes = []uint64{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
|
||||
|
||||
// getDivisibleSize - returns a greatest common divisor of
|
||||
// all the ellipses sizes.
|
||||
func getDivisibleSize(totalSizes []uint64) (result uint64) {
|
||||
gcd := func(x, y uint64) uint64 {
|
||||
for y != 0 {
|
||||
x, y = y, x%y
|
||||
}
|
||||
return x
|
||||
}
|
||||
result = totalSizes[0]
|
||||
for i := 1; i < len(totalSizes); i++ {
|
||||
result = gcd(result, totalSizes[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// isValidSetSize - checks whether given count is a valid set size for erasure coding.
|
||||
var isValidSetSize = func(count uint64) bool {
|
||||
return (count >= setSizes[0] && count <= setSizes[len(setSizes)-1])
|
||||
}
|
||||
|
||||
// possibleSetCountsWithSymmetry returns symmetrical setCounts based on the
|
||||
// input argument patterns, the symmetry calculation is to ensure that
|
||||
// we also use uniform number of drives common across all ellipses patterns.
|
||||
func possibleSetCountsWithSymmetry(setCounts []uint64, argPatterns []ellipses.ArgPattern) []uint64 {
|
||||
var newSetCounts = make(map[uint64]struct{})
|
||||
for _, ss := range setCounts {
|
||||
var symmetry bool
|
||||
for _, argPattern := range argPatterns {
|
||||
for _, p := range argPattern {
|
||||
if uint64(len(p.Seq)) > ss {
|
||||
symmetry = uint64(len(p.Seq))%ss == 0
|
||||
} else {
|
||||
symmetry = ss%uint64(len(p.Seq)) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
// With no arg patterns, it is expected that user knows
|
||||
// the right symmetry, so either ellipses patterns are
|
||||
// provided (recommended) or no ellipses patterns.
|
||||
if _, ok := newSetCounts[ss]; !ok && (symmetry || argPatterns == nil) {
|
||||
newSetCounts[ss] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
setCounts = []uint64{}
|
||||
for setCount := range newSetCounts {
|
||||
setCounts = append(setCounts, setCount)
|
||||
}
|
||||
|
||||
// Not necessarily needed but it ensures to the readers
|
||||
// eyes that we prefer a sorted setCount slice for the
|
||||
// subsequent function to figure out the right common
|
||||
// divisor, it avoids loops.
|
||||
sort.Slice(setCounts, func(i, j int) bool {
|
||||
return setCounts[i] < setCounts[j]
|
||||
})
|
||||
|
||||
return setCounts
|
||||
}
|
||||
|
||||
func commonSetDriveCount(divisibleSize uint64, setCounts []uint64) (setSize uint64) {
|
||||
// prefers setCounts to be sorted for optimal behavior.
|
||||
if divisibleSize < setCounts[len(setCounts)-1] {
|
||||
return divisibleSize
|
||||
}
|
||||
|
||||
// Figure out largest value of total_drives_in_erasure_set which results
|
||||
// in least number of total_drives/total_drives_erasure_set ratio.
|
||||
prevD := divisibleSize / setCounts[0]
|
||||
for _, cnt := range setCounts {
|
||||
if divisibleSize%cnt == 0 {
|
||||
d := divisibleSize / cnt
|
||||
if d <= prevD {
|
||||
prevD = d
|
||||
setSize = cnt
|
||||
}
|
||||
}
|
||||
}
|
||||
return setSize
|
||||
}
|
||||
|
||||
// getSetIndexes returns list of indexes which provides the set size
|
||||
// on each index, this function also determines the final set size
|
||||
// The final set size has the affinity towards choosing smaller
|
||||
// indexes (total sets)
|
||||
func getSetIndexes(args []string, totalSizes []uint64, argPatterns []ellipses.ArgPattern) (setIndexes [][]uint64, err error) {
|
||||
if len(totalSizes) == 0 || len(args) == 0 {
|
||||
return nil, errors.New("invalid argument")
|
||||
}
|
||||
|
||||
setIndexes = make([][]uint64, len(totalSizes))
|
||||
for _, totalSize := range totalSizes {
|
||||
// Check if totalSize has minimum range upto setSize
|
||||
if totalSize < setSizes[0] {
|
||||
return nil, fmt.Errorf("incorrect number of endpoints provided %s", args)
|
||||
}
|
||||
}
|
||||
|
||||
commonSize := getDivisibleSize(totalSizes)
|
||||
possibleSetCounts := func(setSize uint64) (ss []uint64) {
|
||||
for _, s := range setSizes {
|
||||
if setSize%s == 0 {
|
||||
ss = append(ss, s)
|
||||
}
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
setCounts := possibleSetCounts(commonSize)
|
||||
if len(setCounts) == 0 {
|
||||
err = fmt.Errorf("incorrect number of endpoints provided %s, number of disks %d is not divisible by any supported erasure set sizes %d", args, commonSize, setSizes)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Returns possible set counts with symmetry.
|
||||
setCounts = possibleSetCountsWithSymmetry(setCounts, argPatterns)
|
||||
if len(setCounts) == 0 {
|
||||
err = fmt.Errorf("no symmetric distribution detected with input endpoints provided %s, disks %d cannot be spread symmetrically by any supported erasure set sizes %d", args, commonSize, setSizes)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Final set size with all the symmetry accounted for.
|
||||
setSize := commonSetDriveCount(commonSize, setCounts)
|
||||
|
||||
// Check whether setSize is with the supported range.
|
||||
if !isValidSetSize(setSize) {
|
||||
err = fmt.Errorf("incorrect number of endpoints provided %s, number of disks %d is not divisible by any supported erasure set sizes %d", args, commonSize, setSizes)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range totalSizes {
|
||||
for j := uint64(0); j < totalSizes[i]/setSize; j++ {
|
||||
setIndexes[i] = append(setIndexes[i], setSize)
|
||||
}
|
||||
}
|
||||
|
||||
return setIndexes, nil
|
||||
}
|
||||
|
||||
// Return the total size for each argument patterns.
|
||||
func getTotalSizes(argPatterns []ellipses.ArgPattern) []uint64 {
|
||||
var totalSizes []uint64
|
||||
for _, argPattern := range argPatterns {
|
||||
var totalSize uint64 = 1
|
||||
for _, p := range argPattern {
|
||||
totalSize = totalSize * uint64(len(p.Seq))
|
||||
}
|
||||
totalSizes = append(totalSizes, totalSize)
|
||||
}
|
||||
return totalSizes
|
||||
}
|
||||
|
||||
// PossibleParityValues returns possible parities for input args,
|
||||
// parties are calculated in uniform manner for one zone or
|
||||
// multiple zones, ensuring that parities returned are common
|
||||
// and applicable across all zones.
|
||||
func PossibleParityValues(args ...string) ([]string, error) {
|
||||
setIndexes, err := parseEndpointSet(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
maximumParity := setIndexes[0][0] / 2
|
||||
var parities []string
|
||||
for maximumParity >= 2 {
|
||||
parities = append(parities, fmt.Sprintf("EC:%d", maximumParity))
|
||||
maximumParity--
|
||||
}
|
||||
return parities, nil
|
||||
}
|
||||
|
||||
// Parses all arguments and returns an endpointSet which is a collection
|
||||
// of endpoints following the ellipses pattern, this is what is used
|
||||
// by the object layer for initializing itself.
|
||||
func parseEndpointSet(args ...string) (setIndexes [][]uint64, err error) {
|
||||
var argPatterns = make([]ellipses.ArgPattern, len(args))
|
||||
for i, arg := range args {
|
||||
patterns, err := ellipses.FindEllipsesPatterns(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
argPatterns[i] = patterns
|
||||
}
|
||||
|
||||
return getSetIndexes(args, getTotalSizes(argPatterns), argPatterns)
|
||||
}
|
||||
281
pkg/utils/parity_test.go
Normal file
281
pkg/utils/parity_test.go
Normal file
@@ -0,0 +1,281 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// All rights reserved
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/minio/pkg/ellipses"
|
||||
)
|
||||
|
||||
func TestGetDivisibleSize(t *testing.T) {
|
||||
testCases := []struct {
|
||||
totalSizes []uint64
|
||||
result uint64
|
||||
}{{[]uint64{24, 32, 16}, 8},
|
||||
{[]uint64{32, 8, 4}, 4},
|
||||
{[]uint64{8, 8, 8}, 8},
|
||||
{[]uint64{24}, 24},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run("", func(t *testing.T) {
|
||||
gotGCD := getDivisibleSize(testCase.totalSizes)
|
||||
if testCase.result != gotGCD {
|
||||
t.Errorf("Expected %v, got %v", testCase.result, gotGCD)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test tests calculating set indexes.
|
||||
func TestGetSetIndexes(t *testing.T) {
|
||||
testCases := []struct {
|
||||
args []string
|
||||
totalSizes []uint64
|
||||
indexes [][]uint64
|
||||
success bool
|
||||
}{
|
||||
// Invalid inputs.
|
||||
{
|
||||
[]string{"data{1...3}"},
|
||||
[]uint64{3},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"data/controller1/export{1...2}, data/controller2/export{1...4}, data/controller3/export{1...8}"},
|
||||
[]uint64{2, 4, 8},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...17}/export{1...52}"},
|
||||
[]uint64{14144},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Valid inputs.
|
||||
{
|
||||
[]string{"data{1...27}"},
|
||||
[]uint64{27},
|
||||
[][]uint64{{9, 9, 9}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"http://host{1...3}/data{1...180}"},
|
||||
[]uint64{540},
|
||||
[][]uint64{{15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"http://host{1...2}.rack{1...4}/data{1...180}"},
|
||||
[]uint64{1440},
|
||||
[][]uint64{{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"http://host{1...2}/data{1...180}"},
|
||||
[]uint64{360},
|
||||
[][]uint64{{12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data/controller1/export{1...4}, data/controller2/export{1...8}, data/controller3/export{1...12}"},
|
||||
[]uint64{4, 8, 12},
|
||||
[][]uint64{{4}, {4, 4}, {4, 4, 4}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...64}"},
|
||||
[]uint64{64},
|
||||
[][]uint64{{16, 16, 16, 16}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...24}"},
|
||||
[]uint64{24},
|
||||
[][]uint64{{12, 12}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data/controller{1...11}/export{1...8}"},
|
||||
[]uint64{88},
|
||||
[][]uint64{{11, 11, 11, 11, 11, 11, 11, 11}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...4}"},
|
||||
[]uint64{4},
|
||||
[][]uint64{{4}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data/controller1/export{1...10}, data/controller2/export{1...10}, data/controller3/export{1...10}"},
|
||||
[]uint64{10, 10, 10},
|
||||
[][]uint64{{10}, {10}, {10}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
[]string{"data{1...16}/export{1...52}"},
|
||||
[]uint64{832},
|
||||
[][]uint64{{16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run("", func(t *testing.T) {
|
||||
var argPatterns = make([]ellipses.ArgPattern, len(testCase.args))
|
||||
for i, arg := range testCase.args {
|
||||
patterns, err := ellipses.FindEllipsesPatterns(arg)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure %s", err)
|
||||
}
|
||||
argPatterns[i] = patterns
|
||||
}
|
||||
gotIndexes, err := getSetIndexes(testCase.args, testCase.totalSizes, argPatterns)
|
||||
if err != nil && testCase.success {
|
||||
t.Errorf("Expected success but failed instead %s", err)
|
||||
}
|
||||
if err == nil && !testCase.success {
|
||||
t.Errorf("Expected failure but passed instead")
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.indexes, gotIndexes) {
|
||||
t.Errorf("Expected %v, got %v", testCase.indexes, gotIndexes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test tests possible parities returned for any input args
|
||||
func TestPossibleParities(t *testing.T) {
|
||||
testCases := []struct {
|
||||
arg string
|
||||
parities []string
|
||||
success bool
|
||||
}{
|
||||
// Tests invalid inputs.
|
||||
{
|
||||
"...",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// No range specified.
|
||||
{
|
||||
"{...}",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Invalid range.
|
||||
{
|
||||
"http://minio{2...3}/export/set{1...0}",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Range cannot be smaller than 4 minimum.
|
||||
{
|
||||
"/export{1..2}",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Unsupported characters.
|
||||
{
|
||||
"/export/test{1...2O}",
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
// Tests valid inputs.
|
||||
{
|
||||
"{1...27}",
|
||||
[]string{"EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"/export/set{1...64}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// Valid input for distributed setup.
|
||||
{
|
||||
"http://minio{2...3}/export/set{1...64}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// Supporting some advanced cases.
|
||||
{
|
||||
"http://minio{1...64}.mydomain.net/data",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"http://rack{1...4}.mydomain.minio{1...16}/data",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// Supporting kubernetes cases.
|
||||
{
|
||||
"http://minio{0...15}.mydomain.net/data{0...1}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// No host regex, just disks.
|
||||
{
|
||||
"http://server1/data{1...32}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// No host regex, just disks with two position numerics.
|
||||
{
|
||||
"http://server1/data{01...32}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// More than 2 ellipses are supported as well.
|
||||
{
|
||||
"http://minio{2...3}/export/set{1...64}/test{1...2}",
|
||||
[]string{"EC:8", "EC:7", "EC:6", "EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// More than 1 ellipses per argument for standalone setup.
|
||||
{
|
||||
"/export{1...10}/disk{1...10}",
|
||||
[]string{"EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// IPv6 ellipses with hexadecimal expansion
|
||||
{
|
||||
"http://[2001:3984:3989::{1...a}]/disk{1...10}",
|
||||
[]string{"EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
// IPv6 ellipses with hexadecimal expansion with 3 position numerics.
|
||||
{
|
||||
"http://[2001:3984:3989::{001...00a}]/disk{1...10}",
|
||||
[]string{"EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run("", func(t *testing.T) {
|
||||
gotPs, err := PossibleParityValues(testCase.arg)
|
||||
if err != nil && testCase.success {
|
||||
t.Errorf("Expected success but failed instead %s", err)
|
||||
}
|
||||
if err == nil && !testCase.success {
|
||||
t.Errorf("Expected failure but passed instead")
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.parities, gotPs) {
|
||||
t.Errorf("Expected %v, got %v", testCase.parities, gotPs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
333
portal-ui/src/common/types.ts
Normal file
333
portal-ui/src/common/types.ts
Normal file
@@ -0,0 +1,333 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
/* Copyright (c) 2020 MinIO, Inc. All rights reserved. */
|
||||
|
||||
export interface ITenantsObject {
|
||||
tenants: ITenant[];
|
||||
}
|
||||
|
||||
export interface ITenant {
|
||||
creation_date: string;
|
||||
deletion_date: string;
|
||||
currentState: string;
|
||||
image: string;
|
||||
console_image: string;
|
||||
instance_count: string;
|
||||
name: string;
|
||||
namespace?: string;
|
||||
total_size: string;
|
||||
used_size: string;
|
||||
volume_count: string;
|
||||
volume_size: string;
|
||||
volumes_per_server?: string;
|
||||
zone_count: string;
|
||||
zones?: IZoneModel[];
|
||||
used_capacity?: string;
|
||||
endpoint?: string;
|
||||
storage_class?: string;
|
||||
enable_prometheus: boolean;
|
||||
}
|
||||
|
||||
export interface IVolumeConfiguration {
|
||||
size: string;
|
||||
storage_class_name: string;
|
||||
labels?: any;
|
||||
}
|
||||
|
||||
export interface ITenantCreator {
|
||||
name: string;
|
||||
service_name: string;
|
||||
enable_console: boolean;
|
||||
enable_prometheus: boolean;
|
||||
enable_tls: boolean;
|
||||
access_key: string;
|
||||
secret_key: string;
|
||||
image: string;
|
||||
console_image: string;
|
||||
zones: IZoneModel[];
|
||||
namespace: string;
|
||||
erasureCodingParity: number;
|
||||
tls?: ITLSTenantConfiguration;
|
||||
encryption?: IEncryptionConfiguration;
|
||||
idp?: IIDPConfiguration;
|
||||
annotations?: Object;
|
||||
}
|
||||
|
||||
export interface ITenantUpdateObject {
|
||||
image: string;
|
||||
image_registry?: IRegistryObject;
|
||||
}
|
||||
|
||||
export interface IRegistryObject {
|
||||
registry: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface ITenantUsage {
|
||||
used: string;
|
||||
disk_used: string;
|
||||
}
|
||||
|
||||
export interface IAffinityModel {
|
||||
podAntiAffinity: IPodAntiAffinityModel;
|
||||
}
|
||||
|
||||
export interface IPodAntiAffinityModel {
|
||||
requiredDuringSchedulingIgnoredDuringExecution: IPodAffinityTerm[];
|
||||
}
|
||||
|
||||
export interface IPodAffinityTerm {
|
||||
labelSelector: IPodAffinityTermLabelSelector;
|
||||
topologyKey: string;
|
||||
}
|
||||
|
||||
export interface IPodAffinityTermLabelSelector {
|
||||
matchExpressions: IMatchExpressionItem[];
|
||||
}
|
||||
|
||||
export interface IMatchExpressionItem {
|
||||
key: string;
|
||||
operator: string;
|
||||
values: string[];
|
||||
}
|
||||
|
||||
export interface ITolerationModel {
|
||||
effect: string;
|
||||
key: string;
|
||||
operator: string;
|
||||
value?: string;
|
||||
tolerationSeconds?: ITolerationSeconds;
|
||||
}
|
||||
|
||||
export interface ITolerationSeconds {
|
||||
seconds: number;
|
||||
}
|
||||
|
||||
export interface IResourceModel {
|
||||
requests: IResourceRequests;
|
||||
limits?: IResourceLimits;
|
||||
}
|
||||
|
||||
export interface IResourceRequests {
|
||||
memory: number;
|
||||
}
|
||||
|
||||
export interface IResourceLimits {
|
||||
memory: number;
|
||||
}
|
||||
|
||||
export interface ITLSTenantConfiguration {
|
||||
minio: ITLSConfiguration;
|
||||
console: ITLSConfiguration;
|
||||
}
|
||||
|
||||
export interface ITLSConfiguration {
|
||||
crt: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface IEncryptionConfiguration {
|
||||
server: ITLSConfiguration;
|
||||
client: ITLSConfiguration;
|
||||
master_key?: string;
|
||||
gemalto?: IGemaltoConfig;
|
||||
aws?: IAWSConfig;
|
||||
vault?: IVaultConfig;
|
||||
}
|
||||
|
||||
export interface IVaultConfig {
|
||||
endpoint: string;
|
||||
engine?: string;
|
||||
namespace?: string;
|
||||
prefix?: string;
|
||||
approle: IApproleConfig;
|
||||
tls: IVaultTLSConfig;
|
||||
status: IVaultStatusConfig;
|
||||
}
|
||||
|
||||
export interface IGemaltoConfig {
|
||||
keysecure: IKeysecureConfig;
|
||||
}
|
||||
|
||||
export interface IAWSConfig {
|
||||
secretsmanager: ISecretsManagerConfig;
|
||||
}
|
||||
|
||||
export interface IApproleConfig {
|
||||
engine: string;
|
||||
id: string;
|
||||
secret: string;
|
||||
retry: number;
|
||||
}
|
||||
|
||||
export interface IVaultTLSConfig {
|
||||
key: string;
|
||||
crt: string;
|
||||
ca: string;
|
||||
}
|
||||
|
||||
export interface IVaultStatusConfig {
|
||||
ping: number;
|
||||
}
|
||||
|
||||
export interface IKeysecureConfig {
|
||||
endpoint: string;
|
||||
credentials: IGemaltoCredentials;
|
||||
tls: IGemaltoTLS;
|
||||
}
|
||||
|
||||
export interface IGemaltoCredentials {
|
||||
token: string;
|
||||
domain: string;
|
||||
retry?: number;
|
||||
}
|
||||
|
||||
export interface IGemaltoTLS {
|
||||
ca: string;
|
||||
}
|
||||
|
||||
export interface ISecretsManagerConfig {
|
||||
endpoint: string;
|
||||
region: string;
|
||||
kmskey?: string;
|
||||
credentials: IAWSCredentials;
|
||||
}
|
||||
|
||||
export interface IAWSCredentials {
|
||||
accesskey: string;
|
||||
secretkey: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
export interface IIDPConfiguration {
|
||||
oidc?: IOpenIDConfiguration;
|
||||
active_directory: IActiveDirectoryConfiguration;
|
||||
}
|
||||
|
||||
export interface IOpenIDConfiguration {
|
||||
url: string;
|
||||
client_id: string;
|
||||
secret_id: string;
|
||||
}
|
||||
|
||||
export interface IActiveDirectoryConfiguration {
|
||||
url: string;
|
||||
skip_tls_verification: boolean;
|
||||
server_insecure: boolean;
|
||||
user_search_filter: string;
|
||||
group_Search_base_dn: string;
|
||||
group_search_filter: string;
|
||||
group_name_attribute: string;
|
||||
}
|
||||
|
||||
export interface IStorageDistribution {
|
||||
error: number;
|
||||
nodes: number;
|
||||
persistentVolumes: number;
|
||||
disks: number;
|
||||
pvSize: number;
|
||||
}
|
||||
|
||||
export interface IErasureCodeCalc {
|
||||
error: number;
|
||||
maxEC: string;
|
||||
erasureCodeSet: number;
|
||||
rawCapacity: string;
|
||||
storageFactors: IStorageFactors[];
|
||||
defaultEC: string;
|
||||
}
|
||||
|
||||
export interface IStorageFactors {
|
||||
erasureCode: string;
|
||||
storageFactor: number;
|
||||
maxCapacity: string;
|
||||
}
|
||||
|
||||
export interface ITenantHealthInList {
|
||||
name: string;
|
||||
namespace: string;
|
||||
status?: string;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface ITenantsListHealthRequest {
|
||||
tenants: ITenantHealthInList[];
|
||||
}
|
||||
|
||||
export interface IMaxAllocatableMemoryRequest {
|
||||
num_nodes: number;
|
||||
}
|
||||
|
||||
export interface IMaxAllocatableMemoryResponse {
|
||||
max_memory: number;
|
||||
}
|
||||
|
||||
export interface IEncryptionUpdateRequest {
|
||||
encryption: IEncryptionConfiguration;
|
||||
}
|
||||
|
||||
export interface IArchivedTenantsList {
|
||||
tenants: IArchivedTenant[];
|
||||
}
|
||||
|
||||
export interface IArchivedTenant {
|
||||
namespace: string;
|
||||
tenant: string;
|
||||
number_volumes: number;
|
||||
capacity: number;
|
||||
}
|
||||
|
||||
export interface IZoneModel {
|
||||
name?: string;
|
||||
servers: number;
|
||||
volumes_per_server: number;
|
||||
volume_configuration: IVolumeConfiguration;
|
||||
affinity?: IAffinityModel;
|
||||
tolerations?: ITolerationModel[];
|
||||
resources?: IResourceModel;
|
||||
}
|
||||
|
||||
export interface IUpdateZone {
|
||||
zones: IZoneModel[];
|
||||
}
|
||||
|
||||
export interface INode {
|
||||
name: string;
|
||||
freeSpace: string;
|
||||
totalSpace: string;
|
||||
disks: IDisk[];
|
||||
}
|
||||
|
||||
export interface IStorageType {
|
||||
freeSpace: string;
|
||||
totalSpace: string;
|
||||
storageClasses: string[];
|
||||
nodes: INode[];
|
||||
schedulableNodes: INode[];
|
||||
}
|
||||
|
||||
export interface IDisk {
|
||||
name: string;
|
||||
freeSpace: string;
|
||||
totalSpace: string;
|
||||
}
|
||||
|
||||
export interface ICapacity {
|
||||
value: string;
|
||||
unit: string;
|
||||
}
|
||||
@@ -15,6 +15,10 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import storage from "local-storage-fallback";
|
||||
import { ICapacity, IStorageType, IZoneModel } from "./types";
|
||||
|
||||
const minStReq = 1073741824; // Minimal Space required for MinIO
|
||||
const minMemReq = 2147483648; // Minimal Memory required for MinIO in bytes
|
||||
|
||||
export const units = [
|
||||
"B",
|
||||
@@ -28,6 +32,8 @@ export const units = [
|
||||
"YiB",
|
||||
];
|
||||
export const k8sUnits = ["Ki", "Mi", "Gi", "Ti", "Pi", "Ei"];
|
||||
export const k8sCalcUnits = ["B", ...k8sUnits];
|
||||
|
||||
export const niceBytes = (x: string) => {
|
||||
let l = 0,
|
||||
n = parseInt(x, 10) || 0;
|
||||
@@ -90,12 +96,19 @@ export const k8sfactorForDropdown = () => {
|
||||
};
|
||||
|
||||
//getBytes, converts from a value and a unit from units array to bytes
|
||||
export const getBytes = (value: string, unit: string) => {
|
||||
export const getBytes = (
|
||||
value: string,
|
||||
unit: string,
|
||||
fork8s: boolean = false
|
||||
) => {
|
||||
const vl: number = parseFloat(value);
|
||||
const powFactor = units.findIndex((element) => element === unit);
|
||||
|
||||
if (powFactor == -1) {
|
||||
return 0;
|
||||
const unitsTake = fork8s ? k8sCalcUnits : units;
|
||||
|
||||
const powFactor = unitsTake.findIndex((element) => element === unit);
|
||||
|
||||
if (powFactor === -1) {
|
||||
return "0";
|
||||
}
|
||||
const factor = Math.pow(1024, powFactor);
|
||||
const total = vl * factor;
|
||||
@@ -105,6 +118,220 @@ export const getBytes = (value: string, unit: string) => {
|
||||
|
||||
//getTotalSize gets the total size of a value & unit
|
||||
export const getTotalSize = (value: string, unit: string) => {
|
||||
const bytes = getBytes(value, unit).toString(10);
|
||||
const bytes = getBytes(value, unit, true).toString();
|
||||
return niceBytes(bytes);
|
||||
};
|
||||
|
||||
export const setMemoryResource = (
|
||||
memorySize: number,
|
||||
capacitySize: string,
|
||||
maxMemorySize: number
|
||||
) => {
|
||||
// value always comes as Gi
|
||||
const requestedSizeBytes = getBytes(memorySize.toString(10), "Gi", true);
|
||||
const memReqSize = parseInt(requestedSizeBytes, 10);
|
||||
if (maxMemorySize === 0) {
|
||||
return {
|
||||
error: "There is no memory available for the selected number of nodes",
|
||||
request: 0,
|
||||
limit: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (maxMemorySize < minMemReq) {
|
||||
return {
|
||||
error: "There are not enough memory resources available",
|
||||
request: 0,
|
||||
limit: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (memReqSize < minMemReq) {
|
||||
return {
|
||||
error: "The requested memory size must be greater than 2Gi",
|
||||
request: 0,
|
||||
limit: 0,
|
||||
};
|
||||
}
|
||||
if (memReqSize > maxMemorySize) {
|
||||
return {
|
||||
error:
|
||||
"The requested memory is greater than the max available memory for the selected number of nodes",
|
||||
request: 0,
|
||||
limit: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const capSize = parseInt(capacitySize, 10);
|
||||
let memLimitSize = memReqSize;
|
||||
// set memory limit based on the capacitySize
|
||||
// if capacity size is lower than 1TiB we use the limit equal to request
|
||||
if (capSize >= parseInt(getBytes("1", "Pi", true), 10)) {
|
||||
memLimitSize = Math.max(
|
||||
memReqSize,
|
||||
parseInt(getBytes("64", "Gi", true), 10)
|
||||
);
|
||||
} else if (capSize >= parseInt(getBytes("100", "Ti"), 10)) {
|
||||
memLimitSize = Math.max(
|
||||
memReqSize,
|
||||
parseInt(getBytes("32", "Gi", true), 10)
|
||||
);
|
||||
} else if (capSize >= parseInt(getBytes("10", "Ti"), 10)) {
|
||||
memLimitSize = Math.max(
|
||||
memReqSize,
|
||||
parseInt(getBytes("16", "Gi", true), 10)
|
||||
);
|
||||
} else if (capSize >= parseInt(getBytes("1", "Ti"), 10)) {
|
||||
memLimitSize = Math.max(
|
||||
memReqSize,
|
||||
parseInt(getBytes("8", "Gi", true), 10)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
error: "",
|
||||
request: memReqSize,
|
||||
limit: memLimitSize,
|
||||
};
|
||||
};
|
||||
|
||||
export const calculateDistribution = (
|
||||
capacityToUse: ICapacity,
|
||||
forcedNodes: number = 0,
|
||||
limitSize: number = 0
|
||||
) => {
|
||||
let numberOfNodes = {};
|
||||
const requestedSizeBytes = getBytes(
|
||||
capacityToUse.value,
|
||||
capacityToUse.unit,
|
||||
true
|
||||
);
|
||||
|
||||
if (parseInt(requestedSizeBytes, 10) < minStReq) {
|
||||
return {
|
||||
error: "The zone size must be greater than 1Gi",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
pvSize: 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (forcedNodes < 4) {
|
||||
return {
|
||||
error: "Number of nodes cannot be less than 4",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
pvSize: 0,
|
||||
};
|
||||
}
|
||||
|
||||
numberOfNodes = calculateStorage(requestedSizeBytes, forcedNodes, limitSize);
|
||||
|
||||
return numberOfNodes;
|
||||
};
|
||||
|
||||
const calculateStorage = (
|
||||
requestedBytes: string,
|
||||
forcedNodes: number,
|
||||
limitSize: number
|
||||
) => {
|
||||
// Size validation
|
||||
const intReqBytes = parseInt(requestedBytes, 10);
|
||||
const maxDiskSize = minStReq * 256; // 256 GiB
|
||||
|
||||
// We get the distribution
|
||||
return structureCalc(forcedNodes, intReqBytes, maxDiskSize, limitSize);
|
||||
};
|
||||
|
||||
const structureCalc = (
|
||||
nodes: number,
|
||||
desiredCapacity: number,
|
||||
maxDiskSize: number,
|
||||
maxClusterSize: number,
|
||||
disksPerNode: number = 0
|
||||
) => {
|
||||
if (
|
||||
isNaN(nodes) ||
|
||||
isNaN(desiredCapacity) ||
|
||||
isNaN(maxDiskSize) ||
|
||||
isNaN(maxClusterSize)
|
||||
) {
|
||||
return {
|
||||
error: "Some provided data is invalid, please try again.",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
volumePerDisk: 0,
|
||||
}; // Invalid Data
|
||||
}
|
||||
|
||||
let persistentVolumeSize = 0;
|
||||
let numberPersistentVolumes = 0;
|
||||
let volumesPerServer = 0;
|
||||
|
||||
if (disksPerNode === 0) {
|
||||
persistentVolumeSize = Math.floor(
|
||||
Math.min(desiredCapacity / Math.max(4, nodes), maxDiskSize)
|
||||
); // pVS = min((desiredCapacity / max(4 | nodes)) | maxDiskSize)
|
||||
|
||||
numberPersistentVolumes = desiredCapacity / persistentVolumeSize; // nPV = dC / pVS
|
||||
volumesPerServer = numberPersistentVolumes / nodes; // vPS = nPV / n
|
||||
}
|
||||
|
||||
if (disksPerNode) {
|
||||
volumesPerServer = disksPerNode;
|
||||
numberPersistentVolumes = volumesPerServer * nodes;
|
||||
persistentVolumeSize = Math.floor(
|
||||
desiredCapacity / numberPersistentVolumes
|
||||
);
|
||||
}
|
||||
|
||||
// Volumes are not exact, we force the volumes number & minimize the volume size
|
||||
if (volumesPerServer % 1 > 0) {
|
||||
volumesPerServer = Math.ceil(volumesPerServer); // Increment of volumes per server
|
||||
numberPersistentVolumes = volumesPerServer * nodes; // nPV = vPS * n
|
||||
persistentVolumeSize = Math.floor(
|
||||
desiredCapacity / numberPersistentVolumes
|
||||
); // pVS = dC / nPV
|
||||
|
||||
const limitSize = persistentVolumeSize * volumesPerServer * nodes; // lS = pVS * vPS * n
|
||||
|
||||
if (limitSize > maxClusterSize) {
|
||||
return {
|
||||
error: "We were not able to allocate this server.",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
volumePerDisk: 0,
|
||||
}; // Cannot allocate this server
|
||||
}
|
||||
}
|
||||
|
||||
if (persistentVolumeSize < minStReq) {
|
||||
return {
|
||||
error:
|
||||
"Disk Size with this combination would be less than 1Gi, please try another combination",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
volumePerDisk: 0,
|
||||
}; // Cannot allocate this volume size
|
||||
}
|
||||
|
||||
return {
|
||||
error: "",
|
||||
nodes,
|
||||
persistentVolumes: numberPersistentVolumes,
|
||||
disks: volumesPerServer,
|
||||
pvSize: persistentVolumeSize,
|
||||
};
|
||||
};
|
||||
|
||||
// Zone Name Generator
|
||||
export const generateZoneName = (zones: IZoneModel[]) => {
|
||||
const zoneCounter = zones.length;
|
||||
|
||||
return `zone-${zoneCounter}`;
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
LinearProgress
|
||||
LinearProgress,
|
||||
} from "@material-ui/core";
|
||||
import api from "../../../../common/api";
|
||||
import { BucketList } from "../types";
|
||||
@@ -32,8 +32,8 @@ import Typography from "@material-ui/core/Typography";
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red"
|
||||
}
|
||||
color: "red",
|
||||
},
|
||||
});
|
||||
|
||||
interface IDeleteBucketProps {
|
||||
@@ -54,7 +54,7 @@ class DeleteBucket extends React.Component<
|
||||
> {
|
||||
state: IDeleteBucketState = {
|
||||
deleteLoading: false,
|
||||
deleteError: ""
|
||||
deleteError: "",
|
||||
};
|
||||
|
||||
removeRecord() {
|
||||
@@ -66,23 +66,23 @@ class DeleteBucket extends React.Component<
|
||||
this.setState({ deleteLoading: true }, () => {
|
||||
api
|
||||
.invoke("DELETE", `/api/v1/buckets/${selectedBucket}`, {
|
||||
name: selectedBucket
|
||||
name: selectedBucket,
|
||||
})
|
||||
.then((res: BucketList) => {
|
||||
this.setState(
|
||||
{
|
||||
deleteLoading: false,
|
||||
deleteError: ""
|
||||
deleteError: "",
|
||||
},
|
||||
() => {
|
||||
this.props.closeDeleteModalAndRefresh(true);
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
this.setState({
|
||||
deleteLoading: false,
|
||||
deleteError: err
|
||||
deleteError: err,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -339,7 +339,7 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">
|
||||
Bucket > {match.params["bucketName"]}
|
||||
{`Bucket > ${match.params["bucketName"]}`}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from "react";
|
||||
import { Grid, InputLabel, Tooltip } from "@material-ui/core";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { fieldBasic, tooltipHelper } from "../common/styleLibrary";
|
||||
import { fileProcess } from "./utils";
|
||||
|
||||
interface InputBoxProps {
|
||||
label: string;
|
||||
classes: any;
|
||||
onChange: (e: string) => void;
|
||||
id: string;
|
||||
name: string;
|
||||
disabled?: boolean;
|
||||
tooltip?: string;
|
||||
required?: boolean;
|
||||
error?: string;
|
||||
accept?: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...fieldBasic,
|
||||
...tooltipHelper,
|
||||
textBoxContainer: {
|
||||
flexGrow: 1,
|
||||
position: "relative",
|
||||
},
|
||||
errorState: {
|
||||
color: "#b53b4b",
|
||||
fontSize: 14,
|
||||
position: "absolute",
|
||||
top: 7,
|
||||
right: 7,
|
||||
},
|
||||
});
|
||||
|
||||
const FileSelector = ({
|
||||
label,
|
||||
classes,
|
||||
onChange,
|
||||
id,
|
||||
name,
|
||||
disabled = false,
|
||||
tooltip = "",
|
||||
required,
|
||||
error = "",
|
||||
accept = "",
|
||||
}: InputBoxProps) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.fieldContainer} ${
|
||||
error !== "" ? classes.errorInField : ""
|
||||
}`}
|
||||
>
|
||||
{label !== "" && (
|
||||
<InputLabel
|
||||
htmlFor={id}
|
||||
className={`${error !== "" ? classes.fieldLabelError : ""} ${
|
||||
classes.inputLabel
|
||||
}`}
|
||||
>
|
||||
<span>
|
||||
{label}
|
||||
{required ? "*" : ""}
|
||||
</span>
|
||||
{tooltip !== "" && (
|
||||
<div className={classes.tooltipContainer}>
|
||||
<Tooltip title={tooltip} placement="top-start">
|
||||
<HelpIcon className={classes.tooltip} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</InputLabel>
|
||||
)}
|
||||
<div className={classes.textBoxContainer}>
|
||||
<input
|
||||
type="file"
|
||||
name={name}
|
||||
onChange={(e) => {
|
||||
fileProcess(e, (data: any) => {
|
||||
onChange(data);
|
||||
});
|
||||
}}
|
||||
accept={accept}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(FileSelector);
|
||||
@@ -0,0 +1,34 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
export const fileProcess = (evt: any, callback: any) => {
|
||||
const file = evt.target.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
reader.onload = () => {
|
||||
// reader.readAsDataURL(file) output will be something like: data:application/x-x509-ca-cert;base64,LS0tLS1CRUdJTiBDRVJUSU
|
||||
// we care only about the actual base64 part (everything after "data:application/x-x509-ca-cert;base64,")
|
||||
const fileBase64 = reader.result;
|
||||
if (fileBase64) {
|
||||
const fileArray = fileBase64.toString().split("base64,");
|
||||
|
||||
if (fileArray.length === 2) {
|
||||
callback(fileArray[1]);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -42,6 +42,7 @@ interface SelectProps {
|
||||
onChange: (
|
||||
e: React.ChangeEvent<{ name?: string | undefined; value: unknown }>
|
||||
) => void;
|
||||
disabled?: boolean;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ const styles = (theme: Theme) =>
|
||||
...tooltipHelper,
|
||||
inputLabel: {
|
||||
...fieldBasic.inputLabel,
|
||||
width: 116,
|
||||
width: 215,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -88,6 +89,7 @@ const SelectWrapper = ({
|
||||
label,
|
||||
tooltip = "",
|
||||
value,
|
||||
disabled = false,
|
||||
}: SelectProps) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
@@ -111,6 +113,7 @@ const SelectWrapper = ({
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
input={<SelectStyled />}
|
||||
disabled={disabled}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<MenuItem
|
||||
|
||||
@@ -265,7 +265,7 @@ const TableWrapper = ({
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
{records && records.length > 0 ? (
|
||||
{records && !isLoading && records.length > 0 ? (
|
||||
<Table size="small" stickyHeader={stickyHeader}>
|
||||
<TableHead className={classes.minTableHeader}>
|
||||
<TableRow>
|
||||
|
||||
@@ -311,7 +311,7 @@ const Console = ({
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/tenants/:tenantName",
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName",
|
||||
},
|
||||
];
|
||||
const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,11 +27,12 @@ import {
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import api from "../../../../common/api";
|
||||
import { ITenant } from "./types";
|
||||
|
||||
interface IDeleteTenant {
|
||||
classes: any;
|
||||
deleteOpen: boolean;
|
||||
selectedTenant: string;
|
||||
selectedTenant: ITenant;
|
||||
closeDeleteModalAndRefresh: (refreshList: boolean) => any;
|
||||
}
|
||||
|
||||
@@ -54,7 +55,10 @@ const DeleteTenant = ({
|
||||
useEffect(() => {
|
||||
if (deleteLoading) {
|
||||
api
|
||||
.invoke("DELETE", `/api/v1/tenants/${selectedTenant}`)
|
||||
.invoke(
|
||||
"DELETE",
|
||||
`/api/v1/namespaces/${selectedTenant.namespace}/tenants/${selectedTenant.name}`
|
||||
)
|
||||
.then(() => {
|
||||
setDeleteLoading(false);
|
||||
setDeleteError("");
|
||||
@@ -85,7 +89,7 @@ const DeleteTenant = ({
|
||||
<DialogContent>
|
||||
{deleteLoading && <LinearProgress />}
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Are you sure you want to delete tenant <b>{selectedTenant}</b>?
|
||||
Are you sure you want to delete tenant <b>{selectedTenant.name}</b>?
|
||||
{deleteError !== "" && (
|
||||
<React.Fragment>
|
||||
<br />
|
||||
|
||||
@@ -20,7 +20,7 @@ import Typography from "@material-ui/core/Typography";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import { Button } from "@material-ui/core";
|
||||
import { Button, IconButton } from "@material-ui/core";
|
||||
import { CreateIcon } from "../../../../icons";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
||||
@@ -32,6 +32,10 @@ import DeleteTenant from "./DeleteTenant";
|
||||
import AddTenant from "./AddTenant";
|
||||
import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
|
||||
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
|
||||
import history from "../../../../history";
|
||||
import RefreshIcon from "@material-ui/icons/Refresh";
|
||||
import TenantCredentialsPrompt from "./TenantCredentialsPrompt/TenantCredentialsPrompt";
|
||||
import { NewTenantCredential } from "./TenantCredentialsPrompt/types";
|
||||
|
||||
interface ITenantsList {
|
||||
classes: any;
|
||||
@@ -96,11 +100,11 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
const [
|
||||
createdAccount,
|
||||
setCreatedAccount,
|
||||
] = useState<NewServiceAccount | null>(null);
|
||||
] = useState<NewTenantCredential | null>(null);
|
||||
|
||||
const closeAddModalAndRefresh = (
|
||||
reloadData: boolean,
|
||||
res: NewServiceAccount | null
|
||||
res: NewTenantCredential | null
|
||||
) => {
|
||||
setCreateTenantOpen(false);
|
||||
|
||||
@@ -122,11 +126,16 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
}
|
||||
};
|
||||
|
||||
const confirmDeleteTenant = (tenant: string) => {
|
||||
const confirmDeleteTenant = (tenant: ITenant) => {
|
||||
setSelectedTenant(tenant);
|
||||
setDeleteOpen(true);
|
||||
};
|
||||
|
||||
const redirectToTenantDetails = (tenant: ITenant) => {
|
||||
history.push(`/namespaces/${tenant.namespace}/tenants/${tenant.name}`);
|
||||
return;
|
||||
};
|
||||
|
||||
const closeCredentialsModal = () => {
|
||||
setShowNewCredentials(false);
|
||||
setCreatedAccount(null);
|
||||
@@ -149,8 +158,8 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", to: `/tenants`, sendOnlyId: true },
|
||||
{ type: "delete", onClick: confirmDeleteTenant, sendOnlyId: true },
|
||||
{ type: "view", onClick: redirectToTenantDetails },
|
||||
{ type: "delete", onClick: confirmDeleteTenant },
|
||||
];
|
||||
|
||||
const filteredRecords = records
|
||||
@@ -187,9 +196,7 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
}
|
||||
|
||||
for (let i = 0; i < resTenants.length; i++) {
|
||||
const total =
|
||||
resTenants[i].volume_count * resTenants[i].volume_size;
|
||||
resTenants[i].capacity = niceBytes(total + "");
|
||||
resTenants[i].capacity = niceBytes(resTenants[i].total_size + "");
|
||||
}
|
||||
|
||||
setRecords(resTenants);
|
||||
@@ -231,7 +238,7 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
/>
|
||||
)}
|
||||
{showNewCredentials && (
|
||||
<CredentialsPrompt
|
||||
<TenantCredentialsPrompt
|
||||
newServiceAccount={createdAccount}
|
||||
open={showNewCredentials}
|
||||
closeModal={() => {
|
||||
@@ -248,6 +255,17 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="Refresh Tenant List"
|
||||
component="span"
|
||||
onClick={() => {
|
||||
setIsLoading(true);
|
||||
}}
|
||||
>
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
|
||||
<TextField
|
||||
placeholder="Search Tenants"
|
||||
className={classes.searchField}
|
||||
|
||||
@@ -86,7 +86,14 @@ const ZonesMultiSelector = ({
|
||||
onChange,
|
||||
classes,
|
||||
}: IZonesMultiSelector) => {
|
||||
const defaultZone: IZone = { name: "", servers: 0, capacity: "", volumes: 0 };
|
||||
const defaultZone: IZone = {
|
||||
name: "",
|
||||
servers: 0,
|
||||
capacity: "",
|
||||
volumes: 0,
|
||||
volumes_per_server: 0,
|
||||
volume_configuration: { size: 0, storage_class: "", labels: null },
|
||||
};
|
||||
|
||||
const [currentElements, setCurrentElements] = useState<IZone[]>([]);
|
||||
const [internalCounter, setInternalCounter] = useState<number>(1);
|
||||
|
||||
@@ -17,18 +17,32 @@
|
||||
export interface IZone {
|
||||
name: string;
|
||||
servers: number;
|
||||
volumes_per_server: number;
|
||||
volume_configuration: IVolumeConfiguration;
|
||||
// computed
|
||||
capacity: string;
|
||||
volumes: number;
|
||||
}
|
||||
|
||||
export interface IAddZoneRequest {
|
||||
name: string;
|
||||
servers: number;
|
||||
volumes_per_server: number;
|
||||
volume_configuration: IVolumeConfiguration;
|
||||
}
|
||||
|
||||
export interface IVolumeConfiguration {
|
||||
size: number;
|
||||
storage_class: string;
|
||||
labels: { [key: string]: any } | null;
|
||||
}
|
||||
|
||||
export interface ITenant {
|
||||
total_size: number;
|
||||
name: string;
|
||||
namespace: string;
|
||||
image: string;
|
||||
console_image: string;
|
||||
zone_count: number;
|
||||
currentState: string;
|
||||
instance_count: 4;
|
||||
|
||||
@@ -11,13 +11,14 @@ import {
|
||||
niceBytes,
|
||||
} from "../../../../common/utils";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import api from "../../../../common/api";
|
||||
import { IAddZoneRequest, ITenant } from "../ListTenants/types";
|
||||
|
||||
interface IAddZoneProps {
|
||||
tenant: ITenant;
|
||||
classes: any;
|
||||
open: boolean;
|
||||
onCloseZoneAndReload: (shouldReload: boolean) => void;
|
||||
volumesPerInstance: number;
|
||||
volumeSize: number;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -63,18 +64,18 @@ const styles = (theme: Theme) =>
|
||||
});
|
||||
|
||||
const AddZoneModal = ({
|
||||
tenant,
|
||||
classes,
|
||||
open,
|
||||
onCloseZoneAndReload,
|
||||
volumesPerInstance,
|
||||
volumeSize,
|
||||
}: IAddZoneProps) => {
|
||||
const [addSending, setAddSending] = useState<boolean>(false);
|
||||
const [zoneName, setZoneName] = useState<string>("");
|
||||
const [numberOfInstances, setNumberOfInstances] = useState<number>(0);
|
||||
const [numberOfNodes, setNumberOfNodes] = useState<number>(0);
|
||||
const [volumeSize, setVolumeSize] = useState<number>(0);
|
||||
const [volumesPerServer, setVolumesPerSever] = useState<number>(0);
|
||||
|
||||
const instanceCapacity: number = volumeSize * volumesPerInstance;
|
||||
const totalCapacity: number = instanceCapacity * numberOfInstances;
|
||||
const instanceCapacity: number = volumeSize * 1073741824 * volumesPerServer;
|
||||
const totalCapacity: number = instanceCapacity * numberOfNodes;
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
@@ -88,30 +89,66 @@ const AddZoneModal = ({
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setAddSending(true);
|
||||
const data: IAddZoneRequest = {
|
||||
name: "",
|
||||
servers: numberOfNodes,
|
||||
volumes_per_server: volumesPerServer,
|
||||
volume_configuration: {
|
||||
size: volumeSize * 1073741824,
|
||||
storage_class: "",
|
||||
labels: null,
|
||||
},
|
||||
};
|
||||
api
|
||||
.invoke(
|
||||
"POST",
|
||||
`/api/v1/namespaces/${tenant.namespace}/tenants/${tenant.name}/zones`,
|
||||
data
|
||||
)
|
||||
.then(() => {
|
||||
setAddSending(false);
|
||||
onCloseZoneAndReload(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddSending(false);
|
||||
// setDeleteError(err);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="zone_name"
|
||||
name="zone_name"
|
||||
type="string"
|
||||
id="number_of_nodes"
|
||||
name="number_of_nodes"
|
||||
type="number"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setZoneName(e.target.value);
|
||||
setNumberOfNodes(parseInt(e.target.value));
|
||||
}}
|
||||
label="Name"
|
||||
value={zoneName}
|
||||
label="Number o Nodes"
|
||||
value={numberOfNodes.toString(10)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="number_instances"
|
||||
name="number_instances"
|
||||
id="zone_size"
|
||||
name="zone_size"
|
||||
type="number"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setNumberOfInstances(parseInt(e.target.value));
|
||||
setVolumeSize(parseInt(e.target.value));
|
||||
}}
|
||||
label="Drives per Server"
|
||||
value={numberOfInstances.toString(10)}
|
||||
label="Volume Size (Gi)"
|
||||
value={volumeSize.toString(10)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="volumes_per_sever"
|
||||
name="volumes_per_sever"
|
||||
type="number"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setVolumesPerSever(parseInt(e.target.value));
|
||||
}}
|
||||
label="Volumes per Server"
|
||||
value={volumesPerServer.toString(10)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
|
||||
@@ -91,19 +91,6 @@ const styles = (theme: Theme) =>
|
||||
...modalBasic,
|
||||
});
|
||||
|
||||
const mainPagination = {
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: 0,
|
||||
rowsPerPage: 0,
|
||||
page: 0,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
};
|
||||
|
||||
const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||
const [capacity, setCapacity] = useState<number>(0);
|
||||
@@ -142,24 +129,38 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
||||
|
||||
const loadInfo = () => {
|
||||
const tenantName = match.params["tenantName"];
|
||||
const tenantNamespace = match.params["tenantNamespace"];
|
||||
|
||||
setLoading(true);
|
||||
|
||||
api
|
||||
.invoke("GET", `/api/v1/tenants/${tenantName}`)
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}`
|
||||
)
|
||||
.then((res: ITenant) => {
|
||||
const total = res.volume_count * res.volume_size;
|
||||
|
||||
setCapacity(total);
|
||||
setZoneCount(res.zone_count);
|
||||
setVolumes(res.volume_count);
|
||||
setInstances(res.instance_count);
|
||||
const resZones = !res.zones ? [] : res.zones;
|
||||
const total = res.volume_count * res.volume_size;
|
||||
let totalInstances = 0;
|
||||
let totalVolumes = 0;
|
||||
let count = 1;
|
||||
for (let zone of resZones) {
|
||||
zone.volumes = res.volumes_per_server;
|
||||
const cap = res.volumes_per_server * res.volume_size * zone.servers;
|
||||
const cap =
|
||||
zone.volumes_per_server *
|
||||
zone.servers *
|
||||
zone.volume_configuration.size;
|
||||
zone.name = `zone-${count}`;
|
||||
zone.capacity = niceBytes(cap + "");
|
||||
zone.volumes = zone.servers * zone.volumes_per_server;
|
||||
totalInstances += zone.servers;
|
||||
totalVolumes += zone.volumes;
|
||||
count += 1;
|
||||
}
|
||||
setCapacity(res.total_size);
|
||||
setZoneCount(resZones.length);
|
||||
setVolumes(totalVolumes);
|
||||
setInstances(totalInstances);
|
||||
|
||||
setZones(resZones);
|
||||
|
||||
setTenant(res);
|
||||
@@ -182,8 +183,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
||||
<AddZoneModal
|
||||
open={addZoneOpen}
|
||||
onCloseZoneAndReload={onCloseZoneAndRefresh}
|
||||
volumeSize={tenant.volume_size}
|
||||
volumesPerInstance={tenant.volumes_per_server}
|
||||
tenant={tenant}
|
||||
/>
|
||||
)}
|
||||
{addBucketOpen && (
|
||||
@@ -201,7 +201,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">
|
||||
Tenant > {match.params["tenantName"]}
|
||||
{`Tenant > ${match.params["tenantName"]}`}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
@@ -212,40 +212,18 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
||||
<div className={classes.infoGrid}>
|
||||
<div>Capacity:</div>
|
||||
<div>{niceBytes(capacity.toString(10))}</div>
|
||||
<div>Minio:</div>
|
||||
<div>{tenant ? tenant.image : ""}</div>
|
||||
<div>Zones:</div>
|
||||
<div>{zoneCount}</div>
|
||||
<div>External IDP:</div>
|
||||
<div>
|
||||
{externalIDP ? "Yes" : "No"}
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="small"
|
||||
onClick={() => {}}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
</div>
|
||||
<div>Console:</div>
|
||||
<div>{tenant ? tenant.console_image : ""}</div>
|
||||
<div>Instances:</div>
|
||||
<div>{instances}</div>
|
||||
<div>External KMS:</div>
|
||||
<div>{externalKMS ? "Yes" : "No"} </div>
|
||||
<div>Volumes:</div>
|
||||
<div>{volumes}</div>
|
||||
</div>
|
||||
</Paper>
|
||||
<div className={classes.masterActions}>
|
||||
<div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
onClick={() => {}}
|
||||
>
|
||||
Warp
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
@@ -261,185 +239,59 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
||||
aria-label="tenant-tabs"
|
||||
>
|
||||
<Tab label="Zones" />
|
||||
<Tab label="Buckets" />
|
||||
<Tab label="Replication" />
|
||||
</Tabs>
|
||||
</Grid>
|
||||
<Grid item xs={6} className={classes.actionsTray}>
|
||||
{selectedTab === 0 && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddZone(true);
|
||||
}}
|
||||
>
|
||||
Add Zone
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{selectedTab === 1 && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddBucketOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Bucket
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{selectedTab === 2 && (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddReplicationOpen(true);
|
||||
}}
|
||||
>
|
||||
Add Replication
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={() => {
|
||||
setAddZone(true);
|
||||
}}
|
||||
>
|
||||
Add Zone
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{selectedTab === 0 && (
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "view",
|
||||
onClick: (element) => {
|
||||
console.log(element);
|
||||
},
|
||||
sendOnlyId: true,
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "delete",
|
||||
onClick: (element) => {
|
||||
console.log(element);
|
||||
},
|
||||
{
|
||||
type: "delete",
|
||||
onClick: (element) => {
|
||||
console.log(element);
|
||||
},
|
||||
sendOnlyId: true,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{ label: "Name", elementKey: "name" },
|
||||
{ label: "Capacity", elementKey: "capacity" },
|
||||
{ label: "# of Instances", elementKey: "servers" },
|
||||
{ label: "# of Drives", elementKey: "volumes" },
|
||||
]}
|
||||
isLoading={false}
|
||||
records={zones}
|
||||
entityName="Zones"
|
||||
idField="name"
|
||||
paginatorConfig={{
|
||||
...mainPagination,
|
||||
onChangePage: () => {},
|
||||
onChangeRowsPerPage: () => {},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedTab === 1 && (
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "view",
|
||||
onClick: (element) => {
|
||||
console.log(element);
|
||||
},
|
||||
sendOnlyId: true,
|
||||
},
|
||||
{
|
||||
type: "replicate",
|
||||
onClick: (element) => {
|
||||
console.log(element);
|
||||
},
|
||||
sendOnlyId: true,
|
||||
},
|
||||
{
|
||||
type: "mirror",
|
||||
onClick: (element) => {
|
||||
console.log(element);
|
||||
},
|
||||
sendOnlyId: true,
|
||||
},
|
||||
{
|
||||
type: "delete",
|
||||
onClick: (element) => {
|
||||
console.log(element);
|
||||
},
|
||||
sendOnlyId: true,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "status",
|
||||
},
|
||||
{ label: "Name", elementKey: "name" },
|
||||
{ label: "AccessPolicy", elementKey: "access_policy" },
|
||||
]}
|
||||
isLoading={false}
|
||||
records={[]}
|
||||
entityName="Buckets"
|
||||
idField="name"
|
||||
paginatorConfig={{
|
||||
...mainPagination,
|
||||
onChangePage: () => {},
|
||||
onChangeRowsPerPage: () => {},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedTab === 2 && (
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "view",
|
||||
onClick: (element) => {
|
||||
console.log(element);
|
||||
},
|
||||
sendOnlyId: true,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
label: "Source",
|
||||
elementKey: "source",
|
||||
},
|
||||
{ label: "Source Bucket", elementKey: "source_bucket" },
|
||||
{ label: "Destination", elementKey: "destination" },
|
||||
{
|
||||
label: "Destination Bucket",
|
||||
elementKey: "destination_bucket",
|
||||
},
|
||||
]}
|
||||
isLoading={false}
|
||||
records={[]}
|
||||
entityName="Replication"
|
||||
idField="id"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: 0,
|
||||
rowsPerPage: 0,
|
||||
page: 0,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
onChangePage: () => {},
|
||||
onChangeRowsPerPage: () => {},
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
sendOnlyId: true,
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{ label: "Name", elementKey: "name" },
|
||||
{ label: "Capacity", elementKey: "capacity" },
|
||||
{ label: "# of Instances", elementKey: "servers" },
|
||||
{ label: "# of Drives", elementKey: "volumes" },
|
||||
]}
|
||||
isLoading={false}
|
||||
records={zones}
|
||||
entityName="Zones"
|
||||
idField="name"
|
||||
paginatorConfig={{
|
||||
rowsPerPageOptions: [5, 10, 25],
|
||||
colSpan: 3,
|
||||
count: zoneCount,
|
||||
rowsPerPage: 10,
|
||||
page: 0,
|
||||
SelectProps: {
|
||||
inputProps: { "aria-label": "rows per page" },
|
||||
native: true,
|
||||
},
|
||||
ActionsComponent: MinTablePaginationActions,
|
||||
onChangePage: () => {},
|
||||
onChangeRowsPerPage: () => {},
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -19,6 +19,8 @@ export interface IValidation {
|
||||
required: boolean;
|
||||
pattern?: RegExp;
|
||||
customPatternMessage?: string;
|
||||
customValidation?: boolean;
|
||||
customValidationMessage?: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
@@ -31,12 +33,18 @@ export const commonFormValidation = (fieldsValidate: IValidation[]) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (field.customValidation && field.customValidationMessage) {
|
||||
returnErrors[field.fieldKey] = field.customValidationMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
if (field.pattern && field.customPatternMessage) {
|
||||
const rgx = new RegExp(field.pattern, "g");
|
||||
|
||||
if (!field.value.match(rgx)) {
|
||||
returnErrors[field.fieldKey] = field.customPatternMessage;
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4119,7 +4119,7 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debuglog@*, debuglog@^1.0.1:
|
||||
debuglog@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
||||
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
|
||||
@@ -6145,7 +6145,7 @@ import-local@^2.0.0:
|
||||
pkg-dir "^3.0.0"
|
||||
resolve-cwd "^2.0.0"
|
||||
|
||||
imurmurhash@*, imurmurhash@^0.1.4:
|
||||
imurmurhash@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
|
||||
@@ -7709,11 +7709,6 @@ lockfile@^1.0.4:
|
||||
dependencies:
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
lodash._baseindexof@*:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
|
||||
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
|
||||
|
||||
lodash._baseuniq@~4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
|
||||
@@ -7722,33 +7717,11 @@ lodash._baseuniq@~4.6.0:
|
||||
lodash._createset "~4.0.0"
|
||||
lodash._root "~3.0.0"
|
||||
|
||||
lodash._bindcallback@*:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
|
||||
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
|
||||
|
||||
lodash._cacheindexof@*:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
|
||||
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
|
||||
|
||||
lodash._createcache@*:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
|
||||
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
|
||||
dependencies:
|
||||
lodash._getnative "^3.0.0"
|
||||
|
||||
lodash._createset@~4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
|
||||
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
|
||||
|
||||
lodash._getnative@*, lodash._getnative@^3.0.0:
|
||||
version "3.9.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
|
||||
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
|
||||
|
||||
lodash._reinterpolate@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
|
||||
@@ -7774,11 +7747,6 @@ lodash.memoize@^4.1.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||
|
||||
lodash.restparam@*:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
|
||||
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
|
||||
|
||||
lodash.sortby@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
|
||||
@@ -18,11 +18,9 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
@@ -33,7 +31,7 @@ func registerAdminArnsHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIArnListHandler = admin_api.ArnListHandlerFunc(func(params admin_api.ArnListParams, session *models.Principal) middleware.Responder {
|
||||
arnsResp, err := getArnsResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewArnListDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewArnListDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewArnListOK().WithPayload(arnsResp)
|
||||
})
|
||||
@@ -53,11 +51,10 @@ func getArns(ctx context.Context, client MinioAdmin) (*models.ArnsResponse, erro
|
||||
}
|
||||
|
||||
// getArnsResponse returns a list of active arns in the instance
|
||||
func getArnsResponse(session *models.Principal) (*models.ArnsResponse, error) {
|
||||
func getArnsResponse(session *models.Principal) (*models.ArnsResponse, *models.Error) {
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -68,8 +65,7 @@ func getArnsResponse(session *models.Principal) (*models.ArnsResponse, error) {
|
||||
// serialize output
|
||||
arnsList, err := getArns(ctx, adminClient)
|
||||
if err != nil {
|
||||
log.Println("error getting arn list:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return arnsList, nil
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package restapi
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
@@ -36,7 +35,7 @@ func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIListConfigHandler = admin_api.ListConfigHandlerFunc(func(params admin_api.ListConfigParams, session *models.Principal) middleware.Responder {
|
||||
configListResp, err := getListConfigResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewListConfigDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewListConfigDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewListConfigOK().WithPayload(configListResp)
|
||||
})
|
||||
@@ -44,14 +43,14 @@ func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIConfigInfoHandler = admin_api.ConfigInfoHandlerFunc(func(params admin_api.ConfigInfoParams, session *models.Principal) middleware.Responder {
|
||||
config, err := getConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewConfigInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewConfigInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewConfigInfoOK().WithPayload(config)
|
||||
})
|
||||
// Set Configuration
|
||||
api.AdminAPISetConfigHandler = admin_api.SetConfigHandlerFunc(func(params admin_api.SetConfigParams, session *models.Principal) middleware.Responder {
|
||||
if err := setConfigResponse(session, params.Name, params.Body); err != nil {
|
||||
return admin_api.NewSetConfigDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewSetConfigDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewSetConfigNoContent()
|
||||
})
|
||||
@@ -76,11 +75,10 @@ func listConfig(client MinioAdmin) ([]*models.ConfigDescription, error) {
|
||||
}
|
||||
|
||||
// getListConfigResponse performs listConfig() and serializes it to the handler's output
|
||||
func getListConfigResponse(session *models.Principal) (*models.ListConfigResponse, error) {
|
||||
func getListConfigResponse(session *models.Principal) (*models.ListConfigResponse, *models.Error) {
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -88,8 +86,7 @@ func getListConfigResponse(session *models.Principal) (*models.ListConfigRespons
|
||||
|
||||
configDescs, err := listConfig(adminClient)
|
||||
if err != nil {
|
||||
log.Println("error listing configurations:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
listGroupsResponse := &models.ListConfigResponse{
|
||||
Configurations: configDescs,
|
||||
@@ -127,11 +124,10 @@ func getConfig(client MinioAdmin, name string) ([]*models.ConfigurationKV, error
|
||||
}
|
||||
|
||||
// getConfigResponse performs getConfig() and serializes it to the handler's output
|
||||
func getConfigResponse(session *models.Principal, params admin_api.ConfigInfoParams) (*models.Configuration, error) {
|
||||
func getConfigResponse(session *models.Principal, params admin_api.ConfigInfoParams) (*models.Configuration, *models.Error) {
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -139,8 +135,7 @@ func getConfigResponse(session *models.Principal, params admin_api.ConfigInfoPar
|
||||
|
||||
configkv, err := getConfig(adminClient, params.Name)
|
||||
if err != nil {
|
||||
log.Println("error getting configuration:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
configurationObj := &models.Configuration{
|
||||
Name: params.Name,
|
||||
@@ -180,11 +175,10 @@ func buildConfig(configName *string, kvs []*models.ConfigurationKV) *string {
|
||||
}
|
||||
|
||||
// setConfigResponse implements setConfig() to be used by handler
|
||||
func setConfigResponse(session *models.Principal, name string, configRequest *models.SetConfigRequest) error {
|
||||
func setConfigResponse(session *models.Principal, name string, configRequest *models.SetConfigRequest) *models.Error {
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -194,8 +188,7 @@ func setConfigResponse(session *models.Principal, name string, configRequest *mo
|
||||
ctx := context.Background()
|
||||
|
||||
if err := setConfigWithARNAccountID(ctx, adminClient, &configName, configRequest.KeyValues, configRequest.ArnResourceID); err != nil {
|
||||
log.Println("error listing configurations:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/minio/pkg/madmin"
|
||||
|
||||
@@ -36,7 +35,7 @@ func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIListGroupsHandler = admin_api.ListGroupsHandlerFunc(func(params admin_api.ListGroupsParams, session *models.Principal) middleware.Responder {
|
||||
listGroupsResponse, err := getListGroupsResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewListGroupsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewListGroupsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewListGroupsOK().WithPayload(listGroupsResponse)
|
||||
})
|
||||
@@ -44,21 +43,21 @@ func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIGroupInfoHandler = admin_api.GroupInfoHandlerFunc(func(params admin_api.GroupInfoParams, session *models.Principal) middleware.Responder {
|
||||
groupInfo, err := getGroupInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewGroupInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewGroupInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewGroupInfoOK().WithPayload(groupInfo)
|
||||
})
|
||||
// Add Group
|
||||
api.AdminAPIAddGroupHandler = admin_api.AddGroupHandlerFunc(func(params admin_api.AddGroupParams, session *models.Principal) middleware.Responder {
|
||||
if err := getAddGroupResponse(session, params.Body); err != nil {
|
||||
return admin_api.NewAddGroupDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewAddGroupDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewAddGroupCreated()
|
||||
})
|
||||
// Remove Group
|
||||
api.AdminAPIRemoveGroupHandler = admin_api.RemoveGroupHandlerFunc(func(params admin_api.RemoveGroupParams, session *models.Principal) middleware.Responder {
|
||||
if err := getRemoveGroupResponse(session, params); err != nil {
|
||||
return admin_api.NewRemoveGroupDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewRemoveGroupDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewRemoveGroupNoContent()
|
||||
})
|
||||
@@ -66,7 +65,7 @@ func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIUpdateGroupHandler = admin_api.UpdateGroupHandlerFunc(func(params admin_api.UpdateGroupParams, session *models.Principal) middleware.Responder {
|
||||
groupUpdateResp, err := getUpdateGroupResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewUpdateGroupDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewUpdateGroupDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewUpdateGroupOK().WithPayload(groupUpdateResp)
|
||||
})
|
||||
@@ -82,12 +81,11 @@ func listGroups(ctx context.Context, client MinioAdmin) (*[]string, error) {
|
||||
}
|
||||
|
||||
// getListGroupsResponse performs listGroups() and serializes it to the handler's output
|
||||
func getListGroupsResponse(session *models.Principal) (*models.ListGroupsResponse, error) {
|
||||
func getListGroupsResponse(session *models.Principal) (*models.ListGroupsResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -95,8 +93,7 @@ func getListGroupsResponse(session *models.Principal) (*models.ListGroupsRespons
|
||||
|
||||
groups, err := listGroups(ctx, adminClient)
|
||||
if err != nil {
|
||||
log.Println("error listing groups:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// serialize output
|
||||
listGroupsResponse := &models.ListGroupsResponse{
|
||||
@@ -116,12 +113,11 @@ func groupInfo(ctx context.Context, client MinioAdmin, group string) (*madmin.Gr
|
||||
}
|
||||
|
||||
// getGroupInfoResponse performs groupInfo() and serializes it to the handler's output
|
||||
func getGroupInfoResponse(session *models.Principal, params admin_api.GroupInfoParams) (*models.Group, error) {
|
||||
func getGroupInfoResponse(session *models.Principal, params admin_api.GroupInfoParams) (*models.Group, *models.Error) {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -129,8 +125,7 @@ func getGroupInfoResponse(session *models.Principal, params admin_api.GroupInfoP
|
||||
|
||||
groupDesc, err := groupInfo(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
log.Println("error getting group info:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
groupResponse := &models.Group{
|
||||
@@ -157,26 +152,23 @@ func addGroup(ctx context.Context, client MinioAdmin, group string, members []st
|
||||
}
|
||||
|
||||
// getAddGroupResponse performs addGroup() and serializes it to the handler's output
|
||||
func getAddGroupResponse(session *models.Principal, params *models.AddGroupRequest) error {
|
||||
func getAddGroupResponse(session *models.Principal, params *models.AddGroupRequest) *models.Error {
|
||||
ctx := context.Background()
|
||||
// AddGroup request needed to proceed
|
||||
if params == nil {
|
||||
log.Println("error AddGroup body not in request")
|
||||
return errors.New(500, "error AddGroup body not in request")
|
||||
return prepareError(errGroupBodyNotInRequest)
|
||||
}
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
|
||||
if err := addGroup(ctx, adminClient, *params.Group, params.Members); err != nil {
|
||||
log.Println("error adding group:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -196,25 +188,22 @@ func removeGroup(ctx context.Context, client MinioAdmin, group string) error {
|
||||
}
|
||||
|
||||
// getRemoveGroupResponse performs removeGroup() and serializes it to the handler's output
|
||||
func getRemoveGroupResponse(session *models.Principal, params admin_api.RemoveGroupParams) error {
|
||||
func getRemoveGroupResponse(session *models.Principal, params admin_api.RemoveGroupParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
|
||||
if params.Name == "" {
|
||||
log.Println("error group name not in request")
|
||||
return errors.New(500, "error group name not in request")
|
||||
return prepareError(errGroupNameNotInRequest)
|
||||
}
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
|
||||
if err := removeGroup(ctx, adminClient, params.Name); err != nil {
|
||||
log.Println("error removing group:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -276,23 +265,21 @@ func setGroupStatus(ctx context.Context, client MinioAdmin, group, status string
|
||||
// getUpdateGroupResponse updates a group by adding or removing it's members depending on the request,
|
||||
// also sets the group's status if status in the request is different than the current one.
|
||||
// Then serializes the output to be used by the handler.
|
||||
func getUpdateGroupResponse(session *models.Principal, params admin_api.UpdateGroupParams) (*models.Group, error) {
|
||||
func getUpdateGroupResponse(session *models.Principal, params admin_api.UpdateGroupParams) (*models.Group, *models.Error) {
|
||||
ctx := context.Background()
|
||||
if params.Name == "" {
|
||||
log.Println("error group name not in request")
|
||||
return nil, errors.New(500, "error group name not in request")
|
||||
return nil, prepareError(errGroupNameNotInRequest)
|
||||
}
|
||||
if params.Body == nil {
|
||||
log.Println("error body not in request")
|
||||
return nil, errors.New(500, "error body not in request")
|
||||
return nil, prepareError(errGroupBodyNotInRequest)
|
||||
|
||||
}
|
||||
expectedGroupUpdate := params.Body
|
||||
groupName := params.Name
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -300,8 +287,7 @@ func getUpdateGroupResponse(session *models.Principal, params admin_api.UpdateGr
|
||||
|
||||
groupUpdated, err := groupUpdate(ctx, adminClient, groupName, expectedGroupUpdate)
|
||||
if err != nil {
|
||||
log.Println("error updating group:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
groupResponse := &models.Group{
|
||||
Name: groupUpdated.Name,
|
||||
|
||||
@@ -18,11 +18,9 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
@@ -33,7 +31,7 @@ func registerAdminInfoHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIAdminInfoHandler = admin_api.AdminInfoHandlerFunc(func(params admin_api.AdminInfoParams, session *models.Principal) middleware.Responder {
|
||||
infoResp, err := getAdminInfoResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewAdminInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewAdminInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewAdminInfoOK().WithPayload(infoResp)
|
||||
})
|
||||
@@ -72,11 +70,10 @@ func getAdminInfo(ctx context.Context, client MinioAdmin) (*usageInfo, error) {
|
||||
}
|
||||
|
||||
// getAdminInfoResponse returns the response containing total buckets, objects and usage.
|
||||
func getAdminInfoResponse(session *models.Principal) (*models.AdminInfoResponse, error) {
|
||||
func getAdminInfoResponse(session *models.Principal) (*models.AdminInfoResponse, *models.Error) {
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -87,8 +84,7 @@ func getAdminInfoResponse(session *models.Principal) (*models.AdminInfoResponse,
|
||||
// serialize output
|
||||
usage, err := getAdminInfo(ctx, adminClient)
|
||||
if err != nil {
|
||||
log.Println("error getting information:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
sessionResp := &models.AdminInfoResponse{
|
||||
Buckets: usage.Buckets,
|
||||
|
||||
@@ -18,7 +18,6 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"sort"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
@@ -26,7 +25,6 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
@@ -39,7 +37,7 @@ func registerNodesHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIGetMaxAllocatableMemHandler = admin_api.GetMaxAllocatableMemHandlerFunc(func(params admin_api.GetMaxAllocatableMemParams, principal *models.Principal) middleware.Responder {
|
||||
resp, err := getMaxAllocatableMemoryResponse(principal, params.NumNodes)
|
||||
if err != nil {
|
||||
return admin_api.NewGetMaxAllocatableMemDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewGetMaxAllocatableMemDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewGetMaxAllocatableMemOK().WithPayload(resp)
|
||||
})
|
||||
@@ -123,19 +121,16 @@ func min(x, y int64) int64 {
|
||||
return x
|
||||
}
|
||||
|
||||
func getMaxAllocatableMemoryResponse(session *models.Principal, numNodes int32) (*models.MaxAllocatableMemResponse, error) {
|
||||
func getMaxAllocatableMemoryResponse(session *models.Principal, numNodes int32) (*models.MaxAllocatableMemResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
client, err := cluster.K8sClient(session.SessionToken)
|
||||
if err != nil {
|
||||
log.Println("error getting k8sClient:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
clusterResources, err := getMaxAllocatableMemory(ctx, client.CoreV1(), numNodes)
|
||||
if err != nil {
|
||||
log.Println("error getting cluster's resources:", err)
|
||||
return nil, err
|
||||
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return clusterResources, nil
|
||||
}
|
||||
|
||||
@@ -19,11 +19,9 @@ package restapi
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
@@ -34,7 +32,7 @@ func registerAdminNotificationEndpointsHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPINotificationEndpointListHandler = admin_api.NotificationEndpointListHandlerFunc(func(params admin_api.NotificationEndpointListParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getNotificationEndpointsResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewNotificationEndpointListDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewNotificationEndpointListDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewNotificationEndpointListOK().WithPayload(notifEndpoints)
|
||||
})
|
||||
@@ -42,7 +40,7 @@ func registerAdminNotificationEndpointsHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIAddNotificationEndpointHandler = admin_api.AddNotificationEndpointHandlerFunc(func(params admin_api.AddNotificationEndpointParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getAddNotificationEndpointResponse(session, ¶ms)
|
||||
if err != nil {
|
||||
return admin_api.NewAddNotificationEndpointDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewAddNotificationEndpointDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewAddNotificationEndpointCreated().WithPayload(notifEndpoints)
|
||||
})
|
||||
@@ -78,11 +76,10 @@ func getNotificationEndpoints(ctx context.Context, client MinioAdmin) (*models.N
|
||||
}
|
||||
|
||||
// getNotificationEndpointsResponse returns a list of notification endpoints in the instance
|
||||
func getNotificationEndpointsResponse(session *models.Principal) (*models.NotifEndpointResponse, error) {
|
||||
func getNotificationEndpointsResponse(session *models.Principal) (*models.NotifEndpointResponse, *models.Error) {
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -93,8 +90,7 @@ func getNotificationEndpointsResponse(session *models.Principal) (*models.NotifE
|
||||
// serialize output
|
||||
notfEndpointResp, err := getNotificationEndpoints(ctx, adminClient)
|
||||
if err != nil {
|
||||
log.Println("error getting notification endpoint list:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return notfEndpointResp, nil
|
||||
}
|
||||
@@ -150,11 +146,10 @@ func addNotificationEndpoint(ctx context.Context, client MinioAdmin, params *adm
|
||||
}
|
||||
|
||||
// getNotificationEndpointsResponse returns a list of notification endpoints in the instance
|
||||
func getAddNotificationEndpointResponse(session *models.Principal, params *admin_api.AddNotificationEndpointParams) (*models.NotificationEndpoint, error) {
|
||||
func getAddNotificationEndpointResponse(session *models.Principal, params *admin_api.AddNotificationEndpointParams) (*models.NotificationEndpoint, *models.Error) {
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -165,8 +160,7 @@ func getAddNotificationEndpointResponse(session *models.Principal, params *admin
|
||||
// serialize output
|
||||
notfEndpointResp, err := addNotificationEndpoint(ctx, adminClient, params)
|
||||
if err != nil {
|
||||
log.Println("error getting notification endpoint list:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return notfEndpointResp, nil
|
||||
}
|
||||
|
||||
63
restapi/admin_parity.go
Normal file
63
restapi/admin_parity.go
Normal file
@@ -0,0 +1,63 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
)
|
||||
|
||||
func registerParityHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIGetParityHandler = admin_api.GetParityHandlerFunc(func(params admin_api.GetParityParams, principal *models.Principal) middleware.Responder {
|
||||
resp, err := getParityResponse(params)
|
||||
if err != nil {
|
||||
return admin_api.NewGetParityDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewGetParityOK().WithPayload(resp)
|
||||
})
|
||||
}
|
||||
|
||||
func GetParityInfo(nodes int64, disksPerNode int64) (models.ParityResponse, error) {
|
||||
parityVals, err := utils.PossibleParityValues(fmt.Sprintf(`http://minio{1...%d}/export/set{1...%d}`, nodes, disksPerNode))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parityVals, nil
|
||||
}
|
||||
|
||||
func getParityResponse(params admin_api.GetParityParams) (models.ParityResponse, *models.Error) {
|
||||
nodes := params.Nodes
|
||||
disksPerNode := params.DisksPerNode
|
||||
|
||||
parityValues, err := GetParityInfo(nodes, disksPerNode)
|
||||
|
||||
if err != nil {
|
||||
log.Println("error getting parity info:", err)
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
return parityValues, nil
|
||||
}
|
||||
79
restapi/admin_parity_test.go
Normal file
79
restapi/admin_parity_test.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
func Test_getParityInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
wantErr bool
|
||||
nodes int64
|
||||
disksPerNode int64
|
||||
expectedResp models.ParityResponse
|
||||
}{
|
||||
{
|
||||
description: "Incorrect Number of endpoints provided",
|
||||
wantErr: true,
|
||||
nodes: 1,
|
||||
disksPerNode: 1,
|
||||
expectedResp: nil,
|
||||
},
|
||||
{
|
||||
description: "Number of endpoints is valid",
|
||||
wantErr: false,
|
||||
nodes: 4,
|
||||
disksPerNode: 10,
|
||||
expectedResp: models.ParityResponse{"EC:4", "EC:3", "EC:2"},
|
||||
},
|
||||
{
|
||||
description: "More nodes than disks",
|
||||
wantErr: false,
|
||||
nodes: 4,
|
||||
disksPerNode: 1,
|
||||
expectedResp: models.ParityResponse{"EC:2"},
|
||||
},
|
||||
{
|
||||
description: "More disks than nodes",
|
||||
wantErr: false,
|
||||
nodes: 2,
|
||||
disksPerNode: 50,
|
||||
expectedResp: models.ParityResponse{"EC:5", "EC:4", "EC:3", "EC:2"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
parity, err := GetParityInfo(tt.nodes, tt.disksPerNode)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GetParityInfo() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(parity, tt.expectedResp) {
|
||||
ji, _ := json.Marshal(parity)
|
||||
vi, _ := json.Marshal(tt.expectedResp)
|
||||
t.Errorf("\ngot: %s \nwant: %s", ji, vi)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,7 @@ import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
@@ -37,7 +34,7 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIListPoliciesHandler = admin_api.ListPoliciesHandlerFunc(func(params admin_api.ListPoliciesParams, session *models.Principal) middleware.Responder {
|
||||
listPoliciesResponse, err := getListPoliciesResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewListPoliciesDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewListPoliciesDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewListPoliciesOK().WithPayload(listPoliciesResponse)
|
||||
})
|
||||
@@ -45,7 +42,7 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIPolicyInfoHandler = admin_api.PolicyInfoHandlerFunc(func(params admin_api.PolicyInfoParams, session *models.Principal) middleware.Responder {
|
||||
policyInfo, err := getPolicyInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewPolicyInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewPolicyInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewPolicyInfoOK().WithPayload(policyInfo)
|
||||
})
|
||||
@@ -53,24 +50,21 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIAddPolicyHandler = admin_api.AddPolicyHandlerFunc(func(params admin_api.AddPolicyParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getAddPolicyResponse(session, params.Body)
|
||||
if err != nil {
|
||||
return admin_api.NewAddPolicyDefault(500).WithPayload(&models.Error{
|
||||
Code: 500,
|
||||
Message: swag.String(err.Error()),
|
||||
})
|
||||
return admin_api.NewAddPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewAddPolicyCreated().WithPayload(policyResponse)
|
||||
})
|
||||
// Remove Policy
|
||||
api.AdminAPIRemovePolicyHandler = admin_api.RemovePolicyHandlerFunc(func(params admin_api.RemovePolicyParams, session *models.Principal) middleware.Responder {
|
||||
if err := getRemovePolicyResponse(session, params); err != nil {
|
||||
return admin_api.NewRemovePolicyDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewRemovePolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewRemovePolicyNoContent()
|
||||
})
|
||||
// Set Policy
|
||||
api.AdminAPISetPolicyHandler = admin_api.SetPolicyHandlerFunc(func(params admin_api.SetPolicyParams, session *models.Principal) middleware.Responder {
|
||||
if err := getSetPolicyResponse(session, params.Name, params.Body); err != nil {
|
||||
return admin_api.NewSetPolicyDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewSetPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewSetPolicyNoContent()
|
||||
})
|
||||
@@ -97,12 +91,11 @@ func listPolicies(ctx context.Context, client MinioAdmin) ([]*models.Policy, err
|
||||
}
|
||||
|
||||
// getListPoliciesResponse performs listPolicies() and serializes it to the handler's output
|
||||
func getListPoliciesResponse(session *models.Principal) (*models.ListPoliciesResponse, error) {
|
||||
func getListPoliciesResponse(session *models.Principal) (*models.ListPoliciesResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -110,8 +103,7 @@ func getListPoliciesResponse(session *models.Principal) (*models.ListPoliciesRes
|
||||
|
||||
policies, err := listPolicies(ctx, adminClient)
|
||||
if err != nil {
|
||||
log.Println("error listing policies:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// serialize output
|
||||
listPoliciesResponse := &models.ListPoliciesResponse{
|
||||
@@ -131,24 +123,21 @@ func removePolicy(ctx context.Context, client MinioAdmin, name string) error {
|
||||
}
|
||||
|
||||
// getRemovePolicyResponse() performs removePolicy() and serializes it to the handler's output
|
||||
func getRemovePolicyResponse(session *models.Principal, params admin_api.RemovePolicyParams) error {
|
||||
func getRemovePolicyResponse(session *models.Principal, params admin_api.RemovePolicyParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
if params.Name == "" {
|
||||
log.Println("error policy name not in request")
|
||||
return errors.New(500, "error policy name not in request")
|
||||
return prepareError(errPolicyNameNotInRequest)
|
||||
}
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
|
||||
if err := removePolicy(ctx, adminClient, params.Name); err != nil {
|
||||
log.Println("error removing policy:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -173,25 +162,23 @@ func addPolicy(ctx context.Context, client MinioAdmin, name, policy string) (*mo
|
||||
}
|
||||
|
||||
// getAddPolicyResponse performs addPolicy() and serializes it to the handler's output
|
||||
func getAddPolicyResponse(session *models.Principal, params *models.AddPolicyRequest) (*models.Policy, error) {
|
||||
func getAddPolicyResponse(session *models.Principal, params *models.AddPolicyRequest) (*models.Policy, *models.Error) {
|
||||
ctx := context.Background()
|
||||
if params == nil {
|
||||
log.Println("error AddPolicy body not in request")
|
||||
return nil, errors.New(500, "error AddPolicy body not in request")
|
||||
return nil, prepareError(errPolicyBodyNotInRequest)
|
||||
}
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
policy, err := addPolicy(ctx, adminClient, *params.Name, *params.Policy)
|
||||
if err != nil {
|
||||
log.Println("error adding policy")
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
@@ -213,20 +200,18 @@ func policyInfo(ctx context.Context, client MinioAdmin, name string) (*models.Po
|
||||
}
|
||||
|
||||
// getPolicyInfoResponse performs policyInfo() and serializes it to the handler's output
|
||||
func getPolicyInfoResponse(session *models.Principal, params admin_api.PolicyInfoParams) (*models.Policy, error) {
|
||||
func getPolicyInfoResponse(session *models.Principal, params admin_api.PolicyInfoParams) (*models.Policy, *models.Error) {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
policy, err := policyInfo(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
log.Println("error getting group info:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
@@ -244,24 +229,21 @@ func setPolicy(ctx context.Context, client MinioAdmin, name, entityName string,
|
||||
}
|
||||
|
||||
// getSetPolicyResponse() performs setPolicy() and serializes it to the handler's output
|
||||
func getSetPolicyResponse(session *models.Principal, name string, params *models.SetPolicyRequest) error {
|
||||
func getSetPolicyResponse(session *models.Principal, name string, params *models.SetPolicyRequest) *models.Error {
|
||||
ctx := context.Background()
|
||||
if name == "" {
|
||||
log.Println("error policy name not in request")
|
||||
return errors.New(500, "error policy name not in request")
|
||||
return prepareError(errPolicyNameNotInRequest)
|
||||
}
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
|
||||
if err := setPolicy(ctx, adminClient, name, *params.EntityName, params.EntityType); err != nil {
|
||||
log.Println("error setting policy:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,10 +22,8 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
@@ -37,7 +35,7 @@ func registerProfilingHandler(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIProfilingStartHandler = admin_api.ProfilingStartHandlerFunc(func(params admin_api.ProfilingStartParams, session *models.Principal) middleware.Responder {
|
||||
profilingStartResponse, err := getProfilingStartResponse(session, params.Body)
|
||||
if err != nil {
|
||||
return admin_api.NewProfilingStartDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewProfilingStartDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewProfilingStartCreated().WithPayload(profilingStartResponse)
|
||||
})
|
||||
@@ -45,7 +43,7 @@ func registerProfilingHandler(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIProfilingStopHandler = admin_api.ProfilingStopHandlerFunc(func(params admin_api.ProfilingStopParams, session *models.Principal) middleware.Responder {
|
||||
profilingStopResponse, err := getProfilingStopResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewProfilingStopDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewProfilingStopDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
// Custom response writer to set the content-disposition header to tell the
|
||||
// HTTP client the name and extension of the file we are returning
|
||||
@@ -90,24 +88,21 @@ func startProfiling(ctx context.Context, client MinioAdmin, profilerType models.
|
||||
}
|
||||
|
||||
// getProfilingStartResponse performs startProfiling() and serializes it to the handler's output
|
||||
func getProfilingStartResponse(session *models.Principal, params *models.ProfilingStartRequest) (*models.StartProfilingList, error) {
|
||||
func getProfilingStartResponse(session *models.Principal, params *models.ProfilingStartRequest) (*models.StartProfilingList, *models.Error) {
|
||||
ctx := context.Background()
|
||||
if params == nil {
|
||||
log.Println("error profiling type not in body request")
|
||||
return nil, errors.New(500, "error AddPolicy body not in request")
|
||||
return nil, prepareError(errPolicyBodyNotInRequest)
|
||||
}
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
profilingItems, err := startProfiling(ctx, adminClient, params.Type)
|
||||
if err != nil {
|
||||
log.Println("error starting profiling:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
profilingList := &models.StartProfilingList{
|
||||
StartResults: profilingItems,
|
||||
@@ -127,20 +122,18 @@ func stopProfiling(ctx context.Context, client MinioAdmin) (io.ReadCloser, error
|
||||
}
|
||||
|
||||
// getProfilingStopResponse() performs setPolicy() and serializes it to the handler's output
|
||||
func getProfilingStopResponse(session *models.Principal) (io.ReadCloser, error) {
|
||||
func getProfilingStopResponse(session *models.Principal) (io.ReadCloser, *models.Error) {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
profilingData, err := stopProfiling(ctx, adminClient)
|
||||
if err != nil {
|
||||
log.Println("error stopping profiling:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return profilingData, nil
|
||||
}
|
||||
|
||||
@@ -18,11 +18,9 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
|
||||
@@ -33,7 +31,7 @@ func registerServiceHandlers(api *operations.ConsoleAPI) {
|
||||
// Restart Service
|
||||
api.AdminAPIRestartServiceHandler = admin_api.RestartServiceHandlerFunc(func(params admin_api.RestartServiceParams, session *models.Principal) middleware.Responder {
|
||||
if err := getRestartServiceResponse(session); err != nil {
|
||||
return admin_api.NewRestartServiceDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewRestartServiceDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewRestartServiceNoContent()
|
||||
})
|
||||
@@ -61,20 +59,18 @@ func serviceRestart(ctx context.Context, client MinioAdmin) error {
|
||||
}
|
||||
|
||||
// getRestartServiceResponse performs serviceRestart()
|
||||
func getRestartServiceResponse(session *models.Principal) error {
|
||||
func getRestartServiceResponse(session *models.Principal) *models.Error {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
|
||||
if err := serviceRestart(ctx, adminClient); err != nil {
|
||||
log.Println("error restarting service:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -64,8 +64,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPICreateTenantHandler = admin_api.CreateTenantHandlerFunc(func(params admin_api.CreateTenantParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getTenantCreatedResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewCreateTenantDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewCreateTenantDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewCreateTenantOK().WithPayload(resp)
|
||||
})
|
||||
@@ -73,8 +72,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIListAllTenantsHandler = admin_api.ListAllTenantsHandlerFunc(func(params admin_api.ListAllTenantsParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getListAllTenantsResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewListTenantsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewListTenantsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewListTenantsOK().WithPayload(resp)
|
||||
|
||||
@@ -83,8 +81,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIListTenantsHandler = admin_api.ListTenantsHandlerFunc(func(params admin_api.ListTenantsParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getListTenantsResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewListTenantsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewListTenantsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewListTenantsOK().WithPayload(resp)
|
||||
|
||||
@@ -93,8 +90,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPITenantInfoHandler = admin_api.TenantInfoHandlerFunc(func(params admin_api.TenantInfoParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getTenantInfoResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewTenantInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewTenantInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewTenantInfoOK().WithPayload(resp)
|
||||
|
||||
@@ -104,8 +100,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIDeleteTenantHandler = admin_api.DeleteTenantHandlerFunc(func(params admin_api.DeleteTenantParams, session *models.Principal) middleware.Responder {
|
||||
err := getDeleteTenantResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewTenantInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewTenantInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewTenantInfoOK()
|
||||
|
||||
@@ -115,8 +110,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIUpdateTenantHandler = admin_api.UpdateTenantHandlerFunc(func(params admin_api.UpdateTenantParams, session *models.Principal) middleware.Responder {
|
||||
err := getUpdateTenantResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewUpdateTenantDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String("Unable to update tenant")})
|
||||
return admin_api.NewUpdateTenantDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewUpdateTenantCreated()
|
||||
})
|
||||
@@ -125,8 +119,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPITenantAddZoneHandler = admin_api.TenantAddZoneHandlerFunc(func(params admin_api.TenantAddZoneParams, session *models.Principal) middleware.Responder {
|
||||
err := getTenantAddZoneResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewTenantAddZoneDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String("Unable to add zone")})
|
||||
return admin_api.NewTenantAddZoneDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewTenantAddZoneCreated()
|
||||
})
|
||||
@@ -135,8 +128,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIGetTenantUsageHandler = admin_api.GetTenantUsageHandlerFunc(func(params admin_api.GetTenantUsageParams, session *models.Principal) middleware.Responder {
|
||||
payload, err := getTenantUsageResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewGetTenantUsageDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String("Unable to get tenant usage")})
|
||||
return admin_api.NewGetTenantUsageDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewGetTenantUsageOK().WithPayload(payload)
|
||||
})
|
||||
@@ -145,8 +137,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPITenantUpdateZonesHandler = admin_api.TenantUpdateZonesHandlerFunc(func(params admin_api.TenantUpdateZonesParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getTenantUpdateZoneResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewTenantUpdateZonesDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewTenantUpdateZonesDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewTenantUpdateZonesOK().WithPayload(resp)
|
||||
})
|
||||
@@ -155,8 +146,7 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPITenantUpdateCertificateHandler = admin_api.TenantUpdateCertificateHandlerFunc(func(params admin_api.TenantUpdateCertificateParams, session *models.Principal) middleware.Responder {
|
||||
err := getTenantUpdateCertificatesResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewGetTenantUsageDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String("Unable to update tenant certificates")})
|
||||
return admin_api.NewTenantUpdateCertificateDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewTenantUpdateCertificateCreated()
|
||||
})
|
||||
@@ -165,23 +155,22 @@ func registerTenantHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPITenantUpdateEncryptionHandler = admin_api.TenantUpdateEncryptionHandlerFunc(func(params admin_api.TenantUpdateEncryptionParams, session *models.Principal) middleware.Responder {
|
||||
err := getTenantUpdateEncryptionResponse(session, params)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return admin_api.NewGetTenantUsageDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String("Unable to update encryption configuration")})
|
||||
return admin_api.NewTenantUpdateEncryptionDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewTenantUpdateCertificateCreated()
|
||||
return admin_api.NewTenantUpdateEncryptionCreated()
|
||||
})
|
||||
}
|
||||
|
||||
// getDeleteTenantResponse gets the output of deleting a minio instance
|
||||
func getDeleteTenantResponse(session *models.Principal, params admin_api.DeleteTenantParams) error {
|
||||
func getDeleteTenantResponse(session *models.Principal, params admin_api.DeleteTenantParams) *models.Error {
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// get Kubernetes Client
|
||||
clientset, err := cluster.K8sClient(session.SessionToken)
|
||||
if err != nil {
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
@@ -190,7 +179,10 @@ func getDeleteTenantResponse(session *models.Principal, params admin_api.DeleteT
|
||||
if params.Body != nil {
|
||||
deleteTenantPVCs = params.Body.DeletePvcs
|
||||
}
|
||||
return deleteTenantAction(context.Background(), opClient, clientset.CoreV1(), params.Namespace, params.Tenant, deleteTenantPVCs)
|
||||
if err = deleteTenantAction(context.Background(), opClient, clientset.CoreV1(), params.Namespace, params.Tenant, deleteTenantPVCs); err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteTenantAction performs the actions of deleting a tenant
|
||||
@@ -226,15 +218,19 @@ func deleteTenantAction(
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTenantScheme(mi *operator.Tenant) string {
|
||||
// GetTenantServiceURL gets tenant's service url with the proper scheme and port
|
||||
func GetTenantServiceURL(mi *operator.Tenant) (svcURL string) {
|
||||
scheme := "http"
|
||||
port := operator.MinIOPortLoadBalancerSVC
|
||||
if mi.AutoCert() || mi.ExternalCert() {
|
||||
scheme = "https"
|
||||
port = operator.MinIOTLSPortLoadBalancerSVC
|
||||
}
|
||||
return scheme
|
||||
svc := fmt.Sprintf("%s.%s.svc.cluster.local", mi.MinIOCIServiceName(), mi.Namespace)
|
||||
return fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(svc, strconv.Itoa(port)))
|
||||
}
|
||||
|
||||
func getTenantAdminClient(ctx context.Context, client K8sClientI, namespace, tenantName, serviceName, scheme string, insecure bool) (*madmin.AdminClient, error) {
|
||||
func getTenantAdminClient(ctx context.Context, client K8sClientI, namespace, tenantName, svcURL string, insecure bool) (*madmin.AdminClient, error) {
|
||||
// get admin credentials from secret
|
||||
creds, err := client.getSecret(ctx, namespace, fmt.Sprintf("%s-secret", tenantName), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
@@ -250,7 +246,7 @@ func getTenantAdminClient(ctx context.Context, client K8sClientI, namespace, ten
|
||||
log.Println("tenant's secret doesn't contain secretkey")
|
||||
return nil, errorGeneric
|
||||
}
|
||||
mAdmin, pErr := NewAdminClientWithInsecure(scheme+"://"+net.JoinHostPort(serviceName, strconv.Itoa(operator.MinIOPort)), string(accessKey), string(secretkey), insecure)
|
||||
mAdmin, pErr := NewAdminClientWithInsecure(svcURL, string(accessKey), string(secretkey), insecure)
|
||||
if pErr != nil {
|
||||
return nil, pErr.Cause
|
||||
}
|
||||
@@ -265,35 +261,64 @@ func getTenant(ctx context.Context, operatorClient OperatorClientI, namespace, t
|
||||
return minInst, nil
|
||||
}
|
||||
|
||||
func isPrometheusEnabled(annotations map[string]string) bool {
|
||||
if annotations == nil {
|
||||
return false
|
||||
}
|
||||
// if one of the following prometheus annotations are not present
|
||||
// we consider the tenant as not integrated with prometheus
|
||||
if _, ok := annotations[prometheusPath]; !ok {
|
||||
return false
|
||||
}
|
||||
if _, ok := annotations[prometheusPort]; !ok {
|
||||
return false
|
||||
}
|
||||
if _, ok := annotations[prometheusScrape]; !ok {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getTenantInfo(tenant *operator.Tenant) *models.Tenant {
|
||||
var zones []*models.Zone
|
||||
|
||||
consoleImage := ""
|
||||
var totalSize int64
|
||||
for _, z := range tenant.Spec.Zones {
|
||||
zones = append(zones, parseTenantZone(&z))
|
||||
zoneSize := int64(z.Servers) * int64(z.VolumesPerServer) * z.VolumeClaimTemplate.Spec.Resources.Requests.Storage().Value()
|
||||
totalSize = totalSize + zoneSize
|
||||
}
|
||||
var deletion string
|
||||
if tenant.ObjectMeta.DeletionTimestamp != nil {
|
||||
deletion = tenant.ObjectMeta.DeletionTimestamp.String()
|
||||
}
|
||||
|
||||
if tenant.HasConsoleEnabled() {
|
||||
consoleImage = tenant.Spec.Console.Image
|
||||
}
|
||||
|
||||
return &models.Tenant{
|
||||
CreationDate: tenant.ObjectMeta.CreationTimestamp.String(),
|
||||
Name: tenant.Name,
|
||||
TotalSize: totalSize,
|
||||
CurrentState: tenant.Status.CurrentState,
|
||||
Zones: zones,
|
||||
Namespace: tenant.ObjectMeta.Namespace,
|
||||
Image: tenant.Spec.Image,
|
||||
CreationDate: tenant.ObjectMeta.CreationTimestamp.String(),
|
||||
DeletionDate: deletion,
|
||||
Name: tenant.Name,
|
||||
TotalSize: totalSize,
|
||||
CurrentState: tenant.Status.CurrentState,
|
||||
Zones: zones,
|
||||
Namespace: tenant.ObjectMeta.Namespace,
|
||||
Image: tenant.Spec.Image,
|
||||
ConsoleImage: consoleImage,
|
||||
EnablePrometheus: isPrometheusEnabled(tenant.Annotations),
|
||||
}
|
||||
}
|
||||
|
||||
func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInfoParams) (*models.Tenant, error) {
|
||||
func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInfoParams) (*models.Tenant, *models.Error) {
|
||||
// 5 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -302,8 +327,7 @@ func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInf
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
log.Println("error getting minioTenant:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
info := getTenantInfo(minTenant)
|
||||
@@ -339,8 +363,14 @@ func listTenants(ctx context.Context, operatorClient OperatorClientI, namespace
|
||||
}
|
||||
}
|
||||
|
||||
var deletion string
|
||||
if tenant.ObjectMeta.DeletionTimestamp != nil {
|
||||
deletion = tenant.ObjectMeta.DeletionTimestamp.String()
|
||||
}
|
||||
|
||||
tenants = append(tenants, &models.TenantList{
|
||||
CreationDate: tenant.ObjectMeta.CreationTimestamp.String(),
|
||||
DeletionDate: deletion,
|
||||
Name: tenant.ObjectMeta.Name,
|
||||
ZoneCount: int64(len(tenant.Spec.Zones)),
|
||||
InstanceCount: instanceCount,
|
||||
@@ -357,44 +387,40 @@ func listTenants(ctx context.Context, operatorClient OperatorClientI, namespace
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getListAllTenantsResponse(session *models.Principal, params admin_api.ListAllTenantsParams) (*models.ListTenantsResponse, error) {
|
||||
func getListAllTenantsResponse(session *models.Principal, params admin_api.ListAllTenantsParams) (*models.ListTenantsResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
log.Println("error getting operator client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
listT, err := listTenants(ctx, opClient, "", params.Limit)
|
||||
if err != nil {
|
||||
log.Println("error listing tenants:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return listT, nil
|
||||
}
|
||||
|
||||
// getListTenantsResponse list tenants by namespace
|
||||
func getListTenantsResponse(session *models.Principal, params admin_api.ListTenantsParams) (*models.ListTenantsResponse, error) {
|
||||
func getListTenantsResponse(session *models.Principal, params admin_api.ListTenantsParams) (*models.ListTenantsResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
log.Println("error getting operator client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
listT, err := listTenants(ctx, opClient, params.Namespace, params.Limit)
|
||||
if err != nil {
|
||||
log.Println("error listing tenants:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return listT, nil
|
||||
}
|
||||
|
||||
func getTenantCreatedResponse(session *models.Principal, params admin_api.CreateTenantParams) (*models.CreateTenantResponse, error) {
|
||||
func getTenantCreatedResponse(session *models.Principal, params admin_api.CreateTenantParams) (response *models.CreateTenantResponse, mError *models.Error) {
|
||||
tenantReq := params.Body
|
||||
minioImage := tenantReq.Image
|
||||
ctx := context.Background()
|
||||
@@ -412,7 +438,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
client: clientSet,
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
ns := *tenantReq.Namespace
|
||||
@@ -448,14 +474,27 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
|
||||
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// delete secrets created if an error occurred during tenant creation,
|
||||
defer func() {
|
||||
if mError != nil {
|
||||
log.Printf("deleting secrets created for failed tenant: %s if any\n", tenantName)
|
||||
opts := metav1.ListOptions{
|
||||
LabelSelector: fmt.Sprintf("%s=%s", operator.TenantLabel, tenantName),
|
||||
}
|
||||
err = clientSet.CoreV1().Secrets(ns).DeleteCollection(ctx, metav1.DeleteOptions{}, opts)
|
||||
if err != nil {
|
||||
log.Println("error deleting tenant's secrets:", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var envrionmentVariables []corev1.EnvVar
|
||||
// Check the Erasure Coding Parity for validity and pass it to Tenant
|
||||
if tenantReq.ErasureCodingParity > 0 {
|
||||
if tenantReq.ErasureCodingParity < 2 && tenantReq.ErasureCodingParity > 8 {
|
||||
return nil, errors.New("invalid Erasure Coding Value")
|
||||
if tenantReq.ErasureCodingParity < 2 || tenantReq.ErasureCodingParity > 8 {
|
||||
return nil, prepareError(errorInvalidErasureCodingValue)
|
||||
}
|
||||
envrionmentVariables = append(envrionmentVariables, corev1.EnvVar{
|
||||
Name: "MINIO_STORAGE_CLASS_STANDARD",
|
||||
@@ -466,7 +505,8 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
//Construct a MinIO Instance with everything we are getting from parameters
|
||||
minInst := operator.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: tenantName,
|
||||
Name: tenantName,
|
||||
Labels: tenantReq.Labels,
|
||||
},
|
||||
Spec: operator.TenantSpec{
|
||||
Image: minioImage,
|
||||
@@ -529,28 +569,28 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
}
|
||||
}
|
||||
|
||||
isEncryptionAvailable := false
|
||||
isEncryptionEnabled := false
|
||||
if tenantReq.EnableTLS != nil && *tenantReq.EnableTLS {
|
||||
// If user request autoCert, Operator will generate certificate keypair for MinIO (server), Console (server) and KES (server and app mTLS)
|
||||
isEncryptionAvailable = true
|
||||
isEncryptionEnabled = true
|
||||
minInst.Spec.RequestAutoCert = *tenantReq.EnableTLS
|
||||
}
|
||||
|
||||
if !minInst.Spec.RequestAutoCert && tenantReq.TLS != nil && tenantReq.TLS.Minio != nil {
|
||||
// User provided TLS certificates for MinIO
|
||||
isEncryptionAvailable = true
|
||||
isEncryptionEnabled = true
|
||||
// disable autoCert
|
||||
minInst.Spec.RequestAutoCert = false
|
||||
// Certificates used by the MinIO instance
|
||||
externalCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
|
||||
externalCertSecret, err := createOrReplaceExternalCertSecret(ctx, &k8sClient, ns, tenantReq.TLS.Minio, externalCertSecretName, tenantName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
minInst.Spec.ExternalCertSecret = externalCertSecret
|
||||
}
|
||||
|
||||
if tenantReq.Encryption != nil && isEncryptionAvailable {
|
||||
if tenantReq.Encryption != nil && isEncryptionEnabled {
|
||||
// Enable auto encryption
|
||||
minInst.Spec.Env = append(minInst.Spec.Env, corev1.EnvVar{
|
||||
Name: "MINIO_KMS_AUTO_ENCRYPTION",
|
||||
@@ -561,14 +601,18 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
|
||||
minInst.Spec.ExternalClientCertSecret, err = createOrReplaceExternalCertSecret(ctx, &k8sClient, ns, tenantReq.Encryption.Client, tenantExternalClientCertSecretName, tenantName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(errorGeneric)
|
||||
}
|
||||
}
|
||||
// KES configuration for Tenant instance
|
||||
minInst.Spec.KES, err = getKESConfiguration(ctx, &k8sClient, ns, tenantReq.Encryption, secretName, tenantName, minInst.Spec.RequestAutoCert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(errorGeneric)
|
||||
}
|
||||
// Set Labels, Annotations and Node Selector for KES
|
||||
minInst.Spec.KES.Labels = tenantReq.Encryption.Labels
|
||||
minInst.Spec.KES.Annotations = tenantReq.Encryption.Annotations
|
||||
minInst.Spec.KES.NodeSelector = tenantReq.Encryption.NodeSelector
|
||||
}
|
||||
|
||||
// optionals are set below
|
||||
@@ -626,10 +670,10 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
|
||||
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(errorGeneric)
|
||||
}
|
||||
|
||||
const consoleVersion = "minio/console:v0.3.19"
|
||||
const consoleVersion = "minio/console:v0.3.26"
|
||||
minInst.Spec.Console = &operator.ConsoleConfiguration{
|
||||
Replicas: 1,
|
||||
Image: consoleVersion,
|
||||
@@ -645,10 +689,17 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
externalCertSecretName := fmt.Sprintf("%s-console-external-certificates", secretName)
|
||||
externalCertSecret, err := createOrReplaceExternalCertSecret(ctx, &k8sClient, ns, tenantReq.TLS.Console, externalCertSecretName, tenantName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(errorGeneric)
|
||||
}
|
||||
minInst.Spec.Console.ExternalCertSecret = externalCertSecret
|
||||
}
|
||||
|
||||
// Set Labels, Annotations and Node Selector for Console
|
||||
if tenantReq.Console != nil {
|
||||
minInst.Spec.Console.Annotations = tenantReq.Console.Annotations
|
||||
minInst.Spec.Console.Labels = tenantReq.Console.Labels
|
||||
minInst.Spec.Console.NodeSelector = tenantReq.Console.NodeSelector
|
||||
}
|
||||
}
|
||||
|
||||
// set the service name if provided
|
||||
@@ -657,20 +708,16 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
}
|
||||
// add annotations
|
||||
var annotations map[string]string
|
||||
if minInst.Spec.Metadata == nil {
|
||||
minInst.Spec.Metadata = &metav1.ObjectMeta{
|
||||
Annotations: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
if len(tenantReq.Annotations) > 0 {
|
||||
annotations = tenantReq.Annotations
|
||||
minInst.Spec.Metadata.Annotations = annotations
|
||||
minInst.Annotations = annotations
|
||||
}
|
||||
// set the zones if they are provided
|
||||
for _, zone := range tenantReq.Zones {
|
||||
zone, err := parseTenantZoneRequest(zone, annotations)
|
||||
zone, err := parseTenantZoneRequest(zone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
minInst.Spec.Zones = append(minInst.Spec.Zones, *zone)
|
||||
}
|
||||
@@ -685,9 +732,8 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
|
||||
if tenantReq.ImagePullSecret != "" {
|
||||
imagePullSecret = tenantReq.ImagePullSecret
|
||||
} else if imagePullSecret, err = setImageRegistry(ctx, tenantName, tenantReq.ImageRegistry, clientSet.CoreV1(), ns); err != nil {
|
||||
log.Println("error setting image registry secret:", err)
|
||||
return nil, err
|
||||
} else if imagePullSecret, err = setImageRegistry(ctx, tenantReq.ImageRegistry, clientSet.CoreV1(), ns, tenantName); err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// pass the image pull secret to the Tenant
|
||||
if imagePullSecret != "" {
|
||||
@@ -697,10 +743,10 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
}
|
||||
|
||||
// prometheus annotations support
|
||||
if tenantReq.EnablePrometheus != nil && *tenantReq.EnablePrometheus && minInst.Spec.Metadata != nil && minInst.Spec.Metadata.Annotations != nil {
|
||||
minInst.Spec.Metadata.Annotations["prometheus.io/path"] = "/minio/prometheus/metrics"
|
||||
minInst.Spec.Metadata.Annotations["prometheus.io/port"] = fmt.Sprint(operator.MinIOPort)
|
||||
minInst.Spec.Metadata.Annotations["prometheus.io/scrape"] = "true"
|
||||
if tenantReq.EnablePrometheus != nil && *tenantReq.EnablePrometheus && minInst.Annotations != nil {
|
||||
minInst.Annotations[prometheusPath] = "/minio/prometheus/metrics"
|
||||
minInst.Annotations[prometheusPort] = fmt.Sprint(operator.MinIOPort)
|
||||
minInst.Annotations[prometheusScrape] = "true"
|
||||
}
|
||||
|
||||
// set console image if provided
|
||||
@@ -710,22 +756,22 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
|
||||
opClient, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
_, err = opClient.MinioV1().Tenants(ns).Create(context.Background(), &minInst, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
// Integratrions
|
||||
if os.Getenv("GKE_INTEGRATION") != "" {
|
||||
err := gkeIntegration(clientSet, tenantName, ns, session.SessionToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
}
|
||||
response := &models.CreateTenantResponse{
|
||||
response = &models.CreateTenantResponse{
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
}
|
||||
@@ -741,7 +787,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
|
||||
// setImageRegistry creates a secret to store the private registry credentials, if one exist it updates the existing one
|
||||
// returns the name of the secret created/updated
|
||||
func setImageRegistry(ctx context.Context, tenantName string, req *models.ImageRegistry, clientset v1.CoreV1Interface, namespace string) (string, error) {
|
||||
func setImageRegistry(ctx context.Context, req *models.ImageRegistry, clientset v1.CoreV1Interface, namespace, tenantName string) (string, error) {
|
||||
if req == nil || req.Registry == nil || req.Username == nil || req.Password == nil {
|
||||
return "", nil
|
||||
}
|
||||
@@ -765,33 +811,33 @@ func setImageRegistry(ctx context.Context, tenantName string, req *models.ImageR
|
||||
}
|
||||
|
||||
pullSecretName := fmt.Sprintf("%s-regcred", tenantName)
|
||||
|
||||
instanceSecret := corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: pullSecretName,
|
||||
Labels: map[string]string{
|
||||
operator.TenantLabel: tenantName,
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
corev1.DockerConfigJsonKey: []byte(string(imRegistryJSON)),
|
||||
},
|
||||
Type: corev1.SecretTypeDockerConfigJson,
|
||||
secretCredentials := map[string][]byte{
|
||||
corev1.DockerConfigJsonKey: []byte(string(imRegistryJSON)),
|
||||
}
|
||||
|
||||
// Get or Create secret if it doesn't exist
|
||||
_, err = clientset.Secrets(namespace).Get(ctx, pullSecretName, metav1.GetOptions{})
|
||||
currentSecret, err := clientset.Secrets(namespace).Get(ctx, pullSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if k8sErrors.IsNotFound(err) {
|
||||
instanceSecret := corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: pullSecretName,
|
||||
Labels: map[string]string{
|
||||
operator.TenantLabel: tenantName,
|
||||
},
|
||||
},
|
||||
Data: secretCredentials,
|
||||
Type: corev1.SecretTypeDockerConfigJson,
|
||||
}
|
||||
_, err = clientset.Secrets(namespace).Create(ctx, &instanceSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", nil
|
||||
return pullSecretName, nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
_, err = clientset.Secrets(namespace).Update(ctx, &instanceSecret, metav1.UpdateOptions{})
|
||||
currentSecret.Data = secretCredentials
|
||||
_, err = clientset.Secrets(namespace).Update(ctx, currentSecret, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -812,7 +858,7 @@ func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, cli
|
||||
minInst.Spec.ImagePullSecret.Name = params.Body.ImagePullSecret
|
||||
} else {
|
||||
// update the image pull secret content
|
||||
if _, err := setImageRegistry(ctx, params.Tenant, imageRegistryReq, clientset, namespace); err != nil {
|
||||
if _, err := setImageRegistry(ctx, imageRegistryReq, clientset, namespace, params.Tenant); err != nil {
|
||||
log.Println("error setting image registry secret:", err)
|
||||
return err
|
||||
}
|
||||
@@ -834,6 +880,35 @@ func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, cli
|
||||
}
|
||||
}
|
||||
|
||||
// Prometheus Annotations
|
||||
currentAnnotations := minInst.Annotations
|
||||
prometheusAnnotations := map[string]string{
|
||||
prometheusPath: "/minio/prometheus/metrics",
|
||||
prometheusPort: fmt.Sprint(operator.MinIOPort),
|
||||
prometheusScrape: "true",
|
||||
}
|
||||
if params.Body.EnablePrometheus && currentAnnotations != nil {
|
||||
// add prometheus annotations to the tenant
|
||||
minInst.Annotations = addAnnotations(currentAnnotations, prometheusAnnotations)
|
||||
// add prometheus annotations to the each zone
|
||||
if minInst.Spec.Zones != nil {
|
||||
for _, zone := range minInst.Spec.Zones {
|
||||
zoneAnnotations := zone.VolumeClaimTemplate.GetObjectMeta().GetAnnotations()
|
||||
zone.VolumeClaimTemplate.GetObjectMeta().SetAnnotations(addAnnotations(zoneAnnotations, prometheusAnnotations))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// remove prometheus annotations to the tenant
|
||||
minInst.Annotations = removeAnnotations(currentAnnotations, prometheusAnnotations)
|
||||
// add prometheus annotations from each zone
|
||||
if minInst.Spec.Zones != nil {
|
||||
for _, zone := range minInst.Spec.Zones {
|
||||
zoneAnnotations := zone.VolumeClaimTemplate.GetObjectMeta().GetAnnotations()
|
||||
zone.VolumeClaimTemplate.GetObjectMeta().SetAnnotations(removeAnnotations(zoneAnnotations, prometheusAnnotations))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(minInst)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -845,19 +920,39 @@ func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, cli
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUpdateTenantResponse(session *models.Principal, params admin_api.UpdateTenantParams) error {
|
||||
// addAnnotations will merge two annotation maps
|
||||
func addAnnotations(annotationsOne, annotationsTwo map[string]string) map[string]string {
|
||||
if annotationsOne == nil {
|
||||
annotationsOne = map[string]string{}
|
||||
}
|
||||
for key, value := range annotationsTwo {
|
||||
annotationsOne[key] = value
|
||||
}
|
||||
return annotationsOne
|
||||
}
|
||||
|
||||
// removeAnnotations will remove keys from the first annotations map based on the second one
|
||||
func removeAnnotations(annotationsOne, annotationsTwo map[string]string) map[string]string {
|
||||
if annotationsOne == nil {
|
||||
annotationsOne = map[string]string{}
|
||||
}
|
||||
for key := range annotationsTwo {
|
||||
delete(annotationsOne, key)
|
||||
}
|
||||
return annotationsOne
|
||||
}
|
||||
|
||||
func getUpdateTenantResponse(session *models.Principal, params admin_api.UpdateTenantParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
log.Println("error getting operator client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// get Kubernetes Client
|
||||
clientset, err := cluster.K8sClient(session.SessionToken)
|
||||
clientSet, err := cluster.K8sClient(session.SessionToken)
|
||||
if err != nil {
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
@@ -866,10 +961,8 @@ func getUpdateTenantResponse(session *models.Principal, params admin_api.UpdateT
|
||||
Timeout: 4 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
if err := updateTenantAction(ctx, opClient, clientset.CoreV1(), httpC, params.Namespace, params); err != nil {
|
||||
log.Println("error patching Tenant:", err)
|
||||
return err
|
||||
if err := updateTenantAction(ctx, opClient, clientSet.CoreV1(), httpC, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errors.New("unable to update tenant"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -882,7 +975,7 @@ func addTenantZone(ctx context.Context, operatorClient OperatorClientI, params a
|
||||
}
|
||||
|
||||
zoneParams := params.Body
|
||||
zone, err := parseTenantZoneRequest(zoneParams, tenant.ObjectMeta.Annotations)
|
||||
zone, err := parseTenantZoneRequest(zoneParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -899,68 +992,60 @@ func addTenantZone(ctx context.Context, operatorClient OperatorClientI, params a
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTenantAddZoneResponse(session *models.Principal, params admin_api.TenantAddZoneParams) error {
|
||||
func getTenantAddZoneResponse(session *models.Principal, params admin_api.TenantAddZoneParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
log.Println("error getting operator client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := addTenantZone(ctx, opClient, params); err != nil {
|
||||
log.Println("error patching Tenant:", err)
|
||||
return err
|
||||
return prepareError(err, errors.New("unable to add zone"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTenantUsageResponse returns the usage of a tenant
|
||||
func getTenantUsageResponse(session *models.Principal, params admin_api.GetTenantUsageParams) (*models.TenantUsage, error) {
|
||||
func getTenantUsageResponse(session *models.Principal, params admin_api.GetTenantUsageParams) (*models.TenantUsage, *models.Error) {
|
||||
// 5 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
log.Println("error operator client", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
}
|
||||
clientset, err := cluster.K8sClient(session.SessionToken)
|
||||
clientSet, err := cluster.K8sClient(session.SessionToken)
|
||||
if err != nil {
|
||||
log.Println("error getting k8sClient:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
k8sClient := &k8sClient{
|
||||
client: clientset,
|
||||
client: clientSet,
|
||||
}
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
log.Println("error getting minioTenant:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
}
|
||||
minTenant.EnsureDefaults()
|
||||
tenantScheme := getTenantScheme(minTenant)
|
||||
|
||||
svcName := fmt.Sprintf("%s.%s.svc.cluster.local", minTenant.MinIOCIServiceName(), minTenant.Namespace)
|
||||
svcURL := GetTenantServiceURL(minTenant)
|
||||
|
||||
mAdmin, err := getTenantAdminClient(
|
||||
ctx,
|
||||
k8sClient,
|
||||
params.Namespace,
|
||||
params.Tenant,
|
||||
svcName,
|
||||
tenantScheme,
|
||||
svcURL,
|
||||
true)
|
||||
if err != nil {
|
||||
log.Println("error getting tenant's admin client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -968,8 +1053,7 @@ func getTenantUsageResponse(session *models.Principal, params admin_api.GetTenan
|
||||
// serialize output
|
||||
adminInfo, err := getAdminInfo(ctx, adminClient)
|
||||
if err != nil {
|
||||
log.Println("error getting admin info:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
}
|
||||
info := &models.TenantUsage{Used: adminInfo.Usage, DiskUsed: adminInfo.DisksUsage}
|
||||
return info, nil
|
||||
@@ -977,7 +1061,7 @@ func getTenantUsageResponse(session *models.Principal, params admin_api.GetTenan
|
||||
|
||||
// parseTenantZoneRequest parse zone request and returns the equivalent
|
||||
// operator.Zone object
|
||||
func parseTenantZoneRequest(zoneParams *models.Zone, annotations map[string]string) (*operator.Zone, error) {
|
||||
func parseTenantZoneRequest(zoneParams *models.Zone) (*operator.Zone, error) {
|
||||
if zoneParams.VolumeConfiguration == nil {
|
||||
return nil, errors.New("a volume configuration must be specified")
|
||||
}
|
||||
@@ -1126,14 +1210,12 @@ func parseTenantZoneRequest(zoneParams *models.Zone, annotations map[string]stri
|
||||
// Pass annotations to the volume
|
||||
vct := &corev1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "data",
|
||||
Labels: zoneParams.VolumeConfiguration.Labels,
|
||||
Name: "data",
|
||||
Labels: zoneParams.VolumeConfiguration.Labels,
|
||||
Annotations: zoneParams.VolumeConfiguration.Annotations,
|
||||
},
|
||||
Spec: volTemp,
|
||||
}
|
||||
if len(annotations) > 0 {
|
||||
vct.ObjectMeta.Annotations = annotations
|
||||
}
|
||||
|
||||
zone := &operator.Zone{
|
||||
Name: zoneParams.Name,
|
||||
@@ -1390,12 +1472,11 @@ func parseNodeSelectorTerm(term *corev1.NodeSelectorTerm) *models.NodeSelectorTe
|
||||
return &t
|
||||
}
|
||||
|
||||
func getTenantUpdateZoneResponse(session *models.Principal, params admin_api.TenantUpdateZonesParams) (*models.Tenant, error) {
|
||||
func getTenantUpdateZoneResponse(session *models.Principal, params admin_api.TenantUpdateZonesParams) (*models.Tenant, *models.Error) {
|
||||
ctx := context.Background()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
log.Println("error getting operator client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -1405,7 +1486,7 @@ func getTenantUpdateZoneResponse(session *models.Principal, params admin_api.Ten
|
||||
t, err := updateTenantZones(ctx, opClient, params.Namespace, params.Tenant, params.Body.Zones)
|
||||
if err != nil {
|
||||
log.Println("error updating Tenant's zones:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
// parse it to models.Tenant
|
||||
@@ -1428,16 +1509,10 @@ func updateTenantZones(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if minInst.Spec.Metadata == nil {
|
||||
minInst.Spec.Metadata = &metav1.ObjectMeta{
|
||||
Annotations: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
// set the zones if they are provided
|
||||
var newZoneArray []operator.Zone
|
||||
for _, zone := range zonesReq {
|
||||
zone, err := parseTenantZoneRequest(zone, minInst.Spec.Metadata.Annotations)
|
||||
zone, err := parseTenantZoneRequest(zone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1447,6 +1522,9 @@ func updateTenantZones(
|
||||
// replace zones array
|
||||
minInst.Spec.Zones = newZoneArray
|
||||
|
||||
minInst = minInst.DeepCopy()
|
||||
minInst.EnsureDefaults()
|
||||
|
||||
payloadBytes, err := json.Marshal(minInst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -81,25 +81,25 @@ func tenantUpdateCertificates(ctx context.Context, operatorClient OperatorClient
|
||||
}
|
||||
|
||||
// getTenantUpdateCertificatesResponse wrapper of tenantUpdateCertificates
|
||||
func getTenantUpdateCertificatesResponse(session *models.Principal, params admin_api.TenantUpdateCertificateParams) error {
|
||||
func getTenantUpdateCertificatesResponse(session *models.Principal, params admin_api.TenantUpdateCertificateParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.SessionToken)
|
||||
if err != nil {
|
||||
return err
|
||||
return prepareError(err, errorUnableToUpdateTenantCertificates)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
return err
|
||||
return prepareError(err, errorUnableToUpdateTenantCertificates)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := tenantUpdateCertificates(ctx, &opClient, &k8sClient, params.Namespace, params); err != nil {
|
||||
return err
|
||||
return prepareError(err, errorUnableToUpdateTenantCertificates)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -157,25 +157,25 @@ func tenantUpdateEncryption(ctx context.Context, operatorClient OperatorClientI,
|
||||
}
|
||||
|
||||
// getTenantUpdateEncryptionResponse is a wrapper for tenantUpdateEncryption
|
||||
func getTenantUpdateEncryptionResponse(session *models.Principal, params admin_api.TenantUpdateEncryptionParams) error {
|
||||
func getTenantUpdateEncryptionResponse(session *models.Principal, params admin_api.TenantUpdateEncryptionParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.SessionToken)
|
||||
if err != nil {
|
||||
return err
|
||||
return prepareError(err, errorUpdatingEncryptionConfig)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
|
||||
if err != nil {
|
||||
return err
|
||||
return prepareError(err, errorUpdatingEncryptionConfig)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := tenantUpdateEncryption(ctx, &opClient, &k8sClient, params.Namespace, params); err != nil {
|
||||
return err
|
||||
return prepareError(err, errorUpdatingEncryptionConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -183,10 +183,6 @@ func getTenantUpdateEncryptionResponse(session *models.Principal, params admin_a
|
||||
// getKESConfiguration will generate the KES server certificate secrets, the tenant client secrets for mTLS authentication between MinIO and KES and the
|
||||
// kes-configuration.yaml file used by the KES service (how to connect to the external KMS, eg: Vault, AWS, Gemalto, etc)
|
||||
func getKESConfiguration(ctx context.Context, clientSet K8sClientI, ns string, encryptionCfg *models.EncryptionConfiguration, secretName, tenantName string, autoCert bool) (kesConfiguration *operator.KESConfig, err error) {
|
||||
// Secrets used by the MiniO tenant service
|
||||
//
|
||||
// tenantExternalClientCertSecretName is the name of the secret that will store the certificates for mTLS between MinIO and the KES, eg: app.key and app.crt
|
||||
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
|
||||
// Secrets used by the KES service
|
||||
//
|
||||
// kesExternalCertSecretName is the name of the secret that will store the certificates for TLS in the KES server, eg: server.key and server.crt
|
||||
@@ -195,32 +191,10 @@ func getKESConfiguration(ctx context.Context, clientSet K8sClientI, ns string, e
|
||||
kesClientCertSecretName := fmt.Sprintf("%s-kes-client-cert", secretName)
|
||||
// kesConfigurationSecretName is the name of the secret that will store the configuration file, eg: kes-configuration.yaml
|
||||
kesConfigurationSecretName := fmt.Sprintf("%s-kes-configuration", secretName)
|
||||
// if there's an error during this process we delete all KES configuration secrets
|
||||
defer func() {
|
||||
if err != nil {
|
||||
errDelete := clientSet.deleteSecret(ctx, ns, tenantExternalClientCertSecretName, metav1.DeleteOptions{})
|
||||
if errDelete != nil {
|
||||
log.Print(errDelete)
|
||||
}
|
||||
errDelete = clientSet.deleteSecret(ctx, ns, kesExternalCertSecretName, metav1.DeleteOptions{})
|
||||
if errDelete != nil {
|
||||
log.Print(errDelete)
|
||||
}
|
||||
errDelete = clientSet.deleteSecret(ctx, ns, kesClientCertSecretName, metav1.DeleteOptions{})
|
||||
if errDelete != nil {
|
||||
log.Print(errDelete)
|
||||
}
|
||||
errDelete = clientSet.deleteSecret(ctx, ns, kesConfigurationSecretName, metav1.DeleteOptions{})
|
||||
if errDelete != nil {
|
||||
log.Print(errDelete)
|
||||
}
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
kesConfiguration = &operator.KESConfig{
|
||||
Image: "minio/kes:v0.11.0",
|
||||
Replicas: 1,
|
||||
Metadata: nil,
|
||||
}
|
||||
// Using custom image for KES
|
||||
if encryptionCfg.Image != "" {
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
|
||||
import (
|
||||
|
||||
@@ -87,13 +87,12 @@ func Test_TenantInfoTenantAdminClient(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
kClient := k8sClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
client K8sClientI
|
||||
namespace string
|
||||
tenantName string
|
||||
serviceName string
|
||||
scheme string
|
||||
insecure bool
|
||||
ctx context.Context
|
||||
client K8sClientI
|
||||
namespace string
|
||||
tenantName string
|
||||
serviceURL string
|
||||
insecure bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -105,12 +104,11 @@ func Test_TenantInfoTenantAdminClient(t *testing.T) {
|
||||
{
|
||||
name: "Return Tenant Admin, no errors",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceName: "service-1",
|
||||
scheme: "http",
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceURL: "http://service-1.default.svc.cluster.local:80",
|
||||
},
|
||||
mockGetSecret: func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
vals := make(map[string][]byte)
|
||||
@@ -134,12 +132,11 @@ func Test_TenantInfoTenantAdminClient(t *testing.T) {
|
||||
{
|
||||
name: "Access key not stored on secrets",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceName: "service-1",
|
||||
scheme: "http",
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceURL: "http://service-1.default.svc.cluster.local:80",
|
||||
},
|
||||
mockGetSecret: func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
vals := make(map[string][]byte)
|
||||
@@ -162,12 +159,11 @@ func Test_TenantInfoTenantAdminClient(t *testing.T) {
|
||||
{
|
||||
name: "Secret key not stored on secrets",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceName: "service-1",
|
||||
scheme: "http",
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceURL: "http://service-1.default.svc.cluster.local:80",
|
||||
},
|
||||
mockGetSecret: func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
vals := make(map[string][]byte)
|
||||
@@ -190,12 +186,11 @@ func Test_TenantInfoTenantAdminClient(t *testing.T) {
|
||||
{
|
||||
name: "Handle error on getService",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceName: "service-1",
|
||||
scheme: "http",
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceURL: "http://service-1.default.svc.cluster.local:80",
|
||||
},
|
||||
mockGetSecret: func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
vals := make(map[string][]byte)
|
||||
@@ -214,12 +209,11 @@ func Test_TenantInfoTenantAdminClient(t *testing.T) {
|
||||
{
|
||||
name: "Handle error on getSecret",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceName: "service-1",
|
||||
scheme: "http",
|
||||
ctx: ctx,
|
||||
client: kClient,
|
||||
namespace: "default",
|
||||
tenantName: "tenant-1",
|
||||
serviceURL: "http://service-1.default.svc.cluster.local:80",
|
||||
},
|
||||
mockGetSecret: func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return nil, errors.New("error")
|
||||
@@ -239,7 +233,7 @@ func Test_TenantInfoTenantAdminClient(t *testing.T) {
|
||||
k8sclientGetSecretMock = tt.mockGetSecret
|
||||
k8sclientGetServiceMock = tt.mockGetService
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := getTenantAdminClient(tt.args.ctx, tt.args.client, tt.args.namespace, tt.args.tenantName, tt.args.serviceName, tt.args.scheme, tt.args.insecure)
|
||||
got, err := getTenantAdminClient(tt.args.ctx, tt.args.client, tt.args.namespace, tt.args.tenantName, tt.args.serviceURL, tt.args.insecure)
|
||||
if err != nil {
|
||||
if tt.wantErr {
|
||||
return
|
||||
@@ -257,7 +251,6 @@ func Test_TenantInfo(t *testing.T) {
|
||||
testTimeStamp := metav1.Now()
|
||||
type args struct {
|
||||
minioTenant *operator.Tenant
|
||||
tenantInfo *usageInfo
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -298,9 +291,6 @@ func Test_TenantInfo(t *testing.T) {
|
||||
CurrentState: "ready",
|
||||
},
|
||||
},
|
||||
tenantInfo: &usageInfo{
|
||||
DisksUsage: 1024,
|
||||
},
|
||||
},
|
||||
want: &models.Tenant{
|
||||
CreationDate: testTimeStamp.String(),
|
||||
@@ -318,8 +308,143 @@ func Test_TenantInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespace: "minio-ns",
|
||||
Image: "minio/minio:RELEASE.2020-06-14T18-32-17Z",
|
||||
Namespace: "minio-ns",
|
||||
Image: "minio/minio:RELEASE.2020-06-14T18-32-17Z",
|
||||
EnablePrometheus: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Description if DeletionTimeStamp is present, value should be returned as string
|
||||
// If Prometheus annotations are present, EnablePrometheus should be returned as true
|
||||
// All three annotations should be defined to consider Prometheus enabled
|
||||
name: "Get tenant Info w DeletionTimeStamp and Prometheus",
|
||||
args: args{
|
||||
minioTenant: &operator.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: testTimeStamp,
|
||||
Name: "tenant1",
|
||||
Namespace: "minio-ns",
|
||||
DeletionTimestamp: &testTimeStamp,
|
||||
Annotations: map[string]string{
|
||||
prometheusPath: "some/path",
|
||||
prometheusPort: "other/path",
|
||||
prometheusScrape: "other/path",
|
||||
},
|
||||
},
|
||||
Spec: operator.TenantSpec{
|
||||
Zones: []operator.Zone{
|
||||
{
|
||||
Name: "zone1",
|
||||
Servers: int32(2),
|
||||
VolumesPerServer: 4,
|
||||
VolumeClaimTemplate: &corev1.PersistentVolumeClaim{
|
||||
Spec: corev1.PersistentVolumeClaimSpec{
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: map[corev1.ResourceName]resource.Quantity{
|
||||
corev1.ResourceStorage: resource.MustParse("1Mi"),
|
||||
},
|
||||
},
|
||||
StorageClassName: swag.String("standard"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Image: "minio/minio:RELEASE.2020-06-14T18-32-17Z",
|
||||
},
|
||||
Status: operator.TenantStatus{
|
||||
CurrentState: "ready",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &models.Tenant{
|
||||
CreationDate: testTimeStamp.String(),
|
||||
DeletionDate: testTimeStamp.String(),
|
||||
Name: "tenant1",
|
||||
TotalSize: int64(8388608),
|
||||
CurrentState: "ready",
|
||||
Zones: []*models.Zone{
|
||||
{
|
||||
Name: "zone1",
|
||||
Servers: swag.Int64(int64(2)),
|
||||
VolumesPerServer: swag.Int32(4),
|
||||
VolumeConfiguration: &models.ZoneVolumeConfiguration{
|
||||
StorageClassName: "standard",
|
||||
Size: swag.Int64(1024 * 1024),
|
||||
},
|
||||
},
|
||||
},
|
||||
Namespace: "minio-ns",
|
||||
Image: "minio/minio:RELEASE.2020-06-14T18-32-17Z",
|
||||
EnablePrometheus: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
// If Prometheus annotations are present, EnablePrometheus should be returned as true
|
||||
// All three annotations should be defined to consider Prometheus enabled
|
||||
name: "Get tenant Info, not all Prometheus annotations",
|
||||
args: args{
|
||||
minioTenant: &operator.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: testTimeStamp,
|
||||
Name: "tenant1",
|
||||
Namespace: "minio-ns",
|
||||
Annotations: map[string]string{
|
||||
prometheusPath: "some/path",
|
||||
prometheusScrape: "other/path",
|
||||
},
|
||||
},
|
||||
Spec: operator.TenantSpec{
|
||||
Zones: []operator.Zone{},
|
||||
Image: "minio/minio:RELEASE.2020-06-14T18-32-17Z",
|
||||
},
|
||||
Status: operator.TenantStatus{
|
||||
CurrentState: "ready",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &models.Tenant{
|
||||
CreationDate: testTimeStamp.String(),
|
||||
Name: "tenant1",
|
||||
CurrentState: "ready",
|
||||
Namespace: "minio-ns",
|
||||
Image: "minio/minio:RELEASE.2020-06-14T18-32-17Z",
|
||||
EnablePrometheus: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
// If console image is set, it should be returned on tenant info
|
||||
name: "Get tenant Info, Console image set",
|
||||
args: args{
|
||||
minioTenant: &operator.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
CreationTimestamp: testTimeStamp,
|
||||
Name: "tenant1",
|
||||
Namespace: "minio-ns",
|
||||
Annotations: map[string]string{
|
||||
prometheusPath: "some/path",
|
||||
prometheusScrape: "other/path",
|
||||
},
|
||||
},
|
||||
Spec: operator.TenantSpec{
|
||||
Zones: []operator.Zone{},
|
||||
Image: "minio/minio:RELEASE.2020-06-14T18-32-17Z",
|
||||
Console: &operator.ConsoleConfiguration{
|
||||
Image: "minio/console:master",
|
||||
},
|
||||
},
|
||||
Status: operator.TenantStatus{
|
||||
CurrentState: "ready",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &models.Tenant{
|
||||
CreationDate: testTimeStamp.String(),
|
||||
Name: "tenant1",
|
||||
CurrentState: "ready",
|
||||
Namespace: "minio-ns",
|
||||
Image: "minio/minio:RELEASE.2020-06-14T18-32-17Z",
|
||||
EnablePrometheus: false,
|
||||
ConsoleImage: "minio/console:master",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -871,7 +996,7 @@ func Test_UpdateTenantAction(t *testing.T) {
|
||||
},
|
||||
params: admin_api.UpdateTenantParams{
|
||||
Body: &models.UpdateTenantRequest{
|
||||
ConsoleImage: "minio/console:v0.3.19",
|
||||
ConsoleImage: "minio/console:v0.3.26",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -19,7 +19,6 @@ package restapi
|
||||
import (
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
@@ -36,7 +35,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIListUsersHandler = admin_api.ListUsersHandlerFunc(func(params admin_api.ListUsersParams, session *models.Principal) middleware.Responder {
|
||||
listUsersResponse, err := getListUsersResponse(session)
|
||||
if err != nil {
|
||||
return admin_api.NewListUsersDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewListUsersDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewListUsersOK().WithPayload(listUsersResponse)
|
||||
})
|
||||
@@ -44,7 +43,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIAddUserHandler = admin_api.AddUserHandlerFunc(func(params admin_api.AddUserParams, session *models.Principal) middleware.Responder {
|
||||
userResponse, err := getUserAddResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewAddUserDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewAddUserDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewAddUserCreated().WithPayload(userResponse)
|
||||
})
|
||||
@@ -52,7 +51,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIRemoveUserHandler = admin_api.RemoveUserHandlerFunc(func(params admin_api.RemoveUserParams, session *models.Principal) middleware.Responder {
|
||||
err := getRemoveUserResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewRemoveUserDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewRemoveUserDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewRemoveUserNoContent()
|
||||
})
|
||||
@@ -60,7 +59,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIUpdateUserGroupsHandler = admin_api.UpdateUserGroupsHandlerFunc(func(params admin_api.UpdateUserGroupsParams, session *models.Principal) middleware.Responder {
|
||||
userUpdateResponse, err := getUpdateUserGroupsResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewUpdateUserGroupsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewUpdateUserGroupsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return admin_api.NewUpdateUserGroupsOK().WithPayload(userUpdateResponse)
|
||||
@@ -69,7 +68,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIGetUserInfoHandler = admin_api.GetUserInfoHandlerFunc(func(params admin_api.GetUserInfoParams, session *models.Principal) middleware.Responder {
|
||||
userInfoResponse, err := getUserInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewGetUserInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewGetUserInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return admin_api.NewGetUserInfoOK().WithPayload(userInfoResponse)
|
||||
@@ -78,7 +77,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIUpdateUserInfoHandler = admin_api.UpdateUserInfoHandlerFunc(func(params admin_api.UpdateUserInfoParams, session *models.Principal) middleware.Responder {
|
||||
userUpdateResponse, err := getUpdateUserResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewUpdateUserInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewUpdateUserInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return admin_api.NewUpdateUserInfoOK().WithPayload(userUpdateResponse)
|
||||
@@ -87,7 +86,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIBulkUpdateUsersGroupsHandler = admin_api.BulkUpdateUsersGroupsHandlerFunc(func(params admin_api.BulkUpdateUsersGroupsParams, session *models.Principal) middleware.Responder {
|
||||
err := getAddUsersListToGroupsResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewBulkUpdateUsersGroupsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewBulkUpdateUsersGroupsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return admin_api.NewBulkUpdateUsersGroupsOK()
|
||||
@@ -119,12 +118,11 @@ func listUsers(ctx context.Context, client MinioAdmin) ([]*models.User, error) {
|
||||
}
|
||||
|
||||
// getListUsersResponse performs listUsers() and serializes it to the handler's output
|
||||
func getListUsersResponse(session *models.Principal) (*models.ListUsersResponse, error) {
|
||||
func getListUsersResponse(session *models.Principal) (*models.ListUsersResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -132,8 +130,7 @@ func getListUsersResponse(session *models.Principal) (*models.ListUsersResponse,
|
||||
|
||||
users, err := listUsers(ctx, adminClient)
|
||||
if err != nil {
|
||||
log.Println("error listing users:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// serialize output
|
||||
listUsersResponse := &models.ListUsersResponse{
|
||||
@@ -167,12 +164,11 @@ func addUser(ctx context.Context, client MinioAdmin, accessKey, secretKey *strin
|
||||
return userRet, nil
|
||||
}
|
||||
|
||||
func getUserAddResponse(session *models.Principal, params admin_api.AddUserParams) (*models.User, error) {
|
||||
func getUserAddResponse(session *models.Principal, params admin_api.AddUserParams) (*models.User, *models.Error) {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -180,8 +176,7 @@ func getUserAddResponse(session *models.Principal, params admin_api.AddUserParam
|
||||
|
||||
user, err := addUser(ctx, adminClient, params.Body.AccessKey, params.Body.SecretKey, params.Body.Groups)
|
||||
if err != nil {
|
||||
log.Println("error adding user:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
@@ -194,13 +189,12 @@ func removeUser(ctx context.Context, client MinioAdmin, accessKey string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRemoveUserResponse(session *models.Principal, params admin_api.RemoveUserParams) error {
|
||||
func getRemoveUserResponse(session *models.Principal, params admin_api.RemoveUserParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -208,8 +202,7 @@ func getRemoveUserResponse(session *models.Principal, params admin_api.RemoveUse
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
|
||||
if err := removeUser(ctx, adminClient, params.Name); err != nil {
|
||||
log.Println("error removing user:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
|
||||
log.Println("User removed successfully:", params.Name)
|
||||
@@ -226,13 +219,12 @@ func getUserInfo(ctx context.Context, client MinioAdmin, accessKey string) (*mad
|
||||
return &userInfo, nil
|
||||
}
|
||||
|
||||
func getUserInfoResponse(session *models.Principal, params admin_api.GetUserInfoParams) (*models.User, error) {
|
||||
func getUserInfoResponse(session *models.Principal, params admin_api.GetUserInfoParams) (*models.User, *models.Error) {
|
||||
ctx := context.Background()
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -241,8 +233,7 @@ func getUserInfoResponse(session *models.Principal, params admin_api.GetUserInfo
|
||||
|
||||
user, err := getUserInfo(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
log.Println("error getting user:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
userInformation := &models.User{
|
||||
@@ -341,13 +332,12 @@ func updateUserGroups(ctx context.Context, client MinioAdmin, user string, group
|
||||
return userReturn, nil
|
||||
}
|
||||
|
||||
func getUpdateUserGroupsResponse(session *models.Principal, params admin_api.UpdateUserGroupsParams) (*models.User, error) {
|
||||
func getUpdateUserGroupsResponse(session *models.Principal, params admin_api.UpdateUserGroupsParams) (*models.User, *models.Error) {
|
||||
ctx := context.Background()
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -357,8 +347,7 @@ func getUpdateUserGroupsResponse(session *models.Principal, params admin_api.Upd
|
||||
user, err := updateUserGroups(ctx, adminClient, params.Name, params.Body.Groups)
|
||||
|
||||
if err != nil {
|
||||
log.Println("error updating users's groups:", params.Body.Groups)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
@@ -382,13 +371,12 @@ func setUserStatus(ctx context.Context, client MinioAdmin, user string, status s
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUpdateUserResponse(session *models.Principal, params admin_api.UpdateUserInfoParams) (*models.User, error) {
|
||||
func getUpdateUserResponse(session *models.Principal, params admin_api.UpdateUserInfoParams) (*models.User, *models.Error) {
|
||||
ctx := context.Background()
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -400,14 +388,13 @@ func getUpdateUserResponse(session *models.Principal, params admin_api.UpdateUse
|
||||
groups := params.Body.Groups
|
||||
|
||||
if err := setUserStatus(ctx, adminClient, name, status); err != nil {
|
||||
log.Println("error updating user status:", status)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
userElem, errUG := updateUserGroups(ctx, adminClient, name, groups)
|
||||
|
||||
if errUG != nil {
|
||||
return nil, errUG
|
||||
return nil, prepareError(errUG)
|
||||
}
|
||||
return userElem, nil
|
||||
}
|
||||
@@ -455,13 +442,12 @@ func addUsersListToGroups(ctx context.Context, client MinioAdmin, usersToUpdate
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAddUsersListToGroupsResponse(session *models.Principal, params admin_api.BulkUpdateUsersGroupsParams) error {
|
||||
func getAddUsersListToGroupsResponse(session *models.Principal, params admin_api.BulkUpdateUsersGroupsParams) *models.Error {
|
||||
ctx := context.Background()
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -472,8 +458,7 @@ func getAddUsersListToGroupsResponse(session *models.Principal, params admin_api
|
||||
groupsList := params.Body.Groups
|
||||
|
||||
if err := addUsersListToGroups(ctx, adminClient, usersList, groupsList); err != nil {
|
||||
log.Println("error updating groups bulk users:", err.Error())
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -114,6 +114,8 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
registerResourceQuotaHandlers(api)
|
||||
// Register Nodes' handlers
|
||||
registerNodesHandlers(api)
|
||||
// Register Parity' handlers
|
||||
registerParityHandlers(api)
|
||||
|
||||
api.PreServerShutdown = func() {}
|
||||
|
||||
|
||||
@@ -50,3 +50,11 @@ const (
|
||||
ConsoleSecureFeaturePolicy = "CONSOLE_SECURE_FEATURE_POLICY"
|
||||
ConsoleSecureExpectCTHeader = "CONSOLE_SECURE_EXPECT_CT_HEADER"
|
||||
)
|
||||
|
||||
// prometheus annotations
|
||||
|
||||
const (
|
||||
prometheusPath = "prometheus.io/path"
|
||||
prometheusPort = "prometheus.io/port"
|
||||
prometheusScrape = "prometheus.io/scrape"
|
||||
)
|
||||
|
||||
@@ -572,6 +572,45 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/get-parity/{nodes}/{disksPerNode}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Gets parity by sending number of nodes \u0026 number of disks",
|
||||
"operationId": "GetParity",
|
||||
"parameters": [
|
||||
{
|
||||
"minimum": 2,
|
||||
"type": "integer",
|
||||
"name": "nodes",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"minimum": 1,
|
||||
"type": "integer",
|
||||
"name": "disksPerNode",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/parityResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/groups": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -2174,6 +2213,21 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"consoleConfiguration": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/metadataFields"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"image": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"createTenantRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -2191,6 +2245,10 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"console": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/consoleConfiguration"
|
||||
},
|
||||
"console_image": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2226,6 +2284,12 @@ func init() {
|
||||
"image_registry": {
|
||||
"$ref": "#/definitions/imageRegistry"
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"mounth_path": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2285,32 +2349,39 @@ func init() {
|
||||
}
|
||||
},
|
||||
"encryptionConfiguration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"aws": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/awsConfiguration"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/metadataFields"
|
||||
},
|
||||
"client": {
|
||||
{
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/keyPairConfiguration"
|
||||
},
|
||||
"gemalto": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/gemaltoConfiguration"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"server": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/keyPairConfiguration"
|
||||
},
|
||||
"vault": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/vaultConfiguration"
|
||||
"properties": {
|
||||
"aws": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/awsConfiguration"
|
||||
},
|
||||
"client": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/keyPairConfiguration"
|
||||
},
|
||||
"gemalto": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/gemaltoConfiguration"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"server": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/keyPairConfiguration"
|
||||
},
|
||||
"vault": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/vaultConfiguration"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"type": "object",
|
||||
@@ -2320,7 +2391,7 @@ func init() {
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
@@ -2686,6 +2757,29 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"metadataFields": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"node_selector": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodeSelectorTerm": {
|
||||
"description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.",
|
||||
"type": "object",
|
||||
@@ -2875,6 +2969,12 @@ func init() {
|
||||
"get"
|
||||
]
|
||||
},
|
||||
"parityResponse": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"podAffinityTerm": {
|
||||
"description": "Required. A pod affinity term, associated with the corresponding weight.",
|
||||
"type": "object",
|
||||
@@ -3150,12 +3250,21 @@ func init() {
|
||||
"tenant": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"console_image": {
|
||||
"type": "string"
|
||||
},
|
||||
"creation_date": {
|
||||
"type": "string"
|
||||
},
|
||||
"currentState": {
|
||||
"type": "string"
|
||||
},
|
||||
"deletion_date": {
|
||||
"type": "string"
|
||||
},
|
||||
"enable_prometheus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -3186,6 +3295,9 @@ func init() {
|
||||
"currentState": {
|
||||
"type": "string"
|
||||
},
|
||||
"deletion_date": {
|
||||
"type": "string"
|
||||
},
|
||||
"instance_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -3257,6 +3369,9 @@ func init() {
|
||||
"type": "string",
|
||||
"pattern": "^((.*?)/(.*?):(.+))$"
|
||||
},
|
||||
"enable_prometheus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"image": {
|
||||
"type": "string",
|
||||
"pattern": "^((.*?)/(.*?):(.+))$"
|
||||
@@ -3423,6 +3538,12 @@ func init() {
|
||||
"size"
|
||||
],
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
@@ -4196,6 +4317,45 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/get-parity/{nodes}/{disksPerNode}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
],
|
||||
"summary": "Gets parity by sending number of nodes \u0026 number of disks",
|
||||
"operationId": "GetParity",
|
||||
"parameters": [
|
||||
{
|
||||
"minimum": 2,
|
||||
"type": "integer",
|
||||
"name": "nodes",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"minimum": 1,
|
||||
"type": "integer",
|
||||
"name": "disksPerNode",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/parityResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/groups": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -6074,6 +6234,12 @@ func init() {
|
||||
"size"
|
||||
],
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
@@ -6315,6 +6481,21 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"consoleConfiguration": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/metadataFields"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"image": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"createTenantRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -6332,6 +6513,10 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"console": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/consoleConfiguration"
|
||||
},
|
||||
"console_image": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -6367,6 +6552,12 @@ func init() {
|
||||
"image_registry": {
|
||||
"$ref": "#/definitions/imageRegistry"
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"mounth_path": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -6426,32 +6617,39 @@ func init() {
|
||||
}
|
||||
},
|
||||
"encryptionConfiguration": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"aws": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/awsConfiguration"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/metadataFields"
|
||||
},
|
||||
"client": {
|
||||
{
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/keyPairConfiguration"
|
||||
},
|
||||
"gemalto": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/gemaltoConfiguration"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"server": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/keyPairConfiguration"
|
||||
},
|
||||
"vault": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/vaultConfiguration"
|
||||
"properties": {
|
||||
"aws": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/awsConfiguration"
|
||||
},
|
||||
"client": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/keyPairConfiguration"
|
||||
},
|
||||
"gemalto": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/gemaltoConfiguration"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"server": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/keyPairConfiguration"
|
||||
},
|
||||
"vault": {
|
||||
"type": "object",
|
||||
"$ref": "#/definitions/vaultConfiguration"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"error": {
|
||||
"type": "object",
|
||||
@@ -6461,7 +6659,7 @@ func init() {
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
"format": "int32"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
@@ -6827,6 +7025,29 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"metadataFields": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"node_selector": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodeSelectorTerm": {
|
||||
"description": "A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.",
|
||||
"type": "object",
|
||||
@@ -6972,6 +7193,12 @@ func init() {
|
||||
"get"
|
||||
]
|
||||
},
|
||||
"parityResponse": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"podAffinityTerm": {
|
||||
"description": "Required. A pod affinity term, associated with the corresponding weight.",
|
||||
"type": "object",
|
||||
@@ -7225,12 +7452,21 @@ func init() {
|
||||
"tenant": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"console_image": {
|
||||
"type": "string"
|
||||
},
|
||||
"creation_date": {
|
||||
"type": "string"
|
||||
},
|
||||
"currentState": {
|
||||
"type": "string"
|
||||
},
|
||||
"deletion_date": {
|
||||
"type": "string"
|
||||
},
|
||||
"enable_prometheus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -7261,6 +7497,9 @@ func init() {
|
||||
"currentState": {
|
||||
"type": "string"
|
||||
},
|
||||
"deletion_date": {
|
||||
"type": "string"
|
||||
},
|
||||
"instance_count": {
|
||||
"type": "integer"
|
||||
},
|
||||
@@ -7332,6 +7571,9 @@ func init() {
|
||||
"type": "string",
|
||||
"pattern": "^((.*?)/(.*?):(.+))$"
|
||||
},
|
||||
"enable_prometheus": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"image": {
|
||||
"type": "string",
|
||||
"pattern": "^((.*?)/(.*?):(.+))$"
|
||||
@@ -7498,6 +7740,12 @@ func init() {
|
||||
"size"
|
||||
],
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
|
||||
100
restapi/error.go
Normal file
100
restapi/error.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// Generic error messages
|
||||
errorGeneric = errors.New("an error occurred, please try again")
|
||||
errInvalidCredentials = errors.New("invalid Login")
|
||||
errorGenericInvalidSession = errors.New("invalid session")
|
||||
errorGenericUnauthorized = errors.New("unauthorized")
|
||||
errorGenericForbidden = errors.New("forbidden")
|
||||
errorGenericNotFound = errors.New("not found")
|
||||
// Explicit error messages
|
||||
errorInvalidErasureCodingValue = errors.New("invalid Erasure Coding Value")
|
||||
errorUnableToGetTenantUsage = errors.New("unable to get tenant usage")
|
||||
errorUnableToUpdateTenantCertificates = errors.New("unable to update tenant certificates")
|
||||
errorUpdatingEncryptionConfig = errors.New("unable to update encryption configuration")
|
||||
errBucketBodyNotInRequest = errors.New("error bucket body not in request")
|
||||
errBucketNameNotInRequest = errors.New("error bucket name not in request")
|
||||
errGroupBodyNotInRequest = errors.New("error group body not in request")
|
||||
errGroupNameNotInRequest = errors.New("error group name not in request")
|
||||
errPolicyNameNotInRequest = errors.New("error policy name not in request")
|
||||
errPolicyBodyNotInRequest = errors.New("error policy body not in request")
|
||||
)
|
||||
|
||||
// prepareError receives an error object and parse it against k8sErrors, returns the right error code paired with a generic error message
|
||||
func prepareError(err ...error) *models.Error {
|
||||
errorCode := int32(500)
|
||||
errorMessage := errorGeneric.Error()
|
||||
if len(err) > 0 {
|
||||
log.Print("original error: ", err[0].Error())
|
||||
if k8sErrors.IsUnauthorized(err[0]) {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericUnauthorized.Error()
|
||||
}
|
||||
if k8sErrors.IsForbidden(err[0]) {
|
||||
errorCode = 403
|
||||
errorMessage = errorGenericForbidden.Error()
|
||||
}
|
||||
if k8sErrors.IsNotFound(err[0]) {
|
||||
errorCode = 404
|
||||
errorMessage = errorGenericNotFound.Error()
|
||||
}
|
||||
if errors.Is(err[0], errInvalidCredentials) {
|
||||
errorCode = 401
|
||||
errorMessage = errInvalidCredentials.Error()
|
||||
}
|
||||
// console invalid erasure coding value
|
||||
if errors.Is(err[0], errorInvalidErasureCodingValue) {
|
||||
errorCode = 400
|
||||
errorMessage = errorInvalidErasureCodingValue.Error()
|
||||
}
|
||||
if errors.Is(err[0], errBucketBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errBucketBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errBucketNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errBucketNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errGroupBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errGroupBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errGroupNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errGroupNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errPolicyNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errPolicyNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errPolicyBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errPolicyBodyNotInRequest.Error()
|
||||
}
|
||||
// console invalid session error
|
||||
if errors.Is(err[0], errorGenericInvalidSession) {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// if we received a second error take that as friendly message but dont override the code
|
||||
if len(err) > 1 && err[1] != nil {
|
||||
log.Print("friendly error: ", err[1].Error())
|
||||
errorMessage = err[1].Error()
|
||||
}
|
||||
// if we receive third error we just print that as debugging
|
||||
if len(err) > 2 && err[2] != nil {
|
||||
log.Print("debugging error: ", err[2].Error())
|
||||
}
|
||||
}
|
||||
return &models.Error{Code: errorCode, Message: swag.String(errorMessage)}
|
||||
}
|
||||
90
restapi/operations/admin_api/get_parity.go
Normal file
90
restapi/operations/admin_api/get_parity.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package admin_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"
|
||||
)
|
||||
|
||||
// GetParityHandlerFunc turns a function with the right signature into a get parity handler
|
||||
type GetParityHandlerFunc func(GetParityParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn GetParityHandlerFunc) Handle(params GetParityParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// GetParityHandler interface for that can handle valid get parity params
|
||||
type GetParityHandler interface {
|
||||
Handle(GetParityParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewGetParity creates a new http.Handler for the get parity operation
|
||||
func NewGetParity(ctx *middleware.Context, handler GetParityHandler) *GetParity {
|
||||
return &GetParity{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*GetParity swagger:route GET /get-parity/{nodes}/{disksPerNode} AdminAPI getParity
|
||||
|
||||
Gets parity by sending number of nodes & number of disks
|
||||
|
||||
*/
|
||||
type GetParity struct {
|
||||
Context *middleware.Context
|
||||
Handler GetParityHandler
|
||||
}
|
||||
|
||||
func (o *GetParity) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
r = rCtx
|
||||
}
|
||||
var Params = NewGetParityParams()
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
154
restapi/operations/admin_api/get_parity_parameters.go
Normal file
154
restapi/operations/admin_api/get_parity_parameters.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package admin_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"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// NewGetParityParams creates a new GetParityParams object
|
||||
// no default values defined in spec.
|
||||
func NewGetParityParams() GetParityParams {
|
||||
|
||||
return GetParityParams{}
|
||||
}
|
||||
|
||||
// GetParityParams contains all the bound params for the get parity operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters GetParity
|
||||
type GetParityParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
Minimum: 1
|
||||
In: path
|
||||
*/
|
||||
DisksPerNode int64
|
||||
/*
|
||||
Required: true
|
||||
Minimum: 2
|
||||
In: path
|
||||
*/
|
||||
Nodes int64
|
||||
}
|
||||
|
||||
// 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 NewGetParityParams() beforehand.
|
||||
func (o *GetParityParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
rDisksPerNode, rhkDisksPerNode, _ := route.Params.GetOK("disksPerNode")
|
||||
if err := o.bindDisksPerNode(rDisksPerNode, rhkDisksPerNode, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
rNodes, rhkNodes, _ := route.Params.GetOK("nodes")
|
||||
if err := o.bindNodes(rNodes, rhkNodes, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindDisksPerNode binds and validates parameter DisksPerNode from path.
|
||||
func (o *GetParityParams) bindDisksPerNode(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
|
||||
|
||||
value, err := swag.ConvertInt64(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("disksPerNode", "path", "int64", raw)
|
||||
}
|
||||
o.DisksPerNode = value
|
||||
|
||||
if err := o.validateDisksPerNode(formats); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateDisksPerNode carries on validations for parameter DisksPerNode
|
||||
func (o *GetParityParams) validateDisksPerNode(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.MinimumInt("disksPerNode", "path", int64(o.DisksPerNode), 1, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindNodes binds and validates parameter Nodes from path.
|
||||
func (o *GetParityParams) bindNodes(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
|
||||
|
||||
value, err := swag.ConvertInt64(raw)
|
||||
if err != nil {
|
||||
return errors.InvalidType("nodes", "path", "int64", raw)
|
||||
}
|
||||
o.Nodes = value
|
||||
|
||||
if err := o.validateNodes(formats); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateNodes carries on validations for parameter Nodes
|
||||
func (o *GetParityParams) validateNodes(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.MinimumInt("nodes", "path", int64(o.Nodes), 2, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
136
restapi/operations/admin_api/get_parity_responses.go
Normal file
136
restapi/operations/admin_api/get_parity_responses.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package admin_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"
|
||||
)
|
||||
|
||||
// GetParityOKCode is the HTTP code returned for type GetParityOK
|
||||
const GetParityOKCode int = 200
|
||||
|
||||
/*GetParityOK A successful response.
|
||||
|
||||
swagger:response getParityOK
|
||||
*/
|
||||
type GetParityOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload models.ParityResponse `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewGetParityOK creates GetParityOK with default headers values
|
||||
func NewGetParityOK() *GetParityOK {
|
||||
|
||||
return &GetParityOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the get parity o k response
|
||||
func (o *GetParityOK) WithPayload(payload models.ParityResponse) *GetParityOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the get parity o k response
|
||||
func (o *GetParityOK) SetPayload(payload models.ParityResponse) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *GetParityOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
payload := o.Payload
|
||||
if payload == nil {
|
||||
// return empty array
|
||||
payload = models.ParityResponse{}
|
||||
}
|
||||
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
|
||||
/*GetParityDefault Generic error response.
|
||||
|
||||
swagger:response getParityDefault
|
||||
*/
|
||||
type GetParityDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewGetParityDefault creates GetParityDefault with default headers values
|
||||
func NewGetParityDefault(code int) *GetParityDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &GetParityDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the get parity default response
|
||||
func (o *GetParityDefault) WithStatusCode(code int) *GetParityDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the get parity default response
|
||||
func (o *GetParityDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the get parity default response
|
||||
func (o *GetParityDefault) WithPayload(payload *models.Error) *GetParityDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the get parity default response
|
||||
func (o *GetParityDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *GetParityDefault) 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
|
||||
}
|
||||
}
|
||||
}
|
||||
126
restapi/operations/admin_api/get_parity_urlbuilder.go
Normal file
126
restapi/operations/admin_api/get_parity_urlbuilder.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package admin_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"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// GetParityURL generates an URL for the get parity operation
|
||||
type GetParityURL struct {
|
||||
DisksPerNode int64
|
||||
Nodes int64
|
||||
|
||||
_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 *GetParityURL) WithBasePath(bp string) *GetParityURL {
|
||||
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 *GetParityURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *GetParityURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/get-parity/{nodes}/{disksPerNode}"
|
||||
|
||||
disksPerNode := swag.FormatInt64(o.DisksPerNode)
|
||||
if disksPerNode != "" {
|
||||
_path = strings.Replace(_path, "{disksPerNode}", disksPerNode, -1)
|
||||
} else {
|
||||
return nil, errors.New("disksPerNode is required on GetParityURL")
|
||||
}
|
||||
|
||||
nodes := swag.FormatInt64(o.Nodes)
|
||||
if nodes != "" {
|
||||
_path = strings.Replace(_path, "{nodes}", nodes, -1)
|
||||
} else {
|
||||
return nil, errors.New("nodes is required on GetParityURL")
|
||||
}
|
||||
|
||||
_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 *GetParityURL) 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 *GetParityURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *GetParityURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on GetParityURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on GetParityURL")
|
||||
}
|
||||
|
||||
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 *GetParityURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -117,6 +117,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
||||
AdminAPIGetMaxAllocatableMemHandler: admin_api.GetMaxAllocatableMemHandlerFunc(func(params admin_api.GetMaxAllocatableMemParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.GetMaxAllocatableMem has not yet been implemented")
|
||||
}),
|
||||
AdminAPIGetParityHandler: admin_api.GetParityHandlerFunc(func(params admin_api.GetParityParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.GetParity has not yet been implemented")
|
||||
}),
|
||||
AdminAPIGetResourceQuotaHandler: admin_api.GetResourceQuotaHandlerFunc(func(params admin_api.GetResourceQuotaParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation admin_api.GetResourceQuota has not yet been implemented")
|
||||
}),
|
||||
@@ -319,6 +322,8 @@ type ConsoleAPI struct {
|
||||
AdminAPIDeleteTenantHandler admin_api.DeleteTenantHandler
|
||||
// AdminAPIGetMaxAllocatableMemHandler sets the operation handler for the get max allocatable mem operation
|
||||
AdminAPIGetMaxAllocatableMemHandler admin_api.GetMaxAllocatableMemHandler
|
||||
// AdminAPIGetParityHandler sets the operation handler for the get parity operation
|
||||
AdminAPIGetParityHandler admin_api.GetParityHandler
|
||||
// AdminAPIGetResourceQuotaHandler sets the operation handler for the get resource quota operation
|
||||
AdminAPIGetResourceQuotaHandler admin_api.GetResourceQuotaHandler
|
||||
// AdminAPIGetTenantUsageHandler sets the operation handler for the get tenant usage operation
|
||||
@@ -524,6 +529,9 @@ func (o *ConsoleAPI) Validate() error {
|
||||
if o.AdminAPIGetMaxAllocatableMemHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.GetMaxAllocatableMemHandler")
|
||||
}
|
||||
if o.AdminAPIGetParityHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.GetParityHandler")
|
||||
}
|
||||
if o.AdminAPIGetResourceQuotaHandler == nil {
|
||||
unregistered = append(unregistered, "admin_api.GetResourceQuotaHandler")
|
||||
}
|
||||
@@ -816,6 +824,10 @@ func (o *ConsoleAPI) initHandlerCache() {
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/get-parity/{nodes}/{disksPerNode}"] = admin_api.NewGetParity(o.context, o.AdminAPIGetParityHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/namespaces/{namespace}/resourcequotas/{resource-quota-name}"] = admin_api.NewGetResourceQuota(o.context, o.AdminAPIGetResourceQuotaHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
|
||||
@@ -18,12 +18,10 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/admin_api"
|
||||
@@ -35,7 +33,7 @@ func registerResourceQuotaHandlers(api *operations.ConsoleAPI) {
|
||||
api.AdminAPIGetResourceQuotaHandler = admin_api.GetResourceQuotaHandlerFunc(func(params admin_api.GetResourceQuotaParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getResourceQuotaResponse(session, params)
|
||||
if err != nil {
|
||||
return admin_api.NewGetResourceQuotaDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return admin_api.NewGetResourceQuotaDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return admin_api.NewGetResourceQuotaOK().WithPayload(resp)
|
||||
|
||||
@@ -68,20 +66,18 @@ func getResourceQuota(ctx context.Context, client K8sClientI, namespace, resourc
|
||||
return &rq, nil
|
||||
}
|
||||
|
||||
func getResourceQuotaResponse(session *models.Principal, params admin_api.GetResourceQuotaParams) (*models.ResourceQuota, error) {
|
||||
func getResourceQuotaResponse(session *models.Principal, params admin_api.GetResourceQuotaParams) (*models.ResourceQuota, *models.Error) {
|
||||
ctx := context.Background()
|
||||
client, err := cluster.K8sClient(session.SessionToken)
|
||||
if err != nil {
|
||||
log.Println("error getting k8sClient:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
k8sClient := &k8sClient{
|
||||
client: client,
|
||||
}
|
||||
resourceQuota, err := getResourceQuota(ctx, k8sClient, params.Namespace, params.ResourceQuotaName)
|
||||
if err != nil {
|
||||
log.Println("error getting resource quota:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
|
||||
}
|
||||
return resourceQuota, nil
|
||||
|
||||
@@ -20,11 +20,9 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
@@ -39,21 +37,21 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserAPIListBucketsHandler = user_api.ListBucketsHandlerFunc(func(params user_api.ListBucketsParams, session *models.Principal) middleware.Responder {
|
||||
listBucketsResponse, err := getListBucketsResponse(session)
|
||||
if err != nil {
|
||||
return user_api.NewListBucketsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewListBucketsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewListBucketsOK().WithPayload(listBucketsResponse)
|
||||
})
|
||||
// make bucket
|
||||
api.UserAPIMakeBucketHandler = user_api.MakeBucketHandlerFunc(func(params user_api.MakeBucketParams, session *models.Principal) middleware.Responder {
|
||||
if err := getMakeBucketResponse(session, params.Body); err != nil {
|
||||
return user_api.NewMakeBucketDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewMakeBucketDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewMakeBucketCreated()
|
||||
})
|
||||
// delete bucket
|
||||
api.UserAPIDeleteBucketHandler = user_api.DeleteBucketHandlerFunc(func(params user_api.DeleteBucketParams, session *models.Principal) middleware.Responder {
|
||||
if err := getDeleteBucketResponse(session, params); err != nil {
|
||||
return user_api.NewMakeBucketDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewMakeBucketDefault(int(err.Code)).WithPayload(err)
|
||||
|
||||
}
|
||||
return user_api.NewDeleteBucketNoContent()
|
||||
@@ -62,7 +60,7 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserAPIBucketInfoHandler = user_api.BucketInfoHandlerFunc(func(params user_api.BucketInfoParams, session *models.Principal) middleware.Responder {
|
||||
bucketInfoResp, err := getBucketInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return user_api.NewBucketInfoDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewBucketInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return user_api.NewBucketInfoOK().WithPayload(bucketInfoResp)
|
||||
@@ -71,7 +69,7 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserAPIBucketSetPolicyHandler = user_api.BucketSetPolicyHandlerFunc(func(params user_api.BucketSetPolicyParams, session *models.Principal) middleware.Responder {
|
||||
bucketSetPolicyResp, err := getBucketSetPolicyResponse(session, params.Name, params.Body)
|
||||
if err != nil {
|
||||
return user_api.NewBucketSetPolicyDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewBucketSetPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewBucketSetPolicyOK().WithPayload(bucketSetPolicyResp)
|
||||
})
|
||||
@@ -92,22 +90,20 @@ func getaAcountUsageInfo(ctx context.Context, client MinioAdmin) ([]*models.Buck
|
||||
}
|
||||
|
||||
// getListBucketsResponse performs listBuckets() and serializes it to the handler's output
|
||||
func getListBucketsResponse(session *models.Principal) (*models.ListBucketsResponse, error) {
|
||||
func getListBucketsResponse(session *models.Principal) (*models.ListBucketsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
buckets, err := getaAcountUsageInfo(ctx, adminClient)
|
||||
if err != nil {
|
||||
log.Println("error accountingUsageInfo:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
// serialize output
|
||||
@@ -128,26 +124,23 @@ func makeBucket(ctx context.Context, client MinioClient, bucketName string) erro
|
||||
}
|
||||
|
||||
// getMakeBucketResponse performs makeBucket() to create a bucket with its access policy
|
||||
func getMakeBucketResponse(session *models.Principal, br *models.MakeBucketRequest) error {
|
||||
func getMakeBucketResponse(session *models.Principal, br *models.MakeBucketRequest) *models.Error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
// bucket request needed to proceed
|
||||
if br == nil {
|
||||
log.Println("error bucket body not in request")
|
||||
return errors.New(500, "error bucket body not in request")
|
||||
return prepareError(errBucketBodyNotInRequest)
|
||||
}
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating MinIO Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
if err := makeBucket(ctx, minioClient, *br.Name); err != nil {
|
||||
log.Println("error making bucket:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -182,14 +175,13 @@ func setBucketAccessPolicy(ctx context.Context, client MinioClient, bucketName s
|
||||
|
||||
// getBucketSetPolicyResponse calls setBucketAccessPolicy() to set a access policy to a bucket
|
||||
// and returns the serialized output.
|
||||
func getBucketSetPolicyResponse(session *models.Principal, bucketName string, req *models.SetBucketPolicyRequest) (*models.Bucket, error) {
|
||||
func getBucketSetPolicyResponse(session *models.Principal, bucketName string, req *models.SetBucketPolicyRequest) (*models.Bucket, *models.Error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating MinIO Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -197,14 +189,12 @@ func getBucketSetPolicyResponse(session *models.Principal, bucketName string, re
|
||||
|
||||
// set bucket access policy
|
||||
if err := setBucketAccessPolicy(ctx, minioClient, bucketName, req.Access); err != nil {
|
||||
log.Println("error setting bucket access policy:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// get updated bucket details and return it
|
||||
bucket, err := getBucketInfo(minioClient, bucketName)
|
||||
if err != nil {
|
||||
log.Println("error getting bucket's info:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return bucket, nil
|
||||
}
|
||||
@@ -215,23 +205,23 @@ func removeBucket(client MinioClient, bucketName string) error {
|
||||
}
|
||||
|
||||
// getDeleteBucketResponse performs removeBucket() to delete a bucket
|
||||
func getDeleteBucketResponse(session *models.Principal, params user_api.DeleteBucketParams) error {
|
||||
func getDeleteBucketResponse(session *models.Principal, params user_api.DeleteBucketParams) *models.Error {
|
||||
if params.Name == "" {
|
||||
log.Println("error bucket name not in request")
|
||||
return errors.New(500, "error bucket name not in request")
|
||||
return prepareError(errBucketNameNotInRequest)
|
||||
}
|
||||
bucketName := params.Name
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating MinIO Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
return removeBucket(minioClient, bucketName)
|
||||
if err := removeBucket(minioClient, bucketName); err != nil {
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getBucketInfo return bucket information including name, policy access, size and creation date
|
||||
@@ -264,11 +254,10 @@ func getBucketInfo(client MinioClient, bucketName string) (*models.Bucket, error
|
||||
}
|
||||
|
||||
// getBucketInfoResponse calls getBucketInfo() to get the bucket's info
|
||||
func getBucketInfoResponse(session *models.Principal, params user_api.BucketInfoParams) (*models.Bucket, error) {
|
||||
func getBucketInfoResponse(session *models.Principal, params user_api.BucketInfoParams) (*models.Bucket, *models.Error) {
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating MinIO Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -276,8 +265,7 @@ func getBucketInfoResponse(session *models.Principal, params user_api.BucketInfo
|
||||
|
||||
bucket, err := getBucketInfo(minioClient, params.Name)
|
||||
if err != nil {
|
||||
log.Println("error getting bucket's info:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return bucket, nil
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
@@ -34,21 +33,21 @@ func registerBucketEventsHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserAPIListBucketEventsHandler = user_api.ListBucketEventsHandlerFunc(func(params user_api.ListBucketEventsParams, session *models.Principal) middleware.Responder {
|
||||
listBucketEventsResponse, err := getListBucketEventsResponse(session, params)
|
||||
if err != nil {
|
||||
return user_api.NewListBucketEventsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewListBucketEventsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewListBucketEventsOK().WithPayload(listBucketEventsResponse)
|
||||
})
|
||||
// create bucket event
|
||||
api.UserAPICreateBucketEventHandler = user_api.CreateBucketEventHandlerFunc(func(params user_api.CreateBucketEventParams, session *models.Principal) middleware.Responder {
|
||||
if err := getCreateBucketEventsResponse(session, params.BucketName, params.Body); err != nil {
|
||||
return user_api.NewCreateBucketEventDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewCreateBucketEventDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewCreateBucketEventCreated()
|
||||
})
|
||||
// delete bucket event
|
||||
api.UserAPIDeleteBucketEventHandler = user_api.DeleteBucketEventHandlerFunc(func(params user_api.DeleteBucketEventParams, session *models.Principal) middleware.Responder {
|
||||
if err := getDeleteBucketEventsResponse(session, params.BucketName, params.Arn, params.Body.Events, params.Body.Prefix, params.Body.Suffix); err != nil {
|
||||
return user_api.NewDeleteBucketEventDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewDeleteBucketEventDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewDeleteBucketEventNoContent()
|
||||
})
|
||||
@@ -125,11 +124,10 @@ func listBucketEvents(client MinioClient, bucketName string) ([]*models.Notifica
|
||||
}
|
||||
|
||||
// getListBucketsResponse performs listBucketEvents() and serializes it to the handler's output
|
||||
func getListBucketEventsResponse(session *models.Principal, params user_api.ListBucketEventsParams) (*models.ListBucketEventsResponse, error) {
|
||||
func getListBucketEventsResponse(session *models.Principal, params user_api.ListBucketEventsParams) (*models.ListBucketEventsResponse, *models.Error) {
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating MinIO Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -137,8 +135,7 @@ func getListBucketEventsResponse(session *models.Principal, params user_api.List
|
||||
|
||||
bucketEvents, err := listBucketEvents(minioClient, params.BucketName)
|
||||
if err != nil {
|
||||
log.Println("error listing bucket events:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// serialize output
|
||||
listBucketsResponse := &models.ListBucketEventsResponse{
|
||||
@@ -178,20 +175,18 @@ func createBucketEvent(ctx context.Context, client MCClient, arn string, notific
|
||||
}
|
||||
|
||||
// getCreateBucketEventsResponse calls createBucketEvent to add a bucket event notification
|
||||
func getCreateBucketEventsResponse(session *models.Principal, bucketName string, eventReq *models.BucketEventRequest) error {
|
||||
func getCreateBucketEventsResponse(session *models.Principal, bucketName string, eventReq *models.BucketEventRequest) *models.Error {
|
||||
ctx := context.Background()
|
||||
s3Client, err := newS3BucketClient(session, bucketName)
|
||||
if err != nil {
|
||||
log.Println("error creating S3Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
err = createBucketEvent(ctx, mcClient, *eventReq.Configuration.Arn, eventReq.Configuration.Events, eventReq.Configuration.Prefix, eventReq.Configuration.Suffix, eventReq.IgnoreExisting)
|
||||
if err != nil {
|
||||
log.Println("error creating bucket event:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -215,20 +210,18 @@ func joinNotificationEvents(events []models.NotificationEventType) string {
|
||||
}
|
||||
|
||||
// getDeleteBucketEventsResponse calls deleteBucketEventNotification() to delete a bucket event notification
|
||||
func getDeleteBucketEventsResponse(session *models.Principal, bucketName string, arn string, events []models.NotificationEventType, prefix, suffix *string) error {
|
||||
func getDeleteBucketEventsResponse(session *models.Principal, bucketName string, arn string, events []models.NotificationEventType, prefix, suffix *string) *models.Error {
|
||||
ctx := context.Background()
|
||||
s3Client, err := newS3BucketClient(session, bucketName)
|
||||
if err != nil {
|
||||
log.Println("error creating S3Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
err = deleteBucketEventNotification(ctx, mcClient, arn, events, prefix, suffix)
|
||||
if err != nil {
|
||||
log.Println("error deleting bucket event:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,11 +18,9 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg/acl"
|
||||
"github.com/minio/console/pkg/auth"
|
||||
@@ -32,17 +30,12 @@ import (
|
||||
"github.com/minio/console/restapi/operations/user_api"
|
||||
)
|
||||
|
||||
var (
|
||||
errorGeneric = errors.New("an error occurred, please try again")
|
||||
errInvalidCredentials = errors.New("invalid Login")
|
||||
)
|
||||
|
||||
func registerLoginHandlers(api *operations.ConsoleAPI) {
|
||||
// get login strategy
|
||||
api.UserAPILoginDetailHandler = user_api.LoginDetailHandlerFunc(func(params user_api.LoginDetailParams) middleware.Responder {
|
||||
loginDetails, err := getLoginDetailsResponse()
|
||||
if err != nil {
|
||||
return user_api.NewLoginDetailDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewLoginDetailDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewLoginDetailOK().WithPayload(loginDetails)
|
||||
})
|
||||
@@ -50,21 +43,21 @@ func registerLoginHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserAPILoginHandler = user_api.LoginHandlerFunc(func(params user_api.LoginParams) middleware.Responder {
|
||||
loginResponse, err := getLoginResponse(params.Body)
|
||||
if err != nil {
|
||||
return user_api.NewLoginDefault(401).WithPayload(&models.Error{Code: 401, Message: swag.String(err.Error())})
|
||||
return user_api.NewLoginDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewLoginCreated().WithPayload(loginResponse)
|
||||
})
|
||||
api.UserAPILoginOauth2AuthHandler = user_api.LoginOauth2AuthHandlerFunc(func(params user_api.LoginOauth2AuthParams) middleware.Responder {
|
||||
loginResponse, err := getLoginOauth2AuthResponse(params.Body)
|
||||
if err != nil {
|
||||
return user_api.NewLoginOauth2AuthDefault(401).WithPayload(&models.Error{Code: 401, Message: swag.String(err.Error())})
|
||||
return user_api.NewLoginOauth2AuthDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewLoginOauth2AuthCreated().WithPayload(loginResponse)
|
||||
})
|
||||
api.UserAPILoginOperatorHandler = user_api.LoginOperatorHandlerFunc(func(params user_api.LoginOperatorParams) middleware.Responder {
|
||||
loginResponse, err := getLoginOperatorResponse(params.Body)
|
||||
if err != nil {
|
||||
return user_api.NewLoginOperatorDefault(401).WithPayload(&models.Error{Code: 401, Message: swag.String(err.Error())})
|
||||
return user_api.NewLoginOperatorDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewLoginOperatorCreated().WithPayload(loginResponse)
|
||||
})
|
||||
@@ -76,8 +69,7 @@ func login(credentials ConsoleCredentials, actions []string) (*string, error) {
|
||||
// try to obtain consoleCredentials,
|
||||
tokens, err := credentials.Get()
|
||||
if err != nil {
|
||||
log.Println("error authenticating user", err)
|
||||
return nil, errInvalidCredentials
|
||||
return nil, err
|
||||
}
|
||||
// if we made it here, the consoleCredentials work, generate a jwt with claims
|
||||
jwt, err := auth.NewEncryptedTokenForClient(&tokens, actions)
|
||||
@@ -103,32 +95,29 @@ func getConfiguredRegionForLogin(client MinioAdmin) (string, error) {
|
||||
}
|
||||
|
||||
// getLoginResponse performs login() and serializes it to the handler's output
|
||||
func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, error) {
|
||||
func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
mAdmin, err := newSuperMAdminClient()
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, errorGeneric
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
// obtain the configured MinIO region
|
||||
// need it for user authentication
|
||||
location, err := getConfiguredRegionForLogin(adminClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
creds, err := newConsoleCredentials(*lr.AccessKey, *lr.SecretKey, location)
|
||||
if err != nil {
|
||||
log.Println("error login:", err)
|
||||
return nil, errInvalidCredentials
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
credentials := consoleCredentials{consoleCredentials: creds}
|
||||
// obtain the current policy assigned to this user
|
||||
// necessary for generating the list of allowed endpoints
|
||||
userInfo, err := adminClient.getUserInfo(ctx, *lr.AccessKey)
|
||||
if err != nil {
|
||||
log.Println("error login:", err)
|
||||
return nil, errInvalidCredentials
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
policy, _ := adminClient.getPolicy(ctx, userInfo.PolicyName)
|
||||
// by default every user starts with an empty array of available actions
|
||||
@@ -141,7 +130,7 @@ func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, error) {
|
||||
}
|
||||
sessionID, err := login(credentials, actions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
@@ -151,7 +140,7 @@ func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, error) {
|
||||
}
|
||||
|
||||
// getLoginDetailsResponse returns information regarding the Console authentication mechanism.
|
||||
func getLoginDetailsResponse() (*models.LoginDetails, error) {
|
||||
func getLoginDetailsResponse() (*models.LoginDetails, *models.Error) {
|
||||
ctx := context.Background()
|
||||
loginStrategy := models.LoginDetailsLoginStrategyForm
|
||||
redirectURL := ""
|
||||
@@ -162,8 +151,7 @@ func getLoginDetailsResponse() (*models.LoginDetails, error) {
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(ctx, nil)
|
||||
if err != nil {
|
||||
log.Println("error getting new oauth2 provider client", err)
|
||||
return nil, errorGeneric
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// Validate user against IDP
|
||||
identityProvider := &auth.IdentityProvider{Client: oauth2Client}
|
||||
@@ -180,31 +168,29 @@ func loginOauth2Auth(ctx context.Context, provider *auth.IdentityProvider, code,
|
||||
userIdentity, err := provider.VerifyIdentity(ctx, code, state)
|
||||
if err != nil {
|
||||
log.Println("error validating user identity against idp:", err)
|
||||
return nil, errorGeneric
|
||||
return nil, errInvalidCredentials
|
||||
}
|
||||
return userIdentity, nil
|
||||
}
|
||||
|
||||
func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.LoginResponse, error) {
|
||||
func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.LoginResponse, *models.Error) {
|
||||
ctx := context.Background()
|
||||
if oauth2.IsIdpEnabled() {
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(ctx, nil)
|
||||
if err != nil {
|
||||
log.Println("error getting new oauth2 client:", err)
|
||||
return nil, errorGeneric
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// initialize new identity provider
|
||||
identityProvider := &auth.IdentityProvider{Client: oauth2Client}
|
||||
// Validate user against IDP
|
||||
identity, err := loginOauth2Auth(ctx, identityProvider, *lr.Code, *lr.State)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
}
|
||||
mAdmin, err := newSuperMAdminClient()
|
||||
if err != nil {
|
||||
log.Println("error creating Madmin Client:", err)
|
||||
return nil, errorGeneric
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
adminClient := adminClient{client: mAdmin}
|
||||
accessKey := identity.Email
|
||||
@@ -213,12 +199,11 @@ func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.Logi
|
||||
// need it for user authentication
|
||||
location, err := getConfiguredRegionForLogin(adminClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create user in MinIO
|
||||
if _, err := addUser(ctx, adminClient, &accessKey, &secretKey, []string{}); err != nil {
|
||||
log.Println("error adding user:", err)
|
||||
return nil, errorGeneric
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// rollback user if there's an error after this point
|
||||
defer func() {
|
||||
@@ -231,26 +216,23 @@ func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.Logi
|
||||
// assign the "consoleAdmin" policy to this user
|
||||
policyName := oauth2.GetIDPPolicyForUser()
|
||||
if err := setPolicy(ctx, adminClient, policyName, accessKey, models.PolicyEntityUser); err != nil {
|
||||
log.Println("error setting policy:", err)
|
||||
return nil, errorGeneric
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// obtain the current policy details, necessary for generating the list of allowed endpoints
|
||||
policy, err := adminClient.getPolicy(ctx, policyName)
|
||||
if err != nil {
|
||||
log.Println("error reading policy:", err)
|
||||
return nil, errorGeneric
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
actions := acl.GetActionsStringFromPolicy(policy)
|
||||
// User was created correctly, create a new session/JWT
|
||||
creds, err := newConsoleCredentials(accessKey, secretKey, location)
|
||||
if err != nil {
|
||||
log.Println("error login:", err)
|
||||
return nil, errorGeneric
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
credentials := consoleCredentials{consoleCredentials: creds}
|
||||
jwt, err := login(credentials, actions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
@@ -258,21 +240,20 @@ func getLoginOauth2AuthResponse(lr *models.LoginOauth2AuthRequest) (*models.Logi
|
||||
}
|
||||
return loginResponse, nil
|
||||
}
|
||||
return nil, errorGeneric
|
||||
return nil, prepareError(errorGeneric)
|
||||
}
|
||||
|
||||
// getLoginOperatorResponse validate the provided service account token against k8s api
|
||||
func getLoginOperatorResponse(lmr *models.LoginOperatorRequest) (*models.LoginResponse, error) {
|
||||
func getLoginOperatorResponse(lmr *models.LoginOperatorRequest) (*models.LoginResponse, *models.Error) {
|
||||
creds, err := newConsoleCredentials("", *lmr.Jwt, "")
|
||||
if err != nil {
|
||||
log.Println("error login:", err)
|
||||
return nil, errInvalidCredentials
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
credentials := consoleCredentials{consoleCredentials: creds}
|
||||
var actions []string
|
||||
jwt, err := login(credentials, actions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
|
||||
@@ -100,7 +100,7 @@ func TestLoginOauth2Auth(t *testing.T) {
|
||||
return nil, errors.New("error")
|
||||
}
|
||||
if _, err := loginOauth2Auth(ctx, identityProvider, mockCode, mockState); funcAssert.Error(err) {
|
||||
funcAssert.Equal("an error occurred, please try again", err.Error())
|
||||
funcAssert.Equal(errInvalidCredentials.Error(), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,10 @@ package restapi
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/user_api"
|
||||
@@ -36,7 +34,7 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserAPICreateServiceAccountHandler = user_api.CreateServiceAccountHandlerFunc(func(params user_api.CreateServiceAccountParams, session *models.Principal) middleware.Responder {
|
||||
creds, err := getCreateServiceAccountResponse(session, params.Body)
|
||||
if err != nil {
|
||||
return user_api.NewCreateServiceAccountDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewCreateServiceAccountDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewCreateServiceAccountCreated().WithPayload(creds)
|
||||
})
|
||||
@@ -44,7 +42,7 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
api.UserAPIListUserServiceAccountsHandler = user_api.ListUserServiceAccountsHandlerFunc(func(params user_api.ListUserServiceAccountsParams, session *models.Principal) middleware.Responder {
|
||||
serviceAccounts, err := getUserServiceAccountsResponse(session)
|
||||
if err != nil {
|
||||
return user_api.NewListUserServiceAccountsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewListUserServiceAccountsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewListUserServiceAccountsOK().WithPayload(serviceAccounts)
|
||||
})
|
||||
@@ -52,7 +50,7 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
// Delete a User's service account
|
||||
api.UserAPIDeleteServiceAccountHandler = user_api.DeleteServiceAccountHandlerFunc(func(params user_api.DeleteServiceAccountParams, session *models.Principal) middleware.Responder {
|
||||
if err := getDeleteServiceAccountResponse(session, params.AccessKey); err != nil {
|
||||
return user_api.NewDeleteServiceAccountDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return user_api.NewDeleteServiceAccountDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewDeleteServiceAccountNoContent()
|
||||
})
|
||||
@@ -79,14 +77,13 @@ func createServiceAccount(ctx context.Context, userClient MinioAdmin, policy str
|
||||
// getCreateServiceAccountResponse creates a service account with the defined policy for the user that
|
||||
// is requestingit ,it first gets the credentials of the user and creates a client which is going to
|
||||
// make the call to create the Service Account
|
||||
func getCreateServiceAccountResponse(session *models.Principal, serviceAccount *models.ServiceAccountRequest) (*models.ServiceAccountCreds, error) {
|
||||
func getCreateServiceAccountResponse(session *models.Principal, serviceAccount *models.ServiceAccountRequest) (*models.ServiceAccountCreds, *models.Error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
userAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating user Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -94,8 +91,7 @@ func getCreateServiceAccountResponse(session *models.Principal, serviceAccount *
|
||||
|
||||
saCreds, err := createServiceAccount(ctx, userAdminClient, serviceAccount.Policy)
|
||||
if err != nil {
|
||||
log.Println("error creating service account:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return saCreds, nil
|
||||
}
|
||||
@@ -115,14 +111,13 @@ func getUserServiceAccounts(ctx context.Context, userClient MinioAdmin) (models.
|
||||
|
||||
// getUserServiceAccountsResponse authenticates the user and calls
|
||||
// getUserServiceAccounts to list the user's service accounts
|
||||
func getUserServiceAccountsResponse(session *models.Principal) (models.ServiceAccounts, error) {
|
||||
func getUserServiceAccountsResponse(session *models.Principal) (models.ServiceAccounts, *models.Error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
userAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating user Client:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -130,11 +125,9 @@ func getUserServiceAccountsResponse(session *models.Principal) (models.ServiceAc
|
||||
|
||||
serviceAccounts, err := getUserServiceAccounts(ctx, userAdminClient)
|
||||
if err != nil {
|
||||
log.Println("error listing user's service account:", err)
|
||||
return nil, err
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
return serviceAccounts, nil
|
||||
|
||||
}
|
||||
|
||||
// deleteServiceAccount calls delete service account api
|
||||
@@ -143,22 +136,20 @@ func deleteServiceAccount(ctx context.Context, userClient MinioAdmin, accessKey
|
||||
}
|
||||
|
||||
// getDeleteServiceAccountResponse authenticates the user and calls deleteServiceAccount
|
||||
func getDeleteServiceAccountResponse(session *models.Principal, accessKey string) error {
|
||||
func getDeleteServiceAccountResponse(session *models.Principal, accessKey string) *models.Error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
|
||||
userAdmin, err := newMAdminClient(session)
|
||||
if err != nil {
|
||||
log.Println("error creating user Client:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
userAdminClient := adminClient{client: userAdmin}
|
||||
|
||||
if err := deleteServiceAccount(ctx, userAdminClient, accessKey); err != nil {
|
||||
log.Println("error deleting user's service account:", err)
|
||||
return err
|
||||
return prepareError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -17,36 +17,29 @@
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg/acl"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/minio/console/restapi/operations/user_api"
|
||||
)
|
||||
|
||||
var (
|
||||
errorGenericInvalidSession = errors.New("invalid session")
|
||||
)
|
||||
|
||||
func registerSessionHandlers(api *operations.ConsoleAPI) {
|
||||
// session check
|
||||
api.UserAPISessionCheckHandler = user_api.SessionCheckHandlerFunc(func(params user_api.SessionCheckParams, session *models.Principal) middleware.Responder {
|
||||
sessionResp, err := getSessionResponse(session)
|
||||
if err != nil {
|
||||
return user_api.NewSessionCheckDefault(401).WithPayload(&models.Error{Code: 401, Message: swag.String(err.Error())})
|
||||
return user_api.NewSessionCheckDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return user_api.NewSessionCheckOK().WithPayload(sessionResp)
|
||||
})
|
||||
}
|
||||
|
||||
// getSessionResponse parse the jwt of the current session and returns a list of allowed actions to render in the UI
|
||||
func getSessionResponse(session *models.Principal) (*models.SessionResponse, error) {
|
||||
func getSessionResponse(session *models.Principal) (*models.SessionResponse, *models.Error) {
|
||||
// serialize output
|
||||
if session == nil {
|
||||
return nil, errorGenericInvalidSession
|
||||
return nil, prepareError(errorGenericInvalidSession)
|
||||
}
|
||||
sessionResp := &models.SessionResponse{
|
||||
Pages: acl.GetAuthorizedEndpoints(session.Actions),
|
||||
|
||||
145
swagger.yml
145
swagger.yml
@@ -1148,7 +1148,7 @@ paths:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
put:
|
||||
put:
|
||||
summary: Tenant Update Zones
|
||||
operationId: TenantUpdateZones
|
||||
parameters:
|
||||
@@ -1282,7 +1282,7 @@ paths:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
|
||||
/cluster/max-allocatable-memory:
|
||||
get:
|
||||
summary: Get maximum allocatable memory for given number of nodes
|
||||
@@ -1306,6 +1306,33 @@ paths:
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
/get-parity/{nodes}/{disksPerNode}:
|
||||
get:
|
||||
summary: Gets parity by sending number of nodes & number of disks
|
||||
operationId: GetParity
|
||||
parameters:
|
||||
- name: nodes
|
||||
in: path
|
||||
required: true
|
||||
type: integer
|
||||
minimum: 2
|
||||
- name: disksPerNode
|
||||
in: path
|
||||
required: true
|
||||
type: integer
|
||||
minimum: 1
|
||||
responses:
|
||||
200:
|
||||
description: A successful response.
|
||||
schema:
|
||||
$ref: "#/definitions/parityResponse"
|
||||
default:
|
||||
description: Generic error response.
|
||||
schema:
|
||||
$ref: "#/definitions/error"
|
||||
tags:
|
||||
- AdminAPI
|
||||
|
||||
definitions:
|
||||
bucketAccess:
|
||||
type: string
|
||||
@@ -1355,7 +1382,7 @@ definitions:
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
format: int64
|
||||
format: int32
|
||||
message:
|
||||
type: string
|
||||
user:
|
||||
@@ -1828,6 +1855,8 @@ definitions:
|
||||
type: string
|
||||
creation_date:
|
||||
type: string
|
||||
deletion_date:
|
||||
type: string
|
||||
currentState:
|
||||
type: string
|
||||
zones:
|
||||
@@ -1836,11 +1865,15 @@ definitions:
|
||||
$ref: "#/definitions/zone"
|
||||
image:
|
||||
type: string
|
||||
console_image:
|
||||
type: string
|
||||
namespace:
|
||||
type: string
|
||||
total_size:
|
||||
type: integer
|
||||
format: int64
|
||||
enable_prometheus:
|
||||
type: boolean
|
||||
|
||||
tenantUsage:
|
||||
type: object
|
||||
@@ -1867,6 +1900,8 @@ definitions:
|
||||
type: integer
|
||||
creation_date:
|
||||
type: string
|
||||
deletion_date:
|
||||
type: string
|
||||
currentState:
|
||||
type: string
|
||||
namespace:
|
||||
@@ -1898,7 +1933,9 @@ definitions:
|
||||
$ref: "#/definitions/imageRegistry"
|
||||
image_pull_secret:
|
||||
type: string
|
||||
|
||||
enable_prometheus:
|
||||
type: boolean
|
||||
|
||||
imageRegistry:
|
||||
type: object
|
||||
required:
|
||||
@@ -1912,7 +1949,7 @@ definitions:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
|
||||
|
||||
createTenantRequest:
|
||||
type: object
|
||||
required:
|
||||
@@ -1956,6 +1993,10 @@ definitions:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
labels:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
image_registry:
|
||||
$ref: "#/definitions/imageRegistry"
|
||||
image_pull_secret:
|
||||
@@ -1969,6 +2010,25 @@ definitions:
|
||||
encryption:
|
||||
type: object
|
||||
$ref: "#/definitions/encryptionConfiguration"
|
||||
console:
|
||||
type: object
|
||||
$ref: '#/definitions/consoleConfiguration'
|
||||
|
||||
metadataFields:
|
||||
type: object
|
||||
properties:
|
||||
annotations:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
labels:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
node_selector:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
||||
keyPairConfiguration:
|
||||
type: object
|
||||
@@ -2031,26 +2091,36 @@ definitions:
|
||||
server_insecure:
|
||||
type: boolean
|
||||
|
||||
consoleConfiguration:
|
||||
allOf:
|
||||
- $ref: '#/definitions/metadataFields'
|
||||
- type: object
|
||||
properties:
|
||||
image:
|
||||
type: string
|
||||
|
||||
encryptionConfiguration:
|
||||
type: object
|
||||
properties:
|
||||
image:
|
||||
type: string
|
||||
server:
|
||||
type: object
|
||||
$ref: "#/definitions/keyPairConfiguration"
|
||||
client:
|
||||
type: object
|
||||
$ref: "#/definitions/keyPairConfiguration"
|
||||
gemalto:
|
||||
type: object
|
||||
$ref: "#/definitions/gemaltoConfiguration"
|
||||
aws:
|
||||
type: object
|
||||
$ref: "#/definitions/awsConfiguration"
|
||||
vault:
|
||||
type: object
|
||||
$ref: "#/definitions/vaultConfiguration"
|
||||
allOf:
|
||||
- $ref: '#/definitions/metadataFields'
|
||||
- type: object
|
||||
properties:
|
||||
image:
|
||||
type: string
|
||||
server:
|
||||
type: object
|
||||
$ref: "#/definitions/keyPairConfiguration"
|
||||
client:
|
||||
type: object
|
||||
$ref: "#/definitions/keyPairConfiguration"
|
||||
gemalto:
|
||||
type: object
|
||||
$ref: "#/definitions/gemaltoConfiguration"
|
||||
aws:
|
||||
type: object
|
||||
$ref: "#/definitions/awsConfiguration"
|
||||
vault:
|
||||
type: object
|
||||
$ref: "#/definitions/vaultConfiguration"
|
||||
|
||||
vaultConfiguration:
|
||||
type: object
|
||||
@@ -2203,6 +2273,10 @@ definitions:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
annotations:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
resources:
|
||||
$ref: "#/definitions/zoneResources"
|
||||
node_selector:
|
||||
@@ -2253,14 +2327,14 @@ definitions:
|
||||
type: string
|
||||
type: object
|
||||
type: array
|
||||
|
||||
|
||||
zoneTolerationSeconds:
|
||||
description: TolerationSeconds represents the period of
|
||||
time the toleration (which must be of effect NoExecute,
|
||||
otherwise this field is ignored) tolerates the taint.
|
||||
By default, it is not set, which means tolerate the taint
|
||||
forever (do not evict). Zero and negative values will
|
||||
be treated as 0 (evict immediately) by the system.
|
||||
time the toleration (which must be of effect NoExecute,
|
||||
otherwise this field is ignored) tolerates the taint.
|
||||
By default, it is not set, which means tolerate the taint
|
||||
forever (do not evict). Zero and negative values will
|
||||
be treated as 0 (evict immediately) by the system.
|
||||
type: object
|
||||
required:
|
||||
- seconds
|
||||
@@ -2636,13 +2710,13 @@ definitions:
|
||||
used:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
|
||||
deleteTenantRequest:
|
||||
type: object
|
||||
properties:
|
||||
delete_pvcs:
|
||||
type: boolean
|
||||
|
||||
|
||||
zoneUpdateRequest:
|
||||
type: object
|
||||
required:
|
||||
@@ -2652,10 +2726,15 @@ definitions:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/zone"
|
||||
|
||||
|
||||
maxAllocatableMemResponse:
|
||||
type: object
|
||||
properties:
|
||||
max_memory:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
parityResponse:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
Reference in New Issue
Block a user