Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a02c5848b | ||
|
|
e16a926ef8 | ||
|
|
78f4978a9a | ||
|
|
42d617caf9 | ||
|
|
28eb8784a9 | ||
|
|
fcf5d5c9f7 | ||
|
|
a42f1ff4ee | ||
|
|
98f897ed5b | ||
|
|
7afd608faa | ||
|
|
8313a62f17 | ||
|
|
459e2bf61c | ||
|
|
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 | ||
|
|
c59387c2b4 | ||
|
|
c5a3eff745 | ||
|
|
624891ae1f | ||
|
|
83435e1ab9 | ||
|
|
2b4606e773 | ||
|
|
30f5943f8a | ||
|
|
412ac0a603 | ||
|
|
b2aa1349f8 | ||
|
|
8b62aec7fb | ||
|
|
83fe33b499 | ||
|
|
54d0a1d342 | ||
|
|
c59737a71d | ||
|
|
7c2ba707eb | ||
|
|
545a890c45 | ||
|
|
4b42308484 | ||
|
|
5a95fed35b | ||
|
|
f880e3976f | ||
|
|
25fa2f3275 | ||
|
|
9f005b7537 | ||
|
|
1ad6e977f2 |
6
.github/workflows/go.yml
vendored
6
.github/workflows/go.yml
vendored
@@ -3,10 +3,10 @@ name: Go
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.13.x, 1.14.x]
|
go-version: [1.14.x]
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
- name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||||
|
|||||||
@@ -76,21 +76,19 @@ func newApp(name string) *cli.App {
|
|||||||
|
|
||||||
findClosestCommands := func(command string) []string {
|
findClosestCommands := func(command string) []string {
|
||||||
var closestCommands []string
|
var closestCommands []string
|
||||||
for _, value := range commandsTree.PrefixMatch(command) {
|
closestCommands = append(closestCommands, commandsTree.PrefixMatch(command)...)
|
||||||
closestCommands = append(closestCommands, value.(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(closestCommands)
|
sort.Strings(closestCommands)
|
||||||
// Suggest other close commands - allow missed, wrongly added and
|
// Suggest other close commands - allow missed, wrongly added and
|
||||||
// even transposed characters
|
// even transposed characters
|
||||||
for _, value := range commandsTree.Walk(commandsTree.Root()) {
|
for _, value := range commandsTree.Walk(commandsTree.Root()) {
|
||||||
if sort.SearchStrings(closestCommands, value.(string)) < len(closestCommands) {
|
if sort.SearchStrings(closestCommands, value) < len(closestCommands) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// 2 is arbitrary and represents the max
|
// 2 is arbitrary and represents the max
|
||||||
// allowed number of typed errors
|
// allowed number of typed errors
|
||||||
if words.DamerauLevenshteinDistance(command, value.(string)) < 2 {
|
if words.DamerauLevenshteinDistance(command, value) < 2 {
|
||||||
closestCommands = append(closestCommands, value.(string))
|
closestCommands = append(closestCommands, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
go.mod
15
go.mod
@@ -16,16 +16,17 @@ require (
|
|||||||
github.com/jessevdk/go-flags v1.4.0
|
github.com/jessevdk/go-flags v1.4.0
|
||||||
github.com/minio/cli v1.22.0
|
github.com/minio/cli v1.22.0
|
||||||
github.com/minio/kes v0.11.0
|
github.com/minio/kes v0.11.0
|
||||||
github.com/minio/mc v0.0.0-20200808005614-7e52c104bee1
|
github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821
|
||||||
github.com/minio/minio v0.0.0-20200808024306-2a9819aff876
|
github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544
|
||||||
github.com/minio/minio-go/v7 v7.0.5-0.20200807085956-d7db33ea7618
|
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089
|
||||||
github.com/minio/operator v0.0.0-20200806194125-c2ff646f4af1
|
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c
|
||||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
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/stretchr/testify v1.6.1
|
||||||
github.com/unrolled/secure v1.0.7
|
github.com/unrolled/secure v1.0.7
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
golang.org/x/net v0.0.0-20200904194848-62affa334b73
|
||||||
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
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
k8s.io/api v0.18.6
|
k8s.io/api v0.18.6
|
||||||
k8s.io/apimachinery v0.18.6
|
k8s.io/apimachinery v0.18.6
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ rules:
|
|||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
- namespaces
|
- namespaces
|
||||||
- secrets
|
|
||||||
- pods
|
- pods
|
||||||
- services
|
- services
|
||||||
- events
|
- events
|
||||||
@@ -18,6 +17,18 @@ rules:
|
|||||||
- create
|
- create
|
||||||
- list
|
- list
|
||||||
- patch
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- watch
|
||||||
|
- create
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- deletecollection
|
||||||
|
- delete
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- "storage.k8s.io"
|
- "storage.k8s.io"
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
serviceAccountName: console-sa
|
serviceAccountName: console-sa
|
||||||
containers:
|
containers:
|
||||||
- name: console
|
- name: console
|
||||||
image: minio/console:v0.3.12
|
image: minio/console:v0.4.0
|
||||||
imagePullPolicy: "IfNotPresent"
|
imagePullPolicy: "IfNotPresent"
|
||||||
args:
|
args:
|
||||||
- server
|
- server
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ resources:
|
|||||||
- console-configmap.yaml
|
- console-configmap.yaml
|
||||||
- console-service.yaml
|
- console-service.yaml
|
||||||
- console-deployment.yaml
|
- console-deployment.yaml
|
||||||
- minio-operator.yaml
|
- https://github.com/minio/operator/?ref=v3.0.10
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@ rules:
|
|||||||
- list
|
- list
|
||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
|
- deletecollection
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
@@ -22,12 +23,21 @@ rules:
|
|||||||
- services
|
- services
|
||||||
- events
|
- events
|
||||||
- resourcequotas
|
- resourcequotas
|
||||||
|
- nodes
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
- watch
|
- watch
|
||||||
- create
|
- create
|
||||||
- list
|
- list
|
||||||
- patch
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- persistentvolumeclaims
|
||||||
|
verbs:
|
||||||
|
- deletecollection
|
||||||
|
- list
|
||||||
|
- get
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- "storage.k8s.io"
|
- "storage.k8s.io"
|
||||||
resources:
|
resources:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ spec:
|
|||||||
serviceAccountName: console-sa
|
serviceAccountName: console-sa
|
||||||
containers:
|
containers:
|
||||||
- name: console
|
- name: console
|
||||||
image: minio/console:v0.3.12
|
image: minio/console:v0.4.0
|
||||||
imagePullPolicy: "IfNotPresent"
|
imagePullPolicy: "IfNotPresent"
|
||||||
env:
|
env:
|
||||||
- name: CONSOLE_OPERATOR_MODE
|
- name: CONSOLE_OPERATOR_MODE
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ resources:
|
|||||||
- console-configmap.yaml
|
- console-configmap.yaml
|
||||||
- console-service.yaml
|
- console-service.yaml
|
||||||
- console-deployment.yaml
|
- console-deployment.yaml
|
||||||
- minio-operator.yaml
|
- https://github.com/minio/operator/?ref=v3.0.19
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
63
models/add_bucket_replication.go
Normal file
63
models/add_bucket_replication.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddBucketReplication add bucket replication
|
||||||
|
//
|
||||||
|
// swagger:model addBucketReplication
|
||||||
|
type AddBucketReplication struct {
|
||||||
|
|
||||||
|
// arn
|
||||||
|
Arn string `json:"arn,omitempty"`
|
||||||
|
|
||||||
|
// destination bucket
|
||||||
|
DestinationBucket string `json:"destination_bucket,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this add bucket replication
|
||||||
|
func (m *AddBucketReplication) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *AddBucketReplication) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *AddBucketReplication) UnmarshalBinary(b []byte) error {
|
||||||
|
var res AddBucketReplication
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
69
models/bucket_object.go
Normal file
69
models/bucket_object.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketObject bucket object
|
||||||
|
//
|
||||||
|
// swagger:model bucketObject
|
||||||
|
type BucketObject struct {
|
||||||
|
|
||||||
|
// content type
|
||||||
|
ContentType string `json:"content_type,omitempty"`
|
||||||
|
|
||||||
|
// last modified
|
||||||
|
LastModified string `json:"last_modified,omitempty"`
|
||||||
|
|
||||||
|
// name
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// size
|
||||||
|
Size int64 `json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this bucket object
|
||||||
|
func (m *BucketObject) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *BucketObject) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *BucketObject) UnmarshalBinary(b []byte) error {
|
||||||
|
var res BucketObject
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
60
models/bucket_replication_destination.go
Normal file
60
models/bucket_replication_destination.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketReplicationDestination bucket replication destination
|
||||||
|
//
|
||||||
|
// swagger:model bucketReplicationDestination
|
||||||
|
type BucketReplicationDestination struct {
|
||||||
|
|
||||||
|
// bucket
|
||||||
|
Bucket string `json:"bucket,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this bucket replication destination
|
||||||
|
func (m *BucketReplicationDestination) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *BucketReplicationDestination) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *BucketReplicationDestination) UnmarshalBinary(b []byte) error {
|
||||||
|
var res BucketReplicationDestination
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
97
models/bucket_replication_response.go
Normal file
97
models/bucket_replication_response.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// 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 (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketReplicationResponse bucket replication response
|
||||||
|
//
|
||||||
|
// swagger:model bucketReplicationResponse
|
||||||
|
type BucketReplicationResponse struct {
|
||||||
|
|
||||||
|
// rules
|
||||||
|
Rules []*BucketReplicationRule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this bucket replication response
|
||||||
|
func (m *BucketReplicationResponse) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateRules(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *BucketReplicationResponse) validateRules(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.Rules) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(m.Rules); i++ {
|
||||||
|
if swag.IsZero(m.Rules[i]) { // not required
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Rules[i] != nil {
|
||||||
|
if err := m.Rules[i].Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("rules" + "." + strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *BucketReplicationResponse) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *BucketReplicationResponse) UnmarshalBinary(b []byte) error {
|
||||||
|
var res BucketReplicationResponse
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
173
models/bucket_replication_rule.go
Normal file
173
models/bucket_replication_rule.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// 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 (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketReplicationRule bucket replication rule
|
||||||
|
//
|
||||||
|
// swagger:model bucketReplicationRule
|
||||||
|
type BucketReplicationRule struct {
|
||||||
|
|
||||||
|
// delete marker replication
|
||||||
|
DeleteMarkerReplication *BucketReplicationRuleMarker `json:"delete_marker_replication,omitempty"`
|
||||||
|
|
||||||
|
// destination
|
||||||
|
Destination *BucketReplicationDestination `json:"destination,omitempty"`
|
||||||
|
|
||||||
|
// id
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
|
||||||
|
// priority
|
||||||
|
Priority int32 `json:"priority,omitempty"`
|
||||||
|
|
||||||
|
// status
|
||||||
|
// Enum: [Enabled Disabled]
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this bucket replication rule
|
||||||
|
func (m *BucketReplicationRule) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateDeleteMarkerReplication(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateDestination(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateStatus(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *BucketReplicationRule) validateDeleteMarkerReplication(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.DeleteMarkerReplication) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.DeleteMarkerReplication != nil {
|
||||||
|
if err := m.DeleteMarkerReplication.Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("delete_marker_replication")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *BucketReplicationRule) validateDestination(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.Destination) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Destination != nil {
|
||||||
|
if err := m.Destination.Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("destination")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var bucketReplicationRuleTypeStatusPropEnum []interface{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var res []string
|
||||||
|
if err := json.Unmarshal([]byte(`["Enabled","Disabled"]`), &res); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range res {
|
||||||
|
bucketReplicationRuleTypeStatusPropEnum = append(bucketReplicationRuleTypeStatusPropEnum, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// BucketReplicationRuleStatusEnabled captures enum value "Enabled"
|
||||||
|
BucketReplicationRuleStatusEnabled string = "Enabled"
|
||||||
|
|
||||||
|
// BucketReplicationRuleStatusDisabled captures enum value "Disabled"
|
||||||
|
BucketReplicationRuleStatusDisabled string = "Disabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// prop value enum
|
||||||
|
func (m *BucketReplicationRule) validateStatusEnum(path, location string, value string) error {
|
||||||
|
if err := validate.EnumCase(path, location, value, bucketReplicationRuleTypeStatusPropEnum, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *BucketReplicationRule) validateStatus(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.Status) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// value enum
|
||||||
|
if err := m.validateStatusEnum("status", "body", m.Status); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *BucketReplicationRule) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *BucketReplicationRule) UnmarshalBinary(b []byte) error {
|
||||||
|
var res BucketReplicationRule
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
117
models/bucket_replication_rule_marker.go
Normal file
117
models/bucket_replication_rule_marker.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 (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketReplicationRuleMarker bucket replication rule marker
|
||||||
|
//
|
||||||
|
// swagger:model bucketReplicationRuleMarker
|
||||||
|
type BucketReplicationRuleMarker struct {
|
||||||
|
|
||||||
|
// status
|
||||||
|
// Enum: [Enabled Disabled]
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this bucket replication rule marker
|
||||||
|
func (m *BucketReplicationRuleMarker) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateStatus(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var bucketReplicationRuleMarkerTypeStatusPropEnum []interface{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var res []string
|
||||||
|
if err := json.Unmarshal([]byte(`["Enabled","Disabled"]`), &res); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range res {
|
||||||
|
bucketReplicationRuleMarkerTypeStatusPropEnum = append(bucketReplicationRuleMarkerTypeStatusPropEnum, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// BucketReplicationRuleMarkerStatusEnabled captures enum value "Enabled"
|
||||||
|
BucketReplicationRuleMarkerStatusEnabled string = "Enabled"
|
||||||
|
|
||||||
|
// BucketReplicationRuleMarkerStatusDisabled captures enum value "Disabled"
|
||||||
|
BucketReplicationRuleMarkerStatusDisabled string = "Disabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
// prop value enum
|
||||||
|
func (m *BucketReplicationRuleMarker) validateStatusEnum(path, location string, value string) error {
|
||||||
|
if err := validate.EnumCase(path, location, value, bucketReplicationRuleMarkerTypeStatusPropEnum, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *BucketReplicationRuleMarker) validateStatus(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.Status) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// value enum
|
||||||
|
if err := m.validateStatusEnum("status", "body", m.Status); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *BucketReplicationRuleMarker) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *BucketReplicationRuleMarker) UnmarshalBinary(b []byte) error {
|
||||||
|
var res BucketReplicationRuleMarker
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
60
models/bucket_versioning_response.go
Normal file
60
models/bucket_versioning_response.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BucketVersioningResponse bucket versioning response
|
||||||
|
//
|
||||||
|
// swagger:model bucketVersioningResponse
|
||||||
|
type BucketVersioningResponse struct {
|
||||||
|
|
||||||
|
// is versioned
|
||||||
|
IsVersioned bool `json:"is_versioned,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this bucket versioning response
|
||||||
|
func (m *BucketVersioningResponse) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *BucketVersioningResponse) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *BucketVersioningResponse) UnmarshalBinary(b []byte) error {
|
||||||
|
var res BucketVersioningResponse
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
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
|
||||||
|
}
|
||||||
162
models/create_remote_bucket.go
Normal file
162
models/create_remote_bucket.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
// 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"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateRemoteBucket create remote bucket
|
||||||
|
//
|
||||||
|
// swagger:model createRemoteBucket
|
||||||
|
type CreateRemoteBucket struct {
|
||||||
|
|
||||||
|
// access key
|
||||||
|
// Required: true
|
||||||
|
// Min Length: 3
|
||||||
|
AccessKey *string `json:"accessKey"`
|
||||||
|
|
||||||
|
// region
|
||||||
|
Region string `json:"region,omitempty"`
|
||||||
|
|
||||||
|
// secret key
|
||||||
|
// Required: true
|
||||||
|
// Min Length: 8
|
||||||
|
SecretKey *string `json:"secretKey"`
|
||||||
|
|
||||||
|
// source bucket
|
||||||
|
// Required: true
|
||||||
|
SourceBucket *string `json:"sourceBucket"`
|
||||||
|
|
||||||
|
// target bucket
|
||||||
|
// Required: true
|
||||||
|
TargetBucket *string `json:"targetBucket"`
|
||||||
|
|
||||||
|
// target URL
|
||||||
|
// Required: true
|
||||||
|
TargetURL *string `json:"targetURL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this create remote bucket
|
||||||
|
func (m *CreateRemoteBucket) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateAccessKey(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateSecretKey(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateSourceBucket(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateTargetBucket(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateTargetURL(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CreateRemoteBucket) validateAccessKey(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("accessKey", "body", m.AccessKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validate.MinLength("accessKey", "body", string(*m.AccessKey), 3); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CreateRemoteBucket) validateSecretKey(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("secretKey", "body", m.SecretKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validate.MinLength("secretKey", "body", string(*m.SecretKey), 8); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CreateRemoteBucket) validateSourceBucket(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("sourceBucket", "body", m.SourceBucket); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CreateRemoteBucket) validateTargetBucket(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("targetBucket", "body", m.TargetBucket); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CreateRemoteBucket) validateTargetURL(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("targetURL", "body", m.TargetURL); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *CreateRemoteBucket) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *CreateRemoteBucket) UnmarshalBinary(b []byte) error {
|
||||||
|
var res CreateRemoteBucket
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -42,12 +42,18 @@ type CreateTenantRequest struct {
|
|||||||
// annotations
|
// annotations
|
||||||
Annotations map[string]string `json:"annotations,omitempty"`
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
|
||||||
|
// console
|
||||||
|
Console *ConsoleConfiguration `json:"console,omitempty"`
|
||||||
|
|
||||||
// console image
|
// console image
|
||||||
ConsoleImage string `json:"console_image,omitempty"`
|
ConsoleImage string `json:"console_image,omitempty"`
|
||||||
|
|
||||||
// enable console
|
// enable console
|
||||||
EnableConsole *bool `json:"enable_console,omitempty"`
|
EnableConsole *bool `json:"enable_console,omitempty"`
|
||||||
|
|
||||||
|
// enable prometheus
|
||||||
|
EnablePrometheus *bool `json:"enable_prometheus,omitempty"`
|
||||||
|
|
||||||
// enable tls
|
// enable tls
|
||||||
EnableTLS *bool `json:"enable_tls,omitempty"`
|
EnableTLS *bool `json:"enable_tls,omitempty"`
|
||||||
|
|
||||||
@@ -69,6 +75,9 @@ type CreateTenantRequest struct {
|
|||||||
// image registry
|
// image registry
|
||||||
ImageRegistry *ImageRegistry `json:"image_registry,omitempty"`
|
ImageRegistry *ImageRegistry `json:"image_registry,omitempty"`
|
||||||
|
|
||||||
|
// labels
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
|
||||||
// mounth path
|
// mounth path
|
||||||
MounthPath string `json:"mounth_path,omitempty"`
|
MounthPath string `json:"mounth_path,omitempty"`
|
||||||
|
|
||||||
@@ -99,6 +108,10 @@ type CreateTenantRequest struct {
|
|||||||
func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
|
func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
|
||||||
var res []error
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateConsole(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := m.validateEncryption(formats); err != nil {
|
if err := m.validateEncryption(formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
@@ -133,6 +146,24 @@ func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
|
|||||||
return nil
|
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 {
|
func (m *CreateTenantRequest) validateEncryption(formats strfmt.Registry) error {
|
||||||
|
|
||||||
if swag.IsZero(m.Encryption) { // not required
|
if swag.IsZero(m.Encryption) { // not required
|
||||||
|
|||||||
60
models/delete_tenant_request.go
Normal file
60
models/delete_tenant_request.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeleteTenantRequest delete tenant request
|
||||||
|
//
|
||||||
|
// swagger:model deleteTenantRequest
|
||||||
|
type DeleteTenantRequest struct {
|
||||||
|
|
||||||
|
// delete pvcs
|
||||||
|
DeletePvcs bool `json:"delete_pvcs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this delete tenant request
|
||||||
|
func (m *DeleteTenantRequest) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *DeleteTenantRequest) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *DeleteTenantRequest) UnmarshalBinary(b []byte) error {
|
||||||
|
var res DeleteTenantRequest
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import (
|
|||||||
//
|
//
|
||||||
// swagger:model encryptionConfiguration
|
// swagger:model encryptionConfiguration
|
||||||
type EncryptionConfiguration struct {
|
type EncryptionConfiguration struct {
|
||||||
|
MetadataFields
|
||||||
|
|
||||||
// aws
|
// aws
|
||||||
Aws *AwsConfiguration `json:"aws,omitempty"`
|
Aws *AwsConfiguration `json:"aws,omitempty"`
|
||||||
@@ -52,10 +53,100 @@ type EncryptionConfiguration struct {
|
|||||||
Vault *VaultConfiguration `json:"vault,omitempty"`
|
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
|
// Validate validates this encryption configuration
|
||||||
func (m *EncryptionConfiguration) Validate(formats strfmt.Registry) error {
|
func (m *EncryptionConfiguration) Validate(formats strfmt.Registry) error {
|
||||||
var res []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 {
|
if err := m.validateAws(formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import (
|
|||||||
type Error struct {
|
type Error struct {
|
||||||
|
|
||||||
// code
|
// code
|
||||||
Code int64 `json:"code,omitempty"`
|
Code int32 `json:"code,omitempty"`
|
||||||
|
|
||||||
// message
|
// message
|
||||||
// Required: true
|
// Required: true
|
||||||
|
|||||||
100
models/list_objects_response.go
Normal file
100
models/list_objects_response.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// 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 (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListObjectsResponse list objects response
|
||||||
|
//
|
||||||
|
// swagger:model listObjectsResponse
|
||||||
|
type ListObjectsResponse struct {
|
||||||
|
|
||||||
|
// list of resulting objects
|
||||||
|
Objects []*BucketObject `json:"objects"`
|
||||||
|
|
||||||
|
// number of objects
|
||||||
|
Total int64 `json:"total,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this list objects response
|
||||||
|
func (m *ListObjectsResponse) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateObjects(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ListObjectsResponse) validateObjects(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.Objects) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(m.Objects); i++ {
|
||||||
|
if swag.IsZero(m.Objects[i]) { // not required
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Objects[i] != nil {
|
||||||
|
if err := m.Objects[i].Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("objects" + "." + strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *ListObjectsResponse) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *ListObjectsResponse) UnmarshalBinary(b []byte) error {
|
||||||
|
var res ListObjectsResponse
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
100
models/list_remote_buckets_response.go
Normal file
100
models/list_remote_buckets_response.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// 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 (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ListRemoteBucketsResponse list remote buckets response
|
||||||
|
//
|
||||||
|
// swagger:model listRemoteBucketsResponse
|
||||||
|
type ListRemoteBucketsResponse struct {
|
||||||
|
|
||||||
|
// list of remote buckets
|
||||||
|
Buckets []*RemoteBucket `json:"buckets"`
|
||||||
|
|
||||||
|
// number of remote buckets accessible to user
|
||||||
|
Total int64 `json:"total,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this list remote buckets response
|
||||||
|
func (m *ListRemoteBucketsResponse) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateBuckets(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ListRemoteBucketsResponse) validateBuckets(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.Buckets) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(m.Buckets); i++ {
|
||||||
|
if swag.IsZero(m.Buckets[i]) { // not required
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Buckets[i] != nil {
|
||||||
|
if err := m.Buckets[i].Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("buckets" + "." + strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *ListRemoteBucketsResponse) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *ListRemoteBucketsResponse) UnmarshalBinary(b []byte) error {
|
||||||
|
var res ListRemoteBucketsResponse
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -37,6 +37,12 @@ type MakeBucketRequest struct {
|
|||||||
// name
|
// name
|
||||||
// Required: true
|
// Required: true
|
||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
|
|
||||||
|
// quota
|
||||||
|
Quota *SetBucketQuota `json:"quota,omitempty"`
|
||||||
|
|
||||||
|
// versioning
|
||||||
|
Versioning bool `json:"versioning,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates this make bucket request
|
// Validate validates this make bucket request
|
||||||
@@ -47,6 +53,10 @@ func (m *MakeBucketRequest) Validate(formats strfmt.Registry) error {
|
|||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := m.validateQuota(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
if len(res) > 0 {
|
if len(res) > 0 {
|
||||||
return errors.CompositeValidationError(res...)
|
return errors.CompositeValidationError(res...)
|
||||||
}
|
}
|
||||||
@@ -62,6 +72,24 @@ func (m *MakeBucketRequest) validateName(formats strfmt.Registry) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MakeBucketRequest) validateQuota(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.Quota) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Quota != nil {
|
||||||
|
if err := m.Quota.Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("quota")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalBinary interface implementation
|
// MarshalBinary interface implementation
|
||||||
func (m *MakeBucketRequest) MarshalBinary() ([]byte, error) {
|
func (m *MakeBucketRequest) MarshalBinary() ([]byte, error) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
|
|||||||
60
models/max_allocatable_mem_response.go
Normal file
60
models/max_allocatable_mem_response.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxAllocatableMemResponse max allocatable mem response
|
||||||
|
//
|
||||||
|
// swagger:model maxAllocatableMemResponse
|
||||||
|
type MaxAllocatableMemResponse struct {
|
||||||
|
|
||||||
|
// max memory
|
||||||
|
MaxMemory int64 `json:"max_memory,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this max allocatable mem response
|
||||||
|
func (m *MaxAllocatableMemResponse) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *MaxAllocatableMemResponse) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *MaxAllocatableMemResponse) UnmarshalBinary(b []byte) error {
|
||||||
|
var res MaxAllocatableMemResponse
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
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
|
||||||
|
}
|
||||||
200
models/remote_bucket.go
Normal file
200
models/remote_bucket.go
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
// 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 (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RemoteBucket remote bucket
|
||||||
|
//
|
||||||
|
// swagger:model remoteBucket
|
||||||
|
type RemoteBucket struct {
|
||||||
|
|
||||||
|
// access key
|
||||||
|
// Required: true
|
||||||
|
// Min Length: 3
|
||||||
|
AccessKey *string `json:"accessKey"`
|
||||||
|
|
||||||
|
// remote a r n
|
||||||
|
// Required: true
|
||||||
|
RemoteARN *string `json:"remoteARN"`
|
||||||
|
|
||||||
|
// secret key
|
||||||
|
// Min Length: 8
|
||||||
|
SecretKey string `json:"secretKey,omitempty"`
|
||||||
|
|
||||||
|
// service
|
||||||
|
// Enum: [replication]
|
||||||
|
Service string `json:"service,omitempty"`
|
||||||
|
|
||||||
|
// source bucket
|
||||||
|
// Required: true
|
||||||
|
SourceBucket *string `json:"sourceBucket"`
|
||||||
|
|
||||||
|
// status
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
|
||||||
|
// target bucket
|
||||||
|
TargetBucket string `json:"targetBucket,omitempty"`
|
||||||
|
|
||||||
|
// target URL
|
||||||
|
TargetURL string `json:"targetURL,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this remote bucket
|
||||||
|
func (m *RemoteBucket) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateAccessKey(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateRemoteARN(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateSecretKey(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateService(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateSourceBucket(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RemoteBucket) validateAccessKey(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("accessKey", "body", m.AccessKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validate.MinLength("accessKey", "body", string(*m.AccessKey), 3); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RemoteBucket) validateRemoteARN(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("remoteARN", "body", m.RemoteARN); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RemoteBucket) validateSecretKey(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.SecretKey) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := validate.MinLength("secretKey", "body", string(m.SecretKey), 8); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var remoteBucketTypeServicePropEnum []interface{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var res []string
|
||||||
|
if err := json.Unmarshal([]byte(`["replication"]`), &res); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range res {
|
||||||
|
remoteBucketTypeServicePropEnum = append(remoteBucketTypeServicePropEnum, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// RemoteBucketServiceReplication captures enum value "replication"
|
||||||
|
RemoteBucketServiceReplication string = "replication"
|
||||||
|
)
|
||||||
|
|
||||||
|
// prop value enum
|
||||||
|
func (m *RemoteBucket) validateServiceEnum(path, location string, value string) error {
|
||||||
|
if err := validate.EnumCase(path, location, value, remoteBucketTypeServicePropEnum, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RemoteBucket) validateService(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.Service) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// value enum
|
||||||
|
if err := m.validateServiceEnum("service", "body", m.Service); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RemoteBucket) validateSourceBucket(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("sourceBucket", "body", m.SourceBucket); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *RemoteBucket) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *RemoteBucket) UnmarshalBinary(b []byte) error {
|
||||||
|
var res RemoteBucket
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
137
models/set_bucket_quota.go
Normal file
137
models/set_bucket_quota.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
// 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 (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetBucketQuota set bucket quota
|
||||||
|
//
|
||||||
|
// swagger:model setBucketQuota
|
||||||
|
type SetBucketQuota struct {
|
||||||
|
|
||||||
|
// amount
|
||||||
|
Amount int64 `json:"amount,omitempty"`
|
||||||
|
|
||||||
|
// enabled
|
||||||
|
// Required: true
|
||||||
|
Enabled *bool `json:"enabled"`
|
||||||
|
|
||||||
|
// quota type
|
||||||
|
// Enum: [fifo hard]
|
||||||
|
QuotaType string `json:"quota_type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this set bucket quota
|
||||||
|
func (m *SetBucketQuota) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateEnabled(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.validateQuotaType(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SetBucketQuota) validateEnabled(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("enabled", "body", m.Enabled); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var setBucketQuotaTypeQuotaTypePropEnum []interface{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var res []string
|
||||||
|
if err := json.Unmarshal([]byte(`["fifo","hard"]`), &res); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range res {
|
||||||
|
setBucketQuotaTypeQuotaTypePropEnum = append(setBucketQuotaTypeQuotaTypePropEnum, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
// SetBucketQuotaQuotaTypeFifo captures enum value "fifo"
|
||||||
|
SetBucketQuotaQuotaTypeFifo string = "fifo"
|
||||||
|
|
||||||
|
// SetBucketQuotaQuotaTypeHard captures enum value "hard"
|
||||||
|
SetBucketQuotaQuotaTypeHard string = "hard"
|
||||||
|
)
|
||||||
|
|
||||||
|
// prop value enum
|
||||||
|
func (m *SetBucketQuota) validateQuotaTypeEnum(path, location string, value string) error {
|
||||||
|
if err := validate.EnumCase(path, location, value, setBucketQuotaTypeQuotaTypePropEnum, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *SetBucketQuota) validateQuotaType(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if swag.IsZero(m.QuotaType) { // not required
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// value enum
|
||||||
|
if err := m.validateQuotaTypeEnum("quota_type", "body", m.QuotaType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *SetBucketQuota) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *SetBucketQuota) UnmarshalBinary(b []byte) error {
|
||||||
|
var res SetBucketQuota
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
60
models/set_bucket_versioning.go
Normal file
60
models/set_bucket_versioning.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetBucketVersioning set bucket versioning
|
||||||
|
//
|
||||||
|
// swagger:model setBucketVersioning
|
||||||
|
type SetBucketVersioning struct {
|
||||||
|
|
||||||
|
// versioning
|
||||||
|
Versioning bool `json:"versioning,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this set bucket versioning
|
||||||
|
func (m *SetBucketVersioning) Validate(formats strfmt.Registry) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *SetBucketVersioning) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *SetBucketVersioning) UnmarshalBinary(b []byte) error {
|
||||||
|
var res SetBucketVersioning
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -35,12 +35,21 @@ import (
|
|||||||
// swagger:model tenant
|
// swagger:model tenant
|
||||||
type Tenant struct {
|
type Tenant struct {
|
||||||
|
|
||||||
|
// console image
|
||||||
|
ConsoleImage string `json:"console_image,omitempty"`
|
||||||
|
|
||||||
// creation date
|
// creation date
|
||||||
CreationDate string `json:"creation_date,omitempty"`
|
CreationDate string `json:"creation_date,omitempty"`
|
||||||
|
|
||||||
// current state
|
// current state
|
||||||
CurrentState string `json:"currentState,omitempty"`
|
CurrentState string `json:"currentState,omitempty"`
|
||||||
|
|
||||||
|
// deletion date
|
||||||
|
DeletionDate string `json:"deletion_date,omitempty"`
|
||||||
|
|
||||||
|
// enable prometheus
|
||||||
|
EnablePrometheus bool `json:"enable_prometheus,omitempty"`
|
||||||
|
|
||||||
// image
|
// image
|
||||||
Image string `json:"image,omitempty"`
|
Image string `json:"image,omitempty"`
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ type TenantList struct {
|
|||||||
// current state
|
// current state
|
||||||
CurrentState string `json:"currentState,omitempty"`
|
CurrentState string `json:"currentState,omitempty"`
|
||||||
|
|
||||||
|
// deletion date
|
||||||
|
DeletionDate string `json:"deletion_date,omitempty"`
|
||||||
|
|
||||||
// instance count
|
// instance count
|
||||||
InstanceCount int64 `json:"instance_count,omitempty"`
|
InstanceCount int64 `json:"instance_count,omitempty"`
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ package models
|
|||||||
// Editing this file might prove futile when you re-run the swagger generate command
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/go-openapi/errors"
|
"github.com/go-openapi/errors"
|
||||||
"github.com/go-openapi/strfmt"
|
"github.com/go-openapi/strfmt"
|
||||||
"github.com/go-openapi/swag"
|
"github.com/go-openapi/swag"
|
||||||
@@ -37,7 +39,7 @@ type TLSConfiguration struct {
|
|||||||
Console *KeyPairConfiguration `json:"console,omitempty"`
|
Console *KeyPairConfiguration `json:"console,omitempty"`
|
||||||
|
|
||||||
// minio
|
// minio
|
||||||
Minio *KeyPairConfiguration `json:"minio,omitempty"`
|
Minio []*KeyPairConfiguration `json:"minio"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates this tls configuration
|
// Validate validates this tls configuration
|
||||||
@@ -82,13 +84,20 @@ func (m *TLSConfiguration) validateMinio(formats strfmt.Registry) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Minio != nil {
|
for i := 0; i < len(m.Minio); i++ {
|
||||||
if err := m.Minio.Validate(formats); err != nil {
|
if swag.IsZero(m.Minio[i]) { // not required
|
||||||
if ve, ok := err.(*errors.Validation); ok {
|
continue
|
||||||
return ve.ValidateName("minio")
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.Minio[i] != nil {
|
||||||
|
if err := m.Minio[i].Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("minio" + "." + strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ type UpdateTenantRequest struct {
|
|||||||
// Pattern: ^((.*?)/(.*?):(.+))$
|
// Pattern: ^((.*?)/(.*?):(.+))$
|
||||||
ConsoleImage string `json:"console_image,omitempty"`
|
ConsoleImage string `json:"console_image,omitempty"`
|
||||||
|
|
||||||
|
// enable prometheus
|
||||||
|
EnablePrometheus bool `json:"enable_prometheus,omitempty"`
|
||||||
|
|
||||||
// image
|
// image
|
||||||
// Pattern: ^((.*?)/(.*?):(.+))$
|
// Pattern: ^((.*?)/(.*?):(.+))$
|
||||||
Image string `json:"image,omitempty"`
|
Image string `json:"image,omitempty"`
|
||||||
|
|||||||
@@ -207,6 +207,12 @@ func (m *Zone) UnmarshalBinary(b []byte) error {
|
|||||||
// swagger:model ZoneVolumeConfiguration
|
// swagger:model ZoneVolumeConfiguration
|
||||||
type ZoneVolumeConfiguration struct {
|
type ZoneVolumeConfiguration struct {
|
||||||
|
|
||||||
|
// annotations
|
||||||
|
Annotations map[string]string `json:"annotations,omitempty"`
|
||||||
|
|
||||||
|
// labels
|
||||||
|
Labels map[string]string `json:"labels,omitempty"`
|
||||||
|
|
||||||
// size
|
// size
|
||||||
// Required: true
|
// Required: true
|
||||||
Size *int64 `json:"size"`
|
Size *int64 `json:"size"`
|
||||||
|
|||||||
99
models/zone_update_request.go
Normal file
99
models/zone_update_request.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// 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 (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-openapi/errors"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/go-openapi/swag"
|
||||||
|
"github.com/go-openapi/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ZoneUpdateRequest zone update request
|
||||||
|
//
|
||||||
|
// swagger:model zoneUpdateRequest
|
||||||
|
type ZoneUpdateRequest struct {
|
||||||
|
|
||||||
|
// zones
|
||||||
|
// Required: true
|
||||||
|
Zones []*Zone `json:"zones"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates this zone update request
|
||||||
|
func (m *ZoneUpdateRequest) Validate(formats strfmt.Registry) error {
|
||||||
|
var res []error
|
||||||
|
|
||||||
|
if err := m.validateZones(formats); err != nil {
|
||||||
|
res = append(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res) > 0 {
|
||||||
|
return errors.CompositeValidationError(res...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ZoneUpdateRequest) validateZones(formats strfmt.Registry) error {
|
||||||
|
|
||||||
|
if err := validate.Required("zones", "body", m.Zones); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(m.Zones); i++ {
|
||||||
|
if swag.IsZero(m.Zones[i]) { // not required
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Zones[i] != nil {
|
||||||
|
if err := m.Zones[i].Validate(formats); err != nil {
|
||||||
|
if ve, ok := err.(*errors.Validation); ok {
|
||||||
|
return ve.ValidateName("zones" + "." + strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary interface implementation
|
||||||
|
func (m *ZoneUpdateRequest) MarshalBinary() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return swag.WriteJSON(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary interface implementation
|
||||||
|
func (m *ZoneUpdateRequest) UnmarshalBinary(b []byte) error {
|
||||||
|
var res ZoneUpdateRequest
|
||||||
|
if err := swag.ReadJSON(b, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*m = res
|
||||||
|
return nil
|
||||||
|
}
|
||||||
3
package-lock.json
generated
3
package-lock.json
generated
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"lockfileVersion": 1
|
|
||||||
}
|
|
||||||
@@ -22,22 +22,26 @@ import (
|
|||||||
|
|
||||||
// endpoints definition
|
// endpoints definition
|
||||||
var (
|
var (
|
||||||
configuration = "/configurations-list"
|
configuration = "/configurations-list"
|
||||||
users = "/users"
|
users = "/users"
|
||||||
groups = "/groups"
|
groups = "/groups"
|
||||||
iamPolicies = "/policies"
|
iamPolicies = "/policies"
|
||||||
dashboard = "/dashboard"
|
dashboard = "/dashboard"
|
||||||
profiling = "/profiling"
|
profiling = "/profiling"
|
||||||
trace = "/trace"
|
trace = "/trace"
|
||||||
logs = "/logs"
|
logs = "/logs"
|
||||||
watch = "/watch"
|
watch = "/watch"
|
||||||
notifications = "/notification-endpoints"
|
notifications = "/notification-endpoints"
|
||||||
buckets = "/buckets"
|
buckets = "/buckets"
|
||||||
bucketsDetail = "/buckets/:bucketName"
|
bucketsDetail = "/buckets/:bucketName"
|
||||||
serviceAccounts = "/service-accounts"
|
serviceAccounts = "/service-accounts"
|
||||||
tenants = "/tenants"
|
tenants = "/tenants"
|
||||||
tenantsDetail = "/tenants/:tenantName"
|
tenantsDetail = "/namespaces/:tenantNamespace/tenants/:tenantName"
|
||||||
heal = "/heal"
|
heal = "/heal"
|
||||||
|
remoteBuckets = "/remote-buckets"
|
||||||
|
replication = "/replication"
|
||||||
|
objectBrowser = "/object-browser/:bucket?"
|
||||||
|
mainObjectBrowser = "/object-browser"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConfigurationActionSet struct {
|
type ConfigurationActionSet struct {
|
||||||
@@ -208,22 +212,50 @@ var healActionSet = ConfigurationActionSet{
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var remoteBucketsActionSet = ConfigurationActionSet{
|
||||||
|
actionTypes: iampolicy.NewActionSet(
|
||||||
|
iampolicy.AllAdminActions,
|
||||||
|
),
|
||||||
|
actions: iampolicy.NewActionSet(
|
||||||
|
iampolicy.ConfigUpdateAdminAction,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
var replicationActionSet = ConfigurationActionSet{
|
||||||
|
actionTypes: iampolicy.NewActionSet(
|
||||||
|
iampolicy.AllAdminActions,
|
||||||
|
),
|
||||||
|
actions: iampolicy.NewActionSet(
|
||||||
|
iampolicy.ConfigUpdateAdminAction,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// objectBrowserActionSet no actions needed for this module to work
|
||||||
|
var objectBrowserActionSet = ConfigurationActionSet{
|
||||||
|
actionTypes: iampolicy.NewActionSet(),
|
||||||
|
actions: iampolicy.NewActionSet(),
|
||||||
|
}
|
||||||
|
|
||||||
// endpointRules contains the mapping between endpoints and ActionSets, additional rules can be added here
|
// endpointRules contains the mapping between endpoints and ActionSets, additional rules can be added here
|
||||||
var endpointRules = map[string]ConfigurationActionSet{
|
var endpointRules = map[string]ConfigurationActionSet{
|
||||||
configuration: configurationActionSet,
|
configuration: configurationActionSet,
|
||||||
users: usersActionSet,
|
users: usersActionSet,
|
||||||
groups: groupsActionSet,
|
groups: groupsActionSet,
|
||||||
iamPolicies: iamPoliciesActionSet,
|
iamPolicies: iamPoliciesActionSet,
|
||||||
dashboard: dashboardActionSet,
|
dashboard: dashboardActionSet,
|
||||||
profiling: profilingActionSet,
|
profiling: profilingActionSet,
|
||||||
trace: traceActionSet,
|
trace: traceActionSet,
|
||||||
logs: logsActionSet,
|
logs: logsActionSet,
|
||||||
watch: watchActionSet,
|
watch: watchActionSet,
|
||||||
notifications: notificationsActionSet,
|
notifications: notificationsActionSet,
|
||||||
buckets: bucketsActionSet,
|
buckets: bucketsActionSet,
|
||||||
bucketsDetail: bucketsActionSet,
|
bucketsDetail: bucketsActionSet,
|
||||||
serviceAccounts: serviceAccountsActionSet,
|
serviceAccounts: serviceAccountsActionSet,
|
||||||
heal: healActionSet,
|
heal: healActionSet,
|
||||||
|
remoteBuckets: remoteBucketsActionSet,
|
||||||
|
replication: replicationActionSet,
|
||||||
|
objectBrowser: objectBrowserActionSet,
|
||||||
|
mainObjectBrowser: objectBrowserActionSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
// operatorRules contains the mapping between endpoints and ActionSets for operator only mode
|
// operatorRules contains the mapping between endpoints and ActionSets for operator only mode
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
args: args{
|
args: args{
|
||||||
[]string{"admin:ServerInfo"},
|
[]string{"admin:ServerInfo"},
|
||||||
},
|
},
|
||||||
want: 2,
|
want: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "policies endpoint",
|
name: "policies endpoint",
|
||||||
@@ -63,7 +63,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
"admin:ListUserPolicies",
|
"admin:ListUserPolicies",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: 2,
|
want: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all admin endpoints",
|
name: "all admin endpoints",
|
||||||
@@ -72,7 +72,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
"admin:*",
|
"admin:*",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: 11,
|
want: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all s3 endpoints",
|
name: "all s3 endpoints",
|
||||||
@@ -81,7 +81,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
"s3:*",
|
"s3:*",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: 4,
|
want: 6,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "all admin and s3 endpoints",
|
name: "all admin and s3 endpoints",
|
||||||
@@ -91,7 +91,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
|
|||||||
"s3:*",
|
"s3:*",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: 14,
|
want: 18,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no endpoints",
|
name: "no endpoints",
|
||||||
|
|||||||
@@ -17,14 +17,17 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/hmac"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -33,6 +36,9 @@ import (
|
|||||||
"github.com/minio/console/models"
|
"github.com/minio/console/models"
|
||||||
"github.com/minio/console/pkg/auth/token"
|
"github.com/minio/console/pkg/auth/token"
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
|
"github.com/secure-io/sio-go/sioutil"
|
||||||
|
"golang.org/x/crypto/chacha20"
|
||||||
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,6 +46,7 @@ var (
|
|||||||
errNoAuthToken = errors.New("session token missing")
|
errNoAuthToken = errors.New("session token missing")
|
||||||
errReadingToken = errors.New("session token internal data is malformed")
|
errReadingToken = errors.New("session token internal data is malformed")
|
||||||
errClaimsFormat = errors.New("encrypted session token claims not in the right format")
|
errClaimsFormat = errors.New("encrypted session token claims not in the right format")
|
||||||
|
errorGeneric = errors.New("an error has occurred")
|
||||||
)
|
)
|
||||||
|
|
||||||
// derivedKey is the key used to encrypt the session token claims, its derived using pbkdf on CONSOLE_PBKDF_PASSPHRASE with CONSOLE_PBKDF_SALT
|
// derivedKey is the key used to encrypt the session token claims, its derived using pbkdf on CONSOLE_PBKDF_PASSPHRASE with CONSOLE_PBKDF_SALT
|
||||||
@@ -102,9 +109,10 @@ func NewEncryptedTokenForClient(credentials *credentials.Value, actions []string
|
|||||||
// returns a base64 encoded ciphertext
|
// returns a base64 encoded ciphertext
|
||||||
func encryptClaims(accessKeyID, secretAccessKey, sessionToken string, actions []string) (string, error) {
|
func encryptClaims(accessKeyID, secretAccessKey, sessionToken string, actions []string) (string, error) {
|
||||||
payload := []byte(fmt.Sprintf("%s#%s#%s#%s", accessKeyID, secretAccessKey, sessionToken, strings.Join(actions, ",")))
|
payload := []byte(fmt.Sprintf("%s#%s#%s#%s", accessKeyID, secretAccessKey, sessionToken, strings.Join(actions, ",")))
|
||||||
ciphertext, err := encrypt(payload)
|
ciphertext, err := encrypt(payload, []byte{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
log.Println(err)
|
||||||
|
return "", errorGeneric
|
||||||
}
|
}
|
||||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||||
}
|
}
|
||||||
@@ -116,7 +124,7 @@ func decryptClaims(ciphertext string) (*DecryptedClaims, error) {
|
|||||||
log.Println(err)
|
log.Println(err)
|
||||||
return nil, errClaimsFormat
|
return nil, errClaimsFormat
|
||||||
}
|
}
|
||||||
plaintext, err := decrypt(decoded)
|
plaintext, err := decrypt(decoded, []byte{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return nil, errClaimsFormat
|
return nil, errClaimsFormat
|
||||||
@@ -136,37 +144,137 @@ func decryptClaims(ciphertext string) (*DecryptedClaims, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt a blob of data using AEAD (AES-GCM) with a pbkdf2 derived key
|
const (
|
||||||
func encrypt(plaintext []byte) ([]byte, error) {
|
aesGcm = 0x00
|
||||||
block, _ := aes.NewCipher(derivedKey)
|
c20p1305 = 0x01
|
||||||
gcm, err := cipher.NewGCM(block)
|
)
|
||||||
|
|
||||||
|
// Encrypt a blob of data using AEAD scheme, AES-GCM if the executing CPU
|
||||||
|
// provides AES hardware support, otherwise will use ChaCha20-Poly1305
|
||||||
|
// with a pbkdf2 derived key, this function should be used to encrypt a session
|
||||||
|
// or data key provided as plaintext.
|
||||||
|
//
|
||||||
|
// The returned ciphertext data consists of:
|
||||||
|
// AEAD ID | iv | nonce | encrypted data
|
||||||
|
// 1 16 12 ~ len(data)
|
||||||
|
func encrypt(plaintext, associatedData []byte) ([]byte, error) {
|
||||||
|
iv, err := sioutil.Random(16) // 16 bytes IV
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
nonce := make([]byte, gcm.NonceSize())
|
var algorithm byte
|
||||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
if sioutil.NativeAES() {
|
||||||
|
algorithm = aesGcm
|
||||||
|
} else {
|
||||||
|
algorithm = c20p1305
|
||||||
|
}
|
||||||
|
var aead cipher.AEAD
|
||||||
|
switch algorithm {
|
||||||
|
case aesGcm:
|
||||||
|
mac := hmac.New(sha256.New, derivedKey)
|
||||||
|
mac.Write(iv)
|
||||||
|
sealingKey := mac.Sum(nil)
|
||||||
|
|
||||||
|
var block cipher.Block
|
||||||
|
block, err = aes.NewCipher(sealingKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aead, err = cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case c20p1305:
|
||||||
|
var sealingKey []byte
|
||||||
|
sealingKey, err = chacha20.HChaCha20(derivedKey, iv) // HChaCha20 expects nonce of 16 bytes
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aead, err = chacha20poly1305.New(sealingKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nonce, err := sioutil.Random(aead.NonceSize())
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cipherText := gcm.Seal(nonce, nonce, plaintext, nil)
|
|
||||||
return cipherText, nil
|
sealedBytes := aead.Seal(nil, nonce, plaintext, associatedData)
|
||||||
|
|
||||||
|
// ciphertext = AEAD ID | iv | nonce | sealed bytes
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.WriteByte(algorithm)
|
||||||
|
buf.Write(iv)
|
||||||
|
buf.Write(nonce)
|
||||||
|
buf.Write(sealedBytes)
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypts a blob of data using AEAD (AES-GCM) with a pbkdf2 derived key
|
// Decrypts a blob of data using AEAD scheme AES-GCM if the executing CPU
|
||||||
func decrypt(data []byte) ([]byte, error) {
|
// provides AES hardware support, otherwise will use ChaCha20-Poly1305with
|
||||||
block, err := aes.NewCipher(derivedKey)
|
// and a pbkdf2 derived key
|
||||||
|
func decrypt(ciphertext []byte, associatedData []byte) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
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, algorithm[:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(r, iv[:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(r, nonce[:]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var aead cipher.AEAD
|
||||||
|
switch algorithm[0] {
|
||||||
|
case aesGcm:
|
||||||
|
mac := hmac.New(sha256.New, derivedKey)
|
||||||
|
mac.Write(iv[:])
|
||||||
|
sealingKey := mac.Sum(nil)
|
||||||
|
block, err := aes.NewCipher(sealingKey[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aead, err = cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case c20p1305:
|
||||||
|
sealingKey, err := chacha20.HChaCha20(derivedKey, iv[:]) // HChaCha20 expects nonce of 16 bytes
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aead, err = chacha20poly1305.New(sealingKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid algorithm: %v", algorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nonce) != aead.NonceSize() {
|
||||||
|
return nil, fmt.Errorf("invalid nonce size %d, expected %d", len(nonce), aead.NonceSize())
|
||||||
|
}
|
||||||
|
|
||||||
|
sealedBytes, err := ioutil.ReadAll(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
gcm, err := cipher.NewGCM(block)
|
|
||||||
if err != nil {
|
plaintext, err := aead.Open(nil, nonce[:], sealedBytes, associatedData)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
nonceSize := gcm.NonceSize()
|
|
||||||
nonce, cipherText := data[:nonceSize], data[nonceSize:]
|
|
||||||
plaintext, err := gcm.Open(nil, nonce, cipherText, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return plaintext, nil
|
return plaintext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ func TestNewJWTWithClaimsForClient(t *testing.T) {
|
|||||||
funcAssert := assert.New(t)
|
funcAssert := assert.New(t)
|
||||||
// Test-1 : NewEncryptedTokenForClient() is generated correctly without errors
|
// Test-1 : NewEncryptedTokenForClient() is generated correctly without errors
|
||||||
function := "NewEncryptedTokenForClient()"
|
function := "NewEncryptedTokenForClient()"
|
||||||
jwt, err := NewEncryptedTokenForClient(creds, []string{""})
|
token, err := NewEncryptedTokenForClient(creds, []string{""})
|
||||||
if err != nil || jwt == "" {
|
if err != nil || token == "" {
|
||||||
t.Errorf("Failed on %s:, error occurred: %s", function, err)
|
t.Errorf("Failed on %s:, error occurred: %s", function, err)
|
||||||
}
|
}
|
||||||
// saving jwt for future tests
|
// saving token for future tests
|
||||||
goodToken = jwt
|
goodToken = token
|
||||||
// Test-2 : NewEncryptedTokenForClient() throws error because of empty credentials
|
// Test-2 : NewEncryptedTokenForClient() throws error because of empty credentials
|
||||||
if _, err = NewEncryptedTokenForClient(nil, []string{""}); err != nil {
|
if _, err = NewEncryptedTokenForClient(nil, []string{""}); err != nil {
|
||||||
funcAssert.Equal("provided credentials are empty", err.Error())
|
funcAssert.Equal("provided credentials are empty", err.Error())
|
||||||
|
|||||||
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
15
policies/mcsTestUserAddOnly.json
Normal file
15
policies/mcsTestUserAddOnly.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "consoleTestUserAddOnly",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"admin:CreateUser"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": [
|
||||||
|
"arn:aws:s3:::*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "2012-10-17"
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
10
portal-ui/config-overrides.js
Normal file
10
portal-ui/config-overrides.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
const rewireReactHotLoader = require('react-app-rewire-hot-loader');
|
||||||
|
|
||||||
|
/* config-overrides.js */
|
||||||
|
module.exports = function override(config, env) {
|
||||||
|
if (env === 'development') {
|
||||||
|
config.resolve.alias['react-dom'] = '@hot-loader/react-dom';
|
||||||
|
}
|
||||||
|
config = rewireReactHotLoader(config, env);
|
||||||
|
return config;
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-create-regexp-features-plugin": "^7.7.4",
|
"@babel/helper-create-regexp-features-plugin": "^7.7.4",
|
||||||
"@babel/plugin-transform-react-jsx-development": "^7.9.0",
|
"@babel/plugin-transform-react-jsx-development": "^7.9.0",
|
||||||
|
"@hot-loader/react-dom": "^16.9.0",
|
||||||
"@material-ui/core": "^4.9.12",
|
"@material-ui/core": "^4.9.12",
|
||||||
"@material-ui/icons": "^4.9.1",
|
"@material-ui/icons": "^4.9.1",
|
||||||
"@types/history": "^4.7.3",
|
"@types/history": "^4.7.3",
|
||||||
@@ -29,9 +30,13 @@
|
|||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"npm": "^6.14.4",
|
"npm": "^6.14.4",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
|
"react-app-rewire-hot-loader": "^2.0.1",
|
||||||
|
"react-app-rewired": "^2.1.6",
|
||||||
|
"react-async-hook": "^3.6.1",
|
||||||
"react-chartjs-2": "^2.9.0",
|
"react-chartjs-2": "^2.9.0",
|
||||||
"react-codemirror2": "^7.1.0",
|
"react-codemirror2": "^7.1.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
|
"react-hot-loader": "^4.13.0",
|
||||||
"react-moment": "^0.9.7",
|
"react-moment": "^0.9.7",
|
||||||
"react-redux": "^7.1.3",
|
"react-redux": "^7.1.3",
|
||||||
"react-router-dom": "^5.1.2",
|
"react-router-dom": "^5.1.2",
|
||||||
@@ -42,10 +47,11 @@
|
|||||||
"superagent": "^5.1.0",
|
"superagent": "^5.1.0",
|
||||||
"typeface-roboto": "^0.0.75",
|
"typeface-roboto": "^0.0.75",
|
||||||
"typescript": "3.6.4",
|
"typescript": "3.6.4",
|
||||||
|
"use-debounce": "^5.0.1",
|
||||||
"websocket": "^1.0.31"
|
"websocket": "^1.0.31"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "PORT=5000 react-scripts start",
|
"start": "PORT=5000 react-app-rewired start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject"
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { connect } from "react-redux";
|
|||||||
import { AppState } from "./store";
|
import { AppState } from "./store";
|
||||||
import { userLoggedIn } from "./actions";
|
import { userLoggedIn } from "./actions";
|
||||||
import LoginCallback from "./screens/LoginPage/LoginCallback";
|
import LoginCallback from "./screens/LoginPage/LoginCallback";
|
||||||
|
import { hot } from "react-hot-loader/root";
|
||||||
|
|
||||||
const isLoggedIn = () => {
|
const isLoggedIn = () => {
|
||||||
return (
|
return (
|
||||||
@@ -75,4 +76,4 @@ class Routes extends React.Component<RoutesProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connector(Routes);
|
export default hot(connector(Routes));
|
||||||
|
|||||||
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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import storage from "local-storage-fallback";
|
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 = [
|
export const units = [
|
||||||
"B",
|
"B",
|
||||||
@@ -28,6 +32,8 @@ export const units = [
|
|||||||
"YiB",
|
"YiB",
|
||||||
];
|
];
|
||||||
export const k8sUnits = ["Ki", "Mi", "Gi", "Ti", "Pi", "Ei"];
|
export const k8sUnits = ["Ki", "Mi", "Gi", "Ti", "Pi", "Ei"];
|
||||||
|
export const k8sCalcUnits = ["B", ...k8sUnits];
|
||||||
|
|
||||||
export const niceBytes = (x: string) => {
|
export const niceBytes = (x: string) => {
|
||||||
let l = 0,
|
let l = 0,
|
||||||
n = parseInt(x, 10) || 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
|
//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 vl: number = parseFloat(value);
|
||||||
const powFactor = units.findIndex((element) => element === unit);
|
|
||||||
|
|
||||||
if (powFactor == -1) {
|
const unitsTake = fork8s ? k8sCalcUnits : units;
|
||||||
return 0;
|
|
||||||
|
const powFactor = unitsTake.findIndex((element) => element === unit);
|
||||||
|
|
||||||
|
if (powFactor === -1) {
|
||||||
|
return "0";
|
||||||
}
|
}
|
||||||
const factor = Math.pow(1024, powFactor);
|
const factor = Math.pow(1024, powFactor);
|
||||||
const total = vl * factor;
|
const total = vl * factor;
|
||||||
@@ -105,6 +118,220 @@ export const getBytes = (value: string, unit: string) => {
|
|||||||
|
|
||||||
//getTotalSize gets the total size of a value & unit
|
//getTotalSize gets the total size of a value & unit
|
||||||
export const getTotalSize = (value: string, unit: string) => {
|
export const getTotalSize = (value: string, unit: string) => {
|
||||||
const bytes = getBytes(value, unit).toString(10);
|
const bytes = getBytes(value, unit, true).toString();
|
||||||
return niceBytes(bytes);
|
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}`;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 81.879 23.119"><defs><style>.cls-1{fill:#1b1556;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M10.086,13.4,8.969,14.573a4.2,4.2,0,0,0-3.01-1.279,3.886,3.886,0,0,0-3.884,4.093,3.888,3.888,0,0,0,3.884,4.1A4.4,4.4,0,0,0,9,20.2l1.082,1.186A5.344,5.344,0,0,1,6,23.119a5.512,5.512,0,0,1-5.7-5.732A5.509,5.509,0,0,1,6,11.667,5.328,5.328,0,0,1,10.086,13.4Z"/><path class="cls-1" d="M23.608,17.387a5.637,5.637,0,0,1-5.8,5.732,5.628,5.628,0,0,1-5.791-5.732,5.626,5.626,0,0,1,5.791-5.72A5.635,5.635,0,0,1,23.608,17.387Zm-9.825,0a4.024,4.024,0,1,0,8.046,0,4.024,4.024,0,1,0-8.046,0Z"/><path class="cls-1" d="M36.862,16.073V22.9H35.129V16.643c0-2.093-1.163-3.325-3.174-3.325a3.24,3.24,0,0,0-3.371,3.372V22.9H26.839V11.888H28.56v1.569a4.354,4.354,0,0,1,3.709-1.79A4.261,4.261,0,0,1,36.862,16.073Z"/><path class="cls-1" d="M48.2,14.225a6.872,6.872,0,0,0-3.605-1.035c-1.569,0-2.6.663-2.6,1.732,0,.919.8,1.372,2.244,1.547l1.3.163c2.338.3,3.7,1.267,3.7,3.069,0,2.093-1.884,3.407-4.849,3.407A7.725,7.725,0,0,1,39.791,21.7l.8-1.3a5.8,5.8,0,0,0,3.815,1.2c1.86,0,3.034-.616,3.034-1.778,0-.884-.744-1.419-2.3-1.605l-1.314-.151c-2.477-.3-3.639-1.408-3.639-3.046,0-2.082,1.755-3.338,4.4-3.338a8.067,8.067,0,0,1,4.372,1.2Z"/><path class="cls-1" d="M63.033,17.387a5.8,5.8,0,0,1-11.593,0,5.8,5.8,0,0,1,11.593,0Zm-9.825,0a4.023,4.023,0,1,0,8.045,0,4.023,4.023,0,1,0-8.045,0Z"/><path class="cls-1" d="M68.008,22.9H66.264V6.155h1.744Z"/><path class="cls-1" d="M81.879,17.353a5.606,5.606,0,0,1-.035.65H73.019a3.743,3.743,0,0,0,3.9,3.593A5.1,5.1,0,0,0,80.4,20.213l.931,1.186a6.179,6.179,0,0,1-4.524,1.72A5.394,5.394,0,0,1,71.24,17.4a5.406,5.406,0,0,1,5.465-5.732C79.693,11.667,81.856,14,81.879,17.353ZM73.043,16.6h7.069a3.446,3.446,0,0,0-3.442-3.384A3.59,3.59,0,0,0,73.043,16.6Z"/><rect class="cls-1" x="13.484" y="0.12" width="2.328" height="6.875"/><path class="cls-1" d="M10.662.215,5.936,3.1a.21.21,0,0,1-.219,0L.992.215A.651.651,0,0,0,.654.12H.648A.648.648,0,0,0,0,.768v6.22H2.327V4.028a.233.233,0,0,1,.354-.2l2.648,1.62a.829.829,0,0,0,.853.008l2.8-1.639a.232.232,0,0,1,.35.2V6.988h2.327V.768A.648.648,0,0,0,11.006.12H11A.651.651,0,0,0,10.662.215Z"/><path class="cls-1" d="M27.422.12H25.061V3.25a.233.233,0,0,1-.342.205L18.6.2A.662.662,0,0,0,18.3.12h0a.648.648,0,0,0-.648.648v6.22h2.342V3.863a.233.233,0,0,1,.342-.206L26.47,6.915a.646.646,0,0,0,.3.076h0a.648.648,0,0,0,.648-.648Z"/><path class="cls-1" d="M29.252,7V.12h1.072V7Z"/><path class="cls-1" d="M36.629,7.119c-2.882,0-4.927-1.368-4.927-3.56S33.759,0,36.629,0s4.938,1.367,4.938,3.559S39.547,7.119,36.629,7.119Zm0-6.208c-2.143,0-3.794.936-3.794,2.648s1.651,2.648,3.794,2.648,3.805-.923,3.805-2.648S38.772.911,36.629.911Z"/></g></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="121.755" height="29.822" viewBox="0 0 121.755 29.822"><defs><style>.a{fill:#fff;}</style></defs><g transform="translate(3016.56 -1037.757)"><path class="a" d="M-3000.913,1053.692l-1.772,1.194a6.088,6.088,0,0,0-5.135-2.652,6.348,6.348,0,0,0-6.522,6.654,6.348,6.348,0,0,0,6.522,6.654,6.031,6.031,0,0,0,5.124-2.64l1.735,1.266a8.126,8.126,0,0,1-6.859,3.411,8.422,8.422,0,0,1-8.74-8.691,8.422,8.422,0,0,1,8.74-8.691A7.963,7.963,0,0,1-3000.913,1053.692Z"/><path class="a" d="M-2980.919,1058.888a8.422,8.422,0,0,1-8.74,8.691,8.422,8.422,0,0,1-8.739-8.691,8.421,8.421,0,0,1,8.739-8.691A8.422,8.422,0,0,1-2980.919,1058.888Zm-15.261,0a6.347,6.347,0,0,0,6.521,6.654,6.347,6.347,0,0,0,6.521-6.654,6.347,6.347,0,0,0-6.521-6.654A6.347,6.347,0,0,0-2996.18,1058.888Z"/><path class="a" d="M-2962.831,1067.338h-1.917l-10.2-13.26-.012,13.248h-2.122v-16.888h1.917l10.21,13.26V1050.45h2.122Z"/><path class="a" d="M-2947.009,1053.777a8.835,8.835,0,0,0-5-1.555c-2.471,0-4.231,1.109-4.231,2.929,0,1.531,1.29,2.315,3.821,2.628l1.484.181c2.856.35,5.3,1.507,5.3,4.484,0,3.364-3.05,5.123-6.7,5.123a10.935,10.935,0,0,1-6.654-2.194l1.157-1.687a9.018,9.018,0,0,0,5.5,1.868c2.519,0,4.5-1.025,4.5-2.929,0-1.567-1.41-2.314-4.038-2.64l-1.567-.193c-2.784-.337-5-1.627-5-4.508,0-3.255,2.893-5.075,6.449-5.075a10.336,10.336,0,0,1,6.076,1.844Z"/><path class="a" d="M-2925.292,1058.888a8.422,8.422,0,0,1-8.74,8.691,8.422,8.422,0,0,1-8.739-8.691,8.421,8.421,0,0,1,8.739-8.691A8.422,8.422,0,0,1-2925.292,1058.888Zm-15.261,0a6.348,6.348,0,0,0,6.521,6.654,6.347,6.347,0,0,0,6.521-6.654,6.347,6.347,0,0,0-6.521-6.654A6.348,6.348,0,0,0-2940.553,1058.888Z"/><path class="a" d="M-2909.663,1067.326h-11.79V1050.45h2.122v14.863h9.668Z"/><path class="a" d="M-2894.8,1067.326h-11.982V1050.45h11.862v1.988h-9.74v5.389h9.427v2h-9.427v5.509h9.86Z"/><rect class="a" width="2.576" height="7.547" transform="translate(-3001.66 1037.924)"/><path class="a" d="M-3004.759,1037.995l-5.23,3.194a.229.229,0,0,1-.242,0l-5.23-3.194a.726.726,0,0,0-.374-.1h-.006a.717.717,0,0,0-.717.717v6.864h2.574v-3.257a.258.258,0,0,1,.392-.22l2.931,1.793a.919.919,0,0,0,.944.009l3.092-1.814a.258.258,0,0,1,.388.222v3.267h2.575v-6.864a.717.717,0,0,0-.717-.717h-.006A.723.723,0,0,0-3004.759,1037.995Z"/><path class="a" d="M-2986.212,1037.922h-2.613v3.463a.258.258,0,0,1-.379.228l-6.771-3.607a.723.723,0,0,0-.337-.084h0a.717.717,0,0,0-.717.717v6.832h2.592v-3.408a.258.258,0,0,1,.379-.227l6.8,3.606a.714.714,0,0,0,.336.083h0a.716.716,0,0,0,.717-.717v-6.886Z"/><path class="a" d="M-2984.121,1045.469v-7.547h1.2v7.547Z"/><path class="a" d="M-2976.024,1045.635c-3.189,0-5.451-1.513-5.451-3.939s2.276-3.939,5.451-3.939,5.466,1.513,5.466,3.939S-2972.794,1045.635-2976.024,1045.635Zm0-6.87c-2.371,0-4.2,1.036-4.2,2.931s1.826,2.93,4.2,2.93,4.212-1.022,4.212-2.93S-2973.652,1038.765-2976.024,1038.765Z"/></g></svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -1,4 +1,4 @@
|
|||||||
// This file is part of MinIO Buckets Server
|
// This file is part of MinIO Console Server
|
||||||
// Copyright (c) 2020 MinIO, Inc.
|
// Copyright (c) 2020 MinIO, Inc.
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// This program is free software: you can redistribute it and/or modify
|
||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
createStyles,
|
createStyles,
|
||||||
StyledProps,
|
StyledProps,
|
||||||
Theme,
|
Theme,
|
||||||
withStyles
|
withStyles,
|
||||||
} from "@material-ui/core/styles";
|
} from "@material-ui/core/styles";
|
||||||
|
|
||||||
import history from "../../../history";
|
import history from "../../../history";
|
||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
RouteComponentProps,
|
RouteComponentProps,
|
||||||
Router,
|
Router,
|
||||||
Switch,
|
Switch,
|
||||||
withRouter
|
withRouter,
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { AppState } from "../../../store";
|
import { AppState } from "../../../store";
|
||||||
@@ -41,62 +41,62 @@ import ViewBucket from "./ViewBucket/ViewBucket";
|
|||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
root: {
|
root: {
|
||||||
display: "flex"
|
display: "flex",
|
||||||
},
|
},
|
||||||
toolbar: {
|
toolbar: {
|
||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
color: "black",
|
color: "black",
|
||||||
paddingRight: 24 // keep right padding when drawer closed
|
paddingRight: 24, // keep right padding when drawer closed
|
||||||
},
|
},
|
||||||
toolbarIcon: {
|
toolbarIcon: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "flex-end",
|
justifyContent: "flex-end",
|
||||||
padding: "0 8px",
|
padding: "0 8px",
|
||||||
...theme.mixins.toolbar
|
...theme.mixins.toolbar,
|
||||||
},
|
},
|
||||||
appBar: {
|
appBar: {
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
transition: theme.transitions.create(["width", "margin"], {
|
transition: theme.transitions.create(["width", "margin"], {
|
||||||
easing: theme.transitions.easing.sharp,
|
easing: theme.transitions.easing.sharp,
|
||||||
duration: theme.transitions.duration.leavingScreen
|
duration: theme.transitions.duration.leavingScreen,
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
menuButton: {
|
menuButton: {
|
||||||
marginRight: 36
|
marginRight: 36,
|
||||||
},
|
},
|
||||||
menuButtonHidden: {
|
menuButtonHidden: {
|
||||||
display: "none"
|
display: "none",
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
flexGrow: 1
|
flexGrow: 1,
|
||||||
},
|
},
|
||||||
appBarSpacer: {
|
appBarSpacer: {
|
||||||
height: "5px"
|
height: "5px",
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
height: "100vh",
|
height: "100vh",
|
||||||
overflow: "auto"
|
overflow: "auto",
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
paddingTop: theme.spacing(4),
|
paddingTop: theme.spacing(4),
|
||||||
paddingBottom: theme.spacing(4)
|
paddingBottom: theme.spacing(4),
|
||||||
},
|
},
|
||||||
paper: {
|
paper: {
|
||||||
padding: theme.spacing(2),
|
padding: theme.spacing(2),
|
||||||
display: "flex",
|
display: "flex",
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
flexDirection: "column"
|
flexDirection: "column",
|
||||||
},
|
},
|
||||||
fixedHeight: {
|
fixedHeight: {
|
||||||
minHeight: 240
|
minHeight: 240,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapState = (state: AppState) => ({
|
const mapState = (state: AppState) => ({
|
||||||
open: state.system.sidebarOpen
|
open: state.system.sidebarOpen,
|
||||||
});
|
});
|
||||||
|
|
||||||
const connector = connect(mapState, { setMenuOpen });
|
const connector = connect(mapState, { setMenuOpen });
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { Button, LinearProgress } from "@material-ui/core";
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
@@ -23,6 +23,21 @@ import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
|||||||
import api from "../../../../common/api";
|
import api from "../../../../common/api";
|
||||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
|
import CheckboxWrapper from "../../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
|
||||||
|
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||||
|
import { factorForDropdown, getBytes } from "../../../../common/utils";
|
||||||
|
import { AppState } from "../../../../store";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import {
|
||||||
|
addBucketName,
|
||||||
|
addBucketQuota,
|
||||||
|
addBucketQuotaSize,
|
||||||
|
addBucketQuotaType,
|
||||||
|
addBucketQuotaUnit,
|
||||||
|
addBucketVersioned,
|
||||||
|
} from "../actions";
|
||||||
|
import { useDebounce } from "use-debounce";
|
||||||
|
import { MakeBucketRequest } from "../types";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -32,6 +47,15 @@ const styles = (theme: Theme) =>
|
|||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
},
|
},
|
||||||
|
multiContainer: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center" as const,
|
||||||
|
justifyContent: "flex-start" as const,
|
||||||
|
},
|
||||||
|
sizeFactorContainer: {
|
||||||
|
marginLeft: 8,
|
||||||
|
alignSelf: "flex-start" as const,
|
||||||
|
},
|
||||||
...modalBasic,
|
...modalBasic,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -39,119 +63,238 @@ interface IAddBucketProps {
|
|||||||
classes: any;
|
classes: any;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
closeModalAndRefresh: () => void;
|
closeModalAndRefresh: () => void;
|
||||||
}
|
addBucketName: typeof addBucketName;
|
||||||
|
addBucketVersioned: typeof addBucketVersioned;
|
||||||
interface IAddBucketState {
|
addBucketQuota: typeof addBucketQuota;
|
||||||
addLoading: boolean;
|
addBucketQuotaType: typeof addBucketQuotaType;
|
||||||
addError: string;
|
addBucketQuotaSize: typeof addBucketQuotaSize;
|
||||||
|
addBucketQuotaUnit: typeof addBucketQuotaUnit;
|
||||||
bucketName: string;
|
bucketName: string;
|
||||||
|
versioned: boolean;
|
||||||
|
enableQuota: boolean;
|
||||||
|
quotaType: string;
|
||||||
|
quotaSize: string;
|
||||||
|
quotaUnit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddBucket extends React.Component<IAddBucketProps, IAddBucketState> {
|
const AddBucket = ({
|
||||||
state: IAddBucketState = {
|
classes,
|
||||||
addLoading: false,
|
open,
|
||||||
addError: "",
|
closeModalAndRefresh,
|
||||||
bucketName: "",
|
addBucketName,
|
||||||
};
|
addBucketVersioned,
|
||||||
|
addBucketQuota,
|
||||||
|
addBucketQuotaType,
|
||||||
|
addBucketQuotaSize,
|
||||||
|
addBucketQuotaUnit,
|
||||||
|
bucketName,
|
||||||
|
versioned,
|
||||||
|
enableQuota,
|
||||||
|
quotaType,
|
||||||
|
quotaSize,
|
||||||
|
quotaUnit,
|
||||||
|
}: IAddBucketProps) => {
|
||||||
|
const [bName, setBName] = useState<string>(bucketName);
|
||||||
|
const [addLoading, setAddLoading] = useState<boolean>(false);
|
||||||
|
const [addError, setAddError] = useState<string>("");
|
||||||
|
|
||||||
addRecord(event: React.FormEvent) {
|
const addRecord = (event: React.FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const { bucketName, addLoading } = this.state;
|
|
||||||
if (addLoading) {
|
if (addLoading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({ addLoading: true }, () => {
|
setAddLoading(true);
|
||||||
api
|
|
||||||
.invoke("POST", "/api/v1/buckets", {
|
|
||||||
name: bucketName,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
this.setState(
|
|
||||||
{
|
|
||||||
addLoading: false,
|
|
||||||
addError: "",
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
this.props.closeModalAndRefresh();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
this.setState({
|
|
||||||
addLoading: false,
|
|
||||||
addError: err,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
let request: MakeBucketRequest = {
|
||||||
const { classes, open } = this.props;
|
name: bucketName,
|
||||||
const { addLoading, addError, bucketName } = this.state;
|
versioning: versioned,
|
||||||
return (
|
};
|
||||||
<ModalWrapper
|
|
||||||
title="Create Bucket"
|
if (enableQuota) {
|
||||||
modalOpen={open}
|
const amount = getBytes(quotaSize, quotaUnit, false);
|
||||||
onClose={() => {
|
request.quota = {
|
||||||
this.setState({ addError: "" }, () => {
|
enabled: true,
|
||||||
this.props.closeModalAndRefresh();
|
quota_type: quotaType,
|
||||||
});
|
amount: parseInt(amount),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
api
|
||||||
|
.invoke("POST", "/api/v1/buckets", request)
|
||||||
|
.then((res) => {
|
||||||
|
setAddLoading(false);
|
||||||
|
setAddError("");
|
||||||
|
closeModalAndRefresh();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setAddLoading(false);
|
||||||
|
setAddError(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const [value] = useDebounce(bName, 1000);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("called");
|
||||||
|
addBucketName(value);
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalWrapper
|
||||||
|
title="Create Bucket"
|
||||||
|
modalOpen={open}
|
||||||
|
onClose={() => {
|
||||||
|
setAddError("");
|
||||||
|
closeModalAndRefresh();
|
||||||
|
}}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
noValidate
|
||||||
|
autoComplete="off"
|
||||||
|
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
addRecord(e);
|
||||||
}}
|
}}
|
||||||
aria-labelledby="alert-dialog-title"
|
|
||||||
aria-describedby="alert-dialog-description"
|
|
||||||
>
|
>
|
||||||
<form
|
<Grid container>
|
||||||
noValidate
|
<Grid item xs={12} className={classes.formScrollable}>
|
||||||
autoComplete="off"
|
{addError !== "" && (
|
||||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
this.addRecord(e);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid container>
|
|
||||||
<Grid item xs={12} className={classes.formScrollable}>
|
|
||||||
{addError !== "" && (
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography
|
|
||||||
component="p"
|
|
||||||
variant="body1"
|
|
||||||
className={classes.errorBlock}
|
|
||||||
>
|
|
||||||
{addError}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
)}
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<InputBoxWrapper
|
<Typography
|
||||||
id="bucket-name"
|
component="p"
|
||||||
name="bucket-name"
|
variant="body1"
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
className={classes.errorBlock}
|
||||||
this.setState({ bucketName: e.target.value });
|
>
|
||||||
}}
|
{addError}
|
||||||
label="Bucket Name"
|
</Typography>
|
||||||
value={bucketName}
|
|
||||||
/>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} className={classes.buttonContainer}>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
disabled={addLoading}
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
{addLoading && (
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<LinearProgress />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="bucket-name"
|
||||||
|
name="bucket-name"
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setBName(event.target.value);
|
||||||
|
}}
|
||||||
|
label="Bucket Name"
|
||||||
|
value={bName}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<CheckboxWrapper
|
||||||
|
value="versioned"
|
||||||
|
id="versioned"
|
||||||
|
name="versioned"
|
||||||
|
checked={versioned}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
addBucketVersioned(event.target.checked);
|
||||||
|
}}
|
||||||
|
label={"Turn On Versioning"}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<CheckboxWrapper
|
||||||
|
value="bucket_quota"
|
||||||
|
id="bucket_quota"
|
||||||
|
name="bucket_quota"
|
||||||
|
checked={enableQuota}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
addBucketQuota(event.target.checked);
|
||||||
|
}}
|
||||||
|
label={"Enable Bucket Quota"}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
{enableQuota && (
|
||||||
|
<React.Fragment>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<SelectWrapper
|
||||||
|
value={quotaType}
|
||||||
|
label="Quota Type"
|
||||||
|
id="quota_type"
|
||||||
|
name="quota_type"
|
||||||
|
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||||
|
addBucketQuotaType(e.target.value as string);
|
||||||
|
}}
|
||||||
|
options={[
|
||||||
|
{ value: "hard", label: "Hard" },
|
||||||
|
{ value: "fifo", label: "FIFO" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<div className={classes.multiContainer}>
|
||||||
|
<div>
|
||||||
|
<InputBoxWrapper
|
||||||
|
type="number"
|
||||||
|
id="quota_size"
|
||||||
|
name="quota_size"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
addBucketQuotaSize(e.target.value);
|
||||||
|
}}
|
||||||
|
label="Size"
|
||||||
|
value={quotaSize}
|
||||||
|
required
|
||||||
|
min="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={classes.sizeFactorContainer}>
|
||||||
|
<SelectWrapper
|
||||||
|
label=""
|
||||||
|
id="quota_unit"
|
||||||
|
name="quota_unit"
|
||||||
|
value={quotaUnit}
|
||||||
|
onChange={(
|
||||||
|
e: React.ChangeEvent<{ value: unknown }>
|
||||||
|
) => {
|
||||||
|
addBucketQuotaUnit(e.target.value as string);
|
||||||
|
}}
|
||||||
|
options={factorForDropdown()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</form>
|
<Grid item xs={12} className={classes.buttonContainer}>
|
||||||
</ModalWrapper>
|
<Button
|
||||||
);
|
type="submit"
|
||||||
}
|
variant="contained"
|
||||||
}
|
color="primary"
|
||||||
|
disabled={addLoading}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
{addLoading && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<LinearProgress />
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</form>
|
||||||
|
</ModalWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default withStyles(styles)(AddBucket);
|
const mapState = (state: AppState) => ({
|
||||||
|
addBucketModalOpen: state.buckets.open,
|
||||||
|
bucketName: state.buckets.addBucketName,
|
||||||
|
versioned: state.buckets.addBucketVersioning,
|
||||||
|
enableQuota: state.buckets.addBucketQuotaEnabled,
|
||||||
|
quotaType: state.buckets.addBucketQuotaType,
|
||||||
|
quotaSize: state.buckets.addBucketQuotaSize,
|
||||||
|
quotaUnit: state.buckets.addBucketQuotaUnit,
|
||||||
|
});
|
||||||
|
|
||||||
|
const connector = connect(mapState, {
|
||||||
|
addBucketName: addBucketName,
|
||||||
|
addBucketVersioned: addBucketVersioned,
|
||||||
|
addBucketQuota: addBucketQuota,
|
||||||
|
addBucketQuotaType: addBucketQuotaType,
|
||||||
|
addBucketQuotaSize: addBucketQuotaSize,
|
||||||
|
addBucketQuotaUnit: addBucketQuotaUnit,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connector(withStyles(styles)(AddBucket));
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogContentText,
|
DialogContentText,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
LinearProgress
|
LinearProgress,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import api from "../../../../common/api";
|
import api from "../../../../common/api";
|
||||||
import { BucketList } from "../types";
|
import { BucketList } from "../types";
|
||||||
@@ -32,8 +32,8 @@ import Typography from "@material-ui/core/Typography";
|
|||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
errorBlock: {
|
errorBlock: {
|
||||||
color: "red"
|
color: "red",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IDeleteBucketProps {
|
interface IDeleteBucketProps {
|
||||||
@@ -54,7 +54,7 @@ class DeleteBucket extends React.Component<
|
|||||||
> {
|
> {
|
||||||
state: IDeleteBucketState = {
|
state: IDeleteBucketState = {
|
||||||
deleteLoading: false,
|
deleteLoading: false,
|
||||||
deleteError: ""
|
deleteError: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
removeRecord() {
|
removeRecord() {
|
||||||
@@ -66,23 +66,23 @@ class DeleteBucket extends React.Component<
|
|||||||
this.setState({ deleteLoading: true }, () => {
|
this.setState({ deleteLoading: true }, () => {
|
||||||
api
|
api
|
||||||
.invoke("DELETE", `/api/v1/buckets/${selectedBucket}`, {
|
.invoke("DELETE", `/api/v1/buckets/${selectedBucket}`, {
|
||||||
name: selectedBucket
|
name: selectedBucket,
|
||||||
})
|
})
|
||||||
.then((res: BucketList) => {
|
.then((res: BucketList) => {
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
deleteLoading: false,
|
deleteLoading: false,
|
||||||
deleteError: ""
|
deleteError: "",
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.props.closeDeleteModalAndRefresh(true);
|
this.props.closeDeleteModalAndRefresh(true);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
deleteLoading: false,
|
deleteLoading: false,
|
||||||
deleteError: err
|
deleteError: err,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import { Button } from "@material-ui/core";
|
import { Button } from "@material-ui/core";
|
||||||
@@ -31,6 +31,12 @@ import DeleteBucket from "./DeleteBucket";
|
|||||||
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
||||||
import { CreateIcon } from "../../../../icons";
|
import { CreateIcon } from "../../../../icons";
|
||||||
import { niceBytes } from "../../../../common/utils";
|
import { niceBytes } from "../../../../common/utils";
|
||||||
|
import { AppState } from "../../../../store";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { logMessageReceived, logResetMessages } from "../../Logs/actions";
|
||||||
|
import { addBucketOpen, addBucketReset } from "../actions";
|
||||||
|
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -74,176 +80,153 @@ const styles = (theme: Theme) =>
|
|||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
},
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IListBucketsProps {
|
interface IListBucketsProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
|
addBucketOpen: typeof addBucketOpen;
|
||||||
|
addBucketModalOpen: boolean;
|
||||||
|
addBucketReset: typeof addBucketReset;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IListBucketsState {
|
const ListBuckets = ({
|
||||||
records: Bucket[];
|
classes,
|
||||||
totalRecords: number;
|
addBucketOpen,
|
||||||
loading: boolean;
|
addBucketModalOpen,
|
||||||
error: string;
|
addBucketReset,
|
||||||
deleteError: string;
|
}: IListBucketsProps) => {
|
||||||
addScreenOpen: boolean;
|
const [records, setRecords] = useState<Bucket[]>([]);
|
||||||
page: number;
|
const [totalRecords, setTotalRecords] = useState<number>(0);
|
||||||
rowsPerPage: number;
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
deleteOpen: boolean;
|
const [error, setError] = useState<string>("");
|
||||||
selectedBucket: string;
|
const [deleteError, setDeleteError] = useState<string>("");
|
||||||
filterBuckets: string;
|
const [page, setPage] = useState<number>(0);
|
||||||
}
|
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||||
|
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||||
|
const [selectedBucket, setSelectedBucket] = useState<string>("");
|
||||||
|
const [filterBuckets, setFilterBuckets] = useState<string>("");
|
||||||
|
|
||||||
class ListBuckets extends React.Component<
|
useEffect(() => {
|
||||||
IListBucketsProps,
|
if (loading) {
|
||||||
IListBucketsState
|
const fetchRecords = () => {
|
||||||
> {
|
setLoading(true);
|
||||||
state: IListBucketsState = {
|
const offset = page * rowsPerPage;
|
||||||
records: [],
|
api
|
||||||
totalRecords: 0,
|
.invoke(
|
||||||
loading: false,
|
"GET",
|
||||||
error: "",
|
`/api/v1/buckets?offset=${offset}&limit=${rowsPerPage}`
|
||||||
deleteError: "",
|
)
|
||||||
addScreenOpen: false,
|
.then((res: BucketList) => {
|
||||||
page: 0,
|
setLoading(false);
|
||||||
rowsPerPage: 10,
|
setRecords(res.buckets || []);
|
||||||
deleteOpen: false,
|
setTotalRecords(!res.buckets ? 0 : res.total);
|
||||||
selectedBucket: "",
|
setError("");
|
||||||
filterBuckets: "",
|
// if we get 0 results, and page > 0 , go down 1 page
|
||||||
|
if (
|
||||||
|
(res.buckets === undefined ||
|
||||||
|
res.buckets == null ||
|
||||||
|
res.buckets.length === 0) &&
|
||||||
|
page > 0
|
||||||
|
) {
|
||||||
|
const newPage = page - 1;
|
||||||
|
setPage(newPage);
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
setLoading(false);
|
||||||
|
setError(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
fetchRecords();
|
||||||
|
}
|
||||||
|
}, [loading, page, rowsPerPage]);
|
||||||
|
|
||||||
|
const closeAddModalAndRefresh = () => {
|
||||||
|
addBucketOpen(false);
|
||||||
|
addBucketReset();
|
||||||
|
setLoading(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchRecords() {
|
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||||
this.setState({ loading: true }, () => {
|
setDeleteOpen(false);
|
||||||
const { page, rowsPerPage } = this.state;
|
if (refresh) {
|
||||||
const offset = page * rowsPerPage;
|
setLoading(true);
|
||||||
api
|
}
|
||||||
.invoke("GET", `/api/v1/buckets?offset=${offset}&limit=${rowsPerPage}`)
|
};
|
||||||
.then((res: BucketList) => {
|
|
||||||
this.setState({
|
|
||||||
loading: false,
|
|
||||||
records: res.buckets || [],
|
|
||||||
totalRecords: !res.buckets ? 0 : res.total,
|
|
||||||
error: "",
|
|
||||||
});
|
|
||||||
// if we get 0 results, and page > 0 , go down 1 page
|
|
||||||
if (
|
|
||||||
(res.buckets === undefined ||
|
|
||||||
res.buckets == null ||
|
|
||||||
res.buckets.length === 0) &&
|
|
||||||
page > 0
|
|
||||||
) {
|
|
||||||
const newPage = page - 1;
|
|
||||||
this.setState({ page: newPage }, () => {
|
|
||||||
this.fetchRecords();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err: any) => {
|
|
||||||
this.setState({ loading: false, error: err });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeAddModalAndRefresh() {
|
useEffect(() => {
|
||||||
this.setState({ addScreenOpen: false }, () => {
|
setLoading(true);
|
||||||
this.fetchRecords();
|
}, []);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeDeleteModalAndRefresh(refresh: boolean) {
|
useEffect(() => {
|
||||||
this.setState({ deleteOpen: false }, () => {
|
setLoading(true);
|
||||||
if (refresh) {
|
}, [page, rowsPerPage]);
|
||||||
this.fetchRecords();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount(): void {
|
const confirmDeleteBucket = (bucket: string) => {
|
||||||
this.fetchRecords();
|
setDeleteOpen(true);
|
||||||
}
|
setSelectedBucket(bucket);
|
||||||
|
};
|
||||||
|
|
||||||
bucketFilter(): void {}
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
const handleChangeRowsPerPage = (
|
||||||
const { classes } = this.props;
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
const {
|
) => {
|
||||||
records,
|
const rPP = parseInt(event.target.value, 10);
|
||||||
totalRecords,
|
setPage(0);
|
||||||
addScreenOpen,
|
setRowsPerPage(rPP);
|
||||||
loading,
|
};
|
||||||
page,
|
const tableActions = [
|
||||||
rowsPerPage,
|
{ type: "view", to: `/buckets`, sendOnlyId: true },
|
||||||
deleteOpen,
|
{ type: "delete", onClick: confirmDeleteBucket, sendOnlyId: true },
|
||||||
selectedBucket,
|
];
|
||||||
filterBuckets,
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const offset = page * rowsPerPage;
|
const offset = page * rowsPerPage;
|
||||||
|
|
||||||
const handleChangePage = (event: unknown, newPage: number) => {
|
const displayParsedDate = (date: string) => {
|
||||||
this.setState({ page: newPage });
|
return <Moment>{date}</Moment>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChangeRowsPerPage = (
|
const filteredRecords = records
|
||||||
event: React.ChangeEvent<HTMLInputElement>
|
.filter((b: Bucket) => {
|
||||||
) => {
|
if (filterBuckets === "") {
|
||||||
const rPP = parseInt(event.target.value, 10);
|
return true;
|
||||||
this.setState({ page: 0, rowsPerPage: rPP });
|
} else {
|
||||||
};
|
if (b.name.indexOf(filterBuckets) >= 0) {
|
||||||
|
|
||||||
const confirmDeleteBucket = (bucket: string) => {
|
|
||||||
this.setState({ deleteOpen: true, selectedBucket: bucket });
|
|
||||||
};
|
|
||||||
|
|
||||||
const tableActions = [
|
|
||||||
{ type: "view", to: `/buckets`, sendOnlyId: true },
|
|
||||||
{ type: "delete", onClick: confirmDeleteBucket, sendOnlyId: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
const displayParsedDate = (date: string) => {
|
|
||||||
return <Moment>{date}</Moment>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredRecords = records
|
|
||||||
.slice(offset, offset + rowsPerPage)
|
|
||||||
.filter((b: Bucket) => {
|
|
||||||
if (filterBuckets === "") {
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (b.name.indexOf(filterBuckets) >= 0) {
|
return false;
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
.slice(offset, offset + rowsPerPage);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{addScreenOpen && (
|
{addBucketModalOpen && (
|
||||||
<AddBucket
|
<AddBucket
|
||||||
open={addScreenOpen}
|
open={addBucketModalOpen}
|
||||||
closeModalAndRefresh={() => {
|
closeModalAndRefresh={() => {
|
||||||
this.closeAddModalAndRefresh();
|
closeAddModalAndRefresh();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{deleteOpen && (
|
{deleteOpen && (
|
||||||
<DeleteBucket
|
<DeleteBucket
|
||||||
deleteOpen={deleteOpen}
|
deleteOpen={deleteOpen}
|
||||||
selectedBucket={selectedBucket}
|
selectedBucket={selectedBucket}
|
||||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||||
this.closeDeleteModalAndRefresh(refresh);
|
closeDeleteModalAndRefresh(refresh);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Grid container>
|
<PageHeader label={"Buckets"} />
|
||||||
<Grid item xs={12}>
|
<Grid container>
|
||||||
<Typography variant="h6">Buckets</Typography>
|
<Grid item xs={12} className={classes.container}>
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<br />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Search Buckets"
|
placeholder="Search Buckets"
|
||||||
@@ -251,9 +234,7 @@ class ListBuckets extends React.Component<
|
|||||||
id="search-resource"
|
id="search-resource"
|
||||||
label=""
|
label=""
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
this.setState({
|
setFilterBuckets(val.target.value);
|
||||||
filterBuckets: val.target.value,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
disableUnderline: true,
|
disableUnderline: true,
|
||||||
@@ -269,9 +250,7 @@ class ListBuckets extends React.Component<
|
|||||||
color="primary"
|
color="primary"
|
||||||
startIcon={<CreateIcon />}
|
startIcon={<CreateIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.setState({
|
addBucketOpen(true);
|
||||||
addScreenOpen: true,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Create Bucket
|
Create Bucket
|
||||||
@@ -317,9 +296,18 @@ class ListBuckets extends React.Component<
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</Grid>
|
||||||
);
|
</React.Fragment>
|
||||||
}
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default withStyles(styles)(ListBuckets);
|
const mapState = (state: AppState) => ({
|
||||||
|
addBucketModalOpen: state.buckets.open,
|
||||||
|
});
|
||||||
|
|
||||||
|
const connector = connect(mapState, {
|
||||||
|
addBucketOpen: addBucketOpen,
|
||||||
|
addBucketReset: addBucketReset,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connector(withStyles(styles)(ListBuckets));
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
// 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 { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
|
LinearProgress,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import api from "../../../../../../common/api";
|
||||||
|
import { BucketObjectsList } from "../ListObjects/types";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
errorBlock: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IDeleteObjectProps {
|
||||||
|
classes: any;
|
||||||
|
closeDeleteModalAndRefresh: (refresh: boolean) => void;
|
||||||
|
deleteOpen: boolean;
|
||||||
|
selectedObject: string;
|
||||||
|
selectedBucket: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IDeleteObjectState {
|
||||||
|
deleteLoading: boolean;
|
||||||
|
deleteError: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeleteObject extends React.Component<
|
||||||
|
IDeleteObjectProps,
|
||||||
|
IDeleteObjectState
|
||||||
|
> {
|
||||||
|
state: IDeleteObjectState = {
|
||||||
|
deleteLoading: false,
|
||||||
|
deleteError: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
removeRecord() {
|
||||||
|
const { deleteLoading } = this.state;
|
||||||
|
const { selectedObject, selectedBucket } = this.props;
|
||||||
|
if (deleteLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var recursive = false;
|
||||||
|
if (selectedObject.endsWith("/")) {
|
||||||
|
recursive = true;
|
||||||
|
}
|
||||||
|
this.setState({ deleteLoading: true }, () => {
|
||||||
|
api
|
||||||
|
.invoke(
|
||||||
|
"DELETE",
|
||||||
|
`/api/v1/buckets/${selectedBucket}/objects?path=${selectedObject}&recursive=${recursive}`
|
||||||
|
)
|
||||||
|
.then((res: BucketObjectsList) => {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
deleteLoading: false,
|
||||||
|
deleteError: "",
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.props.closeDeleteModalAndRefresh(true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.setState({
|
||||||
|
deleteLoading: false,
|
||||||
|
deleteError: err,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { classes, deleteOpen, selectedObject } = this.props;
|
||||||
|
const { deleteLoading, deleteError } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={deleteOpen}
|
||||||
|
onClose={() => {
|
||||||
|
this.setState({ deleteError: "" }, () => {
|
||||||
|
this.props.closeDeleteModalAndRefresh(false);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
>
|
||||||
|
<DialogTitle id="alert-dialog-title">Delete</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
{deleteLoading && <LinearProgress />}
|
||||||
|
<DialogContentText id="alert-dialog-description">
|
||||||
|
Are you sure you want to delete: <b>{selectedObject}</b>?{" "}
|
||||||
|
{deleteError !== "" && (
|
||||||
|
<React.Fragment>
|
||||||
|
<br />
|
||||||
|
<Typography
|
||||||
|
component="p"
|
||||||
|
variant="body1"
|
||||||
|
className={classes.errorBlock}
|
||||||
|
>
|
||||||
|
{deleteError}
|
||||||
|
</Typography>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({ deleteError: "" }, () => {
|
||||||
|
this.props.closeDeleteModalAndRefresh(false);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
disabled={deleteLoading}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
this.removeRecord();
|
||||||
|
}}
|
||||||
|
color="secondary"
|
||||||
|
autoFocus
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(DeleteObject);
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
// 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 { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
import { Button } from "@material-ui/core";
|
||||||
|
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 { BucketObject, BucketObjectsList } from "./types";
|
||||||
|
import api from "../../../../../../common/api";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import TableWrapper from "../../../../Common/TableWrapper/TableWrapper";
|
||||||
|
import { MinTablePaginationActions } from "../../../../../../common/MinTablePaginationActions";
|
||||||
|
import { CreateIcon } from "../../.././../../../icons";
|
||||||
|
import { niceBytes } from "../../../../../../common/utils";
|
||||||
|
import Moment from "react-moment";
|
||||||
|
import DeleteObject from "./DeleteObject";
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
seeMore: {
|
||||||
|
marginTop: theme.spacing(3),
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
display: "flex",
|
||||||
|
overflow: "auto",
|
||||||
|
flexDirection: "column",
|
||||||
|
},
|
||||||
|
|
||||||
|
addSideBar: {
|
||||||
|
width: "320px",
|
||||||
|
padding: "20px",
|
||||||
|
},
|
||||||
|
errorBlock: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
tableToolbar: {
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
paddingRight: theme.spacing(0),
|
||||||
|
},
|
||||||
|
minTableHeader: {
|
||||||
|
color: "#393939",
|
||||||
|
"& tr": {
|
||||||
|
"& th": {
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionsTray: {
|
||||||
|
textAlign: "right",
|
||||||
|
"& button": {
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
searchField: {
|
||||||
|
background: "#FFFFFF",
|
||||||
|
padding: 12,
|
||||||
|
borderRadius: 5,
|
||||||
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IListObjectsProps {
|
||||||
|
classes: any;
|
||||||
|
match: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IListObjectsState {
|
||||||
|
records: BucketObject[];
|
||||||
|
totalRecords: number;
|
||||||
|
loading: boolean;
|
||||||
|
error: string;
|
||||||
|
deleteOpen: boolean;
|
||||||
|
deleteError: string;
|
||||||
|
selectedObject: string;
|
||||||
|
selectedBucket: string;
|
||||||
|
filterObjects: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ListObjects extends React.Component<
|
||||||
|
IListObjectsProps,
|
||||||
|
IListObjectsState
|
||||||
|
> {
|
||||||
|
state: IListObjectsState = {
|
||||||
|
records: [],
|
||||||
|
totalRecords: 0,
|
||||||
|
loading: false,
|
||||||
|
error: "",
|
||||||
|
deleteOpen: false,
|
||||||
|
deleteError: "",
|
||||||
|
selectedObject: "",
|
||||||
|
selectedBucket: "",
|
||||||
|
filterObjects: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchRecords = () => {
|
||||||
|
this.setState({ loading: true }, () => {
|
||||||
|
const { match } = this.props;
|
||||||
|
const bucketName = match.params["bucket"];
|
||||||
|
api
|
||||||
|
.invoke("GET", `/api/v1/buckets/${bucketName}/objects`)
|
||||||
|
.then((res: BucketObjectsList) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
selectedBucket: bucketName,
|
||||||
|
records: res.objects || [],
|
||||||
|
totalRecords: !res.objects ? 0 : res.total,
|
||||||
|
error: "",
|
||||||
|
});
|
||||||
|
// TODO:
|
||||||
|
// if we get 0 results, and page > 0 , go down 1 page
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
this.setState({ loading: false, error: err });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount(): void {
|
||||||
|
this.fetchRecords();
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDeleteModalAndRefresh(refresh: boolean) {
|
||||||
|
this.setState({ deleteOpen: false }, () => {
|
||||||
|
if (refresh) {
|
||||||
|
this.fetchRecords();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bucketFilter(): void {}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { classes } = this.props;
|
||||||
|
const {
|
||||||
|
records,
|
||||||
|
loading,
|
||||||
|
selectedObject,
|
||||||
|
selectedBucket,
|
||||||
|
deleteOpen,
|
||||||
|
filterObjects,
|
||||||
|
} = this.state;
|
||||||
|
const displayParsedDate = (date: string) => {
|
||||||
|
return <Moment>{date}</Moment>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmDeleteObject = (object: string) => {
|
||||||
|
this.setState({ deleteOpen: true, selectedObject: object });
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableActions = [
|
||||||
|
{ type: "delete", onClick: confirmDeleteObject, sendOnlyId: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const filteredRecords = records.filter((b: BucketObject) => {
|
||||||
|
if (filterObjects === "") {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (b.name.indexOf(filterObjects) >= 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{deleteOpen && (
|
||||||
|
<DeleteObject
|
||||||
|
deleteOpen={deleteOpen}
|
||||||
|
selectedBucket={selectedBucket}
|
||||||
|
selectedObject={selectedObject}
|
||||||
|
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||||
|
this.closeDeleteModalAndRefresh(refresh);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography variant="h6">Objects</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<br />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
|
<TextField
|
||||||
|
placeholder="Search Objects"
|
||||||
|
className={classes.searchField}
|
||||||
|
id="search-resource"
|
||||||
|
label=""
|
||||||
|
onChange={(val) => {
|
||||||
|
this.setState({
|
||||||
|
filterObjects: val.target.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
disableUnderline: true,
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<br />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<TableWrapper
|
||||||
|
itemActions={tableActions}
|
||||||
|
columns={[
|
||||||
|
{ label: "Name", elementKey: "name" },
|
||||||
|
{
|
||||||
|
label: "Last Modified",
|
||||||
|
elementKey: "last_modified",
|
||||||
|
renderFunction: displayParsedDate,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Size",
|
||||||
|
elementKey: "size",
|
||||||
|
renderFunction: niceBytes,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
isLoading={loading}
|
||||||
|
entityName="Objects"
|
||||||
|
idField="name"
|
||||||
|
records={filteredRecords}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withStyles(styles)(ListObjects);
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// 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 interface BucketObject {
|
||||||
|
name: string;
|
||||||
|
size: number;
|
||||||
|
last_modified: Date;
|
||||||
|
content_type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BucketObjectsList {
|
||||||
|
objects: BucketObject[];
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,212 @@
|
|||||||
|
// 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, { useState, useEffect } from "react";
|
||||||
|
import get from "lodash/get";
|
||||||
|
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||||
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
|
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||||
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
|
import api from "../../../../common/api";
|
||||||
|
import {
|
||||||
|
IRemoteBucket,
|
||||||
|
IRemoteBucketsResponse,
|
||||||
|
} from "../../RemoteBuckets/types";
|
||||||
|
import RemoteBucketsList from "../../RemoteBuckets/RemoteBuckets";
|
||||||
|
|
||||||
|
interface IReplicationModal {
|
||||||
|
open: boolean;
|
||||||
|
closeModalAndRefresh: () => any;
|
||||||
|
classes: any;
|
||||||
|
bucketName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
errorBlock: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
minTableHeader: {
|
||||||
|
color: "#393939",
|
||||||
|
"& tr": {
|
||||||
|
"& th": {
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
textAlign: "right",
|
||||||
|
},
|
||||||
|
...modalBasic,
|
||||||
|
});
|
||||||
|
|
||||||
|
const AddReplicationModal = ({
|
||||||
|
open,
|
||||||
|
closeModalAndRefresh,
|
||||||
|
classes,
|
||||||
|
bucketName,
|
||||||
|
}: IReplicationModal) => {
|
||||||
|
const [addError, setAddError] = useState("");
|
||||||
|
const [loadingForm, setLoadingForm] = useState(true);
|
||||||
|
const [addLoading, setAddLoading] = useState(false);
|
||||||
|
const [remoteURL, setRemoteURL] = useState("");
|
||||||
|
const [source, setSource] = useState("");
|
||||||
|
const [target, setTarget] = useState("");
|
||||||
|
const [ARN, setARN] = useState("");
|
||||||
|
const [arnValues, setARNValues] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (addLoading) {
|
||||||
|
addRecord();
|
||||||
|
}
|
||||||
|
}, [addLoading]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (loadingForm) {
|
||||||
|
getARNValues();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const addRecord = () => {
|
||||||
|
const replicationInfo = {
|
||||||
|
destination_bucket: target,
|
||||||
|
arn: ARN,
|
||||||
|
};
|
||||||
|
|
||||||
|
api
|
||||||
|
.invoke(
|
||||||
|
"POST",
|
||||||
|
`/api/v1/buckets/${bucketName}/replication`,
|
||||||
|
replicationInfo
|
||||||
|
)
|
||||||
|
.then((res) => {
|
||||||
|
setAddLoading(false);
|
||||||
|
setAddError("");
|
||||||
|
closeModalAndRefresh();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setAddLoading(false);
|
||||||
|
setAddError(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getARNValues = () => {
|
||||||
|
api
|
||||||
|
.invoke("GET", "/api/v1/remote-buckets")
|
||||||
|
.then((res: any) => {
|
||||||
|
const remoteBuckets = get(res, "buckets", []);
|
||||||
|
|
||||||
|
const remoteARNS = remoteBuckets.map((itemRemote: IRemoteBucket) => {
|
||||||
|
return { label: itemRemote.remoteARN, value: itemRemote.remoteARN };
|
||||||
|
});
|
||||||
|
setLoadingForm(false);
|
||||||
|
setARNValues(remoteARNS);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setLoadingForm(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalWrapper
|
||||||
|
modalOpen={open}
|
||||||
|
onClose={() => {
|
||||||
|
setAddError("");
|
||||||
|
closeModalAndRefresh();
|
||||||
|
}}
|
||||||
|
title="Set Bucket Replication"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
noValidate
|
||||||
|
autoComplete="off"
|
||||||
|
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setAddLoading(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loadingForm && (
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<LinearProgress />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
{!loadingForm && (
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12} className={classes.formScrollable}>
|
||||||
|
{addError !== "" && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography
|
||||||
|
component="p"
|
||||||
|
variant="body1"
|
||||||
|
className={classes.errorBlock}
|
||||||
|
>
|
||||||
|
{addError}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="target"
|
||||||
|
name="target"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setTarget(e.target.value);
|
||||||
|
}}
|
||||||
|
label="Destination Bucket"
|
||||||
|
value={target}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<SelectWrapper
|
||||||
|
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||||
|
setARN(e.target.value as string);
|
||||||
|
}}
|
||||||
|
id="arn"
|
||||||
|
name="arn"
|
||||||
|
label={"ARN"}
|
||||||
|
value={ARN}
|
||||||
|
options={arnValues}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.buttonContainer}>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={addLoading}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
{addLoading && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<LinearProgress />
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</ModalWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(AddReplicationModal);
|
||||||
@@ -23,8 +23,18 @@ import Tabs from "@material-ui/core/Tabs";
|
|||||||
import Tab from "@material-ui/core/Tab";
|
import Tab from "@material-ui/core/Tab";
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
import api from "../../../../common/api";
|
import api from "../../../../common/api";
|
||||||
import { BucketEvent, BucketEventList, BucketInfo, BucketList } from "../types";
|
import {
|
||||||
import { Button } from "@material-ui/core";
|
BucketEvent,
|
||||||
|
BucketEventList,
|
||||||
|
BucketInfo,
|
||||||
|
BucketList,
|
||||||
|
BucketReplication,
|
||||||
|
BucketReplicationDestination,
|
||||||
|
BucketReplicationRule,
|
||||||
|
BucketReplicationRuleDeleteMarker,
|
||||||
|
BucketVersioning,
|
||||||
|
} from "../types";
|
||||||
|
import { Box, Button } from "@material-ui/core";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import SetAccessPolicy from "./SetAccessPolicy";
|
import SetAccessPolicy from "./SetAccessPolicy";
|
||||||
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
||||||
@@ -33,6 +43,9 @@ import AddEvent from "./AddEvent";
|
|||||||
import DeleteEvent from "./DeleteEvent";
|
import DeleteEvent from "./DeleteEvent";
|
||||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||||
import { niceBytes } from "../../../../common/utils";
|
import { niceBytes } from "../../../../common/utils";
|
||||||
|
import AddReplicationModal from "./AddReplicationModal";
|
||||||
|
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -117,6 +130,14 @@ const styles = (theme: Theme) =>
|
|||||||
capitalizeFirst: {
|
capitalizeFirst: {
|
||||||
textTransform: "capitalize",
|
textTransform: "capitalize",
|
||||||
},
|
},
|
||||||
|
doubleElement: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
},
|
||||||
|
tabPan: {
|
||||||
|
marginTop: "5px",
|
||||||
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IViewBucketProps {
|
interface IViewBucketProps {
|
||||||
@@ -124,9 +145,40 @@ interface IViewBucketProps {
|
|||||||
match: any;
|
match: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TabPanelProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
index: any;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TabPanel(props: TabPanelProps) {
|
||||||
|
const { children, value, index, ...other } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="tabpanel"
|
||||||
|
hidden={value !== index}
|
||||||
|
id={`simple-tabpanel-${index}`}
|
||||||
|
aria-labelledby={`simple-tab-${index}`}
|
||||||
|
style={{ marginTop: "5px" }}
|
||||||
|
{...other}
|
||||||
|
>
|
||||||
|
{value === index && <React.Fragment>{children}</React.Fragment>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function a11yProps(index: any) {
|
||||||
|
return {
|
||||||
|
id: `simple-tab-${index}`,
|
||||||
|
"aria-controls": `simple-tabpanel-${index}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface IViewBucketState {
|
interface IViewBucketState {
|
||||||
info: BucketInfo | null;
|
info: BucketInfo | null;
|
||||||
records: BucketEvent[];
|
records: BucketEvent[];
|
||||||
|
replicationRules: BucketReplicationRule[];
|
||||||
totalRecords: number;
|
totalRecords: number;
|
||||||
loadingBucket: boolean;
|
loadingBucket: boolean;
|
||||||
loadingEvents: boolean;
|
loadingEvents: boolean;
|
||||||
@@ -137,18 +189,23 @@ interface IViewBucketState {
|
|||||||
setAccessPolicyScreenOpen: boolean;
|
setAccessPolicyScreenOpen: boolean;
|
||||||
page: number;
|
page: number;
|
||||||
rowsPerPage: number;
|
rowsPerPage: number;
|
||||||
|
curTab: number;
|
||||||
addScreenOpen: boolean;
|
addScreenOpen: boolean;
|
||||||
deleteOpen: boolean;
|
deleteOpen: boolean;
|
||||||
selectedBucket: string;
|
selectedBucket: string;
|
||||||
selectedEvent: BucketEvent | null;
|
selectedEvent: BucketEvent | null;
|
||||||
bucketSize: string;
|
bucketSize: string;
|
||||||
errorSize: string;
|
errorSize: string;
|
||||||
|
replicationSet: boolean;
|
||||||
|
openSetReplication: boolean;
|
||||||
|
isVersioned: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||||
state: IViewBucketState = {
|
state: IViewBucketState = {
|
||||||
info: null,
|
info: null,
|
||||||
records: [],
|
records: [],
|
||||||
|
replicationRules: [],
|
||||||
totalRecords: 0,
|
totalRecords: 0,
|
||||||
loadingBucket: true,
|
loadingBucket: true,
|
||||||
loadingEvents: true,
|
loadingEvents: true,
|
||||||
@@ -158,6 +215,7 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
errBucket: "",
|
errBucket: "",
|
||||||
setAccessPolicyScreenOpen: false,
|
setAccessPolicyScreenOpen: false,
|
||||||
page: 0,
|
page: 0,
|
||||||
|
curTab: 0,
|
||||||
rowsPerPage: 10,
|
rowsPerPage: 10,
|
||||||
addScreenOpen: false,
|
addScreenOpen: false,
|
||||||
deleteOpen: false,
|
deleteOpen: false,
|
||||||
@@ -165,6 +223,9 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
selectedEvent: null,
|
selectedEvent: null,
|
||||||
bucketSize: "0",
|
bucketSize: "0",
|
||||||
errorSize: "",
|
errorSize: "",
|
||||||
|
replicationSet: false,
|
||||||
|
openSetReplication: false,
|
||||||
|
isVersioned: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchEvents() {
|
fetchEvents() {
|
||||||
@@ -195,6 +256,29 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
.catch((err: any) => {
|
.catch((err: any) => {
|
||||||
this.setState({ loadingEvents: false, error: err });
|
this.setState({ loadingEvents: false, error: err });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
api
|
||||||
|
.invoke("GET", `/api/v1/buckets/${bucketName}/versioning`)
|
||||||
|
.then((res: BucketVersioning) => {
|
||||||
|
this.setState({
|
||||||
|
isVersioned: res.is_versioned,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
this.setState({ error: err });
|
||||||
|
});
|
||||||
|
|
||||||
|
api
|
||||||
|
.invoke("GET", `/api/v1/buckets/${bucketName}/replication`)
|
||||||
|
.then((res: BucketReplication) => {
|
||||||
|
const r = res.rules ? res.rules : [];
|
||||||
|
this.setState({
|
||||||
|
replicationRules: r,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
this.setState({ error: err });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +363,11 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
selectedEvent,
|
selectedEvent,
|
||||||
bucketSize,
|
bucketSize,
|
||||||
loadingSize,
|
loadingSize,
|
||||||
|
replicationSet,
|
||||||
|
openSetReplication,
|
||||||
|
isVersioned,
|
||||||
|
replicationRules,
|
||||||
|
curTab,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const offset = page * rowsPerPage;
|
const offset = page * rowsPerPage;
|
||||||
@@ -301,6 +390,7 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let accessPolicy = "n/a";
|
let accessPolicy = "n/a";
|
||||||
|
|
||||||
if (info !== null) {
|
if (info !== null) {
|
||||||
accessPolicy = info.access;
|
accessPolicy = info.access;
|
||||||
}
|
}
|
||||||
@@ -309,9 +399,26 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
return <React.Fragment>{events.join(", ")}</React.Fragment>;
|
return <React.Fragment>{events.join(", ")}</React.Fragment>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ruleDestDisplay = (events: BucketReplicationDestination) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{events.bucket.replace("arn:aws:s3:::", "")}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ruleDelDisplay = (events: BucketReplicationRuleDeleteMarker) => {
|
||||||
|
return <React.Fragment>{events.status}</React.Fragment>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setOpenReplicationOpen = (open = false) => {
|
||||||
|
this.setState({ openSetReplication: open });
|
||||||
|
};
|
||||||
|
|
||||||
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
|
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
|
||||||
|
|
||||||
const filteredRecords = records.slice(offset, offset + rowsPerPage);
|
const filteredRecords = records.slice(offset, offset + rowsPerPage);
|
||||||
|
const filteredRules = replicationRules.slice(offset, offset + rowsPerPage);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@@ -335,130 +442,201 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{openSetReplication && (
|
||||||
|
<AddReplicationModal
|
||||||
|
closeModalAndRefresh={() => {
|
||||||
|
setOpenReplicationOpen(false);
|
||||||
|
this.fetchEvents();
|
||||||
|
}}
|
||||||
|
open={openSetReplication}
|
||||||
|
bucketName={bucketName}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<PageHeader label={`Bucket > ${match.params["bucketName"]}`} />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Typography variant="h6">
|
<Grid item xs={12}>
|
||||||
Bucket > {match.params["bucketName"]}
|
<div className={classes.headerContainer}>
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<br />
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<div className={classes.headerContainer}>
|
|
||||||
<div>
|
|
||||||
<Paper className={classes.paperContainer}>
|
|
||||||
<div className={classes.gridContainer}>
|
|
||||||
<div>Access Policy:</div>
|
|
||||||
<div className={classes.capitalizeFirst}>
|
|
||||||
{loadingBucket ? (
|
|
||||||
<CircularProgress
|
|
||||||
color="primary"
|
|
||||||
size={16}
|
|
||||||
variant="indeterminate"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
accessPolicy.toLowerCase()
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>Reported Usage:</div>
|
|
||||||
<div>
|
|
||||||
{loadingSize ? (
|
|
||||||
<CircularProgress
|
|
||||||
color="primary"
|
|
||||||
size={16}
|
|
||||||
variant="indeterminate"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
niceBytes(bucketSize)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Paper>
|
|
||||||
</div>
|
|
||||||
<div className={classes.masterActions}>
|
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Paper className={classes.paperContainer}>
|
||||||
variant="contained"
|
<div className={classes.gridContainer}>
|
||||||
color="primary"
|
<div>Access Policy:</div>
|
||||||
fullWidth
|
<div className={classes.capitalizeFirst}>
|
||||||
size="medium"
|
{loadingBucket ? (
|
||||||
onClick={() => {
|
<CircularProgress
|
||||||
this.setState({
|
color="primary"
|
||||||
setAccessPolicyScreenOpen: true,
|
size={16}
|
||||||
});
|
variant="indeterminate"
|
||||||
}}
|
/>
|
||||||
>
|
) : (
|
||||||
Change Access Policy
|
accessPolicy.toLowerCase()
|
||||||
</Button>
|
)}
|
||||||
|
</div>
|
||||||
|
<div>Reported Usage:</div>
|
||||||
|
<div>
|
||||||
|
{loadingSize ? (
|
||||||
|
<CircularProgress
|
||||||
|
color="primary"
|
||||||
|
size={16}
|
||||||
|
variant="indeterminate"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
niceBytes(bucketSize)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div>Replication:</div>
|
||||||
|
<div className={classes.doubleElement}>
|
||||||
|
<span>{replicationRules.length ? "Yes" : "No"}</span>
|
||||||
|
</div>
|
||||||
|
<div>Versioning:</div>
|
||||||
|
<div>{isVersioned ? "Yes" : "No"} </div>
|
||||||
|
</div>
|
||||||
|
</Paper>
|
||||||
|
</div>
|
||||||
|
<div className={classes.masterActions}>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
fullWidth
|
||||||
|
size="medium"
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({
|
||||||
|
setAccessPolicyScreenOpen: true,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Change Access Policy
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Grid>
|
||||||
</Grid>
|
<Grid item xs={12}>
|
||||||
<Grid item xs={12}>
|
<br />
|
||||||
<br />
|
</Grid>
|
||||||
</Grid>
|
<Grid item xs={6}>
|
||||||
<Grid item xs={6}>
|
<Tabs
|
||||||
<Tabs
|
value={curTab}
|
||||||
value={0}
|
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
|
||||||
indicatorColor="primary"
|
this.setState({ curTab: newValue });
|
||||||
textColor="primary"
|
}}
|
||||||
aria-label="cluster-tabs"
|
indicatorColor="primary"
|
||||||
>
|
textColor="primary"
|
||||||
<Tab label="Events" />
|
aria-label="cluster-tabs"
|
||||||
</Tabs>
|
>
|
||||||
</Grid>
|
<Tab label="Events" {...a11yProps(0)} />
|
||||||
<Grid item xs={6} className={classes.actionsTray}>
|
<Tab label="Replication" {...a11yProps(1)} />
|
||||||
<Button
|
</Tabs>
|
||||||
variant="contained"
|
</Grid>
|
||||||
color="primary"
|
<Grid item xs={6} className={classes.actionsTray}>
|
||||||
startIcon={<CreateIcon />}
|
{curTab === 0 && (
|
||||||
size="medium"
|
<Button
|
||||||
onClick={() => {
|
variant="contained"
|
||||||
this.setState({
|
color="primary"
|
||||||
addScreenOpen: true,
|
startIcon={<CreateIcon />}
|
||||||
});
|
size="medium"
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
this.setState({
|
||||||
Subcribe to Event
|
addScreenOpen: true,
|
||||||
</Button>
|
});
|
||||||
</Grid>
|
}}
|
||||||
<Grid item xs={12}>
|
>
|
||||||
<br />
|
Subscribe to Event
|
||||||
</Grid>
|
</Button>
|
||||||
<Grid item xs={12}>
|
)}
|
||||||
<TableWrapper
|
{curTab === 1 && (
|
||||||
itemActions={tableActions}
|
<Button
|
||||||
columns={[
|
variant="contained"
|
||||||
{ label: "SQS", elementKey: "arn" },
|
color="primary"
|
||||||
{
|
startIcon={<CreateIcon />}
|
||||||
label: "Events",
|
size="medium"
|
||||||
elementKey: "events",
|
onClick={() => {
|
||||||
renderFunction: eventsDisplay,
|
this.setState({
|
||||||
},
|
openSetReplication: true,
|
||||||
{ label: "Prefix", elementKey: "prefix" },
|
});
|
||||||
{ label: "Suffix", elementKey: "suffix" },
|
}}
|
||||||
]}
|
>
|
||||||
isLoading={loadingEvents}
|
Add Replication Rule
|
||||||
records={filteredRecords}
|
</Button>
|
||||||
entityName="Events"
|
)}
|
||||||
idField="id"
|
</Grid>
|
||||||
paginatorConfig={{
|
<Grid item xs={12}>
|
||||||
rowsPerPageOptions: [5, 10, 25],
|
<TabPanel index={0} value={curTab}>
|
||||||
colSpan: 3,
|
<TableWrapper
|
||||||
count: totalRecords,
|
itemActions={tableActions}
|
||||||
rowsPerPage: rowsPerPage,
|
columns={[
|
||||||
page: page,
|
{ label: "SQS", elementKey: "arn" },
|
||||||
SelectProps: {
|
{
|
||||||
inputProps: { "aria-label": "rows per page" },
|
label: "Events",
|
||||||
native: true,
|
elementKey: "events",
|
||||||
},
|
renderFunction: eventsDisplay,
|
||||||
onChangePage: handleChangePage,
|
},
|
||||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
{ label: "Prefix", elementKey: "prefix" },
|
||||||
ActionsComponent: MinTablePaginationActions,
|
{ label: "Suffix", elementKey: "suffix" },
|
||||||
}}
|
]}
|
||||||
/>
|
isLoading={loadingEvents}
|
||||||
|
records={filteredRecords}
|
||||||
|
entityName="Events"
|
||||||
|
idField="id"
|
||||||
|
paginatorConfig={{
|
||||||
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
|
colSpan: 3,
|
||||||
|
count: totalRecords,
|
||||||
|
rowsPerPage: rowsPerPage,
|
||||||
|
page: page,
|
||||||
|
SelectProps: {
|
||||||
|
inputProps: { "aria-label": "rows per page" },
|
||||||
|
native: true,
|
||||||
|
},
|
||||||
|
onChangePage: handleChangePage,
|
||||||
|
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||||
|
ActionsComponent: MinTablePaginationActions,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel index={1} value={curTab}>
|
||||||
|
<TableWrapper
|
||||||
|
itemActions={tableActions}
|
||||||
|
columns={[
|
||||||
|
{ label: "ID", elementKey: "id" },
|
||||||
|
{
|
||||||
|
label: "Priority",
|
||||||
|
elementKey: "priority",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Destination",
|
||||||
|
elementKey: "destination",
|
||||||
|
renderFunction: ruleDestDisplay,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Delete Replication",
|
||||||
|
elementKey: "delete_marker_replication",
|
||||||
|
renderFunction: ruleDelDisplay,
|
||||||
|
},
|
||||||
|
{ label: "Status", elementKey: "status" },
|
||||||
|
]}
|
||||||
|
isLoading={loadingEvents}
|
||||||
|
records={filteredRules}
|
||||||
|
entityName="Replication Rules"
|
||||||
|
idField="id"
|
||||||
|
paginatorConfig={{
|
||||||
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
|
colSpan: 3,
|
||||||
|
count: totalRecords,
|
||||||
|
rowsPerPage: rowsPerPage,
|
||||||
|
page: page,
|
||||||
|
SelectProps: {
|
||||||
|
inputProps: { "aria-label": "rows per page" },
|
||||||
|
native: true,
|
||||||
|
},
|
||||||
|
onChangePage: handleChangePage,
|
||||||
|
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||||
|
ActionsComponent: MinTablePaginationActions,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
126
portal-ui/src/screens/Console/Buckets/actions.ts
Normal file
126
portal-ui/src/screens/Console/Buckets/actions.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// 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 ADD_BUCKET_OPEN = "ADD_BUCKET_OPEN";
|
||||||
|
export const ADD_BUCKET_NAME = "ADD_BUCKET_NAME";
|
||||||
|
export const ADD_BUCKET_VERSIONED = "ADD_BUCKET_VERSIONED";
|
||||||
|
export const ADD_BUCKET_QUOTA = "ADD_BUCKET_QUOTA";
|
||||||
|
export const ADD_BUCKET_QUOTA_TYPE = "ADD_BUCKET_QUOTA_TYPE";
|
||||||
|
export const ADD_BUCKET_QUOTA_SIZE = "ADD_BUCKET_QUOTA_SIZE";
|
||||||
|
export const ADD_BUCKET_QUOTA_UNIT = "ADD_BUCKET_QUOTA_UNIT";
|
||||||
|
export const ADD_BUCKET_RESET = "ADD_BUCKET_RESET";
|
||||||
|
|
||||||
|
interface AddBucketOpenAction {
|
||||||
|
type: typeof ADD_BUCKET_OPEN;
|
||||||
|
open: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddBucketNameAction {
|
||||||
|
type: typeof ADD_BUCKET_NAME;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddBucketVersionedAction {
|
||||||
|
type: typeof ADD_BUCKET_VERSIONED;
|
||||||
|
versioned: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddBucketQuotaAction {
|
||||||
|
type: typeof ADD_BUCKET_QUOTA;
|
||||||
|
quota: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddBucketQuotaTypeAction {
|
||||||
|
type: typeof ADD_BUCKET_QUOTA_TYPE;
|
||||||
|
quotaType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddBucketQuotaSizeAction {
|
||||||
|
type: typeof ADD_BUCKET_QUOTA_SIZE;
|
||||||
|
quotaSize: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddBucketQuotaUnitAction {
|
||||||
|
type: typeof ADD_BUCKET_QUOTA_UNIT;
|
||||||
|
quotaUnit: string;
|
||||||
|
}
|
||||||
|
interface AddBucketResetAction {
|
||||||
|
type: typeof ADD_BUCKET_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BucketActionTypes =
|
||||||
|
| AddBucketOpenAction
|
||||||
|
| AddBucketNameAction
|
||||||
|
| AddBucketVersionedAction
|
||||||
|
| AddBucketQuotaAction
|
||||||
|
| AddBucketQuotaTypeAction
|
||||||
|
| AddBucketQuotaSizeAction
|
||||||
|
| AddBucketQuotaUnitAction
|
||||||
|
| AddBucketResetAction;
|
||||||
|
|
||||||
|
export function addBucketOpen(open: boolean) {
|
||||||
|
return {
|
||||||
|
type: ADD_BUCKET_OPEN,
|
||||||
|
open: open,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function addBucketName(name: string) {
|
||||||
|
return {
|
||||||
|
type: ADD_BUCKET_NAME,
|
||||||
|
name: name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addBucketVersioned(versioned: boolean) {
|
||||||
|
return {
|
||||||
|
type: ADD_BUCKET_VERSIONED,
|
||||||
|
versioned: versioned,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addBucketQuota(quota: boolean) {
|
||||||
|
return {
|
||||||
|
type: ADD_BUCKET_QUOTA,
|
||||||
|
quota: quota,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addBucketQuotaType(quotaType: string) {
|
||||||
|
return {
|
||||||
|
type: ADD_BUCKET_QUOTA_TYPE,
|
||||||
|
quotaType: quotaType,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addBucketQuotaSize(quotaSize: string) {
|
||||||
|
return {
|
||||||
|
type: ADD_BUCKET_QUOTA_SIZE,
|
||||||
|
quotaSize: quotaSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addBucketQuotaUnit(quotaUnit: string) {
|
||||||
|
return {
|
||||||
|
type: ADD_BUCKET_QUOTA_UNIT,
|
||||||
|
quotaUnit: quotaUnit,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addBucketReset() {
|
||||||
|
return {
|
||||||
|
type: ADD_BUCKET_RESET,
|
||||||
|
};
|
||||||
|
}
|
||||||
102
portal-ui/src/screens/Console/Buckets/reducers.ts
Normal file
102
portal-ui/src/screens/Console/Buckets/reducers.ts
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// 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 {
|
||||||
|
ADD_BUCKET_NAME,
|
||||||
|
ADD_BUCKET_OPEN,
|
||||||
|
ADD_BUCKET_QUOTA,
|
||||||
|
ADD_BUCKET_QUOTA_SIZE,
|
||||||
|
ADD_BUCKET_QUOTA_TYPE,
|
||||||
|
ADD_BUCKET_QUOTA_UNIT,
|
||||||
|
ADD_BUCKET_RESET,
|
||||||
|
ADD_BUCKET_VERSIONED,
|
||||||
|
BucketActionTypes,
|
||||||
|
} from "./actions";
|
||||||
|
|
||||||
|
export interface BucketsState {
|
||||||
|
open: boolean;
|
||||||
|
addBucketName: string;
|
||||||
|
addBucketVersioning: boolean;
|
||||||
|
addBucketQuotaEnabled: boolean;
|
||||||
|
addBucketQuotaType: string;
|
||||||
|
addBucketQuotaSize: string;
|
||||||
|
addBucketQuotaUnit: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: BucketsState = {
|
||||||
|
open: false,
|
||||||
|
addBucketName: "",
|
||||||
|
addBucketVersioning: false,
|
||||||
|
addBucketQuotaEnabled: false,
|
||||||
|
addBucketQuotaType: "hard",
|
||||||
|
addBucketQuotaSize: "1",
|
||||||
|
addBucketQuotaUnit: "TiB",
|
||||||
|
};
|
||||||
|
|
||||||
|
export function bucketsReducer(
|
||||||
|
state = initialState,
|
||||||
|
action: BucketActionTypes
|
||||||
|
): BucketsState {
|
||||||
|
switch (action.type) {
|
||||||
|
case ADD_BUCKET_OPEN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
open: action.open,
|
||||||
|
};
|
||||||
|
case ADD_BUCKET_NAME:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
addBucketName: action.name,
|
||||||
|
};
|
||||||
|
case ADD_BUCKET_VERSIONED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
addBucketVersioning: action.versioned,
|
||||||
|
};
|
||||||
|
case ADD_BUCKET_QUOTA:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
addBucketQuotaEnabled: action.quota,
|
||||||
|
};
|
||||||
|
case ADD_BUCKET_QUOTA_TYPE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
addBucketQuotaType: action.quotaType,
|
||||||
|
};
|
||||||
|
case ADD_BUCKET_QUOTA_SIZE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
addBucketQuotaSize: action.quotaSize,
|
||||||
|
};
|
||||||
|
case ADD_BUCKET_QUOTA_UNIT:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
addBucketQuotaUnit: action.quotaUnit,
|
||||||
|
};
|
||||||
|
case ADD_BUCKET_RESET:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
addBucketName: "",
|
||||||
|
addBucketVersioning: false,
|
||||||
|
addBucketQuotaEnabled: false,
|
||||||
|
addBucketQuotaType: "hard",
|
||||||
|
addBucketQuotaSize: "1",
|
||||||
|
addBucketQuotaUnit: "TiB",
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,3 +45,37 @@ export interface BucketEventList {
|
|||||||
export interface ArnList {
|
export interface ArnList {
|
||||||
arns: string[];
|
arns: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BucketVersioning {
|
||||||
|
is_versioned: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BucketReplicationRuleDeleteMarker {
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
export interface BucketReplicationDestination {
|
||||||
|
bucket: string;
|
||||||
|
}
|
||||||
|
export interface BucketReplicationRule {
|
||||||
|
id: string;
|
||||||
|
status: string;
|
||||||
|
priority: number;
|
||||||
|
delete_marker_replication: BucketReplicationRuleDeleteMarker;
|
||||||
|
Destination: BucketReplicationDestination;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BucketReplication {
|
||||||
|
rules: BucketReplicationRule[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QuotaRequest {
|
||||||
|
enabled: boolean;
|
||||||
|
quota_type: string;
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MakeBucketRequest {
|
||||||
|
name: string;
|
||||||
|
versioning: boolean;
|
||||||
|
quota?: QuotaRequest;
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import get from "lodash/get";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { NewServiceAccount } from "./types";
|
import { NewServiceAccount } from "./types";
|
||||||
import { Button } from "@material-ui/core";
|
import { Button } from "@material-ui/core";
|
||||||
@@ -67,6 +68,8 @@ const CredentialsPrompt = ({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const consoleCreds = get(newServiceAccount, "console", null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalWrapper
|
<ModalWrapper
|
||||||
modalOpen={open}
|
modalOpen={open}
|
||||||
@@ -87,6 +90,21 @@ const CredentialsPrompt = ({
|
|||||||
<b>Secret Key:</b> {newServiceAccount.secretKey}
|
<b>Secret Key:</b> {newServiceAccount.secretKey}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
{consoleCreds && (
|
||||||
|
<React.Fragment>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<strong>Console Credentials</strong>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>Access Key:</b> {consoleCreds.accessKey}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Secret Key:</b> {consoleCreds.secretKey}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
<Typography
|
<Typography
|
||||||
component="p"
|
component="p"
|
||||||
variant="body1"
|
variant="body1"
|
||||||
@@ -99,11 +117,23 @@ const CredentialsPrompt = ({
|
|||||||
<Grid item xs={12} className={classes.buttonContainer}>
|
<Grid item xs={12} className={classes.buttonContainer}>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
let consoleExtras = {};
|
||||||
|
|
||||||
|
if (consoleCreds) {
|
||||||
|
consoleExtras = {
|
||||||
|
console: {
|
||||||
|
access_key: consoleCreds.accessKey,
|
||||||
|
secret_key: consoleCreds.secretKey,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
download(
|
download(
|
||||||
"credentials.json",
|
"credentials.json",
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
access_key: newServiceAccount.accessKey,
|
access_key: newServiceAccount.accessKey,
|
||||||
secret_key: newServiceAccount.secretKey,
|
secret_key: newServiceAccount.secretKey,
|
||||||
|
...consoleExtras,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -17,4 +17,10 @@
|
|||||||
export interface NewServiceAccount {
|
export interface NewServiceAccount {
|
||||||
accessKey: string;
|
accessKey: string;
|
||||||
secretKey: string;
|
secretKey: string;
|
||||||
|
console?: ConsoleSA;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConsoleSA {
|
||||||
|
accessKey: string;
|
||||||
|
secretKey: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
// 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, { useState } from "react";
|
||||||
|
import get from "lodash/get";
|
||||||
|
import { Grid, InputLabel, Tooltip } from "@material-ui/core";
|
||||||
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
|
import AttachFileIcon from "@material-ui/icons/AttachFile";
|
||||||
|
import CancelIcon from "@material-ui/icons/Cancel";
|
||||||
|
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, i: string) => void;
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
tooltip?: string;
|
||||||
|
required?: boolean;
|
||||||
|
error?: string;
|
||||||
|
accept?: string;
|
||||||
|
value?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
...fieldBasic,
|
||||||
|
...tooltipHelper,
|
||||||
|
textBoxContainer: {
|
||||||
|
flexGrow: 1,
|
||||||
|
position: "relative",
|
||||||
|
flexDirection: "column",
|
||||||
|
},
|
||||||
|
errorState: {
|
||||||
|
color: "#b53b4b",
|
||||||
|
fontSize: 14,
|
||||||
|
position: "absolute",
|
||||||
|
top: 7,
|
||||||
|
right: 7,
|
||||||
|
},
|
||||||
|
errorText: {
|
||||||
|
margin: "0",
|
||||||
|
fontSize: "0.75rem",
|
||||||
|
marginTop: 3,
|
||||||
|
textAlign: "left",
|
||||||
|
fontFamily: "Lato,sans-serif",
|
||||||
|
fontWeight: 400,
|
||||||
|
lineHeight: "1.66",
|
||||||
|
color: "#dc1f2e",
|
||||||
|
},
|
||||||
|
valueString: {
|
||||||
|
maxWidth: 350,
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
marginTop: 2,
|
||||||
|
},
|
||||||
|
fileReselect: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const FileSelector = ({
|
||||||
|
label,
|
||||||
|
classes,
|
||||||
|
onChange,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
disabled = false,
|
||||||
|
tooltip = "",
|
||||||
|
required,
|
||||||
|
error = "",
|
||||||
|
accept = "",
|
||||||
|
value = "",
|
||||||
|
}: InputBoxProps) => {
|
||||||
|
const [showFileSelector, setShowSelector] = useState(false);
|
||||||
|
|
||||||
|
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>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showFileSelector || value === "" ? (
|
||||||
|
<div className={classes.textBoxContainer}>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
name={name}
|
||||||
|
onChange={(e) => {
|
||||||
|
const fileName = get(e, "target.files[0].name", "");
|
||||||
|
fileProcess(e, (data: any) => {
|
||||||
|
onChange(data, fileName);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
accept={accept}
|
||||||
|
required={required}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{value !== "" && (
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
aria-label="upload picture"
|
||||||
|
component="span"
|
||||||
|
onClick={() => {
|
||||||
|
setShowSelector(false);
|
||||||
|
}}
|
||||||
|
disableRipple={false}
|
||||||
|
disableFocusRipple={false}
|
||||||
|
>
|
||||||
|
<CancelIcon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{error !== "" && (
|
||||||
|
<React.Fragment>
|
||||||
|
<br />
|
||||||
|
<span className={classes.errorText}>{error}</span>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={classes.fileReselect}>
|
||||||
|
<div className={classes.valueString}>{value}</div>
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
aria-label="upload picture"
|
||||||
|
component="span"
|
||||||
|
onClick={() => {
|
||||||
|
setShowSelector(true);
|
||||||
|
}}
|
||||||
|
disableRipple={false}
|
||||||
|
disableFocusRipple={false}
|
||||||
|
>
|
||||||
|
<AttachFileIcon />
|
||||||
|
</IconButton>
|
||||||
|
</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: (
|
onChange: (
|
||||||
e: React.ChangeEvent<{ name?: string | undefined; value: unknown }>
|
e: React.ChangeEvent<{ name?: string | undefined; value: unknown }>
|
||||||
) => void;
|
) => void;
|
||||||
|
disabled?: boolean;
|
||||||
classes: any;
|
classes: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ const styles = (theme: Theme) =>
|
|||||||
...tooltipHelper,
|
...tooltipHelper,
|
||||||
inputLabel: {
|
inputLabel: {
|
||||||
...fieldBasic.inputLabel,
|
...fieldBasic.inputLabel,
|
||||||
width: 116,
|
width: 215,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -88,6 +89,7 @@ const SelectWrapper = ({
|
|||||||
label,
|
label,
|
||||||
tooltip = "",
|
tooltip = "",
|
||||||
value,
|
value,
|
||||||
|
disabled = false,
|
||||||
}: SelectProps) => {
|
}: SelectProps) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@@ -111,6 +113,7 @@ const SelectWrapper = ({
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
input={<SelectStyled />}
|
input={<SelectStyled />}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|||||||
@@ -79,3 +79,19 @@ export const checkboxIcons = {
|
|||||||
backgroundColor: "#201763",
|
backgroundColor: "#201763",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const containerForHeader = (bottomSpacing: any) => ({
|
||||||
|
container: {
|
||||||
|
padding: "110px 33px 30px",
|
||||||
|
paddingBottom: bottomSpacing,
|
||||||
|
"& h6": {
|
||||||
|
color: "#777777",
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
"& p": {
|
||||||
|
"& span": {
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
|
||||||
|
interface IPageHeader {
|
||||||
|
classes: any;
|
||||||
|
label: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
headerContainer: {
|
||||||
|
position: "absolute",
|
||||||
|
width: "100%",
|
||||||
|
height: 77,
|
||||||
|
display: "flex",
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
borderBottom: "#E3E3E3 1px solid",
|
||||||
|
left: 0,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
labelStyle: {
|
||||||
|
color: "#000",
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 700,
|
||||||
|
marginLeft: 55,
|
||||||
|
marginTop: 8,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const PageHeader = ({ classes, label }: IPageHeader) => {
|
||||||
|
return (
|
||||||
|
<Grid container className={classes.headerContainer}>
|
||||||
|
<Grid item xs={12} className={classes.label}>
|
||||||
|
<Typography variant="h4" className={classes.labelStyle}>
|
||||||
|
{label}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(PageHeader);
|
||||||
@@ -49,6 +49,7 @@ interface IColumns {
|
|||||||
elementKey: string;
|
elementKey: string;
|
||||||
sortable?: boolean;
|
sortable?: boolean;
|
||||||
renderFunction?: (input: any) => any;
|
renderFunction?: (input: any) => any;
|
||||||
|
globalClass?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPaginatorConfig {
|
interface IPaginatorConfig {
|
||||||
@@ -161,7 +162,10 @@ const styles = (theme: Theme) =>
|
|||||||
const titleColumnsMap = (columns: IColumns[]) => {
|
const titleColumnsMap = (columns: IColumns[]) => {
|
||||||
return columns.map((column: IColumns, index: number) => {
|
return columns.map((column: IColumns, index: number) => {
|
||||||
return (
|
return (
|
||||||
<TableCell key={`tbCT-${column.elementKey}-${index}`}>
|
<TableCell
|
||||||
|
key={`tbCT-${column.elementKey}-${index}`}
|
||||||
|
className={column.globalClass}
|
||||||
|
>
|
||||||
{column.label}
|
{column.label}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
@@ -265,7 +269,7 @@ const TableWrapper = ({
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
{records && records.length > 0 ? (
|
{records && !isLoading && records.length > 0 ? (
|
||||||
<Table size="small" stickyHeader={stickyHeader}>
|
<Table size="small" stickyHeader={stickyHeader}>
|
||||||
<TableHead className={classes.minTableHeader}>
|
<TableHead className={classes.minTableHeader}>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
@@ -279,7 +283,10 @@ const TableWrapper = ({
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
{titleColumnsMap(columns)}
|
{titleColumnsMap(columns)}
|
||||||
{itemActions && itemActions.length > 0 && (
|
{((itemActions && itemActions.length > 1) ||
|
||||||
|
(itemActions &&
|
||||||
|
itemActions.length === 1 &&
|
||||||
|
itemActions[0].type !== "view")) && (
|
||||||
<TableCell
|
<TableCell
|
||||||
align="center"
|
align="center"
|
||||||
className={classes.actionsContainer}
|
className={classes.actionsContainer}
|
||||||
@@ -329,7 +336,10 @@ const TableWrapper = ({
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
{rowColumnsMap(columns, record, classes, isSelected)}
|
{rowColumnsMap(columns, record, classes, isSelected)}
|
||||||
{itemActions && itemActions.length > 0 && (
|
{((itemActions && itemActions.length > 1) ||
|
||||||
|
(itemActions &&
|
||||||
|
itemActions.length === 1 &&
|
||||||
|
itemActions[0].type !== "view")) && (
|
||||||
<TableCell
|
<TableCell
|
||||||
align="center"
|
align="center"
|
||||||
className={classes.actionsContainer}
|
className={classes.actionsContainer}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
|||||||
import { configurationElements } from "../utils";
|
import { configurationElements } from "../utils";
|
||||||
import { IConfigurationElement } from "../types";
|
import { IConfigurationElement } from "../types";
|
||||||
import EditConfiguration from "../CustomForms/EditConfiguration";
|
import EditConfiguration from "../CustomForms/EditConfiguration";
|
||||||
|
import { containerForHeader } from "../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
interface IListConfiguration {
|
interface IListConfiguration {
|
||||||
classes: any;
|
classes: any;
|
||||||
@@ -58,6 +60,7 @@ const styles = (theme: Theme) =>
|
|||||||
iconText: {
|
iconText: {
|
||||||
lineHeight: "24px",
|
lineHeight: "24px",
|
||||||
},
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
||||||
@@ -103,47 +106,44 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
|
|||||||
selectedConfiguration={selectedConfiguration}
|
selectedConfiguration={selectedConfiguration}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<PageHeader label="Configurations List" />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Typography variant="h6">Configurations List</Typography>
|
{error !== "" && <Grid container>{error}</Grid>}
|
||||||
</Grid>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<Grid item xs={12}>
|
<TextField
|
||||||
<br />
|
placeholder="Filter"
|
||||||
</Grid>
|
className={classes.searchField}
|
||||||
{error !== "" && <Grid container>{error}</Grid>}
|
id="search-resource"
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
label=""
|
||||||
<TextField
|
onChange={(event) => {
|
||||||
placeholder="Filter"
|
setFilter(event.target.value);
|
||||||
className={classes.searchField}
|
}}
|
||||||
id="search-resource"
|
InputProps={{
|
||||||
label=""
|
disableUnderline: true,
|
||||||
onChange={(event) => {
|
startAdornment: (
|
||||||
setFilter(event.target.value);
|
<InputAdornment position="start">
|
||||||
}}
|
<SearchIcon />
|
||||||
InputProps={{
|
</InputAdornment>
|
||||||
disableUnderline: true,
|
),
|
||||||
startAdornment: (
|
}}
|
||||||
<InputAdornment position="start">
|
/>
|
||||||
<SearchIcon />
|
</Grid>
|
||||||
</InputAdornment>
|
<Grid item xs={12}>
|
||||||
),
|
<br />
|
||||||
}}
|
</Grid>
|
||||||
/>
|
<Grid item xs={12}>
|
||||||
</Grid>
|
<TableWrapper
|
||||||
<Grid item xs={12}>
|
itemActions={tableActions}
|
||||||
<br />
|
columns={[
|
||||||
</Grid>
|
{ label: "Configuration", elementKey: "configuration_id" },
|
||||||
<Grid item xs={12}>
|
]}
|
||||||
<TableWrapper
|
isLoading={false}
|
||||||
itemActions={tableActions}
|
records={filteredRecords}
|
||||||
columns={[
|
entityName="Configurations"
|
||||||
{ label: "Configuration", elementKey: "configuration_id" },
|
idField="configuration_id"
|
||||||
]}
|
/>
|
||||||
isLoading={false}
|
</Grid>
|
||||||
records={filteredRecords}
|
|
||||||
entityName="Configurations"
|
|
||||||
idField="configuration_id"
|
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ import Permissions from "./Permissions/Permissions";
|
|||||||
import Dashboard from "./Dashboard/Dashboard";
|
import Dashboard from "./Dashboard/Dashboard";
|
||||||
import Menu from "./Menu/Menu";
|
import Menu from "./Menu/Menu";
|
||||||
import api from "../../common/api";
|
import api from "../../common/api";
|
||||||
import storage from "local-storage-fallback";
|
|
||||||
import NotFoundPage from "../NotFoundPage";
|
|
||||||
import ServiceAccounts from "./ServiceAccounts/ServiceAccounts";
|
import ServiceAccounts from "./ServiceAccounts/ServiceAccounts";
|
||||||
import Users from "./Users/Users";
|
import Users from "./Users/Users";
|
||||||
import Groups from "./Groups/Groups";
|
import Groups from "./Groups/Groups";
|
||||||
@@ -69,6 +67,9 @@ import { ISessionResponse } from "./types";
|
|||||||
import { saveSessionResponse } from "./actions";
|
import { saveSessionResponse } from "./actions";
|
||||||
import TenantDetails from "./Tenants/TenantDetails/TenantDetails";
|
import TenantDetails from "./Tenants/TenantDetails/TenantDetails";
|
||||||
import { clearSession } from "../../common/utils";
|
import { clearSession } from "../../common/utils";
|
||||||
|
import RemoteBuckets from "./RemoteBuckets/RemoteBuckets";
|
||||||
|
import ObjectBrowser from "./ObjectBrowser/ObjectBrowser";
|
||||||
|
import ListObjects from "./Buckets/ListBuckets/Objects/ListObjects/ListObjects";
|
||||||
|
|
||||||
function Copyright() {
|
function Copyright() {
|
||||||
return (
|
return (
|
||||||
@@ -83,7 +84,7 @@ function Copyright() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const drawerWidth = 254;
|
const drawerWidth = 245;
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -135,6 +136,9 @@ const styles = (theme: Theme) =>
|
|||||||
duration: theme.transitions.duration.enteringScreen,
|
duration: theme.transitions.duration.enteringScreen,
|
||||||
}),
|
}),
|
||||||
overflowX: "hidden",
|
overflowX: "hidden",
|
||||||
|
background:
|
||||||
|
"transparent linear-gradient(90deg, #073052 0%, #081C42 100%) 0% 0% no-repeat padding-box",
|
||||||
|
boxShadow: "0px 3px 7px #00000014",
|
||||||
},
|
},
|
||||||
drawerPaperClose: {
|
drawerPaperClose: {
|
||||||
overflowX: "hidden",
|
overflowX: "hidden",
|
||||||
@@ -147,17 +151,15 @@ const styles = (theme: Theme) =>
|
|||||||
width: theme.spacing(9),
|
width: theme.spacing(9),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appBarSpacer: {
|
|
||||||
height: "5px",
|
|
||||||
},
|
|
||||||
content: {
|
content: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
height: "100vh",
|
height: "100vh",
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
|
position: "relative",
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
paddingTop: theme.spacing(4),
|
|
||||||
paddingBottom: theme.spacing(4),
|
paddingBottom: theme.spacing(4),
|
||||||
|
margin: 0,
|
||||||
},
|
},
|
||||||
paper: {
|
paper: {
|
||||||
padding: theme.spacing(2),
|
padding: theme.spacing(2),
|
||||||
@@ -253,6 +255,14 @@ const Console = ({
|
|||||||
component: Buckets,
|
component: Buckets,
|
||||||
path: "/buckets/:bucketName",
|
path: "/buckets/:bucketName",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: ObjectBrowser,
|
||||||
|
path: "/object-browser",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: ListObjects,
|
||||||
|
path: "/object-browser/:bucket?",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: Watch,
|
component: Watch,
|
||||||
path: "/watch",
|
path: "/watch",
|
||||||
@@ -269,6 +279,10 @@ const Console = ({
|
|||||||
component: Policies,
|
component: Policies,
|
||||||
path: "/policies",
|
path: "/policies",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: RemoteBuckets,
|
||||||
|
path: "/remote-buckets",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: Trace,
|
component: Trace,
|
||||||
path: "/trace",
|
path: "/trace",
|
||||||
@@ -311,7 +325,7 @@ const Console = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: TenantDetails,
|
component: TenantDetails,
|
||||||
path: "/tenants/:tenantName",
|
path: "/namespaces/:tenantNamespace/tenants/:tenantName",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]);
|
const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]);
|
||||||
@@ -359,8 +373,7 @@ const Console = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={classes.appBarSpacer} />
|
<Container className={classes.container}>
|
||||||
<Container maxWidth="lg" className={classes.container}>
|
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<Switch>
|
<Switch>
|
||||||
{allowedRoutes.map((route: any) => (
|
{allowedRoutes.map((route: any) => (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// This file is part of MinIO Console Server
|
// This file is part of MinIO Console Server
|
||||||
// Copyright (c) 2019 MinIO, Inc.
|
// Copyright (c) 2020 MinIO, Inc.
|
||||||
//
|
//
|
||||||
// This program is free software: you can redistribute it and/or modify
|
// 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
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
@@ -27,6 +27,8 @@ import { Usage } from "./types";
|
|||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
import { niceBytes } from "../../../common/utils";
|
import { niceBytes } from "../../../common/utils";
|
||||||
import { LinearProgress } from "@material-ui/core";
|
import { LinearProgress } from "@material-ui/core";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -77,26 +79,20 @@ const styles = (theme: Theme) =>
|
|||||||
height: "100vh",
|
height: "100vh",
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
},
|
},
|
||||||
container: {
|
...containerForHeader(theme.spacing(4)),
|
||||||
paddingBottom: theme.spacing(4),
|
|
||||||
"& h6": {
|
|
||||||
color: "#777777",
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
"& p": {
|
|
||||||
"& span": {
|
|
||||||
fontSize: 16,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
paper: {
|
paper: {
|
||||||
padding: theme.spacing(2),
|
padding: theme.spacing(2),
|
||||||
display: "flex",
|
display: "flex",
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
border: "#eaedee 1px solid",
|
||||||
|
borderRadius: 5,
|
||||||
|
boxShadow: "none",
|
||||||
},
|
},
|
||||||
fixedHeight: {
|
fixedHeight: {
|
||||||
minHeight: 240,
|
minHeight: 165,
|
||||||
|
minWidth: 247,
|
||||||
|
marginRight: 20,
|
||||||
},
|
},
|
||||||
consumptionValue: {
|
consumptionValue: {
|
||||||
color: "#000000",
|
color: "#000000",
|
||||||
@@ -107,6 +103,9 @@ const styles = (theme: Theme) =>
|
|||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
color: "#777777",
|
color: "#777777",
|
||||||
},
|
},
|
||||||
|
notationContainer: {
|
||||||
|
display: "flex",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IDashboardProps {
|
interface IDashboardProps {
|
||||||
@@ -155,11 +154,9 @@ const Dashboard = ({ classes }: IDashboardProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
<PageHeader label="Dashboard" />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid container spacing={3} className={classes.container}>
|
<Grid container spacing={3} className={classes.container}>
|
||||||
<Grid container>
|
|
||||||
<Typography variant="h2">MinIO Console</Typography>
|
|
||||||
</Grid>
|
|
||||||
{error !== "" && <Grid container>{error}</Grid>}
|
{error !== "" && <Grid container>{error}</Grid>}
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Grid item xs={12} md={12} lg={12}>
|
<Grid item xs={12} md={12} lg={12}>
|
||||||
@@ -167,22 +164,33 @@ const Dashboard = ({ classes }: IDashboardProps) => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
) : (
|
) : (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Grid item xs={12} md={4} lg={4}>
|
<Grid item className={classes.notationContainer}>
|
||||||
<Paper className={fixedHeightPaper}>
|
<Paper className={fixedHeightPaper}>
|
||||||
<Grid container direction="row" alignItems="center">
|
<Grid container direction="row" alignItems="center">
|
||||||
<Grid item className={classes.icon}>
|
<Grid item className={classes.icon}>
|
||||||
<ViewHeadlineIcon />
|
<ViewHeadlineIcon />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Grid item>
|
||||||
<Typography variant="h6">Total Buckets</Typography>
|
<Typography variant="h6">All Buckets</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Typography className={classes.consumptionValue}>
|
<Typography className={classes.consumptionValue}>
|
||||||
{usage ? prettyNumber(usage.buckets) : 0}
|
{usage ? prettyNumber(usage.buckets) : 0}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
<Paper className={fixedHeightPaper}>
|
||||||
<Grid item xs={12} md={4} lg={4}>
|
<Grid container direction="row" alignItems="center">
|
||||||
|
<Grid item className={classes.icon}>
|
||||||
|
<PieChartIcon />
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="h6">Usage</Typography>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Typography className={classes.consumptionValue}>
|
||||||
|
{usage ? prettyUsage(usage.usage + "") : 0}
|
||||||
|
</Typography>
|
||||||
|
</Paper>
|
||||||
<Paper className={fixedHeightPaper}>
|
<Paper className={fixedHeightPaper}>
|
||||||
<Grid container direction="row" alignItems="center">
|
<Grid container direction="row" alignItems="center">
|
||||||
<Grid item className={classes.icon}>
|
<Grid item className={classes.icon}>
|
||||||
@@ -197,21 +205,6 @@ const Dashboard = ({ classes }: IDashboardProps) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={4} lg={4}>
|
|
||||||
<Paper className={fixedHeightPaper}>
|
|
||||||
<Grid container direction="row" alignItems="center">
|
|
||||||
<Grid item className={classes.icon}>
|
|
||||||
<PieChartIcon />
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<Typography variant="h6">Usage</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Typography className={classes.consumptionValue}>
|
|
||||||
{usage ? prettyUsage(usage.usage + "") : 0}
|
|
||||||
</Typography>
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import AddGroup from "../Groups/AddGroup";
|
|||||||
import DeleteGroup from "./DeleteGroup";
|
import DeleteGroup from "./DeleteGroup";
|
||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
import SetPolicy from "../Policies/SetPolicy";
|
import SetPolicy from "../Policies/SetPolicy";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
interface IGroupsProps {
|
interface IGroupsProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
@@ -84,6 +86,7 @@ const styles = (theme: Theme) =>
|
|||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
},
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const Groups = ({ classes }: IGroupsProps) => {
|
const Groups = ({ classes }: IGroupsProps) => {
|
||||||
@@ -215,71 +218,68 @@ const Groups = ({ classes }: IGroupsProps) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<PageHeader label={"Groups"} />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Typography variant="h6">Groups</Typography>
|
{error !== "" ? <Grid container>{error}</Grid> : <React.Fragment />}
|
||||||
</Grid>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<Grid item xs={12}>
|
<TextField
|
||||||
<br />
|
placeholder="Search Groups"
|
||||||
</Grid>
|
className={classes.searchField}
|
||||||
{error !== "" ? <Grid container>{error}</Grid> : <React.Fragment />}
|
id="search-resource"
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
label=""
|
||||||
<TextField
|
InputProps={{
|
||||||
placeholder="Search Groups"
|
disableUnderline: true,
|
||||||
className={classes.searchField}
|
startAdornment: (
|
||||||
id="search-resource"
|
<InputAdornment position="start">
|
||||||
label=""
|
<SearchIcon />
|
||||||
InputProps={{
|
</InputAdornment>
|
||||||
disableUnderline: true,
|
),
|
||||||
startAdornment: (
|
}}
|
||||||
<InputAdornment position="start">
|
onChange={(e) => {
|
||||||
<SearchIcon />
|
setFilter(e.target.value);
|
||||||
</InputAdornment>
|
}}
|
||||||
),
|
/>
|
||||||
}}
|
<Button
|
||||||
onChange={(e) => {
|
variant="contained"
|
||||||
setFilter(e.target.value);
|
color="primary"
|
||||||
}}
|
startIcon={<CreateIcon />}
|
||||||
/>
|
onClick={() => {
|
||||||
<Button
|
setSelectedGroup(null);
|
||||||
variant="contained"
|
setGroupOpen(true);
|
||||||
color="primary"
|
}}
|
||||||
startIcon={<CreateIcon />}
|
>
|
||||||
onClick={() => {
|
Create Group
|
||||||
setSelectedGroup(null);
|
</Button>
|
||||||
setGroupOpen(true);
|
</Grid>
|
||||||
}}
|
|
||||||
>
|
|
||||||
Create Group
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TableWrapper
|
<TableWrapper
|
||||||
itemActions={tableActions}
|
itemActions={tableActions}
|
||||||
columns={[{ label: "Name", elementKey: "" }]}
|
columns={[{ label: "Name", elementKey: "" }]}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
records={filteredRecords}
|
records={filteredRecords}
|
||||||
entityName="Groups"
|
entityName="Groups"
|
||||||
idField=""
|
idField=""
|
||||||
paginatorConfig={{
|
paginatorConfig={{
|
||||||
rowsPerPageOptions: [5, 10, 25],
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
colSpan: 3,
|
colSpan: 3,
|
||||||
count: totalRecords,
|
count: totalRecords,
|
||||||
rowsPerPage: rowsPerPage,
|
rowsPerPage: rowsPerPage,
|
||||||
page: page,
|
page: page,
|
||||||
SelectProps: {
|
SelectProps: {
|
||||||
inputProps: { "aria-label": "rows per page" },
|
inputProps: { "aria-label": "rows per page" },
|
||||||
native: true,
|
native: true,
|
||||||
},
|
},
|
||||||
onChangePage: handleChangePage,
|
onChangePage: handleChangePage,
|
||||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||||
ActionsComponent: MinTablePaginationActions,
|
ActionsComponent: MinTablePaginationActions,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import { FormControl, MenuItem, Select } from "@material-ui/core";
|
|||||||
import { BucketList, Bucket } from "../Watch/types";
|
import { BucketList, Bucket } from "../Watch/types";
|
||||||
import { HealStatus, colorH } from "./types";
|
import { HealStatus, colorH } from "./types";
|
||||||
import { niceBytes } from "../../../common/utils";
|
import { niceBytes } from "../../../common/utils";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -58,6 +60,7 @@ const styles = (theme: Theme) =>
|
|||||||
lastElementWPadding: {
|
lastElementWPadding: {
|
||||||
paddingRight: "78",
|
paddingRight: "78",
|
||||||
},
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IHeal {
|
interface IHeal {
|
||||||
@@ -195,130 +198,127 @@ const Heal = ({ classes }: IHeal) => {
|
|||||||
}));
|
}));
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
<PageHeader label="Heal" />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Typography variant="h6">Heal</Typography>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
</Grid>
|
<FormControl variant="outlined">
|
||||||
<Grid item xs={12}>
|
<Select
|
||||||
<br />
|
id="bucket-name"
|
||||||
</Grid>
|
name="bucket-name"
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
value={bucketName}
|
||||||
<FormControl variant="outlined">
|
onChange={(e) => {
|
||||||
<Select
|
setBucketName(e.target.value as string);
|
||||||
id="bucket-name"
|
}}
|
||||||
name="bucket-name"
|
className={classes.fieldContainer}
|
||||||
value={bucketName}
|
disabled={false}
|
||||||
onChange={(e) => {
|
>
|
||||||
setBucketName(e.target.value as string);
|
<MenuItem value="" key={`select-bucket-name-default`}>
|
||||||
}}
|
Select Bucket
|
||||||
className={classes.fieldContainer}
|
|
||||||
disabled={false}
|
|
||||||
>
|
|
||||||
<MenuItem value="" key={`select-bucket-name-default`}>
|
|
||||||
Select Bucket
|
|
||||||
</MenuItem>
|
|
||||||
{bucketNames.map((option) => (
|
|
||||||
<MenuItem
|
|
||||||
value={option.value}
|
|
||||||
key={`select-bucket-name-${option.label}`}
|
|
||||||
>
|
|
||||||
{option.label}
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
{bucketNames.map((option) => (
|
||||||
</Select>
|
<MenuItem
|
||||||
</FormControl>
|
value={option.value}
|
||||||
<TextField
|
key={`select-bucket-name-${option.label}`}
|
||||||
placeholder="Prefix"
|
>
|
||||||
className={classes.inputField}
|
{option.label}
|
||||||
id="prefix-resource"
|
</MenuItem>
|
||||||
label=""
|
))}
|
||||||
disabled={false}
|
</Select>
|
||||||
InputProps={{
|
</FormControl>
|
||||||
disableUnderline: true,
|
<TextField
|
||||||
}}
|
placeholder="Prefix"
|
||||||
onChange={(e) => {
|
className={classes.inputField}
|
||||||
setPrefix(e.target.value);
|
id="prefix-resource"
|
||||||
|
label=""
|
||||||
|
disabled={false}
|
||||||
|
InputProps={{
|
||||||
|
disableUnderline: true,
|
||||||
|
}}
|
||||||
|
onChange={(e) => {
|
||||||
|
setPrefix(e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={start}
|
||||||
|
onClick={() => setStart(true)}
|
||||||
|
>
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<span>{"Recursive"}</span>
|
||||||
|
<Checkbox
|
||||||
|
name="recursive"
|
||||||
|
id="recursive"
|
||||||
|
value="recursive"
|
||||||
|
color="primary"
|
||||||
|
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||||
|
checked={recursive}
|
||||||
|
onChange={(e) => {
|
||||||
|
setRecursive(e.target.checked);
|
||||||
|
}}
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
<span>{"Force Start"}</span>
|
||||||
|
<Checkbox
|
||||||
|
name="recursive"
|
||||||
|
id="recursive"
|
||||||
|
value="recursive"
|
||||||
|
color="primary"
|
||||||
|
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||||
|
checked={forceStart}
|
||||||
|
onChange={(e) => {
|
||||||
|
setForceStart(e.target.checked);
|
||||||
|
}}
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
<span>{"Force Stop"}</span>
|
||||||
|
<Checkbox
|
||||||
|
name="recursive"
|
||||||
|
id="recursive"
|
||||||
|
value="recursive"
|
||||||
|
color="primary"
|
||||||
|
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||||
|
checked={forceStop}
|
||||||
|
onChange={(e) => {
|
||||||
|
setForceStop(e.target.checked);
|
||||||
|
}}
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
<span className={classes.lastElementWPadding}>{""}</span>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<br />
|
||||||
|
</Grid>
|
||||||
|
<HorizontalBar
|
||||||
|
data={data}
|
||||||
|
width={100}
|
||||||
|
height={30}
|
||||||
|
options={{
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: "Item's Health Status [%]",
|
||||||
|
fontSize: 20,
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
position: "right",
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
disabled={start}
|
|
||||||
onClick={() => setStart(true)}
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
</Button>
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<span>{"Recursive"}</span>
|
<br />
|
||||||
<Checkbox
|
Size scanned: {hStatus.sizeScanned}
|
||||||
name="recursive"
|
<br />
|
||||||
id="recursive"
|
Objects healed: {hStatus.objectsHealed} / {hStatus.objectsScanned}
|
||||||
value="recursive"
|
<br />
|
||||||
color="primary"
|
Healing time: {hStatus.healDuration}s
|
||||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
|
||||||
checked={recursive}
|
|
||||||
onChange={(e) => {
|
|
||||||
setRecursive(e.target.checked);
|
|
||||||
}}
|
|
||||||
disabled={false}
|
|
||||||
/>
|
|
||||||
<span>{"Force Start"}</span>
|
|
||||||
<Checkbox
|
|
||||||
name="recursive"
|
|
||||||
id="recursive"
|
|
||||||
value="recursive"
|
|
||||||
color="primary"
|
|
||||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
|
||||||
checked={forceStart}
|
|
||||||
onChange={(e) => {
|
|
||||||
setForceStart(e.target.checked);
|
|
||||||
}}
|
|
||||||
disabled={false}
|
|
||||||
/>
|
|
||||||
<span>{"Force Stop"}</span>
|
|
||||||
<Checkbox
|
|
||||||
name="recursive"
|
|
||||||
id="recursive"
|
|
||||||
value="recursive"
|
|
||||||
color="primary"
|
|
||||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
|
||||||
checked={forceStop}
|
|
||||||
onChange={(e) => {
|
|
||||||
setForceStop(e.target.checked);
|
|
||||||
}}
|
|
||||||
disabled={false}
|
|
||||||
/>
|
|
||||||
<span className={classes.lastElementWPadding}>{""}</span>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
|
||||||
<br />
|
|
||||||
</Grid>
|
|
||||||
<HorizontalBar
|
|
||||||
data={data}
|
|
||||||
width={100}
|
|
||||||
height={30}
|
|
||||||
options={{
|
|
||||||
title: {
|
|
||||||
display: true,
|
|
||||||
text: "Item's Health Status [%]",
|
|
||||||
fontSize: 20,
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: true,
|
|
||||||
position: "right",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<br />
|
|
||||||
Size scanned: {hStatus.sizeScanned}
|
|
||||||
<br />
|
|
||||||
Objects healed: {hStatus.objectsHealed} / {hStatus.objectsScanned}
|
|
||||||
<br />
|
|
||||||
Healing time: {hStatus.healDuration}s
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
|||||||
import { timeFromDate } from "../../../common/utils";
|
import { timeFromDate } from "../../../common/utils";
|
||||||
import { isNullOrUndefined } from "util";
|
import { isNullOrUndefined } from "util";
|
||||||
import { wsProtocol } from "../../../utils/wsUtils";
|
import { wsProtocol } from "../../../utils/wsUtils";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import { Grid } from "@material-ui/core";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -54,6 +57,7 @@ const styles = (theme: Theme) =>
|
|||||||
ansidefault: {
|
ansidefault: {
|
||||||
color: "black",
|
color: "black",
|
||||||
},
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface ILogs {
|
interface ILogs {
|
||||||
@@ -243,16 +247,20 @@ const Logs = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<React.Fragment>
|
||||||
<h1>Logs</h1>
|
<PageHeader label="Logs" />
|
||||||
<div className={classes.logList}>
|
<Grid container>
|
||||||
<ul>
|
<Grid className={classes.container} item xs={12}>
|
||||||
{messages.map((m) => {
|
<div className={classes.logList}>
|
||||||
return renderLog(m);
|
<ul>
|
||||||
})}
|
{messages.map((m) => {
|
||||||
</ul>
|
return renderLog(m);
|
||||||
</div>
|
})}
|
||||||
</div>
|
</ul>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ import ListItem from "@material-ui/core/ListItem";
|
|||||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||||
import WebAssetIcon from "@material-ui/icons/WebAsset";
|
import WebAssetIcon from "@material-ui/icons/WebAsset";
|
||||||
import HealingIcon from "@material-ui/icons/Healing";
|
import HealingIcon from "@material-ui/icons/Healing";
|
||||||
|
import CloudUploadIcon from "@material-ui/icons/CloudUpload";
|
||||||
|
import DescriptionIcon from "@material-ui/icons/Description";
|
||||||
|
import FileCopyIcon from "@material-ui/icons/FileCopy";
|
||||||
import Collapse from "@material-ui/core/Collapse";
|
import Collapse from "@material-ui/core/Collapse";
|
||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import ListItemText from "@material-ui/core/ListItemText";
|
||||||
import List from "@material-ui/core/List";
|
import List from "@material-ui/core/List";
|
||||||
@@ -55,44 +58,53 @@ import { clearSession } from "../../../common/utils";
|
|||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
logo: {
|
logo: {
|
||||||
paddingTop: "42px",
|
paddingTop: 25,
|
||||||
marginBottom: "20px",
|
marginBottom: 30,
|
||||||
textAlign: "center",
|
paddingLeft: 45,
|
||||||
"& img": {
|
"& img": {
|
||||||
width: "120px",
|
width: 120,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
menuList: {
|
menuList: {
|
||||||
"& .active": {
|
"& .active": {
|
||||||
borderTopLeftRadius: "3px",
|
borderTopLeftRadius: 2,
|
||||||
borderBottomLeftRadius: "3px",
|
borderBottomLeftRadius: 2,
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
background:
|
backgroundColor: "rgba(255, 255, 255, .18)",
|
||||||
"transparent linear-gradient(90deg, #362585 0%, #362585 7%, #281B6F 39%, #1F1661 100%) 0% 0% no-repeat padding-box;",
|
|
||||||
boxShadow: "4px 4px 4px #A5A5A512",
|
|
||||||
fontWeight: 700,
|
|
||||||
"& .MuiSvgIcon-root": {
|
"& .MuiSvgIcon-root": {
|
||||||
color: "white",
|
color: "white",
|
||||||
},
|
},
|
||||||
"& .MuiTypography-root": {
|
"& .MuiTypography-root": {
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
|
fontWeight: 700,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
paddingLeft: "30px",
|
|
||||||
"& .MuiSvgIcon-root": {
|
"& .MuiSvgIcon-root": {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: "#362585",
|
color: "rgba(255, 255, 255, 0.8)",
|
||||||
maxWidth: 14,
|
maxWidth: 14,
|
||||||
},
|
},
|
||||||
"& .MuiListItemIcon-root": {
|
"& .MuiListItemIcon-root": {
|
||||||
minWidth: "25px",
|
minWidth: 25,
|
||||||
},
|
},
|
||||||
"& .MuiTypography-root": {
|
"& .MuiTypography-root": {
|
||||||
fontSize: "12px",
|
fontSize: 12,
|
||||||
color: "#2e2e2e",
|
color: "rgba(255, 255, 255, 0.8)",
|
||||||
},
|
},
|
||||||
"& .MuiListItem-gutters": {
|
"& .MuiListItem-gutters": {
|
||||||
paddingRight: 0,
|
paddingRight: 0,
|
||||||
|
fontWeight: 300,
|
||||||
|
},
|
||||||
|
"& .MuiListItem-root": {
|
||||||
|
padding: "2px 0 2px 16px",
|
||||||
|
marginBottom: 8,
|
||||||
|
marginLeft: 30,
|
||||||
|
width: "calc(100% - 30px)",
|
||||||
|
},
|
||||||
|
"& .MuiCollapse-container .MuiCollapse-wrapper .MuiCollapse-wrapperInner .MuiDivider-root": {
|
||||||
|
backgroundColor: "rgba(112,112,112,0.5)",
|
||||||
|
marginBottom: 12,
|
||||||
|
height: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraMargin: {
|
extraMargin: {
|
||||||
@@ -101,31 +113,34 @@ const styles = (theme: Theme) =>
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
groupTitle: {
|
groupTitle: {
|
||||||
color: "#220c7c",
|
color: "#fff",
|
||||||
fontSize: 10,
|
fontSize: 10,
|
||||||
textTransform: "uppercase",
|
textTransform: "uppercase",
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
marginBottom: 3,
|
marginBottom: 3,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
},
|
},
|
||||||
subTitleMenu: {
|
subTitleMenu: {
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
marginLeft: 10,
|
marginLeft: 10,
|
||||||
"&.MuiTypography-root": {
|
"&.MuiTypography-root": {
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: "#220c7c",
|
color: "#fff",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
selectorArrow: {
|
selectorArrow: {
|
||||||
marginLeft: 3,
|
marginRight: 20,
|
||||||
marginTop: 1,
|
marginTop: 1,
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
borderStyle: "solid",
|
borderStyle: "solid",
|
||||||
borderWidth: "3px 2.5px 0 2.5px",
|
borderWidth: "4px 4px 0 4px",
|
||||||
borderColor: "#220C7C transparent transparent transparent",
|
borderColor:
|
||||||
|
"rgba(255, 255, 255, .29) transparent transparent transparent",
|
||||||
transform: "rotateZ(0deg)",
|
transform: "rotateZ(0deg)",
|
||||||
transitionDuration: "0.2s",
|
transitionDuration: "0.2s",
|
||||||
},
|
},
|
||||||
@@ -185,17 +200,17 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
group: "User",
|
group: "User",
|
||||||
type: "item",
|
type: "item",
|
||||||
component: NavLink,
|
component: NavLink,
|
||||||
to: "/buckets",
|
to: "/service-accounts",
|
||||||
name: "Buckets",
|
name: "Service Accounts",
|
||||||
icon: <BucketsIcon />,
|
icon: <ServiceAccountsIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: "User",
|
group: "User",
|
||||||
type: "item",
|
type: "item",
|
||||||
component: NavLink,
|
component: NavLink,
|
||||||
to: "/service-accounts",
|
to: "/object-browser",
|
||||||
name: "Service Accounts",
|
name: "Object Browser",
|
||||||
icon: <ServiceAccountsIcon />,
|
icon: <DescriptionIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: "Admin",
|
group: "Admin",
|
||||||
@@ -213,6 +228,14 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
name: "Groups",
|
name: "Groups",
|
||||||
icon: <GroupsIcon />,
|
icon: <GroupsIcon />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
group: "Admin",
|
||||||
|
type: "item",
|
||||||
|
component: NavLink,
|
||||||
|
to: "/buckets",
|
||||||
|
name: "Buckets",
|
||||||
|
icon: <BucketsIcon />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
group: "Admin",
|
group: "Admin",
|
||||||
type: "item",
|
type: "item",
|
||||||
@@ -221,6 +244,14 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
name: "IAM Policies",
|
name: "IAM Policies",
|
||||||
icon: <IAMPoliciesIcon />,
|
icon: <IAMPoliciesIcon />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
group: "Admin",
|
||||||
|
type: "item",
|
||||||
|
component: NavLink,
|
||||||
|
to: "/remote-buckets",
|
||||||
|
name: "Remote Buckets",
|
||||||
|
icon: <CloudUploadIcon />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
group: "Tools",
|
group: "Tools",
|
||||||
type: "item",
|
type: "item",
|
||||||
@@ -253,12 +284,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
name: "Heal",
|
name: "Heal",
|
||||||
icon: <HealingIcon />,
|
icon: <HealingIcon />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
group: "Admin",
|
|
||||||
type: "title",
|
|
||||||
name: "Configurations",
|
|
||||||
component: Typography,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
group: "Admin",
|
group: "Admin",
|
||||||
type: "item",
|
type: "item",
|
||||||
@@ -266,7 +291,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
to: "/notification-endpoints",
|
to: "/notification-endpoints",
|
||||||
name: "Lambda Notifications",
|
name: "Lambda Notifications",
|
||||||
icon: <LambdaNotificationsIcon />,
|
icon: <LambdaNotificationsIcon />,
|
||||||
extraMargin: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: "Admin",
|
group: "Admin",
|
||||||
@@ -275,7 +299,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
to: "/configurations-list",
|
to: "/configurations-list",
|
||||||
name: "Configurations List",
|
name: "Configurations List",
|
||||||
icon: <ConfigurationsListIcon />,
|
icon: <ConfigurationsListIcon />,
|
||||||
extraMargin: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
group: "Operator",
|
group: "Operator",
|
||||||
@@ -351,7 +374,7 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{groupMember.label}
|
<span>{groupMember.label}</span>
|
||||||
{groupMember.collapsible && (
|
{groupMember.collapsible && (
|
||||||
<span
|
<span
|
||||||
className={`${classes.selectorArrow} ${
|
className={`${classes.selectorArrow} ${
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { MinTablePaginationActions } from "../../../common/MinTablePaginationAct
|
|||||||
import {
|
import {
|
||||||
NotificationEndpointItem,
|
NotificationEndpointItem,
|
||||||
NotificationEndpointsList,
|
NotificationEndpointsList,
|
||||||
TransformedEndpointItem
|
TransformedEndpointItem,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { notificationTransform } from "./utils";
|
import { notificationTransform } from "./utils";
|
||||||
import { CreateIcon } from "../../../icons";
|
import { CreateIcon } from "../../../icons";
|
||||||
@@ -35,6 +35,8 @@ import api from "../../../common/api";
|
|||||||
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
|
import FiberManualRecordIcon from "@material-ui/icons/FiberManualRecord";
|
||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
import AddNotificationEndpoint from "./AddNotificationEndpoint";
|
import AddNotificationEndpoint from "./AddNotificationEndpoint";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
interface IListNotificationEndpoints {
|
interface IListNotificationEndpoints {
|
||||||
classes: any;
|
classes: any;
|
||||||
@@ -43,29 +45,30 @@ interface IListNotificationEndpoints {
|
|||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
errorBlock: {
|
errorBlock: {
|
||||||
color: "red"
|
color: "red",
|
||||||
},
|
},
|
||||||
strongText: {
|
strongText: {
|
||||||
fontWeight: 700
|
fontWeight: 700,
|
||||||
},
|
},
|
||||||
keyName: {
|
keyName: {
|
||||||
marginLeft: 5
|
marginLeft: 5,
|
||||||
},
|
},
|
||||||
actionsTray: {
|
actionsTray: {
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
"& button": {
|
"& button": {
|
||||||
marginLeft: 10
|
marginLeft: 10,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
searchField: {
|
searchField: {
|
||||||
background: "#FFFFFF",
|
background: "#FFFFFF",
|
||||||
padding: 12,
|
padding: 12,
|
||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
boxShadow: "0px 3px 6px #00000012"
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
},
|
},
|
||||||
iconText: {
|
iconText: {
|
||||||
lineHeight: "24px"
|
lineHeight: "24px",
|
||||||
}
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
||||||
@@ -96,7 +99,7 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
|||||||
setError("");
|
setError("");
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch((err) => {
|
||||||
setError(err);
|
setError(err);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
});
|
});
|
||||||
@@ -115,8 +118,8 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
|||||||
type: "delete",
|
type: "delete",
|
||||||
onClick: (row: any) => {
|
onClick: (row: any) => {
|
||||||
//confirmDeleteBucket(row.name);
|
//confirmDeleteBucket(row.name);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const filteredRecords = records.filter((b: TransformedEndpointItem) => {
|
const filteredRecords = records.filter((b: TransformedEndpointItem) => {
|
||||||
@@ -136,7 +139,7 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center"
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FiberManualRecordIcon
|
<FiberManualRecordIcon
|
||||||
@@ -158,83 +161,80 @@ const ListNotificationEndpoints = ({ classes }: IListNotificationEndpoints) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<PageHeader label="Lambda Notification Targets" />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Typography variant="h6">Lambda Notification Targets</Typography>
|
{error !== "" && <Grid container>{error}</Grid>}
|
||||||
</Grid>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
<Grid item xs={12}>
|
<TextField
|
||||||
<br />
|
placeholder="Filter"
|
||||||
</Grid>
|
className={classes.searchField}
|
||||||
{error !== "" && <Grid container>{error}</Grid>}
|
id="search-resource"
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
label=""
|
||||||
<TextField
|
onChange={(event) => {
|
||||||
placeholder="Filter"
|
setFilter(event.target.value);
|
||||||
className={classes.searchField}
|
}}
|
||||||
id="search-resource"
|
InputProps={{
|
||||||
label=""
|
disableUnderline: true,
|
||||||
onChange={event => {
|
startAdornment: (
|
||||||
setFilter(event.target.value);
|
<InputAdornment position="start">
|
||||||
}}
|
<SearchIcon />
|
||||||
InputProps={{
|
</InputAdornment>
|
||||||
disableUnderline: true,
|
),
|
||||||
startAdornment: (
|
}}
|
||||||
<InputAdornment position="start">
|
/>
|
||||||
<SearchIcon />
|
<Button
|
||||||
</InputAdornment>
|
variant="contained"
|
||||||
)
|
color="primary"
|
||||||
}}
|
startIcon={<CreateIcon />}
|
||||||
/>
|
onClick={() => {
|
||||||
<Button
|
setAddScreenOpen(true);
|
||||||
variant="contained"
|
}}
|
||||||
color="primary"
|
>
|
||||||
startIcon={<CreateIcon />}
|
Add Notification Target
|
||||||
onClick={() => {
|
</Button>
|
||||||
setAddScreenOpen(true);
|
</Grid>
|
||||||
}}
|
<Grid item xs={12}>
|
||||||
>
|
<br />
|
||||||
Add Notification Target
|
</Grid>
|
||||||
</Button>
|
<Grid item xs={12}>
|
||||||
</Grid>
|
<TableWrapper
|
||||||
<Grid item xs={12}>
|
itemActions={tableActions}
|
||||||
<br />
|
columns={[
|
||||||
</Grid>
|
{ label: "Service", elementKey: "service_name" },
|
||||||
<Grid item xs={12}>
|
{
|
||||||
<TableWrapper
|
label: "Status",
|
||||||
itemActions={tableActions}
|
elementKey: "status",
|
||||||
columns={[
|
renderFunction: statusDisplay,
|
||||||
{ label: "Service", elementKey: "service_name" },
|
},
|
||||||
{
|
]}
|
||||||
label: "Status",
|
isLoading={isLoading}
|
||||||
elementKey: "status",
|
records={filteredRecords}
|
||||||
renderFunction: statusDisplay
|
entityName="Notification Endpoints"
|
||||||
}
|
idField="service_name"
|
||||||
]}
|
paginatorConfig={{
|
||||||
isLoading={isLoading}
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
records={filteredRecords}
|
colSpan: 3,
|
||||||
entityName="Notification Endpoints"
|
count: totalRecords,
|
||||||
idField="service_name"
|
rowsPerPage: rowsPerPage,
|
||||||
paginatorConfig={{
|
page: page,
|
||||||
rowsPerPageOptions: [5, 10, 25],
|
SelectProps: {
|
||||||
colSpan: 3,
|
inputProps: { "aria-label": "rows per page" },
|
||||||
count: totalRecords,
|
native: true,
|
||||||
rowsPerPage: rowsPerPage,
|
},
|
||||||
page: page,
|
onChangePage: (event: unknown, newPage: number) => {
|
||||||
SelectProps: {
|
setPage(newPage);
|
||||||
inputProps: { "aria-label": "rows per page" },
|
},
|
||||||
native: true
|
onChangeRowsPerPage: (
|
||||||
},
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
onChangePage: (event: unknown, newPage: number) => {
|
) => {
|
||||||
setPage(newPage);
|
const rPP = parseInt(event.target.value, 10);
|
||||||
},
|
setRowsPerPage(rPP);
|
||||||
onChangeRowsPerPage: (
|
},
|
||||||
event: React.ChangeEvent<HTMLInputElement>
|
ActionsComponent: MinTablePaginationActions,
|
||||||
) => {
|
}}
|
||||||
const rPP = parseInt(event.target.value, 10);
|
/>
|
||||||
setRowsPerPage(rPP);
|
</Grid>
|
||||||
},
|
|
||||||
ActionsComponent: MinTablePaginationActions
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
240
portal-ui/src/screens/Console/ObjectBrowser/BrowseBuckets.tsx
Normal file
240
portal-ui/src/screens/Console/ObjectBrowser/BrowseBuckets.tsx
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
// 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, { useState, useEffect } from "react";
|
||||||
|
import get from "lodash/get";
|
||||||
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import AddBucket from "../Buckets/ListBuckets/AddBucket";
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
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 { CreateIcon } from "../../../icons";
|
||||||
|
import { niceBytes } from "../../../common/utils";
|
||||||
|
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
|
||||||
|
import { Bucket, BucketList } from "../Buckets/types";
|
||||||
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
|
import api from "../../../common/api";
|
||||||
|
import history from "../../../history";
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
seeMore: {
|
||||||
|
marginTop: theme.spacing(3),
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
display: "flex",
|
||||||
|
overflow: "auto",
|
||||||
|
flexDirection: "column",
|
||||||
|
},
|
||||||
|
|
||||||
|
addSideBar: {
|
||||||
|
width: "320px",
|
||||||
|
padding: "20px",
|
||||||
|
},
|
||||||
|
errorBlock: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
tableToolbar: {
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
paddingRight: theme.spacing(0),
|
||||||
|
},
|
||||||
|
minTableHeader: {
|
||||||
|
color: "#393939",
|
||||||
|
"& tr": {
|
||||||
|
"& th": {
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionsTray: {
|
||||||
|
textAlign: "right",
|
||||||
|
"& button": {
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
searchField: {
|
||||||
|
background: "#FFFFFF",
|
||||||
|
padding: 12,
|
||||||
|
borderRadius: 5,
|
||||||
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
|
},
|
||||||
|
usedSpaceCol: {
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
subTitleLabel: {
|
||||||
|
alignItems: "center",
|
||||||
|
display: "flex",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IBrowseBucketsProps {
|
||||||
|
classes: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BrowseBuckets = ({ classes }: IBrowseBucketsProps) => {
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [page, setPage] = useState<number>(0);
|
||||||
|
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||||
|
const [error, setError] = useState<string>("");
|
||||||
|
const [records, setRecords] = useState<Bucket[]>([]);
|
||||||
|
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||||
|
const [filterBuckets, setFilterBuckets] = useState<string>("");
|
||||||
|
|
||||||
|
const offset = page * rowsPerPage;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (loading) {
|
||||||
|
api
|
||||||
|
.invoke("GET", `/api/v1/buckets?offset=${offset}&limit=${rowsPerPage}`)
|
||||||
|
.then((res: BucketList) => {
|
||||||
|
const buckets = get(res, "buckets", []);
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
setRecords(buckets);
|
||||||
|
setError("");
|
||||||
|
// if we get 0 results, and page > 0 , go down 1 page
|
||||||
|
if (
|
||||||
|
(res.buckets === undefined ||
|
||||||
|
res.buckets == null ||
|
||||||
|
res.buckets.length === 0) &&
|
||||||
|
page > 0
|
||||||
|
) {
|
||||||
|
const newPage = page - 1;
|
||||||
|
setPage(newPage);
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
setLoading(false);
|
||||||
|
setError(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [loading]);
|
||||||
|
|
||||||
|
const closeAddModalAndRefresh = () => {
|
||||||
|
setAddScreenOpen(false);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredRecords = records
|
||||||
|
.filter((b: Bucket) => {
|
||||||
|
if (filterBuckets === "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return b.name.indexOf(filterBuckets) >= 0;
|
||||||
|
})
|
||||||
|
.slice(offset, offset + rowsPerPage);
|
||||||
|
|
||||||
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangeRowsPerPage = (
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
|
) => {
|
||||||
|
const rPP = parseInt(event.target.value, 10);
|
||||||
|
setPage(0);
|
||||||
|
setRowsPerPage(rPP);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{addScreenOpen && (
|
||||||
|
<AddBucket
|
||||||
|
open={addScreenOpen}
|
||||||
|
closeModalAndRefresh={closeAddModalAndRefresh}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={6} className={classes.subTitleLabel}>
|
||||||
|
<Typography variant="h6">Buckets</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6} className={classes.actionsTray}>
|
||||||
|
<TextField
|
||||||
|
placeholder="Search Buckets"
|
||||||
|
className={classes.searchField}
|
||||||
|
id="search-resource"
|
||||||
|
label=""
|
||||||
|
onChange={(val) => {
|
||||||
|
setFilterBuckets(val.target.value);
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
disableUnderline: true,
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<CreateIcon />}
|
||||||
|
onClick={() => {
|
||||||
|
setAddScreenOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Bucket
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<br />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
{error !== "" && <span className={classes.errorBlock}>{error}</span>}
|
||||||
|
<TableWrapper
|
||||||
|
itemActions={[
|
||||||
|
{ type: "view", to: `/object-browser`, sendOnlyId: true },
|
||||||
|
]}
|
||||||
|
columns={[
|
||||||
|
{ label: "Name", elementKey: "name" },
|
||||||
|
{
|
||||||
|
label: "Used Space",
|
||||||
|
elementKey: "size",
|
||||||
|
renderFunction: niceBytes,
|
||||||
|
globalClass: classes.usedSpaceCol,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
isLoading={loading}
|
||||||
|
records={filteredRecords}
|
||||||
|
entityName="Buckets"
|
||||||
|
idField="name"
|
||||||
|
paginatorConfig={{
|
||||||
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
|
colSpan: 3,
|
||||||
|
count: filteredRecords.length,
|
||||||
|
rowsPerPage: rowsPerPage,
|
||||||
|
page: page,
|
||||||
|
SelectProps: {
|
||||||
|
inputProps: { "aria-label": "rows per page" },
|
||||||
|
native: true,
|
||||||
|
},
|
||||||
|
onChangePage: handleChangePage,
|
||||||
|
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||||
|
ActionsComponent: MinTablePaginationActions,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(BrowseBuckets);
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
// 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 get from "lodash/get";
|
||||||
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import { Grid, Typography } from "@material-ui/core";
|
||||||
|
import BrowseBuckets from "./BrowseBuckets";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
|
interface IObjectBrowserProps {
|
||||||
|
match: any;
|
||||||
|
classes: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
watchList: {
|
||||||
|
background: "white",
|
||||||
|
maxHeight: "400",
|
||||||
|
overflow: "auto",
|
||||||
|
"& ul": {
|
||||||
|
margin: "4",
|
||||||
|
padding: "0",
|
||||||
|
},
|
||||||
|
"& ul li": {
|
||||||
|
listStyle: "none",
|
||||||
|
margin: "0",
|
||||||
|
padding: "0",
|
||||||
|
borderBottom: "1px solid #dedede",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionsTray: {
|
||||||
|
textAlign: "right",
|
||||||
|
"& button": {
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inputField: {
|
||||||
|
background: "#FFFFFF",
|
||||||
|
padding: 12,
|
||||||
|
borderRadius: 5,
|
||||||
|
marginLeft: 10,
|
||||||
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
|
},
|
||||||
|
fieldContainer: {
|
||||||
|
background: "#FFFFFF",
|
||||||
|
padding: 0,
|
||||||
|
borderRadius: 5,
|
||||||
|
marginLeft: 10,
|
||||||
|
textAlign: "left",
|
||||||
|
minWidth: "206",
|
||||||
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
|
},
|
||||||
|
lastElementWPadding: {
|
||||||
|
paddingRight: "78",
|
||||||
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const ObjectBrowser = ({ match, classes }: IObjectBrowserProps) => {
|
||||||
|
const pathIn = get(match, "path", "");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<PageHeader label={"Object Browser"} />
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12} className={classes.container}>
|
||||||
|
{pathIn === "/object-browser" && <BrowseBuckets />}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(ObjectBrowser);
|
||||||
@@ -30,6 +30,8 @@ import AddPolicy from "./AddPolicy";
|
|||||||
import DeletePolicy from "./DeletePolicy";
|
import DeletePolicy from "./DeletePolicy";
|
||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -73,6 +75,7 @@ const styles = (theme: Theme) =>
|
|||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
},
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IPoliciesProps {
|
interface IPoliciesProps {
|
||||||
@@ -222,70 +225,67 @@ const Policies = ({ classes }: IPoliciesProps) => {
|
|||||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<PageHeader label="IAM Policies" />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Typography variant="h6">IAM Policies</Typography>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
</Grid>
|
<TextField
|
||||||
<Grid item xs={12}>
|
placeholder="Search Policies"
|
||||||
<br />
|
className={classes.searchField}
|
||||||
</Grid>
|
id="search-resource"
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
label=""
|
||||||
<TextField
|
onChange={(val) => {
|
||||||
placeholder="Search Policies"
|
setPage(0);
|
||||||
className={classes.searchField}
|
setFilterPolicies(val.target.value);
|
||||||
id="search-resource"
|
}}
|
||||||
label=""
|
InputProps={{
|
||||||
onChange={(val) => {
|
disableUnderline: true,
|
||||||
setPage(0);
|
startAdornment: (
|
||||||
setFilterPolicies(val.target.value);
|
<InputAdornment position="start">
|
||||||
}}
|
<SearchIcon />
|
||||||
InputProps={{
|
</InputAdornment>
|
||||||
disableUnderline: true,
|
),
|
||||||
startAdornment: (
|
}}
|
||||||
<InputAdornment position="start">
|
/>
|
||||||
<SearchIcon />
|
<Button
|
||||||
</InputAdornment>
|
variant="contained"
|
||||||
),
|
color="primary"
|
||||||
}}
|
startIcon={<CreateIcon />}
|
||||||
/>
|
onClick={() => {
|
||||||
<Button
|
setAddScreenOpen(true);
|
||||||
variant="contained"
|
setPolicyEdit(null);
|
||||||
color="primary"
|
}}
|
||||||
startIcon={<CreateIcon />}
|
>
|
||||||
onClick={() => {
|
Create Policy
|
||||||
setAddScreenOpen(true);
|
</Button>
|
||||||
setPolicyEdit(null);
|
</Grid>
|
||||||
}}
|
<Grid item xs={12}>
|
||||||
>
|
<br />
|
||||||
Create Policy
|
</Grid>
|
||||||
</Button>
|
<Grid item xs={12}>
|
||||||
</Grid>
|
<TableWrapper
|
||||||
<Grid item xs={12}>
|
itemActions={tableActions}
|
||||||
<br />
|
columns={[{ label: "Name", elementKey: "name" }]}
|
||||||
</Grid>
|
isLoading={loading}
|
||||||
<Grid item xs={12}>
|
records={paginatedRecords}
|
||||||
<TableWrapper
|
entityName="Policies"
|
||||||
itemActions={tableActions}
|
idField="name"
|
||||||
columns={[{ label: "Name", elementKey: "name" }]}
|
paginatorConfig={{
|
||||||
isLoading={loading}
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
records={paginatedRecords}
|
colSpan: 3,
|
||||||
entityName="Policies"
|
count: filteredRecords.length,
|
||||||
idField="name"
|
rowsPerPage: rowsPerPage,
|
||||||
paginatorConfig={{
|
page: page,
|
||||||
rowsPerPageOptions: [5, 10, 25],
|
SelectProps: {
|
||||||
colSpan: 3,
|
inputProps: { "aria-label": "rows per page" },
|
||||||
count: filteredRecords.length,
|
native: true,
|
||||||
rowsPerPage: rowsPerPage,
|
},
|
||||||
page: page,
|
onChangePage: handleChangePage,
|
||||||
SelectProps: {
|
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||||
inputProps: { "aria-label": "rows per page" },
|
ActionsComponent: MinTablePaginationActions,
|
||||||
native: true,
|
}}
|
||||||
},
|
/>
|
||||||
onChangePage: handleChangePage,
|
</Grid>
|
||||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
|
||||||
ActionsComponent: MinTablePaginationActions,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
209
portal-ui/src/screens/Console/RemoteBuckets/AddRemoteBucket.tsx
Normal file
209
portal-ui/src/screens/Console/RemoteBuckets/AddRemoteBucket.tsx
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
// 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, { useState, useEffect } from "react";
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import { modalBasic } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import api from "../../../common/api";
|
||||||
|
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||||
|
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
|
import SelectWrapper from "../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
errorBlock: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
textAlign: "right",
|
||||||
|
},
|
||||||
|
...modalBasic,
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IAddBucketProps {
|
||||||
|
classes: any;
|
||||||
|
open: boolean;
|
||||||
|
closeModalAndRefresh: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddRemoteBucket = ({
|
||||||
|
classes,
|
||||||
|
open,
|
||||||
|
closeModalAndRefresh,
|
||||||
|
}: IAddBucketProps) => {
|
||||||
|
const [addLoading, setAddLoading] = useState(false);
|
||||||
|
const [addError, setAddError] = useState("");
|
||||||
|
const [accessKey, setAccessKey] = useState("");
|
||||||
|
const [secretKey, setSecretKey] = useState("");
|
||||||
|
const [sourceBucket, setSourceBucket] = useState("");
|
||||||
|
const [targetURL, setTargetURL] = useState("");
|
||||||
|
const [targetBucket, setTargetBucket] = useState("");
|
||||||
|
const [region, setRegion] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (addLoading) {
|
||||||
|
addRecord();
|
||||||
|
}
|
||||||
|
}, [addLoading]);
|
||||||
|
|
||||||
|
const addRecord = () => {
|
||||||
|
const remoteBucketInfo = {
|
||||||
|
accessKey: accessKey,
|
||||||
|
secretKey: secretKey,
|
||||||
|
sourceBucket: sourceBucket,
|
||||||
|
targetURL: targetURL,
|
||||||
|
targetBucket: targetBucket,
|
||||||
|
region: region,
|
||||||
|
};
|
||||||
|
|
||||||
|
api
|
||||||
|
.invoke("POST", "/api/v1/remote-buckets", remoteBucketInfo)
|
||||||
|
.then((res) => {
|
||||||
|
setAddLoading(false);
|
||||||
|
setAddError("");
|
||||||
|
closeModalAndRefresh();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setAddLoading(false);
|
||||||
|
setAddError(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalWrapper
|
||||||
|
title="Create Remote Bucket"
|
||||||
|
modalOpen={open}
|
||||||
|
onClose={() => {
|
||||||
|
setAddError("");
|
||||||
|
closeModalAndRefresh();
|
||||||
|
}}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
noValidate
|
||||||
|
autoComplete="off"
|
||||||
|
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
addRecord();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12} className={classes.formScrollable}>
|
||||||
|
{addError !== "" && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography
|
||||||
|
component="p"
|
||||||
|
variant="body1"
|
||||||
|
className={classes.errorBlock}
|
||||||
|
>
|
||||||
|
{addError}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="accessKey"
|
||||||
|
name="accessKey"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setAccessKey(e.target.value);
|
||||||
|
}}
|
||||||
|
label="Access Key"
|
||||||
|
value={accessKey}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="secretKey"
|
||||||
|
name="secretKey"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSecretKey(e.target.value);
|
||||||
|
}}
|
||||||
|
label="Secret Key"
|
||||||
|
value={secretKey}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="sourceBucket"
|
||||||
|
name="sourceBucket"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSourceBucket(e.target.value);
|
||||||
|
}}
|
||||||
|
label="Source Bucket"
|
||||||
|
value={sourceBucket}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="targetURL"
|
||||||
|
name="targetURL"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setTargetURL(e.target.value);
|
||||||
|
}}
|
||||||
|
placeholder="https://play.min.io:9000"
|
||||||
|
label="Target URL"
|
||||||
|
value={targetURL}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="targetBucket"
|
||||||
|
name="targetBucket"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setTargetBucket(e.target.value);
|
||||||
|
}}
|
||||||
|
label="Target Bucket"
|
||||||
|
value={targetBucket}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="region"
|
||||||
|
name="region"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setRegion(e.target.value);
|
||||||
|
}}
|
||||||
|
label="Region"
|
||||||
|
value={region}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.buttonContainer}>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={addLoading}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
{addLoading && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<LinearProgress />
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</form>
|
||||||
|
</ModalWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(AddRemoteBucket);
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
// 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, { useState, useEffect } from "react";
|
||||||
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import get from "lodash/get";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
|
LinearProgress,
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import api from "../../../common/api";
|
||||||
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
errorBlock: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IDeleteEventProps {
|
||||||
|
classes: any;
|
||||||
|
closeDeleteModalAndRefresh: (refresh: boolean) => void;
|
||||||
|
deleteOpen: boolean;
|
||||||
|
bucketName: any;
|
||||||
|
sourceBucket: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IDeleteEventState {
|
||||||
|
deleteLoading: boolean;
|
||||||
|
deleteError: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DeleteRemoteBucket = ({
|
||||||
|
deleteOpen,
|
||||||
|
closeDeleteModalAndRefresh,
|
||||||
|
classes,
|
||||||
|
bucketName,
|
||||||
|
sourceBucket,
|
||||||
|
}: IDeleteEventProps) => {
|
||||||
|
const [deleteError, setDeleteError] = useState("");
|
||||||
|
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (deleteLoading) {
|
||||||
|
removeRecord();
|
||||||
|
}
|
||||||
|
}, [deleteLoading]);
|
||||||
|
|
||||||
|
const removeRecord = () => {
|
||||||
|
api
|
||||||
|
.invoke("DELETE", `/api/v1/remote-buckets/${sourceBucket}/${bucketName}`)
|
||||||
|
.then(() => {
|
||||||
|
setDeleteLoading(false);
|
||||||
|
setDeleteError("");
|
||||||
|
closeDeleteModalAndRefresh(true);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setDeleteLoading(false);
|
||||||
|
setDeleteError(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={deleteOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setDeleteError("");
|
||||||
|
closeDeleteModalAndRefresh(false);
|
||||||
|
}}
|
||||||
|
aria-labelledby="alert-dialog-title"
|
||||||
|
aria-describedby="alert-dialog-description"
|
||||||
|
>
|
||||||
|
<DialogTitle id="alert-dialog-title">Delete Remote Bucket</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
{deleteLoading && <LinearProgress />}
|
||||||
|
<DialogContentText id="alert-dialog-description">
|
||||||
|
Are you sure you want to delete <strong>'{bucketName}'</strong> Remote
|
||||||
|
Bucket?
|
||||||
|
{deleteError !== "" && (
|
||||||
|
<React.Fragment>
|
||||||
|
<br />
|
||||||
|
<Typography
|
||||||
|
component="p"
|
||||||
|
variant="body1"
|
||||||
|
className={classes.errorBlock}
|
||||||
|
>
|
||||||
|
{deleteError}
|
||||||
|
</Typography>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setDeleteError("");
|
||||||
|
closeDeleteModalAndRefresh(false);
|
||||||
|
}}
|
||||||
|
color="primary"
|
||||||
|
disabled={deleteLoading}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setDeleteLoading(true);
|
||||||
|
}}
|
||||||
|
color="secondary"
|
||||||
|
autoFocus
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(DeleteRemoteBucket);
|
||||||
279
portal-ui/src/screens/Console/RemoteBuckets/RemoteBuckets.tsx
Normal file
279
portal-ui/src/screens/Console/RemoteBuckets/RemoteBuckets.tsx
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
// 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, { useState, useEffect } from "react";
|
||||||
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
import { Button } from "@material-ui/core";
|
||||||
|
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 Moment from "react-moment";
|
||||||
|
import api from "../../../common/api";
|
||||||
|
import { Bucket } from "../Buckets/types";
|
||||||
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
|
import AddRemoteBucket from "./AddRemoteBucket";
|
||||||
|
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
|
||||||
|
import { CreateIcon } from "../../../icons";
|
||||||
|
import { IRemoteBucket, IRemoteBucketsResponse } from "./types";
|
||||||
|
import DeleteRemoteBucket from "./DeleteRemoteBucket";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
seeMore: {
|
||||||
|
marginTop: theme.spacing(3),
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
display: "flex",
|
||||||
|
overflow: "auto",
|
||||||
|
flexDirection: "column",
|
||||||
|
},
|
||||||
|
|
||||||
|
addSideBar: {
|
||||||
|
width: "320px",
|
||||||
|
padding: "20px",
|
||||||
|
},
|
||||||
|
errorBlock: {
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
tableToolbar: {
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
paddingRight: theme.spacing(0),
|
||||||
|
},
|
||||||
|
minTableHeader: {
|
||||||
|
color: "#393939",
|
||||||
|
"& tr": {
|
||||||
|
"& th": {
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionsTray: {
|
||||||
|
textAlign: "right",
|
||||||
|
"& button": {
|
||||||
|
marginLeft: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
searchField: {
|
||||||
|
background: "#FFFFFF",
|
||||||
|
padding: 12,
|
||||||
|
borderRadius: 5,
|
||||||
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IRemoteListBucketsProps {
|
||||||
|
classes: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RemoteBucketsList = ({ classes }: IRemoteListBucketsProps) => {
|
||||||
|
const [records, setRecords] = useState<IRemoteBucket[]>([]);
|
||||||
|
const [totalRecords, setTotalRecords] = useState<number>(0);
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [error, setError] = useState<string>("");
|
||||||
|
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
|
||||||
|
const [deleteScreenOpen, setDeleteOpen] = useState<boolean>(false);
|
||||||
|
const [page, setPage] = useState<number>(0);
|
||||||
|
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||||
|
const [selectedBucket, setSelectedBucket] = useState<IRemoteBucket>({
|
||||||
|
remoteARN: "",
|
||||||
|
accessKey: "",
|
||||||
|
name: "",
|
||||||
|
secretKey: "",
|
||||||
|
service: "",
|
||||||
|
sourceBucket: "",
|
||||||
|
status: "",
|
||||||
|
targetBucket: "",
|
||||||
|
targetURL: "",
|
||||||
|
});
|
||||||
|
const [filterBuckets, setFilterBuckets] = useState<string>("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (loading) {
|
||||||
|
fetchRecords();
|
||||||
|
}
|
||||||
|
}, [loading]);
|
||||||
|
|
||||||
|
const closeAddModalAndRefresh = () => {
|
||||||
|
setAddScreenOpen(false);
|
||||||
|
setLoading(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeDeleteModalAndRefresh = (reload: boolean) => {
|
||||||
|
setDeleteOpen(false);
|
||||||
|
|
||||||
|
if (reload) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchRecords = () => {
|
||||||
|
const offset = page * rowsPerPage;
|
||||||
|
api
|
||||||
|
.invoke("GET", `/api/v1/remote-buckets`)
|
||||||
|
.then((res: IRemoteBucketsResponse) => {
|
||||||
|
setLoading(false);
|
||||||
|
setRecords(res.buckets || []);
|
||||||
|
setTotalRecords(!res.buckets ? 0 : res.total);
|
||||||
|
setError("");
|
||||||
|
// if we get 0 results, and page > 0 , go down 1 page
|
||||||
|
if (
|
||||||
|
(res.buckets === undefined ||
|
||||||
|
res.buckets == null ||
|
||||||
|
res.buckets.length === 0) &&
|
||||||
|
page > 0
|
||||||
|
) {
|
||||||
|
const newPage = page - 1;
|
||||||
|
setPage(newPage);
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
setLoading(false);
|
||||||
|
setError(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const offset = page * rowsPerPage;
|
||||||
|
|
||||||
|
const handleChangePage = (event: unknown, newPage: number) => {
|
||||||
|
setPage(newPage);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangeRowsPerPage = (
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
|
) => {
|
||||||
|
const rPP = parseInt(event.target.value, 10);
|
||||||
|
setPage(0);
|
||||||
|
setRowsPerPage(rPP);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmDeleteRemoteBucket = (arnRemoteBucket: IRemoteBucket) => {
|
||||||
|
setSelectedBucket(arnRemoteBucket);
|
||||||
|
setDeleteOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableActions = [{ type: "delete", onClick: confirmDeleteRemoteBucket }];
|
||||||
|
|
||||||
|
const filteredRecords = records
|
||||||
|
.slice(offset, offset + rowsPerPage)
|
||||||
|
.filter((b: IRemoteBucket) => {
|
||||||
|
if (filterBuckets === "") {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (b.name.indexOf(filterBuckets) >= 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{addScreenOpen && (
|
||||||
|
<AddRemoteBucket
|
||||||
|
open={addScreenOpen}
|
||||||
|
closeModalAndRefresh={() => {
|
||||||
|
closeAddModalAndRefresh();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{deleteScreenOpen && (
|
||||||
|
<DeleteRemoteBucket
|
||||||
|
bucketName={selectedBucket.remoteARN}
|
||||||
|
sourceBucket={selectedBucket.sourceBucket}
|
||||||
|
closeDeleteModalAndRefresh={(reload) => {
|
||||||
|
closeDeleteModalAndRefresh(reload);
|
||||||
|
}}
|
||||||
|
deleteOpen={deleteScreenOpen}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<PageHeader label="Remote Buckets" />
|
||||||
|
<Grid container>
|
||||||
|
<Grid item xs={12} className={classes.container}>
|
||||||
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
|
<TextField
|
||||||
|
placeholder="Search Remote Buckets"
|
||||||
|
className={classes.searchField}
|
||||||
|
id="search-resource"
|
||||||
|
label=""
|
||||||
|
onChange={(val) => {
|
||||||
|
setFilterBuckets(val.target.value);
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
disableUnderline: true,
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<CreateIcon />}
|
||||||
|
onClick={() => {
|
||||||
|
setAddScreenOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Create Remote Bucket
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<br />
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<TableWrapper
|
||||||
|
itemActions={tableActions}
|
||||||
|
columns={[
|
||||||
|
{ label: "Remote ARN", elementKey: "remoteARN" },
|
||||||
|
{ label: "Source Bucket", elementKey: "sourceBucket" },
|
||||||
|
{ label: "Target Bucket", elementKey: "targetBucket" },
|
||||||
|
{ label: "Status", elementKey: "status" },
|
||||||
|
]}
|
||||||
|
isLoading={loading}
|
||||||
|
records={filteredRecords}
|
||||||
|
entityName="Remote Buckets"
|
||||||
|
idField="remoteARN"
|
||||||
|
paginatorConfig={{
|
||||||
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
|
colSpan: 3,
|
||||||
|
count: totalRecords,
|
||||||
|
rowsPerPage: rowsPerPage,
|
||||||
|
page: page,
|
||||||
|
SelectProps: {
|
||||||
|
inputProps: { "aria-label": "rows per page" },
|
||||||
|
native: true,
|
||||||
|
},
|
||||||
|
onChangePage: handleChangePage,
|
||||||
|
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||||
|
ActionsComponent: MinTablePaginationActions,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(RemoteBucketsList);
|
||||||
32
portal-ui/src/screens/Console/RemoteBuckets/types.ts
Normal file
32
portal-ui/src/screens/Console/RemoteBuckets/types.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// 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 interface IRemoteBucketsResponse {
|
||||||
|
buckets: IRemoteBucket[];
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IRemoteBucket {
|
||||||
|
name: string;
|
||||||
|
accessKey: string;
|
||||||
|
secretKey: string;
|
||||||
|
sourceBucket: string;
|
||||||
|
targetURL: string;
|
||||||
|
targetBucket: string;
|
||||||
|
remoteARN: string;
|
||||||
|
status: string;
|
||||||
|
service: string;
|
||||||
|
}
|
||||||
@@ -31,6 +31,8 @@ import InputAdornment from "@material-ui/core/InputAdornment";
|
|||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
import { stringSort } from "../../../utils/sortFunctions";
|
import { stringSort } from "../../../utils/sortFunctions";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -85,6 +87,7 @@ const styles = (theme: Theme) =>
|
|||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
},
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IServiceAccountsProps {
|
interface IServiceAccountsProps {
|
||||||
@@ -225,81 +228,78 @@ const ServiceAccounts = ({ classes }: IServiceAccountsProps) => {
|
|||||||
entity="Service Account"
|
entity="Service Account"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<PageHeader label="Service Accounts" />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Typography variant="h6">Service Accounts</Typography>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
</Grid>
|
<TextField
|
||||||
<Grid item xs={12}>
|
placeholder="Search Service Accounts"
|
||||||
<br />
|
className={classes.searchField}
|
||||||
</Grid>
|
id="search-resource"
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
label=""
|
||||||
<TextField
|
InputProps={{
|
||||||
placeholder="Search Service Accounts"
|
disableUnderline: true,
|
||||||
className={classes.searchField}
|
startAdornment: (
|
||||||
id="search-resource"
|
<InputAdornment position="start">
|
||||||
label=""
|
<SearchIcon />
|
||||||
InputProps={{
|
</InputAdornment>
|
||||||
disableUnderline: true,
|
),
|
||||||
startAdornment: (
|
}}
|
||||||
<InputAdornment position="start">
|
onChange={(e) => {
|
||||||
<SearchIcon />
|
setFilter(e.target.value);
|
||||||
</InputAdornment>
|
setPage(0);
|
||||||
),
|
}}
|
||||||
}}
|
/>
|
||||||
onChange={(e) => {
|
<Button
|
||||||
setFilter(e.target.value);
|
variant="contained"
|
||||||
setPage(0);
|
color="primary"
|
||||||
}}
|
startIcon={<CreateIcon />}
|
||||||
/>
|
onClick={() => {
|
||||||
<Button
|
setAddScreenOpen(true);
|
||||||
variant="contained"
|
setSelectedServiceAccount(null);
|
||||||
color="primary"
|
}}
|
||||||
startIcon={<CreateIcon />}
|
|
||||||
onClick={() => {
|
|
||||||
setAddScreenOpen(true);
|
|
||||||
setSelectedServiceAccount(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Create service account
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<br />
|
|
||||||
</Grid>
|
|
||||||
{error !== "" && (
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<Typography
|
|
||||||
component="p"
|
|
||||||
variant="body1"
|
|
||||||
className={classes.errorBlock}
|
|
||||||
>
|
>
|
||||||
{error}
|
Create service account
|
||||||
</Typography>
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<br />
|
||||||
|
</Grid>
|
||||||
|
{error !== "" && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography
|
||||||
|
component="p"
|
||||||
|
variant="body1"
|
||||||
|
className={classes.errorBlock}
|
||||||
|
>
|
||||||
|
{error}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<TableWrapper
|
||||||
|
isLoading={loading}
|
||||||
|
records={paginatedRecords}
|
||||||
|
entityName={"Service Accounts"}
|
||||||
|
idField={""}
|
||||||
|
columns={[{ label: "Service Account", elementKey: "" }]}
|
||||||
|
itemActions={tableActions}
|
||||||
|
paginatorConfig={{
|
||||||
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
|
colSpan: 4,
|
||||||
|
count: records.length,
|
||||||
|
rowsPerPage: rowsPerPage,
|
||||||
|
page,
|
||||||
|
SelectProps: {
|
||||||
|
inputProps: { "aria-label": "rows per page" },
|
||||||
|
native: true,
|
||||||
|
},
|
||||||
|
onChangePage: handleChangePage,
|
||||||
|
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||||
|
ActionsComponent: MinTablePaginationActions,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
|
||||||
<Grid item xs={12}>
|
|
||||||
<TableWrapper
|
|
||||||
isLoading={loading}
|
|
||||||
records={paginatedRecords}
|
|
||||||
entityName={"Service Accounts"}
|
|
||||||
idField={""}
|
|
||||||
columns={[{ label: "Service Account", elementKey: "" }]}
|
|
||||||
itemActions={tableActions}
|
|
||||||
paginatorConfig={{
|
|
||||||
rowsPerPageOptions: [5, 10, 25],
|
|
||||||
colSpan: 4,
|
|
||||||
count: records.length,
|
|
||||||
rowsPerPage: rowsPerPage,
|
|
||||||
page,
|
|
||||||
SelectProps: {
|
|
||||||
inputProps: { "aria-label": "rows per page" },
|
|
||||||
native: true,
|
|
||||||
},
|
|
||||||
onChangePage: handleChangePage,
|
|
||||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
|
||||||
ActionsComponent: MinTablePaginationActions,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -27,11 +27,12 @@ import {
|
|||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import api from "../../../../common/api";
|
import api from "../../../../common/api";
|
||||||
|
import { ITenant } from "./types";
|
||||||
|
|
||||||
interface IDeleteTenant {
|
interface IDeleteTenant {
|
||||||
classes: any;
|
classes: any;
|
||||||
deleteOpen: boolean;
|
deleteOpen: boolean;
|
||||||
selectedTenant: string;
|
selectedTenant: ITenant;
|
||||||
closeDeleteModalAndRefresh: (refreshList: boolean) => any;
|
closeDeleteModalAndRefresh: (refreshList: boolean) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +55,10 @@ const DeleteTenant = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (deleteLoading) {
|
if (deleteLoading) {
|
||||||
api
|
api
|
||||||
.invoke("DELETE", `/api/v1/tenants/${selectedTenant}`)
|
.invoke(
|
||||||
|
"DELETE",
|
||||||
|
`/api/v1/namespaces/${selectedTenant.namespace}/tenants/${selectedTenant.name}`
|
||||||
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setDeleteLoading(false);
|
setDeleteLoading(false);
|
||||||
setDeleteError("");
|
setDeleteError("");
|
||||||
@@ -85,7 +89,7 @@ const DeleteTenant = ({
|
|||||||
<DialogContent>
|
<DialogContent>
|
||||||
{deleteLoading && <LinearProgress />}
|
{deleteLoading && <LinearProgress />}
|
||||||
<DialogContentText id="alert-dialog-description">
|
<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 !== "" && (
|
{deleteError !== "" && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import Typography from "@material-ui/core/Typography";
|
|||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
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 { CreateIcon } from "../../../../icons";
|
||||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||||
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
import { MinTablePaginationActions } from "../../../../common/MinTablePaginationActions";
|
||||||
@@ -32,6 +32,8 @@ import DeleteTenant from "./DeleteTenant";
|
|||||||
import AddTenant from "./AddTenant";
|
import AddTenant from "./AddTenant";
|
||||||
import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
|
import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
|
||||||
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
|
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
|
||||||
|
import history from "../../../../history";
|
||||||
|
import RefreshIcon from "@material-ui/icons/Refresh";
|
||||||
|
|
||||||
interface ITenantsList {
|
interface ITenantsList {
|
||||||
classes: any;
|
classes: any;
|
||||||
@@ -122,11 +124,16 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmDeleteTenant = (tenant: string) => {
|
const confirmDeleteTenant = (tenant: ITenant) => {
|
||||||
setSelectedTenant(tenant);
|
setSelectedTenant(tenant);
|
||||||
setDeleteOpen(true);
|
setDeleteOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const redirectToTenantDetails = (tenant: ITenant) => {
|
||||||
|
history.push(`/namespaces/${tenant.namespace}/tenants/${tenant.name}`);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
const closeCredentialsModal = () => {
|
const closeCredentialsModal = () => {
|
||||||
setShowNewCredentials(false);
|
setShowNewCredentials(false);
|
||||||
setCreatedAccount(null);
|
setCreatedAccount(null);
|
||||||
@@ -149,8 +156,8 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const tableActions = [
|
const tableActions = [
|
||||||
{ type: "view", to: `/tenants`, sendOnlyId: true },
|
{ type: "view", onClick: redirectToTenantDetails },
|
||||||
{ type: "delete", onClick: confirmDeleteTenant, sendOnlyId: true },
|
{ type: "delete", onClick: confirmDeleteTenant },
|
||||||
];
|
];
|
||||||
|
|
||||||
const filteredRecords = records
|
const filteredRecords = records
|
||||||
@@ -187,9 +194,7 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < resTenants.length; i++) {
|
for (let i = 0; i < resTenants.length; i++) {
|
||||||
const total =
|
resTenants[i].capacity = niceBytes(resTenants[i].total_size + "");
|
||||||
resTenants[i].volume_count * resTenants[i].volume_size;
|
|
||||||
resTenants[i].capacity = niceBytes(total + "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setRecords(resTenants);
|
setRecords(resTenants);
|
||||||
@@ -248,6 +253,17 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
|||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
|
<IconButton
|
||||||
|
color="primary"
|
||||||
|
aria-label="Refresh Tenant List"
|
||||||
|
component="span"
|
||||||
|
onClick={() => {
|
||||||
|
setIsLoading(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RefreshIcon />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Search Tenants"
|
placeholder="Search Tenants"
|
||||||
className={classes.searchField}
|
className={classes.searchField}
|
||||||
|
|||||||
@@ -86,7 +86,14 @@ const ZonesMultiSelector = ({
|
|||||||
onChange,
|
onChange,
|
||||||
classes,
|
classes,
|
||||||
}: IZonesMultiSelector) => {
|
}: 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 [currentElements, setCurrentElements] = useState<IZone[]>([]);
|
||||||
const [internalCounter, setInternalCounter] = useState<number>(1);
|
const [internalCounter, setInternalCounter] = useState<number>(1);
|
||||||
|
|||||||
@@ -17,18 +17,32 @@
|
|||||||
export interface IZone {
|
export interface IZone {
|
||||||
name: string;
|
name: string;
|
||||||
servers: number;
|
servers: number;
|
||||||
|
volumes_per_server: number;
|
||||||
|
volume_configuration: IVolumeConfiguration;
|
||||||
// computed
|
// computed
|
||||||
capacity: string;
|
capacity: string;
|
||||||
volumes: number;
|
volumes: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IAddZoneRequest {
|
||||||
|
name: string;
|
||||||
|
servers: number;
|
||||||
|
volumes_per_server: number;
|
||||||
|
volume_configuration: IVolumeConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IVolumeConfiguration {
|
export interface IVolumeConfiguration {
|
||||||
size: number;
|
size: number;
|
||||||
storage_class: string;
|
storage_class: string;
|
||||||
|
labels: { [key: string]: any } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITenant {
|
export interface ITenant {
|
||||||
|
total_size: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
namespace: string;
|
||||||
|
image: string;
|
||||||
|
console_image: string;
|
||||||
zone_count: number;
|
zone_count: number;
|
||||||
currentState: string;
|
currentState: string;
|
||||||
instance_count: 4;
|
instance_count: 4;
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import {
|
|||||||
niceBytes,
|
niceBytes,
|
||||||
} from "../../../../common/utils";
|
} from "../../../../common/utils";
|
||||||
import { Button, LinearProgress } from "@material-ui/core";
|
import { Button, LinearProgress } from "@material-ui/core";
|
||||||
|
import api from "../../../../common/api";
|
||||||
|
import { IAddZoneRequest, ITenant } from "../ListTenants/types";
|
||||||
|
|
||||||
interface IAddZoneProps {
|
interface IAddZoneProps {
|
||||||
|
tenant: ITenant;
|
||||||
classes: any;
|
classes: any;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onCloseZoneAndReload: (shouldReload: boolean) => void;
|
onCloseZoneAndReload: (shouldReload: boolean) => void;
|
||||||
volumesPerInstance: number;
|
|
||||||
volumeSize: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
@@ -63,18 +64,18 @@ const styles = (theme: Theme) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
const AddZoneModal = ({
|
const AddZoneModal = ({
|
||||||
|
tenant,
|
||||||
classes,
|
classes,
|
||||||
open,
|
open,
|
||||||
onCloseZoneAndReload,
|
onCloseZoneAndReload,
|
||||||
volumesPerInstance,
|
|
||||||
volumeSize,
|
|
||||||
}: IAddZoneProps) => {
|
}: IAddZoneProps) => {
|
||||||
const [addSending, setAddSending] = useState<boolean>(false);
|
const [addSending, setAddSending] = useState<boolean>(false);
|
||||||
const [zoneName, setZoneName] = useState<string>("");
|
const [numberOfNodes, setNumberOfNodes] = useState<number>(0);
|
||||||
const [numberOfInstances, setNumberOfInstances] = useState<number>(0);
|
const [volumeSize, setVolumeSize] = useState<number>(0);
|
||||||
|
const [volumesPerServer, setVolumesPerSever] = useState<number>(0);
|
||||||
|
|
||||||
const instanceCapacity: number = volumeSize * volumesPerInstance;
|
const instanceCapacity: number = volumeSize * 1073741824 * volumesPerServer;
|
||||||
const totalCapacity: number = instanceCapacity * numberOfInstances;
|
const totalCapacity: number = instanceCapacity * numberOfNodes;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalWrapper
|
<ModalWrapper
|
||||||
@@ -88,30 +89,66 @@ const AddZoneModal = ({
|
|||||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setAddSending(true);
|
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}>
|
<Grid item xs={12}>
|
||||||
<InputBoxWrapper
|
<InputBoxWrapper
|
||||||
id="zone_name"
|
id="number_of_nodes"
|
||||||
name="zone_name"
|
name="number_of_nodes"
|
||||||
type="string"
|
type="number"
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setZoneName(e.target.value);
|
setNumberOfNodes(parseInt(e.target.value));
|
||||||
}}
|
}}
|
||||||
label="Name"
|
label="Number o Nodes"
|
||||||
value={zoneName}
|
value={numberOfNodes.toString(10)}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<InputBoxWrapper
|
<InputBoxWrapper
|
||||||
id="number_instances"
|
id="zone_size"
|
||||||
name="number_instances"
|
name="zone_size"
|
||||||
type="number"
|
type="number"
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setNumberOfInstances(parseInt(e.target.value));
|
setVolumeSize(parseInt(e.target.value));
|
||||||
}}
|
}}
|
||||||
label="Drives per Server"
|
label="Volume Size (Gi)"
|
||||||
value={numberOfInstances.toString(10)}
|
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>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
|
|||||||
@@ -91,19 +91,6 @@ const styles = (theme: Theme) =>
|
|||||||
...modalBasic,
|
...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 TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
||||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||||
const [capacity, setCapacity] = useState<number>(0);
|
const [capacity, setCapacity] = useState<number>(0);
|
||||||
@@ -142,24 +129,38 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
|
|
||||||
const loadInfo = () => {
|
const loadInfo = () => {
|
||||||
const tenantName = match.params["tenantName"];
|
const tenantName = match.params["tenantName"];
|
||||||
|
const tenantNamespace = match.params["tenantNamespace"];
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
api
|
api
|
||||||
.invoke("GET", `/api/v1/tenants/${tenantName}`)
|
.invoke(
|
||||||
|
"GET",
|
||||||
|
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}`
|
||||||
|
)
|
||||||
.then((res: ITenant) => {
|
.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 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) {
|
for (let zone of resZones) {
|
||||||
zone.volumes = res.volumes_per_server;
|
const cap =
|
||||||
const cap = res.volumes_per_server * res.volume_size * zone.servers;
|
zone.volumes_per_server *
|
||||||
|
zone.servers *
|
||||||
|
zone.volume_configuration.size;
|
||||||
|
zone.name = `zone-${count}`;
|
||||||
zone.capacity = niceBytes(cap + "");
|
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);
|
setZones(resZones);
|
||||||
|
|
||||||
setTenant(res);
|
setTenant(res);
|
||||||
@@ -182,8 +183,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
<AddZoneModal
|
<AddZoneModal
|
||||||
open={addZoneOpen}
|
open={addZoneOpen}
|
||||||
onCloseZoneAndReload={onCloseZoneAndRefresh}
|
onCloseZoneAndReload={onCloseZoneAndRefresh}
|
||||||
volumeSize={tenant.volume_size}
|
tenant={tenant}
|
||||||
volumesPerInstance={tenant.volumes_per_server}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{addBucketOpen && (
|
{addBucketOpen && (
|
||||||
@@ -201,7 +201,7 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Typography variant="h6">
|
<Typography variant="h6">
|
||||||
Tenant > {match.params["tenantName"]}
|
{`Tenant > ${match.params["tenantName"]}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
@@ -212,40 +212,18 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
<div className={classes.infoGrid}>
|
<div className={classes.infoGrid}>
|
||||||
<div>Capacity:</div>
|
<div>Capacity:</div>
|
||||||
<div>{niceBytes(capacity.toString(10))}</div>
|
<div>{niceBytes(capacity.toString(10))}</div>
|
||||||
|
<div>Minio:</div>
|
||||||
|
<div>{tenant ? tenant.image : ""}</div>
|
||||||
<div>Zones:</div>
|
<div>Zones:</div>
|
||||||
<div>{zoneCount}</div>
|
<div>{zoneCount}</div>
|
||||||
<div>External IDP:</div>
|
<div>Console:</div>
|
||||||
<div>
|
<div>{tenant ? tenant.console_image : ""}</div>
|
||||||
{externalIDP ? "Yes" : "No"}
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
size="small"
|
|
||||||
onClick={() => {}}
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div>Instances:</div>
|
<div>Instances:</div>
|
||||||
<div>{instances}</div>
|
<div>{instances}</div>
|
||||||
<div>External KMS:</div>
|
|
||||||
<div>{externalKMS ? "Yes" : "No"} </div>
|
|
||||||
<div>Volumes:</div>
|
<div>Volumes:</div>
|
||||||
<div>{volumes}</div>
|
<div>{volumes}</div>
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
<div className={classes.masterActions}>
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
fullWidth
|
|
||||||
onClick={() => {}}
|
|
||||||
>
|
|
||||||
Warp
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
@@ -261,185 +239,59 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
|
|||||||
aria-label="tenant-tabs"
|
aria-label="tenant-tabs"
|
||||||
>
|
>
|
||||||
<Tab label="Zones" />
|
<Tab label="Zones" />
|
||||||
<Tab label="Buckets" />
|
|
||||||
<Tab label="Replication" />
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={6} className={classes.actionsTray}>
|
<Grid item xs={6} className={classes.actionsTray}>
|
||||||
{selectedTab === 0 && (
|
<Button
|
||||||
<Button
|
variant="contained"
|
||||||
variant="contained"
|
color="primary"
|
||||||
color="primary"
|
startIcon={<CreateIcon />}
|
||||||
startIcon={<CreateIcon />}
|
onClick={() => {
|
||||||
onClick={() => {
|
setAddZone(true);
|
||||||
setAddZone(true);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
Add Zone
|
||||||
Add Zone
|
</Button>
|
||||||
</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>
|
|
||||||
)}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
{selectedTab === 0 && (
|
<TableWrapper
|
||||||
<TableWrapper
|
itemActions={[
|
||||||
itemActions={[
|
{
|
||||||
{
|
type: "delete",
|
||||||
type: "view",
|
onClick: (element) => {
|
||||||
onClick: (element) => {
|
console.log(element);
|
||||||
console.log(element);
|
|
||||||
},
|
|
||||||
sendOnlyId: true,
|
|
||||||
},
|
},
|
||||||
{
|
sendOnlyId: true,
|
||||||
type: "delete",
|
},
|
||||||
onClick: (element) => {
|
]}
|
||||||
console.log(element);
|
columns={[
|
||||||
},
|
{ label: "Name", elementKey: "name" },
|
||||||
sendOnlyId: true,
|
{ label: "Capacity", elementKey: "capacity" },
|
||||||
},
|
{ label: "# of Instances", elementKey: "servers" },
|
||||||
]}
|
{ label: "# of Drives", elementKey: "volumes" },
|
||||||
columns={[
|
]}
|
||||||
{ label: "Name", elementKey: "name" },
|
isLoading={false}
|
||||||
{ label: "Capacity", elementKey: "capacity" },
|
records={zones}
|
||||||
{ label: "# of Instances", elementKey: "servers" },
|
entityName="Zones"
|
||||||
{ label: "# of Drives", elementKey: "volumes" },
|
idField="name"
|
||||||
]}
|
paginatorConfig={{
|
||||||
isLoading={false}
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
records={zones}
|
colSpan: 3,
|
||||||
entityName="Zones"
|
count: zoneCount,
|
||||||
idField="name"
|
rowsPerPage: 10,
|
||||||
paginatorConfig={{
|
page: 0,
|
||||||
...mainPagination,
|
SelectProps: {
|
||||||
onChangePage: () => {},
|
inputProps: { "aria-label": "rows per page" },
|
||||||
onChangeRowsPerPage: () => {},
|
native: true,
|
||||||
}}
|
},
|
||||||
/>
|
ActionsComponent: MinTablePaginationActions,
|
||||||
)}
|
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,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
|
||||||
import { AppState } from "../../../store";
|
import { AppState } from "../../../store";
|
||||||
@@ -22,6 +23,9 @@ import { TraceMessage } from "./types";
|
|||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import { niceBytes, timeFromDate } from "../../../common/utils";
|
import { niceBytes, timeFromDate } from "../../../common/utils";
|
||||||
import { wsProtocol } from "../../../utils/wsUtils";
|
import { wsProtocol } from "../../../utils/wsUtils";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
import { Grid } from "@material-ui/core";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -40,6 +44,7 @@ const styles = (theme: Theme) =>
|
|||||||
borderBottom: "1px solid #dedede",
|
borderBottom: "1px solid #dedede",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface ITrace {
|
interface ITrace {
|
||||||
@@ -92,23 +97,27 @@ const Trace = ({
|
|||||||
}, [traceMessageReceived]);
|
}, [traceMessageReceived]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<React.Fragment>
|
||||||
<h1>Trace</h1>
|
<PageHeader label={"Trace"} />
|
||||||
<div className={classes.logList}>
|
<Grid container>
|
||||||
<ul>
|
<Grid item xs={12} className={classes.container}>
|
||||||
{messages.map((m) => {
|
<div className={classes.logList}>
|
||||||
return (
|
<ul>
|
||||||
<li key={m.key}>
|
{messages.map((m) => {
|
||||||
{timeFromDate(m.time)} - {m.api}[{m.statusCode} {m.statusMsg}]{" "}
|
return (
|
||||||
{m.api} {m.host} {m.client} {m.callStats.duration} ↑{" "}
|
<li key={m.key}>
|
||||||
{niceBytes(m.callStats.rx + "")} ↓{" "}
|
{timeFromDate(m.time)} - {m.api}[{m.statusCode}{" "}
|
||||||
{niceBytes(m.callStats.tx + "")}
|
{m.statusMsg}] {m.api} {m.host} {m.client}{" "}
|
||||||
</li>
|
{m.callStats.duration} ↑ {niceBytes(m.callStats.rx + "")} ↓{" "}
|
||||||
);
|
{niceBytes(m.callStats.tx + "")}
|
||||||
})}
|
</li>
|
||||||
</ul>
|
);
|
||||||
</div>
|
})}
|
||||||
</div>
|
</ul>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ import AddToGroup from "./AddToGroup";
|
|||||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||||
import DescriptionIcon from "@material-ui/icons/Description";
|
import DescriptionIcon from "@material-ui/icons/Description";
|
||||||
import SetPolicy from "../Policies/SetPolicy";
|
import SetPolicy from "../Policies/SetPolicy";
|
||||||
|
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||||
|
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
@@ -84,6 +86,7 @@ const styles = (theme: Theme) =>
|
|||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
boxShadow: "0px 3px 6px #00000012",
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
},
|
},
|
||||||
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IUsersProps {
|
interface IUsersProps {
|
||||||
@@ -307,90 +310,86 @@ class Users extends React.Component<IUsersProps, IUsersState> {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<PageHeader label={"Users"} />
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12} className={classes.container}>
|
||||||
<Typography variant="h6">Users</Typography>
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
</Grid>
|
<TextField
|
||||||
<Grid item xs={12}>
|
placeholder="Search Users"
|
||||||
<br />
|
className={classes.searchField}
|
||||||
</Grid>
|
id="search-resource"
|
||||||
<Grid item xs={12} className={classes.actionsTray}>
|
label=""
|
||||||
<TextField
|
InputProps={{
|
||||||
placeholder="Search Users"
|
disableUnderline: true,
|
||||||
className={classes.searchField}
|
startAdornment: (
|
||||||
id="search-resource"
|
<InputAdornment position="start">
|
||||||
label=""
|
<SearchIcon />
|
||||||
InputProps={{
|
</InputAdornment>
|
||||||
disableUnderline: true,
|
),
|
||||||
startAdornment: (
|
}}
|
||||||
<InputAdornment position="start">
|
onChange={(e) => {
|
||||||
<SearchIcon />
|
this.setState({ filter: e.target.value, page: 0 });
|
||||||
</InputAdornment>
|
}}
|
||||||
),
|
/>
|
||||||
}}
|
<Button
|
||||||
onChange={(e) => {
|
variant="contained"
|
||||||
this.setState({ filter: e.target.value, page: 0 });
|
color="primary"
|
||||||
}}
|
startIcon={<GroupIcon />}
|
||||||
/>
|
disabled={checkedUsers.length <= 0}
|
||||||
<Button
|
onClick={() => {
|
||||||
variant="contained"
|
if (checkedUsers.length > 0) {
|
||||||
color="primary"
|
this.setState({
|
||||||
startIcon={<GroupIcon />}
|
addGroupOpen: true,
|
||||||
disabled={checkedUsers.length <= 0}
|
});
|
||||||
onClick={() => {
|
}
|
||||||
if (checkedUsers.length > 0) {
|
}}
|
||||||
|
>
|
||||||
|
Add to Group
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<CreateIcon />}
|
||||||
|
onClick={() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
addGroupOpen: true,
|
addScreenOpen: true,
|
||||||
|
selectedUser: null,
|
||||||
});
|
});
|
||||||
}
|
}}
|
||||||
}}
|
>
|
||||||
>
|
Create User
|
||||||
Add to Group
|
</Button>
|
||||||
</Button>
|
</Grid>
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
startIcon={<CreateIcon />}
|
|
||||||
onClick={() => {
|
|
||||||
this.setState({
|
|
||||||
addScreenOpen: true,
|
|
||||||
selectedUser: null,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Create User
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<TableWrapper
|
<TableWrapper
|
||||||
itemActions={tableActions}
|
itemActions={tableActions}
|
||||||
columns={[{ label: "Access Key", elementKey: "accessKey" }]}
|
columns={[{ label: "Access Key", elementKey: "accessKey" }]}
|
||||||
onSelect={selectionChanged}
|
onSelect={selectionChanged}
|
||||||
selectedItems={checkedUsers}
|
selectedItems={checkedUsers}
|
||||||
isLoading={loading}
|
isLoading={loading}
|
||||||
records={paginatedRecords}
|
records={paginatedRecords}
|
||||||
entityName="Users"
|
entityName="Users"
|
||||||
idField="accessKey"
|
idField="accessKey"
|
||||||
paginatorConfig={{
|
paginatorConfig={{
|
||||||
rowsPerPageOptions: [5, 10, 25],
|
rowsPerPageOptions: [5, 10, 25],
|
||||||
colSpan: 3,
|
colSpan: 3,
|
||||||
count: filteredRecords.length,
|
count: filteredRecords.length,
|
||||||
rowsPerPage: rowsPerPage,
|
rowsPerPage: rowsPerPage,
|
||||||
page: page,
|
page: page,
|
||||||
SelectProps: {
|
SelectProps: {
|
||||||
inputProps: { "aria-label": "rows per page" },
|
inputProps: { "aria-label": "rows per page" },
|
||||||
native: true,
|
native: true,
|
||||||
},
|
},
|
||||||
onChangePage: handleChangePage,
|
onChangePage: handleChangePage,
|
||||||
onChangeRowsPerPage: handleChangeRowsPerPage,
|
onChangeRowsPerPage: handleChangeRowsPerPage,
|
||||||
ActionsComponent: MinTablePaginationActions,
|
ActionsComponent: MinTablePaginationActions,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user