// 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 . 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(true); const [tiersList, setTiersList] = useState([]); const [addLoading, setAddLoading] = useState(false); const [versioningInfo, setVersioningInfo] = useState(null); const [prefix, setPrefix] = useState(""); const [tags, setTags] = useState(""); const [storageClass, setStorageClass] = useState(""); const [ilmType, setIlmType] = useState<"expiry" | "transition">("expiry"); const [targetVersion, setTargetVersion] = useState<"current" | "noncurrent">( "current", ); const [lifecycleDays, setLifecycleDays] = useState(""); const [isFormValid, setIsFormValid] = useState(false); const [expiredObjectDM, setExpiredObjectDM] = useState(false); const [expiredAllVersionsDM, setExpiredAllVersionsDM] = useState(false); const [loadingVersioning, setLoadingVersioning] = useState(true); const [expandedAdv, setExpandedAdv] = useState(false); const [expanded, setExpanded] = useState(false); const [expiryUnit, setExpiryUnit] = useState("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 ( { closeModalAndRefresh(false); }} title="Add Lifecycle Rule" titleIcon={} > {loadingTiers && ( )} {!loadingTiers && (
) => { e.preventDefault(); setAddLoading(true); addRecord(); }} > { setIlmType(e.target.value as "expiry" | "transition"); }} selectorOptions={[ { value: "expiry", label: "Expiry" }, { value: "transition", label: "Transition" }, ]} helpTip={ Select{" "} Expiry {" "} to delete Objects per this rule. Select{" "} Transition {" "} to move Objects to a remote storage{" "} Tier {" "} per this rule. } helpTipPlacement="right" /> {versioningInfo?.status === "Enabled" && ( { setStorageClass(value as string); }} options={tiersList} helpTip={ Configure a{" "} remote tier {" "} to receive transitioned Objects } helpTipPlacement="right" /> )} setExpanded(!expanded)} > ) => { setPrefix(e.target.value); }} label="Prefix" value={prefix} /> { setTags(vl); }} keyPlaceholder="Tag Key" valuePlaceholder="Tag Value" withBorder /> {ilmType === "expiry" && targetVersion === "noncurrent" && ( setExpandedAdv(!expandedAdv)} sx={{ marginTop: 15 }} > , ) => { setExpiredObjectDM(event.target.checked); }} label={"Expire Delete Marker"} description={ "Remove the reference to the object if no versions are left" } /> , ) => { setExpiredAllVersionsDM(event.target.checked); }} label={"Expire All Versions"} description={ "Removes all the versions of the object already expired" } /> )}