Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbb6c81986 | ||
|
|
9e843f4ba0 | ||
|
|
4e14ec2742 | ||
|
|
69ccf4872e | ||
|
|
1c31aff147 | ||
|
|
608a5c3787 | ||
|
|
297c980a8d | ||
|
|
4091b11f99 | ||
|
|
4718380bd2 | ||
|
|
52ea19809e | ||
|
|
281bd78104 | ||
|
|
d84e744d05 | ||
|
|
1b5bcad3d8 | ||
|
|
bfc4b138ff | ||
|
|
5a0ec11199 | ||
|
|
c52ba1f859 | ||
|
|
d62235ee58 | ||
|
|
e5d2752436 | ||
|
|
cffaee84bb | ||
|
|
96b1d4fe85 | ||
|
|
02acb76ac9 | ||
|
|
038f542895 | ||
|
|
983e175bc9 | ||
|
|
71ac8d7001 | ||
|
|
5258ac3d1a | ||
|
|
27d1627c8f | ||
|
|
780cf7240b | ||
|
|
ce989e39ab | ||
|
|
63d3c7207d | ||
|
|
eb924ec842 | ||
|
|
f826453284 | ||
|
|
1e00111b00 | ||
|
|
06e1592b54 | ||
|
|
41e0c1e39b | ||
|
|
31f63a387e | ||
|
|
4a1ccf19a0 | ||
|
|
d8b387434b | ||
|
|
2e9a42320c | ||
|
|
1d92f90cbe | ||
|
|
df728fc8e6 | ||
|
|
834e3fb996 | ||
|
|
0b7d4a2c35 | ||
|
|
e13626e92b | ||
|
|
0286010053 | ||
|
|
382e315668 | ||
|
|
5f5c00adb5 | ||
|
|
e093efa931 | ||
|
|
c129eae6a7 | ||
|
|
5b6e5786ea | ||
|
|
8a8471e49a | ||
|
|
719866a574 | ||
|
|
95ebc3bedf | ||
|
|
a8747614bf | ||
|
|
c89f5a7003 | ||
|
|
ebcebfbe5f | ||
|
|
c82782fe9f | ||
|
|
e626f59feb | ||
|
|
cc8d5abcd6 | ||
|
|
83a4c351dd | ||
|
|
4c0c46f5a8 | ||
|
|
1842caff0f | ||
|
|
80d3e8cdb8 | ||
|
|
d936d61b20 | ||
|
|
4a10a81374 | ||
|
|
6404a1b984 | ||
|
|
a3dc145738 | ||
|
|
dbd1b8781a | ||
|
|
9e7874cc04 | ||
|
|
d1d3c96777 | ||
|
|
2d975eb6c9 | ||
|
|
be054fe4ce | ||
|
|
f27902ab92 | ||
|
|
7e43719e20 | ||
|
|
f6016c2769 | ||
|
|
0ba60bd674 | ||
|
|
41b34645f9 | ||
|
|
ceff2840d8 | ||
|
|
6541938f16 | ||
|
|
276eff4f15 | ||
|
|
a3c9d0fe59 | ||
|
|
14f032971b | ||
|
|
9b9c54f775 |
6
.github/workflows/permissions.yml
vendored
6
.github/workflows/permissions.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: "Front-End Permission Tests"
|
||||
name: "UI"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
permissions:
|
||||
name: Front-End Permission Tests
|
||||
name: Permissions Tests
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
- name: Run TestCafe Tests
|
||||
uses: DevExpress/testcafe-action@latest
|
||||
with:
|
||||
args: '"chrome:headless" portal-ui/tests/permissions/ --skip-js-errors '
|
||||
args: '"chrome:headless" portal-ui/tests/permissions/ --skip-js-errors -c 3'
|
||||
|
||||
- name: Clean up users & policies
|
||||
run: |
|
||||
|
||||
@@ -19,9 +19,17 @@ linters:
|
||||
- unparam
|
||||
- unused
|
||||
- structcheck
|
||||
- goheader
|
||||
|
||||
linters-settings:
|
||||
goheader:
|
||||
values:
|
||||
regexp:
|
||||
copyright-holder: Copyright \(c\) (20\d\d\-20\d\d)|2021|({{year}})
|
||||
template-path: .license.tmpl
|
||||
|
||||
service:
|
||||
golangci-lint-version: 1.27.0 # use the fixed version to not introduce new linters unexpectedly
|
||||
golangci-lint-version: 1.43.0 # use the fixed version to not introduce new linters unexpectedly
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
@@ -36,3 +44,4 @@ issues:
|
||||
run:
|
||||
skip-dirs:
|
||||
- pkg/clientgen
|
||||
- pkg/apis/networking.gke.io
|
||||
|
||||
@@ -13,7 +13,7 @@ release:
|
||||
before:
|
||||
hooks:
|
||||
# you may remove this if you don't use vgo
|
||||
- go mod tidy
|
||||
- go mod tidy -compat=1.17
|
||||
|
||||
builds:
|
||||
-
|
||||
|
||||
15
.license.tmpl
Normal file
15
.license.tmpl
Normal file
@@ -0,0 +1,15 @@
|
||||
This file is part of MinIO Console Server
|
||||
{{copyright-holder}} 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/>.
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:14 as uilayer
|
||||
FROM node:17 as uilayer
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -12,7 +12,7 @@ RUN make build-static
|
||||
|
||||
USER node
|
||||
|
||||
FROM golang:1.16 as golayer
|
||||
FROM golang:1.17 as golayer
|
||||
|
||||
RUN apt-get update -y && apt-get install -y ca-certificates
|
||||
|
||||
@@ -31,7 +31,7 @@ ENV CGO_ENABLED=0
|
||||
COPY --from=uilayer /app/build /go/src/github.com/minio/console/portal-ui/build
|
||||
RUN go build -ldflags "-w -s" -a -o console ./cmd/console
|
||||
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5
|
||||
MAINTAINER MinIO Development "dev@min.io"
|
||||
EXPOSE 9090
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:14 as uilayer
|
||||
FROM node:17 as uilayer
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
|
||||
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.5
|
||||
|
||||
ARG TAG
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -20,7 +20,7 @@ k8sdev:
|
||||
|
||||
getdeps:
|
||||
@mkdir -p ${GOPATH}/bin
|
||||
@which golangci-lint 1>/dev/null || (echo "Installing golangci-lint" && curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOPATH)/bin v1.40.1)
|
||||
@which golangci-lint 1>/dev/null || (echo "Installing golangci-lint" && curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOPATH)/bin v1.43.0)
|
||||
|
||||
verifiers: getdeps fmt lint
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
@@ -47,7 +47,7 @@ func (c *HTTPClient) Post(url, contentType string, body io.Reader) (resp *http.R
|
||||
return c.Client.Post(url, contentType, body)
|
||||
}
|
||||
|
||||
// Do implements http.Client.Do()
|
||||
// Do implement http.Client.Do()
|
||||
func (c *HTTPClient) Do(req *http.Request) (*http.Response, error) {
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
//go:build !operator
|
||||
// +build !operator
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
@@ -17,6 +14,9 @@
|
||||
// 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/>.
|
||||
|
||||
//go:build !operator
|
||||
// +build !operator
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
|
||||
@@ -32,22 +32,22 @@ import (
|
||||
|
||||
// Help template for Console.
|
||||
var consoleHelpTemplate = `NAME:
|
||||
{{.Name}} - {{.Usage}}
|
||||
{{.Name}} - {{.Usage}}
|
||||
|
||||
DESCRIPTION:
|
||||
{{.Description}}
|
||||
{{.Description}}
|
||||
|
||||
USAGE:
|
||||
{{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}}{{end}} [ARGS...]
|
||||
{{.HelpName}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}}{{end}} [ARGS...]
|
||||
|
||||
COMMANDS:
|
||||
{{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||
{{end}}{{if .VisibleFlags}}
|
||||
{{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
|
||||
{{end}}{{if .VisibleFlags}}
|
||||
FLAGS:
|
||||
{{range .VisibleFlags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
{{range .VisibleFlags}}{{.}}
|
||||
{{end}}{{end}}
|
||||
VERSION:
|
||||
{{.Version}}
|
||||
{{.Version}}
|
||||
`
|
||||
|
||||
func newApp(name string) *cli.App {
|
||||
|
||||
@@ -225,30 +225,5 @@ func startOperatorServer(ctx *cli.Context) error {
|
||||
|
||||
defer server.Shutdown()
|
||||
|
||||
// subnet license refresh process
|
||||
go func() {
|
||||
// start refreshing subnet license after 5 seconds..
|
||||
time.Sleep(time.Second * 5)
|
||||
|
||||
failedAttempts := 0
|
||||
for {
|
||||
if err := operatorapi.RefreshLicense(); err != nil {
|
||||
operatorapi.LogError("Refreshing subnet license failed: %v", err)
|
||||
failedAttempts++
|
||||
// end license refresh after 3 consecutive failed attempts
|
||||
if failedAttempts >= 3 {
|
||||
return
|
||||
}
|
||||
// wait 5 minutes and retry again
|
||||
time.Sleep(time.Minute * 5)
|
||||
continue
|
||||
}
|
||||
// if license refreshed successfully reset the counter
|
||||
failedAttempts = 0
|
||||
// try to refresh license every 24 hrs
|
||||
time.Sleep(time.Hour * 24)
|
||||
}
|
||||
}()
|
||||
|
||||
return server.Serve()
|
||||
}
|
||||
|
||||
63
go.mod
63
go.mod
@@ -6,9 +6,9 @@ require (
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/cheggaaa/pb/v3 v3.0.6
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/go-openapi/errors v0.20.1
|
||||
github.com/go-openapi/errors v0.20.2
|
||||
github.com/go-openapi/loads v0.21.0
|
||||
github.com/go-openapi/runtime v0.21.0
|
||||
github.com/go-openapi/runtime v0.21.1
|
||||
github.com/go-openapi/spec v0.20.4
|
||||
github.com/go-openapi/strfmt v0.21.1
|
||||
github.com/go-openapi/swag v0.19.15
|
||||
@@ -20,8 +20,8 @@ require (
|
||||
github.com/minio/cli v1.22.0
|
||||
github.com/minio/direct-csi v1.3.5-0.20210601185811-f7776f7961bf
|
||||
github.com/minio/kes v0.11.0
|
||||
github.com/minio/madmin-go v1.1.23
|
||||
github.com/minio/mc v0.0.0-20211207230606-23a05f5a17f2
|
||||
github.com/minio/madmin-go v1.3.0
|
||||
github.com/minio/mc v0.0.0-20220204044644-e048c85d71a7
|
||||
github.com/minio/minio-go/v7 v7.0.21
|
||||
github.com/minio/operator v0.0.0-20220110040724-a5d59a342b7f
|
||||
github.com/minio/pkg v1.1.14
|
||||
@@ -30,9 +30,10 @@ require (
|
||||
github.com/rs/xid v1.3.0
|
||||
github.com/secure-io/sio-go v0.3.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tidwall/gjson v1.12.1
|
||||
github.com/unrolled/secure v1.0.9
|
||||
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
||||
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.21.1
|
||||
@@ -43,17 +44,19 @@ require (
|
||||
require (
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/VividCortex/ewma v1.1.1 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/briandowns/spinner v1.16.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/charmbracelet/bubbles v0.10.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.19.3 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.4.1-0.20220204041308-bf2912e703f6 // indirect
|
||||
github.com/cheggaaa/pb v1.0.29 // indirect
|
||||
github.com/containerd/console v1.0.2 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible // indirect
|
||||
@@ -61,17 +64,18 @@ require (
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/go-logr/logr v0.4.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/analysis v0.20.1 // indirect
|
||||
github.com/go-openapi/analysis v0.21.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/goccy/go-json v0.7.9 // indirect
|
||||
github.com/goccy/go-json v0.8.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gnostic v0.5.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
@@ -85,10 +89,12 @@ require (
|
||||
github.com/lestrrat-go/blackmagic v1.0.0 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.0 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.1 // indirect
|
||||
github.com/lestrrat-go/jwx v1.2.7 // indirect
|
||||
github.com/lestrrat-go/jwx v1.2.14 // indirect
|
||||
github.com/lestrrat-go/option v1.0.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
@@ -102,46 +108,51 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/profile v1.6.0 // indirect
|
||||
github.com/pkg/xattr v0.4.3 // indirect
|
||||
github.com/pkg/xattr v0.4.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/posener/complete v1.2.3 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/prometheus/client_golang v1.11.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.31.1 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rjeczalik/notify v0.9.2 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.21.8 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.21.12 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/tidwall/gjson v1.10.2 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
||||
github.com/tklauser/numcpus v0.3.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.1 // indirect
|
||||
go.mongodb.org/mongo-driver v1.7.5 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 // indirect
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||
google.golang.org/appengine v1.6.6 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210928142010-c7af6a1a74c9 // indirect
|
||||
google.golang.org/grpc v1.41.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect
|
||||
google.golang.org/grpc v1.43.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/h2non/filetype.v1 v1.0.5 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.63.2 // indirect
|
||||
gopkg.in/ini.v1 v1.66.3 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
k8s.io/klog/v2 v2.8.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 // indirect
|
||||
|
||||
15
hack/header.go.txt
Normal file
15
hack/header.go.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
// 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/>.
|
||||
@@ -1,18 +1,4 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
@@ -20,10 +6,7 @@ set -o pipefail
|
||||
|
||||
SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/..
|
||||
|
||||
go get -d k8s.io/code-generator/...
|
||||
|
||||
# Checkout code-generator to compatible version
|
||||
#(cd $GOPATH/src/k8s.io/code-generator && git checkout origin/release-1.14 -B release-1.14)
|
||||
GO111MODULE=off go get -d k8s.io/code-generator/...
|
||||
|
||||
REPOSITORY=github.com/minio/console
|
||||
$GOPATH/src/k8s.io/code-generator/generate-groups.sh all \
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
168
integration/service_account_test.go
Normal file
168
integration/service_account_test.go
Normal file
@@ -0,0 +1,168 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 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 integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAddServiceAccount(t *testing.T) {
|
||||
/*
|
||||
This is an atomic API Test to add a user service account, the intention
|
||||
is simple, add a user and make sure the response is 201 meaning that the
|
||||
user got added successfully.
|
||||
After test completion, it is expected that user is removed, so other
|
||||
tests like users.ts can run over clean data and we don't collide against
|
||||
it.
|
||||
*/
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
// Add service account
|
||||
fmt.Println(".......................TestServiceAccountPolicy(): Create Data to add user")
|
||||
requestDataAddServiceAccount := map[string]interface{}{
|
||||
"accessKey": "testuser1",
|
||||
"secretKey": "password",
|
||||
"policy": "{" +
|
||||
"\n \"Version\": \"2012-10-17\"," +
|
||||
"\n \"Statement\": [" +
|
||||
"\n {" +
|
||||
"\n \"Effect\": \"Allow\"," +
|
||||
"\n \"Action\": [" +
|
||||
"\n \"s3:GetBucketLocation\"," +
|
||||
"\n \"s3:GetObject\"" +
|
||||
"\n ]," +
|
||||
"\n \"Resource\": [" +
|
||||
"\n \"arn:aws:s3:::*\"" +
|
||||
"\n ]" +
|
||||
"\n }" +
|
||||
"\n ]" +
|
||||
"\n}",
|
||||
}
|
||||
|
||||
fmt.Println("..............................TestServiceAccountPolicy(): Prepare the POST")
|
||||
requestDataJSON, _ := json.Marshal(requestDataAddServiceAccount)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"POST", "http://localhost:9090/api/v1/service-account-credentials", requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
fmt.Println(".................................TestServiceAccountPolicy(): Make the POST")
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("..................................TestServiceAccountPolicy(): Verification")
|
||||
fmt.Println(".................................TestServiceAccountPolicy(): POST response")
|
||||
fmt.Println(response)
|
||||
fmt.Println("....................................TestServiceAccountPolicy(): POST error")
|
||||
fmt.Println(err)
|
||||
if response != nil {
|
||||
fmt.Println("POST StatusCode:", response.StatusCode)
|
||||
assert.Equal(201, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
fmt.Println("...................................TestServiceAccountPolicy(): Remove user")
|
||||
|
||||
// Test policy
|
||||
fmt.Println(".......................TestAddUserServiceAccount(): Create Data to add user")
|
||||
request, err = http.NewRequest(
|
||||
"GET", "http://localhost:9090/api/v1/service-accounts/testuser1/policy", nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
fmt.Println(".................................TestAddServiceAccount(): Make the POST")
|
||||
response, err = client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("..................................TestAddServiceAccount(): Verification")
|
||||
fmt.Println(".................................TestAddServiceAccount(): POST response")
|
||||
fmt.Println(response)
|
||||
fmt.Println("....................................TestAddServiceAccount(): POST error")
|
||||
fmt.Println(err)
|
||||
if response != nil {
|
||||
fmt.Println("POST StatusCode:", response.StatusCode)
|
||||
assert.Equal(200, response.StatusCode, "Status Code is incorrect")
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(response.Body)
|
||||
var actual *iampolicy.Policy
|
||||
var expected *iampolicy.Policy
|
||||
json.Unmarshal(buf.Bytes(), actual)
|
||||
policy, err := json.Marshal(requestDataAddServiceAccount["policy"])
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
json.Unmarshal(policy, expected)
|
||||
assert.Equal(expected, actual)
|
||||
}
|
||||
|
||||
fmt.Println("...................................TestServiceAccountPolicy(): Remove service account")
|
||||
|
||||
// {{baseUrl}}/user?name=proident velit
|
||||
// Investiga como se borra en el browser.
|
||||
request, err = http.NewRequest(
|
||||
"DELETE", "http://localhost:9090/api/v1/service-accounts/testuser1", nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
fmt.Println("...............................TestServiceAccountPolicy(): Make the DELETE")
|
||||
response, err = client.Do(request)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("..................................TestServiceAccountPolicy(): Verification")
|
||||
fmt.Println("...............................TestServiceAccountPolicy(): DELETE response")
|
||||
fmt.Println(response)
|
||||
fmt.Println("..................................TestServiceAccountPolicy(): DELETE error")
|
||||
fmt.Println(err)
|
||||
if response != nil {
|
||||
fmt.Println("DELETE StatusCode:", response.StatusCode)
|
||||
assert.Equal(204, response.StatusCode, "has to be 204 when delete user")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,39 +20,32 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAddUser(t *testing.T) {
|
||||
func AddUser(accessKey string, secretKey string, groups []string, policies []string) (*http.Response, error) {
|
||||
/*
|
||||
This is an atomic API Test to add a user via api/v1/users, the intention
|
||||
is simple, add a user and make sure the response is 201 meaning that the
|
||||
user got added successfully.
|
||||
After test completion, it is expected that user is removed, so other
|
||||
tests like users.ts can run over clean data and we don't collide against
|
||||
it.
|
||||
This is an atomic function to add user and can be reused across
|
||||
different functions.
|
||||
*/
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
var x [0]string
|
||||
fmt.Println(x)
|
||||
|
||||
fmt.Println(".......................TestAddUser(): Create Data to add user")
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"accessKey": "accessKey",
|
||||
"secretKey": "secretKey",
|
||||
"groups": x,
|
||||
"policies": x,
|
||||
"accessKey": accessKey,
|
||||
"secretKey": secretKey,
|
||||
"groups": groups,
|
||||
"policies": policies,
|
||||
}
|
||||
|
||||
fmt.Println("..............................TestAddUser(): Prepare the POST")
|
||||
@@ -62,13 +55,302 @@ func TestAddUser(t *testing.T) {
|
||||
"POST", "http://localhost:9090/api/v1/users", requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
fmt.Println(".................................TestAddUser(): Make the POST")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func DeleteUser(userName string) (*http.Response, error) {
|
||||
/*
|
||||
This is an atomic function to delete user and can be reused across
|
||||
different functions.
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
fmt.Println("...................................TestAddUser(): Remove user")
|
||||
request, err := http.NewRequest(
|
||||
"DELETE", "http://localhost:9090/api/v1/user?name="+userName, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
fmt.Println("...............................TestAddUser(): Make the DELETE")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func ListUsers(offset string, limit string) (*http.Response, error) {
|
||||
/*
|
||||
This is an atomic function to list users.
|
||||
{{baseUrl}}/users?offset=-5480083&limit=-5480083
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
fmt.Println("...................................TestAddUser(): Remove user")
|
||||
request, err := http.NewRequest(
|
||||
"GET",
|
||||
"http://localhost:9090/api/v1/users?offset="+offset+"&limit="+limit,
|
||||
nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
fmt.Println("...............................TestAddUser(): Make the DELETE")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func GetUserInformation(userName string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to get user information via API:
|
||||
{{baseUrl}}/user?name=proident velit
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
request, err := http.NewRequest(
|
||||
"GET",
|
||||
"http://localhost:9090/api/v1/user?name="+userName,
|
||||
nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func UpdateUserInformation(name string, status string, groups []string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to update user information:
|
||||
PUT: {{baseUrl}}/user?name=proident velit
|
||||
Body:
|
||||
{
|
||||
"status": "nisi voluptate amet ea",
|
||||
"groups": [
|
||||
"ipsum eu cupidatat",
|
||||
"aliquip non nulla"
|
||||
]
|
||||
}
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"status": status,
|
||||
"groups": groups,
|
||||
}
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"PUT", "http://localhost:9090/api/v1/user?name="+name, requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func RemoveUser(name string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to remove user.
|
||||
DELETE: {{baseUrl}}/user?name=proident velit
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
request, err := http.NewRequest(
|
||||
"DELETE", "http://localhost:9090/api/v1/user?name="+name, nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func UpdateGroupsForAUser(userName string, groups []string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to update groups for a user
|
||||
PUT: {{baseUrl}}/user/groups?name=username
|
||||
{
|
||||
"groups":[
|
||||
"groupone",
|
||||
"grouptwo"
|
||||
]
|
||||
}
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"groups": groups,
|
||||
}
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"PUT",
|
||||
"http://localhost:9090/api/v1/user/groups?name="+userName,
|
||||
requestDataBody,
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func CreateServiceAccountForUser(userName string, policy string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to Create Service Account for user
|
||||
POST: api/v1/user/username/service-accounts
|
||||
{
|
||||
"policy": "ad magna"
|
||||
}
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"policy": policy,
|
||||
}
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"POST",
|
||||
"http://localhost:9090/api/v1/user/"+userName+"/service-accounts",
|
||||
requestDataBody,
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func ReturnsAListOfServiceAccountsForAUser(userName string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to return a list of service accounts for a user.
|
||||
GET: {{baseUrl}}/user/:name/service-accounts
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
request, err := http.NewRequest(
|
||||
"GET",
|
||||
"http://localhost:9090/api/v1/user/"+userName+"/service-accounts",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func AddGroup(group string, members []string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to add a group.
|
||||
*/
|
||||
client := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"group": group,
|
||||
"members": members,
|
||||
}
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"POST",
|
||||
"http://localhost:9090/api/v1/groups",
|
||||
requestDataBody,
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func UsersGroupsBulk(users []string, groups []string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to test Bulk functionality to Add Users to Groups.
|
||||
PUT: {{baseUrl}}/users-groups-bulk
|
||||
{
|
||||
"users": [
|
||||
"magna id",
|
||||
"enim sit tempor incididunt"
|
||||
],
|
||||
"groups": [
|
||||
"nisi est esse",
|
||||
"fugiat eu"
|
||||
]
|
||||
}
|
||||
*/
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"users": users,
|
||||
"groups": groups,
|
||||
}
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"PUT",
|
||||
"http://localhost:9090/api/v1/users-groups-bulk",
|
||||
requestDataBody,
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
client := &http.Client{
|
||||
Timeout: 2 * time.Second,
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func TestAddUser(t *testing.T) {
|
||||
/*
|
||||
This is an API Test to add a user via api/v1/users, the intention
|
||||
is simple, add a user and make sure the response is 201 meaning that the
|
||||
user got added successfully.
|
||||
After test completion, it is expected that user is removed, so other
|
||||
tests like users.ts can run over clean data and we don't collide against
|
||||
it.
|
||||
*/
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
// With no groups & no policies
|
||||
var groups = []string{}
|
||||
var policies = []string{}
|
||||
|
||||
fmt.Println(".................................TestAddUser(): Make the POST")
|
||||
response, err := AddUser("accessKey", "secretKey", groups, policies)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
@@ -83,20 +365,7 @@ func TestAddUser(t *testing.T) {
|
||||
assert.Equal(201, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
fmt.Println("...................................TestAddUser(): Remove user")
|
||||
// {{baseUrl}}/user?name=proident velit
|
||||
// Investiga como se borra en el browser.
|
||||
request, err = http.NewRequest(
|
||||
"DELETE", "http://localhost:9090/api/v1/user?name=accessKey", nil)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
fmt.Println("...............................TestAddUser(): Make the DELETE")
|
||||
response, err = client.Do(request)
|
||||
response, err = DeleteUser("accessKey")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
@@ -112,3 +381,476 @@ func TestAddUser(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestListUsers(t *testing.T) {
|
||||
/*
|
||||
This test is intended to list users via API.
|
||||
1. First, it creates the users
|
||||
2. Then, it lists the users <------ 200 is expected when listing them.
|
||||
3. Finally, it deletes the users
|
||||
*/
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
// With no groups & no policies
|
||||
var groups = []string{}
|
||||
var policies = []string{}
|
||||
|
||||
// 1. Create the users
|
||||
numberOfUsers := 5
|
||||
for i := 1; i < numberOfUsers; i++ {
|
||||
fmt.Println("............................TestListUsers(): Adding users")
|
||||
fmt.Println(strconv.Itoa(i) + "accessKey" + strconv.Itoa(i))
|
||||
response, err := AddUser(
|
||||
strconv.Itoa(i)+"accessKey"+strconv.Itoa(i),
|
||||
"secretKey"+strconv.Itoa(i), groups, policies)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("............................TestListUsers(): Verification")
|
||||
fmt.Println("...........................TestListUsers(): POST response")
|
||||
fmt.Println(response)
|
||||
fmt.Println("..............................TestListUsers(): POST error")
|
||||
fmt.Println(err)
|
||||
if response != nil {
|
||||
fmt.Println("POST StatusCode:", response.StatusCode)
|
||||
assert.Equal(201, response.StatusCode,
|
||||
"Status Code is incorrect on index: "+strconv.Itoa(i))
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
|
||||
// 2. List the users
|
||||
listResponse, listError := ListUsers("-5480083", "-5480083")
|
||||
if listError != nil {
|
||||
log.Fatalln(listError)
|
||||
}
|
||||
if listResponse != nil {
|
||||
fmt.Println("POST StatusCode:", listResponse.StatusCode)
|
||||
assert.Equal(200, listResponse.StatusCode,
|
||||
"TestListUsers(): Status Code is incorrect when listing users")
|
||||
}
|
||||
b, err := io.ReadAll(listResponse.Body)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
|
||||
// 3. Delete the users
|
||||
for i := 1; i < numberOfUsers; i++ {
|
||||
response, err := DeleteUser(
|
||||
strconv.Itoa(i) + "accessKey" + strconv.Itoa(i))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("............................TestListUsers(): Verification")
|
||||
fmt.Println(".........................TestListUsers(): DELETE response")
|
||||
fmt.Println(response)
|
||||
fmt.Println("............................TestListUsers(): DELETE error")
|
||||
fmt.Println(err)
|
||||
if response != nil {
|
||||
fmt.Println("DELETE StatusCode:", response.StatusCode)
|
||||
assert.Equal(204,
|
||||
response.StatusCode, "has to be 204 when delete user")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserInfo(t *testing.T) {
|
||||
/*
|
||||
Test to get the user information via API.
|
||||
*/
|
||||
|
||||
// 1. Create the user
|
||||
fmt.Println("TestGetUserInfo(): 1. Create the user")
|
||||
assert := assert.New(t)
|
||||
var groups = []string{}
|
||||
var policies = []string{}
|
||||
response, err := AddUser("accessKey", "secretKey", groups, policies)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
fmt.Println("POST StatusCode:", response.StatusCode)
|
||||
assert.Equal(201, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 2. Get user information
|
||||
fmt.Println("TestGetUserInfo(): 2. Get user information")
|
||||
response, err = GetUserInformation("accessKey")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
assert.Fail("There was an error in the response")
|
||||
return
|
||||
}
|
||||
|
||||
// 3. Verify user information
|
||||
fmt.Println("TestGetUserInfo(): 3. Verify user information")
|
||||
if response != nil {
|
||||
fmt.Println("POST StatusCode:", response.StatusCode)
|
||||
assert.Equal(200, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
b, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
expected := "{\"accessKey\":\"accessKey\",\"memberOf\":null,\"policy\":[],\"status\":\"enabled\"}\n"
|
||||
obtained := string(b)
|
||||
assert.Equal(expected, obtained, "User Information is wrong")
|
||||
}
|
||||
|
||||
func TestUpdateUserInfoSuccessfulResponse(t *testing.T) {
|
||||
/*
|
||||
Update User Information Test with Successful Response
|
||||
*/
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
// 1. Create an active user
|
||||
var groups = []string{}
|
||||
var policies = []string{}
|
||||
addUserResponse, addUserError := AddUser(
|
||||
"updateuser", "secretKey", groups, policies)
|
||||
if addUserError != nil {
|
||||
log.Println(addUserError)
|
||||
return
|
||||
}
|
||||
if addUserResponse != nil {
|
||||
fmt.Println("StatusCode:", addUserResponse.StatusCode)
|
||||
assert.Equal(
|
||||
201, addUserResponse.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 2. Deactivate the user
|
||||
// '{"status":"disabled","groups":[]}'
|
||||
updateUserResponse, UpdateUserError := UpdateUserInformation(
|
||||
"updateuser", "disabled", groups)
|
||||
|
||||
// 3. Verify user got deactivated
|
||||
if UpdateUserError != nil {
|
||||
log.Println(UpdateUserError)
|
||||
return
|
||||
}
|
||||
if updateUserResponse != nil {
|
||||
fmt.Println("StatusCode:", updateUserResponse.StatusCode)
|
||||
assert.Equal(
|
||||
200, updateUserResponse.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
b, err := io.ReadAll(updateUserResponse.Body)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
assert.True(strings.Contains(string(b), "disabled"))
|
||||
|
||||
}
|
||||
|
||||
func TestUpdateUserInfoGenericErrorResponse(t *testing.T) {
|
||||
/*
|
||||
Update User Information Test with Generic Error Response
|
||||
*/
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
// 1. Create an active user
|
||||
var groups = []string{}
|
||||
var policies = []string{}
|
||||
addUserResponse, addUserError := AddUser(
|
||||
"updateusererror", "secretKey", groups, policies)
|
||||
if addUserError != nil {
|
||||
log.Println(addUserError)
|
||||
return
|
||||
}
|
||||
if addUserResponse != nil {
|
||||
fmt.Println("StatusCode:", addUserResponse.StatusCode)
|
||||
assert.Equal(
|
||||
201, addUserResponse.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 2. Deactivate the user with wrong status
|
||||
updateUserResponse, UpdateUserError := UpdateUserInformation(
|
||||
"updateusererror", "inactive", groups)
|
||||
|
||||
// 3. Verify user got deactivated
|
||||
if UpdateUserError != nil {
|
||||
log.Println(UpdateUserError)
|
||||
assert.Fail("There was an error while updating user info")
|
||||
return
|
||||
}
|
||||
if updateUserResponse != nil {
|
||||
fmt.Println("StatusCode:", updateUserResponse.StatusCode)
|
||||
assert.Equal(
|
||||
500, updateUserResponse.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
b, err := io.ReadAll(updateUserResponse.Body)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
assert.True(strings.Contains(string(b), "status not valid"))
|
||||
|
||||
}
|
||||
|
||||
func TestRemoveUserSuccessfulResponse(t *testing.T) {
|
||||
/*
|
||||
To test removing a user from API
|
||||
*/
|
||||
assert := assert.New(t)
|
||||
|
||||
// 1. Create an active user
|
||||
var groups = []string{}
|
||||
var policies = []string{}
|
||||
addUserResponse, addUserError := AddUser(
|
||||
"testremoveuser1", "secretKey", groups, policies)
|
||||
if addUserError != nil {
|
||||
log.Println(addUserError)
|
||||
return
|
||||
}
|
||||
if addUserResponse != nil {
|
||||
fmt.Println("StatusCode:", addUserResponse.StatusCode)
|
||||
assert.Equal(
|
||||
201, addUserResponse.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 2. Remove the user
|
||||
removeUserResponse, removeUserError := RemoveUser("testremoveuser1")
|
||||
if removeUserError != nil {
|
||||
log.Println(removeUserError)
|
||||
return
|
||||
}
|
||||
if removeUserResponse != nil {
|
||||
fmt.Println("StatusCode:", removeUserResponse.StatusCode)
|
||||
assert.Equal(
|
||||
204, removeUserResponse.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 3. Verify the user got removed
|
||||
getUserInfoResponse, getUserInfoError := GetUserInformation(
|
||||
"testremoveuser1")
|
||||
if getUserInfoError != nil {
|
||||
log.Println(getUserInfoError)
|
||||
assert.Fail("There was an error in the response")
|
||||
return
|
||||
}
|
||||
if getUserInfoResponse != nil {
|
||||
fmt.Println("StatusCode:", getUserInfoResponse.StatusCode)
|
||||
assert.Equal(
|
||||
404, getUserInfoResponse.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
finalResponse := inspectHTTPResponse(getUserInfoResponse)
|
||||
printMessage(finalResponse)
|
||||
assert.True(strings.Contains(
|
||||
finalResponse, "The specified user does not exist"), finalResponse)
|
||||
|
||||
}
|
||||
|
||||
func TestUpdateGroupsForAUser(t *testing.T) {
|
||||
/*
|
||||
To test Update Groups For a User End Point.
|
||||
*/
|
||||
// 1. Create the user
|
||||
numberOfGroups := 3
|
||||
groupName := "updategroupforausergroup"
|
||||
userName := "updategroupsforauser1"
|
||||
assert := assert.New(t)
|
||||
var groups = []string{}
|
||||
var policies = []string{}
|
||||
response, err := AddUser(userName, "secretKey", groups, policies)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
fmt.Println("StatusCode:", response.StatusCode)
|
||||
assert.Equal(201, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 2. Update the groups of the created user with newGroups
|
||||
var newGroups = make([]string, 3)
|
||||
for i := 0; i < numberOfGroups; i++ {
|
||||
newGroups[i] = groupName + strconv.Itoa(i)
|
||||
}
|
||||
response, err = UpdateGroupsForAUser(userName, newGroups)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
fmt.Println("StatusCode:", response.StatusCode)
|
||||
assert.Equal(200, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 3. Verify the newGroups were updated accordingly
|
||||
getUserInfoResponse, getUserInfoErr := GetUserInformation(userName)
|
||||
if getUserInfoErr != nil {
|
||||
log.Println(getUserInfoErr)
|
||||
assert.Fail("There was an error in the response")
|
||||
return
|
||||
}
|
||||
if getUserInfoResponse != nil {
|
||||
fmt.Println("StatusCode:", getUserInfoResponse.StatusCode)
|
||||
assert.Equal(
|
||||
200, getUserInfoResponse.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
finalResponse := inspectHTTPResponse(getUserInfoResponse)
|
||||
for i := 0; i < numberOfGroups; i++ {
|
||||
assert.True(strings.Contains(
|
||||
finalResponse, groupName+strconv.Itoa(i)), finalResponse)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateServiceAccountForUser(t *testing.T) {
|
||||
/*
|
||||
To test creation of service account for a user.
|
||||
*/
|
||||
|
||||
// Test's variables
|
||||
userName := "testcreateserviceaccountforuser1"
|
||||
assert := assert.New(t)
|
||||
policy := ""
|
||||
serviceAccountLengthInBytes := 40 // As observed, update as needed
|
||||
|
||||
// 1. Create the user
|
||||
var groups = []string{}
|
||||
var policies = []string{}
|
||||
response, err := AddUser(userName, "secretKey", groups, policies)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
fmt.Println("StatusCode:", response.StatusCode)
|
||||
assert.Equal(201, response.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 2. Create the service account for the user
|
||||
createServiceAccountResponse,
|
||||
createServiceAccountError := CreateServiceAccountForUser(
|
||||
userName,
|
||||
policy,
|
||||
)
|
||||
if createServiceAccountError != nil {
|
||||
log.Println(createServiceAccountError)
|
||||
assert.Fail("Error in createServiceAccountError")
|
||||
}
|
||||
if createServiceAccountResponse != nil {
|
||||
fmt.Println("StatusCode:", createServiceAccountResponse.StatusCode)
|
||||
assert.Equal(
|
||||
201, createServiceAccountResponse.StatusCode,
|
||||
inspectHTTPResponse(createServiceAccountResponse),
|
||||
)
|
||||
}
|
||||
|
||||
// 3. Verify the service account for the user
|
||||
listOfAccountsResponse,
|
||||
listOfAccountsError := ReturnsAListOfServiceAccountsForAUser(userName)
|
||||
if listOfAccountsError != nil {
|
||||
log.Println(listOfAccountsError)
|
||||
assert.Fail("Error in listOfAccountsError")
|
||||
}
|
||||
finalResponse := inspectHTTPResponse(listOfAccountsResponse)
|
||||
if listOfAccountsResponse != nil {
|
||||
fmt.Println("StatusCode:", listOfAccountsResponse.StatusCode)
|
||||
assert.Equal(
|
||||
200, listOfAccountsResponse.StatusCode,
|
||||
finalResponse,
|
||||
)
|
||||
}
|
||||
assert.Equal(len(finalResponse), serviceAccountLengthInBytes, finalResponse)
|
||||
}
|
||||
|
||||
func TestUsersGroupsBulk(t *testing.T) {
|
||||
/*
|
||||
To test UsersGroupsBulk End Point
|
||||
*/
|
||||
|
||||
// Vars
|
||||
assert := assert.New(t)
|
||||
numberOfUsers := 5
|
||||
numberOfGroups := 1
|
||||
//var groups = []string{}
|
||||
var policies = []string{}
|
||||
username := "testusersgroupbulk"
|
||||
groupName := "testusersgroupsbulkgroupone"
|
||||
var members = []string{}
|
||||
users := make([]string, numberOfUsers)
|
||||
groups := make([]string, numberOfGroups)
|
||||
|
||||
// 1. Create some users
|
||||
for i := 0; i < numberOfUsers; i++ {
|
||||
users[i] = username + strconv.Itoa(i)
|
||||
response, err := AddUser(
|
||||
users[i],
|
||||
"secretKey"+strconv.Itoa(i), []string{}, policies)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
fmt.Println("POST StatusCode:", response.StatusCode)
|
||||
assert.Equal(201, response.StatusCode,
|
||||
"Status Code is incorrect on index: "+strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Create a group with no members
|
||||
responseAddGroup, errorAddGroup := AddGroup(groupName, members)
|
||||
if errorAddGroup != nil {
|
||||
log.Println(errorAddGroup)
|
||||
return
|
||||
}
|
||||
finalResponse := inspectHTTPResponse(responseAddGroup)
|
||||
if responseAddGroup != nil {
|
||||
fmt.Println("POST StatusCode:", responseAddGroup.StatusCode)
|
||||
assert.Equal(
|
||||
201,
|
||||
responseAddGroup.StatusCode,
|
||||
finalResponse,
|
||||
)
|
||||
}
|
||||
|
||||
// 3. Add users to the group
|
||||
groups[0] = groupName
|
||||
responseUsersGroupsBulk, errorUsersGroupsBulk := UsersGroupsBulk(
|
||||
users,
|
||||
groups,
|
||||
)
|
||||
if errorUsersGroupsBulk != nil {
|
||||
log.Println(errorUsersGroupsBulk)
|
||||
return
|
||||
}
|
||||
finalResponse = inspectHTTPResponse(responseUsersGroupsBulk)
|
||||
if responseUsersGroupsBulk != nil {
|
||||
fmt.Println("POST StatusCode:", responseUsersGroupsBulk.StatusCode)
|
||||
assert.Equal(
|
||||
200,
|
||||
responseUsersGroupsBulk.StatusCode,
|
||||
finalResponse,
|
||||
)
|
||||
}
|
||||
|
||||
// 4. Verify users got added to the group
|
||||
for i := 0; i < numberOfUsers; i++ {
|
||||
responseGetUserInfo, errGetUserInfo := GetUserInformation(
|
||||
username + strconv.Itoa(i),
|
||||
)
|
||||
if errGetUserInfo != nil {
|
||||
log.Println(errGetUserInfo)
|
||||
assert.Fail("There was an error in the response")
|
||||
return
|
||||
}
|
||||
finalResponse = inspectHTTPResponse(responseGetUserInfo)
|
||||
if responseGetUserInfo != nil {
|
||||
assert.Equal(200, responseGetUserInfo.StatusCode, finalResponse)
|
||||
}
|
||||
// Make sure the user belongs to the created group
|
||||
assert.True(strings.Contains(string(finalResponse), groupName))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.13.3
|
||||
image: minio/console:v0.14.3
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_OPERATOR_MODE
|
||||
|
||||
@@ -32,7 +32,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: console
|
||||
image: minio/console:v0.13.3
|
||||
image: minio/console:v0.14.3
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_MINIO_SERVER
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -362,7 +362,7 @@ type BucketDetailsQuota struct {
|
||||
Quota int64 `json:"quota,omitempty"`
|
||||
|
||||
// type
|
||||
// Enum: [fifo hard]
|
||||
// Enum: [hard]
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
@@ -384,7 +384,7 @@ var bucketDetailsQuotaTypeTypePropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["fifo","hard"]`), &res); err != nil {
|
||||
if err := json.Unmarshal([]byte(`["hard"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
@@ -394,9 +394,6 @@ func init() {
|
||||
|
||||
const (
|
||||
|
||||
// BucketDetailsQuotaTypeFifo captures enum value "fifo"
|
||||
BucketDetailsQuotaTypeFifo string = "fifo"
|
||||
|
||||
// BucketDetailsQuotaTypeHard captures enum value "hard"
|
||||
BucketDetailsQuotaTypeHard string = "hard"
|
||||
)
|
||||
|
||||
@@ -37,8 +37,12 @@ import (
|
||||
type BucketAccess string
|
||||
|
||||
func NewBucketAccess(value BucketAccess) *BucketAccess {
|
||||
v := value
|
||||
return &v
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated BucketAccess.
|
||||
func (m BucketAccess) Pointer() *BucketAccess {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -37,8 +37,12 @@ import (
|
||||
type BucketEncryptionType string
|
||||
|
||||
func NewBucketEncryptionType(value BucketEncryptionType) *BucketEncryptionType {
|
||||
v := value
|
||||
return &v
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated BucketEncryptionType.
|
||||
func (m BucketEncryptionType) Pointer() *BucketEncryptionType {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -41,7 +41,7 @@ type BucketQuota struct {
|
||||
Quota int64 `json:"quota,omitempty"`
|
||||
|
||||
// type
|
||||
// Enum: [hard fifo]
|
||||
// Enum: [hard]
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ var bucketQuotaTypeTypePropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["hard","fifo"]`), &res); err != nil {
|
||||
if err := json.Unmarshal([]byte(`["hard"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
@@ -75,9 +75,6 @@ const (
|
||||
|
||||
// BucketQuotaTypeHard captures enum value "hard"
|
||||
BucketQuotaTypeHard string = "hard"
|
||||
|
||||
// BucketQuotaTypeFifo captures enum value "fifo"
|
||||
BucketQuotaTypeFifo string = "fifo"
|
||||
)
|
||||
|
||||
// prop value enum
|
||||
|
||||
@@ -37,8 +37,12 @@ import (
|
||||
type NofiticationService string
|
||||
|
||||
func NewNofiticationService(value NofiticationService) *NofiticationService {
|
||||
v := value
|
||||
return &v
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated NofiticationService.
|
||||
func (m NofiticationService) Pointer() *NofiticationService {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -37,8 +37,12 @@ import (
|
||||
type NotificationEventType string
|
||||
|
||||
func NewNotificationEventType(value NotificationEventType) *NotificationEventType {
|
||||
v := value
|
||||
return &v
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated NotificationEventType.
|
||||
func (m NotificationEventType) Pointer() *NotificationEventType {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -37,8 +37,12 @@ import (
|
||||
type ObjectLegalHoldStatus string
|
||||
|
||||
func NewObjectLegalHoldStatus(value ObjectLegalHoldStatus) *ObjectLegalHoldStatus {
|
||||
v := value
|
||||
return &v
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated ObjectLegalHoldStatus.
|
||||
func (m ObjectLegalHoldStatus) Pointer() *ObjectLegalHoldStatus {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -37,8 +37,12 @@ import (
|
||||
type ObjectRetentionMode string
|
||||
|
||||
func NewObjectRetentionMode(value ObjectRetentionMode) *ObjectRetentionMode {
|
||||
v := value
|
||||
return &v
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated ObjectRetentionMode.
|
||||
func (m ObjectRetentionMode) Pointer() *ObjectRetentionMode {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -37,8 +37,12 @@ import (
|
||||
type ObjectRetentionUnit string
|
||||
|
||||
func NewObjectRetentionUnit(value ObjectRetentionUnit) *ObjectRetentionUnit {
|
||||
v := value
|
||||
return &v
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated ObjectRetentionUnit.
|
||||
func (m ObjectRetentionUnit) Pointer() *ObjectRetentionUnit {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -37,8 +37,12 @@ import (
|
||||
type PolicyEntity string
|
||||
|
||||
func NewPolicyEntity(value PolicyEntity) *PolicyEntity {
|
||||
v := value
|
||||
return &v
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated PolicyEntity.
|
||||
func (m PolicyEntity) Pointer() *PolicyEntity {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -37,8 +37,12 @@ import (
|
||||
type ProfilerType string
|
||||
|
||||
func NewProfilerType(value ProfilerType) *ProfilerType {
|
||||
v := value
|
||||
return &v
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated ProfilerType.
|
||||
func (m ProfilerType) Pointer() *ProfilerType {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -45,7 +45,7 @@ type SetBucketQuota struct {
|
||||
Enabled *bool `json:"enabled"`
|
||||
|
||||
// quota type
|
||||
// Enum: [fifo hard]
|
||||
// Enum: [hard]
|
||||
QuotaType string `json:"quota_type,omitempty"`
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ var setBucketQuotaTypeQuotaTypePropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["fifo","hard"]`), &res); err != nil {
|
||||
if err := json.Unmarshal([]byte(`["hard"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
@@ -90,9 +90,6 @@ func init() {
|
||||
|
||||
const (
|
||||
|
||||
// SetBucketQuotaQuotaTypeFifo captures enum value "fifo"
|
||||
SetBucketQuotaQuotaTypeFifo string = "fifo"
|
||||
|
||||
// SetBucketQuotaQuotaTypeHard captures enum value "hard"
|
||||
SetBucketQuotaQuotaTypeHard string = "hard"
|
||||
)
|
||||
|
||||
122
models/subnet_login_m_f_a_request.go
Normal file
122
models/subnet_login_m_f_a_request.go
Normal file
@@ -0,0 +1,122 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// SubnetLoginMFARequest subnet login m f a request
|
||||
//
|
||||
// swagger:model subnetLoginMFARequest
|
||||
type SubnetLoginMFARequest struct {
|
||||
|
||||
// mfa token
|
||||
// Required: true
|
||||
MfaToken *string `json:"mfa_token"`
|
||||
|
||||
// otp
|
||||
// Required: true
|
||||
Otp *string `json:"otp"`
|
||||
|
||||
// username
|
||||
// Required: true
|
||||
Username *string `json:"username"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet login m f a request
|
||||
func (m *SubnetLoginMFARequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateMfaToken(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateOtp(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateUsername(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginMFARequest) validateMfaToken(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("mfa_token", "body", m.MfaToken); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginMFARequest) validateOtp(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("otp", "body", m.Otp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginMFARequest) validateUsername(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("username", "body", m.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet login m f a request based on context it is used
|
||||
func (m *SubnetLoginMFARequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetLoginMFARequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetLoginMFARequest) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetLoginMFARequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
73
models/subnet_login_request.go
Normal file
73
models/subnet_login_request.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SubnetLoginRequest subnet login request
|
||||
//
|
||||
// swagger:model subnetLoginRequest
|
||||
type SubnetLoginRequest struct {
|
||||
|
||||
// api key
|
||||
APIKey string `json:"apiKey,omitempty"`
|
||||
|
||||
// password
|
||||
Password string `json:"password,omitempty"`
|
||||
|
||||
// username
|
||||
Username string `json:"username,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet login request
|
||||
func (m *SubnetLoginRequest) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet login request based on context it is used
|
||||
func (m *SubnetLoginRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetLoginRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetLoginRequest) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetLoginRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
142
models/subnet_login_response.go
Normal file
142
models/subnet_login_response.go
Normal file
@@ -0,0 +1,142 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SubnetLoginResponse subnet login response
|
||||
//
|
||||
// swagger:model subnetLoginResponse
|
||||
type SubnetLoginResponse struct {
|
||||
|
||||
// access token
|
||||
AccessToken string `json:"access_token,omitempty"`
|
||||
|
||||
// mfa token
|
||||
MfaToken string `json:"mfa_token,omitempty"`
|
||||
|
||||
// organizations
|
||||
Organizations []*SubnetOrganization `json:"organizations"`
|
||||
|
||||
// registered
|
||||
Registered bool `json:"registered,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet login response
|
||||
func (m *SubnetLoginResponse) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateOrganizations(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginResponse) validateOrganizations(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.Organizations) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(m.Organizations); i++ {
|
||||
if swag.IsZero(m.Organizations[i]) { // not required
|
||||
continue
|
||||
}
|
||||
|
||||
if m.Organizations[i] != nil {
|
||||
if err := m.Organizations[i].Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("organizations" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("organizations" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this subnet login response based on the context it is used
|
||||
func (m *SubnetLoginResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateOrganizations(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetLoginResponse) contextValidateOrganizations(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
for i := 0; i < len(m.Organizations); i++ {
|
||||
|
||||
if m.Organizations[i] != nil {
|
||||
if err := m.Organizations[i].ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("organizations" + "." + strconv.Itoa(i))
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("organizations" + "." + strconv.Itoa(i))
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetLoginResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetLoginResponse) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetLoginResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
82
models/subnet_organization.go
Normal file
82
models/subnet_organization.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SubnetOrganization subnet organization
|
||||
//
|
||||
// swagger:model subnetOrganization
|
||||
type SubnetOrganization struct {
|
||||
|
||||
// account Id
|
||||
AccountID int64 `json:"accountId,omitempty"`
|
||||
|
||||
// company
|
||||
Company string `json:"company,omitempty"`
|
||||
|
||||
// is account owner
|
||||
IsAccountOwner bool `json:"isAccountOwner,omitempty"`
|
||||
|
||||
// short name
|
||||
ShortName string `json:"shortName,omitempty"`
|
||||
|
||||
// subscription status
|
||||
SubscriptionStatus string `json:"subscriptionStatus,omitempty"`
|
||||
|
||||
// user Id
|
||||
UserID int64 `json:"userId,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet organization
|
||||
func (m *SubnetOrganization) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet organization based on context it is used
|
||||
func (m *SubnetOrganization) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetOrganization) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetOrganization) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetOrganization
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
67
models/subnet_reg_token_response.go
Normal file
67
models/subnet_reg_token_response.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
)
|
||||
|
||||
// SubnetRegTokenResponse subnet reg token response
|
||||
//
|
||||
// swagger:model SubnetRegTokenResponse
|
||||
type SubnetRegTokenResponse struct {
|
||||
|
||||
// reg token
|
||||
RegToken string `json:"regToken,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet reg token response
|
||||
func (m *SubnetRegTokenResponse) Validate(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet reg token response based on context it is used
|
||||
func (m *SubnetRegTokenResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetRegTokenResponse) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetRegTokenResponse) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetRegTokenResponse
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
105
models/subnet_register_request.go
Normal file
105
models/subnet_register_request.go
Normal file
@@ -0,0 +1,105 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// SubnetRegisterRequest subnet register request
|
||||
//
|
||||
// swagger:model subnetRegisterRequest
|
||||
type SubnetRegisterRequest struct {
|
||||
|
||||
// account id
|
||||
// Required: true
|
||||
AccountID *string `json:"account_id"`
|
||||
|
||||
// token
|
||||
// Required: true
|
||||
Token *string `json:"token"`
|
||||
}
|
||||
|
||||
// Validate validates this subnet register request
|
||||
func (m *SubnetRegisterRequest) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.validateAccountID(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateToken(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetRegisterRequest) validateAccountID(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("account_id", "body", m.AccountID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SubnetRegisterRequest) validateToken(formats strfmt.Registry) error {
|
||||
|
||||
if err := validate.Required("token", "body", m.Token); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this subnet register request based on context it is used
|
||||
func (m *SubnetRegisterRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary interface implementation
|
||||
func (m *SubnetRegisterRequest) MarshalBinary() ([]byte, error) {
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return swag.WriteJSON(m)
|
||||
}
|
||||
|
||||
// UnmarshalBinary interface implementation
|
||||
func (m *SubnetRegisterRequest) UnmarshalBinary(b []byte) error {
|
||||
var res SubnetRegisterRequest
|
||||
if err := swag.ReadJSON(b, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
*m = res
|
||||
return nil
|
||||
}
|
||||
@@ -66,6 +66,18 @@ type TenantLogs struct {
|
||||
// labels
|
||||
Labels []*Label `json:"labels"`
|
||||
|
||||
// log CPU request
|
||||
LogCPURequest string `json:"logCPURequest,omitempty"`
|
||||
|
||||
// log d b CPU request
|
||||
LogDBCPURequest string `json:"logDBCPURequest,omitempty"`
|
||||
|
||||
// log d b mem request
|
||||
LogDBMemRequest string `json:"logDBMemRequest,omitempty"`
|
||||
|
||||
// log mem request
|
||||
LogMemRequest string `json:"logMemRequest,omitempty"`
|
||||
|
||||
// node selector
|
||||
NodeSelector []*NodeSelector `json:"nodeSelector"`
|
||||
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
// 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 auth
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// This file is safe to edit. Once it exists it will not be overwritten
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
@@ -94,8 +92,6 @@ func configureAPI(api *operations.OperatorAPI) http.Handler {
|
||||
registerVolumesHandlers(api)
|
||||
// Namespaces handlers
|
||||
registerNamespaceHandlers(api)
|
||||
// Subscription handlers
|
||||
registerSubscriptionHandlers(api)
|
||||
|
||||
api.PreServerShutdown = func() {}
|
||||
|
||||
|
||||
@@ -1261,6 +1261,49 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/tenants/{tenant}/pvcs/{PVCName}/events": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"OperatorAPI"
|
||||
],
|
||||
"summary": "Get Events for PVC",
|
||||
"operationId": "GetPVCEvents",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "tenant",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "PVCName",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/eventListWrapper"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/tenants/{tenant}/security": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -3325,6 +3368,18 @@ func init() {
|
||||
"$ref": "#/definitions/label"
|
||||
}
|
||||
},
|
||||
"logCPURequest": {
|
||||
"type": "string"
|
||||
},
|
||||
"logDBCPURequest": {
|
||||
"type": "string"
|
||||
},
|
||||
"logDBMemRequest": {
|
||||
"type": "string"
|
||||
},
|
||||
"logMemRequest": {
|
||||
"type": "string"
|
||||
},
|
||||
"nodeSelector": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -4891,6 +4946,49 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/tenants/{tenant}/pvcs/{PVCName}/events": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"OperatorAPI"
|
||||
],
|
||||
"summary": "Get Events for PVC",
|
||||
"operationId": "GetPVCEvents",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "tenant",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "PVCName",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/eventListWrapper"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "Generic error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/namespaces/{namespace}/tenants/{tenant}/security": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -7533,6 +7631,18 @@ func init() {
|
||||
"$ref": "#/definitions/label"
|
||||
}
|
||||
},
|
||||
"logCPURequest": {
|
||||
"type": "string"
|
||||
},
|
||||
"logDBCPURequest": {
|
||||
"type": "string"
|
||||
},
|
||||
"logDBMemRequest": {
|
||||
"type": "string"
|
||||
},
|
||||
"logMemRequest": {
|
||||
"type": "string"
|
||||
},
|
||||
"nodeSelector": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
// 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 operatorapi
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -99,6 +99,9 @@ func NewOperatorAPI(spec *loads.Document) *OperatorAPI {
|
||||
OperatorAPIGetMaxAllocatableMemHandler: operator_api.GetMaxAllocatableMemHandlerFunc(func(params operator_api.GetMaxAllocatableMemParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation operator_api.GetMaxAllocatableMem has not yet been implemented")
|
||||
}),
|
||||
OperatorAPIGetPVCEventsHandler: operator_api.GetPVCEventsHandlerFunc(func(params operator_api.GetPVCEventsParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation operator_api.GetPVCEvents has not yet been implemented")
|
||||
}),
|
||||
OperatorAPIGetParityHandler: operator_api.GetParityHandlerFunc(func(params operator_api.GetParityParams, principal *models.Principal) middleware.Responder {
|
||||
return middleware.NotImplemented("operation operator_api.GetParity has not yet been implemented")
|
||||
}),
|
||||
@@ -274,6 +277,8 @@ type OperatorAPI struct {
|
||||
OperatorAPIGetDirectCSIVolumeListHandler operator_api.GetDirectCSIVolumeListHandler
|
||||
// OperatorAPIGetMaxAllocatableMemHandler sets the operation handler for the get max allocatable mem operation
|
||||
OperatorAPIGetMaxAllocatableMemHandler operator_api.GetMaxAllocatableMemHandler
|
||||
// OperatorAPIGetPVCEventsHandler sets the operation handler for the get p v c events operation
|
||||
OperatorAPIGetPVCEventsHandler operator_api.GetPVCEventsHandler
|
||||
// OperatorAPIGetParityHandler sets the operation handler for the get parity operation
|
||||
OperatorAPIGetParityHandler operator_api.GetParityHandler
|
||||
// OperatorAPIGetPodEventsHandler sets the operation handler for the get pod events operation
|
||||
@@ -459,6 +464,9 @@ func (o *OperatorAPI) Validate() error {
|
||||
if o.OperatorAPIGetMaxAllocatableMemHandler == nil {
|
||||
unregistered = append(unregistered, "operator_api.GetMaxAllocatableMemHandler")
|
||||
}
|
||||
if o.OperatorAPIGetPVCEventsHandler == nil {
|
||||
unregistered = append(unregistered, "operator_api.GetPVCEventsHandler")
|
||||
}
|
||||
if o.OperatorAPIGetParityHandler == nil {
|
||||
unregistered = append(unregistered, "operator_api.GetParityHandler")
|
||||
}
|
||||
@@ -710,6 +718,10 @@ func (o *OperatorAPI) initHandlerCache() {
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/namespaces/{namespace}/tenants/{tenant}/pvcs/{PVCName}/events"] = operator_api.NewGetPVCEvents(o.context, o.OperatorAPIGetPVCEventsHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
}
|
||||
o.handlers["GET"]["/get-parity/{nodes}/{disksPerNode}"] = operator_api.NewGetParity(o.context, o.OperatorAPIGetParityHandler)
|
||||
if o.handlers["GET"] == nil {
|
||||
o.handlers["GET"] = make(map[string]http.Handler)
|
||||
|
||||
88
operatorapi/operations/operator_api/get_p_v_c_events.go
Normal file
88
operatorapi/operations/operator_api/get_p_v_c_events.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 operator_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// GetPVCEventsHandlerFunc turns a function with the right signature into a get p v c events handler
|
||||
type GetPVCEventsHandlerFunc func(GetPVCEventsParams, *models.Principal) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn GetPVCEventsHandlerFunc) Handle(params GetPVCEventsParams, principal *models.Principal) middleware.Responder {
|
||||
return fn(params, principal)
|
||||
}
|
||||
|
||||
// GetPVCEventsHandler interface for that can handle valid get p v c events params
|
||||
type GetPVCEventsHandler interface {
|
||||
Handle(GetPVCEventsParams, *models.Principal) middleware.Responder
|
||||
}
|
||||
|
||||
// NewGetPVCEvents creates a new http.Handler for the get p v c events operation
|
||||
func NewGetPVCEvents(ctx *middleware.Context, handler GetPVCEventsHandler) *GetPVCEvents {
|
||||
return &GetPVCEvents{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/* GetPVCEvents swagger:route GET /namespaces/{namespace}/tenants/{tenant}/pvcs/{PVCName}/events OperatorAPI getPVCEvents
|
||||
|
||||
Get Events for PVC
|
||||
|
||||
*/
|
||||
type GetPVCEvents struct {
|
||||
Context *middleware.Context
|
||||
Handler GetPVCEventsHandler
|
||||
}
|
||||
|
||||
func (o *GetPVCEvents) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewGetPVCEventsParams()
|
||||
uprinc, aCtx, err := o.Context.Authorize(r, route)
|
||||
if err != nil {
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
if aCtx != nil {
|
||||
*r = *aCtx
|
||||
}
|
||||
var principal *models.Principal
|
||||
if uprinc != nil {
|
||||
principal = uprinc.(*models.Principal) // this is really a models.Principal, I promise
|
||||
}
|
||||
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params, principal) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 operator_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
// NewGetPVCEventsParams creates a new GetPVCEventsParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewGetPVCEventsParams() GetPVCEventsParams {
|
||||
|
||||
return GetPVCEventsParams{}
|
||||
}
|
||||
|
||||
// GetPVCEventsParams contains all the bound params for the get p v c events operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters GetPVCEvents
|
||||
type GetPVCEventsParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
PVCName string
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
Namespace string
|
||||
/*
|
||||
Required: true
|
||||
In: path
|
||||
*/
|
||||
Tenant string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewGetPVCEventsParams() beforehand.
|
||||
func (o *GetPVCEventsParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
rPVCName, rhkPVCName, _ := route.Params.GetOK("PVCName")
|
||||
if err := o.bindPVCName(rPVCName, rhkPVCName, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
rNamespace, rhkNamespace, _ := route.Params.GetOK("namespace")
|
||||
if err := o.bindNamespace(rNamespace, rhkNamespace, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
rTenant, rhkTenant, _ := route.Params.GetOK("tenant")
|
||||
if err := o.bindTenant(rTenant, rhkTenant, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindPVCName binds and validates parameter PVCName from path.
|
||||
func (o *GetPVCEventsParams) bindPVCName(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.PVCName = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindNamespace binds and validates parameter Namespace from path.
|
||||
func (o *GetPVCEventsParams) bindNamespace(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.Namespace = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindTenant binds and validates parameter Tenant from path.
|
||||
func (o *GetPVCEventsParams) bindTenant(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// Parameter is provided by construction from the route
|
||||
o.Tenant = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 operator_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
)
|
||||
|
||||
// GetPVCEventsOKCode is the HTTP code returned for type GetPVCEventsOK
|
||||
const GetPVCEventsOKCode int = 200
|
||||
|
||||
/*GetPVCEventsOK A successful response.
|
||||
|
||||
swagger:response getPVCEventsOK
|
||||
*/
|
||||
type GetPVCEventsOK struct {
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload models.EventListWrapper `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewGetPVCEventsOK creates GetPVCEventsOK with default headers values
|
||||
func NewGetPVCEventsOK() *GetPVCEventsOK {
|
||||
|
||||
return &GetPVCEventsOK{}
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the get p v c events o k response
|
||||
func (o *GetPVCEventsOK) WithPayload(payload models.EventListWrapper) *GetPVCEventsOK {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the get p v c events o k response
|
||||
func (o *GetPVCEventsOK) SetPayload(payload models.EventListWrapper) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *GetPVCEventsOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(200)
|
||||
payload := o.Payload
|
||||
if payload == nil {
|
||||
// return empty array
|
||||
payload = models.EventListWrapper{}
|
||||
}
|
||||
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
|
||||
/*GetPVCEventsDefault Generic error response.
|
||||
|
||||
swagger:response getPVCEventsDefault
|
||||
*/
|
||||
type GetPVCEventsDefault struct {
|
||||
_statusCode int
|
||||
|
||||
/*
|
||||
In: Body
|
||||
*/
|
||||
Payload *models.Error `json:"body,omitempty"`
|
||||
}
|
||||
|
||||
// NewGetPVCEventsDefault creates GetPVCEventsDefault with default headers values
|
||||
func NewGetPVCEventsDefault(code int) *GetPVCEventsDefault {
|
||||
if code <= 0 {
|
||||
code = 500
|
||||
}
|
||||
|
||||
return &GetPVCEventsDefault{
|
||||
_statusCode: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WithStatusCode adds the status to the get p v c events default response
|
||||
func (o *GetPVCEventsDefault) WithStatusCode(code int) *GetPVCEventsDefault {
|
||||
o._statusCode = code
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStatusCode sets the status to the get p v c events default response
|
||||
func (o *GetPVCEventsDefault) SetStatusCode(code int) {
|
||||
o._statusCode = code
|
||||
}
|
||||
|
||||
// WithPayload adds the payload to the get p v c events default response
|
||||
func (o *GetPVCEventsDefault) WithPayload(payload *models.Error) *GetPVCEventsDefault {
|
||||
o.Payload = payload
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPayload sets the payload to the get p v c events default response
|
||||
func (o *GetPVCEventsDefault) SetPayload(payload *models.Error) {
|
||||
o.Payload = payload
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *GetPVCEventsDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.WriteHeader(o._statusCode)
|
||||
if o.Payload != nil {
|
||||
payload := o.Payload
|
||||
if err := producer.Produce(rw, payload); err != nil {
|
||||
panic(err) // let the recovery middleware deal with this
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// 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 operator_api
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetPVCEventsURL generates an URL for the get p v c events operation
|
||||
type GetPVCEventsURL struct {
|
||||
PVCName string
|
||||
Namespace string
|
||||
Tenant string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *GetPVCEventsURL) WithBasePath(bp string) *GetPVCEventsURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *GetPVCEventsURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *GetPVCEventsURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/namespaces/{namespace}/tenants/{tenant}/pvcs/{PVCName}/events"
|
||||
|
||||
pVCName := o.PVCName
|
||||
if pVCName != "" {
|
||||
_path = strings.Replace(_path, "{PVCName}", pVCName, -1)
|
||||
} else {
|
||||
return nil, errors.New("pVCName is required on GetPVCEventsURL")
|
||||
}
|
||||
|
||||
namespace := o.Namespace
|
||||
if namespace != "" {
|
||||
_path = strings.Replace(_path, "{namespace}", namespace, -1)
|
||||
} else {
|
||||
return nil, errors.New("namespace is required on GetPVCEventsURL")
|
||||
}
|
||||
|
||||
tenant := o.Tenant
|
||||
if tenant != "" {
|
||||
_path = strings.Replace(_path, "{tenant}", tenant, -1)
|
||||
} else {
|
||||
return nil, errors.New("tenant is required on GetPVCEventsURL")
|
||||
}
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *GetPVCEventsURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *GetPVCEventsURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *GetPVCEventsURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on GetPVCEventsURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on GetPVCEventsURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *GetPVCEventsURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -1,416 +0,0 @@
|
||||
// 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 operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/subnet"
|
||||
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
)
|
||||
|
||||
func registerSubscriptionHandlers(api *operations.OperatorAPI) {
|
||||
// Activate license subscription for a particular tenant
|
||||
api.OperatorAPISubscriptionActivateHandler = operator_api.SubscriptionActivateHandlerFunc(func(params operator_api.SubscriptionActivateParams, session *models.Principal) middleware.Responder {
|
||||
err := getSubscriptionActivateResponse(session, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionActivateDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionActivateNoContent()
|
||||
})
|
||||
// Refresh license for k8s cluster
|
||||
api.OperatorAPISubscriptionRefreshHandler = operator_api.SubscriptionRefreshHandlerFunc(func(params operator_api.SubscriptionRefreshParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionRefreshResponse(session)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionRefreshDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionRefreshOK().WithPayload(license)
|
||||
})
|
||||
// Validate subscription handler
|
||||
api.OperatorAPISubscriptionValidateHandler = operator_api.SubscriptionValidateHandlerFunc(func(params operator_api.SubscriptionValidateParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionValidateResponse(session, params.Body)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionValidateDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionValidateOK().WithPayload(license)
|
||||
})
|
||||
// Get subscription information handler
|
||||
api.OperatorAPISubscriptionInfoHandler = operator_api.SubscriptionInfoHandlerFunc(func(params operator_api.SubscriptionInfoParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionInfoResponse(session)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionInfoOK().WithPayload(license)
|
||||
})
|
||||
// Refresh license for k8s cluster
|
||||
api.OperatorAPISubscriptionRefreshHandler = operator_api.SubscriptionRefreshHandlerFunc(func(params operator_api.SubscriptionRefreshParams, session *models.Principal) middleware.Responder {
|
||||
license, err := getSubscriptionRefreshResponse(session)
|
||||
if err != nil {
|
||||
return operator_api.NewSubscriptionRefreshDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return operator_api.NewSubscriptionRefreshOK().WithPayload(license)
|
||||
})
|
||||
}
|
||||
|
||||
// retrieveLicense returns license from K8S secrets
|
||||
func retrieveLicense(ctx context.Context, sessionToken string) (string, error) {
|
||||
var license string
|
||||
|
||||
// configure kubernetes client
|
||||
clientSet, err := cluster.K8sClient(sessionToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
// Get cluster subscription license
|
||||
license, err = getSubscriptionLicense(ctx, &k8sClient, cluster.Namespace, OperatorSubnetLicenseSecretName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return license, nil
|
||||
}
|
||||
|
||||
// getSubscriptionLicense will retrieve stored license jwt from k8s secret
|
||||
func getSubscriptionLicense(ctx context.Context, clientSet K8sClientI, namespace, secretName string) (string, error) {
|
||||
// retrieve license stored in k8s
|
||||
licenseSecret, err := clientSet.getSecret(ctx, namespace, secretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
license, ok := licenseSecret.Data[ConsoleSubnetLicense]
|
||||
if !ok {
|
||||
LogError("subnet secret does not contain a valid subnet license")
|
||||
return "", restapi.ErrorGeneric
|
||||
}
|
||||
return string(license), nil
|
||||
}
|
||||
|
||||
// addSubscriptionLicenseToTenant replace existing console tenant secret and adds the subnet license key
|
||||
func addSubscriptionLicenseToTenant(ctx context.Context, clientSet K8sClientI, opClient OperatorClientI, license string, tenant *miniov2.Tenant) error {
|
||||
// If Tenant has a configuration secret update the license there and MinIO pods doesn't need to get restarted
|
||||
if tenant.HasConfigurationSecret() {
|
||||
// Update the Tenant Configuration
|
||||
tenantConfigurationSecret, err := clientSet.getSecret(ctx, tenant.Namespace, tenant.Spec.Configuration.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := tenantConfigurationSecret.Data["config.env"]; ok {
|
||||
updatedTenantConfiguration := map[string]string{}
|
||||
tenantConfigurationMap := miniov2.ParseRawConfiguration(tenantConfigurationSecret.Data["config.env"])
|
||||
for key, val := range tenantConfigurationMap {
|
||||
updatedTenantConfiguration[key] = string(val)
|
||||
}
|
||||
updatedTenantConfiguration[MinIOSubnetLicense] = license
|
||||
// removing accesskey & secretkey that are added automatically by parsing function
|
||||
// and are not need it for the tenant itself
|
||||
delete(updatedTenantConfiguration, "accesskey")
|
||||
delete(updatedTenantConfiguration, "secretkey")
|
||||
tenantConfigurationSecret.Data = map[string][]byte{
|
||||
"config.env": []byte(GenerateTenantConfigurationFile(updatedTenantConfiguration)),
|
||||
}
|
||||
_, err = clientSet.updateSecret(ctx, tenant.Namespace, tenantConfigurationSecret, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New("tenant configuration secret has wrong format")
|
||||
}
|
||||
} else {
|
||||
// If configuration file is not present set the license to the container env
|
||||
updatedTenant := tenant.DeepCopy()
|
||||
// reset container env vars
|
||||
updatedTenant.Spec.Env = []corev1.EnvVar{}
|
||||
var licenseIsSet bool
|
||||
for _, env := range tenant.GetEnvVars() {
|
||||
// check if license already exists and override
|
||||
if env.Name == MinIOSubnetLicense {
|
||||
updatedTenant.Spec.Env = append(updatedTenant.Spec.Env, corev1.EnvVar{
|
||||
Name: MinIOSubnetLicense,
|
||||
Value: license,
|
||||
})
|
||||
licenseIsSet = true
|
||||
} else {
|
||||
// copy existing container env variables
|
||||
updatedTenant.Spec.Env = append(updatedTenant.Spec.Env, env)
|
||||
}
|
||||
}
|
||||
// if license didnt exists append it
|
||||
if !licenseIsSet {
|
||||
updatedTenant.Spec.Env = append(updatedTenant.Spec.Env, corev1.EnvVar{
|
||||
Name: MinIOSubnetLicense,
|
||||
Value: license,
|
||||
})
|
||||
}
|
||||
// this will start MinIO pods rolling restart
|
||||
_, err := opClient.TenantUpdate(ctx, updatedTenant, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSubscriptionRefreshResponse(session *models.Principal) (*models.License, *models.Error) {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
licenseKey, err := retrieveLicense(ctx, session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
newLicenseInfo, licenseRaw, err := subscriptionRefresh(client, licenseKey)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
// configure kubernetes client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
// save license key to k8s and restart all console pods
|
||||
if err = saveSubscriptionLicense(ctx, &k8sClient, licenseRaw); err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
||||
}
|
||||
// update license for all existing tenants
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
// iterate over all tenants and update licenses
|
||||
tenants, err := opClient.TenantList(ctx, "", metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
for _, tenant := range tenants.Items {
|
||||
if err = addSubscriptionLicenseToTenant(ctx, &k8sClient, &opClient, licenseRaw, &tenant); err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
}
|
||||
|
||||
return newLicenseInfo, nil
|
||||
}
|
||||
|
||||
// RefreshLicense will check current subnet license and try to renew it
|
||||
func RefreshLicense() error {
|
||||
// Get current license
|
||||
saK8SToken := getK8sSAToken()
|
||||
licenseKey, err := retrieveLicense(context.Background(), saK8SToken)
|
||||
if licenseKey == "" {
|
||||
return errors.New("no license present")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
// Attempt to refresh license
|
||||
_, refreshedLicenseKey, err := subscriptionRefresh(client, licenseKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if refreshedLicenseKey == "" {
|
||||
return errors.New("license expired, please open a support ticket at https://subnet.min.io/")
|
||||
}
|
||||
// store new license in memory for console ui
|
||||
LicenseKey = refreshedLicenseKey
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
clientSet, err := cluster.K8sClient(saK8SToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
return saveSubscriptionLicense(ctx, &k8sClient, refreshedLicenseKey)
|
||||
}
|
||||
|
||||
func subscriptionRefresh(httpClient *cluster.HTTPClient, license string) (*models.License, string, error) {
|
||||
licenseInfo, rawLicense, err := subnet.RefreshLicense(httpClient, license)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return &models.License{
|
||||
Email: licenseInfo.Email,
|
||||
AccountID: licenseInfo.AccountID,
|
||||
StorageCapacity: licenseInfo.StorageCapacity,
|
||||
Plan: licenseInfo.Plan,
|
||||
ExpiresAt: licenseInfo.ExpiresAt.String(),
|
||||
Organization: licenseInfo.Organization,
|
||||
}, rawLicense, nil
|
||||
}
|
||||
|
||||
// saveSubscriptionLicense will create or replace an existing subnet license secret in the k8s cluster
|
||||
func saveSubscriptionLicense(ctx context.Context, clientSet K8sClientI, license string) error {
|
||||
licenseSecret, err := clientSet.getSecret(ctx, cluster.Namespace, OperatorSubnetLicenseSecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if k8serrors.IsNotFound(err) {
|
||||
// Save subnet license in k8s secrets
|
||||
licenseSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
ConsoleSubnetLicense: []byte(license),
|
||||
},
|
||||
}
|
||||
_, err = clientSet.createSecret(ctx, cluster.Namespace, licenseSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// update existing license
|
||||
licenseSecret.Data = map[string][]byte{
|
||||
ConsoleSubnetLicense: []byte(license),
|
||||
}
|
||||
_, err = clientSet.updateSecret(ctx, cluster.Namespace, licenseSecret, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// subscriptionValidate will validate the provided jwt license against the subnet public key
|
||||
func subscriptionValidate(client cluster.HTTPClientI, license, email, password string) (*models.License, string, error) {
|
||||
licenseInfo, rawLicense, err := subnet.ValidateLicense(client, license, email, password)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return &models.License{
|
||||
Email: licenseInfo.Email,
|
||||
AccountID: licenseInfo.AccountID,
|
||||
StorageCapacity: licenseInfo.StorageCapacity,
|
||||
Plan: licenseInfo.Plan,
|
||||
ExpiresAt: licenseInfo.ExpiresAt.String(),
|
||||
Organization: licenseInfo.Organization,
|
||||
}, rawLicense, nil
|
||||
}
|
||||
|
||||
// getSubscriptionValidateResponse
|
||||
func getSubscriptionValidateResponse(session *models.Principal, params *models.SubscriptionValidateRequest) (*models.License, *models.Error) {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
// validate license key
|
||||
licenseInfo, license, err := subscriptionValidate(client, params.License, params.Email, params.Password)
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidLicense, nil, err)
|
||||
}
|
||||
// configure kubernetes client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
if err != nil {
|
||||
return nil, prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
// save license key to k8s
|
||||
if err = saveSubscriptionLicense(ctx, &k8sClient, license); err != nil {
|
||||
return nil, prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
return licenseInfo, nil
|
||||
}
|
||||
|
||||
func getSubscriptionActivateResponse(session *models.Principal, namespace, tenantName string) *models.Error {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(errorGeneric, nil, err)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
tenant, err := getTenant(ctx, &opClient, namespace, tenantName)
|
||||
if err != nil {
|
||||
return prepareError(err, errorGeneric)
|
||||
}
|
||||
// configure kubernetes client
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
// Get cluster subscription license
|
||||
license, err := getSubscriptionLicense(ctx, &k8sClient, cluster.Namespace, OperatorSubnetLicenseSecretName)
|
||||
if err != nil {
|
||||
return prepareError(errInvalidCredentials, nil, err)
|
||||
}
|
||||
// add subscription license to existing console Tenant
|
||||
if err = addSubscriptionLicenseToTenant(ctx, &k8sClient, &opClient, license, tenant); err != nil {
|
||||
return prepareError(err, errorGeneric)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSubscriptionInfoResponse returns information about the current configured subnet license for Console
|
||||
func getSubscriptionInfoResponse(session *models.Principal) (*models.License, *models.Error) {
|
||||
// 20 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
var licenseInfo *models.License
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
licenseKey, err := retrieveLicense(ctx, session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
// validate license key and obtain license info
|
||||
licenseInfo, _, err = subscriptionValidate(client, licenseKey, "", "")
|
||||
if err != nil {
|
||||
return nil, prepareError(errLicenseNotFound, nil, err)
|
||||
}
|
||||
return licenseInfo, nil
|
||||
}
|
||||
@@ -1,359 +0,0 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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 operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
v2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
|
||||
"errors"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func Test_addSubscriptionLicenseToTenant(t *testing.T) {
|
||||
k8sClient := k8sClientMock{}
|
||||
opClient := opClientMock{}
|
||||
tenant := &v2.Tenant{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Spec: v2.TenantSpec{},
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
clientSet K8sClientI
|
||||
opClient OperatorClientI
|
||||
license string
|
||||
tenant *v2.Tenant
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "success updating subscription for tenant with configuration file",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: false,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Configuration = &corev1.LocalObjectReference{
|
||||
Name: "minio-configuration",
|
||||
}
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "minio-configuration",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"config.env": []byte("export MINIO_SUBNET_LICENSE=\"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsZW5pbitjMUBtaW5pby5pbyIsInRlYW1OYW1lIjoiY29uc29sZS1jdXN0b21lciIsImV4cCI6MS42Mzk5NTI2MTE2MDkxNDQ3MzJlOSwiaXNzIjoic3VibmV0QG1pbmlvLmlvIiwiY2FwYWNpdHkiOjI1LCJpYXQiOjEuNjA4NDE2NjExNjA5MTQ0NzMyZTksImFjY291bnRJZCI6MTc2LCJzZXJ2aWNlVHlwZSI6IlNUQU5EQVJEIn0.ndtf8V_FJTvhXeemVLlORyDev6RJaSPhZ2djkMVK9SvXD0srR_qlYJATPjC4NljkS71nXMGVDov5uCTuUL97x6FGQEKDruA-z24x_2Zr8kof4LfBb3HUHudCR8QvE--I\""),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
UpdateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.UpdateOptions) (*corev1.Secret, error) {
|
||||
return nil, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error updating subscription for tenant because cannot get configuration file",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Configuration = &corev1.LocalObjectReference{
|
||||
Name: "minio-configuration",
|
||||
}
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return nil, errors.New("something wrong happened")
|
||||
}
|
||||
UpdateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.UpdateOptions) (*corev1.Secret, error) {
|
||||
return nil, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error updating subscription for tenant because configuration file has wrong format",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Configuration = &corev1.LocalObjectReference{
|
||||
Name: "minio-configuration",
|
||||
}
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "minio-configuration",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"aaaaa": []byte("export MINIO_SUBNET_LICENSE=\"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsZW5pbitjMUBtaW5pby5pbyIsInRlYW1OYW1lIjoiY29uc29sZS1jdXN0b21lciIsImV4cCI6MS42Mzk5NTI2MTE2MDkxNDQ3MzJlOSwiaXNzIjoic3VibmV0QG1pbmlvLmlvIiwiY2FwYWNpdHkiOjI1LCJpYXQiOjEuNjA4NDE2NjExNjA5MTQ0NzMyZTksImFjY291bnRJZCI6MTc2LCJzZXJ2aWNlVHlwZSI6IlNUQU5EQVJEIn0.ndtf8V_FJTvhXeemVLlORyDev6RJaSPhZ2djkMVK9SvXD0srR_qlYJATPjC4NljkS71nXMGVDov5uCTuUL97x6FGQEKDruA-z24x_2Zr8kof4LfBb3HUHudCR8QvE--I\""),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
UpdateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.UpdateOptions) (*corev1.Secret, error) {
|
||||
return nil, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error updating subscription for tenant because cannot update configuration file",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Configuration = &corev1.LocalObjectReference{
|
||||
Name: "minio-configuration",
|
||||
}
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "minio-configuration",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"config.env": []byte("export MINIO_SUBNET_LICENSE=\"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsZW5pbitjMUBtaW5pby5pbyIsInRlYW1OYW1lIjoiY29uc29sZS1jdXN0b21lciIsImV4cCI6MS42Mzk5NTI2MTE2MDkxNDQ3MzJlOSwiaXNzIjoic3VibmV0QG1pbmlvLmlvIiwiY2FwYWNpdHkiOjI1LCJpYXQiOjEuNjA4NDE2NjExNjA5MTQ0NzMyZTksImFjY291bnRJZCI6MTc2LCJzZXJ2aWNlVHlwZSI6IlNUQU5EQVJEIn0.ndtf8V_FJTvhXeemVLlORyDev6RJaSPhZ2djkMVK9SvXD0srR_qlYJATPjC4NljkS71nXMGVDov5uCTuUL97x6FGQEKDruA-z24x_2Zr8kof4LfBb3HUHudCR8QvE--I\""),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
UpdateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.UpdateOptions) (*corev1.Secret, error) {
|
||||
return nil, errors.New("something wrong happened")
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success updating subscription for tenant with env variable",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: false,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Env = []corev1.EnvVar{
|
||||
{
|
||||
Name: "MINIO_SUBNET_LICENSE",
|
||||
Value: "",
|
||||
ValueFrom: nil,
|
||||
},
|
||||
}
|
||||
opClientTenantUpdateMock = func(ctx context.Context, tenant *v2.Tenant, opts metav1.UpdateOptions) (*v2.Tenant, error) {
|
||||
return nil, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error updating subscription for tenant with env variable because of update tenant error",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
opClient: opClient,
|
||||
license: "",
|
||||
tenant: tenant,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
tenant.Spec.Env = []corev1.EnvVar{
|
||||
{
|
||||
Name: "MINIO_SUBNET_LICENSE",
|
||||
Value: "",
|
||||
ValueFrom: nil,
|
||||
},
|
||||
}
|
||||
opClientTenantUpdateMock = func(ctx context.Context, tenant *v2.Tenant, opts metav1.UpdateOptions) (*v2.Tenant, error) {
|
||||
return nil, errors.New("something wrong happened")
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
if err := addSubscriptionLicenseToTenant(tt.args.ctx, tt.args.clientSet, tt.args.opClient, tt.args.license, tt.args.tenant); (err != nil) != tt.wantErr {
|
||||
t.Errorf("addSubscriptionLicenseToTenant() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_saveSubscriptionLicense(t *testing.T) {
|
||||
k8sClient := k8sClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
clientSet K8sClientI
|
||||
license string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error deleting existing secret",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
license: "1111111111",
|
||||
},
|
||||
mockFunc: func() {
|
||||
DeleteSecretMock = func(ctx context.Context, namespace string, name string, opts metav1.DeleteOptions) error {
|
||||
return nil
|
||||
}
|
||||
CreateSecretMock = func(ctx context.Context, namespace string, secret *corev1.Secret, opts metav1.CreateOptions) (*corev1.Secret, error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
if err := saveSubscriptionLicense(tt.args.ctx, tt.args.clientSet, tt.args.license); (err != nil) != tt.wantErr {
|
||||
t.Errorf("saveSubscriptionLicense() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getSubscriptionLicense(t *testing.T) {
|
||||
license := "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJsZW5pbitjMUBtaW5pby5pbyIsInRlYW1OYW1lIjoiY29uc29sZS1jdXN0b21lciIsImV4cCI6MS42Mzk5NTI2MTE2MDkxNDQ3MzJlOSwiaXNzIjoic3VibmV0QG1pbmlvLmlvIiwiY2FwYWNpdHkiOjI1LCJpYXQiOjEuNjA4NDE2NjExNjA5MTQ0NzMyZTksImFjY291bnRJZCI6MTc2LCJzZXJ2aWNlVHlwZSI6IlNUQU5EQVJEIn0.ndtf8V_FJTvhXeemVLlORyDev6RJaSPhZ2djkMVK9SvXD0srR_qlYJATPjC4NljkS71nXMGVDov5uCTuUL97x6FGQEKDruA-z24x_2Zr8kof4LfBb3HUHudCR8QvE--I"
|
||||
k8sClient := k8sClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
clientSet K8sClientI
|
||||
namespace string
|
||||
secretName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error because subscription license doesnt exists",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
namespace: "namespace",
|
||||
secretName: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error because license field doesnt exist in k8s secret",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
namespace: "namespace",
|
||||
secretName: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
imm := true
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
Immutable: &imm,
|
||||
Data: map[string][]byte{
|
||||
//ConsoleSubnetLicense: []byte(license),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "license obtained successfully",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
clientSet: k8sClient,
|
||||
namespace: "namespace",
|
||||
secretName: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
wantErr: false,
|
||||
want: license,
|
||||
mockFunc: func() {
|
||||
k8sclientGetSecretMock = func(ctx context.Context, namespace, secretName string, opts metav1.GetOptions) (*corev1.Secret, error) {
|
||||
imm := true
|
||||
return &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: OperatorSubnetLicenseSecretName,
|
||||
},
|
||||
Immutable: &imm,
|
||||
Data: map[string][]byte{
|
||||
ConsoleSubnetLicense: []byte(license),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := getSubscriptionLicense(tt.args.ctx, tt.args.clientSet, tt.args.namespace, tt.args.secretName)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getSubscriptionLicense() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("getSubscriptionLicense() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -237,6 +238,7 @@ func registerTenantHandlers(api *operations.OperatorAPI) {
|
||||
|
||||
//Get tenant monitoring info
|
||||
api.OperatorAPIGetTenantMonitoringHandler = operator_api.GetTenantMonitoringHandlerFunc(func(params operator_api.GetTenantMonitoringParams, session *models.Principal) middleware.Responder {
|
||||
|
||||
payload, err := getTenantMonitoringResponse(session, params)
|
||||
if err != nil {
|
||||
return operator_api.NewGetTenantMonitoringDefault(int(err.Code)).WithPayload(err)
|
||||
@@ -558,18 +560,6 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
info.IdpOidcEnabled = oidcEnabled
|
||||
info.MinioTLS = minTenant.TLS()
|
||||
|
||||
// obtain current subnet license for tenant (if exists)
|
||||
if license, ok := tenantConfiguration[MinIOSubnetLicense]; ok {
|
||||
client := &cluster.HTTPClient{
|
||||
Client: restapi.GetConsoleHTTPClient(),
|
||||
}
|
||||
licenseInfo, _, _ := subscriptionValidate(client, string(license), "", "")
|
||||
// if licenseInfo is present attach it to the tenantInfo response
|
||||
if licenseInfo != nil {
|
||||
info.SubnetLicense = licenseInfo
|
||||
}
|
||||
}
|
||||
|
||||
// attach status information
|
||||
info.Status = &models.TenantStatus{
|
||||
HealthStatus: string(minTenant.Status.HealthStatus),
|
||||
@@ -1241,13 +1231,6 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
}
|
||||
}
|
||||
|
||||
// If Subnet License is present in k8s secrets, copy that to the MINIO_SUBNET_LICENSE env variable
|
||||
// of the console tenant
|
||||
license, _ := getSubscriptionLicense(ctx, &k8sClient, cluster.Namespace, OperatorSubnetLicenseSecretName)
|
||||
if license != "" {
|
||||
tenantConfigurationENV[MinIOSubnetLicense] = license
|
||||
}
|
||||
|
||||
// add annotations
|
||||
var annotations map[string]string
|
||||
|
||||
@@ -1808,14 +1791,6 @@ func getTenantLogsResponse(session *models.Principal, params operator_api.GetTen
|
||||
minTenant.Spec.Log.Audit = &miniov2.AuditConfig{DiskCapacityGB: swag.Int(0)}
|
||||
}
|
||||
|
||||
/*if minTenant.Spec.Log.Image == "" {
|
||||
minTenant.Spec.Log.Image = miniov2.DefaultLogSearchAPIImage
|
||||
}
|
||||
|
||||
if minTenant.Spec.Log.Db.Image == "" {
|
||||
minTenant.Spec.Log.Db.Image = miniov2.LogPgImage
|
||||
}*/
|
||||
|
||||
retval := &models.TenantLogs{
|
||||
Image: minTenant.Spec.Log.Image,
|
||||
DiskCapacityGB: fmt.Sprintf("%d", *minTenant.Spec.Log.Audit.DiskCapacityGB),
|
||||
@@ -1830,11 +1805,33 @@ func getTenantLogsResponse(session *models.Principal, params operator_api.GetTen
|
||||
DbServiceAccountName: minTenant.Spec.Log.Db.ServiceAccountName,
|
||||
Disabled: false,
|
||||
}
|
||||
|
||||
var requestedCPU string
|
||||
var requestedMem string
|
||||
var requestedDBCPU string
|
||||
var requestedDBMem string
|
||||
if minTenant.Spec.Log.Resources.Requests != nil {
|
||||
requestedCPUQ := minTenant.Spec.Log.Resources.Requests["cpu"]
|
||||
requestedCPU = strconv.FormatInt(requestedCPUQ.Value(), 10)
|
||||
requestedMemQ := minTenant.Spec.Log.Resources.Requests["memory"]
|
||||
requestedMem = strconv.FormatInt(requestedMemQ.Value(), 10)
|
||||
|
||||
requestedDBCPUQ := minTenant.Spec.Log.Db.Resources.Requests["cpu"]
|
||||
requestedDBCPU = strconv.FormatInt(requestedDBCPUQ.Value(), 10)
|
||||
requestedDBMemQ := minTenant.Spec.Log.Db.Resources.Requests["memory"]
|
||||
requestedDBMem = strconv.FormatInt(requestedDBMemQ.Value(), 10)
|
||||
|
||||
retval.LogCPURequest = requestedCPU
|
||||
retval.LogMemRequest = requestedMem
|
||||
retval.LogDBCPURequest = requestedDBCPU
|
||||
retval.LogDBMemRequest = requestedDBMem
|
||||
}
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
// setTenantLogsResponse returns the logs of a tenant
|
||||
func setTenantLogsResponse(session *models.Principal, params operator_api.SetTenantLogsParams) (bool, *models.Error) {
|
||||
|
||||
// 30 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
@@ -1852,6 +1849,7 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
}
|
||||
|
||||
var labels = make(map[string]string)
|
||||
for i := 0; i < len(params.Data.Labels); i++ {
|
||||
if params.Data.Labels[i] != nil {
|
||||
@@ -1873,6 +1871,26 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
}
|
||||
}
|
||||
minTenant.Spec.Log.NodeSelector = nodeSelector
|
||||
logResourceRequest := make(corev1.ResourceList)
|
||||
|
||||
if reflect.TypeOf(params.Data.LogCPURequest).Kind() == reflect.String && params.Data.LogCPURequest != "0Gi" && params.Data.LogCPURequest != "" {
|
||||
cpuQuantity, err := resource.ParseQuantity(params.Data.LogCPURequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
}
|
||||
logResourceRequest["cpu"] = cpuQuantity
|
||||
minTenant.Spec.Log.Resources.Requests = logResourceRequest
|
||||
}
|
||||
if reflect.TypeOf(params.Data.LogMemRequest).Kind() == reflect.String {
|
||||
memQuantity, err := resource.ParseQuantity(params.Data.LogMemRequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
}
|
||||
|
||||
logResourceRequest["memory"] = memQuantity
|
||||
minTenant.Spec.Log.Resources.Requests = logResourceRequest
|
||||
}
|
||||
|
||||
modified := false
|
||||
if minTenant.Spec.Log.Db != nil {
|
||||
modified = true
|
||||
@@ -1898,6 +1916,24 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
}
|
||||
modified = true
|
||||
}
|
||||
|
||||
logDBResourceRequest := make(corev1.ResourceList)
|
||||
if reflect.TypeOf(params.Data.LogDBCPURequest).Kind() == reflect.String && params.Data.LogDBCPURequest != "0Gi" && params.Data.LogDBCPURequest != "" {
|
||||
dbCPUQuantity, err := resource.ParseQuantity(params.Data.LogDBCPURequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
}
|
||||
logDBResourceRequest["cpu"] = dbCPUQuantity
|
||||
minTenant.Spec.Log.Db.Resources.Requests = logDBResourceRequest
|
||||
}
|
||||
if reflect.TypeOf(params.Data.LogDBMemRequest).Kind() == reflect.String {
|
||||
dbMemQuantity, err := resource.ParseQuantity(params.Data.LogDBMemRequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
}
|
||||
logDBResourceRequest["memory"] = dbMemQuantity
|
||||
minTenant.Spec.Log.Db.Resources.Requests = logDBResourceRequest
|
||||
}
|
||||
minTenant.Spec.Log.Image = params.Data.Image
|
||||
diskCapacityGB, err := strconv.Atoi(params.Data.DiskCapacityGB)
|
||||
if err == nil {
|
||||
@@ -1941,6 +1977,9 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
NodeSelector: dbNodeSelector,
|
||||
Image: params.Data.DbImage,
|
||||
ServiceAccountName: params.Data.DbServiceAccountName,
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: minTenant.Spec.Log.Db.Resources.Requests,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
minTenant.Spec.Log.Db.Labels = dbLabels
|
||||
@@ -1950,6 +1989,7 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
minTenant.Spec.Log.Db.ServiceAccountName = params.Data.DbServiceAccountName
|
||||
}
|
||||
}
|
||||
|
||||
_, err = opClient.TenantUpdate(ctx, minTenant, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
@@ -2069,9 +2109,13 @@ func getTenantPodsResponse(session *models.Principal, params operator_api.GetTen
|
||||
if len(pod.Status.ContainerStatuses) > 0 {
|
||||
restarts = int64(pod.Status.ContainerStatuses[0].RestartCount)
|
||||
}
|
||||
status := string(pod.Status.Phase)
|
||||
if pod.DeletionTimestamp != nil {
|
||||
status = "Terminating"
|
||||
}
|
||||
retval = append(retval, &models.TenantPod{
|
||||
Name: swag.String(pod.Name),
|
||||
Status: string(pod.Status.Phase),
|
||||
Status: status,
|
||||
TimeCreated: pod.CreationTimestamp.Unix(),
|
||||
PodIP: pod.Status.PodIP,
|
||||
Restarts: restarts,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
@@ -19,6 +19,7 @@ package operatorapi
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1"
|
||||
|
||||
@@ -58,6 +59,17 @@ func registerVolumesHandlers(api *operations.OperatorAPI) {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
api.OperatorAPIGetPVCEventsHandler = operator_api.GetPVCEventsHandlerFunc(func(params operator_api.GetPVCEventsParams, session *models.Principal) middleware.Responder {
|
||||
payload, err := getPVCEventsResponse(session, params)
|
||||
|
||||
if err != nil {
|
||||
return operator_api.NewGetPVCEventsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return operator_api.NewGetPVCEventsOK().WithPayload(payload)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *models.Error) {
|
||||
@@ -83,12 +95,16 @@ func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *mode
|
||||
var ListPVCs []*models.PvcsListResponse
|
||||
|
||||
for _, pvc := range listAllPvcs.Items {
|
||||
status := string(pvc.Status.Phase)
|
||||
if pvc.DeletionTimestamp != nil {
|
||||
status = "Terminating"
|
||||
}
|
||||
pvcResponse := models.PvcsListResponse{
|
||||
Name: pvc.Name,
|
||||
Age: pvc.CreationTimestamp.String(),
|
||||
Capacity: pvc.Status.Capacity.Storage().String(),
|
||||
Namespace: pvc.Namespace,
|
||||
Status: string(pvc.Status.Phase),
|
||||
Status: status,
|
||||
StorageClass: *pvc.Spec.StorageClassName,
|
||||
Volume: pvc.Spec.VolumeName,
|
||||
Tenant: pvc.Labels["v1.min.io/tenant"],
|
||||
@@ -126,12 +142,16 @@ func getPVCsForTenantResponse(session *models.Principal, params operator_api.Lis
|
||||
var ListPVCs []*models.PvcsListResponse
|
||||
|
||||
for _, pvc := range listAllPvcs.Items {
|
||||
status := string(pvc.Status.Phase)
|
||||
if pvc.DeletionTimestamp != nil {
|
||||
status = "Terminating"
|
||||
}
|
||||
pvcResponse := models.PvcsListResponse{
|
||||
Name: pvc.Name,
|
||||
Age: pvc.CreationTimestamp.String(),
|
||||
Capacity: pvc.Status.Capacity.Storage().String(),
|
||||
Namespace: pvc.Namespace,
|
||||
Status: string(pvc.Status.Phase),
|
||||
Status: status,
|
||||
StorageClass: *pvc.Spec.StorageClassName,
|
||||
Volume: pvc.Spec.VolumeName,
|
||||
Tenant: pvc.Labels["v1.min.io/tenant"],
|
||||
@@ -162,3 +182,33 @@ func getDeletePVCResponse(session *models.Principal, params operator_api.DeleteP
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPVCEventsResponse(session *models.Principal, params operator_api.GetPVCEventsParams) (models.EventListWrapper, *models.Error) {
|
||||
ctx := context.Background()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
PVC, err := clientset.CoreV1().PersistentVolumeClaims(params.Namespace).Get(ctx, params.PVCName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
events, err := clientset.CoreV1().Events(params.Namespace).List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.uid=%s", PVC.UID)})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
retval := models.EventListWrapper{}
|
||||
for i := 0; i < len(events.Items); i++ {
|
||||
retval = append(retval, &models.EventListElement{
|
||||
Namespace: events.Items[i].Namespace,
|
||||
LastSeen: events.Items[i].LastTimestamp.Unix(),
|
||||
Message: events.Items[i].Message,
|
||||
EventType: events.Items[i].Type,
|
||||
Reason: events.Items[i].Reason,
|
||||
})
|
||||
}
|
||||
sort.SliceStable(retval, func(i int, j int) bool {
|
||||
return retval[i].LastSeen < retval[j].LastSeen
|
||||
})
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
// 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 operatorapi
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
// 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 kes
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
@@ -20,15 +20,9 @@ import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/minio/pkg/env"
|
||||
"github.com/minio/pkg/licverifier"
|
||||
)
|
||||
|
||||
// GetSubnetURL
|
||||
func GetSubnetURL() string {
|
||||
return env.Get(ConsoleSubnetURL, "https://subnet.min.io")
|
||||
}
|
||||
|
||||
// GetLicenseInfoFromJWT will return license metadata from a jwt string license
|
||||
func GetLicenseInfoFromJWT(license string, publicKeys []string) (*licverifier.LicenseInfo, error) {
|
||||
if license == "" {
|
||||
@@ -49,3 +43,15 @@ func GetLicenseInfoFromJWT(license string, publicKeys []string) (*licverifier.Li
|
||||
}
|
||||
return nil, errors.New("invalid license key")
|
||||
}
|
||||
|
||||
// MfaReq - JSON payload of the SUBNET mfa api
|
||||
type MfaReq struct {
|
||||
Username string `json:"username"`
|
||||
OTP string `json:"otp"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type LoginResp struct {
|
||||
AccessToken string
|
||||
MfaToken string
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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
|
||||
@@ -29,9 +29,4 @@ JkO2PfyyAYEO/5dBlPh1Undu9WQl6J7B
|
||||
const (
|
||||
// Constants for subnet configuration
|
||||
ConsoleSubnetURL = "CONSOLE_SUBNET_URL"
|
||||
// Subnet endpoints
|
||||
publicKey = "/downloads/license-pubkey.pem"
|
||||
loginEndpoint = "/api/auth/login"
|
||||
refreshLicenseKeyEndpoint = "/api/auth/subscription/renew-license"
|
||||
licenseKeyEndpoint = "/api/auth/subscription/license-key"
|
||||
)
|
||||
|
||||
@@ -22,150 +22,115 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/pkg/licverifier"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go"
|
||||
mc "github.com/minio/mc/cmd"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/pkg/licverifier"
|
||||
)
|
||||
|
||||
// subnetLoginRequest body request for subnet login
|
||||
type subnetLoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
func LoginWithMFA(client cluster.HTTPClientI, username, mfaToken, otp string) (*LoginResp, error) {
|
||||
mfaLoginReq := MfaReq{Username: username, OTP: otp, Token: mfaToken}
|
||||
resp, err := subnetPostReq(client, subnetMFAURL(), mfaLoginReq, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token := gjson.Get(resp, "token_info.access_token")
|
||||
if token.Exists() {
|
||||
return &LoginResp{AccessToken: token.String(), MfaToken: ""}, nil
|
||||
}
|
||||
return nil, errors.New("access token not found in response")
|
||||
}
|
||||
|
||||
// tokenInfo
|
||||
type tokenInfo struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn float64 `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
func Login(client cluster.HTTPClientI, username, password string) (*LoginResp, error) {
|
||||
loginReq := map[string]string{
|
||||
"username": username,
|
||||
"password": password,
|
||||
}
|
||||
respStr, err := subnetPostReq(client, subnetLoginURL(), loginReq, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mfaRequired := gjson.Get(respStr, "mfa_required").Bool()
|
||||
if mfaRequired {
|
||||
mfaToken := gjson.Get(respStr, "mfa_token").String()
|
||||
if mfaToken == "" {
|
||||
return nil, errors.New("missing mfa token")
|
||||
}
|
||||
return &LoginResp{AccessToken: "", MfaToken: mfaToken}, nil
|
||||
}
|
||||
token := gjson.Get(respStr, "token_info.access_token")
|
||||
if token.Exists() {
|
||||
return &LoginResp{AccessToken: token.String(), MfaToken: ""}, nil
|
||||
}
|
||||
return nil, errors.New("access token not found in response")
|
||||
}
|
||||
|
||||
// subnetLoginResponse body resonse from subnet after login
|
||||
type subnetLoginResponse struct {
|
||||
HasMembership bool `json:"has_memberships"`
|
||||
TokenInfo tokenInfo `json:"token_info"`
|
||||
func GetOrganizations(client cluster.HTTPClientI, token string) ([]*models.SubnetOrganization, error) {
|
||||
headers := subnetAuthHeaders(token)
|
||||
respStr, err := subnetGetReq(client, subnetOrgsURL(), headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var organizations []*models.SubnetOrganization
|
||||
if err = json.Unmarshal([]byte(respStr), &organizations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return organizations, nil
|
||||
}
|
||||
|
||||
// LicenseMetadata claims in subnet license
|
||||
type LicenseMetadata struct {
|
||||
Email string `json:"email"`
|
||||
Issuer string `json:"issuer"`
|
||||
TeamName string `json:"teamName"`
|
||||
ServiceType string `json:"serviceType"`
|
||||
RequestedAt string `json:"requestedAt"`
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
AccountID int64 `json:"accountId"`
|
||||
Capacity int64 `json:"capacity"`
|
||||
type LicenseTokenConfig struct {
|
||||
APIKey string
|
||||
License string
|
||||
Proxy string
|
||||
}
|
||||
|
||||
// subnetLicenseResponse body response returned by subnet license endpoint
|
||||
type subnetLicenseResponse struct {
|
||||
License string `json:"license"`
|
||||
Metadata LicenseMetadata `json:"metadata"`
|
||||
func Register(client cluster.HTTPClientI, admInfo madmin.InfoMessage, apiKey, token, accountID string) (*LicenseTokenConfig, error) {
|
||||
var headers map[string]string
|
||||
regInfo := GetClusterRegInfo(admInfo)
|
||||
regURL := subnetRegisterURL()
|
||||
if apiKey != "" {
|
||||
regURL += "?api_key=" + apiKey
|
||||
} else {
|
||||
if accountID == "" || token == "" {
|
||||
return nil, errors.New("missing accountID or authentication token")
|
||||
}
|
||||
headers = subnetAuthHeaders(token)
|
||||
regURL += "?aid=" + accountID
|
||||
}
|
||||
regToken, err := GenerateRegToken(regInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reqPayload := mc.ClusterRegistrationReq{Token: regToken}
|
||||
resp, err := subnetPostReq(client, regURL, reqPayload, headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
respJSON := gjson.Parse(resp)
|
||||
subnetAPIKey := respJSON.Get("api_key").String()
|
||||
licenseJwt := respJSON.Get("license").String()
|
||||
|
||||
if subnetAPIKey != "" || licenseJwt != "" {
|
||||
return &LicenseTokenConfig{
|
||||
APIKey: subnetAPIKey,
|
||||
License: licenseJwt,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.New("subnet api key not found")
|
||||
}
|
||||
|
||||
// subnetLoginRequest body request for subnet login
|
||||
type subnetRefreshRequest struct {
|
||||
License string `json:"license"`
|
||||
}
|
||||
|
||||
// getNewLicenseFromExistingLicense will perform license refresh based on the provided license key
|
||||
func getNewLicenseFromExistingLicense(client cluster.HTTPClientI, licenseKey string) (string, error) {
|
||||
request := subnetRefreshRequest{
|
||||
License: licenseKey,
|
||||
}
|
||||
// http body for login request
|
||||
payloadBytes, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetURL := GetSubnetURL()
|
||||
url := fmt.Sprintf("%s%s", subnetURL, refreshLicenseKeyEndpoint)
|
||||
resp, err := client.Post(url, "application/json", bytes.NewReader(payloadBytes))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetLicense := &subnetLicenseResponse{}
|
||||
// Parse subnet login response
|
||||
err = json.Unmarshal(bodyBytes, subnetLicense)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return subnetLicense.License, nil
|
||||
}
|
||||
|
||||
// getLicenseFromCredentials will perform authentication against subnet using
|
||||
// user provided credentials and return the current subnet license key
|
||||
func getLicenseFromCredentials(client cluster.HTTPClientI, username, password string) (string, error) {
|
||||
request := subnetLoginRequest{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
// http body for login request
|
||||
payloadBytes, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetURL := GetSubnetURL()
|
||||
url := fmt.Sprintf("%s%s", subnetURL, loginEndpoint)
|
||||
// Authenticate against subnet using email/password provided by user
|
||||
resp, err := client.Post(url, "application/json", bytes.NewReader(payloadBytes))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
subnetSession := &subnetLoginResponse{}
|
||||
// Parse subnet login response
|
||||
err = json.Unmarshal(bodyBytes, subnetSession)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Get license key using session token
|
||||
token := subnetSession.TokenInfo.AccessToken
|
||||
url = fmt.Sprintf("%s%s", subnetURL, licenseKeyEndpoint)
|
||||
req, err := http.NewRequest("POST", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
bodyBytes, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return "", fmt.Errorf("subnet served returned status %d code", resp.StatusCode)
|
||||
}
|
||||
userLicense := &subnetLicenseResponse{}
|
||||
// Parse subnet license response
|
||||
err = json.Unmarshal(bodyBytes, userLicense)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return userLicense.License, nil
|
||||
}
|
||||
const publicKey = "/downloads/license-pubkey.pem"
|
||||
|
||||
// downloadSubnetPublicKey will download the current subnet public key.
|
||||
func downloadSubnetPublicKey(client cluster.HTTPClientI) (string, error) {
|
||||
// Get the public key directly from Subnet
|
||||
url := fmt.Sprintf("%s%s", GetSubnetURL(), publicKey)
|
||||
url := fmt.Sprintf("%s%s", subnetBaseURL(), publicKey)
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -179,21 +144,10 @@ func downloadSubnetPublicKey(client cluster.HTTPClientI) (string, error) {
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
// ValidateLicense will download the current subnet public key, if the public key its not available for license
|
||||
// verification then console will fall back to verification with hardcoded public keys
|
||||
func ValidateLicense(client cluster.HTTPClientI, licenseKey, email, password string) (licInfo *licverifier.LicenseInfo, license string, err error) {
|
||||
// ParseLicense parses the license with the bundle public key and return it's information
|
||||
func ParseLicense(client cluster.HTTPClientI, license string) (*licverifier.LicenseInfo, error) {
|
||||
var publicKeys []string
|
||||
if email != "" && password != "" {
|
||||
// fetch subnet license key using user credentials
|
||||
license, err = getLicenseFromCredentials(client, email, password)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
} else if licenseKey != "" {
|
||||
license = licenseKey
|
||||
} else {
|
||||
return nil, "", errors.New("invalid license")
|
||||
}
|
||||
|
||||
subnetPubKey, err := downloadSubnetPublicKey(client)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@@ -203,24 +157,11 @@ func ValidateLicense(client cluster.HTTPClientI, licenseKey, email, password str
|
||||
} else {
|
||||
publicKeys = append(publicKeys, subnetPubKey)
|
||||
}
|
||||
licInfo, err = GetLicenseInfoFromJWT(license, publicKeys)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return licInfo, license, nil
|
||||
}
|
||||
|
||||
func RefreshLicense(client cluster.HTTPClientI, licenseKey string) (licInfo *licverifier.LicenseInfo, license string, err error) {
|
||||
if licenseKey != "" {
|
||||
license, err = getNewLicenseFromExistingLicense(client, licenseKey)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
licenseInfo, rawLicense, err := ValidateLicense(client, license, "", "")
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return licenseInfo, rawLicense, nil
|
||||
licenseInfo, err := GetLicenseInfoFromJWT(license, publicKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, "", errors.New("invalid license")
|
||||
|
||||
return licenseInfo, nil
|
||||
}
|
||||
|
||||
@@ -1,332 +0,0 @@
|
||||
// This file is part of MinIO Kubernetes Cloud
|
||||
// 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 subnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"errors"
|
||||
)
|
||||
|
||||
var HTTPGetMock func(url string) (resp *http.Response, err error)
|
||||
var HTTPPostMock func(url, contentType string, body io.Reader) (resp *http.Response, err error)
|
||||
var HTTPDoMock func(req *http.Request) (*http.Response, error)
|
||||
|
||||
type HTTPClientMock struct {
|
||||
Client *http.Client
|
||||
}
|
||||
|
||||
func (c *HTTPClientMock) Get(url string) (resp *http.Response, err error) {
|
||||
return HTTPGetMock(url)
|
||||
}
|
||||
|
||||
func (c *HTTPClientMock) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return HTTPPostMock(url, contentType, body)
|
||||
}
|
||||
|
||||
func (c *HTTPClientMock) Do(req *http.Request) (*http.Response, error) {
|
||||
return HTTPDoMock(req)
|
||||
}
|
||||
|
||||
func Test_getLicenseFromCredentials(t *testing.T) {
|
||||
// HTTP Client mock
|
||||
clientMock := HTTPClientMock{
|
||||
Client: &http.Client{},
|
||||
}
|
||||
type args struct {
|
||||
client HTTPClientMock
|
||||
username string
|
||||
password string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error when login against subnet",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "invalid",
|
||||
password: "invalid",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error because of malformed subnet response",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "invalid",
|
||||
password: "invalid",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("foo")))}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error when obtaining license from subnet",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "valid",
|
||||
password: "valid",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
// returning test jwt token
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("{\"has_memberships\":true,\"token_info\":{\"access_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik4wRXdOa1V5UXpORU1UUkNOekU0UmpSR1JVWkJSa1UxUmtZNE9EY3lOekZHTXpjNU1qZ3hNZyJ9.eyJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2dyb3VwcyI6W10sImh0dHBzOi8vaWQuc3VibmV0Lm1pbi5pby9jbGFpbXMvcm9sZXMiOltdLCJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2VtYWlsIjoibGVuaW4rYzFAbWluaW8uaW8iLCJpc3MiOiJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vIiwic3ViIjoiYXV0aDB8NWZjZWFlYTMyNTNhZjEwMDc3NDZkMDM0IiwiYXVkIjoiaHR0cHM6Ly9zdWJuZXQubWluLmlvL2FwaSIsImlhdCI6MTYwODQxNjE5NiwiZXhwIjoxNjExMDA4MTk2LCJhenAiOiI1WTA0eVZlejNiOFgxUFVzRHVqSmxuZXVuY3ExVjZxaiIsInNjb3BlIjoib2ZmbGluZV9hY2Nlc3MiLCJndHkiOiJwYXNzd29yZCJ9.GC8DRLT0jUEteuBZBmyMXMswLSblCr_89Gu5NcVRUzKSYAaZ5VFW4UFgo1BpiC0sePuWJ0Vykitphx7znTfZfj5B3mZbOw3ejG6kxz7nm9DuYMmySJFYnwroZ9EP02vkW7-n_-YvEg8le1wXfkJ3lTUzO3aWddS4rfQRsZ2YJJUj61GiNyEK_QNP4PrYOuzLyD1wV75NejFqfcFoj7nRkT1K2BM0-89-_f2AFDGTjov6Ig6s1s-zLC9wxcYSmubNwpCJytZmQgPqIepOr065Y6OB4n0n0B5sXguuGuzb8VAkECrHhHPz8ta926fc0jC4XxVCNKdbV1_qC3-1yY7AJA\",\"expires_in\":2592000.0,\"token_type\":\"Bearer\"}}")))}, nil
|
||||
}
|
||||
HTTPDoMock = func(req *http.Request) (*http.Response, error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error when obtaining license from subnet because of malformed response",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "valid",
|
||||
password: "valid",
|
||||
},
|
||||
want: "",
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
// returning test jwt token
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("{\"has_memberships\":true,\"token_info\":{\"access_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik4wRXdOa1V5UXpORU1UUkNOekU0UmpSR1JVWkJSa1UxUmtZNE9EY3lOekZHTXpjNU1qZ3hNZyJ9.eyJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2dyb3VwcyI6W10sImh0dHBzOi8vaWQuc3VibmV0Lm1pbi5pby9jbGFpbXMvcm9sZXMiOltdLCJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2VtYWlsIjoibGVuaW4rYzFAbWluaW8uaW8iLCJpc3MiOiJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vIiwic3ViIjoiYXV0aDB8NWZjZWFlYTMyNTNhZjEwMDc3NDZkMDM0IiwiYXVkIjoiaHR0cHM6Ly9zdWJuZXQubWluLmlvL2FwaSIsImlhdCI6MTYwODQxNjE5NiwiZXhwIjoxNjExMDA4MTk2LCJhenAiOiI1WTA0eVZlejNiOFgxUFVzRHVqSmxuZXVuY3ExVjZxaiIsInNjb3BlIjoib2ZmbGluZV9hY2Nlc3MiLCJndHkiOiJwYXNzd29yZCJ9.GC8DRLT0jUEteuBZBmyMXMswLSblCr_89Gu5NcVRUzKSYAaZ5VFW4UFgo1BpiC0sePuWJ0Vykitphx7znTfZfj5B3mZbOw3ejG6kxz7nm9DuYMmySJFYnwroZ9EP02vkW7-n_-YvEg8le1wXfkJ3lTUzO3aWddS4rfQRsZ2YJJUj61GiNyEK_QNP4PrYOuzLyD1wV75NejFqfcFoj7nRkT1K2BM0-89-_f2AFDGTjov6Ig6s1s-zLC9wxcYSmubNwpCJytZmQgPqIepOr065Y6OB4n0n0B5sXguuGuzb8VAkECrHhHPz8ta926fc0jC4XxVCNKdbV1_qC3-1yY7AJA\",\"expires_in\":2592000.0,\"token_type\":\"Bearer\"}}")))}, nil
|
||||
}
|
||||
HTTPDoMock = func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("foo")))}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "license obtained successfully",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
username: "valid",
|
||||
password: "valid",
|
||||
},
|
||||
want: license,
|
||||
wantErr: false,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
// returning test jwt token
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("{\"has_memberships\":true,\"token_info\":{\"access_token\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik4wRXdOa1V5UXpORU1UUkNOekU0UmpSR1JVWkJSa1UxUmtZNE9EY3lOekZHTXpjNU1qZ3hNZyJ9.eyJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2dyb3VwcyI6W10sImh0dHBzOi8vaWQuc3VibmV0Lm1pbi5pby9jbGFpbXMvcm9sZXMiOltdLCJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vY2xhaW1zL2VtYWlsIjoibGVuaW4rYzFAbWluaW8uaW8iLCJpc3MiOiJodHRwczovL2lkLnN1Ym5ldC5taW4uaW8vIiwic3ViIjoiYXV0aDB8NWZjZWFlYTMyNTNhZjEwMDc3NDZkMDM0IiwiYXVkIjoiaHR0cHM6Ly9zdWJuZXQubWluLmlvL2FwaSIsImlhdCI6MTYwODQxNjE5NiwiZXhwIjoxNjExMDA4MTk2LCJhenAiOiI1WTA0eVZlejNiOFgxUFVzRHVqSmxuZXVuY3ExVjZxaiIsInNjb3BlIjoib2ZmbGluZV9hY2Nlc3MiLCJndHkiOiJwYXNzd29yZCJ9.GC8DRLT0jUEteuBZBmyMXMswLSblCr_89Gu5NcVRUzKSYAaZ5VFW4UFgo1BpiC0sePuWJ0Vykitphx7znTfZfj5B3mZbOw3ejG6kxz7nm9DuYMmySJFYnwroZ9EP02vkW7-n_-YvEg8le1wXfkJ3lTUzO3aWddS4rfQRsZ2YJJUj61GiNyEK_QNP4PrYOuzLyD1wV75NejFqfcFoj7nRkT1K2BM0-89-_f2AFDGTjov6Ig6s1s-zLC9wxcYSmubNwpCJytZmQgPqIepOr065Y6OB4n0n0B5sXguuGuzb8VAkECrHhHPz8ta926fc0jC4XxVCNKdbV1_qC3-1yY7AJA\",\"expires_in\":2592000.0,\"token_type\":\"Bearer\"}}"))),
|
||||
}, nil
|
||||
}
|
||||
HTTPDoMock = func(req *http.Request) (*http.Response, error) {
|
||||
// returning test jwt license
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(bytes.NewReader([]byte("{\"license\":\"" + license + "\",\"metadata\":{\"email\":\"lenin+c1@minio.io\",\"issuer\":\"subnet@minio.io\",\"accountId\":176,\"teamName\":\"console-customer\",\"serviceType\":\"STANDARD\",\"capacity\":25,\"requestedAt\":\"2020-12-19T22:23:31.609144732Z\",\"expiresAt\":\"2021-12-19T22:23:31.609144732Z\"}}"))),
|
||||
}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
got, err := getLicenseFromCredentials(&tt.args.client, tt.args.username, tt.args.password)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getLicenseFromCredentials() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("getLicenseFromCredentials() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_downloadSubnetPublicKey(t *testing.T) {
|
||||
// HTTP Client mock
|
||||
clientMock := HTTPClientMock{
|
||||
Client: &http.Client{},
|
||||
}
|
||||
type args struct {
|
||||
client HTTPClientMock
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error downloading public key",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
},
|
||||
mockFunc: func() {
|
||||
HTTPGetMock = func(url string) (resp *http.Response, err error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
wantErr: true,
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "public key download successfully",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
},
|
||||
mockFunc: func() {
|
||||
HTTPGetMock = func(url string) (resp *http.Response, err error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("foo")))}, nil
|
||||
}
|
||||
},
|
||||
wantErr: false,
|
||||
want: "foo",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
got, err := downloadSubnetPublicKey(&tt.args.client)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("downloadSubnetPublicKey() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("downloadSubnetPublicKey() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateLicense(t *testing.T) {
|
||||
// HTTP Client mock
|
||||
clientMock := HTTPClientMock{
|
||||
Client: &http.Client{},
|
||||
}
|
||||
type args struct {
|
||||
client HTTPClientMock
|
||||
licenseKey string
|
||||
email string
|
||||
password string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantLicense string
|
||||
wantErr bool
|
||||
mockFunc func()
|
||||
}{
|
||||
{
|
||||
name: "error because nor license nor user or password was provided",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
licenseKey: "",
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "error because could not get license from credentials",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
licenseKey: "",
|
||||
email: "email",
|
||||
password: "password",
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPPostMock = func(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return nil, errors.New("something went wrong")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error because invalid license",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
licenseKey: "invalid license",
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
wantErr: true,
|
||||
mockFunc: func() {
|
||||
HTTPGetMock = func(url string) (resp *http.Response, err error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(strings.NewReader(publicKeys[0]))}, nil
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "license validated successfully",
|
||||
args: args{
|
||||
client: clientMock,
|
||||
licenseKey: license,
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
wantErr: false,
|
||||
mockFunc: func() {
|
||||
HTTPGetMock = func(url string) (resp *http.Response, err error) {
|
||||
return &http.Response{Body: ioutil.NopCloser(strings.NewReader(publicKeys[0]))}, nil
|
||||
}
|
||||
},
|
||||
wantLicense: license,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.mockFunc != nil {
|
||||
tt.mockFunc()
|
||||
}
|
||||
_, gotLicense, err := ValidateLicense(&tt.args.client, tt.args.licenseKey, tt.args.email, tt.args.password)
|
||||
if !tt.wantErr {
|
||||
t.Skip() // FIXME: fix all success cases
|
||||
}
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ValidateLicense() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotLicense != tt.wantLicense {
|
||||
t.Errorf("ValidateLicense() gotLicense = %v, want %v", gotLicense, tt.wantLicense)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
162
pkg/subnet/utils.go
Normal file
162
pkg/subnet/utils.go
Normal file
@@ -0,0 +1,162 @@
|
||||
// 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 subnet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/madmin-go"
|
||||
mc "github.com/minio/mc/cmd"
|
||||
"github.com/minio/pkg/env"
|
||||
)
|
||||
|
||||
const (
|
||||
subnetRespBodyLimit = 1 << 20 // 1 MiB
|
||||
)
|
||||
|
||||
func subnetBaseURL() string {
|
||||
return env.Get(ConsoleSubnetURL, "https://subnet.min.io")
|
||||
}
|
||||
|
||||
func subnetRegisterURL() string {
|
||||
return subnetBaseURL() + "/api/cluster/register"
|
||||
}
|
||||
|
||||
func subnetLoginURL() string {
|
||||
return subnetBaseURL() + "/api/auth/login"
|
||||
}
|
||||
|
||||
func subnetOrgsURL() string {
|
||||
return subnetBaseURL() + "/api/auth/organizations"
|
||||
}
|
||||
|
||||
func subnetMFAURL() string {
|
||||
return subnetBaseURL() + "/api/auth/mfa-login"
|
||||
}
|
||||
|
||||
func GenerateRegToken(clusterRegInfo mc.ClusterRegistrationInfo) (string, error) {
|
||||
token, e := json.Marshal(clusterRegInfo)
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(token), nil
|
||||
}
|
||||
|
||||
func subnetAuthHeaders(authToken string) map[string]string {
|
||||
return map[string]string{"Authorization": "Bearer " + authToken}
|
||||
}
|
||||
|
||||
func httpDo(client cluster.HTTPClientI, req *http.Request) (*http.Response, error) {
|
||||
return client.Do(req)
|
||||
}
|
||||
|
||||
func subnetReqDo(client cluster.HTTPClientI, r *http.Request, headers map[string]string) (string, error) {
|
||||
for k, v := range headers {
|
||||
r.Header.Add(k, v)
|
||||
}
|
||||
|
||||
ct := r.Header.Get("Content-Type")
|
||||
if len(ct) == 0 {
|
||||
r.Header.Add("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
resp, e := httpDo(client, r)
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
respBytes, e := ioutil.ReadAll(io.LimitReader(resp.Body, subnetRespBodyLimit))
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
respStr := string(respBytes)
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
return respStr, nil
|
||||
}
|
||||
return respStr, fmt.Errorf("Request failed with code %d and error: %s", resp.StatusCode, respStr)
|
||||
}
|
||||
|
||||
func subnetGetReq(client cluster.HTTPClientI, reqURL string, headers map[string]string) (string, error) {
|
||||
r, e := http.NewRequest(http.MethodGet, reqURL, nil)
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
return subnetReqDo(client, r, headers)
|
||||
}
|
||||
|
||||
func subnetPostReq(client cluster.HTTPClientI, reqURL string, payload interface{}, headers map[string]string) (string, error) {
|
||||
body, e := json.Marshal(payload)
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
r, e := http.NewRequest(http.MethodPost, reqURL, bytes.NewReader(body))
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
return subnetReqDo(client, r, headers)
|
||||
}
|
||||
|
||||
func GetClusterRegInfo(admInfo madmin.InfoMessage) mc.ClusterRegistrationInfo {
|
||||
noOfPools := 1
|
||||
noOfDrives := 0
|
||||
for _, srvr := range admInfo.Servers {
|
||||
if srvr.PoolNumber > noOfPools {
|
||||
noOfPools = srvr.PoolNumber
|
||||
}
|
||||
noOfDrives += len(srvr.Disks)
|
||||
}
|
||||
|
||||
totalSpace, usedSpace := getDriveSpaceInfo(admInfo)
|
||||
|
||||
return mc.ClusterRegistrationInfo{
|
||||
DeploymentID: admInfo.DeploymentID,
|
||||
ClusterName: admInfo.DeploymentID,
|
||||
UsedCapacity: admInfo.Usage.Size,
|
||||
Info: mc.ClusterInfo{
|
||||
MinioVersion: admInfo.Servers[0].Version,
|
||||
NoOfServerPools: noOfPools,
|
||||
NoOfServers: len(admInfo.Servers),
|
||||
NoOfDrives: noOfDrives,
|
||||
TotalDriveSpace: totalSpace,
|
||||
UsedDriveSpace: usedSpace,
|
||||
NoOfBuckets: admInfo.Buckets.Count,
|
||||
NoOfObjects: admInfo.Objects.Count,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getDriveSpaceInfo(admInfo madmin.InfoMessage) (uint64, uint64) {
|
||||
total := uint64(0)
|
||||
used := uint64(0)
|
||||
for _, srvr := range admInfo.Servers {
|
||||
for _, d := range srvr.Disks {
|
||||
total += d.TotalSpace
|
||||
used += d.UsedSpace
|
||||
}
|
||||
}
|
||||
return total, used
|
||||
}
|
||||
@@ -1,7 +1,18 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// All rights reserved
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package utils
|
||||
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
// 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 portalui
|
||||
|
||||
import "embed"
|
||||
|
||||
@@ -1,315 +1,321 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.c4c1effe.css",
|
||||
"main.js": "./static/js/main.e413bbf1.js",
|
||||
"static/js/2178.b2fb43c5.chunk.js": "./static/js/2178.b2fb43c5.chunk.js",
|
||||
"main.js": "./static/js/main.c7ee1a1d.js",
|
||||
"static/js/2178.cf1507fb.chunk.js": "./static/js/2178.cf1507fb.chunk.js",
|
||||
"static/js/5282.e51b8c66.chunk.js": "./static/js/5282.e51b8c66.chunk.js",
|
||||
"static/js/2818.d1afde1a.chunk.js": "./static/js/2818.d1afde1a.chunk.js",
|
||||
"static/js/9560.e48c4920.chunk.js": "./static/js/9560.e48c4920.chunk.js",
|
||||
"static/js/9661.f854f848.chunk.js": "./static/js/9661.f854f848.chunk.js",
|
||||
"static/js/9330.3d1e16a2.chunk.js": "./static/js/9330.3d1e16a2.chunk.js",
|
||||
"static/js/4869.3c1ecd5f.chunk.js": "./static/js/4869.3c1ecd5f.chunk.js",
|
||||
"static/js/2202.2a63896a.chunk.js": "./static/js/2202.2a63896a.chunk.js",
|
||||
"static/js/7436.1b42daaf.chunk.js": "./static/js/7436.1b42daaf.chunk.js",
|
||||
"static/js/1056.c8f0b4b3.chunk.js": "./static/js/1056.c8f0b4b3.chunk.js",
|
||||
"static/js/9779.77d5e14d.chunk.js": "./static/js/9779.77d5e14d.chunk.js",
|
||||
"static/js/3617.90158304.chunk.js": "./static/js/3617.90158304.chunk.js",
|
||||
"static/js/7274.2c258b85.chunk.js": "./static/js/7274.2c258b85.chunk.js",
|
||||
"static/js/7842.8d7284b8.chunk.js": "./static/js/7842.8d7284b8.chunk.js",
|
||||
"static/js/4745.d734a779.chunk.js": "./static/js/4745.d734a779.chunk.js",
|
||||
"static/js/4515.605510ac.chunk.js": "./static/js/4515.605510ac.chunk.js",
|
||||
"static/js/8259.2a0bb90a.chunk.js": "./static/js/8259.2a0bb90a.chunk.js",
|
||||
"static/js/4839.d6536e1e.chunk.js": "./static/js/4839.d6536e1e.chunk.js",
|
||||
"static/js/3830.31b98398.chunk.js": "./static/js/3830.31b98398.chunk.js",
|
||||
"static/js/9275.914f5812.chunk.js": "./static/js/9275.914f5812.chunk.js",
|
||||
"static/js/8190.a6c60008.chunk.js": "./static/js/8190.a6c60008.chunk.js",
|
||||
"static/js/7456.fa86b441.chunk.js": "./static/js/7456.fa86b441.chunk.js",
|
||||
"static/css/6696.8656472c.chunk.css": "./static/css/6696.8656472c.chunk.css",
|
||||
"static/js/6696.e69732dc.chunk.js": "./static/js/6696.e69732dc.chunk.js",
|
||||
"static/js/2699.4452ed45.chunk.js": "./static/js/2699.4452ed45.chunk.js",
|
||||
"static/js/5808.8d639748.chunk.js": "./static/js/5808.8d639748.chunk.js",
|
||||
"static/js/1237.7ae28fb9.chunk.js": "./static/js/1237.7ae28fb9.chunk.js",
|
||||
"static/js/8503.fe4b38b4.chunk.js": "./static/js/8503.fe4b38b4.chunk.js",
|
||||
"static/js/694.043866cc.chunk.js": "./static/js/694.043866cc.chunk.js",
|
||||
"static/css/9807.8656472c.chunk.css": "./static/css/9807.8656472c.chunk.css",
|
||||
"static/js/9807.82005fd9.chunk.js": "./static/js/9807.82005fd9.chunk.js",
|
||||
"static/js/3806.71c34692.chunk.js": "./static/js/3806.71c34692.chunk.js",
|
||||
"static/js/8661.9398e9f6.chunk.js": "./static/js/8661.9398e9f6.chunk.js",
|
||||
"static/js/4237.2a675682.chunk.js": "./static/js/4237.2a675682.chunk.js",
|
||||
"static/js/4577.29aab471.chunk.js": "./static/js/4577.29aab471.chunk.js",
|
||||
"static/js/5947.c28cc807.chunk.js": "./static/js/5947.c28cc807.chunk.js",
|
||||
"static/js/4533.97fb0634.chunk.js": "./static/js/4533.97fb0634.chunk.js",
|
||||
"static/js/6147.e363cc92.chunk.js": "./static/js/6147.e363cc92.chunk.js",
|
||||
"static/js/2805.09ce1581.chunk.js": "./static/js/2805.09ce1581.chunk.js",
|
||||
"static/js/1409.ce8091c2.chunk.js": "./static/js/1409.ce8091c2.chunk.js",
|
||||
"static/js/9560.6ff6a758.chunk.js": "./static/js/9560.6ff6a758.chunk.js",
|
||||
"static/js/4247.636fee85.chunk.js": "./static/js/4247.636fee85.chunk.js",
|
||||
"static/js/9330.5c857d6b.chunk.js": "./static/js/9330.5c857d6b.chunk.js",
|
||||
"static/js/4869.b14a3a6c.chunk.js": "./static/js/4869.b14a3a6c.chunk.js",
|
||||
"static/js/2202.516dc033.chunk.js": "./static/js/2202.516dc033.chunk.js",
|
||||
"static/js/7436.17e2bbf0.chunk.js": "./static/js/7436.17e2bbf0.chunk.js",
|
||||
"static/js/1056.6f1cb8e0.chunk.js": "./static/js/1056.6f1cb8e0.chunk.js",
|
||||
"static/js/9779.0e8be0cc.chunk.js": "./static/js/9779.0e8be0cc.chunk.js",
|
||||
"static/js/3617.38600ea7.chunk.js": "./static/js/3617.38600ea7.chunk.js",
|
||||
"static/js/7274.64442dfa.chunk.js": "./static/js/7274.64442dfa.chunk.js",
|
||||
"static/js/7842.6ec32dd7.chunk.js": "./static/js/7842.6ec32dd7.chunk.js",
|
||||
"static/js/4745.00dcce39.chunk.js": "./static/js/4745.00dcce39.chunk.js",
|
||||
"static/js/4515.fc843c16.chunk.js": "./static/js/4515.fc843c16.chunk.js",
|
||||
"static/js/8259.d34a4da6.chunk.js": "./static/js/8259.d34a4da6.chunk.js",
|
||||
"static/js/4839.cfc74e75.chunk.js": "./static/js/4839.cfc74e75.chunk.js",
|
||||
"static/js/8853.b298ca0c.chunk.js": "./static/js/8853.b298ca0c.chunk.js",
|
||||
"static/js/9275.3648f309.chunk.js": "./static/js/9275.3648f309.chunk.js",
|
||||
"static/js/3795.ca99e16b.chunk.js": "./static/js/3795.ca99e16b.chunk.js",
|
||||
"static/js/7314.1fb79e31.chunk.js": "./static/js/7314.1fb79e31.chunk.js",
|
||||
"static/js/7456.99d2d848.chunk.js": "./static/js/7456.99d2d848.chunk.js",
|
||||
"static/css/5673.ce92c9f5.chunk.css": "./static/css/5673.ce92c9f5.chunk.css",
|
||||
"static/js/5673.d3eb036c.chunk.js": "./static/js/5673.d3eb036c.chunk.js",
|
||||
"static/js/2699.ac935aa8.chunk.js": "./static/js/2699.ac935aa8.chunk.js",
|
||||
"static/js/5808.5e176e4d.chunk.js": "./static/js/5808.5e176e4d.chunk.js",
|
||||
"static/js/1237.f11be969.chunk.js": "./static/js/1237.f11be969.chunk.js",
|
||||
"static/js/8503.6ce0ff69.chunk.js": "./static/js/8503.6ce0ff69.chunk.js",
|
||||
"static/js/6615.f26600a0.chunk.js": "./static/js/6615.f26600a0.chunk.js",
|
||||
"static/css/9807.ce92c9f5.chunk.css": "./static/css/9807.ce92c9f5.chunk.css",
|
||||
"static/js/9807.3f033821.chunk.js": "./static/js/9807.3f033821.chunk.js",
|
||||
"static/js/3806.e2daceb3.chunk.js": "./static/js/3806.e2daceb3.chunk.js",
|
||||
"static/js/2742.f1b14cdb.chunk.js": "./static/js/2742.f1b14cdb.chunk.js",
|
||||
"static/js/3474.378d2159.chunk.js": "./static/js/3474.378d2159.chunk.js",
|
||||
"static/js/4577.ee518ba9.chunk.js": "./static/js/4577.ee518ba9.chunk.js",
|
||||
"static/js/5947.88f9751e.chunk.js": "./static/js/5947.88f9751e.chunk.js",
|
||||
"static/js/4533.e1a532b6.chunk.js": "./static/js/4533.e1a532b6.chunk.js",
|
||||
"static/js/6147.aed56e85.chunk.js": "./static/js/6147.aed56e85.chunk.js",
|
||||
"static/js/2805.73cec790.chunk.js": "./static/js/2805.73cec790.chunk.js",
|
||||
"static/js/3143.e6a9621d.chunk.js": "./static/js/3143.e6a9621d.chunk.js",
|
||||
"static/js/428.092071b6.chunk.js": "./static/js/428.092071b6.chunk.js",
|
||||
"static/js/1069.4329a890.chunk.js": "./static/js/1069.4329a890.chunk.js",
|
||||
"static/js/1140.bc981889.chunk.js": "./static/js/1140.bc981889.chunk.js",
|
||||
"static/js/2094.87c79d6e.chunk.js": "./static/js/2094.87c79d6e.chunk.js",
|
||||
"static/js/7950.8cccfb81.chunk.js": "./static/js/7950.8cccfb81.chunk.js",
|
||||
"static/js/8369.91bb6d80.chunk.js": "./static/js/8369.91bb6d80.chunk.js",
|
||||
"static/js/8961.4409f5d0.chunk.js": "./static/js/8961.4409f5d0.chunk.js",
|
||||
"static/js/3967.ead99d49.chunk.js": "./static/js/3967.ead99d49.chunk.js",
|
||||
"static/css/5677.8656472c.chunk.css": "./static/css/5677.8656472c.chunk.css",
|
||||
"static/js/5677.84696c79.chunk.js": "./static/js/5677.84696c79.chunk.js",
|
||||
"static/css/4360.8656472c.chunk.css": "./static/css/4360.8656472c.chunk.css",
|
||||
"static/js/4360.128ebb74.chunk.js": "./static/js/4360.128ebb74.chunk.js",
|
||||
"static/js/7664.5db3e36a.chunk.js": "./static/js/7664.5db3e36a.chunk.js",
|
||||
"static/js/9080.f4810646.chunk.js": "./static/js/9080.f4810646.chunk.js",
|
||||
"static/js/5961.494f0900.chunk.js": "./static/js/5961.494f0900.chunk.js",
|
||||
"static/js/428.b5539f2b.chunk.js": "./static/js/428.b5539f2b.chunk.js",
|
||||
"static/js/1069.6dfe4ad0.chunk.js": "./static/js/1069.6dfe4ad0.chunk.js",
|
||||
"static/js/1140.0208375a.chunk.js": "./static/js/1140.0208375a.chunk.js",
|
||||
"static/js/2094.4854485e.chunk.js": "./static/js/2094.4854485e.chunk.js",
|
||||
"static/js/7950.455e5528.chunk.js": "./static/js/7950.455e5528.chunk.js",
|
||||
"static/js/671.18a32329.chunk.js": "./static/js/671.18a32329.chunk.js",
|
||||
"static/js/8961.8aa802d6.chunk.js": "./static/js/8961.8aa802d6.chunk.js",
|
||||
"static/js/3967.ef703755.chunk.js": "./static/js/3967.ef703755.chunk.js",
|
||||
"static/css/5677.ce92c9f5.chunk.css": "./static/css/5677.ce92c9f5.chunk.css",
|
||||
"static/js/5677.654039a6.chunk.js": "./static/js/5677.654039a6.chunk.js",
|
||||
"static/css/4360.ce92c9f5.chunk.css": "./static/css/4360.ce92c9f5.chunk.css",
|
||||
"static/js/4360.bbbd8faf.chunk.js": "./static/js/4360.bbbd8faf.chunk.js",
|
||||
"static/js/4109.04a89c2d.chunk.js": "./static/js/4109.04a89c2d.chunk.js",
|
||||
"static/js/9080.bb419916.chunk.js": "./static/js/9080.bb419916.chunk.js",
|
||||
"static/js/5961.04fd21a3.chunk.js": "./static/js/5961.04fd21a3.chunk.js",
|
||||
"static/js/6549.e0686e32.chunk.js": "./static/js/6549.e0686e32.chunk.js",
|
||||
"static/js/7498.5bdce2df.chunk.js": "./static/js/7498.5bdce2df.chunk.js",
|
||||
"static/js/9421.fcfffa57.chunk.js": "./static/js/9421.fcfffa57.chunk.js",
|
||||
"static/js/5284.e972df04.chunk.js": "./static/js/5284.e972df04.chunk.js",
|
||||
"static/js/4818.d857d014.chunk.js": "./static/js/4818.d857d014.chunk.js",
|
||||
"static/js/2401.dab77e89.chunk.js": "./static/js/2401.dab77e89.chunk.js",
|
||||
"static/css/8724.8656472c.chunk.css": "./static/css/8724.8656472c.chunk.css",
|
||||
"static/js/8724.8dc525b3.chunk.js": "./static/js/8724.8dc525b3.chunk.js",
|
||||
"static/js/2182.d27fb965.chunk.js": "./static/js/2182.d27fb965.chunk.js",
|
||||
"static/js/7764.9f2f6b88.chunk.js": "./static/js/7764.9f2f6b88.chunk.js",
|
||||
"static/js/4220.7760227f.chunk.js": "./static/js/4220.7760227f.chunk.js",
|
||||
"static/js/1719.54b40a33.chunk.js": "./static/js/1719.54b40a33.chunk.js",
|
||||
"static/js/3320.646cbb74.chunk.js": "./static/js/3320.646cbb74.chunk.js",
|
||||
"static/js/9923.634b0622.chunk.js": "./static/js/9923.634b0622.chunk.js",
|
||||
"static/js/7498.07dd467b.chunk.js": "./static/js/7498.07dd467b.chunk.js",
|
||||
"static/js/9421.a88c8e02.chunk.js": "./static/js/9421.a88c8e02.chunk.js",
|
||||
"static/js/5284.e23070a0.chunk.js": "./static/js/5284.e23070a0.chunk.js",
|
||||
"static/js/4818.65ba088c.chunk.js": "./static/js/4818.65ba088c.chunk.js",
|
||||
"static/js/2401.74634660.chunk.js": "./static/js/2401.74634660.chunk.js",
|
||||
"static/css/8724.ce92c9f5.chunk.css": "./static/css/8724.ce92c9f5.chunk.css",
|
||||
"static/js/8724.cf8fb340.chunk.js": "./static/js/8724.cf8fb340.chunk.js",
|
||||
"static/js/2182.468a5b4e.chunk.js": "./static/js/2182.468a5b4e.chunk.js",
|
||||
"static/js/7764.3b798da3.chunk.js": "./static/js/7764.3b798da3.chunk.js",
|
||||
"static/js/4220.308c5fb3.chunk.js": "./static/js/4220.308c5fb3.chunk.js",
|
||||
"static/js/1719.fbaa408a.chunk.js": "./static/js/1719.fbaa408a.chunk.js",
|
||||
"static/js/3320.2e853989.chunk.js": "./static/js/3320.2e853989.chunk.js",
|
||||
"static/js/9923.25fa1a75.chunk.js": "./static/js/9923.25fa1a75.chunk.js",
|
||||
"static/js/9586.c44d8f2b.chunk.js": "./static/js/9586.c44d8f2b.chunk.js",
|
||||
"static/js/7261.39f723d9.chunk.js": "./static/js/7261.39f723d9.chunk.js",
|
||||
"static/js/6436.c3cf82d9.chunk.js": "./static/js/6436.c3cf82d9.chunk.js",
|
||||
"static/js/8343.c498d4af.chunk.js": "./static/js/8343.c498d4af.chunk.js",
|
||||
"static/js/2841.73580ff7.chunk.js": "./static/js/2841.73580ff7.chunk.js",
|
||||
"static/js/6167.2cf4273f.chunk.js": "./static/js/6167.2cf4273f.chunk.js",
|
||||
"static/js/3698.2ea7b498.chunk.js": "./static/js/3698.2ea7b498.chunk.js",
|
||||
"static/js/1971.52779ae6.chunk.js": "./static/js/1971.52779ae6.chunk.js",
|
||||
"static/js/7346.5ff362f5.chunk.js": "./static/js/7346.5ff362f5.chunk.js",
|
||||
"static/js/5144.5d8ad21e.chunk.js": "./static/js/5144.5d8ad21e.chunk.js",
|
||||
"static/js/5125.2efed100.chunk.js": "./static/js/5125.2efed100.chunk.js",
|
||||
"static/js/528.c588d7c6.chunk.js": "./static/js/528.c588d7c6.chunk.js",
|
||||
"static/js/7187.7c4714ee.chunk.js": "./static/js/7187.7c4714ee.chunk.js",
|
||||
"static/js/6173.3a1318da.chunk.js": "./static/js/6173.3a1318da.chunk.js",
|
||||
"static/js/7146.77e9b1cc.chunk.js": "./static/js/7146.77e9b1cc.chunk.js",
|
||||
"static/js/9924.8e40abd7.chunk.js": "./static/js/9924.8e40abd7.chunk.js",
|
||||
"static/js/9193.6764f72b.chunk.js": "./static/js/9193.6764f72b.chunk.js",
|
||||
"static/js/7451.0fa5a7e5.chunk.js": "./static/js/7451.0fa5a7e5.chunk.js",
|
||||
"static/js/6511.9ad8d9a8.chunk.js": "./static/js/6511.9ad8d9a8.chunk.js",
|
||||
"static/css/165.8656472c.chunk.css": "./static/css/165.8656472c.chunk.css",
|
||||
"static/js/165.03f5d4c1.chunk.js": "./static/js/165.03f5d4c1.chunk.js",
|
||||
"static/js/4121.b9b399ef.chunk.js": "./static/js/4121.b9b399ef.chunk.js",
|
||||
"static/js/609.3f81f01c.chunk.js": "./static/js/609.3f81f01c.chunk.js",
|
||||
"static/js/2892.1d404509.chunk.js": "./static/js/2892.1d404509.chunk.js",
|
||||
"static/js/7926.7b87452c.chunk.js": "./static/js/7926.7b87452c.chunk.js",
|
||||
"static/js/6145.1b8916cd.chunk.js": "./static/js/6145.1b8916cd.chunk.js",
|
||||
"static/css/6951.8656472c.chunk.css": "./static/css/6951.8656472c.chunk.css",
|
||||
"static/js/6951.34c85fa1.chunk.js": "./static/js/6951.34c85fa1.chunk.js",
|
||||
"static/js/2966.3df2cc1f.chunk.js": "./static/js/2966.3df2cc1f.chunk.js",
|
||||
"static/js/4177.b1d4c761.chunk.js": "./static/js/4177.b1d4c761.chunk.js",
|
||||
"static/js/9679.3942615d.chunk.js": "./static/js/9679.3942615d.chunk.js",
|
||||
"static/js/8333.a7249061.chunk.js": "./static/js/8333.a7249061.chunk.js",
|
||||
"static/js/1711.2e4f085e.chunk.js": "./static/js/1711.2e4f085e.chunk.js",
|
||||
"static/js/9.40a0afdf.chunk.js": "./static/js/9.40a0afdf.chunk.js",
|
||||
"static/js/4487.625e74d7.chunk.js": "./static/js/4487.625e74d7.chunk.js",
|
||||
"static/js/6866.eca0c7d9.chunk.js": "./static/js/6866.eca0c7d9.chunk.js",
|
||||
"static/js/8564.289a240e.chunk.js": "./static/js/8564.289a240e.chunk.js",
|
||||
"static/js/3152.4d22e959.chunk.js": "./static/js/3152.4d22e959.chunk.js",
|
||||
"static/js/7007.7afa6cca.chunk.js": "./static/js/7007.7afa6cca.chunk.js",
|
||||
"static/js/14.ec5eac10.chunk.js": "./static/js/14.ec5eac10.chunk.js",
|
||||
"static/js/892.c5842324.chunk.js": "./static/js/892.c5842324.chunk.js",
|
||||
"static/js/2066.81f72337.chunk.js": "./static/js/2066.81f72337.chunk.js",
|
||||
"static/js/7261.98082dab.chunk.js": "./static/js/7261.98082dab.chunk.js",
|
||||
"static/js/6436.f2bab163.chunk.js": "./static/js/6436.f2bab163.chunk.js",
|
||||
"static/js/8343.b174034e.chunk.js": "./static/js/8343.b174034e.chunk.js",
|
||||
"static/js/2841.95af1213.chunk.js": "./static/js/2841.95af1213.chunk.js",
|
||||
"static/js/6167.edad2368.chunk.js": "./static/js/6167.edad2368.chunk.js",
|
||||
"static/js/3698.49eb7411.chunk.js": "./static/js/3698.49eb7411.chunk.js",
|
||||
"static/js/1971.306de04b.chunk.js": "./static/js/1971.306de04b.chunk.js",
|
||||
"static/js/7346.8e455eaa.chunk.js": "./static/js/7346.8e455eaa.chunk.js",
|
||||
"static/js/5144.f66d75af.chunk.js": "./static/js/5144.f66d75af.chunk.js",
|
||||
"static/js/5125.9822771e.chunk.js": "./static/js/5125.9822771e.chunk.js",
|
||||
"static/js/1973.55987b98.chunk.js": "./static/js/1973.55987b98.chunk.js",
|
||||
"static/js/7187.b3ba71a1.chunk.js": "./static/js/7187.b3ba71a1.chunk.js",
|
||||
"static/js/6173.b70c4e1a.chunk.js": "./static/js/6173.b70c4e1a.chunk.js",
|
||||
"static/js/7146.09b3a43c.chunk.js": "./static/js/7146.09b3a43c.chunk.js",
|
||||
"static/js/9924.a7c06331.chunk.js": "./static/js/9924.a7c06331.chunk.js",
|
||||
"static/js/9193.cbd8d842.chunk.js": "./static/js/9193.cbd8d842.chunk.js",
|
||||
"static/js/7451.43cf8bf6.chunk.js": "./static/js/7451.43cf8bf6.chunk.js",
|
||||
"static/css/165.ce92c9f5.chunk.css": "./static/css/165.ce92c9f5.chunk.css",
|
||||
"static/js/165.6ba70895.chunk.js": "./static/js/165.6ba70895.chunk.js",
|
||||
"static/js/4121.17b5e70f.chunk.js": "./static/js/4121.17b5e70f.chunk.js",
|
||||
"static/js/609.218c57b6.chunk.js": "./static/js/609.218c57b6.chunk.js",
|
||||
"static/js/3421.aa3eeb04.chunk.js": "./static/js/3421.aa3eeb04.chunk.js",
|
||||
"static/js/2892.f2ab7fe7.chunk.js": "./static/js/2892.f2ab7fe7.chunk.js",
|
||||
"static/js/7926.d155ef62.chunk.js": "./static/js/7926.d155ef62.chunk.js",
|
||||
"static/js/6145.819d854c.chunk.js": "./static/js/6145.819d854c.chunk.js",
|
||||
"static/css/6951.ce92c9f5.chunk.css": "./static/css/6951.ce92c9f5.chunk.css",
|
||||
"static/js/6951.e58557fd.chunk.js": "./static/js/6951.e58557fd.chunk.js",
|
||||
"static/js/2966.c61fbf10.chunk.js": "./static/js/2966.c61fbf10.chunk.js",
|
||||
"static/js/4177.bfe448c8.chunk.js": "./static/js/4177.bfe448c8.chunk.js",
|
||||
"static/js/9679.2b8424ba.chunk.js": "./static/js/9679.2b8424ba.chunk.js",
|
||||
"static/js/8333.b0c9e377.chunk.js": "./static/js/8333.b0c9e377.chunk.js",
|
||||
"static/js/1711.a046e26c.chunk.js": "./static/js/1711.a046e26c.chunk.js",
|
||||
"static/js/9.0e073695.chunk.js": "./static/js/9.0e073695.chunk.js",
|
||||
"static/js/4487.3ad24a2d.chunk.js": "./static/js/4487.3ad24a2d.chunk.js",
|
||||
"static/js/6866.aa2f03f0.chunk.js": "./static/js/6866.aa2f03f0.chunk.js",
|
||||
"static/js/8564.11a3023b.chunk.js": "./static/js/8564.11a3023b.chunk.js",
|
||||
"static/js/3152.daa99377.chunk.js": "./static/js/3152.daa99377.chunk.js",
|
||||
"static/js/7007.3606231e.chunk.js": "./static/js/7007.3606231e.chunk.js",
|
||||
"static/js/14.25ecb28d.chunk.js": "./static/js/14.25ecb28d.chunk.js",
|
||||
"static/js/2066.5180dac0.chunk.js": "./static/js/2066.5180dac0.chunk.js",
|
||||
"static/js/5444.e8727da9.chunk.js": "./static/js/5444.e8727da9.chunk.js",
|
||||
"static/js/5594.6e7b403a.chunk.js": "./static/js/5594.6e7b403a.chunk.js",
|
||||
"static/js/3990.5e998248.chunk.js": "./static/js/3990.5e998248.chunk.js",
|
||||
"static/js/5399.5699b879.chunk.js": "./static/js/5399.5699b879.chunk.js",
|
||||
"static/js/892.2aaf58f1.chunk.js": "./static/js/892.2aaf58f1.chunk.js",
|
||||
"static/js/3690.536fb187.chunk.js": "./static/js/3690.536fb187.chunk.js",
|
||||
"static/js/5399.6bc650ac.chunk.js": "./static/js/5399.6bc650ac.chunk.js",
|
||||
"static/js/606.28fdb5bc.chunk.js": "./static/js/606.28fdb5bc.chunk.js",
|
||||
"static/js/9998.e7c54804.chunk.js": "./static/js/9998.e7c54804.chunk.js",
|
||||
"static/js/9769.733c21d3.chunk.js": "./static/js/9769.733c21d3.chunk.js",
|
||||
"static/js/9998.1a613ce5.chunk.js": "./static/js/9998.1a613ce5.chunk.js",
|
||||
"static/js/9769.a2bfb4d7.chunk.js": "./static/js/9769.a2bfb4d7.chunk.js",
|
||||
"static/js/8954.00599ff1.chunk.js": "./static/js/8954.00599ff1.chunk.js",
|
||||
"static/js/7248.1e0c4e19.chunk.js": "./static/js/7248.1e0c4e19.chunk.js",
|
||||
"static/js/4837.afb48283.chunk.js": "./static/js/4837.afb48283.chunk.js",
|
||||
"static/js/4837.98af4cfe.chunk.js": "./static/js/4837.98af4cfe.chunk.js",
|
||||
"static/js/3020.3b2e782c.chunk.js": "./static/js/3020.3b2e782c.chunk.js",
|
||||
"static/js/9056.ad0432f8.chunk.js": "./static/js/9056.ad0432f8.chunk.js",
|
||||
"static/js/9056.41f0e489.chunk.js": "./static/js/9056.41f0e489.chunk.js",
|
||||
"static/js/3360.453f0701.chunk.js": "./static/js/3360.453f0701.chunk.js",
|
||||
"static/js/7561.81edce87.chunk.js": "./static/js/7561.81edce87.chunk.js",
|
||||
"static/js/8420.68d5dfb4.chunk.js": "./static/js/8420.68d5dfb4.chunk.js",
|
||||
"static/js/1420.517f0e88.chunk.js": "./static/js/1420.517f0e88.chunk.js",
|
||||
"static/js/2309.49be65c5.chunk.js": "./static/js/2309.49be65c5.chunk.js",
|
||||
"static/js/2364.a32e93a4.chunk.js": "./static/js/2364.a32e93a4.chunk.js",
|
||||
"static/js/3356.3534f8c0.chunk.js": "./static/js/3356.3534f8c0.chunk.js",
|
||||
"static/js/1233.9903fc82.chunk.js": "./static/js/1233.9903fc82.chunk.js",
|
||||
"static/js/3356.05b42758.chunk.js": "./static/js/3356.05b42758.chunk.js",
|
||||
"static/js/8016.b0ab3b67.chunk.js": "./static/js/8016.b0ab3b67.chunk.js",
|
||||
"static/js/6851.3b47e1b9.chunk.js": "./static/js/6851.3b47e1b9.chunk.js",
|
||||
"static/js/2253.0ed59cf3.chunk.js": "./static/js/2253.0ed59cf3.chunk.js",
|
||||
"static/js/9415.8ad63e79.chunk.js": "./static/js/9415.8ad63e79.chunk.js",
|
||||
"static/js/1811.cf61844d.chunk.js": "./static/js/1811.cf61844d.chunk.js",
|
||||
"static/js/7857.cc3da495.chunk.js": "./static/js/7857.cc3da495.chunk.js",
|
||||
"static/js/3538.1b5c3c94.chunk.js": "./static/js/3538.1b5c3c94.chunk.js",
|
||||
"static/js/3290.cf0a5a06.chunk.js": "./static/js/3290.cf0a5a06.chunk.js",
|
||||
"static/js/7348.44810a11.chunk.js": "./static/js/7348.44810a11.chunk.js",
|
||||
"static/js/2253.d527f443.chunk.js": "./static/js/2253.d527f443.chunk.js",
|
||||
"static/js/6427.028089e0.chunk.js": "./static/js/6427.028089e0.chunk.js",
|
||||
"static/js/3538.60e98cef.chunk.js": "./static/js/3538.60e98cef.chunk.js",
|
||||
"static/js/8749.fa0fb710.chunk.js": "./static/js/8749.fa0fb710.chunk.js",
|
||||
"static/js/7348.b037b952.chunk.js": "./static/js/7348.b037b952.chunk.js",
|
||||
"static/js/1760.8589ea77.chunk.js": "./static/js/1760.8589ea77.chunk.js",
|
||||
"static/js/8708.1735e6e6.chunk.js": "./static/js/8708.1735e6e6.chunk.js",
|
||||
"static/js/3045.691c66fa.chunk.js": "./static/js/3045.691c66fa.chunk.js",
|
||||
"static/js/7049.620c3eec.chunk.js": "./static/js/7049.620c3eec.chunk.js",
|
||||
"static/js/2873.ebf17fc8.chunk.js": "./static/js/2873.ebf17fc8.chunk.js",
|
||||
"static/js/8708.70b62dd5.chunk.js": "./static/js/8708.70b62dd5.chunk.js",
|
||||
"static/js/2594.77f64a1f.chunk.js": "./static/js/2594.77f64a1f.chunk.js",
|
||||
"static/js/440.5bb0a106.chunk.js": "./static/js/440.5bb0a106.chunk.js",
|
||||
"static/js/3045.f1cc8d42.chunk.js": "./static/js/3045.f1cc8d42.chunk.js",
|
||||
"static/js/4817.2c511e72.chunk.js": "./static/js/4817.2c511e72.chunk.js",
|
||||
"static/js/227.5ba677a7.chunk.js": "./static/js/227.5ba677a7.chunk.js",
|
||||
"static/js/4105.1b1d6fe0.chunk.js": "./static/js/4105.1b1d6fe0.chunk.js",
|
||||
"static/js/8174.540bd07a.chunk.js": "./static/js/8174.540bd07a.chunk.js",
|
||||
"static/js/7042.6737005f.chunk.js": "./static/js/7042.6737005f.chunk.js",
|
||||
"static/js/3201.8afb9fa9.chunk.js": "./static/js/3201.8afb9fa9.chunk.js",
|
||||
"static/js/1724.30bcf6f8.chunk.js": "./static/js/1724.30bcf6f8.chunk.js",
|
||||
"static/js/7454.eb7fb0a0.chunk.js": "./static/js/7454.eb7fb0a0.chunk.js",
|
||||
"static/js/6873.177d160e.chunk.js": "./static/js/6873.177d160e.chunk.js",
|
||||
"static/js/3453.3c290198.chunk.js": "./static/js/3453.3c290198.chunk.js",
|
||||
"static/js/938.8f6a5aa1.chunk.js": "./static/js/938.8f6a5aa1.chunk.js",
|
||||
"static/js/4105.a498ca05.chunk.js": "./static/js/4105.a498ca05.chunk.js",
|
||||
"static/js/9876.3e5a4f83.chunk.js": "./static/js/9876.3e5a4f83.chunk.js",
|
||||
"static/js/7777.7ea62662.chunk.js": "./static/js/7777.7ea62662.chunk.js",
|
||||
"static/js/7042.343b48b2.chunk.js": "./static/js/7042.343b48b2.chunk.js",
|
||||
"static/js/3201.9f8a570f.chunk.js": "./static/js/3201.9f8a570f.chunk.js",
|
||||
"static/js/7664.9ba57569.chunk.js": "./static/js/7664.9ba57569.chunk.js",
|
||||
"static/js/5170.2d3119fa.chunk.js": "./static/js/5170.2d3119fa.chunk.js",
|
||||
"static/js/7454.6a33b546.chunk.js": "./static/js/7454.6a33b546.chunk.js",
|
||||
"static/js/6873.a4c0dde3.chunk.js": "./static/js/6873.a4c0dde3.chunk.js",
|
||||
"static/js/3453.9ef32892.chunk.js": "./static/js/3453.9ef32892.chunk.js",
|
||||
"index.html": "./index.html",
|
||||
"main.c4c1effe.css.map": "./static/css/main.c4c1effe.css.map",
|
||||
"main.e413bbf1.js.map": "./static/js/main.e413bbf1.js.map",
|
||||
"2178.b2fb43c5.chunk.js.map": "./static/js/2178.b2fb43c5.chunk.js.map",
|
||||
"main.c7ee1a1d.js.map": "./static/js/main.c7ee1a1d.js.map",
|
||||
"2178.cf1507fb.chunk.js.map": "./static/js/2178.cf1507fb.chunk.js.map",
|
||||
"5282.e51b8c66.chunk.js.map": "./static/js/5282.e51b8c66.chunk.js.map",
|
||||
"2818.d1afde1a.chunk.js.map": "./static/js/2818.d1afde1a.chunk.js.map",
|
||||
"9560.e48c4920.chunk.js.map": "./static/js/9560.e48c4920.chunk.js.map",
|
||||
"9661.f854f848.chunk.js.map": "./static/js/9661.f854f848.chunk.js.map",
|
||||
"9330.3d1e16a2.chunk.js.map": "./static/js/9330.3d1e16a2.chunk.js.map",
|
||||
"4869.3c1ecd5f.chunk.js.map": "./static/js/4869.3c1ecd5f.chunk.js.map",
|
||||
"2202.2a63896a.chunk.js.map": "./static/js/2202.2a63896a.chunk.js.map",
|
||||
"7436.1b42daaf.chunk.js.map": "./static/js/7436.1b42daaf.chunk.js.map",
|
||||
"1056.c8f0b4b3.chunk.js.map": "./static/js/1056.c8f0b4b3.chunk.js.map",
|
||||
"9779.77d5e14d.chunk.js.map": "./static/js/9779.77d5e14d.chunk.js.map",
|
||||
"3617.90158304.chunk.js.map": "./static/js/3617.90158304.chunk.js.map",
|
||||
"7274.2c258b85.chunk.js.map": "./static/js/7274.2c258b85.chunk.js.map",
|
||||
"7842.8d7284b8.chunk.js.map": "./static/js/7842.8d7284b8.chunk.js.map",
|
||||
"4745.d734a779.chunk.js.map": "./static/js/4745.d734a779.chunk.js.map",
|
||||
"4515.605510ac.chunk.js.map": "./static/js/4515.605510ac.chunk.js.map",
|
||||
"8259.2a0bb90a.chunk.js.map": "./static/js/8259.2a0bb90a.chunk.js.map",
|
||||
"4839.d6536e1e.chunk.js.map": "./static/js/4839.d6536e1e.chunk.js.map",
|
||||
"3830.31b98398.chunk.js.map": "./static/js/3830.31b98398.chunk.js.map",
|
||||
"9275.914f5812.chunk.js.map": "./static/js/9275.914f5812.chunk.js.map",
|
||||
"8190.a6c60008.chunk.js.map": "./static/js/8190.a6c60008.chunk.js.map",
|
||||
"7456.fa86b441.chunk.js.map": "./static/js/7456.fa86b441.chunk.js.map",
|
||||
"6696.8656472c.chunk.css.map": "./static/css/6696.8656472c.chunk.css.map",
|
||||
"6696.e69732dc.chunk.js.map": "./static/js/6696.e69732dc.chunk.js.map",
|
||||
"2699.4452ed45.chunk.js.map": "./static/js/2699.4452ed45.chunk.js.map",
|
||||
"5808.8d639748.chunk.js.map": "./static/js/5808.8d639748.chunk.js.map",
|
||||
"1237.7ae28fb9.chunk.js.map": "./static/js/1237.7ae28fb9.chunk.js.map",
|
||||
"8503.fe4b38b4.chunk.js.map": "./static/js/8503.fe4b38b4.chunk.js.map",
|
||||
"694.043866cc.chunk.js.map": "./static/js/694.043866cc.chunk.js.map",
|
||||
"9807.8656472c.chunk.css.map": "./static/css/9807.8656472c.chunk.css.map",
|
||||
"9807.82005fd9.chunk.js.map": "./static/js/9807.82005fd9.chunk.js.map",
|
||||
"3806.71c34692.chunk.js.map": "./static/js/3806.71c34692.chunk.js.map",
|
||||
"8661.9398e9f6.chunk.js.map": "./static/js/8661.9398e9f6.chunk.js.map",
|
||||
"4237.2a675682.chunk.js.map": "./static/js/4237.2a675682.chunk.js.map",
|
||||
"4577.29aab471.chunk.js.map": "./static/js/4577.29aab471.chunk.js.map",
|
||||
"5947.c28cc807.chunk.js.map": "./static/js/5947.c28cc807.chunk.js.map",
|
||||
"4533.97fb0634.chunk.js.map": "./static/js/4533.97fb0634.chunk.js.map",
|
||||
"6147.e363cc92.chunk.js.map": "./static/js/6147.e363cc92.chunk.js.map",
|
||||
"2805.09ce1581.chunk.js.map": "./static/js/2805.09ce1581.chunk.js.map",
|
||||
"1409.ce8091c2.chunk.js.map": "./static/js/1409.ce8091c2.chunk.js.map",
|
||||
"9560.6ff6a758.chunk.js.map": "./static/js/9560.6ff6a758.chunk.js.map",
|
||||
"4247.636fee85.chunk.js.map": "./static/js/4247.636fee85.chunk.js.map",
|
||||
"9330.5c857d6b.chunk.js.map": "./static/js/9330.5c857d6b.chunk.js.map",
|
||||
"4869.b14a3a6c.chunk.js.map": "./static/js/4869.b14a3a6c.chunk.js.map",
|
||||
"2202.516dc033.chunk.js.map": "./static/js/2202.516dc033.chunk.js.map",
|
||||
"7436.17e2bbf0.chunk.js.map": "./static/js/7436.17e2bbf0.chunk.js.map",
|
||||
"1056.6f1cb8e0.chunk.js.map": "./static/js/1056.6f1cb8e0.chunk.js.map",
|
||||
"9779.0e8be0cc.chunk.js.map": "./static/js/9779.0e8be0cc.chunk.js.map",
|
||||
"3617.38600ea7.chunk.js.map": "./static/js/3617.38600ea7.chunk.js.map",
|
||||
"7274.64442dfa.chunk.js.map": "./static/js/7274.64442dfa.chunk.js.map",
|
||||
"7842.6ec32dd7.chunk.js.map": "./static/js/7842.6ec32dd7.chunk.js.map",
|
||||
"4745.00dcce39.chunk.js.map": "./static/js/4745.00dcce39.chunk.js.map",
|
||||
"4515.fc843c16.chunk.js.map": "./static/js/4515.fc843c16.chunk.js.map",
|
||||
"8259.d34a4da6.chunk.js.map": "./static/js/8259.d34a4da6.chunk.js.map",
|
||||
"4839.cfc74e75.chunk.js.map": "./static/js/4839.cfc74e75.chunk.js.map",
|
||||
"8853.b298ca0c.chunk.js.map": "./static/js/8853.b298ca0c.chunk.js.map",
|
||||
"9275.3648f309.chunk.js.map": "./static/js/9275.3648f309.chunk.js.map",
|
||||
"3795.ca99e16b.chunk.js.map": "./static/js/3795.ca99e16b.chunk.js.map",
|
||||
"7314.1fb79e31.chunk.js.map": "./static/js/7314.1fb79e31.chunk.js.map",
|
||||
"7456.99d2d848.chunk.js.map": "./static/js/7456.99d2d848.chunk.js.map",
|
||||
"5673.ce92c9f5.chunk.css.map": "./static/css/5673.ce92c9f5.chunk.css.map",
|
||||
"5673.d3eb036c.chunk.js.map": "./static/js/5673.d3eb036c.chunk.js.map",
|
||||
"2699.ac935aa8.chunk.js.map": "./static/js/2699.ac935aa8.chunk.js.map",
|
||||
"5808.5e176e4d.chunk.js.map": "./static/js/5808.5e176e4d.chunk.js.map",
|
||||
"1237.f11be969.chunk.js.map": "./static/js/1237.f11be969.chunk.js.map",
|
||||
"8503.6ce0ff69.chunk.js.map": "./static/js/8503.6ce0ff69.chunk.js.map",
|
||||
"6615.f26600a0.chunk.js.map": "./static/js/6615.f26600a0.chunk.js.map",
|
||||
"9807.ce92c9f5.chunk.css.map": "./static/css/9807.ce92c9f5.chunk.css.map",
|
||||
"9807.3f033821.chunk.js.map": "./static/js/9807.3f033821.chunk.js.map",
|
||||
"3806.e2daceb3.chunk.js.map": "./static/js/3806.e2daceb3.chunk.js.map",
|
||||
"2742.f1b14cdb.chunk.js.map": "./static/js/2742.f1b14cdb.chunk.js.map",
|
||||
"3474.378d2159.chunk.js.map": "./static/js/3474.378d2159.chunk.js.map",
|
||||
"4577.ee518ba9.chunk.js.map": "./static/js/4577.ee518ba9.chunk.js.map",
|
||||
"5947.88f9751e.chunk.js.map": "./static/js/5947.88f9751e.chunk.js.map",
|
||||
"4533.e1a532b6.chunk.js.map": "./static/js/4533.e1a532b6.chunk.js.map",
|
||||
"6147.aed56e85.chunk.js.map": "./static/js/6147.aed56e85.chunk.js.map",
|
||||
"2805.73cec790.chunk.js.map": "./static/js/2805.73cec790.chunk.js.map",
|
||||
"3143.e6a9621d.chunk.js.map": "./static/js/3143.e6a9621d.chunk.js.map",
|
||||
"428.092071b6.chunk.js.map": "./static/js/428.092071b6.chunk.js.map",
|
||||
"1069.4329a890.chunk.js.map": "./static/js/1069.4329a890.chunk.js.map",
|
||||
"1140.bc981889.chunk.js.map": "./static/js/1140.bc981889.chunk.js.map",
|
||||
"2094.87c79d6e.chunk.js.map": "./static/js/2094.87c79d6e.chunk.js.map",
|
||||
"7950.8cccfb81.chunk.js.map": "./static/js/7950.8cccfb81.chunk.js.map",
|
||||
"8369.91bb6d80.chunk.js.map": "./static/js/8369.91bb6d80.chunk.js.map",
|
||||
"8961.4409f5d0.chunk.js.map": "./static/js/8961.4409f5d0.chunk.js.map",
|
||||
"3967.ead99d49.chunk.js.map": "./static/js/3967.ead99d49.chunk.js.map",
|
||||
"5677.8656472c.chunk.css.map": "./static/css/5677.8656472c.chunk.css.map",
|
||||
"5677.84696c79.chunk.js.map": "./static/js/5677.84696c79.chunk.js.map",
|
||||
"4360.8656472c.chunk.css.map": "./static/css/4360.8656472c.chunk.css.map",
|
||||
"4360.128ebb74.chunk.js.map": "./static/js/4360.128ebb74.chunk.js.map",
|
||||
"7664.5db3e36a.chunk.js.map": "./static/js/7664.5db3e36a.chunk.js.map",
|
||||
"9080.f4810646.chunk.js.map": "./static/js/9080.f4810646.chunk.js.map",
|
||||
"5961.494f0900.chunk.js.map": "./static/js/5961.494f0900.chunk.js.map",
|
||||
"428.b5539f2b.chunk.js.map": "./static/js/428.b5539f2b.chunk.js.map",
|
||||
"1069.6dfe4ad0.chunk.js.map": "./static/js/1069.6dfe4ad0.chunk.js.map",
|
||||
"1140.0208375a.chunk.js.map": "./static/js/1140.0208375a.chunk.js.map",
|
||||
"2094.4854485e.chunk.js.map": "./static/js/2094.4854485e.chunk.js.map",
|
||||
"7950.455e5528.chunk.js.map": "./static/js/7950.455e5528.chunk.js.map",
|
||||
"671.18a32329.chunk.js.map": "./static/js/671.18a32329.chunk.js.map",
|
||||
"8961.8aa802d6.chunk.js.map": "./static/js/8961.8aa802d6.chunk.js.map",
|
||||
"3967.ef703755.chunk.js.map": "./static/js/3967.ef703755.chunk.js.map",
|
||||
"5677.ce92c9f5.chunk.css.map": "./static/css/5677.ce92c9f5.chunk.css.map",
|
||||
"5677.654039a6.chunk.js.map": "./static/js/5677.654039a6.chunk.js.map",
|
||||
"4360.ce92c9f5.chunk.css.map": "./static/css/4360.ce92c9f5.chunk.css.map",
|
||||
"4360.bbbd8faf.chunk.js.map": "./static/js/4360.bbbd8faf.chunk.js.map",
|
||||
"4109.04a89c2d.chunk.js.map": "./static/js/4109.04a89c2d.chunk.js.map",
|
||||
"9080.bb419916.chunk.js.map": "./static/js/9080.bb419916.chunk.js.map",
|
||||
"5961.04fd21a3.chunk.js.map": "./static/js/5961.04fd21a3.chunk.js.map",
|
||||
"6549.e0686e32.chunk.js.map": "./static/js/6549.e0686e32.chunk.js.map",
|
||||
"7498.5bdce2df.chunk.js.map": "./static/js/7498.5bdce2df.chunk.js.map",
|
||||
"9421.fcfffa57.chunk.js.map": "./static/js/9421.fcfffa57.chunk.js.map",
|
||||
"5284.e972df04.chunk.js.map": "./static/js/5284.e972df04.chunk.js.map",
|
||||
"4818.d857d014.chunk.js.map": "./static/js/4818.d857d014.chunk.js.map",
|
||||
"2401.dab77e89.chunk.js.map": "./static/js/2401.dab77e89.chunk.js.map",
|
||||
"8724.8656472c.chunk.css.map": "./static/css/8724.8656472c.chunk.css.map",
|
||||
"8724.8dc525b3.chunk.js.map": "./static/js/8724.8dc525b3.chunk.js.map",
|
||||
"2182.d27fb965.chunk.js.map": "./static/js/2182.d27fb965.chunk.js.map",
|
||||
"7764.9f2f6b88.chunk.js.map": "./static/js/7764.9f2f6b88.chunk.js.map",
|
||||
"4220.7760227f.chunk.js.map": "./static/js/4220.7760227f.chunk.js.map",
|
||||
"1719.54b40a33.chunk.js.map": "./static/js/1719.54b40a33.chunk.js.map",
|
||||
"3320.646cbb74.chunk.js.map": "./static/js/3320.646cbb74.chunk.js.map",
|
||||
"9923.634b0622.chunk.js.map": "./static/js/9923.634b0622.chunk.js.map",
|
||||
"7498.07dd467b.chunk.js.map": "./static/js/7498.07dd467b.chunk.js.map",
|
||||
"9421.a88c8e02.chunk.js.map": "./static/js/9421.a88c8e02.chunk.js.map",
|
||||
"5284.e23070a0.chunk.js.map": "./static/js/5284.e23070a0.chunk.js.map",
|
||||
"4818.65ba088c.chunk.js.map": "./static/js/4818.65ba088c.chunk.js.map",
|
||||
"2401.74634660.chunk.js.map": "./static/js/2401.74634660.chunk.js.map",
|
||||
"8724.ce92c9f5.chunk.css.map": "./static/css/8724.ce92c9f5.chunk.css.map",
|
||||
"8724.cf8fb340.chunk.js.map": "./static/js/8724.cf8fb340.chunk.js.map",
|
||||
"2182.468a5b4e.chunk.js.map": "./static/js/2182.468a5b4e.chunk.js.map",
|
||||
"7764.3b798da3.chunk.js.map": "./static/js/7764.3b798da3.chunk.js.map",
|
||||
"4220.308c5fb3.chunk.js.map": "./static/js/4220.308c5fb3.chunk.js.map",
|
||||
"1719.fbaa408a.chunk.js.map": "./static/js/1719.fbaa408a.chunk.js.map",
|
||||
"3320.2e853989.chunk.js.map": "./static/js/3320.2e853989.chunk.js.map",
|
||||
"9923.25fa1a75.chunk.js.map": "./static/js/9923.25fa1a75.chunk.js.map",
|
||||
"9586.c44d8f2b.chunk.js.map": "./static/js/9586.c44d8f2b.chunk.js.map",
|
||||
"7261.39f723d9.chunk.js.map": "./static/js/7261.39f723d9.chunk.js.map",
|
||||
"6436.c3cf82d9.chunk.js.map": "./static/js/6436.c3cf82d9.chunk.js.map",
|
||||
"8343.c498d4af.chunk.js.map": "./static/js/8343.c498d4af.chunk.js.map",
|
||||
"2841.73580ff7.chunk.js.map": "./static/js/2841.73580ff7.chunk.js.map",
|
||||
"6167.2cf4273f.chunk.js.map": "./static/js/6167.2cf4273f.chunk.js.map",
|
||||
"3698.2ea7b498.chunk.js.map": "./static/js/3698.2ea7b498.chunk.js.map",
|
||||
"1971.52779ae6.chunk.js.map": "./static/js/1971.52779ae6.chunk.js.map",
|
||||
"7346.5ff362f5.chunk.js.map": "./static/js/7346.5ff362f5.chunk.js.map",
|
||||
"5144.5d8ad21e.chunk.js.map": "./static/js/5144.5d8ad21e.chunk.js.map",
|
||||
"5125.2efed100.chunk.js.map": "./static/js/5125.2efed100.chunk.js.map",
|
||||
"528.c588d7c6.chunk.js.map": "./static/js/528.c588d7c6.chunk.js.map",
|
||||
"7187.7c4714ee.chunk.js.map": "./static/js/7187.7c4714ee.chunk.js.map",
|
||||
"6173.3a1318da.chunk.js.map": "./static/js/6173.3a1318da.chunk.js.map",
|
||||
"7146.77e9b1cc.chunk.js.map": "./static/js/7146.77e9b1cc.chunk.js.map",
|
||||
"9924.8e40abd7.chunk.js.map": "./static/js/9924.8e40abd7.chunk.js.map",
|
||||
"9193.6764f72b.chunk.js.map": "./static/js/9193.6764f72b.chunk.js.map",
|
||||
"7451.0fa5a7e5.chunk.js.map": "./static/js/7451.0fa5a7e5.chunk.js.map",
|
||||
"6511.9ad8d9a8.chunk.js.map": "./static/js/6511.9ad8d9a8.chunk.js.map",
|
||||
"165.8656472c.chunk.css.map": "./static/css/165.8656472c.chunk.css.map",
|
||||
"165.03f5d4c1.chunk.js.map": "./static/js/165.03f5d4c1.chunk.js.map",
|
||||
"4121.b9b399ef.chunk.js.map": "./static/js/4121.b9b399ef.chunk.js.map",
|
||||
"609.3f81f01c.chunk.js.map": "./static/js/609.3f81f01c.chunk.js.map",
|
||||
"2892.1d404509.chunk.js.map": "./static/js/2892.1d404509.chunk.js.map",
|
||||
"7926.7b87452c.chunk.js.map": "./static/js/7926.7b87452c.chunk.js.map",
|
||||
"6145.1b8916cd.chunk.js.map": "./static/js/6145.1b8916cd.chunk.js.map",
|
||||
"6951.8656472c.chunk.css.map": "./static/css/6951.8656472c.chunk.css.map",
|
||||
"6951.34c85fa1.chunk.js.map": "./static/js/6951.34c85fa1.chunk.js.map",
|
||||
"2966.3df2cc1f.chunk.js.map": "./static/js/2966.3df2cc1f.chunk.js.map",
|
||||
"4177.b1d4c761.chunk.js.map": "./static/js/4177.b1d4c761.chunk.js.map",
|
||||
"9679.3942615d.chunk.js.map": "./static/js/9679.3942615d.chunk.js.map",
|
||||
"8333.a7249061.chunk.js.map": "./static/js/8333.a7249061.chunk.js.map",
|
||||
"1711.2e4f085e.chunk.js.map": "./static/js/1711.2e4f085e.chunk.js.map",
|
||||
"9.40a0afdf.chunk.js.map": "./static/js/9.40a0afdf.chunk.js.map",
|
||||
"4487.625e74d7.chunk.js.map": "./static/js/4487.625e74d7.chunk.js.map",
|
||||
"6866.eca0c7d9.chunk.js.map": "./static/js/6866.eca0c7d9.chunk.js.map",
|
||||
"8564.289a240e.chunk.js.map": "./static/js/8564.289a240e.chunk.js.map",
|
||||
"3152.4d22e959.chunk.js.map": "./static/js/3152.4d22e959.chunk.js.map",
|
||||
"7007.7afa6cca.chunk.js.map": "./static/js/7007.7afa6cca.chunk.js.map",
|
||||
"14.ec5eac10.chunk.js.map": "./static/js/14.ec5eac10.chunk.js.map",
|
||||
"892.c5842324.chunk.js.map": "./static/js/892.c5842324.chunk.js.map",
|
||||
"2066.81f72337.chunk.js.map": "./static/js/2066.81f72337.chunk.js.map",
|
||||
"7261.98082dab.chunk.js.map": "./static/js/7261.98082dab.chunk.js.map",
|
||||
"6436.f2bab163.chunk.js.map": "./static/js/6436.f2bab163.chunk.js.map",
|
||||
"8343.b174034e.chunk.js.map": "./static/js/8343.b174034e.chunk.js.map",
|
||||
"2841.95af1213.chunk.js.map": "./static/js/2841.95af1213.chunk.js.map",
|
||||
"6167.edad2368.chunk.js.map": "./static/js/6167.edad2368.chunk.js.map",
|
||||
"3698.49eb7411.chunk.js.map": "./static/js/3698.49eb7411.chunk.js.map",
|
||||
"1971.306de04b.chunk.js.map": "./static/js/1971.306de04b.chunk.js.map",
|
||||
"7346.8e455eaa.chunk.js.map": "./static/js/7346.8e455eaa.chunk.js.map",
|
||||
"5144.f66d75af.chunk.js.map": "./static/js/5144.f66d75af.chunk.js.map",
|
||||
"5125.9822771e.chunk.js.map": "./static/js/5125.9822771e.chunk.js.map",
|
||||
"1973.55987b98.chunk.js.map": "./static/js/1973.55987b98.chunk.js.map",
|
||||
"7187.b3ba71a1.chunk.js.map": "./static/js/7187.b3ba71a1.chunk.js.map",
|
||||
"6173.b70c4e1a.chunk.js.map": "./static/js/6173.b70c4e1a.chunk.js.map",
|
||||
"7146.09b3a43c.chunk.js.map": "./static/js/7146.09b3a43c.chunk.js.map",
|
||||
"9924.a7c06331.chunk.js.map": "./static/js/9924.a7c06331.chunk.js.map",
|
||||
"9193.cbd8d842.chunk.js.map": "./static/js/9193.cbd8d842.chunk.js.map",
|
||||
"7451.43cf8bf6.chunk.js.map": "./static/js/7451.43cf8bf6.chunk.js.map",
|
||||
"165.ce92c9f5.chunk.css.map": "./static/css/165.ce92c9f5.chunk.css.map",
|
||||
"165.6ba70895.chunk.js.map": "./static/js/165.6ba70895.chunk.js.map",
|
||||
"4121.17b5e70f.chunk.js.map": "./static/js/4121.17b5e70f.chunk.js.map",
|
||||
"609.218c57b6.chunk.js.map": "./static/js/609.218c57b6.chunk.js.map",
|
||||
"3421.aa3eeb04.chunk.js.map": "./static/js/3421.aa3eeb04.chunk.js.map",
|
||||
"2892.f2ab7fe7.chunk.js.map": "./static/js/2892.f2ab7fe7.chunk.js.map",
|
||||
"7926.d155ef62.chunk.js.map": "./static/js/7926.d155ef62.chunk.js.map",
|
||||
"6145.819d854c.chunk.js.map": "./static/js/6145.819d854c.chunk.js.map",
|
||||
"6951.ce92c9f5.chunk.css.map": "./static/css/6951.ce92c9f5.chunk.css.map",
|
||||
"6951.e58557fd.chunk.js.map": "./static/js/6951.e58557fd.chunk.js.map",
|
||||
"2966.c61fbf10.chunk.js.map": "./static/js/2966.c61fbf10.chunk.js.map",
|
||||
"4177.bfe448c8.chunk.js.map": "./static/js/4177.bfe448c8.chunk.js.map",
|
||||
"9679.2b8424ba.chunk.js.map": "./static/js/9679.2b8424ba.chunk.js.map",
|
||||
"8333.b0c9e377.chunk.js.map": "./static/js/8333.b0c9e377.chunk.js.map",
|
||||
"1711.a046e26c.chunk.js.map": "./static/js/1711.a046e26c.chunk.js.map",
|
||||
"9.0e073695.chunk.js.map": "./static/js/9.0e073695.chunk.js.map",
|
||||
"4487.3ad24a2d.chunk.js.map": "./static/js/4487.3ad24a2d.chunk.js.map",
|
||||
"6866.aa2f03f0.chunk.js.map": "./static/js/6866.aa2f03f0.chunk.js.map",
|
||||
"8564.11a3023b.chunk.js.map": "./static/js/8564.11a3023b.chunk.js.map",
|
||||
"3152.daa99377.chunk.js.map": "./static/js/3152.daa99377.chunk.js.map",
|
||||
"7007.3606231e.chunk.js.map": "./static/js/7007.3606231e.chunk.js.map",
|
||||
"14.25ecb28d.chunk.js.map": "./static/js/14.25ecb28d.chunk.js.map",
|
||||
"2066.5180dac0.chunk.js.map": "./static/js/2066.5180dac0.chunk.js.map",
|
||||
"5444.e8727da9.chunk.js.map": "./static/js/5444.e8727da9.chunk.js.map",
|
||||
"5594.6e7b403a.chunk.js.map": "./static/js/5594.6e7b403a.chunk.js.map",
|
||||
"3990.5e998248.chunk.js.map": "./static/js/3990.5e998248.chunk.js.map",
|
||||
"5399.5699b879.chunk.js.map": "./static/js/5399.5699b879.chunk.js.map",
|
||||
"892.2aaf58f1.chunk.js.map": "./static/js/892.2aaf58f1.chunk.js.map",
|
||||
"3690.536fb187.chunk.js.map": "./static/js/3690.536fb187.chunk.js.map",
|
||||
"5399.6bc650ac.chunk.js.map": "./static/js/5399.6bc650ac.chunk.js.map",
|
||||
"606.28fdb5bc.chunk.js.map": "./static/js/606.28fdb5bc.chunk.js.map",
|
||||
"9998.e7c54804.chunk.js.map": "./static/js/9998.e7c54804.chunk.js.map",
|
||||
"9769.733c21d3.chunk.js.map": "./static/js/9769.733c21d3.chunk.js.map",
|
||||
"9998.1a613ce5.chunk.js.map": "./static/js/9998.1a613ce5.chunk.js.map",
|
||||
"9769.a2bfb4d7.chunk.js.map": "./static/js/9769.a2bfb4d7.chunk.js.map",
|
||||
"8954.00599ff1.chunk.js.map": "./static/js/8954.00599ff1.chunk.js.map",
|
||||
"7248.1e0c4e19.chunk.js.map": "./static/js/7248.1e0c4e19.chunk.js.map",
|
||||
"4837.afb48283.chunk.js.map": "./static/js/4837.afb48283.chunk.js.map",
|
||||
"4837.98af4cfe.chunk.js.map": "./static/js/4837.98af4cfe.chunk.js.map",
|
||||
"3020.3b2e782c.chunk.js.map": "./static/js/3020.3b2e782c.chunk.js.map",
|
||||
"9056.ad0432f8.chunk.js.map": "./static/js/9056.ad0432f8.chunk.js.map",
|
||||
"9056.41f0e489.chunk.js.map": "./static/js/9056.41f0e489.chunk.js.map",
|
||||
"3360.453f0701.chunk.js.map": "./static/js/3360.453f0701.chunk.js.map",
|
||||
"7561.81edce87.chunk.js.map": "./static/js/7561.81edce87.chunk.js.map",
|
||||
"8420.68d5dfb4.chunk.js.map": "./static/js/8420.68d5dfb4.chunk.js.map",
|
||||
"1420.517f0e88.chunk.js.map": "./static/js/1420.517f0e88.chunk.js.map",
|
||||
"2309.49be65c5.chunk.js.map": "./static/js/2309.49be65c5.chunk.js.map",
|
||||
"2364.a32e93a4.chunk.js.map": "./static/js/2364.a32e93a4.chunk.js.map",
|
||||
"3356.3534f8c0.chunk.js.map": "./static/js/3356.3534f8c0.chunk.js.map",
|
||||
"1233.9903fc82.chunk.js.map": "./static/js/1233.9903fc82.chunk.js.map",
|
||||
"3356.05b42758.chunk.js.map": "./static/js/3356.05b42758.chunk.js.map",
|
||||
"8016.b0ab3b67.chunk.js.map": "./static/js/8016.b0ab3b67.chunk.js.map",
|
||||
"6851.3b47e1b9.chunk.js.map": "./static/js/6851.3b47e1b9.chunk.js.map",
|
||||
"2253.0ed59cf3.chunk.js.map": "./static/js/2253.0ed59cf3.chunk.js.map",
|
||||
"9415.8ad63e79.chunk.js.map": "./static/js/9415.8ad63e79.chunk.js.map",
|
||||
"1811.cf61844d.chunk.js.map": "./static/js/1811.cf61844d.chunk.js.map",
|
||||
"7857.cc3da495.chunk.js.map": "./static/js/7857.cc3da495.chunk.js.map",
|
||||
"3538.1b5c3c94.chunk.js.map": "./static/js/3538.1b5c3c94.chunk.js.map",
|
||||
"3290.cf0a5a06.chunk.js.map": "./static/js/3290.cf0a5a06.chunk.js.map",
|
||||
"7348.44810a11.chunk.js.map": "./static/js/7348.44810a11.chunk.js.map",
|
||||
"2253.d527f443.chunk.js.map": "./static/js/2253.d527f443.chunk.js.map",
|
||||
"6427.028089e0.chunk.js.map": "./static/js/6427.028089e0.chunk.js.map",
|
||||
"3538.60e98cef.chunk.js.map": "./static/js/3538.60e98cef.chunk.js.map",
|
||||
"8749.fa0fb710.chunk.js.map": "./static/js/8749.fa0fb710.chunk.js.map",
|
||||
"7348.b037b952.chunk.js.map": "./static/js/7348.b037b952.chunk.js.map",
|
||||
"1760.8589ea77.chunk.js.map": "./static/js/1760.8589ea77.chunk.js.map",
|
||||
"8708.1735e6e6.chunk.js.map": "./static/js/8708.1735e6e6.chunk.js.map",
|
||||
"3045.691c66fa.chunk.js.map": "./static/js/3045.691c66fa.chunk.js.map",
|
||||
"7049.620c3eec.chunk.js.map": "./static/js/7049.620c3eec.chunk.js.map",
|
||||
"2873.ebf17fc8.chunk.js.map": "./static/js/2873.ebf17fc8.chunk.js.map",
|
||||
"8708.70b62dd5.chunk.js.map": "./static/js/8708.70b62dd5.chunk.js.map",
|
||||
"2594.77f64a1f.chunk.js.map": "./static/js/2594.77f64a1f.chunk.js.map",
|
||||
"440.5bb0a106.chunk.js.map": "./static/js/440.5bb0a106.chunk.js.map",
|
||||
"3045.f1cc8d42.chunk.js.map": "./static/js/3045.f1cc8d42.chunk.js.map",
|
||||
"4817.2c511e72.chunk.js.map": "./static/js/4817.2c511e72.chunk.js.map",
|
||||
"227.5ba677a7.chunk.js.map": "./static/js/227.5ba677a7.chunk.js.map",
|
||||
"4105.1b1d6fe0.chunk.js.map": "./static/js/4105.1b1d6fe0.chunk.js.map",
|
||||
"8174.540bd07a.chunk.js.map": "./static/js/8174.540bd07a.chunk.js.map",
|
||||
"7042.6737005f.chunk.js.map": "./static/js/7042.6737005f.chunk.js.map",
|
||||
"3201.8afb9fa9.chunk.js.map": "./static/js/3201.8afb9fa9.chunk.js.map",
|
||||
"1724.30bcf6f8.chunk.js.map": "./static/js/1724.30bcf6f8.chunk.js.map",
|
||||
"7454.eb7fb0a0.chunk.js.map": "./static/js/7454.eb7fb0a0.chunk.js.map",
|
||||
"6873.177d160e.chunk.js.map": "./static/js/6873.177d160e.chunk.js.map",
|
||||
"3453.3c290198.chunk.js.map": "./static/js/3453.3c290198.chunk.js.map"
|
||||
"938.8f6a5aa1.chunk.js.map": "./static/js/938.8f6a5aa1.chunk.js.map",
|
||||
"4105.a498ca05.chunk.js.map": "./static/js/4105.a498ca05.chunk.js.map",
|
||||
"9876.3e5a4f83.chunk.js.map": "./static/js/9876.3e5a4f83.chunk.js.map",
|
||||
"7777.7ea62662.chunk.js.map": "./static/js/7777.7ea62662.chunk.js.map",
|
||||
"7042.343b48b2.chunk.js.map": "./static/js/7042.343b48b2.chunk.js.map",
|
||||
"3201.9f8a570f.chunk.js.map": "./static/js/3201.9f8a570f.chunk.js.map",
|
||||
"7664.9ba57569.chunk.js.map": "./static/js/7664.9ba57569.chunk.js.map",
|
||||
"5170.2d3119fa.chunk.js.map": "./static/js/5170.2d3119fa.chunk.js.map",
|
||||
"7454.6a33b546.chunk.js.map": "./static/js/7454.6a33b546.chunk.js.map",
|
||||
"6873.a4c0dde3.chunk.js.map": "./static/js/6873.a4c0dde3.chunk.js.map",
|
||||
"3453.9ef32892.chunk.js.map": "./static/js/3453.9ef32892.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.c4c1effe.css",
|
||||
"static/js/main.e413bbf1.js"
|
||||
"static/js/main.c7ee1a1d.js"
|
||||
]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="/"/><meta content="width=device-width,initial-scale=1" name="viewport"/><meta content="#081C42" media="(prefers-color-scheme: light)" name="theme-color"/><meta content="#081C42" media="(prefers-color-scheme: dark)" name="theme-color"/><meta content="MinIO Console" name="description"/><link href="./styles/root-styles.css" rel="stylesheet"/><link href="./apple-icon-180x180.png" rel="apple-touch-icon" sizes="180x180"/><link href="./favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"/><link href="./favicon-96x96.png" rel="icon" sizes="96x96" type="image/png"/><link href="./favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"/><link href="./manifest.json" rel="manifest"/><link color="#3a4e54" href="./safari-pinned-tab.svg" rel="mask-icon"/><title>MinIO Console</title><script defer="defer" src="./static/js/main.e413bbf1.js"></script><link href="./static/css/main.c4c1effe.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="loader-block"><svg class="loader-svg-container" viewBox="22 22 44 44"><circle class="loader-style MuiCircularProgress-circle MuiCircularProgress-circleIndeterminate" cx="44" cy="44" fill="none" r="20.2" stroke-width="3.6"></circle></svg></div></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="/"/><meta content="width=device-width,initial-scale=1" name="viewport"/><meta content="#081C42" media="(prefers-color-scheme: light)" name="theme-color"/><meta content="#081C42" media="(prefers-color-scheme: dark)" name="theme-color"/><meta content="MinIO Console" name="description"/><link href="./styles/root-styles.css" rel="stylesheet"/><link href="./apple-icon-180x180.png" rel="apple-touch-icon" sizes="180x180"/><link href="./favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"/><link href="./favicon-96x96.png" rel="icon" sizes="96x96" type="image/png"/><link href="./favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"/><link href="./manifest.json" rel="manifest"/><link color="#3a4e54" href="./safari-pinned-tab.svg" rel="mask-icon"/><title>MinIO Console</title><script defer="defer" src="./static/js/main.c7ee1a1d.js"></script><link href="./static/css/main.c4c1effe.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="loader-block"><svg class="loader-svg-container" viewBox="22 22 44 44"><circle class="loader-style MuiCircularProgress-circle MuiCircularProgress-circleIndeterminate" cx="44" cy="44" fill="none" r="20.2" stroke-width="3.6"></circle></svg></div></div></body></html>
|
||||
@@ -1,2 +1,2 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=165.8656472c.chunk.css.map*/
|
||||
/*# sourceMappingURL=165.ce92c9f5.chunk.css.map*/
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"static/css/165.8656472c.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
{"version":3,"file":"static/css/165.ce92c9f5.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
@@ -1,2 +1,2 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=4360.8656472c.chunk.css.map*/
|
||||
/*# sourceMappingURL=4360.ce92c9f5.chunk.css.map*/
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"static/css/4360.8656472c.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
{"version":3,"file":"static/css/4360.ce92c9f5.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
@@ -1,2 +1,2 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=5677.8656472c.chunk.css.map*/
|
||||
/*# sourceMappingURL=5673.ce92c9f5.chunk.css.map*/
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"static/css/5677.8656472c.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
{"version":3,"file":"static/css/5673.ce92c9f5.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
@@ -1,2 +1,2 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=6696.8656472c.chunk.css.map*/
|
||||
/*# sourceMappingURL=5677.ce92c9f5.chunk.css.map*/
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"static/css/6696.8656472c.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
{"version":3,"file":"static/css/5677.ce92c9f5.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=6951.8656472c.chunk.css.map*/
|
||||
2
portal-ui/build/static/css/6951.ce92c9f5.chunk.css
Normal file
2
portal-ui/build/static/css/6951.ce92c9f5.chunk.css
Normal file
@@ -0,0 +1,2 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=6951.ce92c9f5.chunk.css.map*/
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"static/css/6951.8656472c.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
{"version":3,"file":"static/css/6951.ce92c9f5.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=8724.8656472c.chunk.css.map*/
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"static/css/8724.8656472c.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
2
portal-ui/build/static/css/8724.ce92c9f5.chunk.css
Normal file
2
portal-ui/build/static/css/8724.ce92c9f5.chunk.css
Normal file
@@ -0,0 +1,2 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=8724.ce92c9f5.chunk.css.map*/
|
||||
1
portal-ui/build/static/css/8724.ce92c9f5.chunk.css.map
Normal file
1
portal-ui/build/static/css/8724.ce92c9f5.chunk.css.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"static/css/8724.ce92c9f5.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
@@ -1,2 +0,0 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=9807.8656472c.chunk.css.map*/
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"static/css/9807.8656472c.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
2
portal-ui/build/static/css/9807.ce92c9f5.chunk.css
Normal file
2
portal-ui/build/static/css/9807.ce92c9f5.chunk.css
Normal file
@@ -0,0 +1,2 @@
|
||||
.cm-s-dracula.CodeMirror,.cm-s-dracula .CodeMirror-gutters{background-color:#282a36!important;border:none;color:#f8f8f2!important}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:thin solid #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:hsla(0,0%,100%,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-keyword,.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute,.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:hsla(0,0%,100%,.1)}.cm-s-dracula .CodeMirror-matchingbracket{color:#fff!important;text-decoration:underline}
|
||||
/*# sourceMappingURL=9807.ce92c9f5.chunk.css.map*/
|
||||
1
portal-ui/build/static/css/9807.ce92c9f5.chunk.css.map
Normal file
1
portal-ui/build/static/css/9807.ce92c9f5.chunk.css.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"static/css/9807.ce92c9f5.chunk.css","mappings":"AAUA,2DACE,kCAAoC,CAEpC,WAAY,CADZ,uBAEF,CACA,kCAAoC,aAAgB,CACpD,iCAAmC,8BAAiC,CACpE,qCAAuC,aAAgB,CACvD,mCAAqC,6BAAuC,CAC5E,6IAAuJ,6BAAuC,CAC9L,4JAAsK,6BAAuC,CAC7M,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAC/E,6BAA+B,aAAgB,CAC/C,+BAAiC,aAAgB,CACjD,iCAAmC,UAAc,CACjD,0BAA4B,aAAgB,CAE5C,6DAAgC,aAAgB,CAChD,2BAA6B,aAAgB,CAC7C,2BAA6B,aAAgB,CAC7C,0BAA4B,aAAgB,CAE5C,gEAAkC,aAAgB,CAClD,+BAAiC,aAAgB,CACjD,8BAAgC,aAAgB,CAChD,4DAA+D,aAAgB,CAE/E,gDAAkD,6BAAmC,CACrF,0CAAwE,oBAAuB,CAAnD,yBAAqD","sources":["../node_modules/codemirror/theme/dracula.css"],"sourcesContent":["/*\n\n Name: dracula\n Author: Michael Kaminsky (http://github.com/mkaminsky11)\n\n Original dracula color scheme by Zeno Rocha (https://github.com/zenorocha/dracula-theme)\n\n*/\n\n\n.cm-s-dracula.CodeMirror, .cm-s-dracula .CodeMirror-gutters {\n background-color: #282a36 !important;\n color: #f8f8f2 !important;\n border: none;\n}\n.cm-s-dracula .CodeMirror-gutters { color: #282a36; }\n.cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; }\n.cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; }\n.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); }\n.cm-s-dracula span.cm-comment { color: #6272a4; }\n.cm-s-dracula span.cm-string, .cm-s-dracula span.cm-string-2 { color: #f1fa8c; }\n.cm-s-dracula span.cm-number { color: #bd93f9; }\n.cm-s-dracula span.cm-variable { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-2 { color: white; }\n.cm-s-dracula span.cm-def { color: #50fa7b; }\n.cm-s-dracula span.cm-operator { color: #ff79c6; }\n.cm-s-dracula span.cm-keyword { color: #ff79c6; }\n.cm-s-dracula span.cm-atom { color: #bd93f9; }\n.cm-s-dracula span.cm-meta { color: #f8f8f2; }\n.cm-s-dracula span.cm-tag { color: #ff79c6; }\n.cm-s-dracula span.cm-attribute { color: #50fa7b; }\n.cm-s-dracula span.cm-qualifier { color: #50fa7b; }\n.cm-s-dracula span.cm-property { color: #66d9ef; }\n.cm-s-dracula span.cm-builtin { color: #50fa7b; }\n.cm-s-dracula span.cm-variable-3, .cm-s-dracula span.cm-type { color: #ffb86c; }\n\n.cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); }\n.cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }\n"],"names":[],"sourceRoot":""}
|
||||
2
portal-ui/build/static/js/1056.6f1cb8e0.chunk.js
Normal file
2
portal-ui/build/static/js/1056.6f1cb8e0.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/1056.6f1cb8e0.chunk.js.map
Normal file
1
portal-ui/build/static/js/1056.6f1cb8e0.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
portal-ui/build/static/js/1069.6dfe4ad0.chunk.js
Normal file
2
portal-ui/build/static/js/1069.6dfe4ad0.chunk.js
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user