diff --git a/.github/workflows/jobs.yaml b/.github/workflows/jobs.yaml
index 8ba8b5ba1..bff561d41 100644
--- a/.github/workflows/jobs.yaml
+++ b/.github/workflows/jobs.yaml
@@ -1348,8 +1348,8 @@ jobs:
run: |
make cleanup-permissions
- all-operator-tests:
- name: Operator UI Tests
+ all-operator-tests-1:
+ name: Operator UI Tests Part 1
needs:
- lint-job
- no-warnings-and-make-assets
@@ -1440,7 +1440,383 @@ jobs:
- name: Run TestCafe Tests
uses: DevExpress/testcafe-action@latest
with:
- args: '"chrome:headless" portal-ui/tests/operator/ --skip-js-errors -c 3'
+ args: '"chrome:headless" portal-ui/tests/operator/login --skip-js-errors -c 3'
+
+ all-operator-tests-2:
+ name: Operator UI Tests Part 2
+ needs:
+ - lint-job
+ - no-warnings-and-make-assets
+ - reuse-golang-dependencies
+ - vulnerable-dependencies-checks
+ - semgrep-static-code-analysis
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ go-version: [ 1.18.x ]
+ os: [ ubuntu-latest ]
+ steps:
+ - name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
+ uses: actions/setup-go@v2
+ with:
+ go-version: ${{ matrix.go-version }}
+ id: go
+
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v2
+
+ # To build operator image, we need to clone the repository first
+ - name: clone https://github.com/minio/operator
+ uses: actions/checkout@master
+ with:
+
+ # Repository name with owner. For example, actions/checkout
+ # Default: ${{ github.repository }}
+ repository: minio/operator
+
+ # Relative path under $GITHUB_WORKSPACE to place the repository
+ # To have two repositories under the same test
+ path: 'operator_repository'
+
+ - name: Read .nvmrc
+ id: node_version
+ run: echo ::set-output name=NVMRC::$(cat .nvmrc)
+
+ - uses: actions/setup-node@v2
+ with:
+ node-version: ${{ env.NVMRC }}
+
+ - uses: actions/cache@v2
+ name: Go Mod Cache
+ with:
+ path: |
+ ~/.cache/go-build
+ ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ github.run_id }}
+
+ - name: Get yarn cache directory path
+ id: yarn-cache-dir-path
+ run: echo "::set-output name=dir::$(yarn cache dir)"
+
+ - uses: actions/cache@v2
+ id: yarn-cache
+ name: Yarn Cache
+ with:
+ path: |
+ ${{ steps.yarn-cache-dir-path.outputs.dir }}
+ ./portal-ui/node_modules/
+ key: ${{ runner.os }}-yarn-${{ hashFiles('./portal-ui/yarn.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-yarn-
+
+ - uses: actions/cache@v2
+ id: assets-cache
+ name: Assets Cache
+ with:
+ path: |
+ ./portal-ui/build/
+ key: ${{ runner.os }}-assets-${{ github.run_id }}
+ restore-keys: |
+ ${{ runner.os }}-assets-
+
+ - name: Build Console on ${{ matrix.os }}
+ env:
+ GO111MODULE: on
+ GOOS: linux
+ run: |
+ make console
+
+ # Runs a set of commands using the runners shell
+ - name: Start Kind for Operator UI
+ run: |
+ "${GITHUB_WORKSPACE}/portal-ui/tests/scripts/operator.sh"
+
+ - name: Run TestCafe Tests
+ uses: DevExpress/testcafe-action@latest
+ with:
+ args: '"chrome:headless" portal-ui/tests/operator/tenant/test-1 --skip-js-errors -c 3'
+
+ all-operator-tests-3:
+ name: Operator UI Tests Part 3
+ needs:
+ - lint-job
+ - no-warnings-and-make-assets
+ - reuse-golang-dependencies
+ - vulnerable-dependencies-checks
+ - semgrep-static-code-analysis
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ go-version: [ 1.18.x ]
+ os: [ ubuntu-latest ]
+ steps:
+ - name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
+ uses: actions/setup-go@v2
+ with:
+ go-version: ${{ matrix.go-version }}
+ id: go
+
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v2
+
+ # To build operator image, we need to clone the repository first
+ - name: clone https://github.com/minio/operator
+ uses: actions/checkout@master
+ with:
+
+ # Repository name with owner. For example, actions/checkout
+ # Default: ${{ github.repository }}
+ repository: minio/operator
+
+ # Relative path under $GITHUB_WORKSPACE to place the repository
+ # To have two repositories under the same test
+ path: 'operator_repository'
+
+ - name: Read .nvmrc
+ id: node_version
+ run: echo ::set-output name=NVMRC::$(cat .nvmrc)
+
+ - uses: actions/setup-node@v2
+ with:
+ node-version: ${{ env.NVMRC }}
+
+ - uses: actions/cache@v2
+ name: Go Mod Cache
+ with:
+ path: |
+ ~/.cache/go-build
+ ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ github.run_id }}
+
+ - name: Get yarn cache directory path
+ id: yarn-cache-dir-path
+ run: echo "::set-output name=dir::$(yarn cache dir)"
+
+ - uses: actions/cache@v2
+ id: yarn-cache
+ name: Yarn Cache
+ with:
+ path: |
+ ${{ steps.yarn-cache-dir-path.outputs.dir }}
+ ./portal-ui/node_modules/
+ key: ${{ runner.os }}-yarn-${{ hashFiles('./portal-ui/yarn.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-yarn-
+
+ - uses: actions/cache@v2
+ id: assets-cache
+ name: Assets Cache
+ with:
+ path: |
+ ./portal-ui/build/
+ key: ${{ runner.os }}-assets-${{ github.run_id }}
+ restore-keys: |
+ ${{ runner.os }}-assets-
+
+ - name: Build Console on ${{ matrix.os }}
+ env:
+ GO111MODULE: on
+ GOOS: linux
+ run: |
+ make console
+
+ # Runs a set of commands using the runners shell
+ - name: Start Kind for Operator UI
+ run: |
+ "${GITHUB_WORKSPACE}/portal-ui/tests/scripts/operator.sh"
+
+ - name: Run TestCafe Tests
+ uses: DevExpress/testcafe-action@latest
+ with:
+ args: '"chrome:headless" portal-ui/tests/operator/tenant/test-2 --skip-js-errors -c 3'
+
+ all-operator-tests-4:
+ name: Operator UI Tests Part 4
+ needs:
+ - lint-job
+ - no-warnings-and-make-assets
+ - reuse-golang-dependencies
+ - vulnerable-dependencies-checks
+ - semgrep-static-code-analysis
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ go-version: [ 1.18.x ]
+ os: [ ubuntu-latest ]
+ steps:
+ - name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
+ uses: actions/setup-go@v2
+ with:
+ go-version: ${{ matrix.go-version }}
+ id: go
+
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v2
+
+ # To build operator image, we need to clone the repository first
+ - name: clone https://github.com/minio/operator
+ uses: actions/checkout@master
+ with:
+
+ # Repository name with owner. For example, actions/checkout
+ # Default: ${{ github.repository }}
+ repository: minio/operator
+
+ # Relative path under $GITHUB_WORKSPACE to place the repository
+ # To have two repositories under the same test
+ path: 'operator_repository'
+
+ - name: Read .nvmrc
+ id: node_version
+ run: echo ::set-output name=NVMRC::$(cat .nvmrc)
+
+ - uses: actions/setup-node@v2
+ with:
+ node-version: ${{ env.NVMRC }}
+
+ - uses: actions/cache@v2
+ name: Go Mod Cache
+ with:
+ path: |
+ ~/.cache/go-build
+ ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ github.run_id }}
+
+ - name: Get yarn cache directory path
+ id: yarn-cache-dir-path
+ run: echo "::set-output name=dir::$(yarn cache dir)"
+
+ - uses: actions/cache@v2
+ id: yarn-cache
+ name: Yarn Cache
+ with:
+ path: |
+ ${{ steps.yarn-cache-dir-path.outputs.dir }}
+ ./portal-ui/node_modules/
+ key: ${{ runner.os }}-yarn-${{ hashFiles('./portal-ui/yarn.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-yarn-
+
+ - uses: actions/cache@v2
+ id: assets-cache
+ name: Assets Cache
+ with:
+ path: |
+ ./portal-ui/build/
+ key: ${{ runner.os }}-assets-${{ github.run_id }}
+ restore-keys: |
+ ${{ runner.os }}-assets-
+
+ - name: Build Console on ${{ matrix.os }}
+ env:
+ GO111MODULE: on
+ GOOS: linux
+ run: |
+ make console
+
+ # Runs a set of commands using the runners shell
+ - name: Start Kind for Operator UI
+ run: |
+ "${GITHUB_WORKSPACE}/portal-ui/tests/scripts/operator.sh"
+
+ - name: Run TestCafe Tests
+ uses: DevExpress/testcafe-action@latest
+ with:
+ args: '"chrome:headless" portal-ui/tests/operator/tenant/test-3 --skip-js-errors -c 3'
+
+ all-operator-tests-5:
+ name: Operator UI Tests Part 5
+ needs:
+ - lint-job
+ - no-warnings-and-make-assets
+ - reuse-golang-dependencies
+ - vulnerable-dependencies-checks
+ - semgrep-static-code-analysis
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ go-version: [ 1.18.x ]
+ os: [ ubuntu-latest ]
+ steps:
+ - name: Set up Go ${{ matrix.go-version }} on ${{ matrix.os }}
+ uses: actions/setup-go@v2
+ with:
+ go-version: ${{ matrix.go-version }}
+ id: go
+
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v2
+
+ # To build operator image, we need to clone the repository first
+ - name: clone https://github.com/minio/operator
+ uses: actions/checkout@master
+ with:
+
+ # Repository name with owner. For example, actions/checkout
+ # Default: ${{ github.repository }}
+ repository: minio/operator
+
+ # Relative path under $GITHUB_WORKSPACE to place the repository
+ # To have two repositories under the same test
+ path: 'operator_repository'
+
+ - name: Read .nvmrc
+ id: node_version
+ run: echo ::set-output name=NVMRC::$(cat .nvmrc)
+
+ - uses: actions/setup-node@v2
+ with:
+ node-version: ${{ env.NVMRC }}
+
+ - uses: actions/cache@v2
+ name: Go Mod Cache
+ with:
+ path: |
+ ~/.cache/go-build
+ ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ github.run_id }}
+
+ - name: Get yarn cache directory path
+ id: yarn-cache-dir-path
+ run: echo "::set-output name=dir::$(yarn cache dir)"
+
+ - uses: actions/cache@v2
+ id: yarn-cache
+ name: Yarn Cache
+ with:
+ path: |
+ ${{ steps.yarn-cache-dir-path.outputs.dir }}
+ ./portal-ui/node_modules/
+ key: ${{ runner.os }}-yarn-${{ hashFiles('./portal-ui/yarn.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-yarn-
+
+ - uses: actions/cache@v2
+ id: assets-cache
+ name: Assets Cache
+ with:
+ path: |
+ ./portal-ui/build/
+ key: ${{ runner.os }}-assets-${{ github.run_id }}
+ restore-keys: |
+ ${{ runner.os }}-assets-
+
+ - name: Build Console on ${{ matrix.os }}
+ env:
+ GO111MODULE: on
+ GOOS: linux
+ run: |
+ make console
+
+ # Runs a set of commands using the runners shell
+ - name: Start Kind for Operator UI
+ run: |
+ "${GITHUB_WORKSPACE}/portal-ui/tests/scripts/operator.sh"
+
+ - name: Run TestCafe Tests
+ uses: DevExpress/testcafe-action@latest
+ with:
+ args: '"chrome:headless" portal-ui/tests/operator/tenant/test-4 --skip-js-errors -c 3'
compile-job:
name: Compiles on Go ${{ matrix.go-version }} and ${{ matrix.os }}
diff --git a/portal-ui/tests/operator/login.ts b/portal-ui/tests/operator/login/login.ts
similarity index 85%
rename from portal-ui/tests/operator/login.ts
rename to portal-ui/tests/operator/login/login.ts
index b4805f978..570ddfcb7 100644
--- a/portal-ui/tests/operator/login.ts
+++ b/portal-ui/tests/operator/login/login.ts
@@ -14,9 +14,9 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-import * as roles from "../utils/roles";
-import * as elements from "../utils/elements";
-import { diagnosticsElement, supportElement } from "../utils/elements-menu";
+import * as roles from "../../utils/roles";
+import * as elements from "../../utils/elements";
+import { diagnosticsElement, supportElement } from "../../utils/elements-menu";
fixture("For user with default permissions").page("http://localhost:9090");
diff --git a/portal-ui/tests/operator/tenants.ts b/portal-ui/tests/operator/tenant/test-1/tenant-test-1.ts
similarity index 95%
rename from portal-ui/tests/operator/tenants.ts
rename to portal-ui/tests/operator/tenant/test-1/tenant-test-1.ts
index 23523827e..821e1f697 100644
--- a/portal-ui/tests/operator/tenants.ts
+++ b/portal-ui/tests/operator/tenant/test-1/tenant-test-1.ts
@@ -28,10 +28,11 @@ import {
goToMonitoringSection,
goToLoggingSection,
goToLoggingDBSection,
-} from "./utils";
+} from "../../utils";
fixture("For user with default permissions").page("http://localhost:9090");
+// Test 1
test("Create Tenant and List Tenants", async (t) => {
const tenantName = `tenant-${Math.floor(Math.random() * 10000)}`;
await loginToOperator();
@@ -39,6 +40,7 @@ test("Create Tenant and List Tenants", async (t) => {
await deleteTenant(tenantName);
});
+// Test 2
test("Create Tenant Without Audit Log", async (t) => {
const tenantName = `tenant-${Math.floor(Math.random() * 10000)}`;
await loginToOperator();
@@ -46,6 +48,7 @@ test("Create Tenant Without Audit Log", async (t) => {
await deleteTenant(tenantName);
});
+// Test 3
test("Test describe section for PODs in new tenant", async (t) => {
const tenantName = "storage-lite";
await loginToOperator();
@@ -76,6 +79,7 @@ const checkPodDescribeHasSections = async () => {
.ok();
};
+// Test 4
test("Test describe section for PVCs in new tenant", async (t) => {
const tenantName = `storage-lite`;
await loginToOperator();
@@ -487,27 +491,9 @@ const checkLoggingDBFieldsAcceptValues = async (tenantName: string) => {
.ok();
};
+// Test 5
test("Test Prometheus monitoring can be disabled and enabled", async (t) => {
const tenantName = `storage-lite`;
await loginToOperator();
await checkMonitoringToggle(tenantName);
});
-
-test("Test Prometheus config fields can be edited and submitted", async (t) => {
- const tenantName = `storage-lite`;
- await loginToOperator();
- await checkMonitoringFieldsAcceptValues(tenantName);
-});
-
-test("Test Audit Logging can be disabled and enabled", async (t) => {
- const tenantName = `storage-lite`;
- await loginToOperator();
- await checkLoggingToggle(tenantName);
-});
-
-test("Test Audit Log config fields can be edited and submitted", async (t) => {
- const tenantName = `storage-lite`;
- await loginToOperator();
- await checkLoggingFieldsAcceptValues(tenantName);
- await checkLoggingDBFieldsAcceptValues(tenantName);
-});
diff --git a/portal-ui/tests/operator/tenant/test-2/tenant-test-2.ts b/portal-ui/tests/operator/tenant/test-2/tenant-test-2.ts
new file mode 100644
index 000000000..bee3356c1
--- /dev/null
+++ b/portal-ui/tests/operator/tenant/test-2/tenant-test-2.ts
@@ -0,0 +1,469 @@
+// 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 .
+
+import { t, Selector } from "testcafe";
+import {
+ loginToOperator,
+ createTenant,
+ createTenantWithoutAuditLog,
+ deleteTenant,
+ redirectToTenantsList,
+ goToPodInTenant,
+ goToPodSection,
+ goToPvcInTenant,
+ goToPvcSection,
+ goToMonitoringSection,
+ goToLoggingSection,
+ goToLoggingDBSection,
+} from "../../utils";
+
+fixture("For user with default permissions").page("http://localhost:9090");
+
+const testPODDescribe = async (tenantName: string) => {
+ await goToPodInTenant(tenantName);
+ await goToPodSection(1);
+ await checkPodDescribeHasSections();
+};
+
+const checkPodDescribeHasSections = async () => {
+ await t
+ .expect(Selector("#pod-describe-summary").exists)
+ .ok()
+ .expect(Selector("#pod-describe-annotations").exists)
+ .ok()
+ .expect(Selector("#pod-describe-labels").exists)
+ .ok()
+ .expect(Selector("#pod-describe-conditions").exists)
+ .ok()
+ .expect(Selector("#pod-describe-tolerations").exists)
+ .ok()
+ .expect(Selector("#pod-describe-volumes").exists)
+ .ok()
+ .expect(Selector("#pod-describe-containers").exists)
+ .ok();
+};
+
+const testPvcDescribe = async (tenantName: string) => {
+ await goToPvcInTenant(tenantName);
+ await goToPvcSection(1);
+ await checkPvcDescribeHasSections();
+};
+
+const checkPvcDescribeHasSections = async () => {
+ await t
+ .expect(Selector("#pvc-describe-summary").exists)
+ .ok()
+ .expect(Selector("#pvc-describe-annotations").exists)
+ .ok()
+ .expect(Selector("#pvc-describe-labels").exists)
+ .ok();
+};
+
+export const checkMonitoringToggle = async (tenantName: string) => {
+ await goToMonitoringSection(tenantName);
+ await t
+ .click("#tenant-monitoring")
+ .click("#confirm-ok")
+ .wait(1000)
+ .expect(Selector("#image").exists)
+ .notOk()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("prometheus:")
+ )
+ .notOk();
+ await t
+ .click(Selector(`a[href$="/monitoring"]`))
+ .click("#tenant-monitoring")
+ .click("#confirm-ok")
+ .wait(5000)
+ .expect(Selector("#prometheus_image").exists)
+ .ok()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("prometheus:")
+ )
+ .ok();
+};
+export const checkMonitoringFieldsAcceptValues = async (tenantName: string) => {
+ await goToMonitoringSection(tenantName);
+ await t
+ .typeText("#prometheus_image", "quay.io/prometheus/prometheus:latest", {
+ replace: true,
+ })
+ .typeText("#sidecarImage", "library/alpine:latest", { replace: true })
+ .typeText("#initImage", "library/busybox:1.33.1", { replace: true })
+ .typeText("#diskCapacityGB", "1", { replace: true })
+ .typeText("#cpuRequest", "1", { replace: true })
+ .typeText("#memRequest", "1", { replace: true })
+ .typeText("#serviceAccountName", "monitoringTestServiceAccountName", {
+ replace: true,
+ })
+ .typeText("#storageClassName", "monitoringTestStorageClassName", {
+ replace: true,
+ })
+ .typeText("#securityContext_runAsUser", "1212", { replace: true })
+ .typeText("#securityContext_runAsGroup", "3434", { replace: true })
+ .typeText("#securityContext_fsGroup", "5656", { replace: true })
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .ok()
+ .click("#securityContext_runAsNonRoot")
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .notOk()
+ .typeText("#key-Labels-0", "monitoringLabelKey0Test", { replace: true })
+ .typeText("#val-Labels-0", "monitoringLabelVal0Test", { replace: true })
+ .click("#add-Labels-0")
+ .typeText("#key-Annotations-0", "monitoringAnnotationsKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-Annotations-0", "monitoringAnnotationsVal0Test", {
+ replace: true,
+ })
+ .click("#add-Annotations-0")
+ .typeText("#key-NodeSelector-0", "monitoringNodeSelectorKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-NodeSelector-0", "monitoringNodeSelectorVal0Test", {
+ replace: true,
+ })
+ .click("#add-NodeSelector-0")
+ .expect(Selector("#key-Labels-1").exists)
+ .ok()
+ .expect(Selector("#key-Annotations-1").exists)
+ .ok()
+ .expect(Selector("#key-NodeSelector-1").exists)
+ .ok()
+ .click("#submit_button")
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("image: quay.io/prometheus/prometheus:latest")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("initimage: library/busybox:1.33.1")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("diskCapacityGB: 1")
+ )
+ .ok()
+ .expect((await Selector("#code_wrapper").textContent).includes('cpu: "1"'))
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("memory: 1Gi")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("serviceAccountName: monitoringTestServiceAccountName")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("sidecarimage: library/alpine:latest")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("storageClassName: monitoringTestStorageClassName")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("fsGroup: 5656")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsGroup: 3434")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsUser: 1212")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("monitoringAnnotationsKey0Test: monitoringAnnotationsVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes(
+ "monitoringNodeSelectorKey0Test: monitoringNodeSelectorVal0Test"
+ )
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("monitoringLabelKey0Test: monitoringLabelVal0Test")
+ )
+ .ok();
+};
+
+const checkLoggingToggle = async (tenantName: string) => {
+ await goToLoggingSection(tenantName);
+ await t
+ .click("#tenant_logging")
+ .click("#confirm-ok")
+ .wait(3000)
+ .expect(Selector("#image").exists)
+ .notOk()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect((await Selector("#code_wrapper").textContent).includes("log:"))
+ .notOk();
+ await t
+ .click(Selector(`a[href$="/logging"]`))
+ .click("#tenant_logging")
+ .click("#confirm-ok")
+ .wait(3000)
+ .expect(Selector("#image").exists)
+ .ok()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect((await Selector("#code_wrapper").textContent).includes("log:"))
+ .ok();
+};
+
+const checkLoggingFieldsAcceptValues = async (tenantName: string) => {
+ await goToLoggingSection(tenantName);
+ await t
+ .wait(3000)
+ .typeText("#image", "minio/operator:v4.4.22", { replace: true })
+ .typeText("#diskCapacityGB", "3", { replace: true })
+ .typeText("#cpuRequest", "3", { replace: true })
+ .typeText("#memRequest", "3", { replace: true })
+ .typeText("#serviceAccountName", "loggingTestServiceAccountName", {
+ replace: true,
+ })
+ .typeText("#securityContext_runAsUser", "1111", { replace: true })
+ .typeText("#securityContext_runAsGroup", "2222", { replace: true })
+ .typeText("#securityContext_fsGroup", "3333", { replace: true })
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .notOk()
+ .click("#securityContext_runAsNonRoot")
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .ok()
+ .typeText("#key-Labels-0", "loggingLabelKey0Test", { replace: true })
+ .typeText("#val-Labels-0", "loggingLabelVal0Test", { replace: true })
+ .click("#add-Labels-0")
+ .typeText("#key-Annotations-0", "loggingAnnotationsKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-Annotations-0", "loggingAnnotationsVal0Test", {
+ replace: true,
+ })
+ .click("#add-Annotations-0")
+ .typeText("#key-NodeSelector-0", "loggingNodeSelectorKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-NodeSelector-0", "loggingNodeSelectorVal0Test", {
+ replace: true,
+ })
+ .click("#add-NodeSelector-0")
+ .expect(Selector("#key-Labels-1").exists)
+ .ok()
+ .expect(Selector("#key-Annotations-1").exists)
+ .ok()
+ .expect(Selector("#key-NodeSelector-1").exists)
+ .ok()
+ .click("#submit_button")
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("image: minio/operator:v4.4.22")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("diskCapacityGB: 3")
+ )
+ .ok()
+ .expect((await Selector("#code_wrapper").textContent).includes('cpu: "3"'))
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes('memory: "3"')
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("serviceAccountName: loggingTestServiceAccountName")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("fsGroup: 3333")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsGroup: 2222")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsUser: 1111")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("AnnotationsKey0Test: loggingAnnotationsVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("NodeSelectorKey0Test: loggingNodeSelectorVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingLabelKey0Test: loggingLabelVal0Test")
+ )
+ .ok();
+};
+
+const checkLoggingDBFieldsAcceptValues = async (tenantName: string) => {
+ await goToLoggingDBSection(tenantName);
+ await t
+ .typeText("#dbImage", "library/postgres:13", { replace: true })
+ .typeText("#dbInitImage", "library/busybox:1.33.1", { replace: true })
+ .typeText("#dbCPURequest", "4", { replace: true })
+ .typeText("#dbMemRequest", "4", { replace: true })
+ .typeText("#securityContext_runAsUser", "4444", { replace: true })
+ .typeText("#securityContext_runAsGroup", "5555", { replace: true })
+ .typeText("#securityContext_fsGroup", "6666", { replace: true })
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .notOk()
+ .click("#securityContext_runAsNonRoot")
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .ok()
+ .typeText("#key-dbLabels-0", "loggingdbLabelKey0Test", { replace: true })
+ .typeText("#val-dbLabels-0", "loggingdbLabelVal0Test", { replace: true })
+ .click("#add-dbLabels-0")
+ .typeText("#key-dbAnnotations-0", "loggingdbAnnotationsKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-dbAnnotations-0", "loggingdbAnnotationsVal0Test", {
+ replace: true,
+ })
+ .click("#add-dbAnnotations-0")
+ .typeText("#key-DBNodeSelector-0", "loggingdbNodeSelectorKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-DBNodeSelector-0", "loggingdbNodeSelectorVal0Test", {
+ replace: true,
+ })
+ .click("#add-DBNodeSelector-0")
+ .expect(Selector("#key-dbLabels-1").exists)
+ .ok()
+ .expect(Selector("#key-dbAnnotations-1").exists)
+ .ok()
+ .expect(Selector("#key-DBNodeSelector-1").exists)
+ .ok()
+ .click("#remove-dbLabels-1")
+ .click("#remove-dbAnnotations-1")
+ .click("#remove-DBNodeSelector-1")
+ .expect(Selector("#key-dbLabels-1").exists)
+ .notOk()
+ .expect(Selector("#key-dbAnnotations-1").exists)
+ .notOk()
+ .expect(Selector("#key-DBNodeSelector-1").exists)
+ .notOk()
+ .click("#submit_button")
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("image: library/postgres:13")
+ )
+ .ok()
+ .expect((await Selector("#code_wrapper").textContent).includes('cpu: "4"'))
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes('memory: "4"')
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("fsGroup: 6666")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsGroup: 5555")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsUser: 4444")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingdbAnnotationsKey0Test: loggingdbAnnotationsVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingdbNodeSelectorKey0Test: loggingdbNodeSelectorVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingdbLabelKey0Test: loggingdbLabelVal0Test")
+ )
+ .ok();
+};
+
+// Test 1
+test("Test Prometheus config fields can be edited and submitted", async (t) => {
+ const tenantName = `storage-lite`;
+ await loginToOperator();
+ await checkMonitoringFieldsAcceptValues(tenantName);
+});
diff --git a/portal-ui/tests/operator/tenant/test-3/tenant-test-3.ts b/portal-ui/tests/operator/tenant/test-3/tenant-test-3.ts
new file mode 100644
index 000000000..1648aca63
--- /dev/null
+++ b/portal-ui/tests/operator/tenant/test-3/tenant-test-3.ts
@@ -0,0 +1,473 @@
+// 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 .
+
+import { t, Selector } from "testcafe";
+import {
+ loginToOperator,
+ createTenant,
+ createTenantWithoutAuditLog,
+ deleteTenant,
+ redirectToTenantsList,
+ goToPodInTenant,
+ goToPodSection,
+ goToPvcInTenant,
+ goToPvcSection,
+ goToMonitoringSection,
+ goToLoggingSection,
+ goToLoggingDBSection,
+} from "../../utils";
+
+fixture("For user with default permissions").page("http://localhost:9090");
+
+const testPODDescribe = async (tenantName: string) => {
+ await goToPodInTenant(tenantName);
+ await goToPodSection(1);
+ await checkPodDescribeHasSections();
+};
+
+const checkPodDescribeHasSections = async () => {
+ await t
+ .expect(Selector("#pod-describe-summary").exists)
+ .ok()
+ .expect(Selector("#pod-describe-annotations").exists)
+ .ok()
+ .expect(Selector("#pod-describe-labels").exists)
+ .ok()
+ .expect(Selector("#pod-describe-conditions").exists)
+ .ok()
+ .expect(Selector("#pod-describe-tolerations").exists)
+ .ok()
+ .expect(Selector("#pod-describe-volumes").exists)
+ .ok()
+ .expect(Selector("#pod-describe-containers").exists)
+ .ok();
+};
+
+const testPvcDescribe = async (tenantName: string) => {
+ await goToPvcInTenant(tenantName);
+ await goToPvcSection(1);
+ await checkPvcDescribeHasSections();
+};
+
+const checkPvcDescribeHasSections = async () => {
+ await t
+ .expect(Selector("#pvc-describe-summary").exists)
+ .ok()
+ .expect(Selector("#pvc-describe-annotations").exists)
+ .ok()
+ .expect(Selector("#pvc-describe-labels").exists)
+ .ok();
+};
+
+export const checkMonitoringToggle = async (tenantName: string) => {
+ await goToMonitoringSection(tenantName);
+ await t
+ .click("#tenant-monitoring")
+ .click("#confirm-ok")
+ .wait(1000)
+ .expect(Selector("#image").exists)
+ .notOk()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("prometheus:")
+ )
+ .notOk();
+ await t
+ .click(Selector(`a[href$="/monitoring"]`))
+ .click("#tenant-monitoring")
+ .click("#confirm-ok")
+ .wait(5000)
+ .expect(Selector("#prometheus_image").exists)
+ .ok()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("prometheus:")
+ )
+ .ok();
+};
+export const checkMonitoringFieldsAcceptValues = async (tenantName: string) => {
+ await goToMonitoringSection(tenantName);
+ await t
+ .typeText("#prometheus_image", "quay.io/prometheus/prometheus:latest", {
+ replace: true,
+ })
+ .typeText("#sidecarImage", "library/alpine:latest", { replace: true })
+ .typeText("#initImage", "library/busybox:1.33.1", { replace: true })
+ .typeText("#diskCapacityGB", "1", { replace: true })
+ .typeText("#cpuRequest", "1", { replace: true })
+ .typeText("#memRequest", "1", { replace: true })
+ .typeText("#serviceAccountName", "monitoringTestServiceAccountName", {
+ replace: true,
+ })
+ .typeText("#storageClassName", "monitoringTestStorageClassName", {
+ replace: true,
+ })
+ .typeText("#securityContext_runAsUser", "1212", { replace: true })
+ .typeText("#securityContext_runAsGroup", "3434", { replace: true })
+ .typeText("#securityContext_fsGroup", "5656", { replace: true })
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .ok()
+ .click("#securityContext_runAsNonRoot")
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .notOk()
+ .typeText("#key-Labels-0", "monitoringLabelKey0Test", { replace: true })
+ .typeText("#val-Labels-0", "monitoringLabelVal0Test", { replace: true })
+ .click("#add-Labels-0")
+ .typeText("#key-Annotations-0", "monitoringAnnotationsKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-Annotations-0", "monitoringAnnotationsVal0Test", {
+ replace: true,
+ })
+ .click("#add-Annotations-0")
+ .typeText("#key-NodeSelector-0", "monitoringNodeSelectorKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-NodeSelector-0", "monitoringNodeSelectorVal0Test", {
+ replace: true,
+ })
+ .click("#add-NodeSelector-0")
+ .expect(Selector("#key-Labels-1").exists)
+ .ok()
+ .expect(Selector("#key-Annotations-1").exists)
+ .ok()
+ .expect(Selector("#key-NodeSelector-1").exists)
+ .ok()
+ .click("#submit_button")
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("image: quay.io/prometheus/prometheus:latest")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("initimage: library/busybox:1.33.1")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("diskCapacityGB: 1")
+ )
+ .ok()
+ .expect((await Selector("#code_wrapper").textContent).includes('cpu: "1"'))
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("memory: 1Gi")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("serviceAccountName: monitoringTestServiceAccountName")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("sidecarimage: library/alpine:latest")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("storageClassName: monitoringTestStorageClassName")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("fsGroup: 5656")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsGroup: 3434")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsUser: 1212")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("monitoringAnnotationsKey0Test: monitoringAnnotationsVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes(
+ "monitoringNodeSelectorKey0Test: monitoringNodeSelectorVal0Test"
+ )
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("monitoringLabelKey0Test: monitoringLabelVal0Test")
+ )
+ .ok();
+};
+
+const checkLoggingToggle = async (tenantName: string) => {
+ await goToLoggingSection(tenantName);
+ await t
+ .click("#tenant_logging")
+ .click("#confirm-ok")
+ .wait(3000)
+ .expect(Selector("#image").exists)
+ .notOk()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect((await Selector("#code_wrapper").textContent).includes("log:"))
+ .notOk();
+ await t
+ .click(Selector(`a[href$="/logging"]`))
+ .click("#tenant_logging")
+ .click("#confirm-ok")
+ .wait(3000)
+ .expect(Selector("#image").exists)
+ .ok()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect((await Selector("#code_wrapper").textContent).includes("log:"))
+ .ok();
+};
+
+const checkLoggingFieldsAcceptValues = async (tenantName: string) => {
+ await goToLoggingSection(tenantName);
+ await t
+ .wait(3000)
+ .typeText("#image", "minio/operator:v4.4.22", { replace: true })
+ .typeText("#diskCapacityGB", "3", { replace: true })
+ .typeText("#cpuRequest", "3", { replace: true })
+ .typeText("#memRequest", "3", { replace: true })
+ .typeText("#serviceAccountName", "loggingTestServiceAccountName", {
+ replace: true,
+ })
+ .typeText("#securityContext_runAsUser", "1111", { replace: true })
+ .typeText("#securityContext_runAsGroup", "2222", { replace: true })
+ .typeText("#securityContext_fsGroup", "3333", { replace: true })
+ .expect(Selector("#securityContext_runAsNonRoot").exists)
+ .ok();
+ await t.wait(3000);
+ await t
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .notOk()
+ .click("#securityContext_runAsNonRoot")
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .ok()
+ .typeText("#key-Labels-0", "loggingLabelKey0Test", { replace: true })
+ .typeText("#val-Labels-0", "loggingLabelVal0Test", { replace: true })
+ .click("#add-Labels-0")
+ .typeText("#key-Annotations-0", "loggingAnnotationsKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-Annotations-0", "loggingAnnotationsVal0Test", {
+ replace: true,
+ })
+ .click("#add-Annotations-0")
+ .typeText("#key-NodeSelector-0", "loggingNodeSelectorKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-NodeSelector-0", "loggingNodeSelectorVal0Test", {
+ replace: true,
+ })
+ .click("#add-NodeSelector-0")
+ .expect(Selector("#key-Labels-1").exists)
+ .ok()
+ .expect(Selector("#key-Annotations-1").exists)
+ .ok()
+ .expect(Selector("#key-NodeSelector-1").exists)
+ .ok()
+ .click("#submit_button")
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("image: minio/operator:v4.4.22")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("diskCapacityGB: 3")
+ )
+ .ok()
+ .expect((await Selector("#code_wrapper").textContent).includes('cpu: "3"'))
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes('memory: "3"')
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("serviceAccountName: loggingTestServiceAccountName")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("fsGroup: 3333")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsGroup: 2222")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsUser: 1111")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("AnnotationsKey0Test: loggingAnnotationsVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("NodeSelectorKey0Test: loggingNodeSelectorVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingLabelKey0Test: loggingLabelVal0Test")
+ )
+ .ok();
+};
+
+const checkLoggingDBFieldsAcceptValues = async (tenantName: string) => {
+ await goToLoggingDBSection(tenantName);
+ await t
+ .typeText("#dbImage", "library/postgres:13", { replace: true })
+ .typeText("#dbInitImage", "library/busybox:1.33.1", { replace: true })
+ .typeText("#dbCPURequest", "4", { replace: true })
+ .typeText("#dbMemRequest", "4", { replace: true })
+ .typeText("#securityContext_runAsUser", "4444", { replace: true })
+ .typeText("#securityContext_runAsGroup", "5555", { replace: true })
+ .typeText("#securityContext_fsGroup", "6666", { replace: true })
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .notOk()
+ .click("#securityContext_runAsNonRoot")
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .ok()
+ .typeText("#key-dbLabels-0", "loggingdbLabelKey0Test", { replace: true })
+ .typeText("#val-dbLabels-0", "loggingdbLabelVal0Test", { replace: true })
+ .click("#add-dbLabels-0")
+ .typeText("#key-dbAnnotations-0", "loggingdbAnnotationsKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-dbAnnotations-0", "loggingdbAnnotationsVal0Test", {
+ replace: true,
+ })
+ .click("#add-dbAnnotations-0")
+ .typeText("#key-DBNodeSelector-0", "loggingdbNodeSelectorKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-DBNodeSelector-0", "loggingdbNodeSelectorVal0Test", {
+ replace: true,
+ })
+ .click("#add-DBNodeSelector-0")
+ .expect(Selector("#key-dbLabels-1").exists)
+ .ok()
+ .expect(Selector("#key-dbAnnotations-1").exists)
+ .ok()
+ .expect(Selector("#key-DBNodeSelector-1").exists)
+ .ok()
+ .click("#remove-dbLabels-1")
+ .click("#remove-dbAnnotations-1")
+ .click("#remove-DBNodeSelector-1")
+ .expect(Selector("#key-dbLabels-1").exists)
+ .notOk()
+ .expect(Selector("#key-dbAnnotations-1").exists)
+ .notOk()
+ .expect(Selector("#key-DBNodeSelector-1").exists)
+ .notOk()
+ .click("#submit_button")
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("image: library/postgres:13")
+ )
+ .ok()
+ .expect((await Selector("#code_wrapper").textContent).includes('cpu: "4"'))
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes('memory: "4"')
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("fsGroup: 6666")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsGroup: 5555")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsUser: 4444")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingdbAnnotationsKey0Test: loggingdbAnnotationsVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingdbNodeSelectorKey0Test: loggingdbNodeSelectorVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingdbLabelKey0Test: loggingdbLabelVal0Test")
+ )
+ .ok();
+};
+
+// Test 1
+test("Test Audit Logging can be disabled and enabled", async (t) => {
+ const tenantName = `storage-lite`;
+ await loginToOperator();
+ await checkLoggingToggle(tenantName);
+});
diff --git a/portal-ui/tests/operator/tenant/test-4/tenant-test-4.ts b/portal-ui/tests/operator/tenant/test-4/tenant-test-4.ts
new file mode 100644
index 000000000..9a83ff13e
--- /dev/null
+++ b/portal-ui/tests/operator/tenant/test-4/tenant-test-4.ts
@@ -0,0 +1,474 @@
+// 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 .
+
+import { t, Selector } from "testcafe";
+import {
+ loginToOperator,
+ createTenant,
+ createTenantWithoutAuditLog,
+ deleteTenant,
+ redirectToTenantsList,
+ goToPodInTenant,
+ goToPodSection,
+ goToPvcInTenant,
+ goToPvcSection,
+ goToMonitoringSection,
+ goToLoggingSection,
+ goToLoggingDBSection,
+} from "../../utils";
+
+fixture("For user with default permissions").page("http://localhost:9090");
+
+const testPODDescribe = async (tenantName: string) => {
+ await goToPodInTenant(tenantName);
+ await goToPodSection(1);
+ await checkPodDescribeHasSections();
+};
+
+const checkPodDescribeHasSections = async () => {
+ await t
+ .expect(Selector("#pod-describe-summary").exists)
+ .ok()
+ .expect(Selector("#pod-describe-annotations").exists)
+ .ok()
+ .expect(Selector("#pod-describe-labels").exists)
+ .ok()
+ .expect(Selector("#pod-describe-conditions").exists)
+ .ok()
+ .expect(Selector("#pod-describe-tolerations").exists)
+ .ok()
+ .expect(Selector("#pod-describe-volumes").exists)
+ .ok()
+ .expect(Selector("#pod-describe-containers").exists)
+ .ok();
+};
+
+const testPvcDescribe = async (tenantName: string) => {
+ await goToPvcInTenant(tenantName);
+ await goToPvcSection(1);
+ await checkPvcDescribeHasSections();
+};
+
+const checkPvcDescribeHasSections = async () => {
+ await t
+ .expect(Selector("#pvc-describe-summary").exists)
+ .ok()
+ .expect(Selector("#pvc-describe-annotations").exists)
+ .ok()
+ .expect(Selector("#pvc-describe-labels").exists)
+ .ok();
+};
+
+export const checkMonitoringToggle = async (tenantName: string) => {
+ await goToMonitoringSection(tenantName);
+ await t
+ .click("#tenant-monitoring")
+ .click("#confirm-ok")
+ .wait(1000)
+ .expect(Selector("#image").exists)
+ .notOk()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("prometheus:")
+ )
+ .notOk();
+ await t
+ .click(Selector(`a[href$="/monitoring"]`))
+ .click("#tenant-monitoring")
+ .click("#confirm-ok")
+ .wait(5000)
+ .expect(Selector("#prometheus_image").exists)
+ .ok()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("prometheus:")
+ )
+ .ok();
+};
+export const checkMonitoringFieldsAcceptValues = async (tenantName: string) => {
+ await goToMonitoringSection(tenantName);
+ await t
+ .typeText("#prometheus_image", "quay.io/prometheus/prometheus:latest", {
+ replace: true,
+ })
+ .typeText("#sidecarImage", "library/alpine:latest", { replace: true })
+ .typeText("#initImage", "library/busybox:1.33.1", { replace: true })
+ .typeText("#diskCapacityGB", "1", { replace: true })
+ .typeText("#cpuRequest", "1", { replace: true })
+ .typeText("#memRequest", "1", { replace: true })
+ .typeText("#serviceAccountName", "monitoringTestServiceAccountName", {
+ replace: true,
+ })
+ .typeText("#storageClassName", "monitoringTestStorageClassName", {
+ replace: true,
+ })
+ .typeText("#securityContext_runAsUser", "1212", { replace: true })
+ .typeText("#securityContext_runAsGroup", "3434", { replace: true })
+ .typeText("#securityContext_fsGroup", "5656", { replace: true })
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .ok()
+ .click("#securityContext_runAsNonRoot")
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .notOk()
+ .typeText("#key-Labels-0", "monitoringLabelKey0Test", { replace: true })
+ .typeText("#val-Labels-0", "monitoringLabelVal0Test", { replace: true })
+ .click("#add-Labels-0")
+ .typeText("#key-Annotations-0", "monitoringAnnotationsKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-Annotations-0", "monitoringAnnotationsVal0Test", {
+ replace: true,
+ })
+ .click("#add-Annotations-0")
+ .typeText("#key-NodeSelector-0", "monitoringNodeSelectorKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-NodeSelector-0", "monitoringNodeSelectorVal0Test", {
+ replace: true,
+ })
+ .click("#add-NodeSelector-0")
+ .expect(Selector("#key-Labels-1").exists)
+ .ok()
+ .expect(Selector("#key-Annotations-1").exists)
+ .ok()
+ .expect(Selector("#key-NodeSelector-1").exists)
+ .ok()
+ .click("#submit_button")
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("image: quay.io/prometheus/prometheus:latest")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("initimage: library/busybox:1.33.1")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("diskCapacityGB: 1")
+ )
+ .ok()
+ .expect((await Selector("#code_wrapper").textContent).includes('cpu: "1"'))
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("memory: 1Gi")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("serviceAccountName: monitoringTestServiceAccountName")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("sidecarimage: library/alpine:latest")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("storageClassName: monitoringTestStorageClassName")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("fsGroup: 5656")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsGroup: 3434")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsUser: 1212")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("monitoringAnnotationsKey0Test: monitoringAnnotationsVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes(
+ "monitoringNodeSelectorKey0Test: monitoringNodeSelectorVal0Test"
+ )
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("monitoringLabelKey0Test: monitoringLabelVal0Test")
+ )
+ .ok();
+};
+
+const checkLoggingToggle = async (tenantName: string) => {
+ await goToLoggingSection(tenantName);
+ await t
+ .click("#tenant_logging")
+ .click("#confirm-ok")
+ .wait(3000)
+ .expect(Selector("#image").exists)
+ .notOk()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect((await Selector("#code_wrapper").textContent).includes("log:"))
+ .notOk();
+ await t
+ .click(Selector(`a[href$="/logging"]`))
+ .click("#tenant_logging")
+ .click("#confirm-ok")
+ .wait(3000)
+ .expect(Selector("#image").exists)
+ .ok()
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect((await Selector("#code_wrapper").textContent).includes("log:"))
+ .ok();
+};
+
+const checkLoggingFieldsAcceptValues = async (tenantName: string) => {
+ await goToLoggingSection(tenantName);
+ await t
+ .wait(3000)
+ .typeText("#image", "minio/operator:v4.4.22", { replace: true })
+ .typeText("#diskCapacityGB", "3", { replace: true })
+ .typeText("#cpuRequest", "3", { replace: true })
+ .typeText("#memRequest", "3", { replace: true })
+ .typeText("#serviceAccountName", "loggingTestServiceAccountName", {
+ replace: true,
+ })
+ .typeText("#securityContext_runAsUser", "1111", { replace: true })
+ .typeText("#securityContext_runAsGroup", "2222", { replace: true })
+ .typeText("#securityContext_fsGroup", "3333", { replace: true })
+ .expect(Selector("#securityContext_runAsNonRoot").exists)
+ .ok();
+ await t.wait(3000);
+ await t
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .ok()
+ .click("#securityContext_runAsNonRoot")
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .notOk()
+ .typeText("#key-Labels-0", "loggingLabelKey0Test", { replace: true })
+ .typeText("#val-Labels-0", "loggingLabelVal0Test", { replace: true })
+ .click("#add-Labels-0")
+ .typeText("#key-Annotations-0", "loggingAnnotationsKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-Annotations-0", "loggingAnnotationsVal0Test", {
+ replace: true,
+ })
+ .click("#add-Annotations-0")
+ .typeText("#key-NodeSelector-0", "loggingNodeSelectorKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-NodeSelector-0", "loggingNodeSelectorVal0Test", {
+ replace: true,
+ })
+ .click("#add-NodeSelector-0")
+ .expect(Selector("#key-Labels-1").exists)
+ .ok()
+ .expect(Selector("#key-Annotations-1").exists)
+ .ok()
+ .expect(Selector("#key-NodeSelector-1").exists)
+ .ok()
+ .click("#submit_button")
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("image: minio/operator:v4.4.22")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("diskCapacityGB: 3")
+ )
+ .ok()
+ .expect((await Selector("#code_wrapper").textContent).includes('cpu: "3"'))
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes('memory: "3"')
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("serviceAccountName: loggingTestServiceAccountName")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("fsGroup: 3333")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsGroup: 2222")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsUser: 1111")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("AnnotationsKey0Test: loggingAnnotationsVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("NodeSelectorKey0Test: loggingNodeSelectorVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingLabelKey0Test: loggingLabelVal0Test")
+ )
+ .ok();
+};
+
+const checkLoggingDBFieldsAcceptValues = async (tenantName: string) => {
+ await goToLoggingDBSection(tenantName);
+ await t
+ .typeText("#dbImage", "library/postgres:13", { replace: true })
+ .typeText("#dbInitImage", "library/busybox:1.33.1", { replace: true })
+ .typeText("#dbCPURequest", "4", { replace: true })
+ .typeText("#dbMemRequest", "4", { replace: true })
+ .typeText("#securityContext_runAsUser", "4444", { replace: true })
+ .typeText("#securityContext_runAsGroup", "5555", { replace: true })
+ .typeText("#securityContext_fsGroup", "6666", { replace: true })
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .ok()
+ .click("#securityContext_runAsNonRoot")
+ .expect(Selector("#securityContext_runAsNonRoot").checked)
+ .notOk()
+ .typeText("#key-dbLabels-0", "loggingdbLabelKey0Test", { replace: true })
+ .typeText("#val-dbLabels-0", "loggingdbLabelVal0Test", { replace: true })
+ .click("#add-dbLabels-0")
+ .typeText("#key-dbAnnotations-0", "loggingdbAnnotationsKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-dbAnnotations-0", "loggingdbAnnotationsVal0Test", {
+ replace: true,
+ })
+ .click("#add-dbAnnotations-0")
+ .typeText("#key-DBNodeSelector-0", "loggingdbNodeSelectorKey0Test", {
+ replace: true,
+ })
+ .typeText("#val-DBNodeSelector-0", "loggingdbNodeSelectorVal0Test", {
+ replace: true,
+ })
+ .click("#add-DBNodeSelector-0")
+ .expect(Selector("#key-dbLabels-1").exists)
+ .ok()
+ .expect(Selector("#key-dbAnnotations-1").exists)
+ .ok()
+ .expect(Selector("#key-DBNodeSelector-1").exists)
+ .ok()
+ .click("#remove-dbLabels-1")
+ .click("#remove-dbAnnotations-1")
+ .click("#remove-DBNodeSelector-1")
+ .expect(Selector("#key-dbLabels-1").exists)
+ .notOk()
+ .expect(Selector("#key-dbAnnotations-1").exists)
+ .notOk()
+ .expect(Selector("#key-DBNodeSelector-1").exists)
+ .notOk()
+ .click("#submit_button")
+ .click("#yaml_button")
+ .expect(Selector("#code_wrapper").exists)
+ .ok();
+ await t
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("image: library/postgres:13")
+ )
+ .ok()
+ .expect((await Selector("#code_wrapper").textContent).includes('cpu: "4"'))
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes('memory: "4"')
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("fsGroup: 6666")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsGroup: 5555")
+ )
+ .ok()
+ .expect(
+ (await Selector("#code_wrapper").textContent).includes("runAsUser: 4444")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingdbAnnotationsKey0Test: loggingdbAnnotationsVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingdbNodeSelectorKey0Test: loggingdbNodeSelectorVal0Test")
+ )
+ .ok()
+ .expect(
+ (
+ await Selector("#code_wrapper").textContent
+ ).includes("loggingdbLabelKey0Test: loggingdbLabelVal0Test")
+ )
+ .ok();
+};
+
+// Test 1
+test("Test Audit Log config fields can be edited and submitted", async (t) => {
+ const tenantName = `storage-lite`;
+ await loginToOperator();
+ await checkLoggingFieldsAcceptValues(tenantName);
+ await checkLoggingDBFieldsAcceptValues(tenantName);
+});