Compare commits

...

17 Commits

Author SHA1 Message Date
Daniel Valdivia
9301e3b7de Release v0.15.3 (#1724) 2022-03-15 22:42:05 -07:00
jinapurapu
cf5e5a14b5 Updated tenant creation credentials download to allow mc alias import (#1683)
Removed underscore from credentials
Made credentials download output match mc alias import expected format
Added URL to createServiceAccountCreds return
2022-03-15 21:01:03 -07:00
Cesar Celis Hernandez
7f4546e879 Adding Access Rules Integration Test (#1719) 2022-03-15 20:49:17 -07:00
Alex
1a92c59e3f Added background color to selected version (#1723)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
2022-03-15 20:42:12 -07:00
Alex
ade9731773 Reload main object information after restoring a version (#1720)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
2022-03-15 21:26:57 -06:00
Cesar Celis Hernandez
cc43b3c743 Add list of tenants integration test (#1722) 2022-03-15 21:13:33 -06:00
Alex
b29f6a1640 Changed Sort by field to use only Date & Size (#1721)
Added size column
Changed Versions Sort by field to be only date & size
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
2022-03-15 18:03:42 -07:00
Daniel Valdivia
62b8258989 Fix Get Latest MinIO Image on Tenant Create (#1703)
* Fix Get Latest MinIO Image on Tenant Create

Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>

* remove `operator_` prefix on files in operatorapi

Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
2022-03-15 15:16:26 -07:00
adfost
75bc568e4b Adding delete versions test (#1701)
adding delete versions ui test
2022-03-15 14:40:48 -07:00
Alex
b0119a55df Reload versions list after clicking on Reload button (#1717) 2022-03-15 09:45:28 -07:00
Alex
78983ce76f Added (Default) label to EC Parity selector field & fixed automatic selection of EC Parity (#1716)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
2022-03-14 23:47:38 -07:00
Cesar Celis Hernandez
e060e1d97e Add Bucket LifeCycle Integration Test (#1711) 2022-03-14 19:48:25 -06:00
Daniel Valdivia
82bdc228b2 Add preview icon to object versions (#1713)
Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
2022-03-14 15:48:20 -07:00
adfost
1fa8311af7 adding distributed setup (#1712) 2022-03-14 15:34:21 -06:00
Daniel Valdivia
93243f2c77 Run Testcafe tests with Node 16 (#1714)
Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
2022-03-14 12:11:07 -07:00
adfost
3b423826fd Removing delete all versions option from non versioned bucket (#1710) 2022-03-14 11:13:23 -07:00
Daniel Valdivia
e44a7c94c6 Bug Fix: Preview a specific Object Version ID (#1706) 2022-03-11 22:40:14 -08:00
193 changed files with 1853 additions and 586 deletions

94
.github/workflows/common.sh vendored Executable file
View 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
View 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 "$@"

View File

@@ -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

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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

View File

@@ -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

View 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
View 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
}

View 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
}

View File

@@ -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 {

View File

@@ -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"
]
}

View File

@@ -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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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

View 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

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

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

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

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

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

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

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

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

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

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

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