mirror of
https://github.com/OpenMaxIO/openmaxio-object-browser
synced 2026-07-01 07:41:18 -07:00
Deprecated Lifecycle and Tiering UI (#3470)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -1,112 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2023 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { expect, Page } from "@playwright/test";
|
||||
import { test as baseTest } from "./fixtures/baseFixture";
|
||||
import { minioadminFile } from "./consts";
|
||||
import { BucketsListPage } from "./pom/BucketsListPage";
|
||||
import { CreateBucketPage } from "./pom/CreateBucketPage";
|
||||
import { BucketSummaryPage } from "./pom/BucketSummaryPage";
|
||||
|
||||
type LifeCycleObjectVersionFx = {
|
||||
activeBucketName: string;
|
||||
bucketsListPage: BucketsListPage;
|
||||
createBucketPage: CreateBucketPage;
|
||||
bucketSummaryPage: any;
|
||||
};
|
||||
|
||||
const test = baseTest.extend<LifeCycleObjectVersionFx>({
|
||||
activeBucketName: "",
|
||||
bucketListPage: async ({ page }: { page: Page }, use: any) => {
|
||||
let bucketListPage = new BucketsListPage(page);
|
||||
await bucketListPage.loadPage();
|
||||
await bucketListPage.goToCreateBucket();
|
||||
await use(bucketListPage);
|
||||
},
|
||||
createBucketPage: async ({ page }: { page: Page }, use: any) => {
|
||||
let createBucketPage = new CreateBucketPage(page);
|
||||
await use(createBucketPage);
|
||||
},
|
||||
//bucket name is dynamic in parallel test runs.
|
||||
bucketSummaryPage: async ({ page }: { page: Page }, use: any) => {
|
||||
await use((bucketName: string) => {
|
||||
return new BucketSummaryPage(page, bucketName);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
test.use({ storageState: minioadminFile });
|
||||
|
||||
const versionedBucketName = "versioned-bucket";
|
||||
const nonVersionedBucketName = "non-versioned-bucket";
|
||||
|
||||
test.describe("Add Lifecycle Rule Modal in bucket settings tests for object version ", () => {
|
||||
test("Test if Object Version selector is present in Lifecycle rule modal", async ({
|
||||
page,
|
||||
bucketListPage,
|
||||
createBucketPage,
|
||||
bucketSummaryPage,
|
||||
}) => {
|
||||
await test.step("Create Versioned Bucket", async () => {
|
||||
await createBucketPage.createVersionedBucket(versionedBucketName);
|
||||
await bucketListPage.clickOnBucketRow(versionedBucketName);
|
||||
bucketSummaryPage = bucketSummaryPage(versionedBucketName);
|
||||
await bucketSummaryPage.clickOnTab("lifecycle"); //Tab Text is used.
|
||||
});
|
||||
|
||||
await test.step("Check if object version option is available on a versioned bucket", async () => {
|
||||
const objectVersionsEl = await bucketSummaryPage.getObjectVersionOption();
|
||||
await expect(await objectVersionsEl).toHaveText("Current Version");
|
||||
await expect(await objectVersionsEl).toBeTruthy();
|
||||
await bucketSummaryPage.getLocator("#close").click();
|
||||
});
|
||||
|
||||
await test.step("Clean up bucket and verify the clean up", async () => {
|
||||
await bucketSummaryPage.confirmDeleteBucket();
|
||||
const existBukCount =
|
||||
await bucketListPage.isBucketExist(versionedBucketName);
|
||||
await expect(existBukCount).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
test("Test if Object Version selector is NOT present in Lifecycle rule modal", async ({
|
||||
page,
|
||||
createBucketPage,
|
||||
bucketListPage,
|
||||
bucketSummaryPage,
|
||||
}) => {
|
||||
await test.step("Create NON Versioned Bucket and navigate to lifecycle settings in summary page", async () => {
|
||||
await createBucketPage.createBucket(nonVersionedBucketName);
|
||||
await bucketListPage.clickOnBucketRow(nonVersionedBucketName);
|
||||
bucketSummaryPage = bucketSummaryPage(versionedBucketName);
|
||||
await bucketSummaryPage.clickOnTab("lifecycle");
|
||||
});
|
||||
|
||||
await test.step("Check if object version option is NOT available on a non versioned bucket", async () => {
|
||||
const objectVersionsEl = await bucketSummaryPage.getObjectVersionOption();
|
||||
await expect(await objectVersionsEl.count()).toEqual(0);
|
||||
await bucketSummaryPage.getLocator("#close").click();
|
||||
});
|
||||
|
||||
await test.step("Clean up bucket and verify the clean up", async () => {
|
||||
await bucketSummaryPage.confirmDeleteBucket();
|
||||
const existBukCount = await bucketListPage.isBucketExist(
|
||||
nonVersionedBucketName,
|
||||
);
|
||||
await expect(existBukCount).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
import { Page, Locator } from "@playwright/test";
|
||||
|
||||
export class BucketSummaryPage {
|
||||
page: Page;
|
||||
bucketName: string;
|
||||
|
||||
/* Locators */
|
||||
deleteBucketBtn: Locator | undefined;
|
||||
|
||||
constructor(page: Page, bucketName: string) {
|
||||
this.page = page;
|
||||
this.bucketName = bucketName;
|
||||
|
||||
this.initLocators();
|
||||
}
|
||||
getLocator(selector: string): Locator {
|
||||
const page = this.page;
|
||||
const locator: Locator = page.locator(`${selector}`);
|
||||
return locator;
|
||||
}
|
||||
|
||||
initLocators() {
|
||||
this.deleteBucketBtn = this.getLocator("#delete-bucket-button");
|
||||
}
|
||||
|
||||
async loadPage() {
|
||||
await this.clickOnTab(`Summary`);
|
||||
}
|
||||
|
||||
async clickOnTab(tabID: string) {
|
||||
await this.getLocator(`#${tabID}`).click();
|
||||
|
||||
// await page.goto(`${BUCKET_LIST_PAGE}/${this.bucketName}/admin/${tabName}`);
|
||||
}
|
||||
|
||||
async confirmDeleteBucket() {
|
||||
await this.getLocator("#delete-bucket-button").click();
|
||||
await this.getLocator("#confirm-ok").click();
|
||||
}
|
||||
|
||||
async getObjectVersionOption() {
|
||||
await this.page.getByRole("button", { name: "Add Lifecycle Rule" }).click();
|
||||
return this.getLocator("#object_version-select > div").nth(0);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import { Page, Locator } from "@playwright/test";
|
||||
import { BUCKET_LIST_PAGE } from "../consts";
|
||||
|
||||
export class BucketsListPage {
|
||||
page: Page;
|
||||
|
||||
/* Locators */
|
||||
|
||||
createBucketBtn: Locator | undefined;
|
||||
refreshBucketsBtn: Locator | undefined;
|
||||
setReplicationBtn: Locator | undefined;
|
||||
|
||||
bucketListItemPrefix = "#manageBucket-";
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.initLocators();
|
||||
}
|
||||
getLocator(selector: string): Locator {
|
||||
const page = this.page;
|
||||
const locator: Locator = page.locator(`${selector}`);
|
||||
return locator;
|
||||
}
|
||||
|
||||
initLocators() {
|
||||
this.createBucketBtn = this.getLocator("#create-bucket");
|
||||
this.refreshBucketsBtn = this.getLocator("#refresh-buckets");
|
||||
this.setReplicationBtn = this.getLocator("#set-replication");
|
||||
}
|
||||
|
||||
locateBucket(bucketName: string): Locator {
|
||||
const bucketRow = this.getLocator(
|
||||
`${this.bucketListItemPrefix}${bucketName}`,
|
||||
);
|
||||
return bucketRow;
|
||||
}
|
||||
|
||||
async clickOnBucketRow(bucketName: string) {
|
||||
const bucketRow = this.locateBucket(bucketName);
|
||||
await this.page.waitForTimeout(2500);
|
||||
await this.refreshBucketsBtn.click();
|
||||
await bucketRow.click();
|
||||
}
|
||||
async goToCreateBucket() {
|
||||
await this.createBucketBtn?.click();
|
||||
}
|
||||
|
||||
async isBucketExist(bucketName: string) {
|
||||
const existBukCount = await this.locateBucket(bucketName).count();
|
||||
|
||||
return existBukCount;
|
||||
}
|
||||
|
||||
async loadPage() {
|
||||
const page = this.page;
|
||||
await page.goto(BUCKET_LIST_PAGE);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
import { Page, Locator } from "@playwright/test";
|
||||
import { BUCKET_LIST_PAGE } from "../consts";
|
||||
|
||||
export class CreateBucketPage {
|
||||
page: Page;
|
||||
|
||||
/* Locators */
|
||||
|
||||
submitBtn: Locator | undefined;
|
||||
clearBtn: Locator | undefined;
|
||||
bucketNameInput: Locator | undefined;
|
||||
versioningToggle: Locator | undefined;
|
||||
lockingToggle: Locator | undefined;
|
||||
quotaToggle: Locator | undefined;
|
||||
bucketNamingRules: Locator | undefined;
|
||||
|
||||
bucketRetentionToggle: Locator | undefined;
|
||||
quotaSizeInput: Locator | undefined;
|
||||
retentionModeRadio: Locator | undefined;
|
||||
retentionValidity: Locator | undefined;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.initLocators();
|
||||
}
|
||||
getLocator(selector: string): Locator {
|
||||
const page = this.page;
|
||||
const locator: Locator = page.locator(`${selector}`);
|
||||
return locator;
|
||||
}
|
||||
|
||||
initLocators() {
|
||||
this.submitBtn = this.getLocator("#create-bucket");
|
||||
this.clearBtn = this.getLocator("#clear");
|
||||
this.versioningToggle = this.getLocator("#versioned-switch");
|
||||
this.lockingToggle = this.getLocator("#locking-switch");
|
||||
this.quotaToggle = this.getLocator("#bucket_quota-switch");
|
||||
this.bucketNamingRules = this.getLocator("#toggle-naming-rules");
|
||||
this.bucketNameInput = this.getLocator("#bucket-name");
|
||||
}
|
||||
|
||||
//Lazy/Conditional selectors Note: These respective methods must be called before using them.
|
||||
onVersioningToggleOn() {
|
||||
this.bucketRetentionToggle = this.getLocator("#bucket_retention");
|
||||
}
|
||||
|
||||
onBucketQuotaToggleOn() {
|
||||
this.quotaSizeInput = this.getLocator("#quota_size");
|
||||
}
|
||||
|
||||
onRetentionToggleOn() {
|
||||
this.retentionModeRadio = this.getLocator("#retention_mode");
|
||||
this.retentionValidity = this.getLocator("#retention_validity");
|
||||
}
|
||||
|
||||
loadPage() {
|
||||
const page = this.page;
|
||||
page.goto(BUCKET_LIST_PAGE);
|
||||
}
|
||||
|
||||
async fillBucketName(bucketName: string) {
|
||||
await this.bucketNameInput?.click();
|
||||
await this.bucketNameInput?.fill(bucketName);
|
||||
}
|
||||
|
||||
async toggleBucketNamingRules() {
|
||||
await this.bucketNamingRules?.click();
|
||||
}
|
||||
|
||||
async toggleVersioning() {
|
||||
await this.versioningToggle?.check();
|
||||
this.onVersioningToggleOn();
|
||||
//expect to be on
|
||||
}
|
||||
|
||||
async toggleObjectLocking() {
|
||||
await this.lockingToggle?.click();
|
||||
this.onVersioningToggleOn();
|
||||
this.onRetentionToggleOn();
|
||||
}
|
||||
|
||||
async toggleBucketQuota() {
|
||||
await this.quotaToggle?.click();
|
||||
this.onBucketQuotaToggleOn();
|
||||
}
|
||||
|
||||
async toggleRetention() {
|
||||
await this.bucketRetentionToggle?.click();
|
||||
}
|
||||
|
||||
async submitForm() {
|
||||
await this.submitBtn?.click();
|
||||
}
|
||||
|
||||
//Convenience Methods for easy testing
|
||||
|
||||
//create a bucket without any features like versioning, locking, quota etc.
|
||||
async createBucket(bucketName: string) {
|
||||
await this.fillBucketName(bucketName);
|
||||
await this.submitForm();
|
||||
}
|
||||
|
||||
//create a bucket with versioning feature
|
||||
async createVersionedBucket(bucketName: string) {
|
||||
await this.fillBucketName(bucketName);
|
||||
await this.toggleVersioning();
|
||||
await this.submitForm();
|
||||
}
|
||||
//create a bucket with locking feature
|
||||
|
||||
async goToCreateBucket() {
|
||||
await this.submitBtn?.click();
|
||||
}
|
||||
}
|
||||
@@ -894,189 +894,6 @@ export interface GetBucketRetentionConfig {
|
||||
validity?: number;
|
||||
}
|
||||
|
||||
export interface BucketLifecycleResponse {
|
||||
lifecycle?: ObjectBucketLifecycle[];
|
||||
}
|
||||
|
||||
export interface ExpirationResponse {
|
||||
date?: string;
|
||||
/** @format int64 */
|
||||
days?: number;
|
||||
delete_marker?: boolean;
|
||||
delete_all?: boolean;
|
||||
/** @format int64 */
|
||||
noncurrent_expiration_days?: number;
|
||||
/** @format int64 */
|
||||
newer_noncurrent_expiration_versions?: number;
|
||||
}
|
||||
|
||||
export interface TransitionResponse {
|
||||
date?: string;
|
||||
storage_class?: string;
|
||||
/** @format int64 */
|
||||
days?: number;
|
||||
/** @format int64 */
|
||||
noncurrent_transition_days?: number;
|
||||
noncurrent_storage_class?: string;
|
||||
}
|
||||
|
||||
export interface LifecycleTag {
|
||||
key?: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export interface ObjectBucketLifecycle {
|
||||
id?: string;
|
||||
prefix?: string;
|
||||
status?: string;
|
||||
expiration?: ExpirationResponse;
|
||||
transition?: TransitionResponse;
|
||||
tags?: LifecycleTag[];
|
||||
}
|
||||
|
||||
export interface AddBucketLifecycle {
|
||||
/** ILM Rule type (Expiry or transition) */
|
||||
type?: "expiry" | "transition";
|
||||
/** Non required field, it matches a prefix to perform ILM operations on it */
|
||||
prefix?: string;
|
||||
/** Non required field, tags to match ILM files */
|
||||
tags?: string;
|
||||
/**
|
||||
* Required in case of expiry_date or transition fields are not set. it defines an expiry days for ILM
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
expiry_days?: number;
|
||||
/**
|
||||
* Required in case of transition_date or expiry fields are not set. it defines a transition days for ILM
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
transition_days?: number;
|
||||
/** Required only in case of transition is set. it refers to a tier */
|
||||
storage_class?: string;
|
||||
/** Non required, toggle to disable or enable rule */
|
||||
disable?: boolean;
|
||||
/** Non required, toggle to disable or enable rule */
|
||||
expired_object_delete_marker?: boolean;
|
||||
/** Non required, toggle to disable or enable rule */
|
||||
expired_object_delete_all?: boolean;
|
||||
/**
|
||||
* Non required, can be set in case of expiration is enabled
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
noncurrentversion_expiration_days?: number;
|
||||
/**
|
||||
* Non required, can be set in case of transition is enabled
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
noncurrentversion_transition_days?: number;
|
||||
/**
|
||||
* Non required, can be set in case of expiration is enabled
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
newer_noncurrentversion_expiration_versions?: number;
|
||||
/** Non required, can be set in case of transition is enabled */
|
||||
noncurrentversion_transition_storage_class?: string;
|
||||
}
|
||||
|
||||
export interface UpdateBucketLifecycle {
|
||||
/** ILM Rule type (Expiry or transition) */
|
||||
type: "expiry" | "transition";
|
||||
/** Non required field, it matches a prefix to perform ILM operations on it */
|
||||
prefix?: string;
|
||||
/** Non required field, tags to match ILM files */
|
||||
tags?: string;
|
||||
/**
|
||||
* Required in case of expiry_date or transition fields are not set. it defines an expiry days for ILM
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
expiry_days?: number;
|
||||
/**
|
||||
* Required in case of transition_date or expiry fields are not set. it defines a transition days for ILM
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
transition_days?: number;
|
||||
/** Required only in case of transition is set. it refers to a tier */
|
||||
storage_class?: string;
|
||||
/** Non required, toggle to disable or enable rule */
|
||||
disable?: boolean;
|
||||
/** Non required, toggle to disable or enable rule */
|
||||
expired_object_delete_marker?: boolean;
|
||||
/** Non required, toggle to disable or enable rule */
|
||||
expired_object_delete_all?: boolean;
|
||||
/**
|
||||
* Non required, can be set in case of expiration is enabled
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
noncurrentversion_expiration_days?: number;
|
||||
/**
|
||||
* Non required, can be set in case of transition is enabled
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
noncurrentversion_transition_days?: number;
|
||||
/** Non required, can be set in case of transition is enabled */
|
||||
noncurrentversion_transition_storage_class?: string;
|
||||
}
|
||||
|
||||
export interface AddMultiBucketLifecycle {
|
||||
buckets: string[];
|
||||
/** ILM Rule type (Expiry or transition) */
|
||||
type: "expiry" | "transition";
|
||||
/** Non required field, it matches a prefix to perform ILM operations on it */
|
||||
prefix?: string;
|
||||
/** Non required field, tags to match ILM files */
|
||||
tags?: string;
|
||||
/**
|
||||
* Required in case of expiry_date or transition fields are not set. it defines an expiry days for ILM
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
expiry_days?: number;
|
||||
/**
|
||||
* Required in case of transition_date or expiry fields are not set. it defines a transition days for ILM
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
transition_days?: number;
|
||||
/** Required only in case of transition is set. it refers to a tier */
|
||||
storage_class?: string;
|
||||
/** Non required, toggle to disable or enable rule */
|
||||
expired_object_delete_marker?: boolean;
|
||||
/** Non required, toggle to disable or enable rule */
|
||||
expired_object_delete_all?: boolean;
|
||||
/**
|
||||
* Non required, can be set in case of expiration is enabled
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
noncurrentversion_expiration_days?: number;
|
||||
/**
|
||||
* Non required, can be set in case of transition is enabled
|
||||
* @format int32
|
||||
* @default 0
|
||||
*/
|
||||
noncurrentversion_transition_days?: number;
|
||||
/** Non required, can be set in case of transition is enabled */
|
||||
noncurrentversion_transition_storage_class?: string;
|
||||
}
|
||||
|
||||
export interface MulticycleResultItem {
|
||||
bucketName?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface MultiLifecycleResult {
|
||||
results?: MulticycleResultItem[];
|
||||
}
|
||||
|
||||
export interface PrefixAccessPair {
|
||||
prefix?: string;
|
||||
access?: string;
|
||||
@@ -1116,59 +933,6 @@ export interface PolicyArgs {
|
||||
bucket_name?: string;
|
||||
}
|
||||
|
||||
export interface TierS3 {
|
||||
name?: string;
|
||||
endpoint?: string;
|
||||
accesskey?: string;
|
||||
secretkey?: string;
|
||||
bucket?: string;
|
||||
prefix?: string;
|
||||
region?: string;
|
||||
storageclass?: string;
|
||||
usage?: string;
|
||||
objects?: string;
|
||||
versions?: string;
|
||||
}
|
||||
|
||||
export interface TierMinio {
|
||||
name?: string;
|
||||
endpoint?: string;
|
||||
accesskey?: string;
|
||||
secretkey?: string;
|
||||
bucket?: string;
|
||||
prefix?: string;
|
||||
region?: string;
|
||||
storageclass?: string;
|
||||
usage?: string;
|
||||
objects?: string;
|
||||
versions?: string;
|
||||
}
|
||||
|
||||
export interface TierAzure {
|
||||
name?: string;
|
||||
endpoint?: string;
|
||||
accountname?: string;
|
||||
accountkey?: string;
|
||||
bucket?: string;
|
||||
prefix?: string;
|
||||
region?: string;
|
||||
usage?: string;
|
||||
objects?: string;
|
||||
versions?: string;
|
||||
}
|
||||
|
||||
export interface TierGcs {
|
||||
name?: string;
|
||||
endpoint?: string;
|
||||
creds?: string;
|
||||
bucket?: string;
|
||||
prefix?: string;
|
||||
region?: string;
|
||||
usage?: string;
|
||||
objects?: string;
|
||||
versions?: string;
|
||||
}
|
||||
|
||||
export interface DeleteFile {
|
||||
path?: string;
|
||||
versionID?: string;
|
||||
@@ -1181,30 +945,6 @@ export interface UserSAs {
|
||||
recursive?: boolean;
|
||||
}
|
||||
|
||||
export interface Tier {
|
||||
status?: boolean;
|
||||
type?: "s3" | "gcs" | "azure" | "minio" | "unsupported";
|
||||
s3?: TierS3;
|
||||
gcs?: TierGcs;
|
||||
azure?: TierAzure;
|
||||
minio?: TierMinio;
|
||||
}
|
||||
|
||||
export interface TierListResponse {
|
||||
items?: Tier[];
|
||||
}
|
||||
|
||||
export interface TiersNameListResponse {
|
||||
items?: string[];
|
||||
}
|
||||
|
||||
export interface TierCredentialsRequest {
|
||||
access_key?: string;
|
||||
secret_key?: string;
|
||||
/** a base64 encoded value */
|
||||
creds?: string;
|
||||
}
|
||||
|
||||
export interface RewindItem {
|
||||
last_modified?: string;
|
||||
/** @format int64 */
|
||||
@@ -2729,115 +2469,6 @@ export class Api<
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Bucket
|
||||
* @name GetBucketLifecycle
|
||||
* @summary Bucket Lifecycle
|
||||
* @request GET:/buckets/{bucket_name}/lifecycle
|
||||
* @secure
|
||||
*/
|
||||
getBucketLifecycle: (bucketName: string, params: RequestParams = {}) =>
|
||||
this.request<BucketLifecycleResponse, ApiError>({
|
||||
path: `/buckets/${encodeURIComponent(bucketName)}/lifecycle`,
|
||||
method: "GET",
|
||||
secure: true,
|
||||
format: "json",
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Bucket
|
||||
* @name AddBucketLifecycle
|
||||
* @summary Add Bucket Lifecycle
|
||||
* @request POST:/buckets/{bucket_name}/lifecycle
|
||||
* @secure
|
||||
*/
|
||||
addBucketLifecycle: (
|
||||
bucketName: string,
|
||||
body: AddBucketLifecycle,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<void, ApiError>({
|
||||
path: `/buckets/${encodeURIComponent(bucketName)}/lifecycle`,
|
||||
method: "POST",
|
||||
body: body,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Bucket
|
||||
* @name AddMultiBucketLifecycle
|
||||
* @summary Add Multi Bucket Lifecycle
|
||||
* @request POST:/buckets/multi-lifecycle
|
||||
* @secure
|
||||
*/
|
||||
addMultiBucketLifecycle: (
|
||||
body: AddMultiBucketLifecycle,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<MultiLifecycleResult, ApiError>({
|
||||
path: `/buckets/multi-lifecycle`,
|
||||
method: "POST",
|
||||
body: body,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Bucket
|
||||
* @name UpdateBucketLifecycle
|
||||
* @summary Update Lifecycle rule
|
||||
* @request PUT:/buckets/{bucket_name}/lifecycle/{lifecycle_id}
|
||||
* @secure
|
||||
*/
|
||||
updateBucketLifecycle: (
|
||||
bucketName: string,
|
||||
lifecycleId: string,
|
||||
body: UpdateBucketLifecycle,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<void, ApiError>({
|
||||
path: `/buckets/${encodeURIComponent(bucketName)}/lifecycle/${encodeURIComponent(lifecycleId)}`,
|
||||
method: "PUT",
|
||||
body: body,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Bucket
|
||||
* @name DeleteBucketLifecycleRule
|
||||
* @summary Delete Lifecycle rule
|
||||
* @request DELETE:/buckets/{bucket_name}/lifecycle/{lifecycle_id}
|
||||
* @secure
|
||||
*/
|
||||
deleteBucketLifecycleRule: (
|
||||
bucketName: string,
|
||||
lifecycleId: string,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<void, ApiError>({
|
||||
path: `/buckets/${encodeURIComponent(bucketName)}/lifecycle/${encodeURIComponent(lifecycleId)}`,
|
||||
method: "DELETE",
|
||||
secure: true,
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
@@ -4073,124 +3704,6 @@ export class Api<
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Tiering
|
||||
* @name TiersList
|
||||
* @summary Returns a list of tiers for ilm
|
||||
* @request GET:/admin/tiers
|
||||
* @secure
|
||||
*/
|
||||
tiersList: (params: RequestParams = {}) =>
|
||||
this.request<TierListResponse, ApiError>({
|
||||
path: `/admin/tiers`,
|
||||
method: "GET",
|
||||
secure: true,
|
||||
format: "json",
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Tiering
|
||||
* @name AddTier
|
||||
* @summary Allows to configure a new tier
|
||||
* @request POST:/admin/tiers
|
||||
* @secure
|
||||
*/
|
||||
addTier: (body: Tier, params: RequestParams = {}) =>
|
||||
this.request<void, ApiError>({
|
||||
path: `/admin/tiers`,
|
||||
method: "POST",
|
||||
body: body,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Tiering
|
||||
* @name TiersListNames
|
||||
* @summary Returns a list of tiers' names for ilm
|
||||
* @request GET:/admin/tiers/names
|
||||
* @secure
|
||||
*/
|
||||
tiersListNames: (params: RequestParams = {}) =>
|
||||
this.request<TiersNameListResponse, ApiError>({
|
||||
path: `/admin/tiers/names`,
|
||||
method: "GET",
|
||||
secure: true,
|
||||
format: "json",
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Tiering
|
||||
* @name GetTier
|
||||
* @summary Get Tier
|
||||
* @request GET:/admin/tiers/{type}/{name}
|
||||
* @secure
|
||||
*/
|
||||
getTier: (
|
||||
type: "s3" | "gcs" | "azure" | "minio",
|
||||
name: string,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<Tier, ApiError>({
|
||||
path: `/admin/tiers/${encodeURIComponent(type)}/${encodeURIComponent(name)}`,
|
||||
method: "GET",
|
||||
secure: true,
|
||||
format: "json",
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Tiering
|
||||
* @name EditTierCredentials
|
||||
* @summary Edit Tier Credentials
|
||||
* @request PUT:/admin/tiers/{type}/{name}/credentials
|
||||
* @secure
|
||||
*/
|
||||
editTierCredentials: (
|
||||
type: "s3" | "gcs" | "azure" | "minio",
|
||||
name: string,
|
||||
body: TierCredentialsRequest,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<void, ApiError>({
|
||||
path: `/admin/tiers/${encodeURIComponent(type)}/${encodeURIComponent(name)}/credentials`,
|
||||
method: "PUT",
|
||||
body: body,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
* @tags Tiering
|
||||
* @name RemoveTier
|
||||
* @summary Remove Tier
|
||||
* @request DELETE:/admin/tiers/{name}/remove
|
||||
* @secure
|
||||
*/
|
||||
removeTier: (name: string, params: RequestParams = {}) =>
|
||||
this.request<void, ApiError>({
|
||||
path: `/admin/tiers/${encodeURIComponent(name)}/remove`,
|
||||
method: "DELETE",
|
||||
secure: true,
|
||||
...params,
|
||||
}),
|
||||
|
||||
/**
|
||||
* No description
|
||||
*
|
||||
|
||||
@@ -18,7 +18,6 @@ export const IAM_ROLES = {
|
||||
BUCKET_OWNER: "BUCKET_OWNER", // upload/delete objects from the bucket
|
||||
BUCKET_VIEWER: "BUCKET_VIEWER", // only view objects on the bucket
|
||||
BUCKET_ADMIN: "BUCKET_ADMIN", // administrate the bucket
|
||||
BUCKET_LIFECYCLE: "BUCKET_LIFECYCLE", // can manage bucket lifecycle
|
||||
};
|
||||
|
||||
export const IAM_SCOPES = {
|
||||
@@ -51,8 +50,6 @@ export const IAM_SCOPES = {
|
||||
S3_PUT_BUCKET_NOTIFICATIONS: "s3:PutBucketNotification",
|
||||
S3_GET_REPLICATION_CONFIGURATION: "s3:GetReplicationConfiguration",
|
||||
S3_PUT_REPLICATION_CONFIGURATION: "s3:PutReplicationConfiguration",
|
||||
S3_GET_LIFECYCLE_CONFIGURATION: "s3:GetLifecycleConfiguration",
|
||||
S3_PUT_LIFECYCLE_CONFIGURATION: "s3:PutLifecycleConfiguration",
|
||||
S3_GET_BUCKET_OBJECT_LOCK_CONFIGURATION:
|
||||
"s3:GetBucketObjectLockConfiguration",
|
||||
S3_PUT_BUCKET_OBJECT_LOCK_CONFIGURATION:
|
||||
@@ -68,8 +65,6 @@ export const IAM_SCOPES = {
|
||||
ADMIN_SERVER_INFO: "admin:ServerInfo",
|
||||
ADMIN_GET_BUCKET_QUOTA: "admin:GetBucketQuota",
|
||||
ADMIN_SET_BUCKET_QUOTA: "admin:SetBucketQuota",
|
||||
ADMIN_LIST_TIERS: "admin:ListTier",
|
||||
ADMIN_SET_TIER: "admin:SetTier",
|
||||
ADMIN_LIST_GROUPS: "admin:ListGroups",
|
||||
S3_GET_OBJECT_VERSION_FOR_REPLICATION: "s3:GetObjectVersionForReplication",
|
||||
S3_REPLICATE_TAGS: "s3:ReplicateTags",
|
||||
@@ -194,9 +189,6 @@ export const IAM_PAGES = {
|
||||
EVENT_DESTINATIONS: "/settings/event-destinations",
|
||||
EVENT_DESTINATIONS_ADD: "/settings/event-destinations/add",
|
||||
EVENT_DESTINATIONS_ADD_SERVICE: "/settings/event-destinations/add/:service",
|
||||
TIERS: "/settings/tiers",
|
||||
TIERS_ADD: "/settings/tiers/add",
|
||||
TIERS_ADD_SERVICE: "/settings/tiers/add/:service",
|
||||
};
|
||||
|
||||
// roles
|
||||
@@ -242,8 +234,6 @@ export const IAM_PERMISSIONS = {
|
||||
IAM_SCOPES.S3_BYPASS_GOVERNANCE_RETENTION,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_POLICY,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_LIST_MULTIPART_UPLOAD_PARTS,
|
||||
IAM_SCOPES.S3_LISTEN_BUCKET_NOTIFICATIONS,
|
||||
IAM_SCOPES.S3_LISTEN_NOTIFICATIONS,
|
||||
@@ -267,14 +257,6 @@ export const IAM_PERMISSIONS = {
|
||||
IAM_SCOPES.S3_GET_ACTIONS,
|
||||
IAM_SCOPES.S3_PUT_ACTIONS,
|
||||
],
|
||||
[IAM_ROLES.BUCKET_LIFECYCLE]: [
|
||||
IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_GET_ACTIONS,
|
||||
IAM_SCOPES.S3_PUT_ACTIONS,
|
||||
IAM_SCOPES.ADMIN_LIST_TIERS,
|
||||
IAM_SCOPES.ADMIN_SET_TIER,
|
||||
],
|
||||
};
|
||||
|
||||
// application pages/routes and required scopes/roles
|
||||
@@ -366,17 +348,6 @@ export const IAM_PAGES_PERMISSIONS = {
|
||||
IAM_SCOPES.ADMIN_SERVER_INFO, // displays notifications endpoints
|
||||
IAM_SCOPES.ADMIN_CONFIG_UPDATE, // displays create notification button
|
||||
],
|
||||
[IAM_PAGES.TIERS]: [
|
||||
IAM_SCOPES.ADMIN_LIST_TIERS, // display tiers list
|
||||
],
|
||||
[IAM_PAGES.TIERS_ADD]: [
|
||||
IAM_SCOPES.ADMIN_SET_TIER, // display "add tier" button / shows add service tier page
|
||||
IAM_SCOPES.ADMIN_LIST_TIERS, // display tiers list
|
||||
],
|
||||
[IAM_PAGES.TIERS_ADD_SERVICE]: [
|
||||
IAM_SCOPES.ADMIN_SET_TIER, // display "add tier" button / shows add service tier page
|
||||
IAM_SCOPES.ADMIN_LIST_TIERS, // display tiers list
|
||||
],
|
||||
[IAM_PAGES.TOOLS_LOGS]: [IAM_SCOPES.ADMIN_GET_CONSOLE_LOG],
|
||||
[IAM_PAGES.TOOLS_AUDITLOGS]: [IAM_SCOPES.ADMIN_HEALTH_INFO],
|
||||
[IAM_PAGES.TOOLS_WATCH]: [
|
||||
|
||||
@@ -1,493 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
|
||||
import get from "lodash/get";
|
||||
import {
|
||||
Accordion,
|
||||
AlertIcon,
|
||||
Button,
|
||||
FormLayout,
|
||||
Grid,
|
||||
HelpTip,
|
||||
InputBox,
|
||||
LifecycleConfigIcon,
|
||||
ProgressBar,
|
||||
RadioGroup,
|
||||
Select,
|
||||
Switch,
|
||||
} from "mds";
|
||||
import { useSelector } from "react-redux";
|
||||
import { api } from "api";
|
||||
import { BucketVersioningResponse } from "api/consoleApi";
|
||||
import { errorToHandler } from "api/errors";
|
||||
import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { selDistSet, setModalErrorSnackMessage } from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import { ITiersDropDown } from "../types";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";
|
||||
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
|
||||
import { IAM_PAGES } from "common/SecureComponent/permissions";
|
||||
|
||||
interface IReplicationModal {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: (refresh: boolean) => any;
|
||||
bucketName: string;
|
||||
}
|
||||
|
||||
const AddLifecycleModal = ({
|
||||
open,
|
||||
closeModalAndRefresh,
|
||||
bucketName,
|
||||
}: IReplicationModal) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const distributedSetup = useSelector(selDistSet);
|
||||
const [loadingTiers, setLoadingTiers] = useState<boolean>(true);
|
||||
const [tiersList, setTiersList] = useState<ITiersDropDown[]>([]);
|
||||
const [addLoading, setAddLoading] = useState(false);
|
||||
const [versioningInfo, setVersioningInfo] =
|
||||
useState<BucketVersioningResponse | null>(null);
|
||||
const [prefix, setPrefix] = useState("");
|
||||
const [tags, setTags] = useState<string>("");
|
||||
const [storageClass, setStorageClass] = useState("");
|
||||
|
||||
const [ilmType, setIlmType] = useState<"expiry" | "transition">("expiry");
|
||||
const [targetVersion, setTargetVersion] = useState<"current" | "noncurrent">(
|
||||
"current",
|
||||
);
|
||||
const [lifecycleDays, setLifecycleDays] = useState<string>("");
|
||||
const [isFormValid, setIsFormValid] = useState<boolean>(false);
|
||||
const [expiredObjectDM, setExpiredObjectDM] = useState<boolean>(false);
|
||||
const [expiredAllVersionsDM, setExpiredAllVersionsDM] =
|
||||
useState<boolean>(false);
|
||||
const [loadingVersioning, setLoadingVersioning] = useState<boolean>(true);
|
||||
const [expandedAdv, setExpandedAdv] = useState<boolean>(false);
|
||||
const [expanded, setExpanded] = useState<boolean>(false);
|
||||
const [expiryUnit, setExpiryUnit] = useState<string>("days");
|
||||
|
||||
/*To be removed on component replacement*/
|
||||
const formFieldRowFilter = {
|
||||
"& .MuiPaper-root": { padding: 0 },
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingTiers) {
|
||||
api.admin
|
||||
.tiersListNames()
|
||||
.then((res) => {
|
||||
const tiersList: string[] | null = get(res.data, "items", []);
|
||||
|
||||
if (tiersList !== null && tiersList.length >= 1) {
|
||||
const objList = tiersList.map((tierName: string) => {
|
||||
return { label: tierName, value: tierName };
|
||||
});
|
||||
|
||||
setTiersList(objList);
|
||||
if (objList.length > 0) {
|
||||
setStorageClass(objList[0].value);
|
||||
}
|
||||
}
|
||||
setLoadingTiers(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoadingTiers(false);
|
||||
dispatch(setModalErrorSnackMessage(errorToHandler(err.error)));
|
||||
});
|
||||
}
|
||||
}, [dispatch, loadingTiers]);
|
||||
|
||||
useEffect(() => {
|
||||
let valid = true;
|
||||
|
||||
if (ilmType !== "expiry") {
|
||||
if (storageClass === "") {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (!lifecycleDays || parseInt(lifecycleDays) === 0) {
|
||||
valid = false;
|
||||
}
|
||||
if (parseInt(lifecycleDays) > 2147483647) {
|
||||
//values over int32 cannot be parsed
|
||||
valid = false;
|
||||
}
|
||||
setIsFormValid(valid);
|
||||
}, [ilmType, lifecycleDays, storageClass]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingVersioning && distributedSetup) {
|
||||
api.buckets
|
||||
.getBucketVersioning(bucketName)
|
||||
.then((res) => {
|
||||
setVersioningInfo(res.data);
|
||||
setLoadingVersioning(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
dispatch(setModalErrorSnackMessage(errorToHandler(err)));
|
||||
setLoadingVersioning(false);
|
||||
});
|
||||
}
|
||||
}, [loadingVersioning, dispatch, bucketName, distributedSetup]);
|
||||
|
||||
const addRecord = () => {
|
||||
let rules = {};
|
||||
|
||||
if (ilmType === "expiry") {
|
||||
let expiry: { [key: string]: number } = {};
|
||||
|
||||
if (targetVersion === "current") {
|
||||
expiry["expiry_days"] = parseInt(lifecycleDays);
|
||||
} else if (expiryUnit === "days") {
|
||||
expiry["noncurrentversion_expiration_days"] = parseInt(lifecycleDays);
|
||||
} else {
|
||||
expiry["newer_noncurrentversion_expiration_versions"] =
|
||||
parseInt(lifecycleDays);
|
||||
}
|
||||
|
||||
rules = {
|
||||
...expiry,
|
||||
};
|
||||
} else {
|
||||
let transition: { [key: string]: number | string } = {};
|
||||
if (targetVersion === "current") {
|
||||
transition["transition_days"] = parseInt(lifecycleDays);
|
||||
transition["storage_class"] = storageClass;
|
||||
} else if (expiryUnit === "days") {
|
||||
transition["noncurrentversion_transition_days"] =
|
||||
parseInt(lifecycleDays);
|
||||
transition["noncurrentversion_transition_storage_class"] = storageClass;
|
||||
}
|
||||
|
||||
rules = {
|
||||
...transition,
|
||||
};
|
||||
}
|
||||
|
||||
const lifecycleInsert = {
|
||||
type: ilmType,
|
||||
prefix,
|
||||
tags,
|
||||
expired_object_delete_marker: expiredObjectDM,
|
||||
expired_object_delete_all: expiredAllVersionsDM,
|
||||
...rules,
|
||||
};
|
||||
|
||||
api.buckets
|
||||
.addBucketLifecycle(bucketName, lifecycleInsert)
|
||||
.then(() => {
|
||||
setAddLoading(false);
|
||||
closeModalAndRefresh(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddLoading(false);
|
||||
dispatch(setModalErrorSnackMessage(errorToHandler(err)));
|
||||
});
|
||||
};
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
closeModalAndRefresh(false);
|
||||
}}
|
||||
title="Add Lifecycle Rule"
|
||||
titleIcon={<LifecycleConfigIcon />}
|
||||
>
|
||||
{loadingTiers && (
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<ProgressBar />
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{!loadingTiers && (
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setAddLoading(true);
|
||||
addRecord();
|
||||
}}
|
||||
>
|
||||
<FormLayout withBorders={false} containerPadding={false}>
|
||||
<RadioGroup
|
||||
currentValue={ilmType}
|
||||
id="ilm_type"
|
||||
name="ilm_type"
|
||||
label="Type of Lifecycle"
|
||||
onChange={(e) => {
|
||||
setIlmType(e.target.value as "expiry" | "transition");
|
||||
}}
|
||||
selectorOptions={[
|
||||
{ value: "expiry", label: "Expiry" },
|
||||
{ value: "transition", label: "Transition" },
|
||||
]}
|
||||
helpTip={
|
||||
<Fragment>
|
||||
Select{" "}
|
||||
<a
|
||||
target="blank"
|
||||
href="https://min.io/docs/minio/kubernetes/upstream/administration/object-management/create-lifecycle-management-expiration-rule.html"
|
||||
>
|
||||
Expiry
|
||||
</a>{" "}
|
||||
to delete Objects per this rule. Select{" "}
|
||||
<a
|
||||
target="blank"
|
||||
href="https://min.io/docs/minio/kubernetes/upstream/administration/object-management/transition-objects-to-minio.html"
|
||||
>
|
||||
Transition
|
||||
</a>{" "}
|
||||
to move Objects to a remote storage{" "}
|
||||
<a
|
||||
target="blank"
|
||||
href="https://min.io/docs/minio/windows/administration/object-management/transition-objects-to-minio.html#configure-the-remote-storage-tier"
|
||||
>
|
||||
Tier
|
||||
</a>{" "}
|
||||
per this rule.
|
||||
</Fragment>
|
||||
}
|
||||
helpTipPlacement="right"
|
||||
/>
|
||||
{versioningInfo?.status === "Enabled" && (
|
||||
<Select
|
||||
value={targetVersion}
|
||||
id="object_version"
|
||||
name="object_version"
|
||||
label="Object Version"
|
||||
onChange={(value) => {
|
||||
setTargetVersion(value as "current" | "noncurrent");
|
||||
}}
|
||||
options={[
|
||||
{ value: "current", label: "Current Version" },
|
||||
{ value: "noncurrent", label: "Non-Current Version" },
|
||||
]}
|
||||
helpTip={
|
||||
<Fragment>
|
||||
Select whether to apply the rule to current or non-current
|
||||
Object
|
||||
<a
|
||||
target="blank"
|
||||
href="https://min.io/docs/minio/kubernetes/upstream/administration/object-management/create-lifecycle-management-expiration-rule.html#expire-versioned-objects"
|
||||
>
|
||||
{" "}
|
||||
Versions
|
||||
</a>
|
||||
</Fragment>
|
||||
}
|
||||
helpTipPlacement="right"
|
||||
/>
|
||||
)}
|
||||
|
||||
<InputBox
|
||||
error={
|
||||
lifecycleDays && !isFormValid
|
||||
? parseInt(lifecycleDays) <= 0
|
||||
? `Number of ${expiryUnit} to retain must be greater than zero`
|
||||
: parseInt(lifecycleDays) > 2147483647
|
||||
? `Number of ${expiryUnit} must be less than or equal to 2147483647`
|
||||
: ""
|
||||
: ""
|
||||
}
|
||||
id="expiry_days"
|
||||
name="expiry_days"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.validity.valid) {
|
||||
setLifecycleDays(e.target.value);
|
||||
}
|
||||
}}
|
||||
pattern={"[0-9]*"}
|
||||
label="After"
|
||||
value={lifecycleDays}
|
||||
overlayObject={
|
||||
<Fragment>
|
||||
<Grid container sx={{ justifyContent: "center" }}>
|
||||
<InputUnitMenu
|
||||
id={"expire-current-unit"}
|
||||
unitSelected={expiryUnit}
|
||||
unitsList={[
|
||||
{ label: "Days", value: "days" },
|
||||
{ label: "Versions", value: "versions" },
|
||||
]}
|
||||
disabled={
|
||||
targetVersion !== "noncurrent" || ilmType !== "expiry"
|
||||
}
|
||||
onUnitChange={(newValue) => {
|
||||
setExpiryUnit(newValue);
|
||||
}}
|
||||
/>
|
||||
{ilmType === "expiry" && targetVersion === "noncurrent" && (
|
||||
<HelpTip
|
||||
content={
|
||||
<Fragment>
|
||||
Select to set expiry by days or newer noncurrent
|
||||
versions
|
||||
</Fragment>
|
||||
}
|
||||
placement="right"
|
||||
>
|
||||
{" "}
|
||||
<AlertIcon style={{ width: 15, height: 15 }} />
|
||||
</HelpTip>
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
|
||||
{ilmType === "expiry" ? (
|
||||
<Fragment />
|
||||
) : (
|
||||
<Select
|
||||
label="To Tier"
|
||||
id="storage_class"
|
||||
name="storage_class"
|
||||
value={storageClass}
|
||||
onChange={(value) => {
|
||||
setStorageClass(value as string);
|
||||
}}
|
||||
options={tiersList}
|
||||
helpTip={
|
||||
<Fragment>
|
||||
Configure a{" "}
|
||||
<a
|
||||
href={IAM_PAGES.TIERS_ADD}
|
||||
color="secondary"
|
||||
style={{ textDecoration: "underline" }}
|
||||
>
|
||||
remote tier
|
||||
</a>{" "}
|
||||
to receive transitioned Objects
|
||||
</Fragment>
|
||||
}
|
||||
helpTipPlacement="right"
|
||||
/>
|
||||
)}
|
||||
<Grid item xs={12} sx={formFieldRowFilter}>
|
||||
<Accordion
|
||||
title={"Filters"}
|
||||
id={"lifecycle-filters"}
|
||||
expanded={expanded}
|
||||
onTitleClick={() => setExpanded(!expanded)}
|
||||
>
|
||||
<Grid item xs={12}>
|
||||
<InputBox
|
||||
id="prefix"
|
||||
name="prefix"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPrefix(e.target.value);
|
||||
}}
|
||||
label="Prefix"
|
||||
value={prefix}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<QueryMultiSelector
|
||||
name="tags"
|
||||
label="Tags"
|
||||
elements={""}
|
||||
onChange={(vl: string) => {
|
||||
setTags(vl);
|
||||
}}
|
||||
keyPlaceholder="Tag Key"
|
||||
valuePlaceholder="Tag Value"
|
||||
withBorder
|
||||
/>
|
||||
</Grid>
|
||||
</Accordion>
|
||||
</Grid>
|
||||
{ilmType === "expiry" && targetVersion === "noncurrent" && (
|
||||
<Grid item xs={12} sx={formFieldRowFilter}>
|
||||
<Accordion
|
||||
title={"Advanced"}
|
||||
id={"lifecycle-advanced-filters"}
|
||||
expanded={expandedAdv}
|
||||
onTitleClick={() => setExpandedAdv(!expandedAdv)}
|
||||
sx={{ marginTop: 15 }}
|
||||
>
|
||||
<Grid item xs={12}>
|
||||
<Switch
|
||||
value="expired_delete_marker"
|
||||
id="expired_delete_marker"
|
||||
name="expired_delete_marker"
|
||||
checked={expiredObjectDM}
|
||||
onChange={(
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setExpiredObjectDM(event.target.checked);
|
||||
}}
|
||||
label={"Expire Delete Marker"}
|
||||
description={
|
||||
"Remove the reference to the object if no versions are left"
|
||||
}
|
||||
/>
|
||||
<Switch
|
||||
value="expired_delete_all"
|
||||
id="expired_delete_all"
|
||||
name="expired_delete_all"
|
||||
checked={expiredAllVersionsDM}
|
||||
onChange={(
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setExpiredAllVersionsDM(event.target.checked);
|
||||
}}
|
||||
label={"Expire All Versions"}
|
||||
description={
|
||||
"Removes all the versions of the object already expired"
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Accordion>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
<Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
|
||||
<Button
|
||||
id={"reset"}
|
||||
type="button"
|
||||
variant="regular"
|
||||
disabled={addLoading}
|
||||
onClick={() => {
|
||||
closeModalAndRefresh(false);
|
||||
}}
|
||||
label={"Cancel"}
|
||||
/>
|
||||
<Button
|
||||
id={"save-lifecycle"}
|
||||
type="submit"
|
||||
variant="callAction"
|
||||
color="primary"
|
||||
disabled={addLoading || !isFormValid}
|
||||
label={"Save"}
|
||||
/>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<ProgressBar />
|
||||
</Grid>
|
||||
)}
|
||||
</FormLayout>
|
||||
</form>
|
||||
)}
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddLifecycleModal;
|
||||
@@ -88,9 +88,6 @@ const BucketEventsPanel = withSuspense(
|
||||
const BucketReplicationPanel = withSuspense(
|
||||
React.lazy(() => import("./BucketReplicationPanel")),
|
||||
);
|
||||
const BucketLifecyclePanel = withSuspense(
|
||||
React.lazy(() => import("./BucketLifecyclePanel")),
|
||||
);
|
||||
|
||||
const BucketDetails = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
@@ -143,7 +140,6 @@ const BucketDetails = () => {
|
||||
const manageBucketRoutes: Record<string, any> = {
|
||||
events: "/admin/events",
|
||||
replication: "/admin/replication",
|
||||
lifecycle: "/admin/lifecycle",
|
||||
access: "/admin/access",
|
||||
prefix: "/admin/prefix",
|
||||
};
|
||||
@@ -330,21 +326,6 @@ const BucketDetails = () => {
|
||||
to: getRoutePath("replication"),
|
||||
},
|
||||
},
|
||||
{
|
||||
tabConfig: {
|
||||
label: "Lifecycle",
|
||||
id: "lifecycle",
|
||||
disabled:
|
||||
!distributedSetup ||
|
||||
!hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_GET_ACTIONS,
|
||||
IAM_SCOPES.S3_PUT_ACTIONS,
|
||||
]),
|
||||
to: getRoutePath("lifecycle"),
|
||||
},
|
||||
},
|
||||
{
|
||||
tabConfig: {
|
||||
label: "Access",
|
||||
@@ -379,10 +360,6 @@ const BucketDetails = () => {
|
||||
element={<BucketReplicationPanel />}
|
||||
/>
|
||||
)}
|
||||
{distributedSetup && (
|
||||
<Route path="lifecycle" element={<BucketLifecyclePanel />} />
|
||||
)}
|
||||
|
||||
<Route path="access" element={<AccessDetailsPanel />} />
|
||||
<Route path="prefix" element={<AccessRulePanel />} />
|
||||
<Route
|
||||
|
||||
@@ -1,396 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import {
|
||||
AddIcon,
|
||||
Button,
|
||||
DataTable,
|
||||
Grid,
|
||||
HelpBox,
|
||||
SectionTitle,
|
||||
TiersIcon,
|
||||
HelpTip,
|
||||
} from "mds";
|
||||
import { useSelector } from "react-redux";
|
||||
import { api } from "api";
|
||||
import { ObjectBucketLifecycle } from "api/consoleApi";
|
||||
import { LifeCycleItem } from "../types";
|
||||
import {
|
||||
hasPermission,
|
||||
SecureComponent,
|
||||
} from "../../../../common/SecureComponent";
|
||||
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
|
||||
import { selBucketDetailsLoading } from "./bucketDetailsSlice";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { setHelpName } from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import DeleteBucketLifecycleRule from "./DeleteBucketLifecycleRule";
|
||||
import EditLifecycleConfiguration from "./EditLifecycleConfiguration";
|
||||
import AddLifecycleModal from "./AddLifecycleModal";
|
||||
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
|
||||
|
||||
const BucketLifecyclePanel = () => {
|
||||
const loadingBucket = useSelector(selBucketDetailsLoading);
|
||||
const params = useParams();
|
||||
|
||||
const [loadingLifecycle, setLoadingLifecycle] = useState<boolean>(true);
|
||||
const [lifecycleRecords, setLifecycleRecords] = useState<
|
||||
ObjectBucketLifecycle[]
|
||||
>([]);
|
||||
const [addLifecycleOpen, setAddLifecycleOpen] = useState<boolean>(false);
|
||||
const [editLifecycleOpen, setEditLifecycleOpen] = useState<boolean>(false);
|
||||
const [selectedLifecycleRule, setSelectedLifecycleRule] =
|
||||
useState<LifeCycleItem | null>(null);
|
||||
const [deleteLifecycleOpen, setDeleteLifecycleOpen] =
|
||||
useState<boolean>(false);
|
||||
const [selectedID, setSelectedID] = useState<string | null>(null);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const bucketName = params.bucketName || "";
|
||||
|
||||
const displayLifeCycleRules = hasPermission(bucketName, [
|
||||
IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_GET_ACTIONS,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingBucket) {
|
||||
setLoadingLifecycle(true);
|
||||
}
|
||||
}, [loadingBucket, setLoadingLifecycle]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setHelpName("bucket_detail_lifecycle"));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingLifecycle) {
|
||||
if (displayLifeCycleRules) {
|
||||
api.buckets
|
||||
.getBucketLifecycle(bucketName)
|
||||
.then((res) => {
|
||||
const records = get(res.data, "lifecycle", []);
|
||||
setLifecycleRecords(records || []);
|
||||
setLoadingLifecycle(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err.error);
|
||||
setLifecycleRecords([]);
|
||||
setLoadingLifecycle(false);
|
||||
});
|
||||
} else {
|
||||
setLoadingLifecycle(false);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
loadingLifecycle,
|
||||
setLoadingLifecycle,
|
||||
bucketName,
|
||||
displayLifeCycleRules,
|
||||
]);
|
||||
|
||||
const closeEditLCAndRefresh = (refresh: boolean) => {
|
||||
setEditLifecycleOpen(false);
|
||||
setSelectedLifecycleRule(null);
|
||||
if (refresh) {
|
||||
setLoadingLifecycle(true);
|
||||
}
|
||||
};
|
||||
|
||||
const closeAddLCAndRefresh = (refresh: boolean) => {
|
||||
setAddLifecycleOpen(false);
|
||||
if (refresh) {
|
||||
setLoadingLifecycle(true);
|
||||
}
|
||||
};
|
||||
|
||||
const closeDelLCRefresh = (refresh: boolean) => {
|
||||
setDeleteLifecycleOpen(false);
|
||||
setSelectedID(null);
|
||||
|
||||
if (refresh) {
|
||||
setLoadingLifecycle(true);
|
||||
}
|
||||
};
|
||||
|
||||
const renderStorageClass = (objectST: any) => {
|
||||
let stClass = get(objectST, "transition.storage_class", "");
|
||||
stClass = get(objectST, "transition.noncurrent_storage_class", stClass);
|
||||
|
||||
return stClass;
|
||||
};
|
||||
|
||||
const lifecycleColumns = [
|
||||
{
|
||||
label: "Type",
|
||||
renderFullObject: true,
|
||||
renderFunction: (el: LifeCycleItem) => {
|
||||
if (!el) {
|
||||
return <Fragment />;
|
||||
}
|
||||
if (
|
||||
el.expiration &&
|
||||
(el.expiration.days > 0 ||
|
||||
el.expiration.noncurrent_expiration_days ||
|
||||
(el.expiration.newer_noncurrent_expiration_versions &&
|
||||
el.expiration.newer_noncurrent_expiration_versions > 0))
|
||||
) {
|
||||
return <span>Expiry</span>;
|
||||
}
|
||||
if (
|
||||
el.transition &&
|
||||
(el.transition.days > 0 || el.transition.noncurrent_transition_days)
|
||||
) {
|
||||
return <span>Transition</span>;
|
||||
}
|
||||
return <Fragment />;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Version",
|
||||
renderFullObject: true,
|
||||
renderFunction: (el: LifeCycleItem) => {
|
||||
if (!el) {
|
||||
return <Fragment />;
|
||||
}
|
||||
if (el.expiration) {
|
||||
if (el.expiration.days > 0) {
|
||||
return <span>Current</span>;
|
||||
} else if (
|
||||
el.expiration.noncurrent_expiration_days ||
|
||||
el.expiration.newer_noncurrent_expiration_versions
|
||||
) {
|
||||
return <span>Non-Current</span>;
|
||||
}
|
||||
}
|
||||
if (el.transition) {
|
||||
if (el.transition.days > 0) {
|
||||
return <span>Current</span>;
|
||||
} else if (el.transition.noncurrent_transition_days) {
|
||||
return <span>Non-Current</span>;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Expire Delete Marker",
|
||||
elementKey: "expire_delete_marker",
|
||||
renderFunction: (el: LifeCycleItem) => {
|
||||
if (!el) {
|
||||
return <Fragment />;
|
||||
}
|
||||
if (el.expiration && el.expiration.delete_marker !== undefined) {
|
||||
return <span>{el.expiration.delete_marker ? "true" : "false"}</span>;
|
||||
} else {
|
||||
return <Fragment />;
|
||||
}
|
||||
},
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Tier",
|
||||
elementKey: "storage_class",
|
||||
renderFunction: renderStorageClass,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Prefix",
|
||||
elementKey: "prefix",
|
||||
},
|
||||
{
|
||||
label: "After",
|
||||
renderFullObject: true,
|
||||
renderFunction: (el: LifeCycleItem) => {
|
||||
if (!el) {
|
||||
return <Fragment />;
|
||||
}
|
||||
if (el.transition) {
|
||||
if (el.transition.days > 0) {
|
||||
return <span>{el.transition.days} days</span>;
|
||||
} else if (el.transition.noncurrent_transition_days) {
|
||||
return <span>{el.transition.noncurrent_transition_days} days</span>;
|
||||
}
|
||||
}
|
||||
if (el.expiration) {
|
||||
if (el.expiration.days > 0) {
|
||||
return <span>{el.expiration.days} days</span>;
|
||||
} else if (el.expiration.noncurrent_expiration_days) {
|
||||
return <span>{el.expiration.noncurrent_expiration_days} days</span>;
|
||||
} else {
|
||||
return (
|
||||
<span>
|
||||
{el.expiration.newer_noncurrent_expiration_versions} versions
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "status",
|
||||
},
|
||||
];
|
||||
|
||||
const lifecycleActions = [
|
||||
{
|
||||
type: "view",
|
||||
|
||||
onClick(valueToSend: any): any {
|
||||
setSelectedLifecycleRule(valueToSend);
|
||||
setEditLifecycleOpen(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "delete",
|
||||
onClick(valueToDelete: string): any {
|
||||
setSelectedID(valueToDelete);
|
||||
setDeleteLifecycleOpen(true);
|
||||
},
|
||||
sendOnlyId: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{editLifecycleOpen && selectedLifecycleRule && (
|
||||
<EditLifecycleConfiguration
|
||||
open={editLifecycleOpen}
|
||||
closeModalAndRefresh={closeEditLCAndRefresh}
|
||||
selectedBucket={bucketName}
|
||||
lifecycleRule={selectedLifecycleRule}
|
||||
/>
|
||||
)}
|
||||
{addLifecycleOpen && (
|
||||
<AddLifecycleModal
|
||||
open={addLifecycleOpen}
|
||||
bucketName={bucketName}
|
||||
closeModalAndRefresh={closeAddLCAndRefresh}
|
||||
/>
|
||||
)}
|
||||
{deleteLifecycleOpen && selectedID && (
|
||||
<DeleteBucketLifecycleRule
|
||||
id={selectedID}
|
||||
bucket={bucketName}
|
||||
deleteOpen={deleteLifecycleOpen}
|
||||
onCloseAndRefresh={closeDelLCRefresh}
|
||||
/>
|
||||
)}
|
||||
<SectionTitle
|
||||
separator
|
||||
sx={{ marginBottom: 15 }}
|
||||
actions={
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_ACTIONS,
|
||||
]}
|
||||
resource={bucketName}
|
||||
matchAll
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<TooltipWrapper tooltip={"Add Lifecycle Rule"}>
|
||||
<Button
|
||||
id={"add-bucket-lifecycle-rule"}
|
||||
onClick={() => {
|
||||
setAddLifecycleOpen(true);
|
||||
}}
|
||||
label={"Add Lifecycle Rule"}
|
||||
icon={<AddIcon />}
|
||||
variant={"callAction"}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
</SecureComponent>
|
||||
}
|
||||
>
|
||||
<HelpTip
|
||||
content={
|
||||
<Fragment>
|
||||
MinIO derives it’s behavior and syntax from{" "}
|
||||
<a
|
||||
target="blank"
|
||||
href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html"
|
||||
>
|
||||
S3 lifecycle
|
||||
</a>{" "}
|
||||
for compatibility in migrating workloads and lifecycle rules from
|
||||
S3 to MinIO.
|
||||
</Fragment>
|
||||
}
|
||||
placement="right"
|
||||
>
|
||||
Lifecycle Rules
|
||||
</HelpTip>
|
||||
</SectionTitle>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<SecureComponent
|
||||
scopes={[
|
||||
IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_GET_ACTIONS,
|
||||
]}
|
||||
resource={bucketName}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<DataTable
|
||||
itemActions={lifecycleActions}
|
||||
columns={lifecycleColumns}
|
||||
isLoading={loadingLifecycle}
|
||||
records={lifecycleRecords}
|
||||
entityName="Lifecycle"
|
||||
customEmptyMessage="There are no Lifecycle rules yet"
|
||||
idField="id"
|
||||
customPaperHeight={"400px"}
|
||||
/>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
{!loadingLifecycle && (
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
<HelpBox
|
||||
title={"Lifecycle Rules"}
|
||||
iconComponent={<TiersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO Object Lifecycle Management allows creating rules for
|
||||
time or date based automatic transition or expiry of objects.
|
||||
For object transition, MinIO automatically moves the object to
|
||||
a configured remote storage tier.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://min.io/docs/minio/linux/administration/object-management/object-lifecycle-management.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default BucketLifecyclePanel;
|
||||
@@ -1,78 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
import React, { useEffect, useState, Fragment } from "react";
|
||||
import { ConfirmDeleteIcon } from "mds";
|
||||
import { api } from "api";
|
||||
import { errorToHandler } from "api/errors";
|
||||
import { setErrorSnackMessage } from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
|
||||
|
||||
interface IDeleteLifecycleRule {
|
||||
deleteOpen: boolean;
|
||||
onCloseAndRefresh: (refresh: boolean) => any;
|
||||
bucket: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
const DeleteBucketLifecycleRule = ({
|
||||
onCloseAndRefresh,
|
||||
deleteOpen,
|
||||
bucket,
|
||||
id,
|
||||
}: IDeleteLifecycleRule) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [deletingRule, setDeletingRule] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (deletingRule) {
|
||||
api.buckets
|
||||
.deleteBucketLifecycleRule(bucket, id)
|
||||
.then(() => {
|
||||
setDeletingRule(false);
|
||||
onCloseAndRefresh(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
setDeletingRule(false);
|
||||
dispatch(setErrorSnackMessage(errorToHandler(err.error)));
|
||||
});
|
||||
}
|
||||
}, [deletingRule, bucket, id, onCloseAndRefresh, dispatch]);
|
||||
|
||||
const onConfirmDelete = () => {
|
||||
setDeletingRule(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfirmDialog
|
||||
title={`Delete Lifecycle Rule`}
|
||||
confirmText={"Delete"}
|
||||
isOpen={deleteOpen}
|
||||
isLoading={deletingRule}
|
||||
onConfirm={onConfirmDelete}
|
||||
titleIcon={<ConfirmDeleteIcon />}
|
||||
onClose={() => onCloseAndRefresh(false)}
|
||||
confirmationContent={
|
||||
<Fragment>
|
||||
Are you sure you want to delete the <strong>{id}</strong> rule?
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteBucketLifecycleRule;
|
||||
@@ -1,564 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import {
|
||||
Accordion,
|
||||
Button,
|
||||
FormLayout,
|
||||
Grid,
|
||||
InputBox,
|
||||
LifecycleConfigIcon,
|
||||
Loader,
|
||||
ProgressBar,
|
||||
RadioGroup,
|
||||
Select,
|
||||
Switch,
|
||||
} from "mds";
|
||||
import { api } from "api";
|
||||
import { ApiError } from "api/consoleApi";
|
||||
import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { ITiersDropDown, LifeCycleItem } from "../types";
|
||||
import {
|
||||
setErrorSnackMessage,
|
||||
setModalErrorSnackMessage,
|
||||
} from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";
|
||||
import { errorToHandler } from "../../../../api/errors";
|
||||
|
||||
interface IAddUserContentProps {
|
||||
closeModalAndRefresh: (reload: boolean) => void;
|
||||
selectedBucket: string;
|
||||
lifecycleRule: LifeCycleItem;
|
||||
open: boolean;
|
||||
}
|
||||
|
||||
const EditLifecycleConfiguration = ({
|
||||
closeModalAndRefresh,
|
||||
selectedBucket,
|
||||
lifecycleRule,
|
||||
open,
|
||||
}: IAddUserContentProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [loadingTiers, setLoadingTiers] = useState<boolean>(true);
|
||||
const [addLoading, setAddLoading] = useState<boolean>(false);
|
||||
const [tags, setTags] = useState<string>("");
|
||||
const [enabled, setEnabled] = useState<boolean>(false);
|
||||
const [tiersList, setTiersList] = useState<ITiersDropDown[]>([]);
|
||||
const [prefix, setPrefix] = useState("");
|
||||
const [storageClass, setStorageClass] = useState("");
|
||||
const [NCTransitionSC, setNCTransitionSC] = useState("");
|
||||
const [expiredObjectDM, setExpiredObjectDM] = useState<boolean>(false);
|
||||
const [expiredAllVersionsDM, setExpiredAllVersionsDM] =
|
||||
useState<boolean>(false);
|
||||
const [NCExpirationDays, setNCExpirationDays] = useState<string>("0");
|
||||
const [NCTransitionDays, setNCTransitionDays] = useState<string>("0");
|
||||
const [ilmType, setIlmType] = useState<"transition" | "expiry">("expiry");
|
||||
const [expiryDays, setExpiryDays] = useState<string>("0");
|
||||
const [transitionDays, setTransitionDays] = useState<string>("0");
|
||||
const [isFormValid, setIsFormValid] = useState<boolean>(false);
|
||||
const [expandedAdv, setExpandedAdv] = useState<boolean>(false);
|
||||
const [expanded, setExpanded] = useState<boolean>(false);
|
||||
|
||||
const ILM_TYPES = [
|
||||
{ value: "expiry", label: "Expiry" },
|
||||
{ value: "transition", label: "Transition" },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingTiers) {
|
||||
api.admin
|
||||
.tiersListNames()
|
||||
.then((res) => {
|
||||
const tiersList: string[] | null = get(res.data, "items", []);
|
||||
|
||||
if (tiersList !== null && tiersList.length >= 1) {
|
||||
const objList = tiersList.map((tierName: string) => {
|
||||
return { label: tierName, value: tierName };
|
||||
});
|
||||
setTiersList(objList);
|
||||
if (objList.length > 0) {
|
||||
setStorageClass(lifecycleRule.transition?.storage_class || "");
|
||||
}
|
||||
}
|
||||
setLoadingTiers(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoadingTiers(false);
|
||||
dispatch(setModalErrorSnackMessage(errorToHandler(err.error)));
|
||||
});
|
||||
}
|
||||
}, [dispatch, loadingTiers, lifecycleRule.transition?.storage_class]);
|
||||
|
||||
useEffect(() => {
|
||||
let valid = true;
|
||||
|
||||
if (ilmType !== "expiry") {
|
||||
if (
|
||||
(transitionDays !== "0" && storageClass === "") ||
|
||||
(NCTransitionDays !== "0" && NCTransitionSC === "")
|
||||
) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
setIsFormValid(valid);
|
||||
}, [
|
||||
ilmType,
|
||||
expiryDays,
|
||||
transitionDays,
|
||||
storageClass,
|
||||
NCTransitionDays,
|
||||
NCTransitionSC,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (lifecycleRule.status === "Enabled") {
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
let transitionMode = false;
|
||||
|
||||
if (lifecycleRule.transition) {
|
||||
if (
|
||||
lifecycleRule.transition.days &&
|
||||
lifecycleRule.transition.days !== 0
|
||||
) {
|
||||
setTransitionDays(lifecycleRule.transition.days.toString());
|
||||
setIlmType("transition");
|
||||
transitionMode = true;
|
||||
}
|
||||
if (
|
||||
lifecycleRule.transition.noncurrent_transition_days &&
|
||||
lifecycleRule.transition.noncurrent_transition_days !== 0
|
||||
) {
|
||||
setNCTransitionDays(
|
||||
lifecycleRule.transition.noncurrent_transition_days.toString(),
|
||||
);
|
||||
setIlmType("transition");
|
||||
transitionMode = true;
|
||||
}
|
||||
|
||||
// Fallback to old rules by date
|
||||
if (
|
||||
lifecycleRule.transition.date &&
|
||||
lifecycleRule.transition.date !== "0001-01-01T00:00:00Z"
|
||||
) {
|
||||
setIlmType("transition");
|
||||
transitionMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (lifecycleRule.expiration) {
|
||||
if (
|
||||
lifecycleRule.expiration.days &&
|
||||
lifecycleRule.expiration.days !== 0
|
||||
) {
|
||||
setExpiryDays(lifecycleRule.expiration.days.toString());
|
||||
setIlmType("expiry");
|
||||
transitionMode = false;
|
||||
}
|
||||
if (
|
||||
lifecycleRule.expiration.noncurrent_expiration_days &&
|
||||
lifecycleRule.expiration.noncurrent_expiration_days !== 0
|
||||
) {
|
||||
setNCExpirationDays(
|
||||
lifecycleRule.expiration.noncurrent_expiration_days.toString(),
|
||||
);
|
||||
setIlmType("expiry");
|
||||
transitionMode = false;
|
||||
}
|
||||
|
||||
// Fallback to old rules by date
|
||||
if (
|
||||
lifecycleRule.expiration.date &&
|
||||
lifecycleRule.expiration.date !== "0001-01-01T00:00:00Z"
|
||||
) {
|
||||
setIlmType("expiry");
|
||||
transitionMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Transition fields
|
||||
if (transitionMode) {
|
||||
setStorageClass(lifecycleRule.transition?.storage_class || "");
|
||||
setNCTransitionDays(
|
||||
lifecycleRule.transition?.noncurrent_transition_days?.toString() || "0",
|
||||
);
|
||||
setNCTransitionSC(
|
||||
lifecycleRule.transition?.noncurrent_storage_class || "",
|
||||
);
|
||||
} else {
|
||||
// Expiry fields
|
||||
setNCExpirationDays(
|
||||
lifecycleRule.expiration?.noncurrent_expiration_days?.toString() || "0",
|
||||
);
|
||||
}
|
||||
|
||||
setExpiredObjectDM(!!lifecycleRule.expiration?.delete_marker);
|
||||
setExpiredAllVersionsDM(!!lifecycleRule.expiration?.delete_all);
|
||||
setPrefix(lifecycleRule.prefix || "");
|
||||
|
||||
if (lifecycleRule.tags) {
|
||||
const tgs = lifecycleRule.tags.reduce(
|
||||
(stringLab: string, currItem: any, index: number) => {
|
||||
return `${stringLab}${index !== 0 ? "&" : ""}${currItem.key}=${
|
||||
currItem.value
|
||||
}`;
|
||||
},
|
||||
"",
|
||||
);
|
||||
|
||||
setTags(tgs);
|
||||
}
|
||||
}, [lifecycleRule]);
|
||||
|
||||
const saveRecord = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (addLoading) {
|
||||
return;
|
||||
}
|
||||
setAddLoading(true);
|
||||
if (selectedBucket !== null && lifecycleRule !== null) {
|
||||
let rules = {};
|
||||
|
||||
if (ilmType === "expiry") {
|
||||
let expiry: { [key: string]: number } = {};
|
||||
|
||||
if (
|
||||
lifecycleRule.expiration?.days &&
|
||||
lifecycleRule.expiration?.days > 0
|
||||
) {
|
||||
expiry["expiry_days"] = parseInt(expiryDays);
|
||||
}
|
||||
if (lifecycleRule.expiration?.noncurrent_expiration_days) {
|
||||
expiry["noncurrentversion_expiration_days"] =
|
||||
parseInt(NCExpirationDays);
|
||||
}
|
||||
|
||||
rules = {
|
||||
...expiry,
|
||||
};
|
||||
} else {
|
||||
let transition: { [key: string]: number | string } = {};
|
||||
|
||||
if (
|
||||
lifecycleRule.transition?.days &&
|
||||
lifecycleRule.transition?.days > 0
|
||||
) {
|
||||
transition["transition_days"] = parseInt(transitionDays);
|
||||
transition["storage_class"] = storageClass;
|
||||
}
|
||||
if (lifecycleRule.transition?.noncurrent_transition_days) {
|
||||
transition["noncurrentversion_transition_days"] =
|
||||
parseInt(NCTransitionDays);
|
||||
transition["noncurrentversion_transition_storage_class"] =
|
||||
NCTransitionSC;
|
||||
}
|
||||
|
||||
rules = {
|
||||
...transition,
|
||||
};
|
||||
}
|
||||
|
||||
const lifecycleUpdate = {
|
||||
type: ilmType,
|
||||
disable: !enabled,
|
||||
prefix,
|
||||
tags,
|
||||
expired_object_delete_marker: expiredObjectDM,
|
||||
expired_object_delete_all: expiredAllVersionsDM,
|
||||
...rules,
|
||||
};
|
||||
|
||||
api.buckets
|
||||
.updateBucketLifecycle(
|
||||
selectedBucket,
|
||||
lifecycleRule.id,
|
||||
lifecycleUpdate,
|
||||
)
|
||||
.then((res) => {
|
||||
setAddLoading(false);
|
||||
closeModalAndRefresh(true);
|
||||
})
|
||||
.catch(async (eRes) => {
|
||||
setAddLoading(false);
|
||||
const err = (await eRes.json()) as ApiError;
|
||||
dispatch(setErrorSnackMessage(errorToHandler(err)));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let objectVersion = "";
|
||||
|
||||
if (lifecycleRule.expiration) {
|
||||
if (lifecycleRule.expiration.days > 0) {
|
||||
objectVersion = "Current Version";
|
||||
} else if (lifecycleRule.expiration.noncurrent_expiration_days) {
|
||||
objectVersion = "Non-Current Version";
|
||||
}
|
||||
}
|
||||
|
||||
if (lifecycleRule.transition) {
|
||||
if (lifecycleRule.transition.days > 0) {
|
||||
objectVersion = "Current Version";
|
||||
} else if (lifecycleRule.transition.noncurrent_transition_days) {
|
||||
objectVersion = "Non-Current Version";
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
onClose={() => {
|
||||
closeModalAndRefresh(false);
|
||||
}}
|
||||
modalOpen={open}
|
||||
title={"Edit Lifecycle Configuration"}
|
||||
titleIcon={<LifecycleConfigIcon />}
|
||||
>
|
||||
{!loadingTiers ? (
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<FormLayout containerPadding={false} withBorders={false}>
|
||||
<Switch
|
||||
label="Status"
|
||||
indicatorLabels={["Enabled", "Disabled"]}
|
||||
checked={enabled}
|
||||
value={"user_enabled"}
|
||||
id="rule_status"
|
||||
name="rule_status"
|
||||
onChange={(e) => {
|
||||
setEnabled(e.target.checked);
|
||||
}}
|
||||
/>
|
||||
<InputBox
|
||||
id="id"
|
||||
name="id"
|
||||
label="Id"
|
||||
value={lifecycleRule.id}
|
||||
onChange={() => {}}
|
||||
disabled
|
||||
/>
|
||||
{ilmType ? (
|
||||
<RadioGroup
|
||||
currentValue={ilmType}
|
||||
id="rule_type"
|
||||
name="rule_type"
|
||||
label="Rule Type"
|
||||
selectorOptions={ILM_TYPES}
|
||||
onChange={() => {}}
|
||||
disableOptions
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<InputBox
|
||||
id="object-version"
|
||||
name="object-version"
|
||||
label="Object Version"
|
||||
value={objectVersion}
|
||||
onChange={() => {}}
|
||||
disabled
|
||||
/>
|
||||
|
||||
{ilmType === "expiry" && lifecycleRule.expiration?.days && (
|
||||
<InputBox
|
||||
type="number"
|
||||
id="expiry_days"
|
||||
name="expiry_days"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setExpiryDays(e.target.value);
|
||||
}}
|
||||
label="Expiry Days"
|
||||
value={expiryDays}
|
||||
min="0"
|
||||
/>
|
||||
)}
|
||||
|
||||
{ilmType === "expiry" &&
|
||||
lifecycleRule.expiration?.noncurrent_expiration_days && (
|
||||
<InputBox
|
||||
type="number"
|
||||
id="noncurrentversion_expiration_days"
|
||||
name="noncurrentversion_expiration_days"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setNCExpirationDays(e.target.value);
|
||||
}}
|
||||
label="Non-current Expiration Days"
|
||||
value={NCExpirationDays}
|
||||
min="0"
|
||||
/>
|
||||
)}
|
||||
{ilmType === "transition" && lifecycleRule.transition?.days && (
|
||||
<Fragment>
|
||||
<InputBox
|
||||
type="number"
|
||||
id="transition_days"
|
||||
name="transition_days"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTransitionDays(e.target.value);
|
||||
}}
|
||||
label="Transition Days"
|
||||
value={transitionDays}
|
||||
min="0"
|
||||
/>
|
||||
<Select
|
||||
label="Tier"
|
||||
id="storage_class"
|
||||
name="storage_class"
|
||||
value={storageClass}
|
||||
onChange={(value) => {
|
||||
setStorageClass(value);
|
||||
}}
|
||||
options={tiersList}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
|
||||
{ilmType === "transition" &&
|
||||
lifecycleRule.transition?.noncurrent_transition_days && (
|
||||
<Fragment>
|
||||
<InputBox
|
||||
type="number"
|
||||
id="noncurrentversion_transition_days"
|
||||
name="noncurrentversion_transition_days"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setNCTransitionDays(e.target.value);
|
||||
}}
|
||||
label="Non-current Transition Days"
|
||||
value={NCTransitionDays}
|
||||
min="0"
|
||||
/>
|
||||
<Select
|
||||
label="Non-current Version Transition Storage Class"
|
||||
id="noncurrentversion_t_SC"
|
||||
name="noncurrentversion_t_SC"
|
||||
value={NCTransitionSC}
|
||||
onChange={(value) => {
|
||||
setNCTransitionSC(value);
|
||||
}}
|
||||
options={tiersList}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<Accordion
|
||||
title={"Filters"}
|
||||
id={"lifecycle-filters"}
|
||||
expanded={expanded}
|
||||
onTitleClick={() => setExpanded(!expanded)}
|
||||
>
|
||||
<InputBox
|
||||
id="prefix"
|
||||
name="prefix"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPrefix(e.target.value);
|
||||
}}
|
||||
label="Prefix"
|
||||
value={prefix}
|
||||
/>
|
||||
<QueryMultiSelector
|
||||
name="tags"
|
||||
label="Tags"
|
||||
elements={tags}
|
||||
onChange={(vl: string) => {
|
||||
setTags(vl);
|
||||
}}
|
||||
keyPlaceholder="Tag Key"
|
||||
valuePlaceholder="Tag Value"
|
||||
withBorder
|
||||
/>
|
||||
</Accordion>
|
||||
</Grid>
|
||||
{ilmType === "expiry" &&
|
||||
lifecycleRule.expiration?.noncurrent_expiration_days && (
|
||||
<Grid item xs={12}>
|
||||
<Accordion
|
||||
title={"Advanced"}
|
||||
id={"lifecycle-advanced-filters"}
|
||||
expanded={expandedAdv}
|
||||
onTitleClick={() => setExpandedAdv(!expandedAdv)}
|
||||
sx={{ marginTop: 15 }}
|
||||
>
|
||||
<Switch
|
||||
value="expired_delete_marker"
|
||||
id="expired_delete_marker"
|
||||
name="expired_delete_marker"
|
||||
checked={expiredObjectDM}
|
||||
onChange={(
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setExpiredObjectDM(event.target.checked);
|
||||
}}
|
||||
label={"Expired Object Delete Marker"}
|
||||
/>
|
||||
<Switch
|
||||
value="expired_delete_all"
|
||||
id="expired_delete_all"
|
||||
name="expired_delete_all"
|
||||
checked={expiredAllVersionsDM}
|
||||
onChange={(
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setExpiredAllVersionsDM(event.target.checked);
|
||||
}}
|
||||
label={"Expired All Versions"}
|
||||
/>
|
||||
</Accordion>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
|
||||
<Button
|
||||
id={"cancel"}
|
||||
type="button"
|
||||
variant="regular"
|
||||
disabled={addLoading}
|
||||
onClick={() => {
|
||||
closeModalAndRefresh(false);
|
||||
}}
|
||||
label={"Cancel"}
|
||||
/>
|
||||
<Button
|
||||
id={"save"}
|
||||
type="submit"
|
||||
variant="callAction"
|
||||
color="primary"
|
||||
disabled={addLoading || !isFormValid}
|
||||
label={"Save"}
|
||||
/>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<ProgressBar />
|
||||
</Grid>
|
||||
)}
|
||||
</FormLayout>
|
||||
</form>
|
||||
) : (
|
||||
<Loader style={{ width: 16, height: 16 }} />
|
||||
)}
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditLifecycleConfiguration;
|
||||
@@ -1,415 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import {
|
||||
Box,
|
||||
CheckCircleIcon,
|
||||
FormLayout,
|
||||
Grid,
|
||||
InputBox,
|
||||
RadioGroup,
|
||||
ReadBox,
|
||||
Select,
|
||||
Switch,
|
||||
Tooltip,
|
||||
WarnIcon,
|
||||
Wizard,
|
||||
} from "mds";
|
||||
import get from "lodash/get";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";
|
||||
import { ITiersDropDown } from "../types";
|
||||
import { setModalErrorSnackMessage } from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import { api } from "api";
|
||||
import { MultiLifecycleResult } from "api/consoleApi";
|
||||
import { errorToHandler } from "api/errors";
|
||||
|
||||
interface IBulkReplicationModal {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: (clearSelection: boolean) => any;
|
||||
buckets: string[];
|
||||
}
|
||||
|
||||
const AddBulkReplicationModal = ({
|
||||
open,
|
||||
closeModalAndRefresh,
|
||||
buckets,
|
||||
}: IBulkReplicationModal) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [addLoading, setAddLoading] = useState<boolean>(false);
|
||||
const [loadingTiers, setLoadingTiers] = useState<boolean>(true);
|
||||
const [tiersList, setTiersList] = useState<ITiersDropDown[]>([]);
|
||||
const [prefix, setPrefix] = useState("");
|
||||
const [tags, setTags] = useState<string>("");
|
||||
const [storageClass, setStorageClass] = useState("");
|
||||
const [NCTransitionSC, setNCTransitionSC] = useState("");
|
||||
const [expiredObjectDM, setExpiredObjectDM] = useState<boolean>(false);
|
||||
const [expiredAllVersionsDM, setExpiredAllVersionsDM] =
|
||||
useState<boolean>(false);
|
||||
const [NCExpirationDays, setNCExpirationDays] = useState<string>("0");
|
||||
const [NCTransitionDays, setNCTransitionDays] = useState<string>("0");
|
||||
const [ilmType, setIlmType] = useState<"expiry" | "transition">("expiry");
|
||||
const [expiryDays, setExpiryDays] = useState<string>("0");
|
||||
const [transitionDays, setTransitionDays] = useState<string>("0");
|
||||
const [isFormValid, setIsFormValid] = useState<boolean>(false);
|
||||
const [results, setResults] = useState<MultiLifecycleResult | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingTiers) {
|
||||
api.admin
|
||||
.tiersListNames()
|
||||
.then((res) => {
|
||||
const tiersList: string[] | null = get(res.data, "items", []);
|
||||
|
||||
if (tiersList !== null && tiersList.length >= 1) {
|
||||
const objList = tiersList.map((tierName: string) => {
|
||||
return { label: tierName, value: tierName };
|
||||
});
|
||||
|
||||
setTiersList(objList);
|
||||
if (objList.length > 0) {
|
||||
setStorageClass(objList[0].value);
|
||||
}
|
||||
}
|
||||
setLoadingTiers(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoadingTiers(false);
|
||||
dispatch(setModalErrorSnackMessage(errorToHandler(err.error)));
|
||||
});
|
||||
}
|
||||
}, [dispatch, loadingTiers]);
|
||||
|
||||
useEffect(() => {
|
||||
let valid = true;
|
||||
|
||||
if (ilmType !== "expiry") {
|
||||
if (storageClass === "") {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
setIsFormValid(valid);
|
||||
}, [ilmType, expiryDays, transitionDays, storageClass]);
|
||||
|
||||
const LogoToShow = ({ errString }: { errString: string }) => {
|
||||
switch (errString) {
|
||||
case "":
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
paddingTop: 5,
|
||||
color: "#42C91A",
|
||||
}}
|
||||
>
|
||||
<CheckCircleIcon />
|
||||
</Box>
|
||||
);
|
||||
case "n/a":
|
||||
return null;
|
||||
default:
|
||||
if (errString) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
paddingTop: 5,
|
||||
color: "#C72C48",
|
||||
}}
|
||||
>
|
||||
<Tooltip tooltip={errString} placement="top">
|
||||
<WarnIcon />
|
||||
</Tooltip>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const createLifecycleRules = (to: any) => {
|
||||
let rules = {};
|
||||
|
||||
if (ilmType === "expiry") {
|
||||
let expiry = {
|
||||
expiry_days: parseInt(expiryDays),
|
||||
};
|
||||
|
||||
rules = {
|
||||
...expiry,
|
||||
noncurrentversion_expiration_days: parseInt(NCExpirationDays),
|
||||
};
|
||||
} else {
|
||||
let transition = {
|
||||
transition_days: parseInt(transitionDays),
|
||||
};
|
||||
|
||||
rules = {
|
||||
...transition,
|
||||
noncurrentversion_transition_days: parseInt(NCTransitionDays),
|
||||
noncurrentversion_transition_storage_class: NCTransitionSC,
|
||||
storage_class: storageClass,
|
||||
};
|
||||
}
|
||||
|
||||
const lifecycleInsert = {
|
||||
buckets,
|
||||
type: ilmType,
|
||||
prefix,
|
||||
tags,
|
||||
expired_object_delete_marker: expiredObjectDM,
|
||||
expired_object_delete_all: expiredAllVersionsDM,
|
||||
...rules,
|
||||
};
|
||||
|
||||
api.buckets
|
||||
.addMultiBucketLifecycle(lifecycleInsert)
|
||||
.then((res) => {
|
||||
setAddLoading(false);
|
||||
setResults(res.data);
|
||||
to("++");
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddLoading(false);
|
||||
dispatch(setModalErrorSnackMessage(errorToHandler(err.error)));
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
closeModalAndRefresh(false);
|
||||
}}
|
||||
title="Set Lifecycle to multiple buckets"
|
||||
>
|
||||
<Wizard
|
||||
loadingStep={addLoading || loadingTiers}
|
||||
wizardSteps={[
|
||||
{
|
||||
label: "Lifecycle Configuration",
|
||||
componentRender: (
|
||||
<Fragment>
|
||||
<FormLayout withBorders={false} containerPadding={false}>
|
||||
<Grid item xs={12}>
|
||||
<ReadBox
|
||||
label="Local Buckets to replicate"
|
||||
sx={{ maxWidth: "440px", width: "100%" }}
|
||||
>
|
||||
{buckets.join(", ")}
|
||||
</ReadBox>
|
||||
</Grid>
|
||||
<h4>Remote Endpoint Configuration</h4>
|
||||
<fieldset className={"inputItem"}>
|
||||
<legend>Lifecycle Configuration</legend>
|
||||
<RadioGroup
|
||||
currentValue={ilmType}
|
||||
id="quota_type"
|
||||
name="quota_type"
|
||||
label="ILM Rule"
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
setIlmType(e.target.value as "expiry" | "transition");
|
||||
}}
|
||||
selectorOptions={[
|
||||
{ value: "expiry", label: "Expiry" },
|
||||
{ value: "transition", label: "Transition" },
|
||||
]}
|
||||
/>
|
||||
{ilmType === "expiry" ? (
|
||||
<Fragment>
|
||||
<InputBox
|
||||
type="number"
|
||||
id="expiry_days"
|
||||
name="expiry_days"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setExpiryDays(e.target.value);
|
||||
}}
|
||||
label="Expiry Days"
|
||||
value={expiryDays}
|
||||
min="0"
|
||||
/>
|
||||
<InputBox
|
||||
type="number"
|
||||
id="noncurrentversion_expiration_days"
|
||||
name="noncurrentversion_expiration_days"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setNCExpirationDays(e.target.value);
|
||||
}}
|
||||
label="Non-current Expiration Days"
|
||||
value={NCExpirationDays}
|
||||
min="0"
|
||||
/>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
<InputBox
|
||||
type="number"
|
||||
id="transition_days"
|
||||
name="transition_days"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setTransitionDays(e.target.value);
|
||||
}}
|
||||
label="Transition Days"
|
||||
value={transitionDays}
|
||||
min="0"
|
||||
/>
|
||||
<InputBox
|
||||
type="number"
|
||||
id="noncurrentversion_transition_days"
|
||||
name="noncurrentversion_transition_days"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setNCTransitionDays(e.target.value);
|
||||
}}
|
||||
label="Non-current Transition Days"
|
||||
value={NCTransitionDays}
|
||||
min="0"
|
||||
/>
|
||||
<InputBox
|
||||
id="noncurrentversion_t_SC"
|
||||
name="noncurrentversion_t_SC"
|
||||
onChange={(
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setNCTransitionSC(e.target.value);
|
||||
}}
|
||||
placeholder="Set Non-current Version Transition Storage Class"
|
||||
label="Non-current Version Transition Storage Class"
|
||||
value={NCTransitionSC}
|
||||
/>
|
||||
<Select
|
||||
label="Storage Class"
|
||||
id="storage_class"
|
||||
name="storage_class"
|
||||
value={storageClass}
|
||||
onChange={(value) => {
|
||||
setStorageClass(value);
|
||||
}}
|
||||
options={tiersList}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
</fieldset>
|
||||
<fieldset className={"inputItem"}>
|
||||
<legend>File Configuration</legend>
|
||||
<InputBox
|
||||
id="prefix"
|
||||
name="prefix"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPrefix(e.target.value);
|
||||
}}
|
||||
label="Prefix"
|
||||
value={prefix}
|
||||
/>
|
||||
<QueryMultiSelector
|
||||
name="tags"
|
||||
label="Tags"
|
||||
elements={tags}
|
||||
onChange={(vl: string) => {
|
||||
setTags(vl);
|
||||
}}
|
||||
keyPlaceholder="Tag Key"
|
||||
valuePlaceholder="Tag Value"
|
||||
withBorder
|
||||
/>
|
||||
<Switch
|
||||
value="expired_delete_marker"
|
||||
id="expired_delete_marker"
|
||||
name="expired_delete_marker"
|
||||
checked={expiredObjectDM}
|
||||
onChange={(
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setExpiredObjectDM(event.target.checked);
|
||||
}}
|
||||
label={"Expired Object Delete Marker"}
|
||||
/>
|
||||
<Switch
|
||||
value="expired_delete_all"
|
||||
id="expired_delete_all"
|
||||
name="expired_delete_all"
|
||||
checked={expiredAllVersionsDM}
|
||||
onChange={(
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
) => {
|
||||
setExpiredAllVersionsDM(event.target.checked);
|
||||
}}
|
||||
label={"Expired All Versions"}
|
||||
/>
|
||||
</fieldset>
|
||||
</FormLayout>
|
||||
</Fragment>
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
type: "custom",
|
||||
label: "Create Rules",
|
||||
enabled: !loadingTiers && !addLoading && isFormValid,
|
||||
action: createLifecycleRules,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Results",
|
||||
componentRender: (
|
||||
<Fragment>
|
||||
<h3>Multi Bucket lifecycle Assignments Results</h3>
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<h4>Buckets Results</h4>
|
||||
{results?.results?.map((resultItem) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "45px auto",
|
||||
alignItems: "center",
|
||||
justifyContent: "stretch",
|
||||
}}
|
||||
>
|
||||
{LogoToShow({ errString: resultItem.error || "" })}
|
||||
<span>{resultItem.bucketName}</span>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
),
|
||||
buttons: [
|
||||
{
|
||||
type: "custom",
|
||||
label: "Done",
|
||||
enabled: !addLoading,
|
||||
action: () => closeModalAndRefresh(true),
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
forModal
|
||||
/>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddBulkReplicationModal;
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
BucketsIcon,
|
||||
Button,
|
||||
HelpBox,
|
||||
LifecycleConfigIcon,
|
||||
MultipleBucketsIcon,
|
||||
PageLayout,
|
||||
RefreshIcon,
|
||||
@@ -38,8 +37,6 @@ import { SecureComponent } from "../../../../common/SecureComponent";
|
||||
import {
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES,
|
||||
IAM_PERMISSIONS,
|
||||
IAM_ROLES,
|
||||
IAM_SCOPES,
|
||||
permissionTooltipHelper,
|
||||
} from "../../../../common/SecureComponent/permissions";
|
||||
@@ -56,7 +53,6 @@ import AutoColorIcon from "../../Common/Components/AutoColorIcon";
|
||||
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
|
||||
import SearchBox from "../../Common/SearchBox";
|
||||
import VirtualizedList from "../../Common/VirtualizedList/VirtualizedList";
|
||||
import BulkLifecycleModal from "./BulkLifecycleModal";
|
||||
import hasPermission from "../../../../common/SecureComponent/accessControl";
|
||||
import BucketListItem from "./BucketListItem";
|
||||
import BulkReplicationModal from "./BulkReplicationModal";
|
||||
@@ -71,8 +67,6 @@ const ListBuckets = () => {
|
||||
const [selectedBuckets, setSelectedBuckets] = useState<string[]>([]);
|
||||
const [replicationModalOpen, setReplicationModalOpen] =
|
||||
useState<boolean>(false);
|
||||
const [lifecycleModalOpen, setLifecycleModalOpen] = useState<boolean>(false);
|
||||
const [canPutLifecycle, setCanPutLifecycle] = useState<boolean>(false);
|
||||
const [bulkSelect, setBulkSelect] = useState<boolean>(false);
|
||||
|
||||
const features = useSelector(selFeatures);
|
||||
@@ -137,24 +131,6 @@ const ListBuckets = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const closeBulkLifecycleModal = (unselectAll: boolean) => {
|
||||
setLifecycleModalOpen(false);
|
||||
|
||||
if (unselectAll) {
|
||||
setSelectedBuckets([]);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
var failLifecycle = false;
|
||||
selectedBuckets.forEach((bucket: string) => {
|
||||
hasPermission(bucket, IAM_PERMISSIONS[IAM_ROLES.BUCKET_LIFECYCLE], true)
|
||||
? setCanPutLifecycle(true)
|
||||
: (failLifecycle = true);
|
||||
});
|
||||
failLifecycle ? setCanPutLifecycle(false) : setCanPutLifecycle(true);
|
||||
}, [selectedBuckets]);
|
||||
|
||||
const renderItemLine = (index: number) => {
|
||||
const bucket = filteredRecords[index] || null;
|
||||
if (bucket) {
|
||||
@@ -198,13 +174,6 @@ const ListBuckets = () => {
|
||||
closeModalAndRefresh={closeBulkReplicationModal}
|
||||
/>
|
||||
)}
|
||||
{lifecycleModalOpen && (
|
||||
<BulkLifecycleModal
|
||||
buckets={selectedBuckets}
|
||||
closeModalAndRefresh={closeBulkLifecycleModal}
|
||||
open={lifecycleModalOpen}
|
||||
/>
|
||||
)}
|
||||
{!obOnly && (
|
||||
<PageHeaderWrapper label={"Buckets"} actions={<HelpMenu />} />
|
||||
)}
|
||||
@@ -282,33 +251,6 @@ const ListBuckets = () => {
|
||||
</TooltipWrapper>
|
||||
)}
|
||||
|
||||
<TooltipWrapper
|
||||
tooltip={
|
||||
!hasBuckets
|
||||
? ""
|
||||
: !canPutLifecycle
|
||||
? permissionTooltipHelper(
|
||||
IAM_PERMISSIONS[IAM_ROLES.BUCKET_LIFECYCLE],
|
||||
"configure lifecycle for the selected buckets",
|
||||
)
|
||||
: selectedBuckets.length === 0
|
||||
? bulkSelect
|
||||
? "Please select at least one bucket on which to configure Lifecycle"
|
||||
: "Use the Select Multiple Buckets button to choose buckets on which to configure Lifecycle"
|
||||
: "Set Lifecycle"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
id={"set-lifecycle"}
|
||||
onClick={() => {
|
||||
setLifecycleModalOpen(true);
|
||||
}}
|
||||
icon={<LifecycleConfigIcon />}
|
||||
variant={"regular"}
|
||||
disabled={selectedBuckets.length === 0 || !canPutLifecycle}
|
||||
/>
|
||||
</TooltipWrapper>
|
||||
|
||||
<TooltipWrapper
|
||||
tooltip={
|
||||
!hasBuckets
|
||||
|
||||
@@ -36,43 +36,3 @@ export interface BucketReplicationRule {
|
||||
export interface BucketReplication {
|
||||
rules: BucketReplicationRule[];
|
||||
}
|
||||
|
||||
interface IExpirationLifecycle {
|
||||
days: number;
|
||||
date: string;
|
||||
delete_marker?: boolean;
|
||||
delete_all?: boolean;
|
||||
noncurrent_expiration_days?: number;
|
||||
newer_noncurrent_expiration_versions?: number;
|
||||
}
|
||||
|
||||
interface ITransitionLifecycle {
|
||||
days: number;
|
||||
date: string;
|
||||
storage_class?: string;
|
||||
noncurrent_transition_days?: number;
|
||||
noncurrent_storage_class?: string;
|
||||
}
|
||||
|
||||
export interface LifeCycleItem {
|
||||
id: string;
|
||||
prefix?: string;
|
||||
expiration?: IExpirationLifecycle;
|
||||
transition?: ITransitionLifecycle;
|
||||
tags?: any;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
interface MultiBucketResult {
|
||||
bucketName: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
interface MultiBucketResult {
|
||||
results: MultiBucketResult[];
|
||||
}
|
||||
|
||||
export interface ITiersDropDown {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from "react";
|
||||
import { HelpBox, Box, Grid, breakPoints } from "mds";
|
||||
|
||||
interface IDistributedOnly {
|
||||
iconComponent: any;
|
||||
entity: string;
|
||||
}
|
||||
|
||||
const DistributedOnly = ({ iconComponent, entity }: IDistributedOnly) => {
|
||||
return (
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={`${entity} not available`}
|
||||
iconComponent={iconComponent}
|
||||
help={
|
||||
<Box
|
||||
sx={{
|
||||
fontSize: "14px",
|
||||
[`@media (max-width: ${breakPoints.sm}px)`]: {
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
This feature is not available for a single-disk setup.
|
||||
</span>
|
||||
<span>
|
||||
Please deploy a server in{" "}
|
||||
<a
|
||||
href="https://min.io/docs/minio/linux/operations/install-deploy-manage/deploy-minio-multi-node-multi-drive.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Distributed Mode
|
||||
</a>{" "}
|
||||
to use this feature.
|
||||
</span>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default DistributedOnly;
|
||||
@@ -660,12 +660,6 @@ const IconsScreen = () => {
|
||||
LicenseIcon
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={3} sm={2} md={1}>
|
||||
<cicons.LifecycleConfigIcon />
|
||||
<br />
|
||||
LifecycleConfigIcon
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={3} sm={2} md={1}>
|
||||
<cicons.LinkIcon />
|
||||
<br />
|
||||
@@ -1002,18 +996,6 @@ const IconsScreen = () => {
|
||||
TenantsOutlineIcon
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={3} sm={2} md={1}>
|
||||
<cicons.TiersIcon />
|
||||
<br />
|
||||
TiersIcon
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={3} sm={2} md={1}>
|
||||
<cicons.TiersNotAvailableIcon />
|
||||
<br />
|
||||
TiersNotAvailableIcon
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={3} sm={2} md={1}>
|
||||
<cicons.ToolsIcon />
|
||||
<br />
|
||||
|
||||
@@ -1,486 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import get from "lodash/get";
|
||||
import {
|
||||
BackLink,
|
||||
breakPoints,
|
||||
Button,
|
||||
FileSelector,
|
||||
Grid,
|
||||
InputBox,
|
||||
PageLayout,
|
||||
SectionTitle,
|
||||
} from "mds";
|
||||
import { api } from "api";
|
||||
import { errorToHandler } from "api/errors";
|
||||
import { ApiError } from "api/consoleApi";
|
||||
import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
azureServiceName,
|
||||
gcsServiceName,
|
||||
minioServiceName,
|
||||
s3ServiceName,
|
||||
tierTypes,
|
||||
} from "./utils";
|
||||
import { IAM_PAGES } from "../../../../common/SecureComponent/permissions";
|
||||
import { setErrorSnackMessage, setHelpName } from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import RegionSelectWrapper from "./RegionSelectWrapper";
|
||||
import PageHeaderWrapper from "../../Common/PageHeaderWrapper/PageHeaderWrapper";
|
||||
import HelpMenu from "../../HelpMenu";
|
||||
|
||||
const AddTierConfiguration = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
const params = useParams();
|
||||
|
||||
//Local States
|
||||
const [saving, setSaving] = useState<boolean>(false);
|
||||
|
||||
// Form Items
|
||||
const [name, setName] = useState<string>("");
|
||||
const [endpoint, setEndpoint] = useState<string>("");
|
||||
const [bucket, setBucket] = useState<string>("");
|
||||
const [prefix, setPrefix] = useState<string>("");
|
||||
const [region, setRegion] = useState<string>("");
|
||||
const [storageClass, setStorageClass] = useState<string>("");
|
||||
|
||||
const [accessKey, setAccessKey] = useState<string>("");
|
||||
const [secretKey, setSecretKey] = useState<string>("");
|
||||
|
||||
const [creds, setCreds] = useState<string>("");
|
||||
const [encodedCreds, setEncodedCreds] = useState<string>("");
|
||||
|
||||
const [accountName, setAccountName] = useState<string>("");
|
||||
const [accountKey, setAccountKey] = useState<string>("");
|
||||
|
||||
const [titleSelection, setTitleSelection] = useState<string>("");
|
||||
|
||||
const type = get(params, "service", "s3");
|
||||
|
||||
// Validations
|
||||
const [isFormValid, setIsFormValid] = useState<boolean>(true);
|
||||
const [nameInputError, setNameInputError] = useState<string>("");
|
||||
|
||||
// Extra validation functions
|
||||
|
||||
const validName = useCallback(() => {
|
||||
const patternAgainst = /^[A-Z0-9-_]+$/; // Only allow uppercase, numbers, dashes and underscores
|
||||
if (patternAgainst.test(name)) {
|
||||
setNameInputError("");
|
||||
return true;
|
||||
}
|
||||
|
||||
setNameInputError(
|
||||
"Please verify that string is uppercase only and contains valid characters (numbers, dashes & underscores).",
|
||||
);
|
||||
return false;
|
||||
}, [name]);
|
||||
|
||||
//Effects
|
||||
|
||||
useEffect(() => {
|
||||
if (saving) {
|
||||
let request = {};
|
||||
let fields = {
|
||||
name,
|
||||
endpoint,
|
||||
bucket,
|
||||
prefix,
|
||||
region,
|
||||
};
|
||||
|
||||
let tierType = type;
|
||||
|
||||
switch (type) {
|
||||
case "minio":
|
||||
request = {
|
||||
minio: {
|
||||
...fields,
|
||||
accesskey: accessKey,
|
||||
secretkey: secretKey,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "s3":
|
||||
request = {
|
||||
s3: {
|
||||
...fields,
|
||||
accesskey: accessKey,
|
||||
secretkey: secretKey,
|
||||
storageclass: storageClass,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "gcs":
|
||||
request = {
|
||||
gcs: {
|
||||
...fields,
|
||||
creds: encodedCreds,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "azure":
|
||||
request = {
|
||||
azure: {
|
||||
...fields,
|
||||
accountname: accountName,
|
||||
accountkey: accountKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let payload = {
|
||||
type: tierType as
|
||||
| "azure"
|
||||
| "s3"
|
||||
| "minio"
|
||||
| "gcs"
|
||||
| "unsupported"
|
||||
| undefined,
|
||||
...request,
|
||||
};
|
||||
|
||||
api.admin
|
||||
.addTier(payload)
|
||||
.then(() => {
|
||||
setSaving(false);
|
||||
navigate(IAM_PAGES.TIERS);
|
||||
})
|
||||
.catch(async (res) => {
|
||||
const err = (await res.json()) as ApiError;
|
||||
setSaving(false);
|
||||
dispatch(setErrorSnackMessage(errorToHandler(err)));
|
||||
});
|
||||
}
|
||||
}, [
|
||||
accessKey,
|
||||
accountKey,
|
||||
accountName,
|
||||
bucket,
|
||||
encodedCreds,
|
||||
endpoint,
|
||||
name,
|
||||
prefix,
|
||||
region,
|
||||
saving,
|
||||
secretKey,
|
||||
dispatch,
|
||||
storageClass,
|
||||
type,
|
||||
navigate,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
let valid = true;
|
||||
if (type === "") {
|
||||
valid = false;
|
||||
}
|
||||
if (name === "" || !validName()) {
|
||||
valid = false;
|
||||
}
|
||||
if (endpoint === "") {
|
||||
valid = false;
|
||||
}
|
||||
if (bucket === "") {
|
||||
valid = false;
|
||||
}
|
||||
if (region === "" && type !== "minio") {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (type === "s3" || type === "minio") {
|
||||
if (accessKey === "") {
|
||||
valid = false;
|
||||
}
|
||||
if (secretKey === "") {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (type === "gcs") {
|
||||
if (encodedCreds === "") {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (type === "azure") {
|
||||
if (accountName === "") {
|
||||
valid = false;
|
||||
}
|
||||
if (accountKey === "") {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
setIsFormValid(valid);
|
||||
}, [
|
||||
accessKey,
|
||||
accountKey,
|
||||
accountName,
|
||||
bucket,
|
||||
encodedCreds,
|
||||
endpoint,
|
||||
isFormValid,
|
||||
name,
|
||||
prefix,
|
||||
region,
|
||||
secretKey,
|
||||
storageClass,
|
||||
type,
|
||||
validName,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
switch (type) {
|
||||
case "gcs":
|
||||
setEndpoint("https://storage.googleapis.com");
|
||||
setTitleSelection("Google Cloud");
|
||||
break;
|
||||
case "s3":
|
||||
setEndpoint("https://s3.amazonaws.com");
|
||||
setTitleSelection("Amazon S3");
|
||||
break;
|
||||
case "azure":
|
||||
setEndpoint("http://blob.core.windows.net");
|
||||
setTitleSelection("Azure");
|
||||
break;
|
||||
case "minio":
|
||||
setEndpoint("");
|
||||
setTitleSelection("MinIO");
|
||||
}
|
||||
}, [type]);
|
||||
|
||||
//Fetch Actions
|
||||
const submitForm = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
setSaving(true);
|
||||
};
|
||||
|
||||
// Input actions
|
||||
const updateTierName = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setName(e.target.value.toUpperCase());
|
||||
};
|
||||
|
||||
const targetElement = tierTypes.find((item) => item.serviceName === type);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setHelpName("add-tier-configuration"));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeaderWrapper
|
||||
label={
|
||||
<Fragment>
|
||||
<BackLink
|
||||
label={"Add Tier"}
|
||||
onClick={() => navigate(IAM_PAGES.TIERS_ADD)}
|
||||
/>
|
||||
</Fragment>
|
||||
}
|
||||
actions={<HelpMenu />}
|
||||
/>
|
||||
|
||||
<PageLayout>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
sx={{
|
||||
border: "1px solid #eaeaea",
|
||||
padding: "25px",
|
||||
}}
|
||||
>
|
||||
<form noValidate onSubmit={submitForm}>
|
||||
{type !== "" && targetElement ? (
|
||||
<SectionTitle icon={targetElement.logo} sx={{ marginBottom: 20 }}>
|
||||
{titleSelection ? titleSelection : ""} - Add Tier Configuration
|
||||
</SectionTitle>
|
||||
) : null}
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
sx={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
gridAutoFlow: "row",
|
||||
gridRowGap: 20,
|
||||
gridColumnGap: 50,
|
||||
[`@media (max-width: ${breakPoints.sm}px)`]: {
|
||||
gridTemplateColumns: "1fr",
|
||||
gridAutoFlow: "dense",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{type !== "" && (
|
||||
<Fragment>
|
||||
<InputBox
|
||||
id="name"
|
||||
name="name"
|
||||
label="Name"
|
||||
placeholder="Enter Name (Eg. REMOTE-TIER)"
|
||||
value={name}
|
||||
onChange={updateTierName}
|
||||
error={nameInputError}
|
||||
required
|
||||
/>
|
||||
<InputBox
|
||||
id="endpoint"
|
||||
name="endpoint"
|
||||
label="Endpoint"
|
||||
placeholder="Enter Endpoint"
|
||||
value={endpoint}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setEndpoint(e.target.value);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
{(type === s3ServiceName || type === minioServiceName) && (
|
||||
<Fragment>
|
||||
<InputBox
|
||||
id="accessKey"
|
||||
name="accessKey"
|
||||
label="Access Key"
|
||||
placeholder="Enter Access Key"
|
||||
value={accessKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccessKey(e.target.value);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
<InputBox
|
||||
id="secretKey"
|
||||
name="secretKey"
|
||||
label="Secret Key"
|
||||
placeholder="Enter Secret Key"
|
||||
value={secretKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSecretKey(e.target.value);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{type === gcsServiceName && (
|
||||
<FileSelector
|
||||
accept=".json"
|
||||
id="creds"
|
||||
label="Credentials"
|
||||
name="creds"
|
||||
returnEncodedData
|
||||
onChange={(_, fileName, encodedValue) => {
|
||||
if (encodedValue) {
|
||||
setEncodedCreds(encodedValue);
|
||||
setCreds(fileName);
|
||||
}
|
||||
}}
|
||||
value={creds}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
{type === azureServiceName && (
|
||||
<Fragment>
|
||||
<InputBox
|
||||
id="accountName"
|
||||
name="accountName"
|
||||
label="Account Name"
|
||||
placeholder="Enter Account Name"
|
||||
value={accountName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccountName(e.target.value);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
<InputBox
|
||||
id="accountKey"
|
||||
name="accountKey"
|
||||
label="Account Key"
|
||||
placeholder="Enter Account Key"
|
||||
value={accountKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccountKey(e.target.value);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
<InputBox
|
||||
id="bucket"
|
||||
name="bucket"
|
||||
label="Bucket"
|
||||
placeholder="Enter Bucket"
|
||||
value={bucket}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setBucket(e.target.value);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
<InputBox
|
||||
id="prefix"
|
||||
name="prefix"
|
||||
label="Prefix"
|
||||
placeholder="Enter Prefix"
|
||||
value={prefix}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setPrefix(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<RegionSelectWrapper
|
||||
onChange={(value) => {
|
||||
setRegion(value);
|
||||
}}
|
||||
required={type !== "minio"}
|
||||
label={"Region"}
|
||||
id="region"
|
||||
type={type as "azure" | "s3" | "minio" | "gcs"}
|
||||
/>
|
||||
{type === s3ServiceName && (
|
||||
<InputBox
|
||||
id="storageClass"
|
||||
name="storageClass"
|
||||
label="Storage Class"
|
||||
placeholder="Enter Storage Class"
|
||||
value={storageClass}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setStorageClass(e.target.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
|
||||
<Button
|
||||
id={"save-tier-configuration"}
|
||||
type="submit"
|
||||
variant="callAction"
|
||||
disabled={saving || !isFormValid}
|
||||
label={"Save Tier Configuration"}
|
||||
/>
|
||||
</Grid>
|
||||
</form>
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddTierConfiguration;
|
||||
@@ -1,86 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2024 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React from "react";
|
||||
import { ConfirmModalIcon } from "mds";
|
||||
import { api } from "api";
|
||||
import { setErrorSnackMessage } from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import ConfirmDialog from "screens/Console/Common/ModalWrapper/ConfirmDialog";
|
||||
|
||||
interface ITierDeleteModal {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: (refresh: boolean) => any;
|
||||
tierName: string;
|
||||
}
|
||||
|
||||
const DeleteTierConfirmModal = ({
|
||||
open,
|
||||
closeModalAndRefresh,
|
||||
tierName,
|
||||
}: ITierDeleteModal) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const deleteTier = () => {
|
||||
if (tierName !== "") {
|
||||
api.admin
|
||||
.removeTier(tierName)
|
||||
.then(() => {
|
||||
closeModalAndRefresh(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
err.json().then((body: any) => {
|
||||
dispatch(
|
||||
setErrorSnackMessage({
|
||||
errorMessage: body.message,
|
||||
detailedError: body.detailedMessage,
|
||||
}),
|
||||
);
|
||||
});
|
||||
closeModalAndRefresh(false);
|
||||
});
|
||||
} else {
|
||||
setErrorSnackMessage({
|
||||
errorMessage: "There was an error deleting the tier",
|
||||
detailedError: "",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfirmDialog
|
||||
title={`Delete Tier`}
|
||||
confirmText={"Delete"}
|
||||
isOpen={open}
|
||||
titleIcon={<ConfirmModalIcon />}
|
||||
isLoading={false}
|
||||
onConfirm={() => deleteTier()}
|
||||
onClose={() => closeModalAndRefresh(false)}
|
||||
confirmationContent={
|
||||
<React.Fragment>
|
||||
Are you sure you want to delete the tier <strong>{tierName}</strong>?
|
||||
<br />
|
||||
<br />
|
||||
<strong> Please note</strong>
|
||||
<br /> Only empty tiers can be deleted. If the tier has had objects
|
||||
transitioned into it, it cannot be removed.
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteTierConfirmModal;
|
||||
@@ -1,536 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
ActionLink,
|
||||
AddIcon,
|
||||
Box,
|
||||
Button,
|
||||
DataTable,
|
||||
Grid,
|
||||
HelpBox,
|
||||
PageLayout,
|
||||
ProgressBar,
|
||||
RefreshIcon,
|
||||
TierOfflineIcon,
|
||||
TierOnlineIcon,
|
||||
TiersIcon,
|
||||
TiersNotAvailableIcon,
|
||||
} from "mds";
|
||||
import { api } from "api";
|
||||
import { errorToHandler } from "api/errors";
|
||||
import { Tier } from "api/consoleApi";
|
||||
import { actionsTray } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import {
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES,
|
||||
IAM_PERMISSIONS,
|
||||
IAM_ROLES,
|
||||
IAM_SCOPES,
|
||||
} from "../../../../common/SecureComponent/permissions";
|
||||
import {
|
||||
hasPermission,
|
||||
SecureComponent,
|
||||
} from "../../../../common/SecureComponent";
|
||||
import { tierTypes } from "./utils";
|
||||
|
||||
import {
|
||||
selDistSet,
|
||||
setErrorSnackMessage,
|
||||
setHelpName,
|
||||
} from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import SearchBox from "../../Common/SearchBox";
|
||||
import withSuspense from "../../Common/Components/withSuspense";
|
||||
import DistributedOnly from "../../Common/DistributedOnly/DistributedOnly";
|
||||
import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
|
||||
import PageHeaderWrapper from "../../Common/PageHeaderWrapper/PageHeaderWrapper";
|
||||
import HelpMenu from "../../HelpMenu";
|
||||
import DeleteTierConfirmModal from "./DeleteTierConfirmModal";
|
||||
|
||||
const UpdateTierCredentialsModal = withSuspense(
|
||||
React.lazy(() => import("./UpdateTierCredentialsModal")),
|
||||
);
|
||||
|
||||
const ListTiersConfiguration = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const distributedSetup = useSelector(selDistSet);
|
||||
const [records, setRecords] = useState<Tier[]>([]);
|
||||
const [filter, setFilter] = useState<string>("");
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [updateCredentialsOpen, setUpdateCredentialsOpen] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [deleteTierModalOpen, setDeleteTierModalOpen] =
|
||||
useState<boolean>(false);
|
||||
const [selectedTier, setSelectedTier] = useState<Tier>({
|
||||
type: "unsupported",
|
||||
status: false,
|
||||
});
|
||||
const hasSetTier = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_SET_TIER,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
if (distributedSetup) {
|
||||
const fetchRecords = () => {
|
||||
api.admin
|
||||
.tiersList()
|
||||
.then((res) => {
|
||||
setRecords(res.data.items || []);
|
||||
setIsLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
dispatch(setErrorSnackMessage(errorToHandler(err.error)));
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
fetchRecords();
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
}, [isLoading, dispatch, distributedSetup]);
|
||||
|
||||
const filteredRecords = records.filter((b: Tier) => {
|
||||
if (filter === "") {
|
||||
return true;
|
||||
}
|
||||
const getItemName = get(b, `${b.type}.name`, "");
|
||||
const getItemType = get(b, `type`, "");
|
||||
|
||||
return getItemName.indexOf(filter) >= 0 || getItemType.indexOf(filter) >= 0;
|
||||
});
|
||||
|
||||
const addTier = () => {
|
||||
navigate(IAM_PAGES.TIERS_ADD);
|
||||
};
|
||||
|
||||
const renderTierName = (item: Tier) => {
|
||||
const name = get(item, `${item.type}.name`, "");
|
||||
|
||||
if (name !== null) {
|
||||
return <b>{name}</b>;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const renderTierType = (item: string) => {
|
||||
const { logoXs } =
|
||||
tierTypes.find((tierConf) => tierConf.serviceName === item) || {};
|
||||
if (item) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
"& .min-icon": {
|
||||
width: "18px",
|
||||
height: "22px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{logoXs}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
const renderTierStatus = (item: boolean) => {
|
||||
if (item) {
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyItems: "start",
|
||||
color: "#4CCB92",
|
||||
fontSize: "8px",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<TierOnlineIcon style={{ fill: "#4CCB92", width: 14, height: 14 }} />
|
||||
ONLINE
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
color: "#C83B51",
|
||||
fontSize: "8px",
|
||||
}}
|
||||
>
|
||||
<TierOfflineIcon style={{ fill: "#C83B51", width: 14, height: 14 }} />
|
||||
OFFLINE
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const renderTierPrefix = (item: Tier) => {
|
||||
const prefix = get(item, `${item.type}.prefix`, "");
|
||||
|
||||
if (prefix !== null) {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const renderTierEndpoint = (item: Tier) => {
|
||||
const endpoint = get(item, `${item.type}.endpoint`, "");
|
||||
|
||||
if (endpoint !== null) {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const renderTierBucket = (item: Tier) => {
|
||||
const bucket = get(item, `${item.type}.bucket`, "");
|
||||
|
||||
if (bucket !== null) {
|
||||
return bucket;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const renderTierRegion = (item: Tier) => {
|
||||
const region = get(item, `${item.type}.region`, "");
|
||||
|
||||
if (region !== null) {
|
||||
return region;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const renderTierUsage = (item: Tier) => {
|
||||
const endpoint = get(item, `${item.type}.usage`, "");
|
||||
|
||||
if (endpoint !== null) {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const renderTierObjects = (item: Tier) => {
|
||||
const endpoint = get(item, `${item.type}.objects`, "");
|
||||
|
||||
if (endpoint !== null) {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const renderTierVersions = (item: Tier) => {
|
||||
const endpoint = get(item, `${item.type}.versions`, "");
|
||||
|
||||
if (endpoint !== null) {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
const closeTierCredentials = () => {
|
||||
setUpdateCredentialsOpen(false);
|
||||
};
|
||||
const closeDeleteTier = () => {
|
||||
setDeleteTierModalOpen(false);
|
||||
setIsLoading(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setHelpName("list-tiers-configuration"));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{updateCredentialsOpen && (
|
||||
<UpdateTierCredentialsModal
|
||||
open={updateCredentialsOpen}
|
||||
tierData={selectedTier}
|
||||
closeModalAndRefresh={closeTierCredentials}
|
||||
/>
|
||||
)}
|
||||
{deleteTierModalOpen && (
|
||||
<DeleteTierConfirmModal
|
||||
open={deleteTierModalOpen}
|
||||
tierName={get(selectedTier, `${selectedTier.type}.name`, "")}
|
||||
closeModalAndRefresh={closeDeleteTier}
|
||||
/>
|
||||
)}
|
||||
<PageHeaderWrapper label="Tiers" actions={<HelpMenu />} />
|
||||
|
||||
<PageLayout>
|
||||
{!distributedSetup ? (
|
||||
<DistributedOnly
|
||||
entity={"Tiers"}
|
||||
iconComponent={<TiersNotAvailableIcon />}
|
||||
/>
|
||||
) : (
|
||||
<Fragment>
|
||||
<Grid item xs={12} sx={actionsTray.actionsTray}>
|
||||
<SearchBox
|
||||
placeholder="Filter"
|
||||
onChange={setFilter}
|
||||
value={filter}
|
||||
sx={{
|
||||
marginRight: "auto",
|
||||
maxWidth: 380,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "nowrap",
|
||||
gap: 5,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
id={"refresh-list"}
|
||||
icon={<RefreshIcon />}
|
||||
label={`Refresh List`}
|
||||
onClick={() => {
|
||||
setIsLoading(true);
|
||||
}}
|
||||
/>
|
||||
<TooltipWrapper
|
||||
tooltip={
|
||||
hasSetTier
|
||||
? ""
|
||||
: "You require additional permissions in order to create a new Tier. Please ask your MinIO administrator to grant you " +
|
||||
IAM_SCOPES.ADMIN_SET_TIER +
|
||||
" permission in order to create a Tier."
|
||||
}
|
||||
>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_SET_TIER]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<Button
|
||||
id={"add-tier"}
|
||||
icon={<AddIcon />}
|
||||
label={`Create Tier`}
|
||||
onClick={addTier}
|
||||
variant="callAction"
|
||||
/>
|
||||
</SecureComponent>
|
||||
</TooltipWrapper>
|
||||
</Box>
|
||||
</Grid>
|
||||
{isLoading && <ProgressBar />}
|
||||
{!isLoading && (
|
||||
<Fragment>
|
||||
{records.length > 0 && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_LIST_TIERS]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<DataTable
|
||||
itemActions={[
|
||||
{
|
||||
type: "edit",
|
||||
onClick: (tierData: Tier) => {
|
||||
setSelectedTier(tierData);
|
||||
setUpdateCredentialsOpen(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "delete",
|
||||
isDisabled: !hasPermission(
|
||||
"*",
|
||||
IAM_PERMISSIONS[IAM_ROLES.BUCKET_LIFECYCLE],
|
||||
true,
|
||||
),
|
||||
onClick: (tierData: Tier) => {
|
||||
setSelectedTier(tierData);
|
||||
setDeleteTierModalOpen(true);
|
||||
},
|
||||
},
|
||||
]}
|
||||
columns={[
|
||||
{
|
||||
label: "Tier Name",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierName,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Status",
|
||||
elementKey: "status",
|
||||
renderFunction: renderTierStatus,
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
label: "Type",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierType,
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
label: "Endpoint",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierEndpoint,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Bucket",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierBucket,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Prefix",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierPrefix,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Region",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierRegion,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Usage",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierUsage,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Objects",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierObjects,
|
||||
renderFullObject: true,
|
||||
},
|
||||
{
|
||||
label: "Versions",
|
||||
elementKey: "type",
|
||||
renderFunction: renderTierVersions,
|
||||
renderFullObject: true,
|
||||
},
|
||||
]}
|
||||
isLoading={isLoading}
|
||||
records={filteredRecords}
|
||||
entityName="Tiers"
|
||||
idField="service_name"
|
||||
customPaperHeight={"400px"}
|
||||
/>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
sx={{
|
||||
marginTop: "15px",
|
||||
}}
|
||||
>
|
||||
<HelpBox
|
||||
title={"Learn more about TIERS"}
|
||||
iconComponent={<TiersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
Tiers are used by the MinIO Object Lifecycle
|
||||
Management which allows creating rules for time or
|
||||
date based automatic transition or expiry of
|
||||
objects. For object transition, MinIO automatically
|
||||
moves the object to a configured remote storage
|
||||
tier.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://min.io/docs/minio/linux/administration/object-management/object-lifecycle-management.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length === 0 && (
|
||||
<HelpBox
|
||||
title={"Tiers"}
|
||||
iconComponent={<TiersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
Tiers are used by the MinIO Object Lifecycle Management
|
||||
which allows creating rules for time or date based
|
||||
automatic transition or expiry of objects. For object
|
||||
transition, MinIO automatically moves the object to a
|
||||
configured remote storage tier.
|
||||
<br />
|
||||
<br />
|
||||
{hasSetTier ? (
|
||||
<div>
|
||||
To get started,{" "}
|
||||
<ActionLink
|
||||
isLoading={false}
|
||||
label={""}
|
||||
onClick={addTier}
|
||||
>
|
||||
Create Tier
|
||||
</ActionLink>
|
||||
.
|
||||
</div>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListTiersConfiguration;
|
||||
@@ -1,103 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { Autocomplete, InputBox, SelectorType } from "mds";
|
||||
|
||||
import s3Regions from "./s3-regions";
|
||||
import gcsRegions from "./gcs-regions";
|
||||
import azRegions from "./azure-regions";
|
||||
|
||||
const getRegions = (type: string): any => {
|
||||
let regions: SelectorType[] = [];
|
||||
|
||||
if (type === "s3") {
|
||||
regions = s3Regions;
|
||||
}
|
||||
if (type === "gcs") {
|
||||
regions = gcsRegions;
|
||||
}
|
||||
if (type === "azure") {
|
||||
regions = azRegions;
|
||||
}
|
||||
|
||||
return regions.map((item) => ({
|
||||
value: item.value,
|
||||
label: `${item.label} - ${item.value}`,
|
||||
}));
|
||||
};
|
||||
|
||||
interface RegionSelectBoxProps {
|
||||
label: string;
|
||||
onChange: (value: string) => void;
|
||||
value?: string | boolean;
|
||||
id: string;
|
||||
disabled?: boolean;
|
||||
type: "minio" | "s3" | "gcs" | "azure";
|
||||
tooltip?: string;
|
||||
required?: boolean;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
const RegionSelectWrapper = ({
|
||||
label,
|
||||
onChange,
|
||||
type,
|
||||
tooltip = "",
|
||||
required = false,
|
||||
disabled,
|
||||
placeholder,
|
||||
}: RegionSelectBoxProps) => {
|
||||
const regionList = getRegions(type);
|
||||
const [value, setValue] = useState<string>("");
|
||||
|
||||
if (type === "minio") {
|
||||
return (
|
||||
<InputBox
|
||||
label={label}
|
||||
disabled={disabled}
|
||||
required={required}
|
||||
tooltip={tooltip}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
id={"region-list"}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
label={label}
|
||||
disabled={disabled}
|
||||
required={required}
|
||||
tooltip={tooltip}
|
||||
options={regionList}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
id={"region-list"}
|
||||
onChange={(newValue) => {
|
||||
setValue(newValue);
|
||||
onChange(newValue);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegionSelectWrapper;
|
||||
@@ -1,70 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import get from "lodash/get";
|
||||
|
||||
const TierButtonBase = styled.button(({ theme }) => ({
|
||||
background: get(theme, "boxBackground", "#FFF"),
|
||||
border: `${get(theme, "borderColor", "#E2E2E2")} 1px solid`,
|
||||
borderRadius: 5,
|
||||
height: 80,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "start",
|
||||
marginBottom: 16,
|
||||
marginRight: 8,
|
||||
cursor: "pointer",
|
||||
overflow: "hidden",
|
||||
"&:hover": {
|
||||
backgroundColor: get(theme, "buttons.regular.hover.background", "#ebebeb"),
|
||||
},
|
||||
"& .imageContainer": {
|
||||
width: 80,
|
||||
"& .min-icon": {
|
||||
maxWidth: 46,
|
||||
maxHeight: 46,
|
||||
},
|
||||
},
|
||||
"& .tierNotifTitle": {
|
||||
color: get(theme, "buttons.callAction.enabled.background", "#07193E"),
|
||||
fontSize: 16,
|
||||
fontFamily: "Inter,sans-serif",
|
||||
paddingLeft: 18,
|
||||
fontWeight: "bold",
|
||||
},
|
||||
}));
|
||||
|
||||
type TierTypeCardProps = {
|
||||
onClick: (name: string) => void;
|
||||
icon?: any;
|
||||
name: string;
|
||||
};
|
||||
const TierTypeCard = ({ onClick, icon, name }: TierTypeCardProps) => {
|
||||
return (
|
||||
<TierButtonBase
|
||||
onClick={() => {
|
||||
onClick(name);
|
||||
}}
|
||||
>
|
||||
<span className={"imageContainer"}>{icon}</span>
|
||||
<span className={"tierNotifTitle"}>{name}</span>
|
||||
</TierButtonBase>
|
||||
);
|
||||
};
|
||||
|
||||
export default TierTypeCard;
|
||||
@@ -1,150 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { tierTypes } from "./utils";
|
||||
import { IAM_PAGES } from "../../../../common/SecureComponent/permissions";
|
||||
import TierTypeCard from "./TierTypeCard";
|
||||
import {
|
||||
BackLink,
|
||||
Box,
|
||||
breakPoints,
|
||||
FormLayout,
|
||||
HelpBox,
|
||||
PageLayout,
|
||||
TiersIcon,
|
||||
} from "mds";
|
||||
import PageHeaderWrapper from "../../Common/PageHeaderWrapper/PageHeaderWrapper";
|
||||
import HelpMenu from "../../HelpMenu";
|
||||
import { setHelpName } from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
|
||||
const TierTypeSelector = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const typeSelect = (selectName: string) => {
|
||||
navigate(`${IAM_PAGES.TIERS_ADD}/${selectName}`);
|
||||
};
|
||||
const dispatch = useAppDispatch();
|
||||
useEffect(() => {
|
||||
dispatch(setHelpName("tier-type-selector"));
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeaderWrapper
|
||||
label={
|
||||
<Fragment>
|
||||
<BackLink
|
||||
label="Tier Types"
|
||||
onClick={() => navigate(IAM_PAGES.TIERS)}
|
||||
/>
|
||||
</Fragment>
|
||||
}
|
||||
actions={<HelpMenu />}
|
||||
/>
|
||||
|
||||
<PageLayout>
|
||||
<FormLayout
|
||||
title={"Select Tier Type"}
|
||||
icon={<TiersIcon />}
|
||||
helpBox={
|
||||
<HelpBox
|
||||
iconComponent={<TiersIcon />}
|
||||
title={"Tier Types"}
|
||||
help={
|
||||
<Fragment>
|
||||
MinIO supports creating object transition lifecycle management
|
||||
rules, where MinIO can automatically move an object to a
|
||||
remote storage “tier”.
|
||||
<br />
|
||||
<br />
|
||||
MinIO supports the following Tier types:
|
||||
<br />
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://min.io/docs/minio/kubernetes/upstream/administration/object-management/transition-objects-to-s3.html#minio-lifecycle-management-transition-to-s3?ref=con"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
MinIO or other S3-compatible storage
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://min.io/docs/minio/kubernetes/upstream/administration/object-management/transition-objects-to-s3.html#minio-lifecycle-management-transition-to-s3?ref=con"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Amazon S3
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://min.io/docs/minio/kubernetes/upstream/administration/object-management/transition-objects-to-gcs.html#minio-lifecycle-management-transition-to-gcs?ref=con"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Google Cloud Storage
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://min.io/docs/minio/kubernetes/upstream/administration/object-management/transition-objects-to-azure.html#minio-lifecycle-management-transition-to-azure?ref=con"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Microsoft Azure Blob Storage
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
margin: "15px",
|
||||
display: "grid",
|
||||
gridGap: "20px",
|
||||
gridTemplateColumns: "repeat(2, 1fr)",
|
||||
[`@media (max-width: ${breakPoints.md}px)`]: {
|
||||
gridTemplateColumns: "repeat(1, 1fr)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{tierTypes.map((tierType, index) => (
|
||||
<TierTypeCard
|
||||
key={`tierOpt-${index.toString}-${tierType.targetTitle}`}
|
||||
name={tierType.targetTitle}
|
||||
onClick={() => {
|
||||
typeSelect(tierType.serviceName);
|
||||
}}
|
||||
icon={tierType.logo}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</FormLayout>
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default TierTypeSelector;
|
||||
@@ -1,217 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import get from "lodash/get";
|
||||
import {
|
||||
Button,
|
||||
FileSelector,
|
||||
FormLayout,
|
||||
Grid,
|
||||
InputBox,
|
||||
LockIcon,
|
||||
ProgressBar,
|
||||
} from "mds";
|
||||
import { Tier } from "api/consoleApi";
|
||||
import { api } from "api";
|
||||
import { errorToHandler } from "api/errors";
|
||||
import { modalStyleUtils } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { setModalErrorSnackMessage } from "../../../../systemSlice";
|
||||
import { useAppDispatch } from "../../../../store";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
|
||||
interface ITierCredentialsModal {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: (refresh: boolean) => any;
|
||||
tierData: Tier;
|
||||
}
|
||||
|
||||
const UpdateTierCredentialsModal = ({
|
||||
open,
|
||||
closeModalAndRefresh,
|
||||
tierData,
|
||||
}: ITierCredentialsModal) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const [savingTiers, setSavingTiers] = useState<boolean>(false);
|
||||
const [creds, setCreds] = useState<string>("");
|
||||
const [encodedCreds, setEncodedCreds] = useState<string>("");
|
||||
|
||||
const [accountName, setAccountName] = useState<string>("");
|
||||
const [accountKey, setAccountKey] = useState<string>("");
|
||||
|
||||
// Validations
|
||||
const [isFormValid, setIsFormValid] = useState<boolean>(true);
|
||||
|
||||
const type = get(tierData, "type", "");
|
||||
const name = get(tierData, `${type}.name`, "");
|
||||
|
||||
useEffect(() => {
|
||||
let valid = true;
|
||||
|
||||
if (type === "s3" || type === "azure" || type === "minio") {
|
||||
if (accountName === "" || accountKey === "") {
|
||||
valid = false;
|
||||
}
|
||||
} else if (type === "gcs") {
|
||||
if (encodedCreds === "") {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
setIsFormValid(valid);
|
||||
}, [accountKey, accountName, encodedCreds, type]);
|
||||
|
||||
const addRecord = () => {
|
||||
let rules = {};
|
||||
|
||||
if (type === "s3" || type === "azure" || type === "minio") {
|
||||
rules = {
|
||||
access_key: accountName,
|
||||
secret_key: accountKey,
|
||||
};
|
||||
} else if (type === "gcs") {
|
||||
rules = {
|
||||
creds: encodedCreds,
|
||||
};
|
||||
}
|
||||
if (name !== "") {
|
||||
api.admin
|
||||
.editTierCredentials(
|
||||
type as "azure" | "s3" | "minio" | "gcs",
|
||||
name,
|
||||
rules,
|
||||
)
|
||||
.then(() => {
|
||||
setSavingTiers(false);
|
||||
closeModalAndRefresh(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
setSavingTiers(false);
|
||||
dispatch(setModalErrorSnackMessage(errorToHandler(err.error)));
|
||||
});
|
||||
} else {
|
||||
setModalErrorSnackMessage({
|
||||
errorMessage: "There was an error retrieving tier information",
|
||||
detailedError: "",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
titleIcon={<LockIcon />}
|
||||
onClose={() => {
|
||||
closeModalAndRefresh(false);
|
||||
}}
|
||||
title={`Update Credentials - ${type} / ${name}`}
|
||||
>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setSavingTiers(true);
|
||||
addRecord();
|
||||
}}
|
||||
>
|
||||
<FormLayout withBorders={false} containerPadding={false}>
|
||||
{(type === "s3" || type === "minio") && (
|
||||
<Fragment>
|
||||
<InputBox
|
||||
id="accessKey"
|
||||
name="accessKey"
|
||||
label="Access Key"
|
||||
placeholder="Enter Access Key"
|
||||
value={accountName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccountName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<InputBox
|
||||
id="secretKey"
|
||||
name="secretKey"
|
||||
label="Secret Key"
|
||||
placeholder="Enter Secret Key"
|
||||
value={accountKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccountKey(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{type === "gcs" && (
|
||||
<Fragment>
|
||||
<FileSelector
|
||||
accept=".json"
|
||||
id="creds"
|
||||
label="Credentials"
|
||||
name="creds"
|
||||
returnEncodedData
|
||||
onChange={(_, fileName, encodedValue) => {
|
||||
if (encodedValue) {
|
||||
setEncodedCreds(encodedValue);
|
||||
setCreds(fileName);
|
||||
}
|
||||
}}
|
||||
value={creds}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
{type === "azure" && (
|
||||
<Fragment>
|
||||
<InputBox
|
||||
id="accountName"
|
||||
name="accountName"
|
||||
label="Account Name"
|
||||
placeholder="Enter Account Name"
|
||||
value={accountName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccountName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<InputBox
|
||||
id="accountKey"
|
||||
name="accountKey"
|
||||
label="Account Key"
|
||||
placeholder="Enter Account Key"
|
||||
value={accountKey}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccountKey(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
</FormLayout>
|
||||
{savingTiers && (
|
||||
<Grid item xs={12}>
|
||||
<ProgressBar />
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12} sx={modalStyleUtils.modalButtonBar}>
|
||||
<Button
|
||||
id={"save-credentials"}
|
||||
type="submit"
|
||||
variant="callAction"
|
||||
disabled={savingTiers || !isFormValid}
|
||||
label={"Save"}
|
||||
/>
|
||||
</Grid>
|
||||
</form>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpdateTierCredentialsModal;
|
||||
@@ -1,321 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
import { SelectorType } from "mds";
|
||||
|
||||
const azureRegions: SelectorType[] = [
|
||||
{
|
||||
label: "Asia",
|
||||
value: "asia",
|
||||
},
|
||||
{
|
||||
label: "Asia Pacific",
|
||||
value: "asiapacific",
|
||||
},
|
||||
{
|
||||
label: "Australia",
|
||||
value: "australia",
|
||||
},
|
||||
{
|
||||
label: "Australia Central",
|
||||
value: "australiacentral",
|
||||
},
|
||||
{
|
||||
label: "Australia Central 2",
|
||||
value: "australiacentral2",
|
||||
},
|
||||
{
|
||||
label: "Australia East",
|
||||
value: "australiaeast",
|
||||
},
|
||||
{
|
||||
label: "Australia Southeast",
|
||||
value: "australiasoutheast",
|
||||
},
|
||||
{
|
||||
label: "Brazil",
|
||||
value: "brazil",
|
||||
},
|
||||
{
|
||||
label: "Brazil South",
|
||||
value: "brazilsouth",
|
||||
},
|
||||
{
|
||||
label: "Brazil Southeast",
|
||||
value: "brazilsoutheast",
|
||||
},
|
||||
{
|
||||
label: "Canada",
|
||||
value: "canada",
|
||||
},
|
||||
{
|
||||
label: "Canada Central",
|
||||
value: "canadacentral",
|
||||
},
|
||||
{
|
||||
label: "Canada East",
|
||||
value: "canadaeast",
|
||||
},
|
||||
{
|
||||
label: "Central India",
|
||||
value: "centralindia",
|
||||
},
|
||||
{
|
||||
label: "Central US",
|
||||
value: "centralus",
|
||||
},
|
||||
{
|
||||
label: "Central US (Stage)",
|
||||
value: "centralusstage",
|
||||
},
|
||||
{
|
||||
label: "Central US EUAP",
|
||||
value: "centraluseuap",
|
||||
},
|
||||
{
|
||||
label: "East Asia",
|
||||
value: "eastasia",
|
||||
},
|
||||
{
|
||||
label: "East Asia (Stage)",
|
||||
value: "eastasiastage",
|
||||
},
|
||||
{
|
||||
label: "East US",
|
||||
value: "eastus",
|
||||
},
|
||||
{
|
||||
label: "East US (Stage)",
|
||||
value: "eastusstage",
|
||||
},
|
||||
{
|
||||
label: "East US 2",
|
||||
value: "eastus2",
|
||||
},
|
||||
{
|
||||
label: "East US 2 (Stage)",
|
||||
value: "eastus2stage",
|
||||
},
|
||||
{
|
||||
label: "East US 2 EUAP",
|
||||
value: "eastus2euap",
|
||||
},
|
||||
{
|
||||
label: "Europe",
|
||||
value: "europe",
|
||||
},
|
||||
{
|
||||
label: "France",
|
||||
value: "france",
|
||||
},
|
||||
{
|
||||
label: "France Central",
|
||||
value: "francecentral",
|
||||
},
|
||||
{
|
||||
label: "France South",
|
||||
value: "francesouth",
|
||||
},
|
||||
{
|
||||
label: "Germany",
|
||||
value: "germany",
|
||||
},
|
||||
{
|
||||
label: "Germany North",
|
||||
value: "germanynorth",
|
||||
},
|
||||
{
|
||||
label: "Germany West Central",
|
||||
value: "germanywestcentral",
|
||||
},
|
||||
{
|
||||
label: "Global",
|
||||
value: "global",
|
||||
},
|
||||
{
|
||||
label: "India",
|
||||
value: "india",
|
||||
},
|
||||
{
|
||||
label: "Japan",
|
||||
value: "japan",
|
||||
},
|
||||
{
|
||||
label: "Japan East",
|
||||
value: "japaneast",
|
||||
},
|
||||
{
|
||||
label: "Japan West",
|
||||
value: "japanwest",
|
||||
},
|
||||
{
|
||||
label: "Jio India Central",
|
||||
value: "jioindiacentral",
|
||||
},
|
||||
{
|
||||
label: "Jio India West",
|
||||
value: "jioindiawest",
|
||||
},
|
||||
{
|
||||
label: "Korea",
|
||||
value: "korea",
|
||||
},
|
||||
{
|
||||
label: "Korea Central",
|
||||
value: "koreacentral",
|
||||
},
|
||||
{
|
||||
label: "Korea South",
|
||||
value: "koreasouth",
|
||||
},
|
||||
{
|
||||
label: "North Central US",
|
||||
value: "northcentralus",
|
||||
},
|
||||
{
|
||||
label: "North Central US (Stage)",
|
||||
value: "northcentralusstage",
|
||||
},
|
||||
{
|
||||
label: "North Europe",
|
||||
value: "northeurope",
|
||||
},
|
||||
{
|
||||
label: "Norway",
|
||||
value: "norway",
|
||||
},
|
||||
{
|
||||
label: "Norway East",
|
||||
value: "norwayeast",
|
||||
},
|
||||
{
|
||||
label: "Norway West",
|
||||
value: "norwaywest",
|
||||
},
|
||||
{
|
||||
label: "South Africa",
|
||||
value: "southafrica",
|
||||
},
|
||||
{
|
||||
label: "South Africa North",
|
||||
value: "southafricanorth",
|
||||
},
|
||||
{
|
||||
label: "South Africa West",
|
||||
value: "southafricawest",
|
||||
},
|
||||
{
|
||||
label: "South Central US",
|
||||
value: "southcentralus",
|
||||
},
|
||||
{
|
||||
label: "South Central US (Stage)",
|
||||
value: "southcentralusstage",
|
||||
},
|
||||
{
|
||||
label: "South India",
|
||||
value: "southindia",
|
||||
},
|
||||
{
|
||||
label: "Southeast Asia",
|
||||
value: "southeastasia",
|
||||
},
|
||||
{
|
||||
label: "Southeast Asia (Stage)",
|
||||
value: "southeastasiastage",
|
||||
},
|
||||
{
|
||||
label: "Sweden Central",
|
||||
value: "swedencentral",
|
||||
},
|
||||
{
|
||||
label: "Switzerland",
|
||||
value: "switzerland",
|
||||
},
|
||||
{
|
||||
label: "Switzerland North",
|
||||
value: "switzerlandnorth",
|
||||
},
|
||||
{
|
||||
label: "Switzerland West",
|
||||
value: "switzerlandwest",
|
||||
},
|
||||
{
|
||||
label: "UAE Central",
|
||||
value: "uaecentral",
|
||||
},
|
||||
{
|
||||
label: "UAE North",
|
||||
value: "uaenorth",
|
||||
},
|
||||
{
|
||||
label: "UK South",
|
||||
value: "uksouth",
|
||||
},
|
||||
{
|
||||
label: "UK West",
|
||||
value: "ukwest",
|
||||
},
|
||||
{
|
||||
label: "United Arab Emirates",
|
||||
value: "uae",
|
||||
},
|
||||
{
|
||||
label: "United Kingdom",
|
||||
value: "uk",
|
||||
},
|
||||
{
|
||||
label: "United States",
|
||||
value: "unitedstates",
|
||||
},
|
||||
{
|
||||
label: "United States EUAP",
|
||||
value: "unitedstateseuap",
|
||||
},
|
||||
{
|
||||
label: "West Central US",
|
||||
value: "westcentralus",
|
||||
},
|
||||
{
|
||||
label: "West Europe",
|
||||
value: "westeurope",
|
||||
},
|
||||
{
|
||||
label: "West India",
|
||||
value: "westindia",
|
||||
},
|
||||
{
|
||||
label: "West US",
|
||||
value: "westus",
|
||||
},
|
||||
{
|
||||
label: "West US (Stage)",
|
||||
value: "westusstage",
|
||||
},
|
||||
{
|
||||
label: "West US 2",
|
||||
value: "westus2",
|
||||
},
|
||||
{
|
||||
label: "West US 2 (Stage)",
|
||||
value: "westus2stage",
|
||||
},
|
||||
{
|
||||
label: "West US 3",
|
||||
value: "westus3",
|
||||
},
|
||||
];
|
||||
export default azureRegions;
|
||||
@@ -1,51 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
import { SelectorType } from "mds";
|
||||
|
||||
const gcsRegions: SelectorType[] = [
|
||||
{ label: "Montréal", value: "NORTHAMERICA-NORTHEAST1" },
|
||||
{ label: "Toronto", value: "NORTHAMERICA-NORTHEAST2" },
|
||||
{ label: "Iowa", value: "US-CENTRAL1" },
|
||||
{ label: "South Carolina", value: "US-EAST1" },
|
||||
{ label: "Northern Virginia", value: "US-EAST4" },
|
||||
{ label: "Oregon", value: "US-WEST1" },
|
||||
{ label: "Los Angeles", value: "US-WEST2" },
|
||||
{ label: "Salt Lake City", value: "US-WEST3" },
|
||||
{ label: "Las Vegas", value: "US-WEST4" },
|
||||
{ label: "São Paulo", value: "SOUTHAMERICA-EAST1" },
|
||||
{ label: "Santiago", value: "SOUTHAMERICA-WEST1" },
|
||||
{ label: "Warsaw", value: "EUROPE-CENTRAL2" },
|
||||
{ label: "Finland", value: "EUROPE-NORTH1" },
|
||||
{ label: "Belgium", value: "EUROPE-WEST1" },
|
||||
{ label: "London", value: "EUROPE-WEST2" },
|
||||
{ label: "Frankfurt", value: "EUROPE-WEST3" },
|
||||
{ label: "Netherlands", value: "EUROPE-WEST4" },
|
||||
{ label: "Zürich", value: "EUROPE-WEST6" },
|
||||
{ label: "Taiwan", value: "ASIA-EAST1" },
|
||||
{ label: "Hong Kong", value: "ASIA-EAST2" },
|
||||
{ label: "Tokyo", value: "ASIA-NORTHEAST1" },
|
||||
{ label: "Osaka", value: "ASIA-NORTHEAST2" },
|
||||
{ label: "Seoul", value: "ASIA-NORTHEAST3" },
|
||||
{ label: "Mumbai", value: "ASIA-SOUTH1" },
|
||||
{ label: "Delhi", value: "ASIA-SOUTH2" },
|
||||
{ label: "Singapore", value: "ASIA-SOUTHEAST1" },
|
||||
{ label: "Jakarta", value: "ASIA-SOUTHEAST2" },
|
||||
{ label: "Sydney", value: "AUSTRALIA-SOUTHEAST1" },
|
||||
{ label: "Melbourne", value: "AUSTRALIA-SOUTHEAST2" },
|
||||
];
|
||||
|
||||
export default gcsRegions;
|
||||
@@ -1,48 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
import { SelectorType } from "mds";
|
||||
|
||||
const s3Regions: SelectorType[] = [
|
||||
{ label: "US East (Ohio)", value: "us-east-2" },
|
||||
{ label: "US East (N. Virginia)", value: "us-east-1" },
|
||||
{ label: "US West (N. California)", value: "us-west-1" },
|
||||
{ label: "US West (Oregon)", value: "us-west-2" },
|
||||
{ label: "Africa (Cape Town)", value: "af-south-1" },
|
||||
{ label: "Asia Pacific (Hong Kong)***", value: "ap-east-1" },
|
||||
{ label: "Asia Pacific (Jakarta)", value: "ap-southeast-3" },
|
||||
{ label: "Asia Pacific (Mumbai)", value: "ap-south-1" },
|
||||
{ label: "Asia Pacific (Osaka)", value: "ap-northeast-3" },
|
||||
{ label: "Asia Pacific (Seoul)", value: "ap-northeast-2" },
|
||||
{ label: "Asia Pacific (Singapore)", value: "ap-southeast-1" },
|
||||
{ label: "Asia Pacific (Sydney)", value: "ap-southeast-2" },
|
||||
{ label: "Asia Pacific (Tokyo)", value: "ap-northeast-1" },
|
||||
{ label: "Canada (Central)", value: "ca-central-1" },
|
||||
{ label: "China (Beijing)", value: "cn-north-1" },
|
||||
{ label: "China (Ningxia)", value: "cn-northwest-1" },
|
||||
{ label: "Europe (Frankfurt)", value: "eu-central-1" },
|
||||
{ label: "Europe (Ireland)", value: "eu-west-1" },
|
||||
{ label: "Europe (London)", value: "eu-west-2" },
|
||||
{ label: "Europe (Milan)", value: "eu-south-1" },
|
||||
{ label: "Europe (Paris)", value: "eu-west-3" },
|
||||
{ label: "Europe (Stockholm)", value: "eu-north-1" },
|
||||
{ label: "South America (São Paulo)", value: "sa-east-1" },
|
||||
{ label: "Middle East (Bahrain)", value: "me-south-1" },
|
||||
{ label: "AWS GovCloud (US-East)", value: "us-gov-east-1" },
|
||||
{ label: "AWS GovCloud (US-West)", value: "us-gov-west-1" },
|
||||
];
|
||||
|
||||
export default s3Regions;
|
||||
@@ -1,58 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import {
|
||||
AzureTierIcon,
|
||||
AzureTierIconXs,
|
||||
GoogleTierIcon,
|
||||
GoogleTierIconXs,
|
||||
MinIOTierIcon,
|
||||
MinIOTierIconXs,
|
||||
S3TierIcon,
|
||||
S3TierIconXs,
|
||||
} from "mds";
|
||||
|
||||
export const minioServiceName = "minio";
|
||||
export const gcsServiceName = "gcs";
|
||||
export const s3ServiceName = "s3";
|
||||
export const azureServiceName = "azure";
|
||||
|
||||
export const tierTypes = [
|
||||
{
|
||||
serviceName: minioServiceName,
|
||||
targetTitle: "MinIO",
|
||||
logo: <MinIOTierIcon />,
|
||||
logoXs: <MinIOTierIconXs />,
|
||||
},
|
||||
{
|
||||
serviceName: gcsServiceName,
|
||||
targetTitle: "Google Cloud Storage",
|
||||
logo: <GoogleTierIcon />,
|
||||
logoXs: <GoogleTierIconXs />,
|
||||
},
|
||||
{
|
||||
serviceName: s3ServiceName,
|
||||
targetTitle: "AWS S3",
|
||||
logo: <S3TierIcon />,
|
||||
logoXs: <S3TierIconXs />,
|
||||
},
|
||||
{
|
||||
serviceName: azureServiceName,
|
||||
targetTitle: "Azure",
|
||||
logo: <AzureTierIcon />,
|
||||
logoXs: <AzureTierIconXs />,
|
||||
},
|
||||
];
|
||||
@@ -40,7 +40,6 @@ import { hasPermission } from "../../common/SecureComponent";
|
||||
import { IRouteRule } from "./Menu/types";
|
||||
import {
|
||||
menuOpen,
|
||||
selDistSet,
|
||||
serverIsLoading,
|
||||
setServerNeedsRestart,
|
||||
setSnackBarMessage,
|
||||
@@ -58,17 +57,6 @@ const AddEventDestination = React.lazy(
|
||||
const EventTypeSelector = React.lazy(
|
||||
() => import("./EventDestinations/EventTypeSelector"),
|
||||
);
|
||||
|
||||
const ListTiersConfiguration = React.lazy(
|
||||
() => import("./Configurations/TiersConfiguration/ListTiersConfiguration"),
|
||||
);
|
||||
const TierTypeSelector = React.lazy(
|
||||
() => import("./Configurations/TiersConfiguration/TierTypeSelector"),
|
||||
);
|
||||
const AddTierConfiguration = React.lazy(
|
||||
() => import("./Configurations/TiersConfiguration/AddTierConfiguration"),
|
||||
);
|
||||
|
||||
const ErrorLogs = React.lazy(() => import("./Logs/ErrorLogs/ErrorLogs"));
|
||||
const LogsSearchMain = React.lazy(
|
||||
() => import("./Logs/LogSearch/LogsSearchMain"),
|
||||
@@ -132,7 +120,6 @@ const Console = () => {
|
||||
const open = useSelector((state: AppState) => state.system.sidebarOpen);
|
||||
const session = useSelector(selSession);
|
||||
const features = useSelector(selFeatures);
|
||||
const distributedSetup = useSelector(selDistSet);
|
||||
const snackBarMessage = useSelector(
|
||||
(state: AppState) => state.system.snackBar,
|
||||
);
|
||||
@@ -334,20 +321,6 @@ const Console = () => {
|
||||
component: EventDestinations,
|
||||
path: IAM_PAGES.EVENT_DESTINATIONS,
|
||||
},
|
||||
{
|
||||
component: AddTierConfiguration,
|
||||
path: IAM_PAGES.TIERS_ADD_SERVICE,
|
||||
fsHidden: !distributedSetup,
|
||||
},
|
||||
{
|
||||
component: TierTypeSelector,
|
||||
path: IAM_PAGES.TIERS_ADD,
|
||||
fsHidden: !distributedSetup,
|
||||
},
|
||||
{
|
||||
component: ListTiersConfiguration,
|
||||
path: IAM_PAGES.TIERS,
|
||||
},
|
||||
{
|
||||
component: Account,
|
||||
path: IAM_PAGES.ACCOUNT,
|
||||
|
||||
@@ -72,8 +72,6 @@ const OBHeader = ({ bucketName }: IOBHeader) => {
|
||||
IAM_SCOPES.S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
IAM_SCOPES.S3_GET_REPLICATION_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_REPLICATION_CONFIGURATION,
|
||||
IAM_SCOPES.S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
IAM_SCOPES.ADMIN_GET_BUCKET_QUOTA,
|
||||
IAM_SCOPES.ADMIN_SET_BUCKET_QUOTA,
|
||||
IAM_SCOPES.S3_PUT_BUCKET_TAGGING,
|
||||
|
||||
@@ -41,7 +41,6 @@ import {
|
||||
MonitoringMenuIcon,
|
||||
ObjectBrowserIcon,
|
||||
SettingsIcon,
|
||||
TiersIcon,
|
||||
UsersMenuIcon,
|
||||
} from "mds";
|
||||
import { hasPermission } from "../../common/SecureComponent";
|
||||
@@ -211,13 +210,6 @@ export const validRoutes = (
|
||||
icon: <LambdaIcon />,
|
||||
id: "lambda",
|
||||
},
|
||||
{
|
||||
group: "Administrator",
|
||||
path: IAM_PAGES.TIERS,
|
||||
name: "Tiering",
|
||||
icon: <TiersIcon />,
|
||||
id: "tiers",
|
||||
},
|
||||
{
|
||||
group: "Administrator",
|
||||
path: IAM_PAGES.KMS_KEYS,
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
import * as roles from "../utils/roles";
|
||||
import * as elements from "../utils/elements";
|
||||
import { tiersElement } from "../utils/elements-menu";
|
||||
|
||||
fixture("For user with Tiers permissions")
|
||||
.page("http://localhost:9090")
|
||||
.beforeEach(async (t) => {
|
||||
await t.useRole(roles.tiers);
|
||||
});
|
||||
|
||||
test("Tiers sidebar item exists", async (t) => {
|
||||
await t.expect(tiersElement.exists).ok();
|
||||
});
|
||||
|
||||
test("Add Tier button exists", async (t) => {
|
||||
const createTierButtonExists = elements.createTierButton.exists;
|
||||
await t
|
||||
.navigateTo("http://localhost:9090/settings/tiers")
|
||||
.expect(createTierButtonExists)
|
||||
.ok();
|
||||
});
|
||||
|
||||
test("Add Tier button is clickable", async (t) => {
|
||||
await t
|
||||
.navigateTo("http://localhost:9090/settings/tiers")
|
||||
.click(elements.createTierButton);
|
||||
});
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
monitoringElement,
|
||||
notificationEndpointsElement,
|
||||
serviceAcctsElement,
|
||||
tiersElement,
|
||||
usersElement,
|
||||
} from "../utils/elements-menu";
|
||||
|
||||
@@ -58,12 +57,6 @@ test("All sidebar items exist", async (t) => {
|
||||
.ok()
|
||||
.expect(notificationEndpointsElement.exists)
|
||||
.ok()
|
||||
.expect(tiersElement.exists)
|
||||
.ok()
|
||||
.expect(elements.diagnosticsElement.exists)
|
||||
.ok()
|
||||
.expect(elements.performanceElement.exists)
|
||||
.ok()
|
||||
.expect(licenseExists)
|
||||
.ok();
|
||||
});
|
||||
|
||||
@@ -75,10 +75,6 @@ export const configurationsElement = getMenuElement("configurations");
|
||||
|
||||
export const notificationEndpointsElement = getMenuElement("lambda");
|
||||
|
||||
export const tiersElement = getMenuElement("tiers");
|
||||
|
||||
export const diagnosticsElement = getMenuElement("diagnostics");
|
||||
export const performanceElement = getMenuElement("performance");
|
||||
export const inspectElement = getMenuElement("inspectObjects");
|
||||
|
||||
export const licenseElement = getMenuElement("license");
|
||||
|
||||
@@ -31,8 +31,6 @@ export const deleteButton = Selector("button:enabled").withExactText("Delete");
|
||||
export const addEventDestination = Selector("button:enabled").withText(
|
||||
"Add Event Destination",
|
||||
);
|
||||
export const createTierButton =
|
||||
Selector("button:enabled").withText("Create Tier");
|
||||
export const createUserButton =
|
||||
Selector("button:enabled").withText("Create User");
|
||||
export const createGroupButton =
|
||||
|
||||
Reference in New Issue
Block a user