Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6705b685c | ||
|
|
6b11d403a6 | ||
|
|
8958cbec69 | ||
|
|
5ef66c3cfc | ||
|
|
d4395e1409 | ||
|
|
8a4139c8e7 | ||
|
|
34bcd25c9f | ||
|
|
7853aa6bb9 |
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.15.x]
|
||||
go-version: [1.15.x, 1.16.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
|
||||
@@ -6,8 +6,8 @@ COPY LICENSE /licenses/LICENSE
|
||||
LABEL name="MinIO" \
|
||||
vendor="MinIO Inc <dev@min.io>" \
|
||||
maintainer="MinIO Inc <dev@min.io>" \
|
||||
version="v0.6.0" \
|
||||
release="v0.6.0" \
|
||||
version="v0.6.3" \
|
||||
release="v0.6.3" \
|
||||
summary="A graphical user interface for MinIO" \
|
||||
description="MinIO object storage is fundamentally different. Designed for performance and the S3 API, it is 100% open-source. MinIO is ideal for large, private cloud environments with stringent security requirements and delivers mission-critical availability across a diverse range of workloads."
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ VERSION:
|
||||
|
||||
var appCmds = []cli.Command{
|
||||
serverCmd,
|
||||
updateCmd,
|
||||
}
|
||||
|
||||
func newApp(name string) *cli.App {
|
||||
|
||||
154
cmd/console/update.go
Normal file
154
cmd/console/update.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver/v4"
|
||||
"github.com/cheggaaa/pb/v3"
|
||||
"github.com/minio/cli"
|
||||
"github.com/minio/console/pkg"
|
||||
"github.com/minio/selfupdate"
|
||||
)
|
||||
|
||||
func getUpdateTransport(timeout time.Duration) http.RoundTripper {
|
||||
var updateTransport http.RoundTripper = &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: timeout,
|
||||
KeepAlive: timeout,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
IdleConnTimeout: timeout,
|
||||
TLSHandshakeTimeout: timeout,
|
||||
ExpectContinueTimeout: timeout,
|
||||
DisableCompression: true,
|
||||
}
|
||||
return updateTransport
|
||||
}
|
||||
|
||||
func getUpdateReaderFromURL(u string, transport http.RoundTripper) (io.ReadCloser, int64, error) {
|
||||
clnt := &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
|
||||
resp, err := clnt.Do(req)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
return resp.Body, resp.ContentLength, nil
|
||||
}
|
||||
|
||||
const defaultPubKey = "RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav"
|
||||
|
||||
func getLatestRelease(tr http.RoundTripper) (string, error) {
|
||||
releaseURL := "https://api.github.com/repos/minio/console/releases/latest"
|
||||
|
||||
body, _, err := getUpdateReaderFromURL(releaseURL, tr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to access github release URL %w", err)
|
||||
}
|
||||
defer body.Close()
|
||||
|
||||
lm := make(map[string]interface{})
|
||||
if err = json.NewDecoder(body).Decode(&lm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
rel, ok := lm["tag_name"].(string)
|
||||
if !ok {
|
||||
return "", errors.New("unable to find latest release tag")
|
||||
}
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
// update console in-place
|
||||
var updateCmd = cli.Command{
|
||||
Name: "update",
|
||||
Usage: "update console to latest release",
|
||||
Action: updateInplace,
|
||||
}
|
||||
|
||||
func updateInplace(ctx *cli.Context) error {
|
||||
transport := getUpdateTransport(30 * time.Second)
|
||||
rel, err := getLatestRelease(transport)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
latest, err := semver.Make(strings.TrimPrefix(rel, "v"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
current, err := semver.Make(pkg.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if current.GTE(latest) {
|
||||
fmt.Printf("You are already running the latest version v%v.\n", pkg.Version)
|
||||
return nil
|
||||
}
|
||||
|
||||
consoleBin := fmt.Sprintf("https://github.com/minio/console/releases/download/%s/console-%s-%s", rel, runtime.GOOS, runtime.GOARCH)
|
||||
reader, length, err := getUpdateReaderFromURL(consoleBin, transport)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to fetch binary from %s: %w", consoleBin, err)
|
||||
}
|
||||
|
||||
minisignPubkey := os.Getenv("CONSOLE_MINISIGN_PUBKEY")
|
||||
if minisignPubkey == "" {
|
||||
minisignPubkey = defaultPubKey
|
||||
}
|
||||
|
||||
v := selfupdate.NewVerifier()
|
||||
if err = v.LoadFromURL(consoleBin+".minisig", minisignPubkey, transport); err != nil {
|
||||
return fmt.Errorf("unable to fetch binary signature for %s: %w", consoleBin, err)
|
||||
}
|
||||
opts := selfupdate.Options{
|
||||
Verifier: v,
|
||||
}
|
||||
|
||||
tmpl := `{{ red "Downloading:" }} {{bar . (red "[") (green "=") (red "]")}} {{speed . | rndcolor }}`
|
||||
bar := pb.ProgressBarTemplate(tmpl).Start64(length)
|
||||
barReader := bar.NewProxyReader(reader)
|
||||
if err = selfupdate.Apply(barReader, opts); err != nil {
|
||||
bar.Finish()
|
||||
if rerr := selfupdate.RollbackError(err); rerr != nil {
|
||||
return rerr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
bar.Finish()
|
||||
fmt.Printf("Updated 'console' to latest release %s\n", rel)
|
||||
return nil
|
||||
}
|
||||
9
go.mod
9
go.mod
@@ -3,6 +3,8 @@ module github.com/minio/console
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/cheggaaa/pb/v3 v3.0.6
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0
|
||||
github.com/go-openapi/errors v0.19.6
|
||||
@@ -16,11 +18,12 @@ require (
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/minio/cli v1.22.0
|
||||
github.com/minio/kes v0.11.0
|
||||
github.com/minio/mc v0.0.0-20210213084525-7672841f8c58
|
||||
github.com/minio/minio v0.0.0-20210216195645-87cce344f6e4
|
||||
github.com/minio/minio-go/v7 v7.0.9-0.20210210235136-83423dddb072
|
||||
github.com/minio/mc v0.0.0-20210301162250-f9d36f9b5243
|
||||
github.com/minio/minio v0.0.0-20210301203133-e8d8dfa3ae8f
|
||||
github.com/minio/minio-go/v7 v7.0.10
|
||||
github.com/minio/operator v0.0.0-20210201110528-753019b838b4
|
||||
github.com/minio/operator/logsearchapi v0.0.0-20210201110528-753019b838b4
|
||||
github.com/minio/selfupdate v0.3.1
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
|
||||
32
go.sum
32
go.sum
@@ -127,6 +127,8 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||
@@ -184,7 +186,10 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/bombsimon/wsl/v2 v2.0.0/go.mod h1:mf25kr/SqFEPhhcxW1+7pxzGlW+hIl/hYTKY95VwV8U=
|
||||
github.com/bombsimon/wsl/v2 v2.2.0/go.mod h1:Azh8c3XGEJl9LyX0/sFC+CKMc7Ssgua0g+6abzXN4Pg=
|
||||
@@ -203,6 +208,8 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo=
|
||||
github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
|
||||
github.com/cheggaaa/pb/v3 v3.0.6 h1:ULPm1wpzvj60FvmCrX7bIaB80UgbhI+zSaQJKRfCbAs=
|
||||
github.com/cheggaaa/pb/v3 v3.0.6/go.mod h1:X1L61/+36nz9bjIsrDU52qHKOQukUQe2Ge+YvGuquCw=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -853,6 +860,8 @@ github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd
|
||||
github.com/klauspost/cpuid/v2 v2.0.2/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.3 h1:DNljyrHyxlkk8139OXIAAauCwV8eQGDD6Z8YqnDXdZw=
|
||||
github.com/klauspost/cpuid/v2 v2.0.3/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/klauspost/readahead v1.3.1 h1:QqXNYvm+VvqYcbrRT4LojUciM0XrznFRIDrbHiJtu/0=
|
||||
@@ -927,6 +936,7 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
@@ -952,21 +962,21 @@ github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz
|
||||
github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
github.com/minio/kes v0.11.0 h1:8ma6OCVSxKT50b1uYXLJro3m7PmZtCLxBaTddQexI5k=
|
||||
github.com/minio/kes v0.11.0/go.mod h1:mTF1Bv8YVEtQqF/B7Felp4tLee44Pp+dgI0rhCvgNg8=
|
||||
github.com/minio/mc v0.0.0-20210213084525-7672841f8c58 h1:QK75hYQDMFDCqw55nkTkeOf60ldtDXfxvbl46JaXJmI=
|
||||
github.com/minio/mc v0.0.0-20210213084525-7672841f8c58/go.mod h1:LlP1KaJaa36kvrIC6TClPsGKXelN7AQBmDfs8/BOFHI=
|
||||
github.com/minio/mc v0.0.0-20210301162250-f9d36f9b5243 h1:V0EoJ/I/p86J8FH2zuOSTsTzNzDXQX4xZKvBwBLS/Qk=
|
||||
github.com/minio/mc v0.0.0-20210301162250-f9d36f9b5243/go.mod h1:nkHp/atLUKkhML5YGfvaDDFqlcBmuii7s9Dbk3ulB1Q=
|
||||
github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
|
||||
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
||||
github.com/minio/md5-simd v1.1.1 h1:9ojcLbuZ4gXbB2sX53MKn8JUZ0sB/2wfwsEcRw+I08U=
|
||||
github.com/minio/md5-simd v1.1.1/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
||||
github.com/minio/minio v0.0.0-20210128013121-e79829b5b368 h1:oRvZaqSesXt/SVTqkfJ9UhmXpkDiBUITpmSJU1hwmSA=
|
||||
github.com/minio/minio v0.0.0-20210128013121-e79829b5b368/go.mod h1:6jySvEwvfCfr9SphRrAb+TMtEXRgRJ4sb79UKFJWmFM=
|
||||
github.com/minio/minio v0.0.0-20210213070509-a94a9c37faf5/go.mod h1:o/m0gi1249Gv7Mxd8A3bgS+2Sq+mTH2s+AgR+KZeKW8=
|
||||
github.com/minio/minio v0.0.0-20210216195645-87cce344f6e4 h1:q3bgpFZfgwZNm3tC2cFpBDgkI4swMh+5agWf+KDyRWk=
|
||||
github.com/minio/minio v0.0.0-20210216195645-87cce344f6e4/go.mod h1:ghYpyXxPZNmgM4s/u01DySXObPJfSH5eyvuRhthXCSs=
|
||||
github.com/minio/minio v0.0.0-20210301081546-0b9c17443eb8/go.mod h1:E7ngQWKJdbRG9dqHZ86lnhGS0RxqtEJTnWDEM/P9BQs=
|
||||
github.com/minio/minio v0.0.0-20210301203133-e8d8dfa3ae8f h1:dAFaii7oqV0Mu9rUNfsgKdE10aZADSgIXoK5saLVeOE=
|
||||
github.com/minio/minio v0.0.0-20210301203133-e8d8dfa3ae8f/go.mod h1:E7ngQWKJdbRG9dqHZ86lnhGS0RxqtEJTnWDEM/P9BQs=
|
||||
github.com/minio/minio-go/v7 v7.0.8-0.20210127003153-c40722862654 h1:pRHAWZsfFGyqG58dSB8S4vlDeR1r1godusC3NHVquns=
|
||||
github.com/minio/minio-go/v7 v7.0.8-0.20210127003153-c40722862654/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc=
|
||||
github.com/minio/minio-go/v7 v7.0.9-0.20210210235136-83423dddb072 h1:zlheLAzZ66jYLUsa81R8gwPtSgKRI5FMJyAKuaJpkHE=
|
||||
github.com/minio/minio-go/v7 v7.0.9-0.20210210235136-83423dddb072/go.mod h1:pEZBUa+L2m9oECoIA6IcSK8bv/qggtQVLovjeKK5jYc=
|
||||
github.com/minio/minio-go/v7 v7.0.10 h1:1oUKe4EOPUEhw2qnPQaPsJ0lmVTYLFu03SiItauXs94=
|
||||
github.com/minio/minio-go/v7 v7.0.10/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo=
|
||||
github.com/minio/operator v0.0.0-20210201110528-753019b838b4 h1:2TtnWOrVkMC8N/wLWwlnsEIMOHpZOIsF8JZ0cPDI1m0=
|
||||
github.com/minio/operator v0.0.0-20210201110528-753019b838b4/go.mod h1:xjLK0CsJr9Zo0AUdgpsnBbTjHyM5XXr15JM/MHNBppI=
|
||||
github.com/minio/operator/logsearchapi v0.0.0-20210201110528-753019b838b4 h1:7HNd0WPMFcQzQbgPs7VQJfiXVm8xjuxnS3/1yi4twwM=
|
||||
@@ -975,6 +985,8 @@ github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlE
|
||||
github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM=
|
||||
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
|
||||
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
|
||||
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
|
||||
github.com/minio/simdjson-go v0.2.0/go.mod h1:JPUSkRykfSPS+AhO0YPA1h0l5vY7NqrF4zel2b12wxc=
|
||||
github.com/minio/simdjson-go v0.2.1 h1:nxYlp4Qd0w2pwLlif00l5vTFL6PcNAKpyHq27/pageg=
|
||||
github.com/minio/simdjson-go v0.2.1/go.mod h1:JPUSkRykfSPS+AhO0YPA1h0l5vY7NqrF4zel2b12wxc=
|
||||
@@ -1168,7 +1180,6 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k=
|
||||
github.com/quasilyte/go-ruleguard v0.2.1/go.mod h1:hN2rVc/uS4bQhQKTio2XaSJSafJwqBUWWwtssT3cQmc=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
|
||||
@@ -1212,6 +1223,8 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm
|
||||
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
|
||||
github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto=
|
||||
github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil/v3 v3.21.1 h1:dA72XXj5WOXIZkAL2iYTKRVcNOOqh4yfLn9Rm7t8BMM=
|
||||
github.com/shirou/gopsutil/v3 v3.21.1/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
@@ -1312,7 +1325,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tommy-muehle/go-mnd v1.1.1/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
|
||||
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
@@ -1606,6 +1618,7 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck=
|
||||
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -1717,7 +1730,6 @@ golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200616195046-dc31b401abb5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 h1:BTs2GMGSMWpgtCpv1CE7vkJTv7XcHdcLLnAMu7UbgTY=
|
||||
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
|
||||
BIN
images/pic1.png
BIN
images/pic1.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.7 MiB |
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.6.0
|
||||
image: minio/console:v0.6.3
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
args:
|
||||
- server
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.6.0
|
||||
image: minio/console:v0.6.3
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_OPERATOR_MODE
|
||||
|
||||
@@ -26,3 +26,7 @@ import (
|
||||
func GetOperatorMode() bool {
|
||||
return strings.ToLower(env.Get(consoleOperatorMode, "off")) == "on"
|
||||
}
|
||||
|
||||
func GetLDAPEnabled() bool {
|
||||
return strings.ToLower(env.Get(ConsoleLDAPEnabled, "off")) == "on"
|
||||
}
|
||||
|
||||
@@ -18,4 +18,6 @@ package acl
|
||||
|
||||
const (
|
||||
consoleOperatorMode = "CONSOLE_OPERATOR_MODE"
|
||||
// const for ldap configuration
|
||||
ConsoleLDAPEnabled = "CONSOLE_LDAP_ENABLED"
|
||||
)
|
||||
|
||||
@@ -32,7 +32,6 @@ var (
|
||||
bucketsDetail = "/buckets/:bucketName"
|
||||
serviceAccounts = "/account"
|
||||
tenants = "/tenants"
|
||||
addTenant = "/add-tenant"
|
||||
tenantsDetail = "/namespaces/:tenantNamespace/tenants/:tenantName"
|
||||
remoteBuckets = "/remote-buckets"
|
||||
replication = "/replication"
|
||||
@@ -243,6 +242,17 @@ var healthInfoActionSet = ConfigurationActionSet{
|
||||
),
|
||||
}
|
||||
|
||||
var displayRules = map[string]func() bool{
|
||||
// disable users page if LDAP is enabled
|
||||
users: func() bool {
|
||||
return !GetLDAPEnabled()
|
||||
},
|
||||
// disable groups page if LDAP is enabled
|
||||
groups: func() bool {
|
||||
return !GetLDAPEnabled()
|
||||
},
|
||||
}
|
||||
|
||||
// endpointRules contains the mapping between endpoints and ActionSets, additional rules can be added here
|
||||
var endpointRules = map[string]ConfigurationActionSet{
|
||||
configuration: configurationActionSet,
|
||||
@@ -271,7 +281,6 @@ var endpointRules = map[string]ConfigurationActionSet{
|
||||
var operatorRules = map[string]ConfigurationActionSet{
|
||||
tenants: tenantsActionSet,
|
||||
tenantsDetail: tenantsActionSet,
|
||||
addTenant: tenantsActionSet,
|
||||
license: licenseActionSet,
|
||||
}
|
||||
|
||||
@@ -337,6 +346,15 @@ func GetAuthorizedEndpoints(actions []string) []string {
|
||||
userAllowedAction := actionsStringToActionSet(actions)
|
||||
var allowedEndpoints []string
|
||||
for endpoint, rules := range rangeTake {
|
||||
|
||||
// check if display rule exists for this endpoint, this will control
|
||||
// what user sees on the console UI
|
||||
if rule, ok := displayRules[endpoint]; ok {
|
||||
if rule != nil && !rule() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// check if user policy matches s3:* or admin:* typesIntersection
|
||||
endpointActionTypes := rules.actionTypes
|
||||
typesIntersection := endpointActionTypes.Intersection(userAllowedAction)
|
||||
|
||||
@@ -116,7 +116,7 @@ func TestOperatorOnlyEndpoints(t *testing.T) {
|
||||
"admin:*",
|
||||
},
|
||||
},
|
||||
want: 4,
|
||||
want: 3,
|
||||
},
|
||||
{
|
||||
name: "Operator Only - all s3 endpoints",
|
||||
@@ -125,7 +125,7 @@ func TestOperatorOnlyEndpoints(t *testing.T) {
|
||||
"s3:*",
|
||||
},
|
||||
},
|
||||
want: 4,
|
||||
want: 3,
|
||||
},
|
||||
{
|
||||
name: "Operator Only - all admin and s3 endpoints",
|
||||
@@ -135,14 +135,14 @@ func TestOperatorOnlyEndpoints(t *testing.T) {
|
||||
"s3:*",
|
||||
},
|
||||
},
|
||||
want: 4,
|
||||
want: 3,
|
||||
},
|
||||
{
|
||||
name: "Operator Only - default endpoints",
|
||||
args: args{
|
||||
[]string{},
|
||||
},
|
||||
want: 4,
|
||||
want: 3,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -452,3 +452,55 @@ export const snackBarCommon = {
|
||||
maxWidth: "calc(100% - 140px)",
|
||||
},
|
||||
};
|
||||
|
||||
export const wizardCommon = {
|
||||
multiContainer: {
|
||||
display: "flex" as const,
|
||||
alignItems: "center" as const,
|
||||
justifyContent: "flex-start" as const,
|
||||
},
|
||||
sizeFactorContainer: {
|
||||
marginLeft: 8,
|
||||
alignSelf: "flex-start" as const,
|
||||
},
|
||||
headerElement: {
|
||||
position: "sticky" as const,
|
||||
top: 0,
|
||||
paddingTop: 5,
|
||||
marginBottom: 10,
|
||||
zIndex: 500,
|
||||
backgroundColor: "#fff",
|
||||
},
|
||||
tableTitle: {
|
||||
fontWeight: 700,
|
||||
width: "30%",
|
||||
},
|
||||
poolError: {
|
||||
color: "#dc1f2e",
|
||||
fontSize: "0.75rem",
|
||||
paddingLeft: 120,
|
||||
},
|
||||
error: {
|
||||
color: "#dc1f2e",
|
||||
fontSize: "0.75rem",
|
||||
},
|
||||
h3Section: {
|
||||
marginTop: 0,
|
||||
},
|
||||
descriptionText: {
|
||||
fontSize: 13,
|
||||
color: "#777777",
|
||||
},
|
||||
container: {
|
||||
padding: "77px 0 0 0",
|
||||
"& h6": {
|
||||
color: "#777777",
|
||||
fontSize: 14,
|
||||
},
|
||||
"& p": {
|
||||
"& span:not(*[class*='smallUnit'])": {
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -18,23 +18,31 @@ import React, { useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { IWizardMain } from "./types";
|
||||
import WizardPage from "./WizardPage";
|
||||
import { Grid, Paper } from "@material-ui/core";
|
||||
import { Grid } from "@material-ui/core";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
wizardMain: {
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
flexGrow: 1,
|
||||
},
|
||||
wizFromContainer: {
|
||||
marginTop: "32px",
|
||||
height: "calc(100vh - 365px)",
|
||||
minHeight: 450,
|
||||
padding: "0 30px",
|
||||
},
|
||||
wizardSteps: {
|
||||
minWidth: 180,
|
||||
marginRight: 10,
|
||||
borderRight: "#eaeaea 1px solid",
|
||||
display: "flex",
|
||||
flexGrow: 1,
|
||||
flexDirection: "column",
|
||||
height: "100%",
|
||||
"& ul": {
|
||||
padding: "0px 15px 0 30px",
|
||||
padding: "0 15px 0 40px",
|
||||
marginTop: "0px",
|
||||
|
||||
"& li": {
|
||||
@@ -56,15 +64,14 @@ const styles = (theme: Theme) =>
|
||||
boxShadow: "none",
|
||||
},
|
||||
},
|
||||
paddedGridItem: {
|
||||
padding: "0px 10px 0px 10px",
|
||||
paddedContentGrid: {
|
||||
padding: "0 10px",
|
||||
},
|
||||
menuPaper: {
|
||||
padding: "20px",
|
||||
},
|
||||
paperContainer: {
|
||||
padding: "10px",
|
||||
maxWidth: "900px",
|
||||
stepsLabel: {
|
||||
fontSize: 20,
|
||||
color: "#393939",
|
||||
fontWeight: 600,
|
||||
margin: "15px 12px",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -114,34 +121,25 @@ const GenericWizard = ({ classes, wizardSteps }: IWizardMain) => {
|
||||
|
||||
return (
|
||||
<Grid container className={classes.wizFromContainer}>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
sm={3}
|
||||
md={3}
|
||||
lg={3}
|
||||
xl={2}
|
||||
className={classes.paddedGridItem}
|
||||
>
|
||||
<Paper className={classes.menuPaper}>
|
||||
<div className={classes.wizardSteps}>
|
||||
<ul>
|
||||
{wizardSteps.map((step, index) => {
|
||||
return (
|
||||
<li key={`wizard-${index.toString()}`}>
|
||||
<button
|
||||
onClick={() => pageChange(index)}
|
||||
disabled={index > currentStep}
|
||||
className={classes.buttonList}
|
||||
>
|
||||
{step.label}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</Paper>
|
||||
<Grid item xs={12} sm={3} md={3} lg={3} xl={2}>
|
||||
<div className={classes.wizardSteps}>
|
||||
<span className={classes.stepsLabel}>Steps</span>
|
||||
<ul>
|
||||
{wizardSteps.map((step, index) => {
|
||||
return (
|
||||
<li key={`wizard-${index.toString()}`}>
|
||||
<button
|
||||
onClick={() => pageChange(index)}
|
||||
disabled={index > currentStep}
|
||||
className={classes.buttonList}
|
||||
>
|
||||
{step.label}
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
@@ -150,11 +148,9 @@ const GenericWizard = ({ classes, wizardSteps }: IWizardMain) => {
|
||||
md={9}
|
||||
lg={9}
|
||||
xl={10}
|
||||
className={classes.paddedGridItem}
|
||||
className={classes.paddedContentGrid}
|
||||
>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<WizardPage page={wizardSteps[currentStep]} pageChange={pageChange} />
|
||||
</Paper>
|
||||
<WizardPage page={wizardSteps[currentStep]} pageChange={pageChange} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
|
||||
@@ -28,11 +28,14 @@ const styles = (theme: Theme) =>
|
||||
wizardComponent: {
|
||||
overflowY: "auto",
|
||||
marginBottom: 10,
|
||||
height: "calc(100vh - 435px)",
|
||||
},
|
||||
buttonsContainer: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end" as const,
|
||||
padding: "10px 0",
|
||||
borderTop: "#EAEAEA 1px solid",
|
||||
"& button": {
|
||||
marginLeft: 10,
|
||||
},
|
||||
|
||||
@@ -45,7 +45,7 @@ import Users from "./Users/Users";
|
||||
import Groups from "./Groups/Groups";
|
||||
import ConfigurationMain from "./Configurations/ConfigurationMain";
|
||||
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
|
||||
import ListTenants from "./Tenants/ListTenants/ListTenants";
|
||||
import TenantsMain from "./Tenants/TenantsMain";
|
||||
import TenantDetails from "./Tenants/TenantDetails/TenantDetails";
|
||||
import ObjectBrowser from "./ObjectBrowser/ObjectBrowser";
|
||||
import ObjectRouting from "./Buckets/ListBuckets/Objects/ListObjects/ObjectRouting";
|
||||
@@ -55,7 +55,6 @@ import LogsMain from "./Logs/LogsMain";
|
||||
import Heal from "./Heal/Heal";
|
||||
import Watch from "./Watch/Watch";
|
||||
import HealthInfo from "./HealthInfo/HealthInfo";
|
||||
import AddTenant from "./Tenants/ListTenants/AddTenant";
|
||||
|
||||
const drawerWidth = 245;
|
||||
|
||||
@@ -291,13 +290,9 @@ const Console = ({
|
||||
path: "/webhook/audit",
|
||||
},
|
||||
{
|
||||
component: ListTenants,
|
||||
component: TenantsMain,
|
||||
path: "/tenants",
|
||||
},
|
||||
{
|
||||
component: AddTenant,
|
||||
path: "/add-tenant",
|
||||
},
|
||||
{
|
||||
component: TenantDetails,
|
||||
path: "/namespaces/:tenantNamespace/tenants/:tenantName",
|
||||
|
||||
@@ -31,6 +31,8 @@ import { LicenseInfo } from "./types";
|
||||
import { LinearProgress } from "@material-ui/core";
|
||||
import { AppState } from "../../../store";
|
||||
import { connect } from "react-redux";
|
||||
import { niceBytes } from "../../../common/utils";
|
||||
import Moment from "react-moment";
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
operatorMode: state.system.operatorMode,
|
||||
@@ -260,7 +262,16 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
api
|
||||
.invoke("GET", `/api/v1/subscription/info`)
|
||||
.then((res: LicenseInfo) => {
|
||||
setLicenseInfo(res);
|
||||
if (res) {
|
||||
if (res.plan === "STANDARD") {
|
||||
setCurrentPlanID(1);
|
||||
} else if (res.plan === "ENTERPRISE") {
|
||||
setCurrentPlanID(2);
|
||||
} else {
|
||||
setCurrentPlanID(1);
|
||||
}
|
||||
setLicenseInfo(res);
|
||||
}
|
||||
setLoadingLicenseInfo(false);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
@@ -273,6 +284,7 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
);
|
||||
|
||||
const [licenseInfo, setLicenseInfo] = useState<LicenseInfo>();
|
||||
const [currentPlanID, setCurrentPlanID] = useState<number>(0);
|
||||
const [loadingLicenseInfo, setLoadingLicenseInfo] = useState<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -286,7 +298,6 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
let currentPlanID = 0;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<React.Fragment>
|
||||
@@ -348,7 +359,10 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
gutterBottom
|
||||
className={classes.licenseInfoValue}
|
||||
>
|
||||
{licenseInfo.storage_capacity}
|
||||
{niceBytes(
|
||||
(licenseInfo.storage_capacity * 1099511627776) // 1 Terabyte = 1099511627776 Bytes
|
||||
.toString(10)
|
||||
)}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="button"
|
||||
@@ -364,7 +378,7 @@ const License = ({ classes, operatorMode }: ILicenseProps) => {
|
||||
gutterBottom
|
||||
className={classes.licenseInfoValue}
|
||||
>
|
||||
{licenseInfo.expires_at}
|
||||
<Moment>{licenseInfo.expires_at}</Moment>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
|
||||
621
portal-ui/src/screens/Console/Tenants/AddTenant/AddTenant.tsx
Normal file
621
portal-ui/src/screens/Console/Tenants/AddTenant/AddTenant.tsx
Normal file
@@ -0,0 +1,621 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState, Fragment } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { LinearProgress } from "@material-ui/core";
|
||||
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
modalBasic,
|
||||
settingsCommon,
|
||||
wizardCommon,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import api from "../../../../common/api";
|
||||
import { generatePoolName } from "../../../../common/utils";
|
||||
import GenericWizard from "../../Common/GenericWizard/GenericWizard";
|
||||
import { IWizardElement } from "../../Common/GenericWizard/types";
|
||||
import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
|
||||
import { IAffinityModel, ITenantCreator } from "../../../../common/types";
|
||||
import { KeyPair } from "../ListTenants/utils";
|
||||
|
||||
import { setModalErrorSnackMessage } from "../../../../actions";
|
||||
import { getHardcodedAffinity } from "../TenantDetails/utils";
|
||||
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
|
||||
import NameTenant from "./Steps/NameTenant";
|
||||
import { AppState } from "../../../../store";
|
||||
import { ICertificatesItems, IFieldStore } from "../types";
|
||||
import { updateAddField } from "../actions";
|
||||
import Configure from "./Steps/Configure";
|
||||
import IdentityProvider from "./Steps/IdentityProvider";
|
||||
import Security from "./Steps/Security";
|
||||
import Encryption from "./Steps/Encryption";
|
||||
import TenantSize from "./Steps/TenantSize";
|
||||
import Preview from "./Steps/Preview";
|
||||
|
||||
interface IAddTenantProps {
|
||||
closeAndRefresh: (reloadData: boolean) => any;
|
||||
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
|
||||
updateAddField: typeof updateAddField;
|
||||
fields: IFieldStore;
|
||||
certificates: ICertificatesItems;
|
||||
namespace: string;
|
||||
validPages: string[];
|
||||
advancedMode: boolean;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...wizardCommon,
|
||||
...settingsCommon,
|
||||
});
|
||||
|
||||
const AddTenant = ({
|
||||
classes,
|
||||
advancedMode,
|
||||
fields,
|
||||
certificates,
|
||||
namespace,
|
||||
validPages,
|
||||
setModalErrorSnackMessage,
|
||||
closeAndRefresh,
|
||||
}: IAddTenantProps) => {
|
||||
// Modals
|
||||
const [showNewCredentials, setShowNewCredentials] = useState<boolean>(false);
|
||||
const [
|
||||
createdAccount,
|
||||
setCreatedAccount,
|
||||
] = useState<NewServiceAccount | null>(null);
|
||||
|
||||
// Fields
|
||||
const [addSending, setAddSending] = useState<boolean>(false);
|
||||
|
||||
/* Send Information to backend */
|
||||
useEffect(() => {
|
||||
const tenantName = fields.nameTenant.tenantName;
|
||||
const selectedStorageClass = fields.nameTenant.selectedStorageClass;
|
||||
const imageName = fields.configure.imageName;
|
||||
const consoleImage = fields.configure.consoleImage;
|
||||
const customDockerhub = fields.configure.customDockerhub;
|
||||
const imageRegistry = fields.configure.imageRegistry;
|
||||
const imageRegistryUsername = fields.configure.imageRegistryUsername;
|
||||
const imageRegistryPassword = fields.configure.imageRegistryPassword;
|
||||
const exposeMinIO = fields.configure.exposeMinIO;
|
||||
const exposeConsole = fields.configure.exposeConsole;
|
||||
const idpSelection = fields.identityProvider.idpSelection;
|
||||
const openIDURL = fields.identityProvider.openIDURL;
|
||||
const openIDClientID = fields.identityProvider.openIDClientID;
|
||||
const openIDSecretID = fields.identityProvider.openIDSecretID;
|
||||
const ADURL = fields.identityProvider.ADURL;
|
||||
const ADSkipTLS = fields.identityProvider.ADSkipTLS;
|
||||
const ADServerInsecure = fields.identityProvider.ADServerInsecure;
|
||||
const ADUserNameFilter = fields.identityProvider.ADUserNameFilter;
|
||||
const ADGroupBaseDN = fields.identityProvider.ADGroupBaseDN;
|
||||
const ADGroupSearchFilter = fields.identityProvider.ADGroupSearchFilter;
|
||||
const ADNameAttribute = fields.identityProvider.ADNameAttribute;
|
||||
const minioCertificates = certificates.minioCertificates;
|
||||
const caCertificates = certificates.caCertificates;
|
||||
const consoleCertificate = certificates.consoleCertificate;
|
||||
const serverCertificate = certificates.serverCertificate;
|
||||
const clientCertificate = certificates.clientCertificate;
|
||||
const vaultCertificate = certificates.vaultCertificate;
|
||||
const vaultCA = certificates.vaultCA;
|
||||
const gemaltoCA = certificates.gemaltoCA;
|
||||
const enableEncryption = fields.encryption.enableEncryption;
|
||||
const encryptionType = fields.encryption.encryptionType;
|
||||
const gemaltoEndpoint = fields.encryption.gemaltoEndpoint;
|
||||
const gemaltoToken = fields.encryption.gemaltoToken;
|
||||
const gemaltoDomain = fields.encryption.gemaltoDomain;
|
||||
const gemaltoRetry = fields.encryption.gemaltoRetry;
|
||||
const awsEndpoint = fields.encryption.awsEndpoint;
|
||||
const awsRegion = fields.encryption.awsRegion;
|
||||
const awsKMSKey = fields.encryption.awsKMSKey;
|
||||
const awsAccessKey = fields.encryption.awsAccessKey;
|
||||
const awsSecretKey = fields.encryption.awsSecretKey;
|
||||
const awsToken = fields.encryption.awsToken;
|
||||
const vaultEndpoint = fields.encryption.vaultEndpoint;
|
||||
const vaultEngine = fields.encryption.vaultEngine;
|
||||
const vaultNamespace = fields.encryption.vaultNamespace;
|
||||
const vaultPrefix = fields.encryption.vaultPrefix;
|
||||
const vaultAppRoleEngine = fields.encryption.vaultAppRoleEngine;
|
||||
const vaultId = fields.encryption.vaultId;
|
||||
const vaultSecret = fields.encryption.vaultSecret;
|
||||
const vaultRetry = fields.encryption.vaultRetry;
|
||||
const vaultPing = fields.encryption.vaultPing;
|
||||
const gcpProjectID = fields.encryption.gcpProjectID;
|
||||
const gcpEndpoint = fields.encryption.gcpEndpoint;
|
||||
const gcpClientEmail = fields.encryption.gcpClientEmail;
|
||||
const gcpClientID = fields.encryption.gcpClientID;
|
||||
const gcpPrivateKeyID = fields.encryption.gcpPrivateKeyID;
|
||||
const gcpPrivateKey = fields.encryption.gcpPrivateKey;
|
||||
const enableAutoCert = fields.security.enableAutoCert;
|
||||
const ecParity = fields.tenantSize.ecParity;
|
||||
const distribution = fields.tenantSize.distribution;
|
||||
const memorySize = fields.tenantSize.memorySize;
|
||||
|
||||
if (addSending) {
|
||||
const poolName = generatePoolName([]);
|
||||
|
||||
const hardCodedAffinity: IAffinityModel = getHardcodedAffinity(
|
||||
tenantName,
|
||||
poolName
|
||||
);
|
||||
|
||||
const erasureCode = ecParity.split(":")[1];
|
||||
|
||||
let dataSend: ITenantCreator = {
|
||||
name: tenantName,
|
||||
namespace: namespace,
|
||||
access_key: "",
|
||||
secret_key: "",
|
||||
enable_tls: enableAutoCert,
|
||||
enable_console: true,
|
||||
enable_prometheus: true,
|
||||
service_name: "",
|
||||
image: imageName,
|
||||
console_image: consoleImage,
|
||||
expose_minio: exposeMinIO,
|
||||
expose_console: exposeConsole,
|
||||
pools: [
|
||||
{
|
||||
name: poolName,
|
||||
servers: distribution.nodes,
|
||||
volumes_per_server: distribution.disks,
|
||||
volume_configuration: {
|
||||
size: distribution.pvSize,
|
||||
storage_class_name: selectedStorageClass,
|
||||
},
|
||||
resources: {
|
||||
requests: {
|
||||
memory: memorySize.request,
|
||||
},
|
||||
limits: {
|
||||
memory: memorySize.limit,
|
||||
},
|
||||
},
|
||||
affinity: hardCodedAffinity,
|
||||
},
|
||||
],
|
||||
erasureCodingParity: parseInt(erasureCode, 10),
|
||||
};
|
||||
|
||||
if (customDockerhub) {
|
||||
dataSend = {
|
||||
...dataSend,
|
||||
image_registry: {
|
||||
registry: imageRegistry,
|
||||
username: imageRegistryUsername,
|
||||
password: imageRegistryPassword,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let tenantCerts: any = null;
|
||||
let consoleCerts: any = null;
|
||||
let caCerts: any = null;
|
||||
|
||||
if (caCertificates.length > 0) {
|
||||
caCerts = {
|
||||
ca_certificates: caCertificates
|
||||
.map((keyPair: KeyPair) => keyPair.encoded_cert)
|
||||
.filter((keyPair) => keyPair),
|
||||
};
|
||||
}
|
||||
|
||||
if (minioCertificates.length > 0) {
|
||||
tenantCerts = {
|
||||
minio: minioCertificates
|
||||
.map((keyPair: KeyPair) => ({
|
||||
crt: keyPair.encoded_cert,
|
||||
key: keyPair.encoded_key,
|
||||
}))
|
||||
.filter((keyPair) => keyPair.crt && keyPair.key),
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
consoleCertificate.encoded_cert !== "" &&
|
||||
consoleCertificate.encoded_key !== ""
|
||||
) {
|
||||
consoleCerts = {
|
||||
console: {
|
||||
crt: consoleCertificate.encoded_cert,
|
||||
key: consoleCertificate.encoded_key,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (tenantCerts || consoleCerts || caCerts) {
|
||||
dataSend = {
|
||||
...dataSend,
|
||||
tls: {
|
||||
...tenantCerts,
|
||||
...consoleCerts,
|
||||
...caCerts,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (enableEncryption) {
|
||||
let insertEncrypt = {};
|
||||
|
||||
switch (encryptionType) {
|
||||
case "gemalto":
|
||||
let gemaltoCAIntroduce = {};
|
||||
|
||||
if (gemaltoCA.encoded_cert !== "") {
|
||||
gemaltoCAIntroduce = {
|
||||
ca: gemaltoCA.encoded_cert,
|
||||
};
|
||||
}
|
||||
insertEncrypt = {
|
||||
gemalto: {
|
||||
keysecure: {
|
||||
endpoint: gemaltoEndpoint,
|
||||
credentials: {
|
||||
token: gemaltoToken,
|
||||
domain: gemaltoDomain,
|
||||
retry: parseInt(gemaltoRetry),
|
||||
},
|
||||
tls: {
|
||||
...gemaltoCAIntroduce,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "aws":
|
||||
insertEncrypt = {
|
||||
aws: {
|
||||
secretsmanager: {
|
||||
endpoint: awsEndpoint,
|
||||
region: awsRegion,
|
||||
kmskey: awsKMSKey,
|
||||
credentials: {
|
||||
accesskey: awsAccessKey,
|
||||
secretkey: awsSecretKey,
|
||||
token: awsToken,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "gcp":
|
||||
insertEncrypt = {
|
||||
gcp: {
|
||||
secretmanager: {
|
||||
project_id: gcpProjectID,
|
||||
endpoint: gcpEndpoint,
|
||||
credentials: {
|
||||
client_email: gcpClientEmail,
|
||||
client_id: gcpClientID,
|
||||
private_key_id: gcpPrivateKeyID,
|
||||
private_key: gcpPrivateKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "vault":
|
||||
let vaultKeyPair = null;
|
||||
let vaultCAInsert = null;
|
||||
if (
|
||||
vaultCertificate.encoded_key !== "" &&
|
||||
vaultCertificate.encoded_cert !== ""
|
||||
) {
|
||||
vaultKeyPair = {
|
||||
key: vaultCertificate.encoded_key,
|
||||
crt: vaultCertificate.encoded_cert,
|
||||
};
|
||||
}
|
||||
if (vaultCA.encoded_cert !== "") {
|
||||
vaultCAInsert = {
|
||||
ca: vaultCA.encoded_cert,
|
||||
};
|
||||
}
|
||||
let vaultTLS = null;
|
||||
if (vaultKeyPair || vaultCA) {
|
||||
vaultTLS = {
|
||||
tls: {
|
||||
...vaultKeyPair,
|
||||
...vaultCAInsert,
|
||||
},
|
||||
};
|
||||
}
|
||||
insertEncrypt = {
|
||||
vault: {
|
||||
endpoint: vaultEndpoint,
|
||||
engine: vaultEngine,
|
||||
namespace: vaultNamespace,
|
||||
prefix: vaultPrefix,
|
||||
approle: {
|
||||
engine: vaultAppRoleEngine,
|
||||
id: vaultId,
|
||||
secret: vaultSecret,
|
||||
retry: parseInt(vaultRetry),
|
||||
},
|
||||
...vaultTLS,
|
||||
status: {
|
||||
ping: parseInt(vaultPing),
|
||||
},
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
let encryptionServerKeyPair: any = {};
|
||||
let encryptionClientKeyPair: any = {};
|
||||
|
||||
if (
|
||||
clientCertificate.encoded_key !== "" &&
|
||||
clientCertificate.encoded_cert !== ""
|
||||
) {
|
||||
encryptionClientKeyPair = {
|
||||
client: {
|
||||
key: clientCertificate.encoded_key,
|
||||
crt: clientCertificate.encoded_cert,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
serverCertificate.encoded_key !== "" &&
|
||||
serverCertificate.encoded_cert !== ""
|
||||
) {
|
||||
encryptionServerKeyPair = {
|
||||
server: {
|
||||
key: serverCertificate.encoded_key,
|
||||
crt: serverCertificate.encoded_cert,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
dataSend = {
|
||||
...dataSend,
|
||||
encryption: {
|
||||
...encryptionClientKeyPair,
|
||||
...encryptionServerKeyPair,
|
||||
...insertEncrypt,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (idpSelection !== "Built-in") {
|
||||
let dataIDP: any = {};
|
||||
|
||||
switch (idpSelection) {
|
||||
case "OpenID":
|
||||
dataIDP = {
|
||||
oidc: {
|
||||
url: openIDURL,
|
||||
client_id: openIDClientID,
|
||||
secret_id: openIDSecretID,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "AD":
|
||||
dataIDP = {
|
||||
active_directory: {
|
||||
url: ADURL,
|
||||
skip_tls_verification: ADSkipTLS,
|
||||
server_insecure: ADServerInsecure,
|
||||
username_format: "",
|
||||
user_search_filter: ADUserNameFilter,
|
||||
group_search_base_dn: ADGroupBaseDN,
|
||||
group_search_filter: ADGroupSearchFilter,
|
||||
group_name_attribute: ADNameAttribute,
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
dataSend = {
|
||||
...dataSend,
|
||||
idp: { ...dataIDP },
|
||||
};
|
||||
}
|
||||
|
||||
api
|
||||
.invoke("POST", `/api/v1/tenants`, dataSend)
|
||||
.then((res) => {
|
||||
const newSrvAcc: NewServiceAccount = {
|
||||
console: {
|
||||
accessKey: res.console.access_key,
|
||||
secretKey: res.console.secret_key,
|
||||
},
|
||||
};
|
||||
|
||||
setAddSending(false);
|
||||
|
||||
setShowNewCredentials(true);
|
||||
setCreatedAccount(newSrvAcc);
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddSending(false);
|
||||
setModalErrorSnackMessage(err);
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [addSending]);
|
||||
|
||||
const cancelButton = {
|
||||
label: "Cancel",
|
||||
type: "other",
|
||||
enabled: true,
|
||||
action: () => {
|
||||
closeAndRefresh(false);
|
||||
},
|
||||
};
|
||||
|
||||
const wizardSteps: IWizardElement[] = [
|
||||
{
|
||||
label: "Name Tenant",
|
||||
componentRender: <NameTenant />,
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{
|
||||
label: "Next",
|
||||
type: "next",
|
||||
enabled: validPages.includes("nameTenant"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Configure",
|
||||
advancedOnly: true,
|
||||
componentRender: <Configure />,
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{ label: "Back", type: "back", enabled: true },
|
||||
{
|
||||
label: "Next",
|
||||
type: "next",
|
||||
enabled: validPages.includes("configure"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Identity Provider",
|
||||
advancedOnly: true,
|
||||
componentRender: <IdentityProvider />,
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{ label: "Back", type: "back", enabled: true },
|
||||
{
|
||||
label: "Next",
|
||||
type: "next",
|
||||
enabled: validPages.includes("identityProvider"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Security",
|
||||
advancedOnly: true,
|
||||
componentRender: <Security />,
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{ label: "Back", type: "back", enabled: true },
|
||||
{
|
||||
label: "Next",
|
||||
type: "next",
|
||||
enabled: validPages.includes("security"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Encryption",
|
||||
advancedOnly: true,
|
||||
componentRender: <Encryption />,
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{ label: "Back", type: "back", enabled: true },
|
||||
{
|
||||
label: "Next",
|
||||
type: "next",
|
||||
enabled: validPages.includes("encryption"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Tenant Size",
|
||||
componentRender: <TenantSize />,
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{ label: "Back", type: "back", enabled: true },
|
||||
{
|
||||
label: "Next",
|
||||
type: "next",
|
||||
enabled: validPages.includes("tenantSize"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Preview Configuration",
|
||||
componentRender: <Preview />,
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{ label: "Back", type: "back", enabled: true },
|
||||
{
|
||||
label: "Create",
|
||||
type: "submit",
|
||||
enabled: !addSending,
|
||||
action: () => {
|
||||
setAddSending(true);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
let filteredWizardSteps = wizardSteps;
|
||||
|
||||
if (!advancedMode) {
|
||||
filteredWizardSteps = wizardSteps.filter((step) => !step.advancedOnly);
|
||||
}
|
||||
|
||||
const closeCredentialsModal = () => {
|
||||
closeAndRefresh(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.customTitle}>
|
||||
Create New Tenant
|
||||
</Grid>
|
||||
{addSending && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
{showNewCredentials && (
|
||||
<CredentialsPrompt
|
||||
newServiceAccount={createdAccount}
|
||||
open={showNewCredentials}
|
||||
closeModal={() => {
|
||||
closeCredentialsModal();
|
||||
}}
|
||||
entity="Tenant"
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<GenericWizard wizardSteps={filteredWizardSteps} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
advancedMode: state.tenants.createTenant.advancedModeOn,
|
||||
namespace: state.tenants.createTenant.fields.nameTenant.namespace,
|
||||
validPages: state.tenants.createTenant.validPages,
|
||||
fields: state.tenants.createTenant.fields,
|
||||
certificates: state.tenants.createTenant.certificates,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
setModalErrorSnackMessage,
|
||||
updateAddField,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(AddTenant));
|
||||
@@ -0,0 +1,328 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState, useCallback, Fragment } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import {
|
||||
modalBasic,
|
||||
wizardCommon,
|
||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
||||
import { updateAddField, isPageValid } from "../../actions";
|
||||
import { AppState } from "../../../../../store";
|
||||
import { clearValidationError } from "../../utils";
|
||||
import {
|
||||
commonFormValidation,
|
||||
IValidation,
|
||||
} from "../../../../../utils/validationFunctions";
|
||||
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
|
||||
interface IConfigureProps {
|
||||
updateAddField: typeof updateAddField;
|
||||
isPageValid: typeof isPageValid;
|
||||
classes: any;
|
||||
customImage: boolean;
|
||||
imageName: string;
|
||||
consoleImage: string;
|
||||
customDockerhub: boolean;
|
||||
imageRegistry: string;
|
||||
imageRegistryUsername: string;
|
||||
imageRegistryPassword: string;
|
||||
exposeMinIO: boolean;
|
||||
exposeConsole: boolean;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...wizardCommon,
|
||||
});
|
||||
|
||||
const Configure = ({
|
||||
classes,
|
||||
customImage,
|
||||
imageName,
|
||||
consoleImage,
|
||||
customDockerhub,
|
||||
imageRegistry,
|
||||
imageRegistryUsername,
|
||||
imageRegistryPassword,
|
||||
exposeMinIO,
|
||||
exposeConsole,
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
}: IConfigureProps) => {
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
|
||||
// Common
|
||||
const updateField = useCallback(
|
||||
(field: string, value: any) => {
|
||||
updateAddField("configure", field, value);
|
||||
},
|
||||
[updateAddField]
|
||||
);
|
||||
|
||||
// Validation
|
||||
useEffect(() => {
|
||||
let customAccountValidation: IValidation[] = [];
|
||||
|
||||
if (customImage) {
|
||||
customAccountValidation = [
|
||||
...customAccountValidation,
|
||||
{
|
||||
fieldKey: "image",
|
||||
required: true,
|
||||
value: imageName,
|
||||
pattern: /^((.*?)\/(.*?):(.+))$/,
|
||||
customPatternMessage: "Format must be of form: 'minio/minio:VERSION'",
|
||||
},
|
||||
{
|
||||
fieldKey: "consoleImage",
|
||||
required: true,
|
||||
value: consoleImage,
|
||||
pattern: /^((.*?)\/(.*?):(.+))$/,
|
||||
customPatternMessage:
|
||||
"Format must be of form: 'minio/console:VERSION'",
|
||||
},
|
||||
];
|
||||
if (customDockerhub) {
|
||||
customAccountValidation = [
|
||||
...customAccountValidation,
|
||||
{
|
||||
fieldKey: "registry",
|
||||
required: true,
|
||||
value: imageRegistry,
|
||||
},
|
||||
{
|
||||
fieldKey: "registryUsername",
|
||||
required: true,
|
||||
value: imageRegistryUsername,
|
||||
},
|
||||
{
|
||||
fieldKey: "registryPassword",
|
||||
required: true,
|
||||
value: imageRegistryPassword,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const commonVal = commonFormValidation(customAccountValidation);
|
||||
|
||||
isPageValid("configure", Object.keys(commonVal).length === 0);
|
||||
|
||||
setValidationErrors(commonVal);
|
||||
}, [
|
||||
customImage,
|
||||
imageName,
|
||||
consoleImage,
|
||||
customDockerhub,
|
||||
imageRegistry,
|
||||
imageRegistryUsername,
|
||||
imageRegistryPassword,
|
||||
isPageValid,
|
||||
]);
|
||||
|
||||
const cleanValidation = (fieldName: string) => {
|
||||
setValidationErrors(clearValidationError(validationErrors, fieldName));
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
<h3 className={classes.h3Section}>Configure</h3>
|
||||
<span className={classes.descriptionText}>
|
||||
Basic configurations for tenant management
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="custom_image"
|
||||
id="custom_image"
|
||||
name="custom_image"
|
||||
checked={customImage}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
updateField("customImage", checked);
|
||||
}}
|
||||
label={"Use custom image"}
|
||||
/>
|
||||
</Grid>
|
||||
{customImage && (
|
||||
<Fragment>
|
||||
Please enter the MinIO image from dockerhub to use
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="image"
|
||||
name="image"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("imageName", e.target.value);
|
||||
cleanValidation("image");
|
||||
}}
|
||||
label="MinIO's Image"
|
||||
value={imageName}
|
||||
error={validationErrors["image"] || ""}
|
||||
placeholder="E.g. minio/minio:RELEASE.2020-05-08T02-40-49Z"
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="consoleImage"
|
||||
name="consoleImage"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("consoleImage", e.target.value);
|
||||
cleanValidation("consoleImage");
|
||||
}}
|
||||
label="Console's Image"
|
||||
value={consoleImage}
|
||||
error={validationErrors["consoleImage"] || ""}
|
||||
placeholder="E.g. minio/console:v0.3.13"
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{customImage && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="custom_docker_hub"
|
||||
id="custom_docker_hub"
|
||||
name="custom_docker_hub"
|
||||
checked={customDockerhub}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("customDockerhub", checked);
|
||||
}}
|
||||
label={"Set/Update Image Registry"}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{customDockerhub && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="registry"
|
||||
name="registry"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("imageRegistry", e.target.value);
|
||||
}}
|
||||
label="Endpoint"
|
||||
value={imageRegistry}
|
||||
error={validationErrors["registry"] || ""}
|
||||
placeholder="E.g. https://index.docker.io/v1/"
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="registryUsername"
|
||||
name="registryUsername"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("imageRegistryUsername", e.target.value);
|
||||
}}
|
||||
label="Username"
|
||||
value={imageRegistryUsername}
|
||||
error={validationErrors["registryUsername"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="registryPassword"
|
||||
name="registryPassword"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("imageRegistryPassword", e.target.value);
|
||||
}}
|
||||
label="Password"
|
||||
value={imageRegistryPassword}
|
||||
error={validationErrors["registryPassword"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
<div className={classes.headerElement}>
|
||||
<h3 className={classes.h3Section}>Expose Services</h3>
|
||||
<span className={classes.descriptionText}>
|
||||
Whether the tenant's services should request an external IP.
|
||||
</span>
|
||||
</div>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="expose_minio"
|
||||
id="expose_minio"
|
||||
name="expose_minio"
|
||||
checked={exposeMinIO}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("exposeMinIO", checked);
|
||||
}}
|
||||
label={"Expose MiniO Service"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="expose_console"
|
||||
id="expose_console"
|
||||
name="expose_console"
|
||||
checked={exposeConsole}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("exposeConsole", checked);
|
||||
}}
|
||||
label={"Expose Console Service"}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
customImage: state.tenants.createTenant.fields.configure.customImage,
|
||||
imageName: state.tenants.createTenant.fields.configure.imageName,
|
||||
consoleImage: state.tenants.createTenant.fields.configure.consoleImage,
|
||||
customDockerhub: state.tenants.createTenant.fields.configure.customDockerhub,
|
||||
imageRegistry: state.tenants.createTenant.fields.configure.imageRegistry,
|
||||
imageRegistryUsername:
|
||||
state.tenants.createTenant.fields.configure.imageRegistryUsername,
|
||||
imageRegistryPassword:
|
||||
state.tenants.createTenant.fields.configure.imageRegistryPassword,
|
||||
exposeMinIO: state.tenants.createTenant.fields.configure.exposeMinIO,
|
||||
exposeConsole: state.tenants.createTenant.fields.configure.exposeConsole,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(Configure));
|
||||
@@ -0,0 +1,923 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useState, useEffect, useCallback } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import {
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
addFileServerCert,
|
||||
addFileClientCert,
|
||||
addFileVaultCert,
|
||||
addFileVaultCa,
|
||||
addFileGemaltoCa,
|
||||
} from "../../actions";
|
||||
import {
|
||||
modalBasic,
|
||||
wizardCommon,
|
||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
||||
import { AppState } from "../../../../../store";
|
||||
import { clearValidationError } from "../../utils";
|
||||
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
import FileSelector from "../../../Common/FormComponents/FileSelector/FileSelector";
|
||||
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import {
|
||||
commonFormValidation,
|
||||
IValidation,
|
||||
} from "../../../../../utils/validationFunctions";
|
||||
import { KeyPair } from "../../ListTenants/utils";
|
||||
|
||||
interface IEncryptionProps {
|
||||
classes: any;
|
||||
updateAddField: typeof updateAddField;
|
||||
isPageValid: typeof isPageValid;
|
||||
addFileServerCert: typeof addFileServerCert;
|
||||
addFileClientCert: typeof addFileClientCert;
|
||||
addFileVaultCert: typeof addFileVaultCert;
|
||||
addFileVaultCa: typeof addFileVaultCa;
|
||||
addFileGemaltoCa: typeof addFileGemaltoCa;
|
||||
enableEncryption: boolean;
|
||||
encryptionType: string;
|
||||
gemaltoEndpoint: string;
|
||||
gemaltoToken: string;
|
||||
gemaltoDomain: string;
|
||||
gemaltoRetry: string;
|
||||
awsEndpoint: string;
|
||||
awsRegion: string;
|
||||
awsKMSKey: string;
|
||||
awsAccessKey: string;
|
||||
awsSecretKey: string;
|
||||
awsToken: string;
|
||||
vaultEndpoint: string;
|
||||
vaultEngine: string;
|
||||
vaultNamespace: string;
|
||||
vaultPrefix: string;
|
||||
vaultAppRoleEngine: string;
|
||||
vaultId: string;
|
||||
vaultSecret: string;
|
||||
vaultRetry: string;
|
||||
vaultPing: string;
|
||||
gcpProjectID: string;
|
||||
gcpEndpoint: string;
|
||||
gcpClientEmail: string;
|
||||
gcpClientID: string;
|
||||
gcpPrivateKeyID: string;
|
||||
gcpPrivateKey: string;
|
||||
enableCustomCertsForKES: boolean;
|
||||
enableAutoCert: boolean;
|
||||
enableTLS: boolean;
|
||||
enableCustomCerts: boolean;
|
||||
minioCertificates: KeyPair[];
|
||||
serverCertificate: KeyPair;
|
||||
clientCertificate: KeyPair;
|
||||
vaultCertificate: KeyPair;
|
||||
vaultCA: KeyPair;
|
||||
gemaltoCA: KeyPair;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...wizardCommon,
|
||||
});
|
||||
|
||||
const Encryption = ({
|
||||
classes,
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
addFileServerCert,
|
||||
addFileClientCert,
|
||||
addFileVaultCert,
|
||||
addFileVaultCa,
|
||||
addFileGemaltoCa,
|
||||
enableEncryption,
|
||||
enableCustomCerts,
|
||||
encryptionType,
|
||||
gemaltoEndpoint,
|
||||
gemaltoToken,
|
||||
gemaltoDomain,
|
||||
gemaltoRetry,
|
||||
awsEndpoint,
|
||||
awsRegion,
|
||||
awsKMSKey,
|
||||
awsAccessKey,
|
||||
awsSecretKey,
|
||||
awsToken,
|
||||
vaultEndpoint,
|
||||
vaultEngine,
|
||||
vaultNamespace,
|
||||
vaultPrefix,
|
||||
vaultAppRoleEngine,
|
||||
vaultId,
|
||||
vaultSecret,
|
||||
vaultRetry,
|
||||
vaultPing,
|
||||
gcpProjectID,
|
||||
gcpEndpoint,
|
||||
gcpClientEmail,
|
||||
gcpClientID,
|
||||
gcpPrivateKeyID,
|
||||
gcpPrivateKey,
|
||||
enableCustomCertsForKES,
|
||||
enableAutoCert,
|
||||
enableTLS,
|
||||
minioCertificates,
|
||||
serverCertificate,
|
||||
clientCertificate,
|
||||
vaultCertificate,
|
||||
vaultCA,
|
||||
gemaltoCA,
|
||||
}: IEncryptionProps) => {
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
|
||||
let encryptionAvailable = false;
|
||||
if (
|
||||
enableTLS &&
|
||||
(enableAutoCert ||
|
||||
(minioCertificates &&
|
||||
minioCertificates.filter(
|
||||
(item) => item.encoded_key && item.encoded_cert
|
||||
).length > 0))
|
||||
) {
|
||||
encryptionAvailable = true;
|
||||
}
|
||||
|
||||
// Common
|
||||
const updateField = useCallback(
|
||||
(field: string, value: any) => {
|
||||
updateAddField("encryption", field, value);
|
||||
},
|
||||
[updateAddField]
|
||||
);
|
||||
|
||||
const cleanValidation = (fieldName: string) => {
|
||||
setValidationErrors(clearValidationError(validationErrors, fieldName));
|
||||
};
|
||||
|
||||
// Validation
|
||||
useEffect(() => {
|
||||
let encryptionValidation: IValidation[] = [];
|
||||
|
||||
if (enableEncryption) {
|
||||
if (enableCustomCerts) {
|
||||
encryptionValidation = [
|
||||
...encryptionValidation,
|
||||
{
|
||||
fieldKey: "serverKey",
|
||||
required: !enableAutoCert,
|
||||
value: serverCertificate.encoded_key,
|
||||
},
|
||||
{
|
||||
fieldKey: "serverCert",
|
||||
required: !enableAutoCert,
|
||||
value: serverCertificate.encoded_cert,
|
||||
},
|
||||
{
|
||||
fieldKey: "clientKey",
|
||||
required: !enableAutoCert,
|
||||
value: clientCertificate.encoded_key,
|
||||
},
|
||||
{
|
||||
fieldKey: "clientCert",
|
||||
required: !enableAutoCert,
|
||||
value: clientCertificate.encoded_cert,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (encryptionType === "vault") {
|
||||
encryptionValidation = [
|
||||
...encryptionValidation,
|
||||
{
|
||||
fieldKey: "vault_endpoint",
|
||||
required: true,
|
||||
value: vaultEndpoint,
|
||||
},
|
||||
{
|
||||
fieldKey: "vault_id",
|
||||
required: true,
|
||||
value: vaultId,
|
||||
},
|
||||
{
|
||||
fieldKey: "vault_secret",
|
||||
required: true,
|
||||
value: vaultSecret,
|
||||
},
|
||||
{
|
||||
fieldKey: "vault_ping",
|
||||
required: false,
|
||||
value: vaultPing,
|
||||
customValidation: parseInt(vaultPing) < 0,
|
||||
customValidationMessage: "Value needs to be 0 or greater",
|
||||
},
|
||||
{
|
||||
fieldKey: "vault_retry",
|
||||
required: false,
|
||||
value: vaultRetry,
|
||||
customValidation: parseInt(vaultRetry) < 0,
|
||||
customValidationMessage: "Value needs to be 0 or greater",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (encryptionType === "aws") {
|
||||
encryptionValidation = [
|
||||
...encryptionValidation,
|
||||
{
|
||||
fieldKey: "aws_endpoint",
|
||||
required: true,
|
||||
value: awsEndpoint,
|
||||
},
|
||||
{
|
||||
fieldKey: "aws_region",
|
||||
required: true,
|
||||
value: awsRegion,
|
||||
},
|
||||
{
|
||||
fieldKey: "aws_accessKey",
|
||||
required: true,
|
||||
value: awsAccessKey,
|
||||
},
|
||||
{
|
||||
fieldKey: "aws_secretKey",
|
||||
required: true,
|
||||
value: awsSecretKey,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (encryptionType === "gemalto") {
|
||||
encryptionValidation = [
|
||||
...encryptionValidation,
|
||||
{
|
||||
fieldKey: "gemalto_endpoint",
|
||||
required: true,
|
||||
value: gemaltoEndpoint,
|
||||
},
|
||||
{
|
||||
fieldKey: "gemalto_token",
|
||||
required: true,
|
||||
value: gemaltoToken,
|
||||
},
|
||||
{
|
||||
fieldKey: "gemalto_domain",
|
||||
required: true,
|
||||
value: gemaltoDomain,
|
||||
},
|
||||
{
|
||||
fieldKey: "gemalto_retry",
|
||||
required: false,
|
||||
value: gemaltoRetry,
|
||||
customValidation: parseInt(gemaltoRetry) < 0,
|
||||
customValidationMessage: "Value needs to be 0 or greater",
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const commonVal = commonFormValidation(encryptionValidation);
|
||||
|
||||
isPageValid("encryption", Object.keys(commonVal).length === 0);
|
||||
|
||||
setValidationErrors(commonVal);
|
||||
}, [
|
||||
enableEncryption,
|
||||
encryptionType,
|
||||
vaultEndpoint,
|
||||
vaultEngine,
|
||||
vaultId,
|
||||
vaultSecret,
|
||||
vaultPing,
|
||||
vaultRetry,
|
||||
awsEndpoint,
|
||||
awsRegion,
|
||||
awsSecretKey,
|
||||
awsAccessKey,
|
||||
gemaltoEndpoint,
|
||||
gemaltoToken,
|
||||
gemaltoDomain,
|
||||
gemaltoRetry,
|
||||
gcpProjectID,
|
||||
isPageValid,
|
||||
enableAutoCert,
|
||||
enableCustomCerts,
|
||||
serverCertificate.encoded_key,
|
||||
serverCertificate.encoded_cert,
|
||||
clientCertificate.encoded_key,
|
||||
clientCertificate.encoded_cert,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
<h3 className={classes.h3Section}>Encryption</h3>
|
||||
<span className={classes.descriptionText}>
|
||||
How would you like to encrypt the information at rest.
|
||||
</span>
|
||||
</div>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="enableEncryption"
|
||||
id="enableEncryption"
|
||||
name="enableEncryption"
|
||||
checked={enableEncryption}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("enableEncryption", checked);
|
||||
}}
|
||||
label={"Enable Server Side Encryption"}
|
||||
disabled={!encryptionAvailable}
|
||||
/>
|
||||
</Grid>
|
||||
{enableEncryption && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<RadioGroupSelector
|
||||
currentSelection={encryptionType}
|
||||
id="encryptionType"
|
||||
name="encryptionType"
|
||||
label="Encryption Options"
|
||||
onChange={(e) => {
|
||||
updateField("encryptionType", e.target.value);
|
||||
}}
|
||||
selectorOptions={[
|
||||
{ label: "Vault", value: "vault" },
|
||||
{ label: "AWS", value: "aws" },
|
||||
{ label: "Gemalto", value: "gemalto" },
|
||||
{ label: "GCP", value: "gcp" },
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
{encryptionType === "vault" && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="enableCustomCertsForKES"
|
||||
id="enableCustomCertsForKES"
|
||||
name="enableCustomCertsForKES"
|
||||
checked={enableCustomCertsForKES || !enableAutoCert}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("enableCustomCertsForKES", checked);
|
||||
}}
|
||||
label={"Custom Certificates"}
|
||||
disabled={!enableAutoCert}
|
||||
/>
|
||||
</Grid>
|
||||
{(enableCustomCertsForKES || !enableAutoCert) && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="overline" display="block" gutterBottom>
|
||||
Encryption Service Certificates
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
<Grid item xs={6}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileServerCert("key", fileName, encodedValue);
|
||||
cleanValidation("serverKey");
|
||||
}}
|
||||
accept=".key,.pem"
|
||||
id="serverKey"
|
||||
name="serverKey"
|
||||
label="Key"
|
||||
error={validationErrors["serverKey"] || ""}
|
||||
value={serverCertificate.key}
|
||||
required={!enableAutoCert}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileServerCert("cert", fileName, encodedValue);
|
||||
cleanValidation("serverCert");
|
||||
}}
|
||||
accept=".cer,.crt,.cert,.pem"
|
||||
id="serverCert"
|
||||
name="serverCert"
|
||||
label="Cert"
|
||||
error={validationErrors["serverCert"] || ""}
|
||||
value={serverCertificate.cert}
|
||||
required={!enableAutoCert}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="overline" display="block" gutterBottom>
|
||||
Mutual TLS authentication
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
<Grid item xs={6}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileClientCert("key", fileName, encodedValue);
|
||||
cleanValidation("clientKey");
|
||||
}}
|
||||
accept=".key,.pem"
|
||||
id="clientKey"
|
||||
name="clientKey"
|
||||
label="Key"
|
||||
error={validationErrors["clientKey"] || ""}
|
||||
value={clientCertificate.key}
|
||||
required={!enableAutoCert}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileClientCert("cert", fileName, encodedValue);
|
||||
cleanValidation("clientCert");
|
||||
}}
|
||||
accept=".cer,.crt,.cert,.pem"
|
||||
id="clientCert"
|
||||
name="clientCert"
|
||||
label="Cert"
|
||||
error={validationErrors["clientCert"] || ""}
|
||||
value={clientCertificate.cert}
|
||||
required={!enableAutoCert}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="vault_endpoint"
|
||||
name="vault_endpoint"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("vaultEndpoint", e.target.value);
|
||||
cleanValidation("vault_endpoint");
|
||||
}}
|
||||
label="Endpoint"
|
||||
value={vaultEndpoint}
|
||||
error={validationErrors["vault_endpoint"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="vault_engine"
|
||||
name="vault_engine"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("vaultEngine", e.target.value);
|
||||
cleanValidation("vault_engine");
|
||||
}}
|
||||
label="Engine"
|
||||
value={vaultEngine}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="vault_namespace"
|
||||
name="vault_namespace"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("vaultNamespace", e.target.value);
|
||||
}}
|
||||
label="Namespace"
|
||||
value={vaultNamespace}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="vault_prefix"
|
||||
name="vault_prefix"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("vaultPrefix", e.target.value);
|
||||
}}
|
||||
label="Prefix"
|
||||
value={vaultPrefix}
|
||||
/>
|
||||
</Grid>
|
||||
<h5>App Role</h5>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="vault_approle_engine"
|
||||
name="vault_approle_engine"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("vaultAppRoleEngine", e.target.value);
|
||||
}}
|
||||
label="Engine"
|
||||
value={vaultAppRoleEngine}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="vault_id"
|
||||
name="vault_id"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("vaultId", e.target.value);
|
||||
cleanValidation("vault_id");
|
||||
}}
|
||||
label="AppRole ID"
|
||||
value={vaultId}
|
||||
error={validationErrors["vault_id"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="vault_secret"
|
||||
name="vault_secret"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("vaultSecret", e.target.value);
|
||||
cleanValidation("vault_secret");
|
||||
}}
|
||||
label="AppRole Secret"
|
||||
value={vaultSecret}
|
||||
error={validationErrors["vault_secret"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
min="0"
|
||||
id="vault_retry"
|
||||
name="vault_retry"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("vaultRetry", e.target.value);
|
||||
cleanValidation("vault_retry");
|
||||
}}
|
||||
label="Retry (Seconds)"
|
||||
value={vaultRetry}
|
||||
error={validationErrors["vault_retry"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<h5>Mutual TLS authentication (optional)</h5>
|
||||
<Grid container>
|
||||
<Grid item xs={6}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileVaultCert("key", fileName, encodedValue);
|
||||
cleanValidation("vault_key");
|
||||
}}
|
||||
accept=".key,.pem"
|
||||
id="vault_key"
|
||||
name="vault_key"
|
||||
label="Key"
|
||||
value={vaultCertificate.key}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileVaultCert("cert", fileName, encodedValue);
|
||||
cleanValidation("vault_cert");
|
||||
}}
|
||||
accept=".cer,.crt,.cert,.pem"
|
||||
id="vault_cert"
|
||||
name="vault_cert"
|
||||
label="Cert"
|
||||
value={vaultCertificate.cert}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileVaultCa(fileName, encodedValue);
|
||||
cleanValidation("vault_ca");
|
||||
}}
|
||||
accept=".cer,.crt,.cert,.pem"
|
||||
id="vault_ca"
|
||||
name="vault_ca"
|
||||
label="CA"
|
||||
value={vaultCA.cert}
|
||||
/>
|
||||
</Grid>
|
||||
<h5>Status</h5>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
min="0"
|
||||
id="vault_ping"
|
||||
name="vault_ping"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("vaultPing", e.target.value);
|
||||
cleanValidation("vault_ping");
|
||||
}}
|
||||
label="Ping (Seconds)"
|
||||
value={vaultPing}
|
||||
error={validationErrors["vault_ping"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{encryptionType === "gcp" && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="gcp_project_id"
|
||||
name="gcp_project_id"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gcpProjectID", e.target.value);
|
||||
}}
|
||||
label="Project ID"
|
||||
value={gcpProjectID}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="gcp_endpoint"
|
||||
name="gcp_endpoint"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gcpEndpoint", e.target.value);
|
||||
}}
|
||||
label="Endpoint"
|
||||
value={gcpEndpoint}
|
||||
/>
|
||||
</Grid>
|
||||
<h5>Credentials</h5>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="gcp_client_email"
|
||||
name="gcp_client_email"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gcpClientEmail", e.target.value);
|
||||
}}
|
||||
label="Client Email"
|
||||
value={gcpClientEmail}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="gcp_client_id"
|
||||
name="gcp_client_id"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gcpClientID", e.target.value);
|
||||
}}
|
||||
label="Client ID"
|
||||
value={gcpClientID}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="gcp_private_key_id"
|
||||
name="gcp_private_key_id"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gcpPrivateKeyID", e.target.value);
|
||||
}}
|
||||
label="Private Key ID"
|
||||
value={gcpPrivateKeyID}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="gcp_private_key"
|
||||
name="gcp_private_key"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gcpPrivateKey", e.target.value);
|
||||
}}
|
||||
label="Private Key"
|
||||
value={gcpPrivateKey}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{encryptionType === "aws" && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="aws_endpoint"
|
||||
name="aws_endpoint"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("awsEndpoint", e.target.value);
|
||||
cleanValidation("aws_endpoint");
|
||||
}}
|
||||
label="Endpoint"
|
||||
value={awsEndpoint}
|
||||
error={validationErrors["aws_endpoint"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="aws_region"
|
||||
name="aws_region"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("awsRegion", e.target.value);
|
||||
cleanValidation("aws_region");
|
||||
}}
|
||||
label="Region"
|
||||
value={awsRegion}
|
||||
error={validationErrors["aws_region"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="aws_kmsKey"
|
||||
name="aws_kmsKey"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("awsKMSKey", e.target.value);
|
||||
}}
|
||||
label="KMS Key"
|
||||
value={awsKMSKey}
|
||||
/>
|
||||
</Grid>
|
||||
<h5>Credentials</h5>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="aws_accessKey"
|
||||
name="aws_accessKey"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("awsAccessKey", e.target.value);
|
||||
cleanValidation("aws_accessKey");
|
||||
}}
|
||||
label="Access Key"
|
||||
value={awsAccessKey}
|
||||
error={validationErrors["aws_accessKey"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="aws_secretKey"
|
||||
name="aws_secretKey"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("awsSecretKey", e.target.value);
|
||||
cleanValidation("aws_secretKey");
|
||||
}}
|
||||
label="Secret Key"
|
||||
value={awsSecretKey}
|
||||
error={validationErrors["aws_secretKey"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="aws_token"
|
||||
name="aws_token"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("awsToken", e.target.value);
|
||||
}}
|
||||
label="Token"
|
||||
value={awsToken}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{encryptionType === "gemalto" && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="gemalto_endpoint"
|
||||
name="gemalto_endpoint"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gemaltoEndpoint", e.target.value);
|
||||
cleanValidation("gemalto_endpoint");
|
||||
}}
|
||||
label="Endpoint"
|
||||
value={gemaltoEndpoint}
|
||||
error={validationErrors["gemalto_endpoint"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<h5>Credentials</h5>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="gemalto_token"
|
||||
name="gemalto_token"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gemaltoToken", e.target.value);
|
||||
cleanValidation("gemalto_token");
|
||||
}}
|
||||
label="Token"
|
||||
value={gemaltoToken}
|
||||
error={validationErrors["gemalto_token"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="gemalto_domain"
|
||||
name="gemalto_domain"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gemaltoDomain", e.target.value);
|
||||
cleanValidation("gemalto_domain");
|
||||
}}
|
||||
label="Domain"
|
||||
value={gemaltoDomain}
|
||||
error={validationErrors["gemalto_domain"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
min="0"
|
||||
id="gemalto_retry"
|
||||
name="gemalto_retry"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("gemaltoRetry", e.target.value);
|
||||
cleanValidation("gemalto_retry");
|
||||
}}
|
||||
label="Retry (seconds)"
|
||||
value={gemaltoRetry}
|
||||
error={validationErrors["gemalto_retry"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<h5>Custom CA Root certificate verification</h5>
|
||||
<Grid item xs={12}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileGemaltoCa(fileName, encodedValue);
|
||||
cleanValidation("gemalto_ca");
|
||||
}}
|
||||
accept=".cer,.crt,.cert,.pem"
|
||||
id="gemalto_ca"
|
||||
name="gemalto_ca"
|
||||
label="CA"
|
||||
value={gemaltoCA.cert}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
enableEncryption:
|
||||
state.tenants.createTenant.fields.encryption.enableEncryption,
|
||||
encryptionType: state.tenants.createTenant.fields.encryption.encryptionType,
|
||||
gemaltoEndpoint: state.tenants.createTenant.fields.encryption.gemaltoEndpoint,
|
||||
gemaltoToken: state.tenants.createTenant.fields.encryption.gemaltoToken,
|
||||
gemaltoDomain: state.tenants.createTenant.fields.encryption.gemaltoDomain,
|
||||
gemaltoRetry: state.tenants.createTenant.fields.encryption.gemaltoRetry,
|
||||
awsEndpoint: state.tenants.createTenant.fields.encryption.awsEndpoint,
|
||||
awsRegion: state.tenants.createTenant.fields.encryption.awsRegion,
|
||||
awsKMSKey: state.tenants.createTenant.fields.encryption.awsKMSKey,
|
||||
awsAccessKey: state.tenants.createTenant.fields.encryption.awsAccessKey,
|
||||
awsSecretKey: state.tenants.createTenant.fields.encryption.awsSecretKey,
|
||||
awsToken: state.tenants.createTenant.fields.encryption.awsToken,
|
||||
vaultEndpoint: state.tenants.createTenant.fields.encryption.vaultEndpoint,
|
||||
vaultEngine: state.tenants.createTenant.fields.encryption.vaultEngine,
|
||||
vaultNamespace: state.tenants.createTenant.fields.encryption.vaultNamespace,
|
||||
vaultPrefix: state.tenants.createTenant.fields.encryption.vaultPrefix,
|
||||
vaultAppRoleEngine:
|
||||
state.tenants.createTenant.fields.encryption.vaultAppRoleEngine,
|
||||
vaultId: state.tenants.createTenant.fields.encryption.vaultId,
|
||||
vaultSecret: state.tenants.createTenant.fields.encryption.vaultSecret,
|
||||
vaultRetry: state.tenants.createTenant.fields.encryption.vaultRetry,
|
||||
vaultPing: state.tenants.createTenant.fields.encryption.vaultPing,
|
||||
gcpProjectID: state.tenants.createTenant.fields.encryption.gcpProjectID,
|
||||
gcpEndpoint: state.tenants.createTenant.fields.encryption.gcpEndpoint,
|
||||
gcpClientEmail: state.tenants.createTenant.fields.encryption.gcpClientEmail,
|
||||
gcpClientID: state.tenants.createTenant.fields.encryption.gcpClientID,
|
||||
gcpPrivateKeyID: state.tenants.createTenant.fields.encryption.gcpPrivateKeyID,
|
||||
gcpPrivateKey: state.tenants.createTenant.fields.encryption.gcpPrivateKey,
|
||||
enableCustomCertsForKES:
|
||||
state.tenants.createTenant.fields.encryption.enableCustomCertsForKES,
|
||||
enableAutoCert: state.tenants.createTenant.fields.security.enableAutoCert,
|
||||
enableTLS: state.tenants.createTenant.fields.security.enableTLS,
|
||||
minioCertificates: state.tenants.createTenant.certificates.minioCertificates,
|
||||
serverCertificate: state.tenants.createTenant.certificates.serverCertificate,
|
||||
clientCertificate: state.tenants.createTenant.certificates.clientCertificate,
|
||||
vaultCertificate: state.tenants.createTenant.certificates.vaultCertificate,
|
||||
vaultCA: state.tenants.createTenant.certificates.vaultCA,
|
||||
gemaltoCA: state.tenants.createTenant.certificates.gemaltoCA,
|
||||
enableCustomCerts:
|
||||
state.tenants.createTenant.fields.security.enableCustomCerts,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
addFileServerCert,
|
||||
addFileClientCert,
|
||||
addFileVaultCert,
|
||||
addFileVaultCa,
|
||||
addFileGemaltoCa,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(Encryption));
|
||||
@@ -0,0 +1,394 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState, useCallback, Fragment } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Grid, Typography } from "@material-ui/core";
|
||||
import {
|
||||
modalBasic,
|
||||
wizardCommon,
|
||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
||||
import { updateAddField, isPageValid } from "../../actions";
|
||||
import {
|
||||
commonFormValidation,
|
||||
IValidation,
|
||||
} from "../../../../../utils/validationFunctions";
|
||||
import { AppState } from "../../../../../store";
|
||||
import { clearValidationError } from "../../utils";
|
||||
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
|
||||
interface IIdentityProviderProps {
|
||||
classes: any;
|
||||
idpSelection: string;
|
||||
openIDURL: string;
|
||||
openIDClientID: string;
|
||||
openIDSecretID: string;
|
||||
ADURL: string;
|
||||
ADSkipTLS: boolean;
|
||||
ADServerInsecure: boolean;
|
||||
ADUserNameFilter: string;
|
||||
ADGroupBaseDN: string;
|
||||
ADGroupSearchFilter: string;
|
||||
ADNameAttribute: string;
|
||||
updateAddField: typeof updateAddField;
|
||||
isPageValid: typeof isPageValid;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...wizardCommon,
|
||||
});
|
||||
|
||||
const IdentityProvider = ({
|
||||
classes,
|
||||
idpSelection,
|
||||
openIDURL,
|
||||
openIDClientID,
|
||||
openIDSecretID,
|
||||
ADURL,
|
||||
ADSkipTLS,
|
||||
ADServerInsecure,
|
||||
ADUserNameFilter,
|
||||
ADGroupBaseDN,
|
||||
ADGroupSearchFilter,
|
||||
ADNameAttribute,
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
}: IIdentityProviderProps) => {
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
|
||||
// Common
|
||||
const updateField = useCallback(
|
||||
(field: string, value: any) => {
|
||||
updateAddField("identityProvider", field, value);
|
||||
},
|
||||
[updateAddField]
|
||||
);
|
||||
|
||||
const cleanValidation = (fieldName: string) => {
|
||||
setValidationErrors(clearValidationError(validationErrors, fieldName));
|
||||
};
|
||||
|
||||
// Validation
|
||||
|
||||
useEffect(() => {
|
||||
let customIDPValidation: IValidation[] = [];
|
||||
|
||||
if (idpSelection === "Built-in") {
|
||||
isPageValid("identityProvider", true);
|
||||
setValidationErrors({});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (idpSelection === "OpenID") {
|
||||
customIDPValidation = [
|
||||
...customIDPValidation,
|
||||
{
|
||||
fieldKey: "openID_URL",
|
||||
required: true,
|
||||
value: openIDURL,
|
||||
},
|
||||
{
|
||||
fieldKey: "openID_clientID",
|
||||
required: true,
|
||||
value: openIDClientID,
|
||||
},
|
||||
{
|
||||
fieldKey: "openID_secretID",
|
||||
required: true,
|
||||
value: openIDSecretID,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (idpSelection === "AD") {
|
||||
customIDPValidation = [
|
||||
...customIDPValidation,
|
||||
{
|
||||
fieldKey: "AD_URL",
|
||||
required: true,
|
||||
value: ADURL,
|
||||
},
|
||||
{
|
||||
fieldKey: "ad_userNameFilter",
|
||||
required: true,
|
||||
value: ADUserNameFilter,
|
||||
},
|
||||
{
|
||||
fieldKey: "ad_groupBaseDN",
|
||||
required: true,
|
||||
value: ADGroupBaseDN,
|
||||
},
|
||||
{
|
||||
fieldKey: "ad_groupSearchFilter",
|
||||
required: true,
|
||||
value: ADGroupSearchFilter,
|
||||
},
|
||||
{
|
||||
fieldKey: "ad_nameAttribute",
|
||||
required: true,
|
||||
value: ADNameAttribute,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const commonVal = commonFormValidation(customIDPValidation);
|
||||
|
||||
isPageValid("identityProvider", Object.keys(commonVal).length === 0);
|
||||
|
||||
setValidationErrors(commonVal);
|
||||
}, [
|
||||
idpSelection,
|
||||
openIDURL,
|
||||
openIDClientID,
|
||||
openIDSecretID,
|
||||
ADURL,
|
||||
ADUserNameFilter,
|
||||
ADGroupBaseDN,
|
||||
ADGroupSearchFilter,
|
||||
ADNameAttribute,
|
||||
isPageValid,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
<h3 className={classes.h3Section}>Identity Provider</h3>
|
||||
<span className={classes.descriptionText}>
|
||||
Access to the tenant can be controlled via an external Identity
|
||||
Manager.
|
||||
</span>
|
||||
</div>
|
||||
<Grid item xs={12}>
|
||||
<RadioGroupSelector
|
||||
currentSelection={idpSelection}
|
||||
id="idp-options"
|
||||
name="idp-options"
|
||||
label="Protocol"
|
||||
onChange={(e) => {
|
||||
updateField("idpSelection", e.target.value);
|
||||
}}
|
||||
selectorOptions={[
|
||||
{ label: "Built-in", value: "Built-in" },
|
||||
{ label: "OpenID", value: "OpenID" },
|
||||
{ label: "Active Directory", value: "AD" },
|
||||
]}
|
||||
/>
|
||||
MinIO supports both OpenID and Active Directory
|
||||
</Grid>
|
||||
|
||||
{idpSelection === "OpenID" && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="openID_URL"
|
||||
name="openID_URL"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("openIDURL", e.target.value);
|
||||
cleanValidation("openID_URL");
|
||||
}}
|
||||
label="URL"
|
||||
value={openIDURL}
|
||||
error={validationErrors["openID_URL"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="openID_clientID"
|
||||
name="openID_clientID"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("openIDClientID", e.target.value);
|
||||
cleanValidation("openID_clientID");
|
||||
}}
|
||||
label="Client ID"
|
||||
value={openIDClientID}
|
||||
error={validationErrors["openID_clientID"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="openID_secretID"
|
||||
name="openID_secretID"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("openIDSecretID", e.target.value);
|
||||
cleanValidation("openID_secretID");
|
||||
}}
|
||||
label="Secret ID"
|
||||
value={openIDSecretID}
|
||||
error={validationErrors["openID_secretID"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{idpSelection === "AD" && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="AD_URL"
|
||||
name="AD_URL"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("ADURL", e.target.value);
|
||||
cleanValidation("AD_URL");
|
||||
}}
|
||||
label="URL"
|
||||
value={ADURL}
|
||||
error={validationErrors["AD_URL"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="ad_skipTLS"
|
||||
id="ad_skipTLS"
|
||||
name="ad_skipTLS"
|
||||
checked={ADSkipTLS}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("ADSkipTLS", checked);
|
||||
}}
|
||||
label={"Skip TLS Verification"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="ad_serverInsecure"
|
||||
id="ad_serverInsecure"
|
||||
name="ad_serverInsecure"
|
||||
checked={ADServerInsecure}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("ADServerInsecure", checked);
|
||||
}}
|
||||
label={"Server Insecure"}
|
||||
/>
|
||||
</Grid>
|
||||
{ADServerInsecure ? (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
className={classes.error}
|
||||
variant="caption"
|
||||
display="block"
|
||||
gutterBottom
|
||||
>
|
||||
Warning: All traffic with Active Directory will be unencrypted
|
||||
</Typography>
|
||||
<br />
|
||||
</Grid>
|
||||
) : null}
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="ad_userNameFilter"
|
||||
name="ad_userNameFilter"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("ADUserNameFilter", e.target.value);
|
||||
cleanValidation("ad_userNameFilter");
|
||||
}}
|
||||
label="User Search Filter"
|
||||
value={ADUserNameFilter}
|
||||
error={validationErrors["ad_userNameFilter"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="ad_groupBaseDN"
|
||||
name="ad_groupBaseDN"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("ADGroupBaseDN", e.target.value);
|
||||
cleanValidation("ad_groupBaseDN");
|
||||
}}
|
||||
label="Group Search Base DN"
|
||||
value={ADGroupBaseDN}
|
||||
error={validationErrors["ad_groupBaseDN"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="ad_groupSearchFilter"
|
||||
name="ad_groupSearchFilter"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("ADGroupSearchFilter", e.target.value);
|
||||
cleanValidation("ad_groupSearchFilter");
|
||||
}}
|
||||
label="Group Search Filter"
|
||||
value={ADGroupSearchFilter}
|
||||
error={validationErrors["ad_groupSearchFilter"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="ad_nameAttribute"
|
||||
name="ad_nameAttribute"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("ADNameAttribute", e.target.value);
|
||||
cleanValidation("ad_nameAttribute");
|
||||
}}
|
||||
label="Group Name Attribute"
|
||||
value={ADNameAttribute}
|
||||
error={validationErrors["ad_nameAttribute"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
idpSelection: state.tenants.createTenant.fields.identityProvider.idpSelection,
|
||||
openIDURL: state.tenants.createTenant.fields.identityProvider.openIDURL,
|
||||
openIDClientID:
|
||||
state.tenants.createTenant.fields.identityProvider.openIDClientID,
|
||||
openIDSecretID:
|
||||
state.tenants.createTenant.fields.identityProvider.openIDSecretID,
|
||||
ADURL: state.tenants.createTenant.fields.identityProvider.ADURL,
|
||||
ADSkipTLS: state.tenants.createTenant.fields.identityProvider.ADSkipTLS,
|
||||
ADServerInsecure:
|
||||
state.tenants.createTenant.fields.identityProvider.ADServerInsecure,
|
||||
ADUserNameFilter:
|
||||
state.tenants.createTenant.fields.identityProvider.ADUserNameFilter,
|
||||
ADGroupBaseDN:
|
||||
state.tenants.createTenant.fields.identityProvider.ADGroupBaseDN,
|
||||
ADGroupSearchFilter:
|
||||
state.tenants.createTenant.fields.identityProvider.ADGroupSearchFilter,
|
||||
ADNameAttribute:
|
||||
state.tenants.createTenant.fields.identityProvider.ADNameAttribute,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(IdentityProvider));
|
||||
@@ -0,0 +1,272 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useState, useMemo, useCallback } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import get from "lodash/get";
|
||||
import debounce from "lodash/debounce";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import {
|
||||
modalBasic,
|
||||
wizardCommon,
|
||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
||||
import { setModalErrorSnackMessage } from "../../../../../actions";
|
||||
import {
|
||||
setAdvancedMode,
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
setStorageClassesList,
|
||||
setLimitSize,
|
||||
} from "../../actions";
|
||||
import {
|
||||
IQuotaElement,
|
||||
IQuotas,
|
||||
Opts,
|
||||
getLimitSizes,
|
||||
} from "../../ListTenants/utils";
|
||||
import { AppState } from "../../../../../store";
|
||||
import { commonFormValidation } from "../../../../../utils/validationFunctions";
|
||||
import { clearValidationError } from "../../utils";
|
||||
import api from "../../../../../common/api";
|
||||
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import SelectWrapper from "../../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...wizardCommon,
|
||||
});
|
||||
|
||||
interface INameTenantScreen {
|
||||
classes: any;
|
||||
storageClasses: Opts[];
|
||||
setAdvancedMode: typeof setAdvancedMode;
|
||||
updateAddField: typeof updateAddField;
|
||||
isPageValid: typeof isPageValid;
|
||||
setStorageClassesList: typeof setStorageClassesList;
|
||||
setLimitSize: typeof setLimitSize;
|
||||
tenantName: string;
|
||||
namespace: string;
|
||||
selectedStorageClass: string;
|
||||
advancedMode: boolean;
|
||||
}
|
||||
|
||||
const NameTenant = ({
|
||||
classes,
|
||||
storageClasses,
|
||||
advancedMode,
|
||||
tenantName,
|
||||
namespace,
|
||||
selectedStorageClass,
|
||||
setAdvancedMode,
|
||||
updateAddField,
|
||||
setStorageClassesList,
|
||||
setLimitSize,
|
||||
isPageValid,
|
||||
}: INameTenantScreen) => {
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
|
||||
// Common
|
||||
const updateField = useCallback(
|
||||
(field: string, value: string) => {
|
||||
updateAddField("nameTenant", field, value);
|
||||
},
|
||||
[updateAddField]
|
||||
);
|
||||
|
||||
// Storage classes retrieval
|
||||
const getNamespaceInformation = useCallback(() => {
|
||||
updateField("selectedStorageClass", "");
|
||||
|
||||
setStorageClassesList([]);
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/namespaces/${namespace}/resourcequotas/${namespace}-storagequota`
|
||||
)
|
||||
.then((res: IQuotas) => {
|
||||
const elements: IQuotaElement[] = get(res, "elements", []);
|
||||
setLimitSize(getLimitSizes(res));
|
||||
|
||||
const newStorage = elements.map((storageClass: any) => {
|
||||
const name = get(storageClass, "name", "").split(
|
||||
".storageclass.storage.k8s.io/requests.storage"
|
||||
)[0];
|
||||
|
||||
return { label: name, value: name };
|
||||
});
|
||||
|
||||
setStorageClassesList(newStorage);
|
||||
if (newStorage.length > 0) {
|
||||
updateField("selectedStorageClass", newStorage[0].value);
|
||||
}
|
||||
})
|
||||
.catch((err: any) => {
|
||||
console.error(err);
|
||||
});
|
||||
}, [namespace, setLimitSize, setStorageClassesList, updateField]);
|
||||
|
||||
const debounceNamespace = useMemo(
|
||||
() => debounce(getNamespaceInformation, 500),
|
||||
[getNamespaceInformation]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (namespace !== "") {
|
||||
debounceNamespace();
|
||||
|
||||
// Cancel previous debounce calls during useEffect cleanup.
|
||||
return debounceNamespace.cancel;
|
||||
}
|
||||
}, [debounceNamespace, namespace]);
|
||||
|
||||
// Validation
|
||||
useEffect(() => {
|
||||
const commonValidation = commonFormValidation([
|
||||
{
|
||||
fieldKey: "tenant-name",
|
||||
required: true,
|
||||
pattern: /^[a-z0-9-]{3,63}$/,
|
||||
customPatternMessage:
|
||||
"Name only can contain lowercase letters, numbers and '-'. Min. Length: 3",
|
||||
value: tenantName,
|
||||
},
|
||||
{
|
||||
fieldKey: "namespace",
|
||||
required: true,
|
||||
value: namespace,
|
||||
customValidation: storageClasses.length < 1,
|
||||
customValidationMessage: "Please enter a valid namespace",
|
||||
},
|
||||
]);
|
||||
|
||||
const isValid =
|
||||
!("tenant-name" in commonValidation) &&
|
||||
!("namespace" in commonValidation) &&
|
||||
storageClasses.length > 0;
|
||||
|
||||
isPageValid("nameTenant", isValid);
|
||||
|
||||
setValidationErrors(commonValidation);
|
||||
}, [storageClasses, namespace, tenantName, isPageValid]);
|
||||
|
||||
const frmValidationCleanup = (fieldName: string) => {
|
||||
setValidationErrors(clearValidationError(validationErrors, fieldName));
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
<h3 className={classes.h3Section}>Name Tenant</h3>
|
||||
<span className={classes.descriptionText}>
|
||||
How would you like to name this new tenant?
|
||||
</span>
|
||||
</div>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="tenant-name"
|
||||
name="tenant-name"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("tenantName", e.target.value);
|
||||
frmValidationCleanup("tenant-name");
|
||||
}}
|
||||
label="Name"
|
||||
value={tenantName}
|
||||
required
|
||||
error={validationErrors["tenant-name"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="namespace"
|
||||
name="namespace"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("namespace", e.target.value);
|
||||
frmValidationCleanup("namespace");
|
||||
}}
|
||||
label="Namespace"
|
||||
value={namespace}
|
||||
error={validationErrors["namespace"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SelectWrapper
|
||||
id="storage_class"
|
||||
name="storage_class"
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
updateField("selectedStorageClass", e.target.value as string);
|
||||
}}
|
||||
label="Storage Class"
|
||||
value={selectedStorageClass}
|
||||
options={storageClasses}
|
||||
disabled={storageClasses.length < 1}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
<span className={classes.descriptionText}>
|
||||
Check 'Advanced Mode' for additional configuration options, such as
|
||||
configuring an Identity Provider, Encryption at rest, and customized
|
||||
TLS/SSL Certificates.
|
||||
<br />
|
||||
Leave 'Advanced Mode' unchecked to use the secure default settings for
|
||||
the tenant.
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
<FormSwitchWrapper
|
||||
value="adv_mode"
|
||||
id="adv_mode"
|
||||
name="adv_mode"
|
||||
checked={advancedMode}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
setAdvancedMode(checked);
|
||||
}}
|
||||
label={"Advanced Mode"}
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
advancedMode: state.tenants.createTenant.advancedModeOn,
|
||||
tenantName: state.tenants.createTenant.fields.nameTenant.tenantName,
|
||||
namespace: state.tenants.createTenant.fields.nameTenant.namespace,
|
||||
selectedStorageClass:
|
||||
state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
|
||||
storageClasses: state.tenants.createTenant.storageClasses,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
setModalErrorSnackMessage,
|
||||
setAdvancedMode,
|
||||
updateAddField,
|
||||
setStorageClassesList,
|
||||
setLimitSize,
|
||||
isPageValid,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(NameTenant));
|
||||
@@ -0,0 +1,156 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import { AppState } from "../../../../../store";
|
||||
import {
|
||||
modalBasic,
|
||||
wizardCommon,
|
||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
||||
|
||||
interface IPreviewProps {
|
||||
classes: any;
|
||||
tenantName: string;
|
||||
customImage: boolean;
|
||||
imageName: string;
|
||||
consoleImage: string;
|
||||
namespace: string;
|
||||
selectedStorageClass: string;
|
||||
volumeSize: string;
|
||||
sizeFactor: string;
|
||||
advancedMode: boolean;
|
||||
enableTLS: boolean;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...wizardCommon,
|
||||
});
|
||||
|
||||
const Preview = ({
|
||||
classes,
|
||||
tenantName,
|
||||
customImage,
|
||||
imageName,
|
||||
consoleImage,
|
||||
namespace,
|
||||
selectedStorageClass,
|
||||
volumeSize,
|
||||
sizeFactor,
|
||||
advancedMode,
|
||||
enableTLS,
|
||||
}: IPreviewProps) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
<h3 className={classes.h3Section}>Review</h3>
|
||||
<span className={classes.descriptionText}>
|
||||
Review the details of the new tenant
|
||||
</span>
|
||||
</div>
|
||||
<Table size="small">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Tenant Name
|
||||
</TableCell>
|
||||
<TableCell>{tenantName}</TableCell>
|
||||
</TableRow>
|
||||
|
||||
{customImage && (
|
||||
<Fragment>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
MinIO Image
|
||||
</TableCell>
|
||||
<TableCell>{imageName}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Console Image
|
||||
</TableCell>
|
||||
<TableCell>{consoleImage}</TableCell>
|
||||
</TableRow>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{namespace !== "" && (
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Namespace
|
||||
</TableCell>
|
||||
<TableCell>{namespace}</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Storage Class
|
||||
</TableCell>
|
||||
<TableCell>{selectedStorageClass}</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Total Size
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{volumeSize} {sizeFactor}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{advancedMode && (
|
||||
<Fragment>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Enable TLS
|
||||
</TableCell>
|
||||
<TableCell>{enableTLS ? "Enabled" : "Disabled"}</TableCell>
|
||||
</TableRow>
|
||||
</Fragment>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
advancedMode: state.tenants.createTenant.advancedModeOn,
|
||||
enableTLS: state.tenants.createTenant.fields.security.enableTLS,
|
||||
tenantName: state.tenants.createTenant.fields.nameTenant.tenantName,
|
||||
selectedStorageClass:
|
||||
state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
|
||||
customImage: state.tenants.createTenant.fields.configure.customImage,
|
||||
imageName: state.tenants.createTenant.fields.configure.imageName,
|
||||
consoleImage: state.tenants.createTenant.fields.configure.consoleImage,
|
||||
namespace: state.tenants.createTenant.fields.nameTenant.namespace,
|
||||
volumeSize: state.tenants.createTenant.fields.tenantSize.volumeSize,
|
||||
sizeFactor: state.tenants.createTenant.fields.tenantSize.sizeFactor,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {});
|
||||
|
||||
export default withStyles(styles)(connector(Preview));
|
||||
@@ -0,0 +1,358 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useCallback, Fragment } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button, Divider, Grid, Typography } from "@material-ui/core";
|
||||
import {
|
||||
modalBasic,
|
||||
wizardCommon,
|
||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
addFileToCaCertificates,
|
||||
deleteCaCertificate,
|
||||
addCaCertificate,
|
||||
addKeyPair,
|
||||
addFileToKeyPair,
|
||||
deleteKeyPair,
|
||||
addConsoleCertificate,
|
||||
} from "../../actions";
|
||||
import { AppState } from "../../../../../store";
|
||||
import { KeyPair } from "../../ListTenants/utils";
|
||||
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
import FileSelector from "../../../Common/FormComponents/FileSelector/FileSelector";
|
||||
|
||||
interface ISecurityProps {
|
||||
classes: any;
|
||||
enableTLS: boolean;
|
||||
enableAutoCert: boolean;
|
||||
enableCustomCerts: boolean;
|
||||
minioCertificates: KeyPair[];
|
||||
caCertificates: KeyPair[];
|
||||
consoleCertificate: KeyPair;
|
||||
updateAddField: typeof updateAddField;
|
||||
isPageValid: typeof isPageValid;
|
||||
addFileToCaCertificates: typeof addFileToCaCertificates;
|
||||
deleteCaCertificate: typeof deleteCaCertificate;
|
||||
addCaCertificate: typeof addCaCertificate;
|
||||
addKeyPair: typeof addKeyPair;
|
||||
addFileToKeyPair: typeof addFileToKeyPair;
|
||||
deleteKeyPair: typeof deleteKeyPair;
|
||||
addConsoleCertificate: typeof addConsoleCertificate;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...wizardCommon,
|
||||
});
|
||||
|
||||
const Security = ({
|
||||
classes,
|
||||
enableTLS,
|
||||
enableAutoCert,
|
||||
enableCustomCerts,
|
||||
minioCertificates,
|
||||
caCertificates,
|
||||
consoleCertificate,
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
addFileToCaCertificates,
|
||||
deleteCaCertificate,
|
||||
addCaCertificate,
|
||||
addKeyPair,
|
||||
addFileToKeyPair,
|
||||
deleteKeyPair,
|
||||
}: ISecurityProps) => {
|
||||
// Common
|
||||
const updateField = useCallback(
|
||||
(field: string, value: any) => {
|
||||
updateAddField("security", field, value);
|
||||
},
|
||||
[updateAddField]
|
||||
);
|
||||
|
||||
// Validation
|
||||
|
||||
useEffect(() => {
|
||||
if (!enableTLS) {
|
||||
isPageValid("security", true);
|
||||
return;
|
||||
}
|
||||
if (enableAutoCert) {
|
||||
isPageValid("security", true);
|
||||
return;
|
||||
}
|
||||
if (enableCustomCerts) {
|
||||
isPageValid("security", true);
|
||||
return;
|
||||
}
|
||||
isPageValid("security", false);
|
||||
}, [enableTLS, enableAutoCert, enableCustomCerts, isPageValid]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
<h3 className={classes.h3Section}>Security</h3>
|
||||
</div>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="enableTLS"
|
||||
id="enableTLS"
|
||||
name="enableTLS"
|
||||
checked={enableTLS}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("enableTLS", checked);
|
||||
}}
|
||||
label={"Enable TLS"}
|
||||
/>
|
||||
Enable TLS for the tenant, this is required for Encryption Configuration
|
||||
{enableTLS && (
|
||||
<Fragment>
|
||||
<br />
|
||||
<br />
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
AutoCert: MinIO Operator will generate all TLS certificates
|
||||
automatically
|
||||
</Typography>
|
||||
<Typography variant="caption" display="block" gutterBottom>
|
||||
Custom certificates: Allow user to provide your own certificates
|
||||
</Typography>
|
||||
<br />
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
{enableTLS && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<FormSwitchWrapper
|
||||
value="enableAutoCert"
|
||||
id="enableAutoCert"
|
||||
name="enableAutoCert"
|
||||
checked={enableAutoCert}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("enableAutoCert", checked);
|
||||
}}
|
||||
label={"Enable AutoCert"}
|
||||
/>
|
||||
<FormSwitchWrapper
|
||||
value="enableCustomCerts"
|
||||
id="enableCustomCerts"
|
||||
name="enableCustomCerts"
|
||||
checked={enableCustomCerts}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
updateField("enableCustomCerts", checked);
|
||||
}}
|
||||
label={"Custom Certificates"}
|
||||
/>
|
||||
</Grid>
|
||||
{enableCustomCerts && (
|
||||
<Fragment>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="overline" display="block" gutterBottom>
|
||||
MinIO Certificates
|
||||
</Typography>
|
||||
</Grid>
|
||||
{minioCertificates.map((keyPair: KeyPair) => (
|
||||
<Fragment key={keyPair.id}>
|
||||
<Grid item xs={5}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileToKeyPair(
|
||||
keyPair.id,
|
||||
"key",
|
||||
fileName,
|
||||
encodedValue
|
||||
);
|
||||
}}
|
||||
accept=".key,.pem"
|
||||
id="tlsKey"
|
||||
name="tlsKey"
|
||||
label="Key"
|
||||
value={keyPair.key}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={5}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileToKeyPair(
|
||||
keyPair.id,
|
||||
"cert",
|
||||
fileName,
|
||||
encodedValue
|
||||
);
|
||||
}}
|
||||
accept=".cer,.crt,.cert,.pem"
|
||||
id="tlsCert"
|
||||
name="tlsCert"
|
||||
label="Cert"
|
||||
value={keyPair.cert}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={1}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
deleteKeyPair(keyPair.id);
|
||||
}}
|
||||
color="secondary"
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
))}
|
||||
<Grid item xs={12}>
|
||||
<Button onClick={addKeyPair} color="primary">
|
||||
Add More
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
<Divider />
|
||||
<br />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="overline" display="block" gutterBottom>
|
||||
CA Certificates
|
||||
</Typography>
|
||||
</Grid>
|
||||
{caCertificates.map((keyPair: KeyPair) => (
|
||||
<Fragment key={keyPair.id}>
|
||||
<Grid item xs={10}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addFileToCaCertificates(
|
||||
keyPair.id,
|
||||
"cert",
|
||||
fileName,
|
||||
encodedValue
|
||||
);
|
||||
}}
|
||||
accept=".cer,.crt,.cert,.pem"
|
||||
id="tlsCert"
|
||||
name="tlsCert"
|
||||
label="Cert"
|
||||
value={keyPair.cert}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={1}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
deleteCaCertificate(keyPair.id);
|
||||
}}
|
||||
color="secondary"
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
))}
|
||||
<Grid item xs={12}>
|
||||
<Button onClick={addCaCertificate} color="primary">
|
||||
Add More
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
<Divider />
|
||||
<br />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="overline" display="block" gutterBottom>
|
||||
Console Certificates
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addConsoleCertificate("key", fileName, encodedValue);
|
||||
}}
|
||||
accept=".key,.pem"
|
||||
id="consoleKey"
|
||||
name="consoleKey"
|
||||
label="Key"
|
||||
value={consoleCertificate.key}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<FileSelector
|
||||
onChange={(encodedValue, fileName) => {
|
||||
addConsoleCertificate("cert", fileName, encodedValue);
|
||||
}}
|
||||
accept=".cer,.crt,.cert,.pem"
|
||||
id="consoleCert"
|
||||
name="consoleCert"
|
||||
label="Cert"
|
||||
value={consoleCertificate.cert}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
enableTLS: state.tenants.createTenant.fields.security.enableTLS,
|
||||
enableAutoCert: state.tenants.createTenant.fields.security.enableAutoCert,
|
||||
enableCustomCerts:
|
||||
state.tenants.createTenant.fields.security.enableCustomCerts,
|
||||
minioCertificates: state.tenants.createTenant.certificates.minioCertificates,
|
||||
caCertificates: state.tenants.createTenant.certificates.caCertificates,
|
||||
consoleCertificate:
|
||||
state.tenants.createTenant.certificates.consoleCertificate,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
addFileToCaCertificates,
|
||||
deleteCaCertificate,
|
||||
addCaCertificate,
|
||||
addKeyPair,
|
||||
addFileToKeyPair,
|
||||
deleteKeyPair,
|
||||
addConsoleCertificate,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(Security));
|
||||
@@ -0,0 +1,520 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useState, useEffect, useCallback } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { AppState } from "../../../../../store";
|
||||
import { updateAddField, isPageValid } from "../../actions";
|
||||
import {
|
||||
wizardCommon,
|
||||
modalBasic,
|
||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Table from "@material-ui/core/Table";
|
||||
import TableBody from "@material-ui/core/TableBody";
|
||||
import TableCell from "@material-ui/core/TableCell";
|
||||
import TableRow from "@material-ui/core/TableRow";
|
||||
import {
|
||||
getBytes,
|
||||
k8sfactorForDropdown,
|
||||
niceBytes,
|
||||
calculateDistribution,
|
||||
erasureCodeCalc,
|
||||
setMemoryResource,
|
||||
} from "../../../../../common/utils";
|
||||
import { clearValidationError } from "../../utils";
|
||||
import { ecListTransform, Opts } from "../../ListTenants/utils";
|
||||
import { IMemorySize } from "../../ListTenants/types";
|
||||
import { ICapacity, IErasureCodeCalc } from "../../../../../common/types";
|
||||
import api from "../../../../../common/api";
|
||||
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import SelectWrapper from "../../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
import { commonFormValidation } from "../../../../../utils/validationFunctions";
|
||||
|
||||
interface ITenantSizeProps {
|
||||
classes: any;
|
||||
updateAddField: typeof updateAddField;
|
||||
isPageValid: typeof isPageValid;
|
||||
advancedMode: boolean;
|
||||
volumeSize: string;
|
||||
sizeFactor: string;
|
||||
drivesPerServer: string;
|
||||
nodes: string;
|
||||
memoryNode: string;
|
||||
ecParity: string;
|
||||
ecParityChoices: Opts[];
|
||||
cleanECChoices: string[];
|
||||
maxAllocableMemo: number;
|
||||
memorySize: IMemorySize;
|
||||
distribution: any;
|
||||
ecParityCalc: IErasureCodeCalc;
|
||||
limitSize: any;
|
||||
selectedStorageClass: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
...modalBasic,
|
||||
...wizardCommon,
|
||||
});
|
||||
|
||||
const TenantSize = ({
|
||||
classes,
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
advancedMode,
|
||||
volumeSize,
|
||||
sizeFactor,
|
||||
drivesPerServer,
|
||||
nodes,
|
||||
memoryNode,
|
||||
ecParity,
|
||||
ecParityChoices,
|
||||
cleanECChoices,
|
||||
maxAllocableMemo,
|
||||
memorySize,
|
||||
distribution,
|
||||
ecParityCalc,
|
||||
limitSize,
|
||||
selectedStorageClass,
|
||||
}: ITenantSizeProps) => {
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
const usableInformation = ecParityCalc.storageFactors.find(
|
||||
(element) => element.erasureCode === ecParity
|
||||
);
|
||||
|
||||
// Common
|
||||
const updateField = useCallback(
|
||||
(field: string, value: any) => {
|
||||
updateAddField("tenantSize", field, value);
|
||||
},
|
||||
[updateAddField]
|
||||
);
|
||||
|
||||
const cleanValidation = (fieldName: string) => {
|
||||
setValidationErrors(clearValidationError(validationErrors, fieldName));
|
||||
};
|
||||
|
||||
/*Debounce functions*/
|
||||
|
||||
// Storage Quotas
|
||||
|
||||
const validateMemorySize = useCallback(() => {
|
||||
const memSize = parseInt(memoryNode) || 0;
|
||||
const clusterSize = volumeSize || 0;
|
||||
const maxMemSize = maxAllocableMemo || 0;
|
||||
const clusterSizeFactor = sizeFactor;
|
||||
|
||||
const clusterSizeBytes = getBytes(
|
||||
clusterSize.toString(10),
|
||||
clusterSizeFactor
|
||||
);
|
||||
const memoSize = setMemoryResource(memSize, clusterSizeBytes, maxMemSize);
|
||||
|
||||
updateField("memorySize", memoSize);
|
||||
}, [maxAllocableMemo, memoryNode, sizeFactor, updateField, volumeSize]);
|
||||
|
||||
const getMaxAllocableMemory = (nodes: string) => {
|
||||
if (nodes !== "" && !isNaN(parseInt(nodes))) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/cluster/max-allocatable-memory?num_nodes=${nodes}`
|
||||
)
|
||||
.then((res: { max_memory: number }) => {
|
||||
const maxMemory = res.max_memory ? res.max_memory : 0;
|
||||
updateField("maxAllocableMemo", maxMemory);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
updateField("maxAllocableMemo", 0);
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
validateMemorySize();
|
||||
}, [memoryNode, validateMemorySize]);
|
||||
|
||||
useEffect(() => {
|
||||
validateMemorySize();
|
||||
}, [maxAllocableMemo, validateMemorySize]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ecParityChoices.length > 0 && distribution.error === "") {
|
||||
const ecCodeValidated = erasureCodeCalc(
|
||||
cleanECChoices,
|
||||
distribution.persistentVolumes,
|
||||
distribution.pvSize,
|
||||
distribution.nodes
|
||||
);
|
||||
|
||||
updateField("ecParityCalc", ecCodeValidated);
|
||||
updateField("ecParity", ecCodeValidated.defaultEC);
|
||||
}
|
||||
}, [ecParityChoices.length, distribution, cleanECChoices, updateField]);
|
||||
/*End debounce functions*/
|
||||
|
||||
/*Calculate Allocation*/
|
||||
useEffect(() => {
|
||||
validateClusterSize();
|
||||
getECValue();
|
||||
getMaxAllocableMemory(nodes);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [nodes, volumeSize, sizeFactor, drivesPerServer]);
|
||||
|
||||
const validateClusterSize = () => {
|
||||
const size = volumeSize;
|
||||
const factor = sizeFactor;
|
||||
const limitSize = getBytes("12", "Ti", true);
|
||||
|
||||
const clusterCapacity: ICapacity = {
|
||||
unit: factor,
|
||||
value: size.toString(),
|
||||
};
|
||||
|
||||
const distrCalculate = calculateDistribution(
|
||||
clusterCapacity,
|
||||
parseInt(nodes),
|
||||
parseInt(limitSize),
|
||||
parseInt(drivesPerServer)
|
||||
);
|
||||
|
||||
updateField("distribution", distrCalculate);
|
||||
};
|
||||
|
||||
const getECValue = () => {
|
||||
updateField("ecParity", "");
|
||||
|
||||
if (nodes.trim() !== "" && drivesPerServer.trim() !== "") {
|
||||
api
|
||||
.invoke("GET", `/api/v1/get-parity/${nodes}/${drivesPerServer}`)
|
||||
.then((ecList: string[]) => {
|
||||
updateField("ecParityChoices", ecListTransform(ecList));
|
||||
updateField("cleanECChoices", ecList);
|
||||
})
|
||||
.catch((err: any) => {
|
||||
updateField("ecparityChoices", []);
|
||||
isPageValid("tenantSize", false);
|
||||
updateField("ecParity", "");
|
||||
});
|
||||
}
|
||||
};
|
||||
/*Calculate Allocation End*/
|
||||
|
||||
/* Validations of pages */
|
||||
|
||||
useEffect(() => {
|
||||
const parsedSize = getBytes(volumeSize, sizeFactor, true);
|
||||
|
||||
const commonValidation = commonFormValidation([
|
||||
{
|
||||
fieldKey: "nodes",
|
||||
required: true,
|
||||
value: nodes,
|
||||
customValidation: parseInt(nodes) < 4,
|
||||
customValidationMessage: "Number of nodes cannot be less than 4",
|
||||
},
|
||||
{
|
||||
fieldKey: "volume_size",
|
||||
required: true,
|
||||
value: volumeSize,
|
||||
customValidation:
|
||||
parseInt(parsedSize) < 1073741824 ||
|
||||
parseInt(parsedSize) > limitSize[selectedStorageClass],
|
||||
customValidationMessage: `Volume size must be greater than 1Gi and less than ${niceBytes(
|
||||
limitSize[selectedStorageClass],
|
||||
true
|
||||
)}`,
|
||||
},
|
||||
{
|
||||
fieldKey: "memory_per_node",
|
||||
required: true,
|
||||
value: memoryNode,
|
||||
customValidation: parseInt(memoryNode) < 2,
|
||||
customValidationMessage: "Memory size must be greater than 2Gi",
|
||||
},
|
||||
{
|
||||
fieldKey: "drivesps",
|
||||
required: true,
|
||||
value: drivesPerServer,
|
||||
customValidation: parseInt(drivesPerServer) < 1,
|
||||
customValidationMessage: "There must be at least one drive",
|
||||
},
|
||||
]);
|
||||
|
||||
isPageValid(
|
||||
"tenantSize",
|
||||
!("nodes" in commonValidation) &&
|
||||
!("volume_size" in commonValidation) &&
|
||||
!("memory_per_node" in commonValidation) &&
|
||||
!("drivesps" in commonValidation) &&
|
||||
distribution.error === "" &&
|
||||
ecParityCalc.error === 0 &&
|
||||
memorySize.error === ""
|
||||
);
|
||||
|
||||
setValidationErrors(commonValidation);
|
||||
}, [
|
||||
nodes,
|
||||
volumeSize,
|
||||
sizeFactor,
|
||||
memoryNode,
|
||||
distribution,
|
||||
drivesPerServer,
|
||||
ecParityCalc,
|
||||
memorySize,
|
||||
limitSize,
|
||||
selectedStorageClass,
|
||||
]);
|
||||
|
||||
/* End Validation of pages */
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
<h3 className={classes.h3Section}>Tenant Size</h3>
|
||||
<span className={classes.descriptionText}>
|
||||
Please select the desired capacity
|
||||
</span>
|
||||
</div>
|
||||
<span className={classes.error}>{distribution.error}</span>
|
||||
<span className={classes.error}>{memorySize.error}</span>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="nodes"
|
||||
name="nodes"
|
||||
type="number"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("nodes", e.target.value);
|
||||
cleanValidation("nodes");
|
||||
}}
|
||||
label="Number of Servers"
|
||||
value={nodes}
|
||||
min="4"
|
||||
required
|
||||
error={validationErrors["nodes"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="drivesps"
|
||||
name="drivesps"
|
||||
type="number"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("drivesPerServer", e.target.value);
|
||||
cleanValidation("drivesps");
|
||||
}}
|
||||
label="Number of Drives per Server"
|
||||
value={drivesPerServer}
|
||||
min="1"
|
||||
required
|
||||
error={validationErrors["drivesps"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.multiContainer}>
|
||||
<div>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="volume_size"
|
||||
name="volume_size"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("volumeSize", e.target.value);
|
||||
cleanValidation("volume_size");
|
||||
}}
|
||||
label="Total Size"
|
||||
value={volumeSize}
|
||||
required
|
||||
error={validationErrors["volume_size"] || ""}
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.sizeFactorContainer}>
|
||||
<SelectWrapper
|
||||
label={"Unit"}
|
||||
id="size_factor"
|
||||
name="size_factor"
|
||||
value={sizeFactor}
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
updateField("sizeFactor", e.target.value as string);
|
||||
}}
|
||||
options={k8sfactorForDropdown()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Grid>
|
||||
{advancedMode && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="memory_per_node"
|
||||
name="memory_per_node"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
updateField("memoryNode", e.target.value);
|
||||
cleanValidation("memory_per_node");
|
||||
}}
|
||||
label="Memory per Node [Gi]"
|
||||
value={memoryNode}
|
||||
required
|
||||
error={validationErrors["memory_per_node"] || ""}
|
||||
min="2"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<SelectWrapper
|
||||
id="ec_parity"
|
||||
name="ec_parity"
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
updateField("ecParity", e.target.value as string);
|
||||
}}
|
||||
label="Erasure Code Parity"
|
||||
value={ecParity}
|
||||
options={ecParityChoices}
|
||||
/>
|
||||
<span className={classes.descriptionText}>
|
||||
Please select the desired parity. This setting will change the max
|
||||
usable capacity in the cluster
|
||||
</span>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
<h4>Resource Allocation</h4>
|
||||
<Table className={classes.table} aria-label="simple table">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Number of Servers
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{parseInt(nodes) > 0 ? nodes : "-"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Drives per Server
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{distribution ? distribution.disks : "-"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Drive Capacity
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{distribution ? niceBytes(distribution.pvSize) : "-"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Total Number of Volumes
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{distribution ? distribution.persistentVolumes : "-"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{!advancedMode && (
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Memory per Node
|
||||
</TableCell>
|
||||
<TableCell align="right">{memoryNode} Gi</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{ecParityCalc.error === 0 && usableInformation && (
|
||||
<Fragment>
|
||||
<h4>Erasure Code Configuration</h4>
|
||||
<Table className={classes.table} aria-label="simple table">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
EC Parity
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{ecParity !== "" ? ecParity : "-"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Raw Capacity
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{niceBytes(ecParityCalc.rawCapacity)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Usable Capacity
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{niceBytes(usableInformation.maxCapacity)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">
|
||||
Number of server failures to tolerate
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{distribution
|
||||
? Math.floor(
|
||||
usableInformation.maxFailureTolerations /
|
||||
distribution.disks
|
||||
)
|
||||
: "-"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
advancedMode: state.tenants.createTenant.advancedModeOn,
|
||||
volumeSize: state.tenants.createTenant.fields.tenantSize.volumeSize,
|
||||
sizeFactor: state.tenants.createTenant.fields.tenantSize.sizeFactor,
|
||||
drivesPerServer: state.tenants.createTenant.fields.tenantSize.drivesPerServer,
|
||||
nodes: state.tenants.createTenant.fields.tenantSize.nodes,
|
||||
memoryNode: state.tenants.createTenant.fields.tenantSize.memoryNode,
|
||||
ecParity: state.tenants.createTenant.fields.tenantSize.ecParity,
|
||||
ecParityChoices: state.tenants.createTenant.fields.tenantSize.ecParityChoices,
|
||||
cleanECChoices: state.tenants.createTenant.fields.tenantSize.cleanECChoices,
|
||||
maxAllocableMemo:
|
||||
state.tenants.createTenant.fields.tenantSize.maxAllocableMemo,
|
||||
memorySize: state.tenants.createTenant.fields.tenantSize.memorySize,
|
||||
distribution: state.tenants.createTenant.fields.tenantSize.distribution,
|
||||
ecParityCalc: state.tenants.createTenant.fields.tenantSize.ecParityCalc,
|
||||
limitSize: state.tenants.createTenant.fields.tenantSize.limitSize,
|
||||
selectedStorageClass:
|
||||
state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
|
||||
});
|
||||
|
||||
const connector = connect(mapState, {
|
||||
updateAddField,
|
||||
isPageValid,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(TenantSize));
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@
|
||||
// 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, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, Fragment } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
@@ -27,28 +27,33 @@ import { niceBytes } from "../../../../common/utils";
|
||||
import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
searchField,
|
||||
settingsCommon,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import { CreateIcon } from "../../../../icons";
|
||||
import api from "../../../../common/api";
|
||||
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import DeleteTenant from "./DeleteTenant";
|
||||
import AddTenant from "./AddTenant";
|
||||
import AddTenant from "../AddTenant/AddTenant";
|
||||
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
|
||||
import history from "../../../../history";
|
||||
import RefreshIcon from "@material-ui/icons/Refresh";
|
||||
import PageHeader from "../../Common/PageHeader/PageHeader";
|
||||
import { Link } from "react-router-dom";
|
||||
import SlideOptions from "../../Common/SlideOptions/SlideOptions";
|
||||
import BackSettingsIcon from "../../../../icons/BackSettingsIcon";
|
||||
import { resetAddTenantForm } from "../actions";
|
||||
|
||||
interface ITenantsList {
|
||||
classes: any;
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
resetAddTenantForm: typeof resetAddTenantForm;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...settingsCommon,
|
||||
seeMore: {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
@@ -74,13 +79,28 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
},
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
actionsTray: {
|
||||
...actionsTray.actionsTray,
|
||||
padding: "0 38px",
|
||||
},
|
||||
tenantsContainer: {
|
||||
padding: "15px 0",
|
||||
},
|
||||
customConfigurationPage: {
|
||||
height: "calc(100vh - 440px)",
|
||||
scrollbarWidth: "none" as const,
|
||||
"&::-webkit-scrollbar": {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const ListTenants = ({ classes, setErrorSnackMessage }: ITenantsList) => {
|
||||
const [createTenantOpen, setCreateTenantOpen] = useState<boolean>(false);
|
||||
const ListTenants = ({
|
||||
classes,
|
||||
setErrorSnackMessage,
|
||||
resetAddTenantForm,
|
||||
}: ITenantsList) => {
|
||||
const [currentPanel, setCurrentPanel] = useState<number>(0);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [selectedTenant, setSelectedTenant] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
@@ -92,16 +112,9 @@ const ListTenants = ({ classes, setErrorSnackMessage }: ITenantsList) => {
|
||||
setCreatedAccount,
|
||||
] = useState<NewServiceAccount | null>(null);
|
||||
|
||||
const closeAddModalAndRefresh = (
|
||||
reloadData: boolean,
|
||||
res: NewServiceAccount | null
|
||||
) => {
|
||||
setCreateTenantOpen(false);
|
||||
|
||||
if (res !== null) {
|
||||
setShowNewCredentials(true);
|
||||
setCreatedAccount(res);
|
||||
}
|
||||
const closeAddModalAndRefresh = (reloadData: boolean) => {
|
||||
setCurrentPanel(0);
|
||||
resetAddTenantForm();
|
||||
|
||||
if (reloadData) {
|
||||
setIsLoading(true);
|
||||
@@ -131,6 +144,11 @@ const ListTenants = ({ classes, setErrorSnackMessage }: ITenantsList) => {
|
||||
setCreatedAccount(null);
|
||||
};
|
||||
|
||||
const backClick = () => {
|
||||
setCurrentPanel(currentPanel - 1);
|
||||
resetAddTenantForm();
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", onClick: redirectToTenantDetails },
|
||||
{ type: "delete", onClick: confirmDeleteTenant },
|
||||
@@ -183,14 +201,12 @@ const ListTenants = ({ classes, setErrorSnackMessage }: ITenantsList) => {
|
||||
setIsLoading(true);
|
||||
}, []);
|
||||
|
||||
const createTenant = () => {
|
||||
setCurrentPanel(1);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{createTenantOpen && (
|
||||
<AddTenant
|
||||
open={createTenantOpen}
|
||||
closeModalAndRefresh={closeAddModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
<Fragment>
|
||||
{deleteOpen && (
|
||||
<DeleteTenant
|
||||
deleteOpen={deleteOpen}
|
||||
@@ -208,74 +224,105 @@ const ListTenants = ({ classes, setErrorSnackMessage }: ITenantsList) => {
|
||||
entity="Tenant"
|
||||
/>
|
||||
)}
|
||||
<PageHeader label={"Tenants"} />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Tenants"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setFilterTenants(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="Refresh Tenant List"
|
||||
component="span"
|
||||
onClick={() => {
|
||||
setIsLoading(true);
|
||||
}}
|
||||
>
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
component={Link}
|
||||
to="/add-tenant"
|
||||
>
|
||||
Create Tenant
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Name", elementKey: "name" },
|
||||
{ label: "Namespace", elementKey: "namespace" },
|
||||
{ label: "Capacity", elementKey: "capacity" },
|
||||
{ label: "# of Pools", elementKey: "pool_count" },
|
||||
{ label: "State", elementKey: "currentState" },
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Tenants"
|
||||
idField="name"
|
||||
/>
|
||||
<div className={classes.settingsOptionsContainer}>
|
||||
<SlideOptions
|
||||
slideOptions={[
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.customTitle}>
|
||||
Tenants List
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} className={classes.tenantsContainer}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Tenants"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setFilterTenants(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="Refresh Tenant List"
|
||||
component="span"
|
||||
onClick={() => {
|
||||
setIsLoading(true);
|
||||
}}
|
||||
>
|
||||
<RefreshIcon />
|
||||
</IconButton>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<CreateIcon />}
|
||||
onClick={createTenant}
|
||||
>
|
||||
Create Tenant
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.tenantsContainer}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Name", elementKey: "name" },
|
||||
{ label: "Namespace", elementKey: "namespace" },
|
||||
{ label: "Capacity", elementKey: "capacity" },
|
||||
{ label: "# of Pools", elementKey: "pool_count" },
|
||||
{ label: "State", elementKey: "currentState" },
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Tenants"
|
||||
idField="name"
|
||||
customPaperHeight={classes.customConfigurationPage}
|
||||
noBackground
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>,
|
||||
<Fragment>
|
||||
<Grid item xs={12} className={classes.backContainer}>
|
||||
<button
|
||||
onClick={backClick}
|
||||
className={classes.backButton}
|
||||
>
|
||||
<BackSettingsIcon />
|
||||
Back To Tenants List
|
||||
</button>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{currentPanel === 1 && (
|
||||
<AddTenant closeAndRefresh={closeAddModalAndRefresh} />
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>,
|
||||
]}
|
||||
currentSlide={currentPanel}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const connector = connect(null, {
|
||||
setErrorSnackMessage,
|
||||
resetAddTenantForm,
|
||||
});
|
||||
|
||||
export default withStyles(styles)(connector(ListTenants));
|
||||
|
||||
59
portal-ui/src/screens/Console/Tenants/TenantsMain.tsx
Normal file
59
portal-ui/src/screens/Console/Tenants/TenantsMain.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import React, { Fragment, useState } from "react";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import { Grid } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import Tabs from "@material-ui/core/Tabs";
|
||||
import ListTenants from "./ListTenants/ListTenants";
|
||||
|
||||
interface IConfigurationMain {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
headerLabel: {
|
||||
fontSize: 22,
|
||||
fontWeight: 600,
|
||||
color: "#000",
|
||||
marginTop: 4,
|
||||
},
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
const TenantsMain = ({ classes }: IConfigurationMain) => {
|
||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader label="Tenants" />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.headerLabel}>
|
||||
Tenants Management
|
||||
</Grid>
|
||||
<Tabs
|
||||
value={selectedTab}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
onChange={(_, newValue: number) => {
|
||||
setSelectedTab(newValue);
|
||||
}}
|
||||
aria-label="tenant-tabs"
|
||||
>
|
||||
<Tab label="Tenants" />
|
||||
</Tabs>
|
||||
<Grid item xs={12}>
|
||||
{selectedTab === 0 && (
|
||||
<Grid item xs={12}>
|
||||
<ListTenants />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(TenantsMain);
|
||||
222
portal-ui/src/screens/Console/Tenants/actions.ts
Normal file
222
portal-ui/src/screens/Console/Tenants/actions.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { Opts } from "./ListTenants/utils";
|
||||
import {
|
||||
ADD_TENANT_SET_ADVANCED_MODE,
|
||||
ADD_TENANT_SET_CURRENT_PAGE,
|
||||
ADD_TENANT_UPDATE_FIELD,
|
||||
ADD_TENANT_SET_PAGE_VALID,
|
||||
ADD_TENANT_SET_STORAGE_CLASSES_LIST,
|
||||
ADD_TENANT_SET_LIMIT_SIZE,
|
||||
ADD_TENANT_ADD_CA_KEYPAIR,
|
||||
ADD_TENANT_DELETE_CA_KEYPAIR,
|
||||
ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR,
|
||||
ADD_TENANT_ADD_MINIO_KEYPAIR,
|
||||
ADD_TENANT_DELETE_MINIO_KEYPAIR,
|
||||
ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR,
|
||||
ADD_TENANT_ADD_CONSOLE_CERT,
|
||||
ADD_TENANT_ENCRYPTION_SERVER_CERT,
|
||||
ADD_TENANT_ENCRYPTION_CLIENT_CERT,
|
||||
ADD_TENANT_ENCRYPTION_VAULT_CERT,
|
||||
ADD_TENANT_ENCRYPTION_VAULT_CA,
|
||||
ADD_TENANT_ENCRYPTION_GEMALTO_CA,
|
||||
ADD_TENANT_RESET_FORM,
|
||||
} from "./types";
|
||||
|
||||
// Basic actions
|
||||
export const setWizardPage = (page: number) => {
|
||||
return {
|
||||
type: ADD_TENANT_SET_CURRENT_PAGE,
|
||||
page,
|
||||
};
|
||||
};
|
||||
|
||||
export const setAdvancedMode = (state: boolean) => {
|
||||
return {
|
||||
type: ADD_TENANT_SET_ADVANCED_MODE,
|
||||
state,
|
||||
};
|
||||
};
|
||||
|
||||
export const updateAddField = (
|
||||
pageName: string,
|
||||
fieldName: string,
|
||||
value: any
|
||||
) => {
|
||||
return {
|
||||
type: ADD_TENANT_UPDATE_FIELD,
|
||||
pageName,
|
||||
field: fieldName,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const isPageValid = (pageName: string, valid: boolean) => {
|
||||
return {
|
||||
type: ADD_TENANT_SET_PAGE_VALID,
|
||||
pageName,
|
||||
valid,
|
||||
};
|
||||
};
|
||||
|
||||
// Name Tenant actions
|
||||
|
||||
export const setStorageClassesList = (storageClasses: Opts[]) => {
|
||||
return {
|
||||
type: ADD_TENANT_SET_STORAGE_CLASSES_LIST,
|
||||
storageClasses,
|
||||
};
|
||||
};
|
||||
|
||||
export const setLimitSize = (limitSize: any) => {
|
||||
return {
|
||||
type: ADD_TENANT_SET_LIMIT_SIZE,
|
||||
limitSize,
|
||||
};
|
||||
};
|
||||
|
||||
// Security actions
|
||||
|
||||
export const addCaCertificate = () => {
|
||||
return {
|
||||
type: ADD_TENANT_ADD_CA_KEYPAIR,
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteCaCertificate = (id: string) => {
|
||||
return {
|
||||
type: ADD_TENANT_DELETE_CA_KEYPAIR,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export const addFileToCaCertificates = (
|
||||
id: string,
|
||||
key: string,
|
||||
fileName: string,
|
||||
value: string
|
||||
) => {
|
||||
return {
|
||||
type: ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR,
|
||||
id,
|
||||
key,
|
||||
fileName,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const addKeyPair = () => {
|
||||
return {
|
||||
type: ADD_TENANT_ADD_MINIO_KEYPAIR,
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteKeyPair = (id: string) => {
|
||||
return {
|
||||
type: ADD_TENANT_DELETE_MINIO_KEYPAIR,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export const addFileToKeyPair = (
|
||||
id: string,
|
||||
key: string,
|
||||
fileName: string,
|
||||
value: string
|
||||
) => {
|
||||
return {
|
||||
type: ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR,
|
||||
id,
|
||||
key,
|
||||
fileName,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const addConsoleCertificate = (
|
||||
key: string,
|
||||
fileName: string,
|
||||
value: string
|
||||
) => {
|
||||
return {
|
||||
type: ADD_TENANT_ADD_CONSOLE_CERT,
|
||||
key,
|
||||
fileName,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const addFileServerCert = (
|
||||
key: string,
|
||||
fileName: string,
|
||||
value: string
|
||||
) => {
|
||||
return {
|
||||
type: ADD_TENANT_ENCRYPTION_SERVER_CERT,
|
||||
key,
|
||||
fileName,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const addFileClientCert = (
|
||||
key: string,
|
||||
fileName: string,
|
||||
value: string
|
||||
) => {
|
||||
return {
|
||||
type: ADD_TENANT_ENCRYPTION_CLIENT_CERT,
|
||||
key,
|
||||
fileName,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const addFileVaultCert = (
|
||||
key: string,
|
||||
fileName: string,
|
||||
value: string
|
||||
) => {
|
||||
return {
|
||||
type: ADD_TENANT_ENCRYPTION_VAULT_CERT,
|
||||
key,
|
||||
fileName,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const addFileVaultCa = (fileName: string, value: string) => {
|
||||
return {
|
||||
type: ADD_TENANT_ENCRYPTION_VAULT_CA,
|
||||
fileName,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const addFileGemaltoCa = (fileName: string, value: string) => {
|
||||
return {
|
||||
type: ADD_TENANT_ENCRYPTION_GEMALTO_CA,
|
||||
fileName,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export const resetAddTenantForm = () => {
|
||||
return {
|
||||
type: ADD_TENANT_RESET_FORM,
|
||||
};
|
||||
};
|
||||
592
portal-ui/src/screens/Console/Tenants/reducer.ts
Normal file
592
portal-ui/src/screens/Console/Tenants/reducer.ts
Normal file
@@ -0,0 +1,592 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import has from "lodash/has";
|
||||
import get from "lodash/get";
|
||||
import {
|
||||
TenantsManagementTypes,
|
||||
ITenantState,
|
||||
ADD_TENANT_SET_CURRENT_PAGE,
|
||||
ADD_TENANT_SET_ADVANCED_MODE,
|
||||
ADD_TENANT_UPDATE_FIELD,
|
||||
ADD_TENANT_SET_PAGE_VALID,
|
||||
ADD_TENANT_SET_STORAGE_CLASSES_LIST,
|
||||
ADD_TENANT_ADD_MINIO_KEYPAIR,
|
||||
ADD_TENANT_DELETE_MINIO_KEYPAIR,
|
||||
ADD_TENANT_ADD_CA_KEYPAIR,
|
||||
ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR,
|
||||
ADD_TENANT_DELETE_CA_KEYPAIR,
|
||||
ADD_TENANT_ADD_CONSOLE_CERT,
|
||||
ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR,
|
||||
ADD_TENANT_ENCRYPTION_SERVER_CERT,
|
||||
ADD_TENANT_ENCRYPTION_CLIENT_CERT,
|
||||
ADD_TENANT_ENCRYPTION_VAULT_CERT,
|
||||
ADD_TENANT_ENCRYPTION_VAULT_CA,
|
||||
ADD_TENANT_ENCRYPTION_GEMALTO_CA,
|
||||
ADD_TENANT_RESET_FORM,
|
||||
} from "./types";
|
||||
import { KeyPair } from "./ListTenants/utils";
|
||||
|
||||
const initialState: ITenantState = {
|
||||
createTenant: {
|
||||
page: 0,
|
||||
validPages: [],
|
||||
advancedModeOn: false,
|
||||
storageClasses: [],
|
||||
limitSize: {},
|
||||
fields: {
|
||||
nameTenant: {
|
||||
tenantName: "",
|
||||
namespace: "",
|
||||
selectedStorageClass: "",
|
||||
},
|
||||
configure: {
|
||||
customImage: false,
|
||||
imageName: "",
|
||||
consoleImage: "",
|
||||
customDockerhub: false,
|
||||
imageRegistry: "",
|
||||
imageRegistryUsername: "",
|
||||
imageRegistryPassword: "",
|
||||
exposeMinIO: true,
|
||||
exposeConsole: true,
|
||||
},
|
||||
identityProvider: {
|
||||
idpSelection: "Built-in",
|
||||
openIDURL: "",
|
||||
openIDClientID: "",
|
||||
openIDSecretID: "",
|
||||
ADURL: "",
|
||||
ADSkipTLS: false,
|
||||
ADServerInsecure: false,
|
||||
ADUserNameFilter: "",
|
||||
ADGroupBaseDN: "",
|
||||
ADGroupSearchFilter: "",
|
||||
ADNameAttribute: "",
|
||||
},
|
||||
security: {
|
||||
enableAutoCert: true,
|
||||
enableCustomCerts: false,
|
||||
enableTLS: true,
|
||||
},
|
||||
encryption: {
|
||||
enableEncryption: false,
|
||||
encryptionType: "vault",
|
||||
gemaltoEndpoint: "",
|
||||
gemaltoToken: "",
|
||||
gemaltoDomain: "",
|
||||
gemaltoRetry: "0",
|
||||
awsEndpoint: "",
|
||||
awsRegion: "",
|
||||
awsKMSKey: "",
|
||||
awsAccessKey: "",
|
||||
awsSecretKey: "",
|
||||
awsToken: "",
|
||||
vaultEndpoint: "",
|
||||
vaultEngine: "",
|
||||
vaultNamespace: "",
|
||||
vaultPrefix: "",
|
||||
vaultAppRoleEngine: "",
|
||||
vaultId: "",
|
||||
vaultSecret: "",
|
||||
vaultRetry: "0",
|
||||
vaultPing: "0",
|
||||
gcpProjectID: "",
|
||||
gcpEndpoint: "",
|
||||
gcpClientEmail: "",
|
||||
gcpClientID: "",
|
||||
gcpPrivateKeyID: "",
|
||||
gcpPrivateKey: "",
|
||||
enableCustomCertsForKES: false,
|
||||
},
|
||||
tenantSize: {
|
||||
volumeSize: "100",
|
||||
sizeFactor: "Gi",
|
||||
drivesPerServer: "1",
|
||||
nodes: "4",
|
||||
memoryNode: "2",
|
||||
ecParity: "",
|
||||
ecParityChoices: [],
|
||||
cleanECChoices: [],
|
||||
maxAllocableMemo: 0,
|
||||
memorySize: {
|
||||
error: "",
|
||||
limit: 0,
|
||||
request: 0,
|
||||
},
|
||||
distribution: {
|
||||
error: "",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
volumePerDisk: 0,
|
||||
},
|
||||
ecParityCalc: {
|
||||
error: 0,
|
||||
defaultEC: "",
|
||||
erasureCodeSet: 0,
|
||||
maxEC: "",
|
||||
rawCapacity: "0",
|
||||
storageFactors: [],
|
||||
},
|
||||
limitSize: {},
|
||||
},
|
||||
},
|
||||
certificates: {
|
||||
minioCertificates: [
|
||||
{
|
||||
id: Date.now().toString(),
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
],
|
||||
caCertificates: [
|
||||
{
|
||||
id: Date.now().toString(),
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
],
|
||||
consoleCertificate: {
|
||||
id: "console_cert_pair",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
serverCertificate: {
|
||||
id: "encryptionServerCertificate",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
clientCertificate: {
|
||||
id: "encryptionClientCertificate",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
vaultCertificate: {
|
||||
id: "encryptionVaultCertificate",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
vaultCA: {
|
||||
id: "encryptionVaultCA",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
gemaltoCA: {
|
||||
id: "encryptionGemaltoCA",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function tenantsReducer(
|
||||
state = initialState,
|
||||
action: TenantsManagementTypes
|
||||
): ITenantState {
|
||||
let newState: ITenantState = { ...state };
|
||||
|
||||
switch (action.type) {
|
||||
case ADD_TENANT_SET_CURRENT_PAGE:
|
||||
newState.createTenant.page = action.page;
|
||||
|
||||
return { ...newState };
|
||||
case ADD_TENANT_SET_ADVANCED_MODE:
|
||||
newState.createTenant.advancedModeOn = action.state;
|
||||
|
||||
return { ...newState };
|
||||
case ADD_TENANT_UPDATE_FIELD:
|
||||
if (
|
||||
has(newState.createTenant.fields, `${action.pageName}.${action.field}`)
|
||||
) {
|
||||
const originPageNameItems = get(
|
||||
newState.createTenant.fields,
|
||||
`${action.pageName}`,
|
||||
{}
|
||||
);
|
||||
|
||||
let newValue: typeof originPageNameItems = {};
|
||||
newValue[action.field] = action.value;
|
||||
|
||||
const joinValue = { ...originPageNameItems, ...newValue };
|
||||
|
||||
newState.createTenant.fields[action.pageName] = { ...joinValue };
|
||||
|
||||
return { ...newState };
|
||||
}
|
||||
return state;
|
||||
case ADD_TENANT_SET_PAGE_VALID:
|
||||
let originValidPages = state.createTenant.validPages;
|
||||
|
||||
if (action.valid) {
|
||||
if (!originValidPages.includes(action.pageName)) {
|
||||
originValidPages.push(action.pageName);
|
||||
|
||||
newState.createTenant.validPages = [...originValidPages];
|
||||
}
|
||||
} else {
|
||||
const newSetOfPages = originValidPages.filter(
|
||||
(elm) => elm !== action.pageName
|
||||
);
|
||||
|
||||
newState.createTenant.validPages = [...newSetOfPages];
|
||||
}
|
||||
|
||||
return { ...newState };
|
||||
case ADD_TENANT_SET_STORAGE_CLASSES_LIST:
|
||||
const changeCL = {
|
||||
...state,
|
||||
createTenant: {
|
||||
...state.createTenant,
|
||||
storageClasses: action.storageClasses,
|
||||
},
|
||||
};
|
||||
return { ...changeCL };
|
||||
case ADD_TENANT_ADD_MINIO_KEYPAIR:
|
||||
const minioCerts = [
|
||||
...state.createTenant.certificates.minioCertificates,
|
||||
{
|
||||
id: Date.now().toString(),
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
];
|
||||
newState.createTenant.certificates.minioCertificates = [...minioCerts];
|
||||
return { ...newState };
|
||||
case ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR:
|
||||
const minioCertificates =
|
||||
state.createTenant.certificates.minioCertificates;
|
||||
|
||||
const NCertList = minioCertificates.map((item: KeyPair) => {
|
||||
if (item.id === action.id) {
|
||||
return {
|
||||
...item,
|
||||
[action.key]: action.fileName,
|
||||
[`encoded_${action.key}`]: action.value,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
newState.createTenant.certificates.minioCertificates = [...NCertList];
|
||||
return { ...newState };
|
||||
case ADD_TENANT_DELETE_MINIO_KEYPAIR:
|
||||
const minioCertsList = state.createTenant.certificates.minioCertificates;
|
||||
|
||||
if (minioCertsList.length > 1) {
|
||||
const cleanMinioCertsList = minioCertsList.filter(
|
||||
(item: KeyPair) => item.id !== action.id
|
||||
);
|
||||
newState.createTenant.certificates.minioCertificates = [
|
||||
...cleanMinioCertsList,
|
||||
];
|
||||
return { ...newState };
|
||||
}
|
||||
return { ...state };
|
||||
case ADD_TENANT_ADD_CA_KEYPAIR:
|
||||
const CACerts = [
|
||||
...state.createTenant.certificates.caCertificates,
|
||||
{
|
||||
id: Date.now().toString(),
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
];
|
||||
newState.createTenant.certificates.caCertificates = [...CACerts];
|
||||
return { ...newState };
|
||||
case ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR:
|
||||
const caCertificates = state.createTenant.certificates.caCertificates;
|
||||
|
||||
const NACList = caCertificates.map((item: KeyPair) => {
|
||||
if (item.id === action.id) {
|
||||
return {
|
||||
...item,
|
||||
[action.key]: action.fileName,
|
||||
[`encoded_${action.key}`]: action.value,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
newState.createTenant.certificates.caCertificates = [...NACList];
|
||||
return { ...newState };
|
||||
case ADD_TENANT_DELETE_CA_KEYPAIR:
|
||||
const CACertsList = state.createTenant.certificates.minioCertificates;
|
||||
|
||||
if (CACertsList.length > 1) {
|
||||
const cleanMinioCertsList = CACertsList.filter(
|
||||
(item: KeyPair) => item.id !== action.id
|
||||
);
|
||||
newState.createTenant.certificates.caCertificates = [
|
||||
...cleanMinioCertsList,
|
||||
];
|
||||
return { ...newState };
|
||||
}
|
||||
return { ...state };
|
||||
case ADD_TENANT_ADD_CONSOLE_CERT:
|
||||
const consoleCert = state.createTenant.certificates.consoleCertificate;
|
||||
|
||||
newState.createTenant.certificates.consoleCertificate = {
|
||||
...consoleCert,
|
||||
[action.key]: action.fileName,
|
||||
[`encoded_${action.key}`]: action.value,
|
||||
};
|
||||
|
||||
return { ...newState };
|
||||
case ADD_TENANT_ENCRYPTION_SERVER_CERT:
|
||||
const encServerCert = state.createTenant.certificates.serverCertificate;
|
||||
|
||||
newState.createTenant.certificates.serverCertificate = {
|
||||
...encServerCert,
|
||||
[action.key]: action.fileName,
|
||||
[`encoded_${action.key}`]: action.value,
|
||||
};
|
||||
|
||||
return { ...newState };
|
||||
case ADD_TENANT_ENCRYPTION_CLIENT_CERT:
|
||||
const encClientCert = state.createTenant.certificates.clientCertificate;
|
||||
|
||||
newState.createTenant.certificates.clientCertificate = {
|
||||
...encClientCert,
|
||||
[action.key]: action.fileName,
|
||||
[`encoded_${action.key}`]: action.value,
|
||||
};
|
||||
|
||||
return { ...newState };
|
||||
case ADD_TENANT_ENCRYPTION_VAULT_CERT:
|
||||
const encVaultCert = state.createTenant.certificates.vaultCertificate;
|
||||
|
||||
newState.createTenant.certificates.vaultCertificate = {
|
||||
...encVaultCert,
|
||||
[action.key]: action.fileName,
|
||||
[`encoded_${action.key}`]: action.value,
|
||||
};
|
||||
|
||||
return { ...newState };
|
||||
case ADD_TENANT_ENCRYPTION_VAULT_CA:
|
||||
const encVaultCA = state.createTenant.certificates.vaultCA;
|
||||
|
||||
newState.createTenant.certificates.vaultCA = {
|
||||
...encVaultCA,
|
||||
cert: action.fileName,
|
||||
encoded_cert: action.value,
|
||||
};
|
||||
|
||||
return { ...newState };
|
||||
case ADD_TENANT_ENCRYPTION_GEMALTO_CA:
|
||||
const encGemaltoCA = state.createTenant.certificates.gemaltoCA;
|
||||
|
||||
newState.createTenant.certificates.gemaltoCA = {
|
||||
...encGemaltoCA,
|
||||
cert: action.fileName,
|
||||
encoded_cert: action.value,
|
||||
};
|
||||
|
||||
return { ...newState };
|
||||
case ADD_TENANT_RESET_FORM:
|
||||
return {
|
||||
...state,
|
||||
createTenant: {
|
||||
page: 0,
|
||||
validPages: [],
|
||||
advancedModeOn: false,
|
||||
storageClasses: [],
|
||||
limitSize: {},
|
||||
fields: {
|
||||
nameTenant: {
|
||||
tenantName: "",
|
||||
namespace: "",
|
||||
selectedStorageClass: "",
|
||||
},
|
||||
configure: {
|
||||
customImage: false,
|
||||
imageName: "",
|
||||
consoleImage: "",
|
||||
customDockerhub: false,
|
||||
imageRegistry: "",
|
||||
imageRegistryUsername: "",
|
||||
imageRegistryPassword: "",
|
||||
exposeMinIO: true,
|
||||
exposeConsole: true,
|
||||
},
|
||||
identityProvider: {
|
||||
idpSelection: "Built-in",
|
||||
openIDURL: "",
|
||||
openIDClientID: "",
|
||||
openIDSecretID: "",
|
||||
ADURL: "",
|
||||
ADSkipTLS: false,
|
||||
ADServerInsecure: false,
|
||||
ADUserNameFilter: "",
|
||||
ADGroupBaseDN: "",
|
||||
ADGroupSearchFilter: "",
|
||||
ADNameAttribute: "",
|
||||
},
|
||||
security: {
|
||||
enableAutoCert: true,
|
||||
enableCustomCerts: false,
|
||||
enableTLS: true,
|
||||
},
|
||||
encryption: {
|
||||
enableEncryption: false,
|
||||
encryptionType: "vault",
|
||||
gemaltoEndpoint: "",
|
||||
gemaltoToken: "",
|
||||
gemaltoDomain: "",
|
||||
gemaltoRetry: "0",
|
||||
awsEndpoint: "",
|
||||
awsRegion: "",
|
||||
awsKMSKey: "",
|
||||
awsAccessKey: "",
|
||||
awsSecretKey: "",
|
||||
awsToken: "",
|
||||
vaultEndpoint: "",
|
||||
vaultEngine: "",
|
||||
vaultNamespace: "",
|
||||
vaultPrefix: "",
|
||||
vaultAppRoleEngine: "",
|
||||
vaultId: "",
|
||||
vaultSecret: "",
|
||||
vaultRetry: "0",
|
||||
vaultPing: "0",
|
||||
gcpProjectID: "",
|
||||
gcpEndpoint: "",
|
||||
gcpClientEmail: "",
|
||||
gcpClientID: "",
|
||||
gcpPrivateKeyID: "",
|
||||
gcpPrivateKey: "",
|
||||
enableCustomCertsForKES: false,
|
||||
},
|
||||
tenantSize: {
|
||||
volumeSize: "100",
|
||||
sizeFactor: "Gi",
|
||||
drivesPerServer: "1",
|
||||
nodes: "4",
|
||||
memoryNode: "2",
|
||||
ecParity: "",
|
||||
ecParityChoices: [],
|
||||
cleanECChoices: [],
|
||||
maxAllocableMemo: 0,
|
||||
memorySize: {
|
||||
error: "",
|
||||
limit: 0,
|
||||
request: 0,
|
||||
},
|
||||
distribution: {
|
||||
error: "",
|
||||
nodes: 0,
|
||||
persistentVolumes: 0,
|
||||
disks: 0,
|
||||
volumePerDisk: 0,
|
||||
},
|
||||
ecParityCalc: {
|
||||
error: 0,
|
||||
defaultEC: "",
|
||||
erasureCodeSet: 0,
|
||||
maxEC: "",
|
||||
rawCapacity: "0",
|
||||
storageFactors: [],
|
||||
},
|
||||
limitSize: {},
|
||||
},
|
||||
},
|
||||
certificates: {
|
||||
minioCertificates: [
|
||||
{
|
||||
id: Date.now().toString(),
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
],
|
||||
caCertificates: [
|
||||
{
|
||||
id: Date.now().toString(),
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
],
|
||||
consoleCertificate: {
|
||||
id: "console_cert_pair",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
serverCertificate: {
|
||||
id: "encryptionServerCertificate",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
clientCertificate: {
|
||||
id: "encryptionClientCertificate",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
vaultCertificate: {
|
||||
id: "encryptionVaultCertificate",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
vaultCA: {
|
||||
id: "encryptionVaultCA",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
gemaltoCA: {
|
||||
id: "encryptionGemaltoCA",
|
||||
key: "",
|
||||
cert: "",
|
||||
encoded_key: "",
|
||||
encoded_cert: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
306
portal-ui/src/screens/Console/Tenants/types.ts
Normal file
306
portal-ui/src/screens/Console/Tenants/types.ts
Normal file
@@ -0,0 +1,306 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { IErasureCodeCalc } from "../../../common/types";
|
||||
import { IMemorySize } from "./ListTenants/types";
|
||||
import { KeyPair, Opts } from "./ListTenants/utils";
|
||||
|
||||
export const ADD_TENANT_SET_CURRENT_PAGE = "ADD_TENANT/SET_CURRENT_PAGE";
|
||||
export const ADD_TENANT_SET_ADVANCED_MODE = "ADD_TENANT/SET_ADVANCED_MODE";
|
||||
export const ADD_TENANT_UPDATE_FIELD = "ADD_TENANT/UPDATE_FIELD";
|
||||
export const ADD_TENANT_SET_PAGE_VALID = "ADD_TENANT/SET_PAGE_VALID";
|
||||
export const ADD_TENANT_RESET_FORM = "ADD_TENANT/RESET_FORM";
|
||||
|
||||
// Name Tenant
|
||||
export const ADD_TENANT_SET_STORAGE_CLASSES_LIST =
|
||||
"ADD_TENANT/SET_STORAGE_CLASSES_LIST";
|
||||
export const ADD_TENANT_SET_LIMIT_SIZE = "ADD_TENANT/SET_LIMIT_SIZE";
|
||||
|
||||
// Security
|
||||
export const ADD_TENANT_ADD_MINIO_KEYPAIR = "ADD_TENANT/ADD_MINIO_KEYPAIR";
|
||||
export const ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR =
|
||||
"ADD_TENANT/ADD_FILE_MINIO_KEYPAIR";
|
||||
export const ADD_TENANT_DELETE_MINIO_KEYPAIR =
|
||||
"ADD_TENANT/DELETE_MINIO_KEYPAIR";
|
||||
export const ADD_TENANT_ADD_CA_KEYPAIR = "ADD_TENANT/ADD_CA_KEYPAIR";
|
||||
export const ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR =
|
||||
"ADD_TENANT/ADD_FILE_TO_CA_KEYPAIR";
|
||||
export const ADD_TENANT_DELETE_CA_KEYPAIR = "ADD_TENANT/DELETE_CA_KEYPAIR";
|
||||
export const ADD_TENANT_ADD_CONSOLE_CERT = "ADD_TENANT/ADD_CONSOLE_CERT";
|
||||
|
||||
// Encryption
|
||||
export const ADD_TENANT_ENCRYPTION_SERVER_CERT =
|
||||
"ADD_TENANT/ENCRYPTION_SERVER_CERT";
|
||||
export const ADD_TENANT_ENCRYPTION_CLIENT_CERT =
|
||||
"ADD_TENANT/ENCRYPTION_CLIENT_CERT";
|
||||
export const ADD_TENANT_ENCRYPTION_VAULT_CERT =
|
||||
"ADD_TENANT/ENCRYPTION_VAULT_CERT";
|
||||
export const ADD_TENANT_ENCRYPTION_VAULT_CA = "ADD_TENANT/ENCRYPTION_VAULT_CA";
|
||||
export const ADD_TENANT_ENCRYPTION_GEMALTO_CA =
|
||||
"ADD_TENANT/ENCRYPTION_GEMALTO_CA";
|
||||
|
||||
export interface ICreateTenant {
|
||||
page: number;
|
||||
validPages: string[];
|
||||
advancedModeOn: boolean;
|
||||
storageClasses: Opts[];
|
||||
limitSize: any;
|
||||
fields: IFieldStore;
|
||||
certificates: ICertificatesItems;
|
||||
}
|
||||
|
||||
export interface ICertificatesItems {
|
||||
minioCertificates: KeyPair[];
|
||||
caCertificates: KeyPair[];
|
||||
consoleCertificate: KeyPair;
|
||||
serverCertificate: KeyPair;
|
||||
clientCertificate: KeyPair;
|
||||
vaultCertificate: KeyPair;
|
||||
vaultCA: KeyPair;
|
||||
gemaltoCA: KeyPair;
|
||||
}
|
||||
|
||||
export interface IFieldStore {
|
||||
nameTenant: INameTenantFields;
|
||||
configure: IConfigureFields;
|
||||
identityProvider: IIdentityProviderFields;
|
||||
security: ISecurityFields;
|
||||
encryption: IEncryptionFields;
|
||||
tenantSize: ITenantSizeFields;
|
||||
}
|
||||
|
||||
export interface INameTenantFields {
|
||||
tenantName: string;
|
||||
namespace: string;
|
||||
selectedStorageClass: string;
|
||||
}
|
||||
|
||||
export interface IConfigureFields {
|
||||
customImage: boolean;
|
||||
imageName: string;
|
||||
consoleImage: string;
|
||||
customDockerhub: boolean;
|
||||
imageRegistry: string;
|
||||
imageRegistryUsername: string;
|
||||
imageRegistryPassword: string;
|
||||
exposeMinIO: boolean;
|
||||
exposeConsole: boolean;
|
||||
}
|
||||
|
||||
export interface IIdentityProviderFields {
|
||||
idpSelection: string;
|
||||
openIDURL: string;
|
||||
openIDClientID: string;
|
||||
openIDSecretID: string;
|
||||
ADURL: string;
|
||||
ADSkipTLS: boolean;
|
||||
ADServerInsecure: boolean;
|
||||
ADUserNameFilter: string;
|
||||
ADGroupBaseDN: string;
|
||||
ADGroupSearchFilter: string;
|
||||
ADNameAttribute: string;
|
||||
}
|
||||
|
||||
export interface ISecurityFields {
|
||||
enableTLS: boolean;
|
||||
enableAutoCert: boolean;
|
||||
enableCustomCerts: boolean;
|
||||
}
|
||||
|
||||
export interface IEncryptionFields {
|
||||
enableEncryption: boolean;
|
||||
encryptionType: string;
|
||||
gemaltoEndpoint: string;
|
||||
gemaltoToken: string;
|
||||
gemaltoDomain: string;
|
||||
gemaltoRetry: string;
|
||||
awsEndpoint: string;
|
||||
awsRegion: string;
|
||||
awsKMSKey: string;
|
||||
awsAccessKey: string;
|
||||
awsSecretKey: string;
|
||||
awsToken: string;
|
||||
vaultEndpoint: string;
|
||||
vaultEngine: string;
|
||||
vaultNamespace: string;
|
||||
vaultPrefix: string;
|
||||
vaultAppRoleEngine: string;
|
||||
vaultId: string;
|
||||
vaultSecret: string;
|
||||
vaultRetry: string;
|
||||
vaultPing: string;
|
||||
gcpProjectID: string;
|
||||
gcpEndpoint: string;
|
||||
gcpClientEmail: string;
|
||||
gcpClientID: string;
|
||||
gcpPrivateKeyID: string;
|
||||
gcpPrivateKey: string;
|
||||
enableCustomCertsForKES: boolean;
|
||||
}
|
||||
|
||||
export interface ITenantSizeFields {
|
||||
volumeSize: string;
|
||||
sizeFactor: string;
|
||||
drivesPerServer: string;
|
||||
nodes: string;
|
||||
memoryNode: string;
|
||||
ecParity: string;
|
||||
ecParityChoices: Opts[];
|
||||
cleanECChoices: string[];
|
||||
maxAllocableMemo: number;
|
||||
memorySize: IMemorySize;
|
||||
distribution: any;
|
||||
ecParityCalc: IErasureCodeCalc;
|
||||
limitSize: any;
|
||||
}
|
||||
|
||||
export interface ITenantState {
|
||||
createTenant: ICreateTenant;
|
||||
}
|
||||
|
||||
interface SetTenantWizardPage {
|
||||
type: typeof ADD_TENANT_SET_CURRENT_PAGE;
|
||||
page: number;
|
||||
}
|
||||
|
||||
interface SetAdvancedMode {
|
||||
type: typeof ADD_TENANT_SET_ADVANCED_MODE;
|
||||
state: boolean;
|
||||
}
|
||||
|
||||
interface UpdateATField {
|
||||
type: typeof ADD_TENANT_UPDATE_FIELD;
|
||||
pageName: keyof IFieldStore;
|
||||
field: keyof FieldsToHandle;
|
||||
value: any;
|
||||
}
|
||||
|
||||
interface SetPageValid {
|
||||
type: typeof ADD_TENANT_SET_PAGE_VALID;
|
||||
pageName: keyof IFieldStore;
|
||||
valid: boolean;
|
||||
}
|
||||
|
||||
interface SetStorageClassesList {
|
||||
type: typeof ADD_TENANT_SET_STORAGE_CLASSES_LIST;
|
||||
storageClasses: Opts[];
|
||||
}
|
||||
|
||||
interface SetLimitSize {
|
||||
type: typeof ADD_TENANT_SET_LIMIT_SIZE;
|
||||
limitSize: any;
|
||||
}
|
||||
|
||||
interface AddMinioKeyPair {
|
||||
type: typeof ADD_TENANT_ADD_MINIO_KEYPAIR;
|
||||
}
|
||||
|
||||
interface AddFileToMinioKeyPair {
|
||||
type: typeof ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR;
|
||||
id: string;
|
||||
key: string;
|
||||
fileName: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface DeleteMinioKeyPair {
|
||||
type: typeof ADD_TENANT_DELETE_MINIO_KEYPAIR;
|
||||
id: string;
|
||||
}
|
||||
interface AddCAKeyPair {
|
||||
type: typeof ADD_TENANT_ADD_CA_KEYPAIR;
|
||||
}
|
||||
|
||||
interface AddFileToCAKeyPair {
|
||||
type: typeof ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR;
|
||||
id: string;
|
||||
key: string;
|
||||
fileName: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface DeleteCAKeyPair {
|
||||
type: typeof ADD_TENANT_DELETE_CA_KEYPAIR;
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface AddFileConsoleCert {
|
||||
type: typeof ADD_TENANT_ADD_CONSOLE_CERT;
|
||||
key: string;
|
||||
fileName: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
// Encryption Certs
|
||||
interface AddFileServerCert {
|
||||
type: typeof ADD_TENANT_ENCRYPTION_SERVER_CERT;
|
||||
key: string;
|
||||
fileName: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface AddFileClientCert {
|
||||
type: typeof ADD_TENANT_ENCRYPTION_CLIENT_CERT;
|
||||
key: string;
|
||||
fileName: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface AddFileVaultCert {
|
||||
type: typeof ADD_TENANT_ENCRYPTION_VAULT_CERT;
|
||||
key: string;
|
||||
fileName: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface AddFileVaultCa {
|
||||
type: typeof ADD_TENANT_ENCRYPTION_VAULT_CA;
|
||||
fileName: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface AddFileGemaltoCa {
|
||||
type: typeof ADD_TENANT_ENCRYPTION_GEMALTO_CA;
|
||||
fileName: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface ResetForm {
|
||||
type: typeof ADD_TENANT_RESET_FORM;
|
||||
}
|
||||
|
||||
export type FieldsToHandle = INameTenantFields;
|
||||
|
||||
export type TenantsManagementTypes =
|
||||
| SetTenantWizardPage
|
||||
| SetAdvancedMode
|
||||
| UpdateATField
|
||||
| SetPageValid
|
||||
| SetStorageClassesList
|
||||
| SetLimitSize
|
||||
| AddMinioKeyPair
|
||||
| DeleteMinioKeyPair
|
||||
| AddCAKeyPair
|
||||
| DeleteCAKeyPair
|
||||
| AddFileConsoleCert
|
||||
| AddFileToMinioKeyPair
|
||||
| AddFileToCAKeyPair
|
||||
| AddFileServerCert
|
||||
| AddFileClientCert
|
||||
| AddFileVaultCert
|
||||
| AddFileVaultCa
|
||||
| AddFileGemaltoCa
|
||||
| ResetForm;
|
||||
25
portal-ui/src/screens/Console/Tenants/utils.ts
Normal file
25
portal-ui/src/screens/Console/Tenants/utils.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export const clearValidationError = (
|
||||
validationErrors: any,
|
||||
fieldKey: string
|
||||
) => {
|
||||
const newValidationElement = { ...validationErrors };
|
||||
delete newValidationElement[fieldKey];
|
||||
|
||||
return newValidationElement;
|
||||
};
|
||||
@@ -24,6 +24,7 @@ import { watchReducer } from "./screens/Console/Watch/reducers";
|
||||
import { consoleReducer } from "./screens/Console/reducer";
|
||||
import { bucketsReducer } from "./screens/Console/Buckets/reducers";
|
||||
import { objectBrowserReducer } from "./screens/Console/ObjectBrowser/reducers";
|
||||
import { tenantsReducer } from "./screens/Console/Tenants/reducer";
|
||||
|
||||
const globalReducer = combineReducers({
|
||||
system: systemReducer,
|
||||
@@ -34,6 +35,7 @@ const globalReducer = combineReducers({
|
||||
buckets: bucketsReducer,
|
||||
objectBrowser: objectBrowserReducer,
|
||||
healthInfo: healthInfoReducer,
|
||||
tenants: tenantsReducer,
|
||||
});
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -887,7 +887,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
|
||||
}
|
||||
// default activate lgo search and prometheus
|
||||
minInst.Spec.Log = &miniov2.LogConfig{
|
||||
Image: "miniodev/logsearch:v4.0.0",
|
||||
Image: "minio/logsearch:v4.0.0",
|
||||
Audit: &miniov2.AuditConfig{DiskCapacityGB: swag.Int(10)},
|
||||
}
|
||||
minInst.Spec.Prometheus = &miniov2.PrometheusConfig{
|
||||
|
||||
@@ -1028,7 +1028,7 @@ func Test_UpdateTenantAction(t *testing.T) {
|
||||
},
|
||||
params: admin_api.UpdateTenantParams{
|
||||
Body: &models.UpdateTenantRequest{
|
||||
ConsoleImage: "minio/console:v0.6.0",
|
||||
ConsoleImage: "minio/console:v0.6.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -63,7 +63,7 @@ const (
|
||||
// Image versions
|
||||
const (
|
||||
KESImageVersion = "minio/kes:v0.13.4"
|
||||
ConsoleImageDefaultVersion = "minio/console:v0.6.0"
|
||||
ConsoleImageDefaultVersion = "minio/console:v0.6.3"
|
||||
)
|
||||
|
||||
// K8s
|
||||
|
||||
@@ -58,7 +58,9 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
|
||||
// createServiceAccount adds a service account to the userClient and assigns a policy to him if defined.
|
||||
func createServiceAccount(ctx context.Context, userClient MinioAdmin, policy string) (*models.ServiceAccountCreds, error) {
|
||||
iamPolicy := &iampolicy.Policy{}
|
||||
// By default a nil policy will be used so the service account inherit the parent account policy, otherwise
|
||||
// we override with the user provided iam policy
|
||||
var iamPolicy *iampolicy.Policy
|
||||
if strings.TrimSpace(policy) != "" {
|
||||
iamp, err := iampolicy.ParseConfig(bytes.NewReader([]byte(policy)))
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user