Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9301e3b7de | ||
|
|
cf5e5a14b5 | ||
|
|
7f4546e879 | ||
|
|
1a92c59e3f | ||
|
|
ade9731773 | ||
|
|
cc43b3c743 | ||
|
|
b29f6a1640 | ||
|
|
62b8258989 | ||
|
|
75bc568e4b | ||
|
|
b0119a55df | ||
|
|
78983ce76f | ||
|
|
e060e1d97e | ||
|
|
82bdc228b2 | ||
|
|
1fa8311af7 | ||
|
|
93243f2c77 | ||
|
|
3b423826fd | ||
|
|
e44a7c94c6 |
94
.github/workflows/common.sh
vendored
Executable file
94
.github/workflows/common.sh
vendored
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (C) 2022, MinIO, Inc.
|
||||
#
|
||||
# This code is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License, version 3,
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# 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, version 3,
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
yell() { echo "$0: $*" >&2; }
|
||||
|
||||
die() {
|
||||
yell "$*"
|
||||
(kind delete cluster || true ) && exit 111
|
||||
}
|
||||
|
||||
try() { "$@" || die "cannot $*"; }
|
||||
|
||||
function setup_kind() {
|
||||
# TODO once feature is added: https://github.com/kubernetes-sigs/kind/issues/1300
|
||||
echo "kind: Cluster" > kind-config.yaml
|
||||
echo "apiVersion: kind.x-k8s.io/v1alpha4" >> kind-config.yaml
|
||||
echo "nodes:" >> kind-config.yaml
|
||||
echo " - role: control-plane" >> kind-config.yaml
|
||||
echo " - role: worker" >> kind-config.yaml
|
||||
echo " - role: worker" >> kind-config.yaml
|
||||
echo " - role: worker" >> kind-config.yaml
|
||||
echo " - role: worker" >> kind-config.yaml
|
||||
try kind create cluster --config kind-config.yaml
|
||||
echo "Kind is ready"
|
||||
try kubectl get nodes
|
||||
}
|
||||
|
||||
function install_operator() {
|
||||
echo "Installing Current Operator"
|
||||
|
||||
# TODO: Compile the current branch and create an overlay to use that image version
|
||||
try kubectl apply -k "${SCRIPT_DIR}/../../portal-ui/tests/scripts/resources"
|
||||
|
||||
echo "Waiting for k8s api"
|
||||
sleep 10
|
||||
echo "Waiting for Operator Pods to come online (2m timeout)"
|
||||
|
||||
try kubectl wait --namespace minio-operator \
|
||||
--for=condition=ready pod \
|
||||
--selector=name=minio-operator \
|
||||
--timeout=120s
|
||||
}
|
||||
|
||||
function destroy_kind() {
|
||||
kind delete cluster
|
||||
}
|
||||
|
||||
function check_tenant_status() {
|
||||
# Check MinIO is accessible
|
||||
|
||||
waitdone=0
|
||||
totalwait=0
|
||||
while true; do
|
||||
waitdone=$(kubectl -n $1 get pods -l v1.min.io/tenant=$2 --no-headers | wc -l)
|
||||
if [ "$waitdone" -ne 0 ]; then
|
||||
echo "Found $waitdone pods"
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
totalwait=$((totalwait + 5))
|
||||
if [ "$totalwait" -gt 305 ]; then
|
||||
echo "Unable to create tenant after 5 minutes, exiting."
|
||||
try false
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Waiting for pods to be ready. (5m timeout)"
|
||||
|
||||
USER=$(kubectl -n $1 get secrets $2-env-configuration -o go-template='{{index .data "config.env"|base64decode }}' | grep 'export MINIO_ROOT_USER="' | sed -e 's/export MINIO_ROOT_USER="//g' | sed -e 's/"//g')
|
||||
PASSWORD=$(kubectl -n $1 get secrets $2-env-configuration -o go-template='{{index .data "config.env"|base64decode }}' | grep 'export MINIO_ROOT_PASSWORD="' | sed -e 's/export MINIO_ROOT_PASSWORD="//g' | sed -e 's/"//g')
|
||||
|
||||
try kubectl wait --namespace $1 \
|
||||
--for=condition=ready pod \
|
||||
--selector=v1.min.io/tenant=$2 \
|
||||
--timeout=300s
|
||||
|
||||
echo "Tenant is created successfully, proceeding to validate 'mc admin info minio/'"
|
||||
|
||||
kubectl run admin-mc -i --tty --image minio/mc --command -- bash -c "until (mc alias set minio/ https://minio.$1.svc.cluster.local $USER $PASSWORD); do echo \"...waiting... for 5secs\" && sleep 5; done; mc admin info minio/;"
|
||||
|
||||
echo "Done."
|
||||
}
|
||||
71
.github/workflows/deploy-tenant.sh
vendored
Executable file
71
.github/workflows/deploy-tenant.sh
vendored
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (C) 2022, MinIO, Inc.
|
||||
#
|
||||
# This code is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License, version 3,
|
||||
# as published by the Free Software Foundation.
|
||||
#
|
||||
# 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, version 3,
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
# This script requires: kubectl, kind
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
export SCRIPT_DIR
|
||||
|
||||
source "${SCRIPT_DIR}/common.sh"
|
||||
|
||||
|
||||
|
||||
function install_tenant() {
|
||||
echo "Installing lite tenant"
|
||||
|
||||
try kubectl apply -k "${SCRIPT_DIR}/../../portal-ui/tests/scripts/tenant"
|
||||
|
||||
echo "Waiting for the tenant statefulset, this indicates the tenant is being fulfilled"
|
||||
waitdone=0
|
||||
totalwait=0
|
||||
while true; do
|
||||
waitdone=$(kubectl -n tenant-lite get pods -l v1.min.io/tenant=storage-lite --no-headers | wc -l)
|
||||
if [ "$waitdone" -ne 0 ]; then
|
||||
echo "Found $waitdone pods"
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
totalwait=$((totalwait + 5))
|
||||
if [ "$totalwait" -gt 300 ]; then
|
||||
echo "Tenant never created statefulset after 5 minutes"
|
||||
try false
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Waiting for tenant pods to come online (5m timeout)"
|
||||
try kubectl wait --namespace tenant-lite \
|
||||
--for=condition=ready pod \
|
||||
--selector="v1.min.io/tenant=storage-lite" \
|
||||
--timeout=300s
|
||||
|
||||
echo "Build passes basic tenant creation"
|
||||
}
|
||||
|
||||
|
||||
function main() {
|
||||
destroy_kind
|
||||
|
||||
setup_kind
|
||||
|
||||
install_operator
|
||||
|
||||
install_tenant
|
||||
|
||||
check_tenant_status tenant-lite storage-lite
|
||||
|
||||
kubectl -n minio-operator port-forward svc/console 9090 &
|
||||
}
|
||||
|
||||
main "$@"
|
||||
75
.github/workflows/jobs.yaml
vendored
75
.github/workflows/jobs.yaml
vendored
@@ -15,6 +15,56 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
operator-api-tests:
|
||||
|
||||
name: Operator API Tests
|
||||
needs:
|
||||
- lint-job
|
||||
- no-warnings-and-make-assets
|
||||
- reuse-golang-dependencies
|
||||
- vulnerable-dependencies-checks
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ 1.17.x ]
|
||||
|
||||
steps:
|
||||
|
||||
- name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
id: go
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/cache@v2
|
||||
name: Go Mod Cache
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ github.run_id }}
|
||||
|
||||
- name: Operator API Tests
|
||||
run: |
|
||||
curl -sLO "https://dl.k8s.io/release/v1.23.1/bin/linux/amd64/kubectl" -o kubectl
|
||||
chmod +x kubectl
|
||||
mv kubectl /usr/local/bin
|
||||
"${GITHUB_WORKSPACE}/.github/workflows/deploy-tenant.sh"
|
||||
echo "start ---> make test-operator-integration";
|
||||
make test-operator-integration;
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: coverage-cache-operator
|
||||
name: Coverage Cache Operator
|
||||
with:
|
||||
path: |
|
||||
./operator-integration/coverage/
|
||||
key: ${{ runner.os }}-coverage-2-operator-${{ github.run_id }}
|
||||
|
||||
lint-job:
|
||||
name: Checking Lint
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -566,7 +616,7 @@ jobs:
|
||||
id: go
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '17'
|
||||
node-version: '16'
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2
|
||||
@@ -639,7 +689,7 @@ jobs:
|
||||
id: go
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '17'
|
||||
node-version: '16'
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -702,6 +752,8 @@ jobs:
|
||||
needs:
|
||||
- integration-tests
|
||||
- test-restapi-on-go
|
||||
- operator-api-tests
|
||||
- test-pkg-on-go
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -713,9 +765,6 @@ jobs:
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
id: go
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '17'
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
- name: Check out gocovmerge as a nested repository
|
||||
@@ -739,6 +788,14 @@ jobs:
|
||||
./integration/coverage/
|
||||
key: ${{ runner.os }}-coverage-2-${{ github.run_id }}
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: coverage-cache-operator
|
||||
name: Coverage Cache Operator
|
||||
with:
|
||||
path: |
|
||||
./operator-integration/coverage/
|
||||
key: ${{ runner.os }}-coverage-2-operator-${{ github.run_id }}
|
||||
|
||||
- uses: actions/cache@v2
|
||||
id: coverage-cache-restapi
|
||||
name: Coverage Cache RestAPI
|
||||
@@ -766,17 +823,17 @@ jobs:
|
||||
echo "go build gocoverage.go"
|
||||
go build gocovmerge.go
|
||||
echo "put together the outs for final coverage resolution"
|
||||
./gocovmerge ../integration/coverage/system.out ../restapi/coverage/coverage.out ../pkg/coverage/coverage-pkg.out > all.out
|
||||
./gocovmerge ../integration/coverage/system.out ../restapi/coverage/coverage.out ../pkg/coverage/coverage-pkg.out ../operator-integration/coverage/operator-api.out > all.out
|
||||
echo "grep to obtain the result"
|
||||
go tool cover -func=all.out | grep total > tmp2
|
||||
result=`cat tmp2 | awk 'END {print $3}'`
|
||||
result=${result%\%}
|
||||
echo "result:"
|
||||
echo $result
|
||||
threshold=49.4
|
||||
threshold=50.5
|
||||
if (( $(echo "$result >= $threshold" |bc -l) )); then
|
||||
echo "greater than threshold, passed!"
|
||||
echo "It is equal or greater than threshold, passed!"
|
||||
else
|
||||
echo "smaller than threshold, failed!"
|
||||
echo "It is smaller than threshold value, failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
5
Makefile
5
Makefile
@@ -80,6 +80,11 @@ test-integration:
|
||||
@(docker stop minio)
|
||||
@(docker network rm mynet123)
|
||||
|
||||
test-operator-integration:
|
||||
@(echo "Start cd operator-integration && go test:")
|
||||
@(pwd)
|
||||
@(cd operator-integration && go test -coverpkg=../restapi -c -tags testrunmain . && mkdir -p coverage && ./operator-integration.test -test.run "^Test*" -test.coverprofile=coverage/operator-api.out)
|
||||
|
||||
test-operator:
|
||||
@(env bash $(PWD)/portal-ui/tests/scripts/operator.sh)
|
||||
@(docker stop minio)
|
||||
|
||||
@@ -61,13 +61,6 @@ func GetNsFromFile() string {
|
||||
// Namespace will run only once at console startup
|
||||
var Namespace = GetNsFromFile()
|
||||
|
||||
var latestMinIOImage, errLatestMinIOImage = utils.GetLatestMinIOImage(
|
||||
&utils.HTTPClient{
|
||||
Client: &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
},
|
||||
})
|
||||
|
||||
// GetMinioImage returns the image URL to be used when deploying a MinIO instance, if there is
|
||||
// a preferred image to be used (configured via ENVIRONMENT VARIABLES) GetMinioImage will return that
|
||||
// if not, GetMinioImage will try to obtain the image URL for the latest version of MinIO and return that
|
||||
@@ -77,17 +70,15 @@ func GetMinioImage() (*string, error) {
|
||||
if image != "" {
|
||||
return &image, nil
|
||||
}
|
||||
latestMinIOImage, errLatestMinIOImage := utils.GetLatestMinIOImage(
|
||||
&utils.HTTPClient{
|
||||
Client: &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
})
|
||||
|
||||
if errLatestMinIOImage != nil {
|
||||
return nil, errLatestMinIOImage
|
||||
}
|
||||
return latestMinIOImage, nil
|
||||
}
|
||||
|
||||
// GetLatestMinioImage returns the latest image URL on minio repository
|
||||
func GetLatestMinioImage(client utils.HTTPClientI) (*string, error) {
|
||||
latestMinIOImage, err := utils.GetLatestMinIOImage(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return latestMinIOImage, nil
|
||||
}
|
||||
|
||||
@@ -3103,3 +3103,469 @@ func DisableBucketEncryption(bucketName string) (*http.Response, error) {
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func UpdateLifecycleRule(bucketName string, Type string, disable bool, prefix string, tags string, expiredObjectDeleteMarker bool, expiryDays int64, noncurrentversionExpirationDays int64, lifecycleID string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to update lifecycle rule
|
||||
HTTP Verb: PUT
|
||||
URL: /buckets/{bucket_name}/lifecycle/{lifecycle_id}
|
||||
Body Example:
|
||||
{
|
||||
"type":"expiry",
|
||||
"disable":false,
|
||||
"prefix":"",
|
||||
"tags":"",
|
||||
"expired_object_delete_marker":false,
|
||||
"expiry_days":2,
|
||||
"noncurrentversion_expiration_days":0
|
||||
}
|
||||
*/
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"type": Type,
|
||||
"disable": disable,
|
||||
"prefix": prefix,
|
||||
"tags": tags,
|
||||
"expired_object_delete_marker": expiredObjectDeleteMarker,
|
||||
"expiry_days": expiryDays,
|
||||
"noncurrentversion_expiration_days": noncurrentversionExpirationDays,
|
||||
}
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest("PUT",
|
||||
"http://localhost:9090/api/v1/buckets/"+bucketName+"/lifecycle/"+lifecycleID,
|
||||
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 GetBucketLifeCycle(bucketName string) (*http.Response, error) {
|
||||
/*
|
||||
Get Bucket Lifecycle
|
||||
HTTP Verb: GET
|
||||
URL: /buckets/{bucket_name}/lifecycle
|
||||
Response Example:
|
||||
{
|
||||
"lifecycle": [
|
||||
{
|
||||
"expiration": {
|
||||
"date": "0001-01-01T00:00:00Z",
|
||||
"days": 1
|
||||
},
|
||||
"id": "c8nmpte49b3m6uu3pac0",
|
||||
"status": "Enabled",
|
||||
"tags": null,
|
||||
"transition": {
|
||||
"date": "0001-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
request, err := http.NewRequest(
|
||||
"GET", "http://localhost:9090/api/v1/buckets/"+bucketName+"/lifecycle", nil)
|
||||
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 AddBucketLifecycle(bucketName string, Type string, prefix string, tags string, expiredObjectDeleteMarker bool, expiryDays int64, noncurrentversionExpirationDays int64) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to add bucket lifecycle
|
||||
URL: /buckets/{bucket_name}/lifecycle
|
||||
HTTP Verb: POST
|
||||
Body Example:
|
||||
{
|
||||
"type":"expiry",
|
||||
"prefix":"",
|
||||
"tags":"",
|
||||
"expired_object_delete_marker":false,
|
||||
"expiry_days":1,
|
||||
"noncurrentversion_expiration_days":null
|
||||
}
|
||||
*/
|
||||
// Needed Parameters for API Call
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"type": Type,
|
||||
"prefix": prefix,
|
||||
"tags": tags,
|
||||
"expired_object_delete_marker": expiredObjectDeleteMarker,
|
||||
"expiry_days": expiryDays,
|
||||
"noncurrentversion_expiration_days": noncurrentversionExpirationDays,
|
||||
}
|
||||
|
||||
// Creating the Call by adding the URL and Headers
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"POST",
|
||||
"http://localhost:9090/api/v1/buckets/"+bucketName+"/lifecycle",
|
||||
requestDataBody,
|
||||
)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
request.Header.Add("Cookie", fmt.Sprintf("token=%s", token))
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
// Performing the call
|
||||
client := &http.Client{
|
||||
Timeout: 2 * time.Second,
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
return response, err
|
||||
}
|
||||
|
||||
func DeleteLifecycleRule(bucketName string, lifecycleID string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to delete lifecycle rule
|
||||
HTTP Verb: DELETE
|
||||
URL: /buckets/{bucket_name}/lifecycle/{lifecycle_id}
|
||||
*/
|
||||
request, err := http.NewRequest(
|
||||
"DELETE", "http://localhost:9090/api/v1/buckets/"+bucketName+"/lifecycle/"+lifecycleID, nil)
|
||||
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 TestBucketLifeCycle(t *testing.T) {
|
||||
|
||||
printStartFunc("TestBucketLifeCycle")
|
||||
|
||||
// Variables
|
||||
assert := assert.New(t)
|
||||
bucketName := "test-bucket-life-cycle"
|
||||
locking := false
|
||||
versioning := false
|
||||
Type := "expiry"
|
||||
prefix := ""
|
||||
tags := ""
|
||||
var expiryDays int64 = 1
|
||||
var expiryDays2 int64 = 2
|
||||
disable := false
|
||||
expiredObjectDeleteMarker := false
|
||||
var noncurrentversionExpirationDays int64
|
||||
|
||||
// 1. Add bucket
|
||||
if !BucketGotAdded(bucketName, locking, versioning, nil, nil, assert, 201) {
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Add Bucket Lifecycle
|
||||
resp, err := AddBucketLifecycle(
|
||||
bucketName,
|
||||
Type,
|
||||
prefix,
|
||||
tags,
|
||||
expiredObjectDeleteMarker,
|
||||
expiryDays,
|
||||
noncurrentversionExpirationDays,
|
||||
)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
201, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 3. Get Bucket LifeCycle
|
||||
resp, err = GetBucketLifeCycle(bucketName)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
200, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
bodyBytes, _ := ioutil.ReadAll(resp.Body)
|
||||
result := models.BucketLifecycleResponse{}
|
||||
err = json.Unmarshal(bodyBytes, &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
assert.Nil(err)
|
||||
}
|
||||
Status := &result.Lifecycle[0].Status
|
||||
Days := &result.Lifecycle[0].Expiration.Days
|
||||
lifecycleID := &result.Lifecycle[0].ID
|
||||
assert.Equal(expiryDays, *Days, *Days) // Checking it is one day expiration
|
||||
assert.Equal("Enabled", *Status, *Status) // Checking it's enabled
|
||||
|
||||
// 4. Update from 1 day expiration to 2 days expiration
|
||||
resp, err = UpdateLifecycleRule(
|
||||
bucketName,
|
||||
Type,
|
||||
disable,
|
||||
prefix,
|
||||
tags,
|
||||
expiredObjectDeleteMarker,
|
||||
expiryDays2,
|
||||
noncurrentversionExpirationDays,
|
||||
*lifecycleID,
|
||||
)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
200, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 5. Verify 2 expiration days got updated
|
||||
resp, err = GetBucketLifeCycle(bucketName)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
200, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
bodyBytes, _ = ioutil.ReadAll(resp.Body)
|
||||
result = models.BucketLifecycleResponse{}
|
||||
err = json.Unmarshal(bodyBytes, &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
assert.Nil(err)
|
||||
}
|
||||
Days = &result.Lifecycle[0].Expiration.Days
|
||||
assert.Equal(expiryDays2, *Days, *Days) // Checking it is two days expiration
|
||||
|
||||
// 6. Delete Bucket Lifecycle
|
||||
resp, err = DeleteLifecycleRule(bucketName, *lifecycleID)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
204, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 6. Verify bucket lifecycle got deleted
|
||||
resp, err = GetBucketLifeCycle(bucketName)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
404, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
printEndFunc("TestBucketLifeCycle")
|
||||
}
|
||||
|
||||
func SetAccessRuleWithBucket(bucketName string, prefix string, access string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to Set Access Rule within Bucket
|
||||
HTTP Verb: PUT
|
||||
URL: /bucket/{bucket}/access-rules
|
||||
Data Example:
|
||||
{
|
||||
"prefix":"prefix",
|
||||
"access":"readonly"
|
||||
}
|
||||
*/
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"prefix": prefix,
|
||||
"access": access,
|
||||
}
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"PUT",
|
||||
"http://localhost:9090/api/v1/bucket/"+bucketName+"/access-rules",
|
||||
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 ListAccessRulesWithBucket(bucketName string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to List Access Rules Within Bucket
|
||||
HTTP Verb: GET
|
||||
URL: /bucket/{bucket}/access-rules
|
||||
*/
|
||||
request, err := http.NewRequest(
|
||||
"GET",
|
||||
"http://localhost:9090/api/v1/bucket/"+bucketName+"/access-rules", nil)
|
||||
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 DeleteAccessRuleWithBucket(bucketName string, prefix string) (*http.Response, error) {
|
||||
/*
|
||||
Helper function to Delete Access Rule With Bucket
|
||||
HTTP Verb: DELETE
|
||||
URL: /bucket/{bucket}/access-rules
|
||||
Data Example: {"prefix":"prefix"}
|
||||
*/
|
||||
requestDataAdd := map[string]interface{}{
|
||||
"prefix": prefix,
|
||||
}
|
||||
requestDataJSON, _ := json.Marshal(requestDataAdd)
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
request, err := http.NewRequest(
|
||||
"DELETE",
|
||||
"http://localhost:9090/api/v1/bucket/"+bucketName+"/access-rules",
|
||||
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 TestAccessRule(t *testing.T) {
|
||||
|
||||
printStartFunc("TestAccessRule")
|
||||
|
||||
// Variables
|
||||
assert := assert.New(t)
|
||||
bucketName := "test-access-rule-bucket"
|
||||
locking := false
|
||||
versioning := false
|
||||
prefix := "prefix"
|
||||
access := "readonly"
|
||||
|
||||
// 1. Add bucket
|
||||
if !BucketGotAdded(bucketName, locking, versioning, nil, nil, assert, 201) {
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Set Access Rule With Bucket
|
||||
resp, err := SetAccessRuleWithBucket(
|
||||
bucketName,
|
||||
prefix,
|
||||
access,
|
||||
)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
200, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 3. List Access Rule
|
||||
resp, err = ListAccessRulesWithBucket(bucketName)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
200, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
bodyBytes, _ := ioutil.ReadAll(resp.Body)
|
||||
result := models.ListAccessRulesResponse{}
|
||||
err = json.Unmarshal(bodyBytes, &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
assert.Nil(err)
|
||||
}
|
||||
Access := &result.AccessRules[0].Access
|
||||
Prefix := &result.AccessRules[0].Prefix
|
||||
assert.Equal("readonly", *Access, *Access)
|
||||
assert.Equal("prefix", *Prefix, *Prefix)
|
||||
|
||||
// 4. Delete Access Rule
|
||||
resp, err = DeleteAccessRuleWithBucket(
|
||||
bucketName,
|
||||
prefix,
|
||||
)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
200, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
|
||||
// 5. Verify access rule was deleted
|
||||
resp, err = ListAccessRulesWithBucket(bucketName)
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
200, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
bodyBytes, _ = ioutil.ReadAll(resp.Body)
|
||||
result = models.ListAccessRulesResponse{}
|
||||
err = json.Unmarshal(bodyBytes, &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
assert.Nil(err)
|
||||
}
|
||||
AccessRules := &result.AccessRules // The array has to be empty, no index accessible
|
||||
if len(*AccessRules) == 0 {
|
||||
printMessage("Cool, access rules are gone from this bucket")
|
||||
} else {
|
||||
assert.Fail("Access Rule not deleted")
|
||||
}
|
||||
|
||||
printEndFunc("TestAccessRule")
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
serviceAccountName: console-sa
|
||||
containers:
|
||||
- name: console
|
||||
image: 'minio/console:v0.15.2'
|
||||
image: 'minio/console:v0.15.3'
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_OPERATOR_MODE
|
||||
|
||||
@@ -32,7 +32,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: console
|
||||
image: 'minio/console:v0.15.2'
|
||||
image: 'minio/console:v0.15.3'
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
env:
|
||||
- name: CONSOLE_MINIO_SERVER
|
||||
|
||||
269
operator-integration/tenant_test.go
Normal file
269
operator-integration/tenant_test.go
Normal file
@@ -0,0 +1,269 @@
|
||||
// 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 operatorintegration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
b64 "encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var token string
|
||||
|
||||
func decodeBase64(value string) string {
|
||||
/*
|
||||
Helper function to decode in base64
|
||||
*/
|
||||
result, err := b64.StdEncoding.DecodeString(value)
|
||||
if err != nil {
|
||||
log.Fatal("error:", err)
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func printMessage(message string) {
|
||||
/*
|
||||
Helper function to print HTTP response.
|
||||
*/
|
||||
fmt.Println(message)
|
||||
}
|
||||
|
||||
func printLoggingMessage(message string, functionName string) {
|
||||
/*
|
||||
Helper function to have standard output across the tests.
|
||||
*/
|
||||
finalString := "......................." + functionName + "(): " + message
|
||||
printMessage(finalString)
|
||||
}
|
||||
|
||||
func printStartFunc(functionName string) {
|
||||
/*
|
||||
Common function for all tests to tell that test has started
|
||||
*/
|
||||
printMessage("")
|
||||
printLoggingMessage("started", functionName)
|
||||
}
|
||||
|
||||
func printEndFunc(functionName string) {
|
||||
/*
|
||||
Helper function for all tests to tell that test has ended, is completed
|
||||
*/
|
||||
printLoggingMessage("completed", functionName)
|
||||
printMessage("")
|
||||
}
|
||||
|
||||
func initConsoleServer() (*restapi.Server, error) {
|
||||
|
||||
//os.Setenv("CONSOLE_MINIO_SERVER", "localhost:9000")
|
||||
|
||||
swaggerSpec, err := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
noLog := func(string, ...interface{}) {
|
||||
// nothing to log
|
||||
}
|
||||
|
||||
// Initialize MinIO loggers
|
||||
restapi.LogInfo = noLog
|
||||
restapi.LogError = noLog
|
||||
|
||||
api := operations.NewConsoleAPI(swaggerSpec)
|
||||
api.Logger = noLog
|
||||
|
||||
server := restapi.NewServer(api)
|
||||
// register all APIs
|
||||
server.ConfigureAPI()
|
||||
|
||||
//restapi.GlobalRootCAs, restapi.GlobalPublicCerts, restapi.GlobalTLSCertsManager = globalRootCAs, globalPublicCerts, globalTLSCerts
|
||||
|
||||
consolePort, _ := strconv.Atoi("9090")
|
||||
|
||||
server.Host = "0.0.0.0"
|
||||
server.Port = consolePort
|
||||
restapi.Port = "9090"
|
||||
restapi.Hostname = "0.0.0.0"
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
printStartFunc("TestMain")
|
||||
// start console server
|
||||
go func() {
|
||||
fmt.Println("start server")
|
||||
srv, err := initConsoleServer()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
log.Println("init fail")
|
||||
return
|
||||
}
|
||||
srv.Serve()
|
||||
|
||||
}()
|
||||
|
||||
fmt.Println("sleeping")
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: 2 * time.Second,
|
||||
}
|
||||
|
||||
// kubectl to get token
|
||||
app := "kubectl"
|
||||
arg0 := "get"
|
||||
arg1 := "serviceaccount"
|
||||
arg2 := "console-sa"
|
||||
arg3 := "--namespace"
|
||||
arg4 := "minio-operator"
|
||||
arg5 := "-o"
|
||||
arg6 := "jsonpath=\"{.secrets[0].name}\""
|
||||
cmd := exec.Command(app, arg0, arg1, arg2, arg3, arg4, arg5, arg6)
|
||||
var out bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = &stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
|
||||
return
|
||||
}
|
||||
secret := out.String() // "console-sa-token-kxdw2" <-- secret
|
||||
app2 := "kubectl"
|
||||
argu0 := "--namespace"
|
||||
argu1 := "minio-operator"
|
||||
argu2 := "get"
|
||||
argu3 := "secret"
|
||||
argu4 := secret[1 : len(secret)-1]
|
||||
argu5 := "-o"
|
||||
argu6 := "jsonpath=\"{.data.token}\""
|
||||
cmd2 := exec.Command(app2, argu0, argu1, argu2, argu3, argu4, argu5, argu6)
|
||||
var out2 bytes.Buffer
|
||||
var stderr2 bytes.Buffer
|
||||
cmd2.Stdout = &out2
|
||||
cmd2.Stderr = &stderr2
|
||||
err2 := cmd2.Run()
|
||||
if err2 != nil {
|
||||
fmt.Println(fmt.Sprint(err2) + ": -> " + stderr2.String())
|
||||
return
|
||||
}
|
||||
secret2 := out2.String()
|
||||
secret3 := decodeBase64(secret2[1 : len(secret2)-1])
|
||||
requestData := map[string]string{
|
||||
"jwt": secret3,
|
||||
}
|
||||
|
||||
requestDataJSON, _ := json.Marshal(requestData)
|
||||
|
||||
requestDataBody := bytes.NewReader(requestDataJSON)
|
||||
|
||||
request, err := http.NewRequest("POST", "http://localhost:9090/api/v1/login/operator", requestDataBody)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
|
||||
response, err := client.Do(request)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if response != nil {
|
||||
for _, cookie := range response.Cookies() {
|
||||
if cookie.Name == "token" {
|
||||
token = cookie.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
log.Println("authentication token not found in cookies response")
|
||||
return
|
||||
}
|
||||
|
||||
code := m.Run()
|
||||
printEndFunc("TestMain")
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func ListTenants() (*http.Response, error) {
|
||||
/*
|
||||
Helper function to list buckets
|
||||
HTTP Verb: GET
|
||||
URL: http://localhost:9090/api/v1/tenants
|
||||
*/
|
||||
request, err := http.NewRequest(
|
||||
"GET", "http://localhost:9090/api/v1/tenants", nil)
|
||||
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 TestListTenants(t *testing.T) {
|
||||
// Tenants can be listed via API: https://github.com/miniohq/engineering/issues/591
|
||||
printStartFunc("TestListTenants")
|
||||
assert := assert.New(t)
|
||||
resp, err := ListTenants()
|
||||
assert.Nil(err)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if resp != nil {
|
||||
assert.Equal(
|
||||
200, resp.StatusCode, "Status Code is incorrect")
|
||||
}
|
||||
bodyBytes, _ := ioutil.ReadAll(resp.Body)
|
||||
result := models.ListTenantsResponse{}
|
||||
err = json.Unmarshal(bodyBytes, &result)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
assert.Nil(err)
|
||||
}
|
||||
TenantName := &result.Tenants[0].Name // The array has to be empty, no index accessible
|
||||
fmt.Println(*TenantName)
|
||||
assert.Equal("storage-lite", *TenantName, *TenantName)
|
||||
printEndFunc("TestListTenants")
|
||||
}
|
||||
156
operatorapi/tenant_get.go
Normal file
156
operatorapi/tenant_get.go
Normal file
@@ -0,0 +1,156 @@
|
||||
// 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 operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
"github.com/minio/console/restapi"
|
||||
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func getTenantDetailsResponse(session *models.Principal, params operator_api.TenantDetailsParams) (*models.Tenant, *models.Error) {
|
||||
// 5 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
info := getTenantInfo(minTenant)
|
||||
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
|
||||
tenantConfiguration, err := GetTenantConfiguration(ctx, &k8sClient, minTenant)
|
||||
if err != nil {
|
||||
restapi.LogError("unable to fetch configuration for tenant %s: %v", minTenant.Name, err)
|
||||
}
|
||||
|
||||
// detect if AD/LDAP is enabled
|
||||
ldapEnabled := false
|
||||
if string(tenantConfiguration["MINIO_IDENTITY_LDAP_SERVER_ADDR"]) != "" {
|
||||
ldapEnabled = true
|
||||
}
|
||||
|
||||
// detect if OpenID is enabled
|
||||
oidcEnabled := false
|
||||
if string(tenantConfiguration["MINIO_IDENTITY_OPENID_CONFIG_URL"]) != "" {
|
||||
oidcEnabled = true
|
||||
}
|
||||
|
||||
// detect if encryption is enabled
|
||||
if minTenant.HasKESEnabled() || string(tenantConfiguration["MINIO_KMS_SECRET_KEY"]) != "" {
|
||||
info.EncryptionEnabled = true
|
||||
}
|
||||
|
||||
info.LogEnabled = minTenant.HasLogEnabled()
|
||||
info.MonitoringEnabled = minTenant.HasPrometheusEnabled()
|
||||
info.IdpAdEnabled = ldapEnabled
|
||||
info.IdpOidcEnabled = oidcEnabled
|
||||
info.MinioTLS = minTenant.TLS()
|
||||
|
||||
// attach status information
|
||||
info.Status = &models.TenantStatus{
|
||||
HealthStatus: string(minTenant.Status.HealthStatus),
|
||||
DrivesHealing: minTenant.Status.DrivesHealing,
|
||||
DrivesOffline: minTenant.Status.DrivesOffline,
|
||||
DrivesOnline: minTenant.Status.DrivesOnline,
|
||||
WriteQuorum: minTenant.Status.WriteQuorum,
|
||||
Usage: &models.TenantStatusUsage{
|
||||
Raw: minTenant.Status.Usage.RawCapacity,
|
||||
RawUsage: minTenant.Status.Usage.RawUsage,
|
||||
Capacity: minTenant.Status.Usage.Capacity,
|
||||
CapacityUsage: minTenant.Status.Usage.Usage,
|
||||
},
|
||||
}
|
||||
|
||||
// get tenant service
|
||||
minTenant.EnsureDefaults()
|
||||
//minio service
|
||||
minSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.MinIOCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
restapi.LogError("Unable to get MinIO service name: %v, continuing", err)
|
||||
}
|
||||
//console service
|
||||
conSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.ConsoleCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
restapi.LogError("Unable to get MinIO console service name: %v, continuing", err)
|
||||
}
|
||||
|
||||
schema := "http"
|
||||
consoleSchema := "http"
|
||||
consolePort := fmt.Sprintf(":%d", miniov2.ConsolePort)
|
||||
if minTenant.TLS() {
|
||||
schema = "https"
|
||||
consoleSchema = "https"
|
||||
consolePort = fmt.Sprintf(":%d", miniov2.ConsoleTLSPort)
|
||||
}
|
||||
var minioEndpoint string
|
||||
var consoleEndpoint string
|
||||
if minSvc != nil && len(minSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
if minSvc.Status.LoadBalancer.Ingress[0].IP != "" {
|
||||
minioEndpoint = fmt.Sprintf("%s://%s", schema, minSvc.Status.LoadBalancer.Ingress[0].IP)
|
||||
}
|
||||
|
||||
if minSvc.Status.LoadBalancer.Ingress[0].Hostname != "" {
|
||||
minioEndpoint = fmt.Sprintf("%s://%s", schema, minSvc.Status.LoadBalancer.Ingress[0].Hostname)
|
||||
}
|
||||
|
||||
}
|
||||
if conSvc != nil && len(conSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
if conSvc.Status.LoadBalancer.Ingress[0].IP != "" {
|
||||
consoleEndpoint = fmt.Sprintf("%s://%s%s", consoleSchema, conSvc.Status.LoadBalancer.Ingress[0].IP, consolePort)
|
||||
}
|
||||
if conSvc.Status.LoadBalancer.Ingress[0].Hostname != "" {
|
||||
consoleEndpoint = fmt.Sprintf("%s://%s%s", consoleSchema, conSvc.Status.LoadBalancer.Ingress[0].Hostname, consolePort)
|
||||
}
|
||||
}
|
||||
if minioEndpoint != "" || consoleEndpoint != "" {
|
||||
info.Endpoints = &models.TenantEndpoints{
|
||||
Console: consoleEndpoint,
|
||||
Minio: minioEndpoint,
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
103
operatorapi/tenant_update.go
Normal file
103
operatorapi/tenant_update.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// 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 operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
utils2 "github.com/minio/console/pkg/utils"
|
||||
"github.com/minio/console/restapi"
|
||||
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
// updateTenantAction does an update on the minioTenant by patching the desired changes
|
||||
func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, clientset v1.CoreV1Interface, httpCl utils2.HTTPClientI, namespace string, params operator_api.UpdateTenantParams) error {
|
||||
imageToUpdate := params.Body.Image
|
||||
imageRegistryReq := params.Body.ImageRegistry
|
||||
|
||||
minInst, err := operatorClient.TenantGet(ctx, namespace, params.Tenant, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we can take either the `image_pull_secret` of the `image_registry` but not both
|
||||
if params.Body.ImagePullSecret != "" {
|
||||
minInst.Spec.ImagePullSecret.Name = params.Body.ImagePullSecret
|
||||
} else {
|
||||
// update the image pull secret content
|
||||
if _, err := setImageRegistry(ctx, imageRegistryReq, clientset, namespace, params.Tenant); err != nil {
|
||||
restapi.LogError("error setting image registry secret: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// if image to update is empty we'll use the latest image by default
|
||||
if strings.TrimSpace(imageToUpdate) != "" {
|
||||
minInst.Spec.Image = imageToUpdate
|
||||
} else {
|
||||
im, err := utils2.GetLatestMinIOImage(httpCl)
|
||||
// if we can't get the MinIO image, we won' auto-update it unless it's explicit by name
|
||||
if err == nil {
|
||||
minInst.Spec.Image = *im
|
||||
}
|
||||
}
|
||||
|
||||
// Prometheus Annotations
|
||||
currentAnnotations := minInst.Annotations
|
||||
prometheusAnnotations := map[string]string{
|
||||
prometheusPath: "/minio/prometheus/metrics",
|
||||
prometheusPort: fmt.Sprint(miniov2.MinIOPort),
|
||||
prometheusScrape: "true",
|
||||
}
|
||||
if params.Body.EnablePrometheus && currentAnnotations != nil {
|
||||
// add prometheus annotations to the tenant
|
||||
minInst.Annotations = addAnnotations(currentAnnotations, prometheusAnnotations)
|
||||
// add prometheus annotations to the each pool
|
||||
if minInst.Spec.Pools != nil {
|
||||
for _, pool := range minInst.Spec.Pools {
|
||||
poolAnnotations := pool.VolumeClaimTemplate.GetObjectMeta().GetAnnotations()
|
||||
pool.VolumeClaimTemplate.GetObjectMeta().SetAnnotations(addAnnotations(poolAnnotations, prometheusAnnotations))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// remove prometheus annotations to the tenant
|
||||
minInst.Annotations = removeAnnotations(currentAnnotations, prometheusAnnotations)
|
||||
// add prometheus annotations from each pool
|
||||
if minInst.Spec.Pools != nil {
|
||||
for _, pool := range minInst.Spec.Pools {
|
||||
poolAnnotations := pool.VolumeClaimTemplate.GetObjectMeta().GetAnnotations()
|
||||
pool.VolumeClaimTemplate.GetObjectMeta().SetAnnotations(removeAnnotations(poolAnnotations, prometheusAnnotations))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(minInst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = operatorClient.TenantPatch(ctx, namespace, minInst.Name, types.MergePatchType, payloadBytes, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -528,132 +528,6 @@ func getTenantInfo(tenant *miniov2.Tenant) *models.Tenant {
|
||||
}
|
||||
}
|
||||
|
||||
func getTenantDetailsResponse(session *models.Principal, params operator_api.TenantDetailsParams) (*models.Tenant, *models.Error) {
|
||||
// 5 seconds timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
info := getTenantInfo(minTenant)
|
||||
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
|
||||
tenantConfiguration, err := GetTenantConfiguration(ctx, &k8sClient, minTenant)
|
||||
if err != nil {
|
||||
restapi.LogError("unable to fetch configuration for tenant %s: %v", minTenant.Name, err)
|
||||
}
|
||||
|
||||
// detect if AD/LDAP is enabled
|
||||
ldapEnabled := false
|
||||
if string(tenantConfiguration["MINIO_IDENTITY_LDAP_SERVER_ADDR"]) != "" {
|
||||
ldapEnabled = true
|
||||
}
|
||||
|
||||
// detect if OpenID is enabled
|
||||
oidcEnabled := false
|
||||
if string(tenantConfiguration["MINIO_IDENTITY_OPENID_CONFIG_URL"]) != "" {
|
||||
oidcEnabled = true
|
||||
}
|
||||
|
||||
// detect if encryption is enabled
|
||||
if minTenant.HasKESEnabled() || string(tenantConfiguration["MINIO_KMS_SECRET_KEY"]) != "" {
|
||||
info.EncryptionEnabled = true
|
||||
}
|
||||
|
||||
info.LogEnabled = minTenant.HasLogEnabled()
|
||||
info.MonitoringEnabled = minTenant.HasPrometheusEnabled()
|
||||
info.IdpAdEnabled = ldapEnabled
|
||||
info.IdpOidcEnabled = oidcEnabled
|
||||
info.MinioTLS = minTenant.TLS()
|
||||
|
||||
// attach status information
|
||||
info.Status = &models.TenantStatus{
|
||||
HealthStatus: string(minTenant.Status.HealthStatus),
|
||||
DrivesHealing: minTenant.Status.DrivesHealing,
|
||||
DrivesOffline: minTenant.Status.DrivesOffline,
|
||||
DrivesOnline: minTenant.Status.DrivesOnline,
|
||||
WriteQuorum: minTenant.Status.WriteQuorum,
|
||||
Usage: &models.TenantStatusUsage{
|
||||
Raw: minTenant.Status.Usage.RawCapacity,
|
||||
RawUsage: minTenant.Status.Usage.RawUsage,
|
||||
Capacity: minTenant.Status.Usage.Capacity,
|
||||
CapacityUsage: minTenant.Status.Usage.Usage,
|
||||
},
|
||||
}
|
||||
|
||||
// get tenant service
|
||||
minTenant.EnsureDefaults()
|
||||
//minio service
|
||||
minSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.MinIOCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
restapi.LogError("Unable to get MinIO service name: %v, continuing", err)
|
||||
}
|
||||
//console service
|
||||
conSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.ConsoleCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
restapi.LogError("Unable to get MinIO console service name: %v, continuing", err)
|
||||
}
|
||||
|
||||
schema := "http"
|
||||
consoleSchema := "http"
|
||||
consolePort := fmt.Sprintf(":%d", miniov2.ConsolePort)
|
||||
if minTenant.TLS() {
|
||||
schema = "https"
|
||||
consoleSchema = "https"
|
||||
consolePort = fmt.Sprintf(":%d", miniov2.ConsoleTLSPort)
|
||||
}
|
||||
var minioEndpoint string
|
||||
var consoleEndpoint string
|
||||
if minSvc != nil && len(minSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
if minSvc.Status.LoadBalancer.Ingress[0].IP != "" {
|
||||
minioEndpoint = fmt.Sprintf("%s://%s", schema, minSvc.Status.LoadBalancer.Ingress[0].IP)
|
||||
}
|
||||
|
||||
if minSvc.Status.LoadBalancer.Ingress[0].Hostname != "" {
|
||||
minioEndpoint = fmt.Sprintf("%s://%s", schema, minSvc.Status.LoadBalancer.Ingress[0].Hostname)
|
||||
}
|
||||
|
||||
}
|
||||
if conSvc != nil && len(conSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
if conSvc.Status.LoadBalancer.Ingress[0].IP != "" {
|
||||
consoleEndpoint = fmt.Sprintf("%s://%s%s", consoleSchema, conSvc.Status.LoadBalancer.Ingress[0].IP, consolePort)
|
||||
}
|
||||
if conSvc.Status.LoadBalancer.Ingress[0].Hostname != "" {
|
||||
consoleEndpoint = fmt.Sprintf("%s://%s%s", consoleSchema, conSvc.Status.LoadBalancer.Ingress[0].Hostname, consolePort)
|
||||
}
|
||||
}
|
||||
if minioEndpoint != "" || consoleEndpoint != "" {
|
||||
info.Endpoints = &models.TenantEndpoints{
|
||||
Console: consoleEndpoint,
|
||||
Minio: minioEndpoint,
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func parseCertificate(name string, rawCert []byte) (*models.CertificateInfo, error) {
|
||||
block, _ := pem.Decode(rawCert)
|
||||
if block == nil {
|
||||
@@ -1040,77 +914,6 @@ func setImageRegistry(ctx context.Context, req *models.ImageRegistry, clientset
|
||||
return pullSecretName, nil
|
||||
}
|
||||
|
||||
// updateTenantAction does an update on the minioTenant by patching the desired changes
|
||||
func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, clientset v1.CoreV1Interface, httpCl utils2.HTTPClientI, namespace string, params operator_api.UpdateTenantParams) error {
|
||||
imageToUpdate := params.Body.Image
|
||||
imageRegistryReq := params.Body.ImageRegistry
|
||||
|
||||
minInst, err := operatorClient.TenantGet(ctx, namespace, params.Tenant, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we can take either the `image_pull_secret` of the `image_registry` but not both
|
||||
if params.Body.ImagePullSecret != "" {
|
||||
minInst.Spec.ImagePullSecret.Name = params.Body.ImagePullSecret
|
||||
} else {
|
||||
// update the image pull secret content
|
||||
if _, err := setImageRegistry(ctx, imageRegistryReq, clientset, namespace, params.Tenant); err != nil {
|
||||
restapi.LogError("error setting image registry secret: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// if image to update is empty we'll use the latest image by default
|
||||
if strings.TrimSpace(imageToUpdate) != "" {
|
||||
minInst.Spec.Image = imageToUpdate
|
||||
} else {
|
||||
im, err := cluster.GetLatestMinioImage(httpCl)
|
||||
// if we can't get the MinIO image, we won' auto-update it unless it's explicit by name
|
||||
if err == nil {
|
||||
minInst.Spec.Image = *im
|
||||
}
|
||||
}
|
||||
|
||||
// Prometheus Annotations
|
||||
currentAnnotations := minInst.Annotations
|
||||
prometheusAnnotations := map[string]string{
|
||||
prometheusPath: "/minio/prometheus/metrics",
|
||||
prometheusPort: fmt.Sprint(miniov2.MinIOPort),
|
||||
prometheusScrape: "true",
|
||||
}
|
||||
if params.Body.EnablePrometheus && currentAnnotations != nil {
|
||||
// add prometheus annotations to the tenant
|
||||
minInst.Annotations = addAnnotations(currentAnnotations, prometheusAnnotations)
|
||||
// add prometheus annotations to the each pool
|
||||
if minInst.Spec.Pools != nil {
|
||||
for _, pool := range minInst.Spec.Pools {
|
||||
poolAnnotations := pool.VolumeClaimTemplate.GetObjectMeta().GetAnnotations()
|
||||
pool.VolumeClaimTemplate.GetObjectMeta().SetAnnotations(addAnnotations(poolAnnotations, prometheusAnnotations))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// remove prometheus annotations to the tenant
|
||||
minInst.Annotations = removeAnnotations(currentAnnotations, prometheusAnnotations)
|
||||
// add prometheus annotations from each pool
|
||||
if minInst.Spec.Pools != nil {
|
||||
for _, pool := range minInst.Spec.Pools {
|
||||
poolAnnotations := pool.VolumeClaimTemplate.GetObjectMeta().GetAnnotations()
|
||||
pool.VolumeClaimTemplate.GetObjectMeta().SetAnnotations(removeAnnotations(poolAnnotations, prometheusAnnotations))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(minInst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = operatorClient.TenantPatch(ctx, namespace, minInst.Name, types.MergePatchType, payloadBytes, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addAnnotations will merge two annotation maps
|
||||
func addAnnotations(annotationsOne, annotationsTwo map[string]string) map[string]string {
|
||||
if annotationsOne == nil {
|
||||
@@ -1,30 +1,30 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.90d417ae.css",
|
||||
"main.js": "./static/js/main.69273a1d.js",
|
||||
"main.js": "./static/js/main.fd4d26b1.js",
|
||||
"static/js/1660.1a3b5397.chunk.js": "./static/js/1660.1a3b5397.chunk.js",
|
||||
"static/js/5282.cb13b8c4.chunk.js": "./static/js/5282.cb13b8c4.chunk.js",
|
||||
"static/js/2818.a52e5730.chunk.js": "./static/js/2818.a52e5730.chunk.js",
|
||||
"static/js/9560.6e9b7779.chunk.js": "./static/js/9560.6e9b7779.chunk.js",
|
||||
"static/js/9661.92a4e63a.chunk.js": "./static/js/9661.92a4e63a.chunk.js",
|
||||
"static/js/9330.122bf1a7.chunk.js": "./static/js/9330.122bf1a7.chunk.js",
|
||||
"static/js/7436.fed787a2.chunk.js": "./static/js/7436.fed787a2.chunk.js",
|
||||
"static/js/8428.f4d88368.chunk.js": "./static/js/8428.f4d88368.chunk.js",
|
||||
"static/js/9779.fed48861.chunk.js": "./static/js/9779.fed48861.chunk.js",
|
||||
"static/js/3617.b62a9bb2.chunk.js": "./static/js/3617.b62a9bb2.chunk.js",
|
||||
"static/js/7274.9c913af6.chunk.js": "./static/js/7274.9c913af6.chunk.js",
|
||||
"static/js/7842.8dfd596a.chunk.js": "./static/js/7842.8dfd596a.chunk.js",
|
||||
"static/js/4745.27ab197f.chunk.js": "./static/js/4745.27ab197f.chunk.js",
|
||||
"static/js/1796.26462172.chunk.js": "./static/js/1796.26462172.chunk.js",
|
||||
"static/js/8259.3b2561ab.chunk.js": "./static/js/8259.3b2561ab.chunk.js",
|
||||
"static/js/6023.aec2d9d1.chunk.js": "./static/js/6023.aec2d9d1.chunk.js",
|
||||
"static/js/2818.21be97b7.chunk.js": "./static/js/2818.21be97b7.chunk.js",
|
||||
"static/js/9560.bf1e7276.chunk.js": "./static/js/9560.bf1e7276.chunk.js",
|
||||
"static/js/9661.1e636902.chunk.js": "./static/js/9661.1e636902.chunk.js",
|
||||
"static/js/9330.ea489cb0.chunk.js": "./static/js/9330.ea489cb0.chunk.js",
|
||||
"static/js/7436.d9fafb61.chunk.js": "./static/js/7436.d9fafb61.chunk.js",
|
||||
"static/js/8428.d94dd7f0.chunk.js": "./static/js/8428.d94dd7f0.chunk.js",
|
||||
"static/js/9779.868ea932.chunk.js": "./static/js/9779.868ea932.chunk.js",
|
||||
"static/js/3617.c5deb048.chunk.js": "./static/js/3617.c5deb048.chunk.js",
|
||||
"static/js/7274.5c2a8fde.chunk.js": "./static/js/7274.5c2a8fde.chunk.js",
|
||||
"static/js/7842.151c41a8.chunk.js": "./static/js/7842.151c41a8.chunk.js",
|
||||
"static/js/4745.3fab4951.chunk.js": "./static/js/4745.3fab4951.chunk.js",
|
||||
"static/js/1796.840a4e41.chunk.js": "./static/js/1796.840a4e41.chunk.js",
|
||||
"static/js/8259.7c8fbb38.chunk.js": "./static/js/8259.7c8fbb38.chunk.js",
|
||||
"static/js/6023.d2f6ea4d.chunk.js": "./static/js/6023.d2f6ea4d.chunk.js",
|
||||
"static/js/6147.f2a7d841.chunk.js": "./static/js/6147.f2a7d841.chunk.js",
|
||||
"static/js/9275.6a834668.chunk.js": "./static/js/9275.6a834668.chunk.js",
|
||||
"static/js/8190.d56fddaa.chunk.js": "./static/js/8190.d56fddaa.chunk.js",
|
||||
"static/js/7314.0058a98f.chunk.js": "./static/js/7314.0058a98f.chunk.js",
|
||||
"static/js/9275.2fec5cc7.chunk.js": "./static/js/9275.2fec5cc7.chunk.js",
|
||||
"static/js/8190.13d7b7e6.chunk.js": "./static/js/8190.13d7b7e6.chunk.js",
|
||||
"static/js/7314.01eaeb0f.chunk.js": "./static/js/7314.01eaeb0f.chunk.js",
|
||||
"static/js/7456.e2ad891d.chunk.js": "./static/js/7456.e2ad891d.chunk.js",
|
||||
"static/css/5822.dd80c1b6.chunk.css": "./static/css/5822.dd80c1b6.chunk.css",
|
||||
"static/js/5822.c1d62c79.chunk.js": "./static/js/5822.c1d62c79.chunk.js",
|
||||
"static/js/5822.fa49be62.chunk.js": "./static/js/5822.fa49be62.chunk.js",
|
||||
"static/js/2699.3102be0f.chunk.js": "./static/js/2699.3102be0f.chunk.js",
|
||||
"static/js/5808.8da21793.chunk.js": "./static/js/5808.8da21793.chunk.js",
|
||||
"static/js/1237.b46ec019.chunk.js": "./static/js/1237.b46ec019.chunk.js",
|
||||
@@ -32,29 +32,29 @@
|
||||
"static/css/964.dd80c1b6.chunk.css": "./static/css/964.dd80c1b6.chunk.css",
|
||||
"static/js/964.c1751e75.chunk.js": "./static/js/964.c1751e75.chunk.js",
|
||||
"static/css/9807.dd80c1b6.chunk.css": "./static/css/9807.dd80c1b6.chunk.css",
|
||||
"static/js/9807.731eb17a.chunk.js": "./static/js/9807.731eb17a.chunk.js",
|
||||
"static/js/3806.b5b2c03c.chunk.js": "./static/js/3806.b5b2c03c.chunk.js",
|
||||
"static/js/8661.ade9e15c.chunk.js": "./static/js/8661.ade9e15c.chunk.js",
|
||||
"static/js/2886.a03a185d.chunk.js": "./static/js/2886.a03a185d.chunk.js",
|
||||
"static/js/9807.ee1a2bdb.chunk.js": "./static/js/9807.ee1a2bdb.chunk.js",
|
||||
"static/js/3806.8464efb7.chunk.js": "./static/js/3806.8464efb7.chunk.js",
|
||||
"static/js/8661.e8f2397f.chunk.js": "./static/js/8661.e8f2397f.chunk.js",
|
||||
"static/js/2886.92b60a3b.chunk.js": "./static/js/2886.92b60a3b.chunk.js",
|
||||
"static/js/4577.cd5b16fc.chunk.js": "./static/js/4577.cd5b16fc.chunk.js",
|
||||
"static/js/4298.e6c437dd.chunk.js": "./static/js/4298.e6c437dd.chunk.js",
|
||||
"static/js/2805.b4302353.chunk.js": "./static/js/2805.b4302353.chunk.js",
|
||||
"static/js/6873.27fbf284.chunk.js": "./static/js/6873.27fbf284.chunk.js",
|
||||
"static/js/428.ad58c353.chunk.js": "./static/js/428.ad58c353.chunk.js",
|
||||
"static/js/1069.77bb316e.chunk.js": "./static/js/1069.77bb316e.chunk.js",
|
||||
"static/js/9080.884725e7.chunk.js": "./static/js/9080.884725e7.chunk.js",
|
||||
"static/js/9080.d84213b3.chunk.js": "./static/js/9080.d84213b3.chunk.js",
|
||||
"static/js/3276.1afa9e35.chunk.js": "./static/js/3276.1afa9e35.chunk.js",
|
||||
"static/js/6458.c6bf60f8.chunk.js": "./static/js/6458.c6bf60f8.chunk.js",
|
||||
"static/js/7551.b8d5fd21.chunk.js": "./static/js/7551.b8d5fd21.chunk.js",
|
||||
"static/js/7950.1793a548.chunk.js": "./static/js/7950.1793a548.chunk.js",
|
||||
"static/js/290.3a78b888.chunk.js": "./static/js/290.3a78b888.chunk.js",
|
||||
"static/js/8961.c7471e8d.chunk.js": "./static/js/8961.c7471e8d.chunk.js",
|
||||
"static/js/3967.eefb50e4.chunk.js": "./static/js/3967.eefb50e4.chunk.js",
|
||||
"static/js/7950.0e4eac80.chunk.js": "./static/js/7950.0e4eac80.chunk.js",
|
||||
"static/js/290.8bfc0b83.chunk.js": "./static/js/290.8bfc0b83.chunk.js",
|
||||
"static/js/8961.37cf027e.chunk.js": "./static/js/8961.37cf027e.chunk.js",
|
||||
"static/js/3967.2cb35507.chunk.js": "./static/js/3967.2cb35507.chunk.js",
|
||||
"static/css/4084.dd80c1b6.chunk.css": "./static/css/4084.dd80c1b6.chunk.css",
|
||||
"static/js/4084.57e7784d.chunk.js": "./static/js/4084.57e7784d.chunk.js",
|
||||
"static/js/4084.34cce774.chunk.js": "./static/js/4084.34cce774.chunk.js",
|
||||
"static/js/8394.fa043a82.chunk.js": "./static/js/8394.fa043a82.chunk.js",
|
||||
"static/js/7664.b099b462.chunk.js": "./static/js/7664.b099b462.chunk.js",
|
||||
"static/js/1140.044ef809.chunk.js": "./static/js/1140.044ef809.chunk.js",
|
||||
"static/js/1140.ab5579e6.chunk.js": "./static/js/1140.ab5579e6.chunk.js",
|
||||
"static/js/5961.7a71f2c5.chunk.js": "./static/js/5961.7a71f2c5.chunk.js",
|
||||
"static/js/2401.dcfa0659.chunk.js": "./static/js/2401.dcfa0659.chunk.js",
|
||||
"static/js/7498.adcacaca.chunk.js": "./static/js/7498.adcacaca.chunk.js",
|
||||
@@ -64,7 +64,7 @@
|
||||
"static/js/6549.bc76401b.chunk.js": "./static/js/6549.bc76401b.chunk.js",
|
||||
"static/css/8724.dd80c1b6.chunk.css": "./static/css/8724.dd80c1b6.chunk.css",
|
||||
"static/js/8724.2fda7628.chunk.js": "./static/js/8724.2fda7628.chunk.js",
|
||||
"static/js/2182.e1389a8d.chunk.js": "./static/js/2182.e1389a8d.chunk.js",
|
||||
"static/js/2182.45ee7ad0.chunk.js": "./static/js/2182.45ee7ad0.chunk.js",
|
||||
"static/js/7764.7133a78a.chunk.js": "./static/js/7764.7133a78a.chunk.js",
|
||||
"static/js/4220.8d9a9028.chunk.js": "./static/js/4220.8d9a9028.chunk.js",
|
||||
"static/js/1719.a1c5fbc9.chunk.js": "./static/js/1719.a1c5fbc9.chunk.js",
|
||||
@@ -148,38 +148,38 @@
|
||||
"static/js/9831.1b5a6bb6.chunk.js": "./static/js/9831.1b5a6bb6.chunk.js",
|
||||
"static/js/9382.581734f3.chunk.js": "./static/js/9382.581734f3.chunk.js",
|
||||
"static/js/8174.6e95ea0c.chunk.js": "./static/js/8174.6e95ea0c.chunk.js",
|
||||
"static/js/1116.6c0eed8e.chunk.js": "./static/js/1116.6c0eed8e.chunk.js",
|
||||
"static/js/1116.824f2d28.chunk.js": "./static/js/1116.824f2d28.chunk.js",
|
||||
"static/js/6430.e747de7c.chunk.js": "./static/js/6430.e747de7c.chunk.js",
|
||||
"static/js/4966.c825dc1c.chunk.js": "./static/js/4966.c825dc1c.chunk.js",
|
||||
"static/js/2363.5fc6aebe.chunk.js": "./static/js/2363.5fc6aebe.chunk.js",
|
||||
"static/js/2464.5dc72a90.chunk.js": "./static/js/2464.5dc72a90.chunk.js",
|
||||
"static/js/7520.087167a1.chunk.js": "./static/js/7520.087167a1.chunk.js",
|
||||
"static/js/2464.ea29031c.chunk.js": "./static/js/2464.ea29031c.chunk.js",
|
||||
"static/js/7520.609b689b.chunk.js": "./static/js/7520.609b689b.chunk.js",
|
||||
"index.html": "./index.html",
|
||||
"main.90d417ae.css.map": "./static/css/main.90d417ae.css.map",
|
||||
"main.69273a1d.js.map": "./static/js/main.69273a1d.js.map",
|
||||
"main.fd4d26b1.js.map": "./static/js/main.fd4d26b1.js.map",
|
||||
"1660.1a3b5397.chunk.js.map": "./static/js/1660.1a3b5397.chunk.js.map",
|
||||
"5282.cb13b8c4.chunk.js.map": "./static/js/5282.cb13b8c4.chunk.js.map",
|
||||
"2818.a52e5730.chunk.js.map": "./static/js/2818.a52e5730.chunk.js.map",
|
||||
"9560.6e9b7779.chunk.js.map": "./static/js/9560.6e9b7779.chunk.js.map",
|
||||
"9661.92a4e63a.chunk.js.map": "./static/js/9661.92a4e63a.chunk.js.map",
|
||||
"9330.122bf1a7.chunk.js.map": "./static/js/9330.122bf1a7.chunk.js.map",
|
||||
"7436.fed787a2.chunk.js.map": "./static/js/7436.fed787a2.chunk.js.map",
|
||||
"8428.f4d88368.chunk.js.map": "./static/js/8428.f4d88368.chunk.js.map",
|
||||
"9779.fed48861.chunk.js.map": "./static/js/9779.fed48861.chunk.js.map",
|
||||
"3617.b62a9bb2.chunk.js.map": "./static/js/3617.b62a9bb2.chunk.js.map",
|
||||
"7274.9c913af6.chunk.js.map": "./static/js/7274.9c913af6.chunk.js.map",
|
||||
"7842.8dfd596a.chunk.js.map": "./static/js/7842.8dfd596a.chunk.js.map",
|
||||
"4745.27ab197f.chunk.js.map": "./static/js/4745.27ab197f.chunk.js.map",
|
||||
"1796.26462172.chunk.js.map": "./static/js/1796.26462172.chunk.js.map",
|
||||
"8259.3b2561ab.chunk.js.map": "./static/js/8259.3b2561ab.chunk.js.map",
|
||||
"6023.aec2d9d1.chunk.js.map": "./static/js/6023.aec2d9d1.chunk.js.map",
|
||||
"2818.21be97b7.chunk.js.map": "./static/js/2818.21be97b7.chunk.js.map",
|
||||
"9560.bf1e7276.chunk.js.map": "./static/js/9560.bf1e7276.chunk.js.map",
|
||||
"9661.1e636902.chunk.js.map": "./static/js/9661.1e636902.chunk.js.map",
|
||||
"9330.ea489cb0.chunk.js.map": "./static/js/9330.ea489cb0.chunk.js.map",
|
||||
"7436.d9fafb61.chunk.js.map": "./static/js/7436.d9fafb61.chunk.js.map",
|
||||
"8428.d94dd7f0.chunk.js.map": "./static/js/8428.d94dd7f0.chunk.js.map",
|
||||
"9779.868ea932.chunk.js.map": "./static/js/9779.868ea932.chunk.js.map",
|
||||
"3617.c5deb048.chunk.js.map": "./static/js/3617.c5deb048.chunk.js.map",
|
||||
"7274.5c2a8fde.chunk.js.map": "./static/js/7274.5c2a8fde.chunk.js.map",
|
||||
"7842.151c41a8.chunk.js.map": "./static/js/7842.151c41a8.chunk.js.map",
|
||||
"4745.3fab4951.chunk.js.map": "./static/js/4745.3fab4951.chunk.js.map",
|
||||
"1796.840a4e41.chunk.js.map": "./static/js/1796.840a4e41.chunk.js.map",
|
||||
"8259.7c8fbb38.chunk.js.map": "./static/js/8259.7c8fbb38.chunk.js.map",
|
||||
"6023.d2f6ea4d.chunk.js.map": "./static/js/6023.d2f6ea4d.chunk.js.map",
|
||||
"6147.f2a7d841.chunk.js.map": "./static/js/6147.f2a7d841.chunk.js.map",
|
||||
"9275.6a834668.chunk.js.map": "./static/js/9275.6a834668.chunk.js.map",
|
||||
"8190.d56fddaa.chunk.js.map": "./static/js/8190.d56fddaa.chunk.js.map",
|
||||
"7314.0058a98f.chunk.js.map": "./static/js/7314.0058a98f.chunk.js.map",
|
||||
"9275.2fec5cc7.chunk.js.map": "./static/js/9275.2fec5cc7.chunk.js.map",
|
||||
"8190.13d7b7e6.chunk.js.map": "./static/js/8190.13d7b7e6.chunk.js.map",
|
||||
"7314.01eaeb0f.chunk.js.map": "./static/js/7314.01eaeb0f.chunk.js.map",
|
||||
"7456.e2ad891d.chunk.js.map": "./static/js/7456.e2ad891d.chunk.js.map",
|
||||
"5822.dd80c1b6.chunk.css.map": "./static/css/5822.dd80c1b6.chunk.css.map",
|
||||
"5822.c1d62c79.chunk.js.map": "./static/js/5822.c1d62c79.chunk.js.map",
|
||||
"5822.fa49be62.chunk.js.map": "./static/js/5822.fa49be62.chunk.js.map",
|
||||
"2699.3102be0f.chunk.js.map": "./static/js/2699.3102be0f.chunk.js.map",
|
||||
"5808.8da21793.chunk.js.map": "./static/js/5808.8da21793.chunk.js.map",
|
||||
"1237.b46ec019.chunk.js.map": "./static/js/1237.b46ec019.chunk.js.map",
|
||||
@@ -187,29 +187,29 @@
|
||||
"964.dd80c1b6.chunk.css.map": "./static/css/964.dd80c1b6.chunk.css.map",
|
||||
"964.c1751e75.chunk.js.map": "./static/js/964.c1751e75.chunk.js.map",
|
||||
"9807.dd80c1b6.chunk.css.map": "./static/css/9807.dd80c1b6.chunk.css.map",
|
||||
"9807.731eb17a.chunk.js.map": "./static/js/9807.731eb17a.chunk.js.map",
|
||||
"3806.b5b2c03c.chunk.js.map": "./static/js/3806.b5b2c03c.chunk.js.map",
|
||||
"8661.ade9e15c.chunk.js.map": "./static/js/8661.ade9e15c.chunk.js.map",
|
||||
"2886.a03a185d.chunk.js.map": "./static/js/2886.a03a185d.chunk.js.map",
|
||||
"9807.ee1a2bdb.chunk.js.map": "./static/js/9807.ee1a2bdb.chunk.js.map",
|
||||
"3806.8464efb7.chunk.js.map": "./static/js/3806.8464efb7.chunk.js.map",
|
||||
"8661.e8f2397f.chunk.js.map": "./static/js/8661.e8f2397f.chunk.js.map",
|
||||
"2886.92b60a3b.chunk.js.map": "./static/js/2886.92b60a3b.chunk.js.map",
|
||||
"4577.cd5b16fc.chunk.js.map": "./static/js/4577.cd5b16fc.chunk.js.map",
|
||||
"4298.e6c437dd.chunk.js.map": "./static/js/4298.e6c437dd.chunk.js.map",
|
||||
"2805.b4302353.chunk.js.map": "./static/js/2805.b4302353.chunk.js.map",
|
||||
"6873.27fbf284.chunk.js.map": "./static/js/6873.27fbf284.chunk.js.map",
|
||||
"428.ad58c353.chunk.js.map": "./static/js/428.ad58c353.chunk.js.map",
|
||||
"1069.77bb316e.chunk.js.map": "./static/js/1069.77bb316e.chunk.js.map",
|
||||
"9080.884725e7.chunk.js.map": "./static/js/9080.884725e7.chunk.js.map",
|
||||
"9080.d84213b3.chunk.js.map": "./static/js/9080.d84213b3.chunk.js.map",
|
||||
"3276.1afa9e35.chunk.js.map": "./static/js/3276.1afa9e35.chunk.js.map",
|
||||
"6458.c6bf60f8.chunk.js.map": "./static/js/6458.c6bf60f8.chunk.js.map",
|
||||
"7551.b8d5fd21.chunk.js.map": "./static/js/7551.b8d5fd21.chunk.js.map",
|
||||
"7950.1793a548.chunk.js.map": "./static/js/7950.1793a548.chunk.js.map",
|
||||
"290.3a78b888.chunk.js.map": "./static/js/290.3a78b888.chunk.js.map",
|
||||
"8961.c7471e8d.chunk.js.map": "./static/js/8961.c7471e8d.chunk.js.map",
|
||||
"3967.eefb50e4.chunk.js.map": "./static/js/3967.eefb50e4.chunk.js.map",
|
||||
"7950.0e4eac80.chunk.js.map": "./static/js/7950.0e4eac80.chunk.js.map",
|
||||
"290.8bfc0b83.chunk.js.map": "./static/js/290.8bfc0b83.chunk.js.map",
|
||||
"8961.37cf027e.chunk.js.map": "./static/js/8961.37cf027e.chunk.js.map",
|
||||
"3967.2cb35507.chunk.js.map": "./static/js/3967.2cb35507.chunk.js.map",
|
||||
"4084.dd80c1b6.chunk.css.map": "./static/css/4084.dd80c1b6.chunk.css.map",
|
||||
"4084.57e7784d.chunk.js.map": "./static/js/4084.57e7784d.chunk.js.map",
|
||||
"4084.34cce774.chunk.js.map": "./static/js/4084.34cce774.chunk.js.map",
|
||||
"8394.fa043a82.chunk.js.map": "./static/js/8394.fa043a82.chunk.js.map",
|
||||
"7664.b099b462.chunk.js.map": "./static/js/7664.b099b462.chunk.js.map",
|
||||
"1140.044ef809.chunk.js.map": "./static/js/1140.044ef809.chunk.js.map",
|
||||
"1140.ab5579e6.chunk.js.map": "./static/js/1140.ab5579e6.chunk.js.map",
|
||||
"5961.7a71f2c5.chunk.js.map": "./static/js/5961.7a71f2c5.chunk.js.map",
|
||||
"2401.dcfa0659.chunk.js.map": "./static/js/2401.dcfa0659.chunk.js.map",
|
||||
"7498.adcacaca.chunk.js.map": "./static/js/7498.adcacaca.chunk.js.map",
|
||||
@@ -219,7 +219,7 @@
|
||||
"6549.bc76401b.chunk.js.map": "./static/js/6549.bc76401b.chunk.js.map",
|
||||
"8724.dd80c1b6.chunk.css.map": "./static/css/8724.dd80c1b6.chunk.css.map",
|
||||
"8724.2fda7628.chunk.js.map": "./static/js/8724.2fda7628.chunk.js.map",
|
||||
"2182.e1389a8d.chunk.js.map": "./static/js/2182.e1389a8d.chunk.js.map",
|
||||
"2182.45ee7ad0.chunk.js.map": "./static/js/2182.45ee7ad0.chunk.js.map",
|
||||
"7764.7133a78a.chunk.js.map": "./static/js/7764.7133a78a.chunk.js.map",
|
||||
"4220.8d9a9028.chunk.js.map": "./static/js/4220.8d9a9028.chunk.js.map",
|
||||
"1719.a1c5fbc9.chunk.js.map": "./static/js/1719.a1c5fbc9.chunk.js.map",
|
||||
@@ -303,15 +303,15 @@
|
||||
"9831.1b5a6bb6.chunk.js.map": "./static/js/9831.1b5a6bb6.chunk.js.map",
|
||||
"9382.581734f3.chunk.js.map": "./static/js/9382.581734f3.chunk.js.map",
|
||||
"8174.6e95ea0c.chunk.js.map": "./static/js/8174.6e95ea0c.chunk.js.map",
|
||||
"1116.6c0eed8e.chunk.js.map": "./static/js/1116.6c0eed8e.chunk.js.map",
|
||||
"1116.824f2d28.chunk.js.map": "./static/js/1116.824f2d28.chunk.js.map",
|
||||
"6430.e747de7c.chunk.js.map": "./static/js/6430.e747de7c.chunk.js.map",
|
||||
"4966.c825dc1c.chunk.js.map": "./static/js/4966.c825dc1c.chunk.js.map",
|
||||
"2363.5fc6aebe.chunk.js.map": "./static/js/2363.5fc6aebe.chunk.js.map",
|
||||
"2464.5dc72a90.chunk.js.map": "./static/js/2464.5dc72a90.chunk.js.map",
|
||||
"7520.087167a1.chunk.js.map": "./static/js/7520.087167a1.chunk.js.map"
|
||||
"2464.ea29031c.chunk.js.map": "./static/js/2464.ea29031c.chunk.js.map",
|
||||
"7520.609b689b.chunk.js.map": "./static/js/7520.609b689b.chunk.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.90d417ae.css",
|
||||
"static/js/main.69273a1d.js"
|
||||
"static/js/main.fd4d26b1.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.69273a1d.js"></script><link href="./static/css/main.90d417ae.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="preload"><img src="./images/background.svg"/> <img src="./images/background-wave-orig2.svg"/></div><div id="loader-block"><img src="./Loader.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.fd4d26b1.js"></script><link href="./static/css/main.90d417ae.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"><div id="preload"><img src="./images/background.svg"/> <img src="./images/background-wave-orig2.svg"/></div><div id="loader-block"><img src="./Loader.svg"/></div></div></body></html>
|
||||
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/1116.824f2d28.chunk.js
Normal file
2
portal-ui/build/static/js/1116.824f2d28.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/1116.824f2d28.chunk.js.map
Normal file
1
portal-ui/build/static/js/1116.824f2d28.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
"use strict";(self.webpackChunkportal_ui=self.webpackChunkportal_ui||[]).push([[1140],{39080:function(e,n,t){t.r(n),t.d(n,{default:function(){return Z}});var i=t(18489),r=t(35531),a=t(50390),s=t(38342),l=t.n(s),o=t(86509),c=t(4285),d=t(66946),u=t(51002),m=t(25594),h=t(58217),p=t(65771),x=t(70758),y=t(33034),g=t.n(y),f=t(86362),v=t(72462),j=t(62559),b=(0,c.Z)((function(e){return(0,o.Z)({container:{display:"flex",flexFlow:"column",padding:"20px 0 8px 0"},inputWithCopy:{"& .MuiInputBase-root ":{width:"100%",background:"#FBFAFA","& .MuiInputBase-input":{height:".8rem"},"& .MuiInputAdornment-positionEnd":{marginRight:".5rem","& .MuiButtonBase-root":{height:"2rem"}}},"& .MuiButtonBase-root .min-icon":{width:".8rem",height:".8rem"}},inputLabel:(0,i.Z)((0,i.Z)({},v.YI.inputLabel),{},{fontSize:".8rem"})})}))((function(e){var n=e.label,t=void 0===n?"":n,i=e.value,r=void 0===i?"":i,a=e.classes,s=void 0===a?{}:a;return(0,j.jsxs)("div",{className:s.container,children:[(0,j.jsxs)("div",{className:s.inputLabel,children:[t,":"]}),(0,j.jsx)("div",{className:s.inputWithCopy,children:(0,j.jsx)(h.Z,{value:r,readOnly:!0,endAdornment:(0,j.jsx)(p.Z,{position:"end",children:(0,j.jsx)(g(),{text:r,children:(0,j.jsx)(x.Z,{"aria-label":"copy",tooltip:"Copy",onClick:function(){},onMouseDown:function(){},edge:"end",children:(0,j.jsx)(f.TI,{})})})})})})]})})),w=t(47424),Z=(0,c.Z)((function(e){return(0,o.Z)({warningBlock:{color:"red",fontSize:".85rem",margin:".5rem 0 .5rem 0",display:"flex",alignItems:"center","& svg ":{marginRight:".3rem",height:16,width:16}},credentialTitle:{padding:".8rem 0 0 0",fontWeight:600,fontSize:".9rem"},buttonContainer:{textAlign:"right",marginTop:"1rem"},credentialsPanel:{overflowY:"auto",maxHeight:350},promptTitle:{display:"flex",alignItems:"center"},buttonSpacer:{marginRight:".9rem"},promptIcon:{marginRight:".1rem",display:"flex",alignItems:"center",height:"2rem",width:"2rem"}})}))((function(e){var n=e.classes,t=e.newServiceAccount,s=e.open,o=e.closeModal,c=e.entity;if(!t)return null;var h=l()(t,"console",null),p=l()(t,"idp",!1);return(0,j.jsx)(u.Z,{modalOpen:s,onClose:function(){o()},title:(0,j.jsx)("div",{className:n.promptTitle,children:(0,j.jsxs)("div",{children:["New ",c," Created"]})}),titleIcon:(0,j.jsx)(f.tV,{}),children:(0,j.jsxs)(m.ZP,{container:!0,children:[(0,j.jsxs)(m.ZP,{item:!0,xs:12,className:n.formScrollable,children:["A new ",c," has been created with the following details:",!p&&h&&(0,j.jsx)(a.Fragment,{children:(0,j.jsxs)(m.ZP,{item:!0,xs:12,className:n.credentialsPanel,children:[(0,j.jsx)("div",{className:n.credentialTitle,children:"Console Credentials"}),Array.isArray(h)&&h.map((function(e,n){return(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(b,{label:"Access Key",value:e.accessKey}),(0,j.jsx)(b,{label:"Secret Key",value:e.secretKey})]})})),!Array.isArray(h)&&(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(b,{label:"Access Key",value:h.accessKey}),(0,j.jsx)(b,{label:"Secret Key",value:h.secretKey})]})]})}),p?(0,j.jsx)("div",{className:n.warningBlock,children:"Please Login via the configured external identity provider."}):(0,j.jsxs)("div",{className:n.warningBlock,children:[(0,j.jsx)(w.Z,{}),(0,j.jsx)("span",{children:"Write these down, as this is the only time the secret will be displayed."})]})]}),(0,j.jsxs)(m.ZP,{item:!0,xs:12,className:n.buttonContainer,children:[(0,j.jsx)(d.Z,{id:"done-button",variant:"outlined",className:n.buttonSpacer,onClick:function(){o()},color:"primary",children:"Done"}),!p&&(0,j.jsx)(d.Z,{id:"download-button",onClick:function(){var e={};if(h)if(Array.isArray(h)){var n=h.map((function(e){return{url:e.url,access_key:e.accessKey,secret_key:e.secretKey,api:"s3v4",path:"auto"}}));e={console:(0,r.Z)(n)}}else e={console:[{url:h.url,access_key:h.accessKey,secret_key:h.secretKey,api:"s3v4",path:"auto"}]};!function(e,n){var t=document.createElement("a");t.setAttribute("href","data:text/plain;charset=utf-8,"+encodeURIComponent(n)),t.setAttribute("download",e),t.style.display="none",document.body.appendChild(t),t.click(),document.body.removeChild(t)}("credentials.json",JSON.stringify((0,i.Z)({},e)))},endIcon:(0,j.jsx)(f._8,{}),variant:"contained",color:"primary",children:"Download"})]})]})})}))}}]);
|
||||
//# sourceMappingURL=1140.044ef809.chunk.js.map
|
||||
File diff suppressed because one or more lines are too long
2
portal-ui/build/static/js/1140.ab5579e6.chunk.js
Normal file
2
portal-ui/build/static/js/1140.ab5579e6.chunk.js
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";(self.webpackChunkportal_ui=self.webpackChunkportal_ui||[]).push([[1140],{39080:function(e,n,t){t.r(n),t.d(n,{default:function(){return w}});var i=t(18489),r=t(50390),a=t(38342),s=t.n(a),l=t(86509),o=t(4285),c=t(66946),d=t(51002),u=t(25594),m=t(58217),h=t(65771),p=t(70758),x=t(33034),y=t.n(x),g=t(86362),f=t(72462),v=t(62559),j=(0,o.Z)((function(e){return(0,l.Z)({container:{display:"flex",flexFlow:"column",padding:"20px 0 8px 0"},inputWithCopy:{"& .MuiInputBase-root ":{width:"100%",background:"#FBFAFA","& .MuiInputBase-input":{height:".8rem"},"& .MuiInputAdornment-positionEnd":{marginRight:".5rem","& .MuiButtonBase-root":{height:"2rem"}}},"& .MuiButtonBase-root .min-icon":{width:".8rem",height:".8rem"}},inputLabel:(0,i.Z)((0,i.Z)({},f.YI.inputLabel),{},{fontSize:".8rem"})})}))((function(e){var n=e.label,t=void 0===n?"":n,i=e.value,r=void 0===i?"":i,a=e.classes,s=void 0===a?{}:a;return(0,v.jsxs)("div",{className:s.container,children:[(0,v.jsxs)("div",{className:s.inputLabel,children:[t,":"]}),(0,v.jsx)("div",{className:s.inputWithCopy,children:(0,v.jsx)(m.Z,{value:r,readOnly:!0,endAdornment:(0,v.jsx)(h.Z,{position:"end",children:(0,v.jsx)(y(),{text:r,children:(0,v.jsx)(p.Z,{"aria-label":"copy",tooltip:"Copy",onClick:function(){},onMouseDown:function(){},edge:"end",children:(0,v.jsx)(g.TI,{})})})})})})]})})),b=t(47424),w=(0,o.Z)((function(e){return(0,l.Z)({warningBlock:{color:"red",fontSize:".85rem",margin:".5rem 0 .5rem 0",display:"flex",alignItems:"center","& svg ":{marginRight:".3rem",height:16,width:16}},credentialTitle:{padding:".8rem 0 0 0",fontWeight:600,fontSize:".9rem"},buttonContainer:{textAlign:"right",marginTop:"1rem"},credentialsPanel:{overflowY:"auto",maxHeight:350},promptTitle:{display:"flex",alignItems:"center"},buttonSpacer:{marginRight:".9rem"},promptIcon:{marginRight:".1rem",display:"flex",alignItems:"center",height:"2rem",width:"2rem"}})}))((function(e){var n=e.classes,t=e.newServiceAccount,a=e.open,l=e.closeModal,o=e.entity;if(!t)return null;var m=s()(t,"console",null),h=s()(t,"idp",!1);return(0,v.jsx)(d.Z,{modalOpen:a,onClose:function(){l()},title:(0,v.jsx)("div",{className:n.promptTitle,children:(0,v.jsxs)("div",{children:["New ",o," Created"]})}),titleIcon:(0,v.jsx)(g.tV,{}),children:(0,v.jsxs)(u.ZP,{container:!0,children:[(0,v.jsxs)(u.ZP,{item:!0,xs:12,className:n.formScrollable,children:["A new ",o," has been created with the following details:",!h&&m&&(0,v.jsx)(r.Fragment,{children:(0,v.jsxs)(u.ZP,{item:!0,xs:12,className:n.credentialsPanel,children:[(0,v.jsx)("div",{className:n.credentialTitle,children:"Console Credentials"}),Array.isArray(m)&&m.map((function(e,n){return(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(j,{label:"Access Key",value:e.accessKey}),(0,v.jsx)(j,{label:"Secret Key",value:e.secretKey})]})})),!Array.isArray(m)&&(0,v.jsxs)(v.Fragment,{children:[(0,v.jsx)(j,{label:"Access Key",value:m.accessKey}),(0,v.jsx)(j,{label:"Secret Key",value:m.secretKey})]})]})}),h?(0,v.jsx)("div",{className:n.warningBlock,children:"Please Login via the configured external identity provider."}):(0,v.jsxs)("div",{className:n.warningBlock,children:[(0,v.jsx)(b.Z,{}),(0,v.jsx)("span",{children:"Write these down, as this is the only time the secret will be displayed."})]})]}),(0,v.jsxs)(u.ZP,{item:!0,xs:12,className:n.buttonContainer,children:[(0,v.jsx)(c.Z,{id:"done-button",variant:"outlined",className:n.buttonSpacer,onClick:function(){l()},color:"primary",children:"Done"}),!h&&(0,v.jsx)(c.Z,{id:"download-button",onClick:function(){var e={};m&&(e=Array.isArray(m)?m.map((function(e){return{url:e.url,accessKey:e.accessKey,secretKey:e.secretKey,api:"s3v4",path:"auto"}}))[0]:{url:m.url,accessKey:m.accessKey,secretKey:m.secretKey,api:"s3v4",path:"auto"});!function(e,n){var t=document.createElement("a");t.setAttribute("href","data:text/plain;charset=utf-8,"+encodeURIComponent(n)),t.setAttribute("download",e),t.style.display="none",document.body.appendChild(t),t.click(),document.body.removeChild(t)}("credentials.json",JSON.stringify((0,i.Z)({},e)))},endIcon:(0,v.jsx)(g._8,{}),variant:"contained",color:"primary",children:"Download"})]})]})})}))}}]);
|
||||
//# sourceMappingURL=1140.ab5579e6.chunk.js.map
|
||||
1
portal-ui/build/static/js/1140.ab5579e6.chunk.js.map
Normal file
1
portal-ui/build/static/js/1140.ab5579e6.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
2
portal-ui/build/static/js/1796.840a4e41.chunk.js
Normal file
2
portal-ui/build/static/js/1796.840a4e41.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/1796.840a4e41.chunk.js.map
Normal file
1
portal-ui/build/static/js/1796.840a4e41.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
portal-ui/build/static/js/2182.45ee7ad0.chunk.js
Normal file
2
portal-ui/build/static/js/2182.45ee7ad0.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/2182.45ee7ad0.chunk.js.map
Normal file
1
portal-ui/build/static/js/2182.45ee7ad0.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
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/2464.ea29031c.chunk.js.map
Normal file
1
portal-ui/build/static/js/2464.ea29031c.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
portal-ui/build/static/js/2818.21be97b7.chunk.js
Normal file
2
portal-ui/build/static/js/2818.21be97b7.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/2818.21be97b7.chunk.js.map
Normal file
1
portal-ui/build/static/js/2818.21be97b7.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
2
portal-ui/build/static/js/2886.92b60a3b.chunk.js
Normal file
2
portal-ui/build/static/js/2886.92b60a3b.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/2886.92b60a3b.chunk.js.map
Normal file
1
portal-ui/build/static/js/2886.92b60a3b.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/290.8bfc0b83.chunk.js
Normal file
2
portal-ui/build/static/js/290.8bfc0b83.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/290.8bfc0b83.chunk.js.map
Normal file
1
portal-ui/build/static/js/290.8bfc0b83.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
2
portal-ui/build/static/js/3617.c5deb048.chunk.js
Normal file
2
portal-ui/build/static/js/3617.c5deb048.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/3617.c5deb048.chunk.js.map
Normal file
1
portal-ui/build/static/js/3617.c5deb048.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
2
portal-ui/build/static/js/3806.8464efb7.chunk.js
Normal file
2
portal-ui/build/static/js/3806.8464efb7.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/3806.8464efb7.chunk.js.map
Normal file
1
portal-ui/build/static/js/3806.8464efb7.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
2
portal-ui/build/static/js/3967.2cb35507.chunk.js
Normal file
2
portal-ui/build/static/js/3967.2cb35507.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/3967.2cb35507.chunk.js.map
Normal file
1
portal-ui/build/static/js/3967.2cb35507.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
2
portal-ui/build/static/js/4084.34cce774.chunk.js
Normal file
2
portal-ui/build/static/js/4084.34cce774.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/4084.34cce774.chunk.js.map
Normal file
1
portal-ui/build/static/js/4084.34cce774.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
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/4745.3fab4951.chunk.js.map
Normal file
1
portal-ui/build/static/js/4745.3fab4951.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
File diff suppressed because one or more lines are too long
2
portal-ui/build/static/js/5822.fa49be62.chunk.js
Normal file
2
portal-ui/build/static/js/5822.fa49be62.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/5822.fa49be62.chunk.js.map
Normal file
1
portal-ui/build/static/js/5822.fa49be62.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
2
portal-ui/build/static/js/6023.d2f6ea4d.chunk.js
Normal file
2
portal-ui/build/static/js/6023.d2f6ea4d.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/6023.d2f6ea4d.chunk.js.map
Normal file
1
portal-ui/build/static/js/6023.d2f6ea4d.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
2
portal-ui/build/static/js/7274.5c2a8fde.chunk.js
Normal file
2
portal-ui/build/static/js/7274.5c2a8fde.chunk.js
Normal file
File diff suppressed because one or more lines are too long
1
portal-ui/build/static/js/7274.5c2a8fde.chunk.js.map
Normal file
1
portal-ui/build/static/js/7274.5c2a8fde.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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user