Add Edit pool capability (#1806)
Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -2321,6 +2321,21 @@ func parseTenantPool(pool *miniov2.Pool) *models.Pool {
|
|||||||
tolerations = append(tolerations, toleration)
|
tolerations = append(tolerations, toleration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var securityContext models.SecurityContext
|
||||||
|
|
||||||
|
if pool.SecurityContext != nil {
|
||||||
|
fsGroup := strconv.Itoa(int(*pool.SecurityContext.FSGroup))
|
||||||
|
runAsGroup := strconv.Itoa(int(*pool.SecurityContext.RunAsGroup))
|
||||||
|
runAsUser := strconv.Itoa(int(*pool.SecurityContext.RunAsUser))
|
||||||
|
|
||||||
|
securityContext = models.SecurityContext{
|
||||||
|
FsGroup: &fsGroup,
|
||||||
|
RunAsGroup: &runAsGroup,
|
||||||
|
RunAsNonRoot: pool.SecurityContext.RunAsNonRoot,
|
||||||
|
RunAsUser: &runAsUser,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
poolModel := &models.Pool{
|
poolModel := &models.Pool{
|
||||||
Name: pool.Name,
|
Name: pool.Name,
|
||||||
Servers: swag.Int64(int64(pool.Servers)),
|
Servers: swag.Int64(int64(pool.Servers)),
|
||||||
@@ -2329,10 +2344,11 @@ func parseTenantPool(pool *miniov2.Pool) *models.Pool {
|
|||||||
Size: size,
|
Size: size,
|
||||||
StorageClassName: storageClassName,
|
StorageClassName: storageClassName,
|
||||||
},
|
},
|
||||||
NodeSelector: pool.NodeSelector,
|
NodeSelector: pool.NodeSelector,
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
Affinity: affinity,
|
Affinity: affinity,
|
||||||
Tolerations: tolerations,
|
Tolerations: tolerations,
|
||||||
|
SecurityContext: &securityContext,
|
||||||
}
|
}
|
||||||
return poolModel
|
return poolModel
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,6 +183,8 @@ export const IAM_PAGES = {
|
|||||||
"/namespaces/:tenantNamespace/tenants/:tenantName/pools",
|
"/namespaces/:tenantNamespace/tenants/:tenantName/pools",
|
||||||
NAMESPACE_TENANT_POOLS_ADD:
|
NAMESPACE_TENANT_POOLS_ADD:
|
||||||
"/namespaces/:tenantNamespace/tenants/:tenantName/add-pool",
|
"/namespaces/:tenantNamespace/tenants/:tenantName/add-pool",
|
||||||
|
NAMESPACE_TENANT_POOLS_EDIT:
|
||||||
|
"/namespaces/:tenantNamespace/tenants/:tenantName/edit-pool",
|
||||||
NAMESPACE_TENANT_VOLUMES:
|
NAMESPACE_TENANT_VOLUMES:
|
||||||
"/namespaces/:tenantNamespace/tenants/:tenantName/volumes",
|
"/namespaces/:tenantNamespace/tenants/:tenantName/volumes",
|
||||||
NAMESPACE_TENANT_LICENSE:
|
NAMESPACE_TENANT_LICENSE:
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export interface INodeAffinityTerms {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface INodeAffinityLabelsSelector {
|
export interface INodeAffinityLabelsSelector {
|
||||||
matchExpressions: object[];
|
matchExpressions: IMatchExpressionItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMatchExpressionItem {
|
export interface IMatchExpressionItem {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import {
|
|||||||
import { hasPermission } from "../../common/SecureComponent";
|
import { hasPermission } from "../../common/SecureComponent";
|
||||||
import { IRouteRule } from "./Menu/types";
|
import { IRouteRule } from "./Menu/types";
|
||||||
import LoadingComponent from "../../common/LoadingComponent";
|
import LoadingComponent from "../../common/LoadingComponent";
|
||||||
|
import EditPool from "./Tenants/TenantDetails/Pools/EditPool/EditPool";
|
||||||
|
|
||||||
const Trace = React.lazy(() => import("./Trace/Trace"));
|
const Trace = React.lazy(() => import("./Trace/Trace"));
|
||||||
const Heal = React.lazy(() => import("./Heal/Heal"));
|
const Heal = React.lazy(() => import("./Heal/Heal"));
|
||||||
@@ -114,7 +115,7 @@ const ConfigurationOptions = React.lazy(
|
|||||||
() => import("./Configurations/ConfigurationPanels/ConfigurationOptions")
|
() => import("./Configurations/ConfigurationPanels/ConfigurationOptions")
|
||||||
);
|
);
|
||||||
const AddPool = React.lazy(
|
const AddPool = React.lazy(
|
||||||
() => import("./Tenants/TenantDetails/Pools/AddPool")
|
() => import("./Tenants/TenantDetails/Pools/AddPool/AddPool")
|
||||||
);
|
);
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
@@ -439,6 +440,11 @@ const Console = ({
|
|||||||
path: IAM_PAGES.NAMESPACE_TENANT_POOLS_ADD,
|
path: IAM_PAGES.NAMESPACE_TENANT_POOLS_ADD,
|
||||||
forceDisplay: true,
|
forceDisplay: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: EditPool,
|
||||||
|
path: IAM_PAGES.NAMESPACE_TENANT_POOLS_EDIT,
|
||||||
|
forceDisplay: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: TenantDetails,
|
component: TenantDetails,
|
||||||
path: IAM_PAGES.NAMESPACE_TENANT_VOLUMES,
|
path: IAM_PAGES.NAMESPACE_TENANT_VOLUMES,
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ const Affinity = ({
|
|||||||
}}
|
}}
|
||||||
selectorOptions={[
|
selectorOptions={[
|
||||||
{ label: "None", value: "none" },
|
{ label: "None", value: "none" },
|
||||||
{ label: "Default (Pod Anti-Affinnity)", value: "default" },
|
{ label: "Default (Pod Anti-Affinity)", value: "default" },
|
||||||
{ label: "Node Selector", value: "nodeSelector" },
|
{ label: "Node Selector", value: "nodeSelector" },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ export interface IPool {
|
|||||||
volumes: number;
|
volumes: number;
|
||||||
label?: string;
|
label?: string;
|
||||||
resources?: IResources;
|
resources?: IResources;
|
||||||
|
affinity?: IAffinityModel;
|
||||||
|
tolerations?: ITolerationModel[];
|
||||||
|
securityContext?: ISecurityContext | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPodListElement {
|
export interface IPodListElement {
|
||||||
@@ -253,3 +256,17 @@ export interface CapacityValue {
|
|||||||
label: string;
|
label: string;
|
||||||
color: string;
|
color: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IEditPoolItem {
|
||||||
|
name: string;
|
||||||
|
servers: number;
|
||||||
|
volumes_per_server: number;
|
||||||
|
volume_configuration: IVolumeConfiguration;
|
||||||
|
affinity?: IAffinityModel;
|
||||||
|
tolerations?: ITolerationModel[];
|
||||||
|
securityContext?: ISecurityContext | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEditPoolRequest {
|
||||||
|
pools: IEditPoolItem[]
|
||||||
|
}
|
||||||
@@ -21,38 +21,38 @@ import withStyles from "@mui/styles/withStyles";
|
|||||||
import {
|
import {
|
||||||
formFieldStyles,
|
formFieldStyles,
|
||||||
modalStyleUtils,
|
modalStyleUtils,
|
||||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import { generatePoolName, niceBytes } from "../../../../../common/utils";
|
import { generatePoolName, niceBytes } from "../../../../../../common/utils";
|
||||||
import { LinearProgress } from "@mui/material";
|
import { LinearProgress } from "@mui/material";
|
||||||
import { IAddPoolRequest, ITenant } from "../../ListTenants/types";
|
import { IAddPoolRequest, ITenant } from "../../../ListTenants/types";
|
||||||
import PageHeader from "../../../Common/PageHeader/PageHeader";
|
import PageHeader from "../../../../Common/PageHeader/PageHeader";
|
||||||
import PageLayout from "../../../Common/Layout/PageLayout";
|
import PageLayout from "../../../../Common/Layout/PageLayout";
|
||||||
import GenericWizard from "../../../Common/GenericWizard/GenericWizard";
|
import GenericWizard from "../../../../Common/GenericWizard/GenericWizard";
|
||||||
import { IWizardElement } from "../../../Common/GenericWizard/types";
|
import { IWizardElement } from "../../../../Common/GenericWizard/types";
|
||||||
import history from "../../../../../history";
|
import history from "../../../../../../history";
|
||||||
import PoolResources from "./PoolResources";
|
import PoolResources from "./PoolResources";
|
||||||
import ScreenTitle from "../../../Common/ScreenTitle/ScreenTitle";
|
import ScreenTitle from "../../../../Common/ScreenTitle/ScreenTitle";
|
||||||
import TenantsIcon from "../../../../../icons/TenantsIcon";
|
import TenantsIcon from "../../../../../../icons/TenantsIcon";
|
||||||
import {
|
import {
|
||||||
isPoolPageValid,
|
isPoolPageValid,
|
||||||
resetPoolForm,
|
resetPoolForm,
|
||||||
setPoolField,
|
setPoolField,
|
||||||
setTenantDetailsLoad,
|
setTenantDetailsLoad,
|
||||||
} from "../../actions";
|
} from "../../../actions";
|
||||||
import { AppState } from "../../../../../store";
|
import { AppState } from "../../../../../../store";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import PoolConfiguration from "./PoolConfiguration";
|
import PoolConfiguration from "./PoolConfiguration";
|
||||||
import PoolPodPlacement from "./PoolPodPlacement";
|
import PoolPodPlacement from "./PoolPodPlacement";
|
||||||
import {
|
import {
|
||||||
ErrorResponseHandler,
|
ErrorResponseHandler,
|
||||||
ITolerationModel,
|
ITolerationModel,
|
||||||
} from "../../../../../common/types";
|
} from "../../../../../../common/types";
|
||||||
import { getDefaultAffinity, getNodeSelector } from "../utils";
|
import { getDefaultAffinity, getNodeSelector } from "../../utils";
|
||||||
import api from "../../../../../common/api";
|
import api from "../../../../../../common/api";
|
||||||
import { ISecurityContext } from "../../types";
|
import { ISecurityContext } from "../../../types";
|
||||||
import BackLink from "../../../../../common/BackLink";
|
import BackLink from "../../../../../../common/BackLink";
|
||||||
import { setErrorSnackMessage } from "../../../../../actions";
|
import { setErrorSnackMessage } from "../../../../../../actions";
|
||||||
|
|
||||||
interface IAddPoolProps {
|
interface IAddPoolProps {
|
||||||
tenant: ITenant | null;
|
tenant: ITenant | null;
|
||||||
@@ -138,6 +138,7 @@ const AddPool = ({
|
|||||||
securityContext,
|
securityContext,
|
||||||
volumesPerServer,
|
volumesPerServer,
|
||||||
setTenantDetailsLoad,
|
setTenantDetailsLoad,
|
||||||
|
setErrorSnackMessage,
|
||||||
}: IAddPoolProps) => {
|
}: IAddPoolProps) => {
|
||||||
const [addSending, setAddSending] = useState<boolean>(false);
|
const [addSending, setAddSending] = useState<boolean>(false);
|
||||||
|
|
||||||
@@ -220,6 +221,7 @@ const AddPool = ({
|
|||||||
volumeSize,
|
volumeSize,
|
||||||
volumesPerServer,
|
volumesPerServer,
|
||||||
withPodAntiAffinity,
|
withPodAntiAffinity,
|
||||||
|
setErrorSnackMessage,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const cancelButton = {
|
const cancelButton = {
|
||||||
@@ -24,17 +24,17 @@ import {
|
|||||||
createTenantCommon,
|
createTenantCommon,
|
||||||
modalBasic,
|
modalBasic,
|
||||||
wizardCommon,
|
wizardCommon,
|
||||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
import { isPoolPageValid, setPoolField } from "../../actions";
|
import { isPoolPageValid, setPoolField } from "../../../actions";
|
||||||
import { AppState } from "../../../../../store";
|
import { AppState } from "../../../../../../store";
|
||||||
import { clearValidationError } from "../../utils";
|
import { clearValidationError } from "../../../utils";
|
||||||
import {
|
import {
|
||||||
commonFormValidation,
|
commonFormValidation,
|
||||||
IValidation,
|
IValidation,
|
||||||
} from "../../../../../utils/validationFunctions";
|
} from "../../../../../../utils/validationFunctions";
|
||||||
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||||
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import { ISecurityContext } from "../../types";
|
import { ISecurityContext } from "../../../types";
|
||||||
|
|
||||||
interface IConfigureProps {
|
interface IConfigureProps {
|
||||||
setPoolField: typeof setPoolField;
|
setPoolField: typeof setPoolField;
|
||||||
@@ -20,7 +20,7 @@ import { Theme } from "@mui/material/styles";
|
|||||||
import createStyles from "@mui/styles/createStyles";
|
import createStyles from "@mui/styles/createStyles";
|
||||||
import withStyles from "@mui/styles/withStyles";
|
import withStyles from "@mui/styles/withStyles";
|
||||||
import { Grid, IconButton, Paper, SelectChangeEvent } from "@mui/material";
|
import { Grid, IconButton, Paper, SelectChangeEvent } from "@mui/material";
|
||||||
import { AppState } from "../../../../../store";
|
import { AppState } from "../../../../../../store";
|
||||||
import {
|
import {
|
||||||
isPoolPageValid,
|
isPoolPageValid,
|
||||||
setPoolField,
|
setPoolField,
|
||||||
@@ -28,29 +28,29 @@ import {
|
|||||||
addNewPoolToleration,
|
addNewPoolToleration,
|
||||||
removePoolToleration,
|
removePoolToleration,
|
||||||
setPoolKeyValuePairs,
|
setPoolKeyValuePairs,
|
||||||
} from "../../actions";
|
} from "../../../actions";
|
||||||
import { setModalErrorSnackMessage } from "../../../../../actions";
|
import { setModalErrorSnackMessage } from "../../../../../../actions";
|
||||||
import {
|
import {
|
||||||
modalBasic,
|
modalBasic,
|
||||||
wizardCommon,
|
wizardCommon,
|
||||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
import {
|
import {
|
||||||
commonFormValidation,
|
commonFormValidation,
|
||||||
IValidation,
|
IValidation,
|
||||||
} from "../../../../../utils/validationFunctions";
|
} from "../../../../../../utils/validationFunctions";
|
||||||
import {
|
import {
|
||||||
ErrorResponseHandler,
|
ErrorResponseHandler,
|
||||||
ITolerationModel,
|
ITolerationModel,
|
||||||
} from "../../../../../common/types";
|
} from "../../../../../../common/types";
|
||||||
import { LabelKeyPair } from "../../types";
|
import { LabelKeyPair } from "../../../types";
|
||||||
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
import RadioGroupSelector from "../../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||||
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||||
import api from "../../../../../common/api";
|
import api from "../../../../../../common/api";
|
||||||
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import AddIcon from "../../../../../icons/AddIcon";
|
import AddIcon from "../../../../../../icons/AddIcon";
|
||||||
import RemoveIcon from "../../../../../icons/RemoveIcon";
|
import RemoveIcon from "../../../../../../icons/RemoveIcon";
|
||||||
import SelectWrapper from "../../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
import SelectWrapper from "../../../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||||
import TolerationSelector from "../../../Common/TolerationSelector/TolerationSelector";
|
import TolerationSelector from "../../../../Common/TolerationSelector/TolerationSelector";
|
||||||
|
|
||||||
interface IAffinityProps {
|
interface IAffinityProps {
|
||||||
classes: any;
|
classes: any;
|
||||||
@@ -277,7 +277,7 @@ const Affinity = ({
|
|||||||
}}
|
}}
|
||||||
selectorOptions={[
|
selectorOptions={[
|
||||||
{ label: "None", value: "none" },
|
{ label: "None", value: "none" },
|
||||||
{ label: "Default (Pod Anti-Affinnity)", value: "default" },
|
{ label: "Default (Pod Anti-Affinity)", value: "default" },
|
||||||
{ label: "Node Selector", value: "nodeSelector" },
|
{ label: "Node Selector", value: "nodeSelector" },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@@ -22,28 +22,28 @@ import withStyles from "@mui/styles/withStyles";
|
|||||||
import {
|
import {
|
||||||
formFieldStyles,
|
formFieldStyles,
|
||||||
wizardCommon,
|
wizardCommon,
|
||||||
} from "../../../Common/FormComponents/common/styleLibrary";
|
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import { niceBytes } from "../../../../../common/utils";
|
import { niceBytes } from "../../../../../../common/utils";
|
||||||
import { Paper, SelectChangeEvent } from "@mui/material";
|
import { Paper, SelectChangeEvent } from "@mui/material";
|
||||||
import api from "../../../../../common/api";
|
import api from "../../../../../../common/api";
|
||||||
import { ITenant } from "../../ListTenants/types";
|
import { ITenant } from "../../../ListTenants/types";
|
||||||
import { ErrorResponseHandler } from "../../../../../common/types";
|
import { ErrorResponseHandler } from "../../../../../../common/types";
|
||||||
import SelectWrapper from "../../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
import SelectWrapper from "../../../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||||
import { IQuotaElement, IQuotas, Opts } from "../../ListTenants/utils";
|
import { IQuotaElement, IQuotas, Opts } from "../../../ListTenants/utils";
|
||||||
import { AppState } from "../../../../../store";
|
import { AppState } from "../../../../../../store";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import {
|
import {
|
||||||
isPoolPageValid,
|
isPoolPageValid,
|
||||||
setPoolField,
|
setPoolField,
|
||||||
setPoolStorageClasses,
|
setPoolStorageClasses,
|
||||||
} from "../../actions";
|
} from "../../../actions";
|
||||||
import {
|
import {
|
||||||
commonFormValidation,
|
commonFormValidation,
|
||||||
IValidation,
|
IValidation,
|
||||||
} from "../../../../../utils/validationFunctions";
|
} from "../../../../../../utils/validationFunctions";
|
||||||
import InputUnitMenu from "../../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
|
import InputUnitMenu from "../../../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
|
||||||
|
|
||||||
interface IPoolResourcesProps {
|
interface IPoolResourcesProps {
|
||||||
tenant: ITenant | null;
|
tenant: ITenant | null;
|
||||||
@@ -34,13 +34,16 @@ import { ITenant } from "../../../ListTenants/types";
|
|||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import LabelValuePair from "../../../../Common/UsageBarWrapper/LabelValuePair";
|
import LabelValuePair from "../../../../Common/UsageBarWrapper/LabelValuePair";
|
||||||
import { niceBytesInt } from "../../../../../../common/utils";
|
import { niceBytesInt } from "../../../../../../common/utils";
|
||||||
|
import StackRow from "../../../../Common/UsageBarWrapper/StackRow";
|
||||||
|
import RBIconButton from "../../../../Buckets/BucketDetails/SummaryItems/RBIconButton";
|
||||||
|
import { EditTenantIcon } from "../../../../../../icons";
|
||||||
|
|
||||||
interface IPoolDetails {
|
interface IPoolDetails {
|
||||||
classes: any;
|
classes: any;
|
||||||
|
history: any;
|
||||||
loadingTenant: boolean;
|
loadingTenant: boolean;
|
||||||
tenant: ITenant | null;
|
tenant: ITenant | null;
|
||||||
selectedPool: string | null;
|
selectedPool: string | null;
|
||||||
closeDetailsView: () => void;
|
|
||||||
setTenantDetailsLoad: typeof setTenantDetailsLoad;
|
setTenantDetailsLoad: typeof setTenantDetailsLoad;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,18 +56,22 @@ const styles = (theme: Theme) =>
|
|||||||
...containerForHeader(theme.spacing(4)),
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const stylingLayout = {
|
||||||
|
border: "#EAEAEA 1px solid",
|
||||||
|
borderRadius: "3px",
|
||||||
|
padding: "0px 20px",
|
||||||
|
position: "relative",
|
||||||
|
};
|
||||||
|
|
||||||
const twoColCssGridLayoutConfig = {
|
const twoColCssGridLayoutConfig = {
|
||||||
display: "grid",
|
display: "grid",
|
||||||
gridTemplateColumns: { xs: "1fr", sm: "2fr 1fr" },
|
gridTemplateColumns: { xs: "1fr", sm: "2fr 1fr" },
|
||||||
gridAutoFlow: { xs: "dense", sm: "row" },
|
gridAutoFlow: { xs: "dense", sm: "row" },
|
||||||
gap: 2,
|
gap: 2,
|
||||||
|
padding: "15px",
|
||||||
};
|
};
|
||||||
|
|
||||||
const PoolDetails = ({
|
const PoolDetails = ({ tenant, selectedPool, history }: IPoolDetails) => {
|
||||||
closeDetailsView,
|
|
||||||
tenant,
|
|
||||||
selectedPool,
|
|
||||||
}: IPoolDetails) => {
|
|
||||||
const poolInformation =
|
const poolInformation =
|
||||||
tenant?.pools.find((pool) => pool.name === selectedPool) || null;
|
tenant?.pools.find((pool) => pool.name === selectedPool) || null;
|
||||||
|
|
||||||
@@ -72,17 +79,50 @@ const PoolDetails = ({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let affinityType = "None";
|
||||||
|
|
||||||
|
if (poolInformation.affinity) {
|
||||||
|
if (poolInformation.affinity.nodeAffinity) {
|
||||||
|
affinityType = "Node Selector";
|
||||||
|
} else {
|
||||||
|
affinityType = "Default (Pod Anti-Affinity)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const HeaderSection = ({ title }: { title: string }) => {
|
||||||
|
return (
|
||||||
|
<StackRow
|
||||||
|
sx={{
|
||||||
|
borderBottom: "1px solid #eaeaea",
|
||||||
|
margin: 0,
|
||||||
|
marginBottom: "20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3>{title}</h3>
|
||||||
|
</StackRow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12} sx={{ ...stylingLayout }}>
|
||||||
<Grid container>
|
<div style={{ position: "absolute", right: 20, top: 18 }}>
|
||||||
<Grid item xs={8}>
|
<RBIconButton
|
||||||
<h4>Pool Configuration</h4>
|
icon={<EditTenantIcon />}
|
||||||
</Grid>
|
onClick={() => {
|
||||||
<Grid item xs={4} />
|
history.push(
|
||||||
</Grid>
|
`/namespaces/${tenant?.namespace || ""}/tenants/${
|
||||||
|
tenant?.name || ""
|
||||||
|
}/edit-pool`
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
text={"Edit Pool"}
|
||||||
|
id={"editPool"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<HeaderSection title={"Pool Configuration"} />
|
||||||
<Box sx={{ ...twoColCssGridLayoutConfig }}>
|
<Box sx={{ ...twoColCssGridLayoutConfig }}>
|
||||||
<LabelValuePair label={"Pool Name"} value={poolInformation.label} />
|
<LabelValuePair label={"Pool Name"} value={poolInformation.name} />
|
||||||
<LabelValuePair
|
<LabelValuePair
|
||||||
label={"Total Volumes"}
|
label={"Total Volumes"}
|
||||||
value={poolInformation.volumes}
|
value={poolInformation.volumes}
|
||||||
@@ -93,12 +133,7 @@ const PoolDetails = ({
|
|||||||
/>
|
/>
|
||||||
<LabelValuePair label={"Capacity"} value={poolInformation.capacity} />
|
<LabelValuePair label={"Capacity"} value={poolInformation.capacity} />
|
||||||
</Box>
|
</Box>
|
||||||
<Grid container>
|
<HeaderSection title={"Resources"} />
|
||||||
<Grid item xs={8}>
|
|
||||||
<h4>Resources</h4>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4} />
|
|
||||||
</Grid>
|
|
||||||
<Box sx={{ ...twoColCssGridLayoutConfig }}>
|
<Box sx={{ ...twoColCssGridLayoutConfig }}>
|
||||||
{poolInformation.resources && (
|
{poolInformation.resources && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@@ -121,6 +156,123 @@ const PoolDetails = ({
|
|||||||
value={poolInformation.volume_configuration.storage_class_name}
|
value={poolInformation.volume_configuration.storage_class_name}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
{poolInformation.securityContext &&
|
||||||
|
(poolInformation.securityContext.runAsNonRoot ||
|
||||||
|
poolInformation.securityContext.runAsUser ||
|
||||||
|
poolInformation.securityContext.runAsGroup ||
|
||||||
|
poolInformation.securityContext.fsGroup) && (
|
||||||
|
<Fragment>
|
||||||
|
<HeaderSection title={"Security Context"} />
|
||||||
|
<Box>
|
||||||
|
{poolInformation.securityContext.runAsNonRoot !== null && (
|
||||||
|
<Box sx={{ ...twoColCssGridLayoutConfig }}>
|
||||||
|
<LabelValuePair
|
||||||
|
label={"Run as Non Root"}
|
||||||
|
value={
|
||||||
|
poolInformation.securityContext.runAsNonRoot
|
||||||
|
? "Yes"
|
||||||
|
: "No"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
...twoColCssGridLayoutConfig,
|
||||||
|
gridTemplateColumns: {
|
||||||
|
xs: "1fr",
|
||||||
|
sm: "2fr 1fr",
|
||||||
|
md: "1fr 1fr 1fr",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{poolInformation.securityContext.runAsUser && (
|
||||||
|
<LabelValuePair
|
||||||
|
label={"Run as User"}
|
||||||
|
value={poolInformation.securityContext.runAsUser}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{poolInformation.securityContext.runAsGroup && (
|
||||||
|
<LabelValuePair
|
||||||
|
label={"Run as Group"}
|
||||||
|
value={poolInformation.securityContext.runAsGroup}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{poolInformation.securityContext.fsGroup && (
|
||||||
|
<LabelValuePair
|
||||||
|
label={"FsGroup"}
|
||||||
|
value={poolInformation.securityContext.fsGroup}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
<HeaderSection title={"Affinity"} />
|
||||||
|
<Box>
|
||||||
|
<Box sx={{ ...twoColCssGridLayoutConfig }}>
|
||||||
|
<LabelValuePair label={"Type"} value={affinityType} />
|
||||||
|
{poolInformation.affinity?.nodeAffinity &&
|
||||||
|
poolInformation.affinity?.podAntiAffinity ? (
|
||||||
|
<LabelValuePair label={"With Pod Anti affinity"} value={"Yes"} />
|
||||||
|
) : (
|
||||||
|
<span />
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
{poolInformation.affinity?.nodeAffinity && (
|
||||||
|
<Fragment>
|
||||||
|
<HeaderSection title={"Labels"} />
|
||||||
|
<ul>
|
||||||
|
{poolInformation.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.map(
|
||||||
|
(term) => {
|
||||||
|
return term.matchExpressions.map((trm) => {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
{trm.key} - {trm.values.join(", ")}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
{poolInformation.tolerations && poolInformation.tolerations.length > 0 && (
|
||||||
|
<Fragment>
|
||||||
|
<HeaderSection title={"Tolerations"} />
|
||||||
|
<Box>
|
||||||
|
<ul>
|
||||||
|
{poolInformation.tolerations.map((tolItem) => {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
{tolItem.operator === "Equal" ? (
|
||||||
|
<Fragment>
|
||||||
|
If <strong>{tolItem.key}</strong> is equal to{" "}
|
||||||
|
<strong>{tolItem.value}</strong> then{" "}
|
||||||
|
<strong>{tolItem.effect}</strong> after{" "}
|
||||||
|
<strong>
|
||||||
|
{tolItem.tolerationSeconds?.seconds || 0}
|
||||||
|
</strong>{" "}
|
||||||
|
seconds
|
||||||
|
</Fragment>
|
||||||
|
) : (
|
||||||
|
<Fragment>
|
||||||
|
If <strong>{tolItem.key}</strong> exists then{" "}
|
||||||
|
<strong>{tolItem.effect}</strong> after{" "}
|
||||||
|
<strong>
|
||||||
|
{tolItem.tolerationSeconds?.seconds || 0}
|
||||||
|
</strong>{" "}
|
||||||
|
seconds
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</Box>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,390 @@
|
|||||||
|
// 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 { connect } from "react-redux";
|
||||||
|
import { Theme } from "@mui/material/styles";
|
||||||
|
import createStyles from "@mui/styles/createStyles";
|
||||||
|
import withStyles from "@mui/styles/withStyles";
|
||||||
|
import Grid from "@mui/material/Grid";
|
||||||
|
import PageHeader from "../../../../Common/PageHeader/PageHeader";
|
||||||
|
import PageLayout from "../../../../Common/Layout/PageLayout";
|
||||||
|
import GenericWizard from "../../../../Common/GenericWizard/GenericWizard";
|
||||||
|
import api from "../../../../../../common/api";
|
||||||
|
import ScreenTitle from "../../../../Common/ScreenTitle/ScreenTitle";
|
||||||
|
import TenantsIcon from "../../../../../../icons/TenantsIcon";
|
||||||
|
import BackLink from "../../../../../../common/BackLink";
|
||||||
|
import EditPoolResources from "./EditPoolResources";
|
||||||
|
import EditPoolConfiguration from "./EditPoolConfiguration";
|
||||||
|
import EditPoolPlacement from "./EditPoolPlacement";
|
||||||
|
import history from "../../../../../../history";
|
||||||
|
import { IWizardElement } from "../../../../Common/GenericWizard/types";
|
||||||
|
import { LinearProgress } from "@mui/material";
|
||||||
|
import { generatePoolName, niceBytes } from "../../../../../../common/utils";
|
||||||
|
import {
|
||||||
|
formFieldStyles,
|
||||||
|
modalStyleUtils,
|
||||||
|
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import {
|
||||||
|
IEditPoolItem,
|
||||||
|
IEditPoolRequest,
|
||||||
|
ITenant,
|
||||||
|
} from "../../../ListTenants/types";
|
||||||
|
import {
|
||||||
|
isPoolPageValid,
|
||||||
|
resetPoolForm,
|
||||||
|
setInitialPoolDetails,
|
||||||
|
setPoolField,
|
||||||
|
setTenantDetailsLoad,
|
||||||
|
} from "../../../actions";
|
||||||
|
import { AppState } from "../../../../../../store";
|
||||||
|
import {
|
||||||
|
ErrorResponseHandler,
|
||||||
|
ITolerationModel,
|
||||||
|
} from "../../../../../../common/types";
|
||||||
|
import { getDefaultAffinity, getNodeSelector } from "../../utils";
|
||||||
|
import { ISecurityContext } from "../../../types";
|
||||||
|
import { setErrorSnackMessage } from "../../../../../../actions";
|
||||||
|
|
||||||
|
interface IEditPoolProps {
|
||||||
|
tenant: ITenant | null;
|
||||||
|
classes: any;
|
||||||
|
open: boolean;
|
||||||
|
match: any;
|
||||||
|
selectedStorageClass: string;
|
||||||
|
selectedPool: string | null;
|
||||||
|
validPages: string[];
|
||||||
|
numberOfNodes: number;
|
||||||
|
volumeSize: number;
|
||||||
|
volumesPerServer: number;
|
||||||
|
affinityType: string;
|
||||||
|
nodeSelectorLabels: string;
|
||||||
|
withPodAntiAffinity: boolean;
|
||||||
|
securityContextEnabled: boolean;
|
||||||
|
tolerations: ITolerationModel[];
|
||||||
|
securityContext: ISecurityContext;
|
||||||
|
resetPoolForm: typeof resetPoolForm;
|
||||||
|
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||||
|
setTenantDetailsLoad: typeof setTenantDetailsLoad;
|
||||||
|
setInitialPoolDetails: typeof setInitialPoolDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
buttonContainer: {
|
||||||
|
textAlign: "right",
|
||||||
|
},
|
||||||
|
bottomContainer: {
|
||||||
|
display: "flex",
|
||||||
|
flexGrow: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
margin: "auto",
|
||||||
|
justifyContent: "center",
|
||||||
|
"& div": {
|
||||||
|
width: 150,
|
||||||
|
"@media (max-width: 900px)": {
|
||||||
|
flexFlow: "column",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
factorElements: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
marginLeft: 30,
|
||||||
|
},
|
||||||
|
sizeNumber: {
|
||||||
|
fontSize: 35,
|
||||||
|
fontWeight: 700,
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
sizeDescription: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#777",
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
pageBox: {
|
||||||
|
border: "1px solid #EAEAEA",
|
||||||
|
borderTop: 0,
|
||||||
|
},
|
||||||
|
editPoolTitle: {
|
||||||
|
border: "1px solid #EAEAEA",
|
||||||
|
borderBottom: 0,
|
||||||
|
},
|
||||||
|
...formFieldStyles,
|
||||||
|
...modalStyleUtils,
|
||||||
|
});
|
||||||
|
|
||||||
|
const requiredPages = ["setup", "affinity", "configure"];
|
||||||
|
|
||||||
|
const EditPool = ({
|
||||||
|
tenant,
|
||||||
|
classes,
|
||||||
|
resetPoolForm,
|
||||||
|
selectedPool,
|
||||||
|
selectedStorageClass,
|
||||||
|
validPages,
|
||||||
|
numberOfNodes,
|
||||||
|
volumeSize,
|
||||||
|
affinityType,
|
||||||
|
nodeSelectorLabels,
|
||||||
|
withPodAntiAffinity,
|
||||||
|
tolerations,
|
||||||
|
securityContextEnabled,
|
||||||
|
securityContext,
|
||||||
|
volumesPerServer,
|
||||||
|
setTenantDetailsLoad,
|
||||||
|
setInitialPoolDetails,
|
||||||
|
setErrorSnackMessage,
|
||||||
|
}: IEditPoolProps) => {
|
||||||
|
const [editSending, setEditSending] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const poolsURL = `/namespaces/${tenant?.namespace || ""}/tenants/${
|
||||||
|
tenant?.name || ""
|
||||||
|
}/pools`;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedPool) {
|
||||||
|
const poolDetails = tenant?.pools.find(
|
||||||
|
(pool) => pool.name === selectedPool
|
||||||
|
);
|
||||||
|
|
||||||
|
if (poolDetails) {
|
||||||
|
setInitialPoolDetails(poolDetails);
|
||||||
|
} else {
|
||||||
|
history.push("/tenants");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [selectedPool, setInitialPoolDetails, tenant]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editSending && tenant) {
|
||||||
|
const poolName = generatePoolName(tenant.pools);
|
||||||
|
|
||||||
|
let affinityObject = {};
|
||||||
|
|
||||||
|
switch (affinityType) {
|
||||||
|
case "default":
|
||||||
|
affinityObject = {
|
||||||
|
affinity: getDefaultAffinity(tenant.name, poolName),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "nodeSelector":
|
||||||
|
affinityObject = {
|
||||||
|
affinity: getNodeSelector(
|
||||||
|
nodeSelectorLabels,
|
||||||
|
withPodAntiAffinity,
|
||||||
|
tenant.name,
|
||||||
|
poolName
|
||||||
|
),
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tolerationValues = tolerations.filter(
|
||||||
|
(toleration) => toleration.key.trim() !== ""
|
||||||
|
);
|
||||||
|
|
||||||
|
const cleanPools = tenant.pools
|
||||||
|
.filter((pool) => pool.name !== selectedPool)
|
||||||
|
.map((pool) => {
|
||||||
|
let securityContextOption = null;
|
||||||
|
|
||||||
|
if (pool.securityContext) {
|
||||||
|
if (
|
||||||
|
!!pool.securityContext.runAsUser ||
|
||||||
|
!!pool.securityContext.runAsGroup ||
|
||||||
|
!!pool.securityContext.fsGroup
|
||||||
|
) {
|
||||||
|
securityContextOption = { ...pool.securityContext };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const request: IEditPoolItem = {
|
||||||
|
...pool,
|
||||||
|
securityContext: securityContextOption,
|
||||||
|
};
|
||||||
|
|
||||||
|
return request;
|
||||||
|
});
|
||||||
|
|
||||||
|
const data: IEditPoolRequest = {
|
||||||
|
pools: [
|
||||||
|
...cleanPools,
|
||||||
|
{
|
||||||
|
name: selectedPool || poolName,
|
||||||
|
servers: numberOfNodes,
|
||||||
|
volumes_per_server: volumesPerServer,
|
||||||
|
volume_configuration: {
|
||||||
|
size: volumeSize * 1073741824,
|
||||||
|
storage_class_name: selectedStorageClass,
|
||||||
|
labels: null,
|
||||||
|
},
|
||||||
|
tolerations: tolerationValues,
|
||||||
|
securityContext: securityContextEnabled ? securityContext : null,
|
||||||
|
...affinityObject,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
api
|
||||||
|
.invoke(
|
||||||
|
"PUT",
|
||||||
|
`/api/v1/namespaces/${tenant.namespace}/tenants/${tenant.name}/pools`,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
setEditSending(false);
|
||||||
|
resetPoolForm();
|
||||||
|
setTenantDetailsLoad(true);
|
||||||
|
history.push(poolsURL);
|
||||||
|
})
|
||||||
|
.catch((err: ErrorResponseHandler) => {
|
||||||
|
setEditSending(false);
|
||||||
|
setErrorSnackMessage(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
selectedPool,
|
||||||
|
setErrorSnackMessage,
|
||||||
|
editSending,
|
||||||
|
poolsURL,
|
||||||
|
resetPoolForm,
|
||||||
|
setTenantDetailsLoad,
|
||||||
|
affinityType,
|
||||||
|
nodeSelectorLabels,
|
||||||
|
numberOfNodes,
|
||||||
|
securityContext,
|
||||||
|
securityContextEnabled,
|
||||||
|
selectedStorageClass,
|
||||||
|
tenant,
|
||||||
|
tolerations,
|
||||||
|
volumeSize,
|
||||||
|
volumesPerServer,
|
||||||
|
withPodAntiAffinity,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const cancelButton = {
|
||||||
|
label: "Cancel",
|
||||||
|
type: "other",
|
||||||
|
enabled: true,
|
||||||
|
action: () => {
|
||||||
|
resetPoolForm();
|
||||||
|
history.push(poolsURL);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const createButton = {
|
||||||
|
label: "Update",
|
||||||
|
type: "submit",
|
||||||
|
enabled:
|
||||||
|
!editSending &&
|
||||||
|
selectedStorageClass !== "" &&
|
||||||
|
requiredPages.every((v) => validPages.includes(v)),
|
||||||
|
action: () => {
|
||||||
|
setEditSending(true);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const wizardSteps: IWizardElement[] = [
|
||||||
|
{
|
||||||
|
label: "Pool Resources",
|
||||||
|
componentRender: <EditPoolResources />,
|
||||||
|
buttons: [cancelButton, createButton],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Configuration",
|
||||||
|
advancedOnly: true,
|
||||||
|
componentRender: <EditPoolConfiguration />,
|
||||||
|
buttons: [cancelButton, createButton],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Pod Placement",
|
||||||
|
advancedOnly: true,
|
||||||
|
componentRender: <EditPoolPlacement />,
|
||||||
|
buttons: [cancelButton, createButton],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<PageHeader
|
||||||
|
label={
|
||||||
|
<Fragment>
|
||||||
|
<BackLink to={poolsURL} label={`Pool Details`} />
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<PageLayout>
|
||||||
|
<Grid item xs={12} className={classes.editPoolTitle}>
|
||||||
|
<ScreenTitle
|
||||||
|
icon={<TenantsIcon />}
|
||||||
|
title={`Edit Pool - ${selectedPool}`}
|
||||||
|
subTitle={
|
||||||
|
<Fragment>
|
||||||
|
Namespace: {tenant?.namespace || ""} / Current Capacity:{" "}
|
||||||
|
{niceBytes((tenant?.total_size || 0).toString(10))} / Tenant:{" "}
|
||||||
|
{tenant?.name || ""}
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{editSending && (
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<LinearProgress />
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
<Grid item xs={12} className={classes.pageBox}>
|
||||||
|
<GenericWizard wizardSteps={wizardSteps} />
|
||||||
|
</Grid>
|
||||||
|
</PageLayout>
|
||||||
|
</Grid>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapState = (state: AppState) => {
|
||||||
|
const editPool = state.tenants.editPool;
|
||||||
|
return {
|
||||||
|
tenant: state.tenants.tenantDetails.tenantInfo,
|
||||||
|
selectedPool: state.tenants.tenantDetails.selectedPool,
|
||||||
|
selectedStorageClass: editPool.fields.setup.storageClass,
|
||||||
|
validPages: editPool.validPages,
|
||||||
|
storageClasses: editPool.storageClasses,
|
||||||
|
numberOfNodes: editPool.fields.setup.numberOfNodes,
|
||||||
|
volumeSize: editPool.fields.setup.volumeSize,
|
||||||
|
volumesPerServer: editPool.fields.setup.volumesPerServer,
|
||||||
|
affinityType: editPool.fields.affinity.podAffinity,
|
||||||
|
nodeSelectorLabels: editPool.fields.affinity.nodeSelectorLabels,
|
||||||
|
withPodAntiAffinity: editPool.fields.affinity.withPodAntiAffinity,
|
||||||
|
tolerations: editPool.fields.tolerations,
|
||||||
|
securityContextEnabled:
|
||||||
|
editPool.fields.configuration.securityContextEnabled,
|
||||||
|
securityContext: editPool.fields.configuration.securityContext,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(mapState, {
|
||||||
|
resetPoolForm,
|
||||||
|
setPoolField,
|
||||||
|
isPoolPageValid,
|
||||||
|
setErrorSnackMessage,
|
||||||
|
setTenantDetailsLoad,
|
||||||
|
setInitialPoolDetails,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withStyles(styles)(connector(EditPool));
|
||||||
@@ -0,0 +1,289 @@
|
|||||||
|
// 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, { useCallback, useEffect, useState } from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { Theme } from "@mui/material/styles";
|
||||||
|
import createStyles from "@mui/styles/createStyles";
|
||||||
|
import withStyles from "@mui/styles/withStyles";
|
||||||
|
import { Grid, Paper } from "@mui/material";
|
||||||
|
import {
|
||||||
|
createTenantCommon,
|
||||||
|
modalBasic,
|
||||||
|
wizardCommon,
|
||||||
|
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import { isEditPoolPageValid, setEditPoolField } from "../../../actions";
|
||||||
|
import { AppState } from "../../../../../../store";
|
||||||
|
import { clearValidationError } from "../../../utils";
|
||||||
|
import {
|
||||||
|
commonFormValidation,
|
||||||
|
IValidation,
|
||||||
|
} from "../../../../../../utils/validationFunctions";
|
||||||
|
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||||
|
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
|
import { ISecurityContext } from "../../../types";
|
||||||
|
|
||||||
|
interface IConfigureProps {
|
||||||
|
setEditPoolField: typeof setEditPoolField;
|
||||||
|
isEditPoolPageValid: typeof isEditPoolPageValid;
|
||||||
|
classes: any;
|
||||||
|
securityContextEnabled: boolean;
|
||||||
|
securityContext: ISecurityContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
configSectionItem: {
|
||||||
|
marginRight: 15,
|
||||||
|
|
||||||
|
"& .multiContainer": {
|
||||||
|
border: "1px solid red",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tenantCustomizationFields: {
|
||||||
|
marginLeft: 30, // 2nd Level(15+15)
|
||||||
|
width: "88%",
|
||||||
|
margin: "auto",
|
||||||
|
},
|
||||||
|
containerItem: {
|
||||||
|
marginRight: 15,
|
||||||
|
},
|
||||||
|
fieldGroup: {
|
||||||
|
...createTenantCommon.fieldGroup,
|
||||||
|
paddingTop: 15,
|
||||||
|
marginBottom: 25,
|
||||||
|
},
|
||||||
|
responsiveSectionItem: {
|
||||||
|
"@media (max-width: 900px)": {
|
||||||
|
flexFlow: "column",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
|
||||||
|
"& div > div": {
|
||||||
|
marginBottom: 5,
|
||||||
|
marginRight: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
fieldSpaceTop: {
|
||||||
|
marginTop: 15,
|
||||||
|
},
|
||||||
|
|
||||||
|
...modalBasic,
|
||||||
|
...wizardCommon,
|
||||||
|
});
|
||||||
|
|
||||||
|
const PoolConfiguration = ({
|
||||||
|
classes,
|
||||||
|
setEditPoolField,
|
||||||
|
securityContextEnabled,
|
||||||
|
isEditPoolPageValid,
|
||||||
|
securityContext,
|
||||||
|
}: IConfigureProps) => {
|
||||||
|
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||||
|
|
||||||
|
// Common
|
||||||
|
const updateField = useCallback(
|
||||||
|
(field: string, value: any) => {
|
||||||
|
setEditPoolField("configuration", field, value);
|
||||||
|
},
|
||||||
|
[setEditPoolField]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
useEffect(() => {
|
||||||
|
let customAccountValidation: IValidation[] = [];
|
||||||
|
if (securityContextEnabled) {
|
||||||
|
customAccountValidation = [
|
||||||
|
...customAccountValidation,
|
||||||
|
{
|
||||||
|
fieldKey: "pool_securityContext_runAsUser",
|
||||||
|
required: true,
|
||||||
|
value: securityContext.runAsUser,
|
||||||
|
customValidation:
|
||||||
|
securityContext.runAsUser === "" ||
|
||||||
|
parseInt(securityContext.runAsUser) < 0,
|
||||||
|
customValidationMessage: `runAsUser must be present and be 0 or more`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldKey: "pool_securityContext_runAsGroup",
|
||||||
|
required: true,
|
||||||
|
value: securityContext.runAsGroup,
|
||||||
|
customValidation:
|
||||||
|
securityContext.runAsGroup === "" ||
|
||||||
|
parseInt(securityContext.runAsGroup) < 0,
|
||||||
|
customValidationMessage: `runAsGroup must be present and be 0 or more`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldKey: "pool_securityContext_fsGroup",
|
||||||
|
required: true,
|
||||||
|
value: securityContext.fsGroup,
|
||||||
|
customValidation:
|
||||||
|
securityContext.fsGroup === "" ||
|
||||||
|
parseInt(securityContext.fsGroup) < 0,
|
||||||
|
customValidationMessage: `fsGroup must be present and be 0 or more`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonVal = commonFormValidation(customAccountValidation);
|
||||||
|
|
||||||
|
isEditPoolPageValid("configure", Object.keys(commonVal).length === 0);
|
||||||
|
|
||||||
|
setValidationErrors(commonVal);
|
||||||
|
}, [isEditPoolPageValid, securityContextEnabled, securityContext]);
|
||||||
|
|
||||||
|
const cleanValidation = (fieldName: string) => {
|
||||||
|
setValidationErrors(clearValidationError(validationErrors, fieldName));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper className={classes.paperWrapper}>
|
||||||
|
<div className={classes.headerElement}>
|
||||||
|
<h3 className={classes.h3Section}>Configure</h3>
|
||||||
|
</div>
|
||||||
|
<Grid item xs={12} className={classes.configSectionItem}>
|
||||||
|
<FormSwitchWrapper
|
||||||
|
value="tenantConfig"
|
||||||
|
id="pool_configuration"
|
||||||
|
name="pool_configuration"
|
||||||
|
checked={securityContextEnabled}
|
||||||
|
onChange={(e) => {
|
||||||
|
const targetD = e.target;
|
||||||
|
const checked = targetD.checked;
|
||||||
|
|
||||||
|
updateField("securityContextEnabled", checked);
|
||||||
|
}}
|
||||||
|
label={"Security Context"}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
{securityContextEnabled && (
|
||||||
|
<Grid item xs={12} className={classes.tenantCustomizationFields}>
|
||||||
|
<fieldset className={classes.fieldGroup}>
|
||||||
|
<legend className={classes.descriptionText}>
|
||||||
|
Pool's Security Context
|
||||||
|
</legend>
|
||||||
|
<Grid item xs={12} className={`${classes.configSectionItem}`}>
|
||||||
|
<div
|
||||||
|
className={`${classes.multiContainer} ${classes.responsiveSectionItem}`}
|
||||||
|
>
|
||||||
|
<div className={classes.containerItem}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
type="number"
|
||||||
|
id="pool_securityContext_runAsUser"
|
||||||
|
name="pool_securityContext_runAsUser"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
updateField("securityContext", {
|
||||||
|
...securityContext,
|
||||||
|
runAsUser: e.target.value,
|
||||||
|
});
|
||||||
|
cleanValidation("pool_securityContext_runAsUser");
|
||||||
|
}}
|
||||||
|
label="Run As User"
|
||||||
|
value={securityContext.runAsUser}
|
||||||
|
required
|
||||||
|
error={
|
||||||
|
validationErrors["pool_securityContext_runAsUser"] || ""
|
||||||
|
}
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={classes.containerItem}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
type="number"
|
||||||
|
id="pool_securityContext_runAsGroup"
|
||||||
|
name="pool_securityContext_runAsGroup"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
updateField("securityContext", {
|
||||||
|
...securityContext,
|
||||||
|
runAsGroup: e.target.value,
|
||||||
|
});
|
||||||
|
cleanValidation("pool_securityContext_runAsGroup");
|
||||||
|
}}
|
||||||
|
label="Run As Group"
|
||||||
|
value={securityContext.runAsGroup}
|
||||||
|
required
|
||||||
|
error={
|
||||||
|
validationErrors["pool_securityContext_runAsGroup"] || ""
|
||||||
|
}
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={classes.containerItem}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
type="number"
|
||||||
|
id="pool_securityContext_fsGroup"
|
||||||
|
name="pool_securityContext_fsGroup"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
updateField("securityContext", {
|
||||||
|
...securityContext,
|
||||||
|
fsGroup: e.target.value,
|
||||||
|
});
|
||||||
|
cleanValidation("pool_securityContext_fsGroup");
|
||||||
|
}}
|
||||||
|
label="FsGroup"
|
||||||
|
value={securityContext.fsGroup}
|
||||||
|
required
|
||||||
|
error={
|
||||||
|
validationErrors["pool_securityContext_fsGroup"] || ""
|
||||||
|
}
|
||||||
|
min="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
<br />
|
||||||
|
<Grid item xs={12} className={classes.configSectionItem}>
|
||||||
|
<div className={classes.multiContainer}>
|
||||||
|
<FormSwitchWrapper
|
||||||
|
value="securityContextRunAsNonRoot"
|
||||||
|
id="pool_securityContext_runAsNonRoot"
|
||||||
|
name="pool_securityContext_runAsNonRoot"
|
||||||
|
checked={securityContext.runAsNonRoot}
|
||||||
|
onChange={(e) => {
|
||||||
|
const targetD = e.target;
|
||||||
|
const checked = targetD.checked;
|
||||||
|
updateField("securityContext", {
|
||||||
|
...securityContext,
|
||||||
|
runAsNonRoot: checked,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
label={"Do not run as Root"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
</fieldset>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapState = (state: AppState) => {
|
||||||
|
const configuration = state.tenants.editPool.fields.configuration;
|
||||||
|
|
||||||
|
return {
|
||||||
|
securityContextEnabled: configuration.securityContextEnabled,
|
||||||
|
securityContext: configuration.securityContext,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(mapState, {
|
||||||
|
setEditPoolField,
|
||||||
|
isEditPoolPageValid,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withStyles(styles)(connector(PoolConfiguration));
|
||||||
@@ -0,0 +1,534 @@
|
|||||||
|
// 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, useCallback, useEffect, useState } from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { Theme } from "@mui/material/styles";
|
||||||
|
import createStyles from "@mui/styles/createStyles";
|
||||||
|
import withStyles from "@mui/styles/withStyles";
|
||||||
|
import { Grid, IconButton, Paper, SelectChangeEvent } from "@mui/material";
|
||||||
|
import { AppState } from "../../../../../../store";
|
||||||
|
import {
|
||||||
|
setEditPoolField,
|
||||||
|
isEditPoolPageValid,
|
||||||
|
setEditPoolKeyValuePairs,
|
||||||
|
setEditPoolTolerationInfo,
|
||||||
|
addNewEditPoolToleration,
|
||||||
|
removeEditPoolToleration,
|
||||||
|
} from "../../../actions";
|
||||||
|
import { setModalErrorSnackMessage } from "../../../../../../actions";
|
||||||
|
import {
|
||||||
|
modalBasic,
|
||||||
|
wizardCommon,
|
||||||
|
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import {
|
||||||
|
commonFormValidation,
|
||||||
|
IValidation,
|
||||||
|
} from "../../../../../../utils/validationFunctions";
|
||||||
|
import {
|
||||||
|
ErrorResponseHandler,
|
||||||
|
ITolerationModel,
|
||||||
|
} from "../../../../../../common/types";
|
||||||
|
import { LabelKeyPair } from "../../../types";
|
||||||
|
import RadioGroupSelector from "../../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||||
|
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||||
|
import api from "../../../../../../common/api";
|
||||||
|
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
|
import AddIcon from "../../../../../../icons/AddIcon";
|
||||||
|
import RemoveIcon from "../../../../../../icons/RemoveIcon";
|
||||||
|
import SelectWrapper from "../../../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||||
|
import TolerationSelector from "../../../../Common/TolerationSelector/TolerationSelector";
|
||||||
|
|
||||||
|
interface IAffinityProps {
|
||||||
|
classes: any;
|
||||||
|
podAffinity: string;
|
||||||
|
nodeSelectorLabels: string;
|
||||||
|
withPodAntiAffinity: boolean;
|
||||||
|
keyValuePairs: LabelKeyPair[];
|
||||||
|
tolerations: ITolerationModel[];
|
||||||
|
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
|
||||||
|
setEditPoolField: typeof setEditPoolField;
|
||||||
|
isEditPoolPageValid: typeof isEditPoolPageValid;
|
||||||
|
setEditPoolKeyValuePairs: typeof setEditPoolKeyValuePairs;
|
||||||
|
setEditPoolTolerationInfo: typeof setEditPoolTolerationInfo;
|
||||||
|
removeEditPoolToleration: typeof removeEditPoolToleration;
|
||||||
|
addNewEditPoolToleration: typeof addNewEditPoolToleration;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
overlayAction: {
|
||||||
|
marginLeft: 10,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
"& svg": {
|
||||||
|
maxWidth: 15,
|
||||||
|
maxHeight: 15,
|
||||||
|
},
|
||||||
|
"& button": {
|
||||||
|
background: "#EAEAEA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
affinityConfigField: {
|
||||||
|
display: "flex",
|
||||||
|
},
|
||||||
|
affinityFieldLabel: {
|
||||||
|
display: "flex",
|
||||||
|
flexFlow: "column",
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
radioField: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
marginTop: 10,
|
||||||
|
"& div:first-child": {
|
||||||
|
display: "flex",
|
||||||
|
flexFlow: "column",
|
||||||
|
alignItems: "baseline",
|
||||||
|
textAlign: "left !important",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
affinityLabelKey: {
|
||||||
|
"& div:first-child": {
|
||||||
|
marginBottom: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
affinityLabelValue: {
|
||||||
|
marginLeft: 10,
|
||||||
|
"& div:first-child": {
|
||||||
|
marginBottom: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowActions: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
fieldContainer: {
|
||||||
|
marginBottom: 0,
|
||||||
|
},
|
||||||
|
affinityRow: {
|
||||||
|
marginBottom: 10,
|
||||||
|
display: "flex",
|
||||||
|
},
|
||||||
|
...modalBasic,
|
||||||
|
...wizardCommon,
|
||||||
|
});
|
||||||
|
|
||||||
|
interface OptionPair {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Affinity = ({
|
||||||
|
classes,
|
||||||
|
podAffinity,
|
||||||
|
nodeSelectorLabels,
|
||||||
|
withPodAntiAffinity,
|
||||||
|
setModalErrorSnackMessage,
|
||||||
|
keyValuePairs,
|
||||||
|
setEditPoolField,
|
||||||
|
isEditPoolPageValid,
|
||||||
|
setEditPoolKeyValuePairs,
|
||||||
|
setEditPoolTolerationInfo,
|
||||||
|
addNewEditPoolToleration,
|
||||||
|
removeEditPoolToleration,
|
||||||
|
tolerations,
|
||||||
|
}: IAffinityProps) => {
|
||||||
|
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||||
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
const [keyValueMap, setKeyValueMap] = useState<{ [key: string]: string[] }>(
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const [keyOptions, setKeyOptions] = useState<OptionPair[]>([]);
|
||||||
|
|
||||||
|
// Common
|
||||||
|
const updateField = useCallback(
|
||||||
|
(field: string, value: any) => {
|
||||||
|
setEditPoolField("affinity", field, value);
|
||||||
|
},
|
||||||
|
[setEditPoolField]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (loading) {
|
||||||
|
api
|
||||||
|
.invoke("GET", `/api/v1/nodes/labels`)
|
||||||
|
.then((res: { [key: string]: string[] }) => {
|
||||||
|
setLoading(false);
|
||||||
|
setKeyValueMap(res);
|
||||||
|
let keys: OptionPair[] = [];
|
||||||
|
for (let k in res) {
|
||||||
|
keys.push({
|
||||||
|
label: k,
|
||||||
|
value: k,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setKeyOptions(keys);
|
||||||
|
})
|
||||||
|
.catch((err: ErrorResponseHandler) => {
|
||||||
|
setLoading(false);
|
||||||
|
setModalErrorSnackMessage(err);
|
||||||
|
setKeyValueMap({});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [setModalErrorSnackMessage, loading]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (keyValuePairs) {
|
||||||
|
const vlr = keyValuePairs
|
||||||
|
.filter((kvp) => kvp.key !== "")
|
||||||
|
.map((kvp) => `${kvp.key}=${kvp.value}`)
|
||||||
|
.filter((kvs, i, a) => a.indexOf(kvs) === i);
|
||||||
|
const vl = vlr.join("&");
|
||||||
|
updateField("nodeSelectorLabels", vl);
|
||||||
|
}
|
||||||
|
}, [keyValuePairs, updateField]);
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
useEffect(() => {
|
||||||
|
let customAccountValidation: IValidation[] = [];
|
||||||
|
|
||||||
|
if (podAffinity === "nodeSelector") {
|
||||||
|
let valid = true;
|
||||||
|
|
||||||
|
const splittedLabels = nodeSelectorLabels.split("&");
|
||||||
|
|
||||||
|
if (splittedLabels.length === 1 && splittedLabels[0] === "") {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
splittedLabels.forEach((item: string, index: number) => {
|
||||||
|
const splitItem = item.split("=");
|
||||||
|
|
||||||
|
if (splitItem.length !== 2) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index + 1 !== splittedLabels.length) {
|
||||||
|
if (splitItem[0] === "" || splitItem[1] === "") {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
customAccountValidation = [
|
||||||
|
...customAccountValidation,
|
||||||
|
{
|
||||||
|
fieldKey: "labels",
|
||||||
|
required: true,
|
||||||
|
value: nodeSelectorLabels,
|
||||||
|
customValidation: !valid,
|
||||||
|
customValidationMessage:
|
||||||
|
"You need to add at least one label key-pair",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonVal = commonFormValidation(customAccountValidation);
|
||||||
|
|
||||||
|
isEditPoolPageValid("affinity", Object.keys(commonVal).length === 0);
|
||||||
|
|
||||||
|
setValidationErrors(commonVal);
|
||||||
|
}, [isEditPoolPageValid, podAffinity, nodeSelectorLabels]);
|
||||||
|
|
||||||
|
const updateToleration = (index: number, field: string, value: any) => {
|
||||||
|
const alterToleration = { ...tolerations[index], [field]: value };
|
||||||
|
|
||||||
|
setEditPoolTolerationInfo(index, alterToleration);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper className={classes.paperWrapper}>
|
||||||
|
<div className={classes.headerElement}>
|
||||||
|
<h3 className={classes.h3Section}>Pod Placement</h3>
|
||||||
|
</div>
|
||||||
|
<Grid item xs={12} className={classes.affinityConfigField}>
|
||||||
|
<Grid item className={classes.affinityFieldLabel}>
|
||||||
|
<div className={classes.label}>Type</div>
|
||||||
|
<div
|
||||||
|
className={`${classes.descriptionText} ${classes.affinityHelpText}`}
|
||||||
|
>
|
||||||
|
MinIO supports multiple configurations for Pod Affinity
|
||||||
|
</div>
|
||||||
|
<Grid item className={classes.radioField}>
|
||||||
|
<RadioGroupSelector
|
||||||
|
currentSelection={podAffinity}
|
||||||
|
id="affinity-options"
|
||||||
|
name="affinity-options"
|
||||||
|
label={" "}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateField("podAffinity", e.target.value);
|
||||||
|
}}
|
||||||
|
selectorOptions={[
|
||||||
|
{ label: "None", value: "none" },
|
||||||
|
{ label: "Default (Pod Anti-Affinity)", value: "default" },
|
||||||
|
{ label: "Node Selector", value: "nodeSelector" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
{podAffinity === "nodeSelector" && (
|
||||||
|
<Fragment>
|
||||||
|
<br />
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<FormSwitchWrapper
|
||||||
|
value="with_pod_anti_affinity"
|
||||||
|
id="with_pod_anti_affinity"
|
||||||
|
name="with_pod_anti_affinity"
|
||||||
|
checked={withPodAntiAffinity}
|
||||||
|
onChange={(e) => {
|
||||||
|
const targetD = e.target;
|
||||||
|
const checked = targetD.checked;
|
||||||
|
|
||||||
|
updateField("withPodAntiAffinity", checked);
|
||||||
|
}}
|
||||||
|
label={"With Pod Anti-Affinity"}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<h3>Labels</h3>
|
||||||
|
<span className={classes.error}>{validationErrors["labels"]}</span>
|
||||||
|
<Grid container>
|
||||||
|
{keyValuePairs &&
|
||||||
|
keyValuePairs.map((kvp, i) => {
|
||||||
|
return (
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={12}
|
||||||
|
className={classes.affinityRow}
|
||||||
|
key={`affinity-keyVal-${i.toString()}`}
|
||||||
|
>
|
||||||
|
<Grid item xs={5} className={classes.affinityLabelKey}>
|
||||||
|
{keyOptions.length > 0 && (
|
||||||
|
<SelectWrapper
|
||||||
|
onChange={(e: SelectChangeEvent<string>) => {
|
||||||
|
const newKey = e.target.value as string;
|
||||||
|
const arrCp: LabelKeyPair[] = Object.assign(
|
||||||
|
[],
|
||||||
|
keyValuePairs
|
||||||
|
);
|
||||||
|
|
||||||
|
arrCp[i].key = e.target.value as string;
|
||||||
|
arrCp[i].value = keyValueMap[newKey][0];
|
||||||
|
setEditPoolKeyValuePairs(arrCp);
|
||||||
|
}}
|
||||||
|
id="select-access-policy"
|
||||||
|
name="select-access-policy"
|
||||||
|
label={""}
|
||||||
|
value={kvp.key}
|
||||||
|
options={keyOptions}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{keyOptions.length === 0 && (
|
||||||
|
<InputBoxWrapper
|
||||||
|
id={`nodeselector-key-${i.toString()}`}
|
||||||
|
label={""}
|
||||||
|
name={`nodeselector-${i.toString()}`}
|
||||||
|
value={kvp.key}
|
||||||
|
onChange={(e) => {
|
||||||
|
const arrCp: LabelKeyPair[] = Object.assign(
|
||||||
|
[],
|
||||||
|
keyValuePairs
|
||||||
|
);
|
||||||
|
arrCp[i].key = e.target.value;
|
||||||
|
setEditPoolKeyValuePairs(arrCp);
|
||||||
|
}}
|
||||||
|
index={i}
|
||||||
|
placeholder={"Key"}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={5} className={classes.affinityLabelValue}>
|
||||||
|
{keyOptions.length > 0 && (
|
||||||
|
<SelectWrapper
|
||||||
|
onChange={(e: SelectChangeEvent<string>) => {
|
||||||
|
const arrCp: LabelKeyPair[] = Object.assign(
|
||||||
|
[],
|
||||||
|
keyValuePairs
|
||||||
|
);
|
||||||
|
arrCp[i].value = e.target.value as string;
|
||||||
|
setEditPoolKeyValuePairs(arrCp);
|
||||||
|
}}
|
||||||
|
id="select-access-policy"
|
||||||
|
name="select-access-policy"
|
||||||
|
label={""}
|
||||||
|
value={kvp.value}
|
||||||
|
options={
|
||||||
|
keyValueMap[kvp.key]
|
||||||
|
? keyValueMap[kvp.key].map((v) => {
|
||||||
|
return { label: v, value: v };
|
||||||
|
})
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{keyOptions.length === 0 && (
|
||||||
|
<InputBoxWrapper
|
||||||
|
id={`nodeselector-value-${i.toString()}`}
|
||||||
|
label={""}
|
||||||
|
name={`nodeselector-${i.toString()}`}
|
||||||
|
value={kvp.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
const arrCp: LabelKeyPair[] = Object.assign(
|
||||||
|
[],
|
||||||
|
keyValuePairs
|
||||||
|
);
|
||||||
|
arrCp[i].value = e.target.value;
|
||||||
|
setEditPoolKeyValuePairs(arrCp);
|
||||||
|
}}
|
||||||
|
index={i}
|
||||||
|
placeholder={"value"}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={2} className={classes.rowActions}>
|
||||||
|
<div className={classes.overlayAction}>
|
||||||
|
<IconButton
|
||||||
|
size={"small"}
|
||||||
|
onClick={() => {
|
||||||
|
const arrCp = Object.assign([], keyValuePairs);
|
||||||
|
if (keyOptions.length > 0) {
|
||||||
|
arrCp.push({
|
||||||
|
key: keyOptions[0].value,
|
||||||
|
value: keyValueMap[keyOptions[0].value][0],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
arrCp.push({ key: "", value: "" });
|
||||||
|
}
|
||||||
|
|
||||||
|
setEditPoolKeyValuePairs(arrCp);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AddIcon />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
{keyValuePairs.length > 1 && (
|
||||||
|
<div className={classes.overlayAction}>
|
||||||
|
<IconButton
|
||||||
|
size={"small"}
|
||||||
|
onClick={() => {
|
||||||
|
const arrCp = keyValuePairs.filter(
|
||||||
|
(item, index) => index !== i
|
||||||
|
);
|
||||||
|
setEditPoolKeyValuePairs(arrCp);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RemoveIcon />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
<Grid item xs={12} className={classes.affinityConfigField}>
|
||||||
|
<Grid item className={classes.affinityFieldLabel}>
|
||||||
|
<h3>Tolerations</h3>
|
||||||
|
<span className={classes.error}>
|
||||||
|
{validationErrors["tolerations"]}
|
||||||
|
</span>
|
||||||
|
<Grid container>
|
||||||
|
{tolerations &&
|
||||||
|
tolerations.map((tol, i) => {
|
||||||
|
return (
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={12}
|
||||||
|
className={classes.affinityRow}
|
||||||
|
key={`affinity-keyVal-${i.toString()}`}
|
||||||
|
>
|
||||||
|
<TolerationSelector
|
||||||
|
effect={tol.effect}
|
||||||
|
onEffectChange={(value) => {
|
||||||
|
updateToleration(i, "effect", value);
|
||||||
|
}}
|
||||||
|
tolerationKey={tol.key}
|
||||||
|
onTolerationKeyChange={(value) => {
|
||||||
|
updateToleration(i, "key", value);
|
||||||
|
}}
|
||||||
|
operator={tol.operator}
|
||||||
|
onOperatorChange={(value) => {
|
||||||
|
updateToleration(i, "operator", value);
|
||||||
|
}}
|
||||||
|
value={tol.value}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
updateToleration(i, "value", value);
|
||||||
|
}}
|
||||||
|
tolerationSeconds={tol.tolerationSeconds?.seconds || 0}
|
||||||
|
onSecondsChange={(value) => {
|
||||||
|
updateToleration(i, "tolerationSeconds", {
|
||||||
|
seconds: value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
index={i}
|
||||||
|
/>
|
||||||
|
<div className={classes.overlayAction}>
|
||||||
|
<IconButton
|
||||||
|
size={"small"}
|
||||||
|
onClick={addNewEditPoolToleration}
|
||||||
|
disabled={i !== tolerations.length - 1}
|
||||||
|
>
|
||||||
|
<AddIcon />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={classes.overlayAction}>
|
||||||
|
<IconButton
|
||||||
|
size={"small"}
|
||||||
|
onClick={() => removeEditPoolToleration(i)}
|
||||||
|
disabled={tolerations.length <= 1}
|
||||||
|
>
|
||||||
|
<RemoveIcon />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapState = (state: AppState) => {
|
||||||
|
const editPool = state.tenants.editPool;
|
||||||
|
|
||||||
|
return {
|
||||||
|
podAffinity: editPool.fields.affinity.podAffinity,
|
||||||
|
nodeSelectorLabels: editPool.fields.affinity.nodeSelectorLabels,
|
||||||
|
withPodAntiAffinity: editPool.fields.affinity.withPodAntiAffinity,
|
||||||
|
keyValuePairs: editPool.fields.nodeSelectorPairs,
|
||||||
|
tolerations: editPool.fields.tolerations,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(mapState, {
|
||||||
|
setModalErrorSnackMessage,
|
||||||
|
setEditPoolField,
|
||||||
|
isEditPoolPageValid,
|
||||||
|
setEditPoolKeyValuePairs,
|
||||||
|
setEditPoolTolerationInfo,
|
||||||
|
addNewEditPoolToleration,
|
||||||
|
removeEditPoolToleration,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withStyles(styles)(connector(Affinity));
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
// 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 } from "react";
|
||||||
|
import get from "lodash/get";
|
||||||
|
import { Theme } from "@mui/material/styles";
|
||||||
|
import createStyles from "@mui/styles/createStyles";
|
||||||
|
import withStyles from "@mui/styles/withStyles";
|
||||||
|
import {
|
||||||
|
formFieldStyles,
|
||||||
|
wizardCommon,
|
||||||
|
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||||
|
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||||
|
import Grid from "@mui/material/Grid";
|
||||||
|
import { niceBytes } from "../../../../../../common/utils";
|
||||||
|
import { Paper, SelectChangeEvent } from "@mui/material";
|
||||||
|
import api from "../../../../../../common/api";
|
||||||
|
import { ITenant } from "../../../ListTenants/types";
|
||||||
|
import { ErrorResponseHandler } from "../../../../../../common/types";
|
||||||
|
import SelectWrapper from "../../../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||||
|
import { IQuotaElement, IQuotas, Opts } from "../../../ListTenants/utils";
|
||||||
|
import { AppState } from "../../../../../../store";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import {
|
||||||
|
setEditPoolField,
|
||||||
|
isEditPoolPageValid,
|
||||||
|
setEditPoolStorageClasses,
|
||||||
|
} from "../../../actions";
|
||||||
|
import {
|
||||||
|
commonFormValidation,
|
||||||
|
IValidation,
|
||||||
|
} from "../../../../../../utils/validationFunctions";
|
||||||
|
import InputUnitMenu from "../../../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
|
||||||
|
|
||||||
|
interface IPoolResourcesProps {
|
||||||
|
tenant: ITenant | null;
|
||||||
|
classes: any;
|
||||||
|
storageClasses: Opts[];
|
||||||
|
numberOfNodes: string;
|
||||||
|
storageClass: string;
|
||||||
|
volumeSize: string;
|
||||||
|
volumesPerServer: string;
|
||||||
|
setEditPoolField: typeof setEditPoolField;
|
||||||
|
isEditPoolPageValid: typeof isEditPoolPageValid;
|
||||||
|
setEditPoolStorageClasses: typeof setEditPoolStorageClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
buttonContainer: {
|
||||||
|
textAlign: "right",
|
||||||
|
},
|
||||||
|
bottomContainer: {
|
||||||
|
display: "flex",
|
||||||
|
flexGrow: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
margin: "auto",
|
||||||
|
justifyContent: "center",
|
||||||
|
"& div": {
|
||||||
|
width: 150,
|
||||||
|
"@media (max-width: 900px)": {
|
||||||
|
flexFlow: "column",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
factorElements: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
marginLeft: 30,
|
||||||
|
},
|
||||||
|
sizeNumber: {
|
||||||
|
fontSize: 35,
|
||||||
|
fontWeight: 700,
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
sizeDescription: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#777",
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
...formFieldStyles,
|
||||||
|
...wizardCommon,
|
||||||
|
});
|
||||||
|
|
||||||
|
const PoolResources = ({
|
||||||
|
tenant,
|
||||||
|
classes,
|
||||||
|
storageClasses,
|
||||||
|
numberOfNodes,
|
||||||
|
storageClass,
|
||||||
|
volumeSize,
|
||||||
|
volumesPerServer,
|
||||||
|
setEditPoolField,
|
||||||
|
setEditPoolStorageClasses,
|
||||||
|
isEditPoolPageValid,
|
||||||
|
}: IPoolResourcesProps) => {
|
||||||
|
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||||
|
|
||||||
|
const instanceCapacity: number =
|
||||||
|
parseInt(volumeSize) * 1073741824 * parseInt(volumesPerServer);
|
||||||
|
const totalCapacity: number = instanceCapacity * parseInt(numberOfNodes);
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
useEffect(() => {
|
||||||
|
let customAccountValidation: IValidation[] = [
|
||||||
|
{
|
||||||
|
fieldKey: "number_of_nodes",
|
||||||
|
required: true,
|
||||||
|
value: numberOfNodes.toString(),
|
||||||
|
customValidation:
|
||||||
|
parseInt(numberOfNodes) < 1 || isNaN(parseInt(numberOfNodes)),
|
||||||
|
customValidationMessage: "Number of servers must be at least 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldKey: "pool_size",
|
||||||
|
required: true,
|
||||||
|
value: volumeSize.toString(),
|
||||||
|
customValidation:
|
||||||
|
parseInt(volumeSize) < 1 || isNaN(parseInt(volumeSize)),
|
||||||
|
customValidationMessage: "Pool Size cannot be 0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldKey: "volumes_per_server",
|
||||||
|
required: true,
|
||||||
|
value: volumesPerServer.toString(),
|
||||||
|
customValidation:
|
||||||
|
parseInt(volumesPerServer) < 1 || isNaN(parseInt(volumesPerServer)),
|
||||||
|
customValidationMessage: "1 volume or more are required",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const commonVal = commonFormValidation(customAccountValidation);
|
||||||
|
|
||||||
|
isEditPoolPageValid("setup", Object.keys(commonVal).length === 0);
|
||||||
|
|
||||||
|
setValidationErrors(commonVal);
|
||||||
|
}, [
|
||||||
|
isEditPoolPageValid,
|
||||||
|
numberOfNodes,
|
||||||
|
volumeSize,
|
||||||
|
volumesPerServer,
|
||||||
|
storageClass,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (storageClasses.length === 0 && tenant) {
|
||||||
|
api
|
||||||
|
.invoke(
|
||||||
|
"GET",
|
||||||
|
`/api/v1/namespaces/${tenant.namespace}/resourcequotas/${tenant.namespace}-storagequota`
|
||||||
|
)
|
||||||
|
.then((res: IQuotas) => {
|
||||||
|
const elements: IQuotaElement[] = get(res, "elements", []);
|
||||||
|
|
||||||
|
const newStorage = elements.map((storageClass: any) => {
|
||||||
|
const name = get(storageClass, "name", "").split(
|
||||||
|
".storageclass.storage.k8s.io/requests.storage"
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
return { label: name, value: name };
|
||||||
|
});
|
||||||
|
|
||||||
|
setEditPoolField("setup", "storageClass", newStorage[0].value);
|
||||||
|
|
||||||
|
setEditPoolStorageClasses(newStorage);
|
||||||
|
})
|
||||||
|
.catch((err: ErrorResponseHandler) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [tenant, storageClasses, setEditPoolStorageClasses, setEditPoolField]);
|
||||||
|
|
||||||
|
const setFieldInfo = (fieldName: string, value: any) => {
|
||||||
|
setEditPoolField("setup", fieldName, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper className={classes.paperWrapper}>
|
||||||
|
<div className={classes.headerElement}>
|
||||||
|
<h3 className={classes.h3Section}>Pool Resources</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Grid item xs={12} className={classes.formFieldRow}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="number_of_nodes"
|
||||||
|
name="number_of_nodes"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const intValue = parseInt(e.target.value);
|
||||||
|
|
||||||
|
if (e.target.validity.valid && !isNaN(intValue)) {
|
||||||
|
setFieldInfo("numberOfNodes", intValue);
|
||||||
|
} else if (isNaN(intValue)) {
|
||||||
|
setFieldInfo("numberOfNodes", 0);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
label="Number of Servers"
|
||||||
|
value={numberOfNodes}
|
||||||
|
error={validationErrors["number_of_nodes"] || ""}
|
||||||
|
pattern={"[0-9]*"}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.formFieldRow}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="pool_size"
|
||||||
|
name="pool_size"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const intValue = parseInt(e.target.value);
|
||||||
|
|
||||||
|
if (e.target.validity.valid && !isNaN(intValue)) {
|
||||||
|
setFieldInfo("volumeSize", intValue);
|
||||||
|
} else if (isNaN(intValue)) {
|
||||||
|
setFieldInfo("volumeSize", 0);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
label="Volume Size"
|
||||||
|
value={volumeSize}
|
||||||
|
error={validationErrors["pool_size"] || ""}
|
||||||
|
pattern={"[0-9]*"}
|
||||||
|
overlayObject={
|
||||||
|
<InputUnitMenu
|
||||||
|
id={"quota_unit"}
|
||||||
|
onUnitChange={() => {}}
|
||||||
|
unitSelected={"Gi"}
|
||||||
|
unitsList={[{ label: "Gi", value: "Gi" }]}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.formFieldRow}>
|
||||||
|
<InputBoxWrapper
|
||||||
|
id="volumes_per_sever"
|
||||||
|
name="volumes_per_sever"
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const intValue = parseInt(e.target.value);
|
||||||
|
|
||||||
|
if (e.target.validity.valid && !isNaN(intValue)) {
|
||||||
|
setFieldInfo("volumesPerServer", intValue);
|
||||||
|
} else if (isNaN(intValue)) {
|
||||||
|
setFieldInfo("volumesPerServer", 0);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
label="Volumes per Server"
|
||||||
|
value={volumesPerServer}
|
||||||
|
error={validationErrors["volumes_per_server"] || ""}
|
||||||
|
pattern={"[0-9]*"}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.formFieldRow}>
|
||||||
|
<SelectWrapper
|
||||||
|
id="storage_class"
|
||||||
|
name="storage_class"
|
||||||
|
onChange={(e: SelectChangeEvent<string>) => {
|
||||||
|
setFieldInfo("storageClasses", e.target.value as string);
|
||||||
|
}}
|
||||||
|
label="Storage Class"
|
||||||
|
value={storageClass}
|
||||||
|
options={storageClasses}
|
||||||
|
disabled={storageClasses.length < 1}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className={classes.bottomContainer}>
|
||||||
|
<div className={classes.factorElements}>
|
||||||
|
<div>
|
||||||
|
<div className={classes.sizeNumber}>
|
||||||
|
{niceBytes(instanceCapacity.toString(10))}
|
||||||
|
</div>
|
||||||
|
<div className={classes.sizeDescription}>Instance Capacity</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className={classes.sizeNumber}>
|
||||||
|
{niceBytes(totalCapacity.toString(10))}
|
||||||
|
</div>
|
||||||
|
<div className={classes.sizeDescription}>Total Capacity</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Grid>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapState = (state: AppState) => {
|
||||||
|
const setupFields = state.tenants.editPool.fields.setup;
|
||||||
|
return {
|
||||||
|
tenant: state.tenants.tenantDetails.tenantInfo,
|
||||||
|
storageClasses: state.tenants.editPool.storageClasses,
|
||||||
|
numberOfNodes: setupFields.numberOfNodes.toString(),
|
||||||
|
storageClass: setupFields.storageClass,
|
||||||
|
volumeSize: setupFields.volumeSize.toString(),
|
||||||
|
volumesPerServer: setupFields.volumesPerServer.toString(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(mapState, {
|
||||||
|
setEditPoolField,
|
||||||
|
isEditPoolPageValid,
|
||||||
|
setEditPoolStorageClasses,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withStyles(styles)(connector(PoolResources));
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Fragment, useState } from "react";
|
import React, { Fragment } from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Theme } from "@mui/material/styles";
|
import { Theme } from "@mui/material/styles";
|
||||||
import createStyles from "@mui/styles/createStyles";
|
import createStyles from "@mui/styles/createStyles";
|
||||||
@@ -28,7 +28,11 @@ import {
|
|||||||
import Grid from "@mui/material/Grid";
|
import Grid from "@mui/material/Grid";
|
||||||
import { setErrorSnackMessage } from "../../../../actions";
|
import { setErrorSnackMessage } from "../../../../actions";
|
||||||
import { AppState } from "../../../../store";
|
import { AppState } from "../../../../store";
|
||||||
import { setSelectedPool, setTenantDetailsLoad } from "../actions";
|
import {
|
||||||
|
setOpenPoolDetails,
|
||||||
|
setSelectedPool,
|
||||||
|
setTenantDetailsLoad,
|
||||||
|
} from "../actions";
|
||||||
import PoolsListing from "./Pools/Details/PoolsListing";
|
import PoolsListing from "./Pools/Details/PoolsListing";
|
||||||
import PoolDetails from "./Pools/Details/PoolDetails";
|
import PoolDetails from "./Pools/Details/PoolDetails";
|
||||||
import BackLink from "../../../../common/BackLink";
|
import BackLink from "../../../../common/BackLink";
|
||||||
@@ -39,9 +43,11 @@ interface IPoolsSummary {
|
|||||||
history: any;
|
history: any;
|
||||||
match: any;
|
match: any;
|
||||||
selectedPool: string | null;
|
selectedPool: string | null;
|
||||||
|
poolDetailsOpen: boolean;
|
||||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||||
setTenantDetailsLoad: typeof setTenantDetailsLoad;
|
setTenantDetailsLoad: typeof setTenantDetailsLoad;
|
||||||
setSelectedPool: typeof setSelectedPool;
|
setSelectedPool: typeof setSelectedPool;
|
||||||
|
setOpenPoolDetails: typeof setOpenPoolDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
@@ -57,15 +63,16 @@ const PoolsSummary = ({
|
|||||||
history,
|
history,
|
||||||
selectedPool,
|
selectedPool,
|
||||||
match,
|
match,
|
||||||
|
poolDetailsOpen,
|
||||||
|
setOpenPoolDetails,
|
||||||
}: IPoolsSummary) => {
|
}: IPoolsSummary) => {
|
||||||
const [poolDetailsOpen, setPoolDetailsOpen] = useState<boolean>(false);
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{poolDetailsOpen && (
|
{poolDetailsOpen && (
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<BackLink
|
<BackLink
|
||||||
executeOnClick={() => {
|
executeOnClick={() => {
|
||||||
setPoolDetailsOpen(false);
|
setOpenPoolDetails(false);
|
||||||
}}
|
}}
|
||||||
label={"Back to Pools list"}
|
label={"Back to Pools list"}
|
||||||
to={match.url}
|
to={match.url}
|
||||||
@@ -77,15 +84,11 @@ const PoolsSummary = ({
|
|||||||
</h1>
|
</h1>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
{poolDetailsOpen ? (
|
{poolDetailsOpen ? (
|
||||||
<PoolDetails
|
<PoolDetails history={history} />
|
||||||
closeDetailsView={() => {
|
|
||||||
setPoolDetailsOpen(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<PoolsListing
|
<PoolsListing
|
||||||
setPoolDetailsView={() => {
|
setPoolDetailsView={() => {
|
||||||
setPoolDetailsOpen(true);
|
setOpenPoolDetails(true);
|
||||||
}}
|
}}
|
||||||
history={history}
|
history={history}
|
||||||
/>
|
/>
|
||||||
@@ -100,12 +103,14 @@ const mapState = (state: AppState) => ({
|
|||||||
selectedTenant: state.tenants.tenantDetails.currentTenant,
|
selectedTenant: state.tenants.tenantDetails.currentTenant,
|
||||||
selectedPool: state.tenants.tenantDetails.selectedPool,
|
selectedPool: state.tenants.tenantDetails.selectedPool,
|
||||||
tenant: state.tenants.tenantDetails.tenantInfo,
|
tenant: state.tenants.tenantDetails.tenantInfo,
|
||||||
|
poolDetailsOpen: state.tenants.tenantDetails.poolDetailsOpen,
|
||||||
});
|
});
|
||||||
|
|
||||||
const connector = connect(mapState, {
|
const connector = connect(mapState, {
|
||||||
setErrorSnackMessage,
|
setErrorSnackMessage,
|
||||||
setTenantDetailsLoad,
|
setTenantDetailsLoad,
|
||||||
setSelectedPool,
|
setSelectedPool,
|
||||||
|
setOpenPoolDetails,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withStyles(styles)(connector(PoolsSummary));
|
export default withStyles(styles)(connector(PoolsSummary));
|
||||||
|
|||||||
@@ -78,6 +78,5 @@ export const getNodeSelector = (
|
|||||||
const def = getDefaultAffinity(tenantName, poolName);
|
const def = getDefaultAffinity(tenantName, poolName);
|
||||||
nodeSelector.podAntiAffinity = def.podAntiAffinity;
|
nodeSelector.podAntiAffinity = def.podAntiAffinity;
|
||||||
}
|
}
|
||||||
console.log(nodeSelector);
|
|
||||||
return nodeSelector;
|
return nodeSelector;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { ITenant } from "./ListTenants/types";
|
import { IPool, ITenant } from "./ListTenants/types";
|
||||||
import { Opts } from "./ListTenants/utils";
|
import { Opts } from "./ListTenants/utils";
|
||||||
import {
|
import {
|
||||||
ADD_TENANT_ADD_CA_KEYPAIR,
|
ADD_TENANT_ADD_CA_KEYPAIR,
|
||||||
@@ -33,7 +33,6 @@ import {
|
|||||||
ADD_TENANT_ENCRYPTION_VAULT_CA,
|
ADD_TENANT_ENCRYPTION_VAULT_CA,
|
||||||
ADD_TENANT_ENCRYPTION_VAULT_CERT,
|
ADD_TENANT_ENCRYPTION_VAULT_CERT,
|
||||||
ADD_TENANT_RESET_FORM,
|
ADD_TENANT_RESET_FORM,
|
||||||
ADD_TENANT_SET_CURRENT_PAGE,
|
|
||||||
ADD_TENANT_SET_LIMIT_SIZE,
|
ADD_TENANT_SET_LIMIT_SIZE,
|
||||||
ADD_TENANT_SET_PAGE_VALID,
|
ADD_TENANT_SET_PAGE_VALID,
|
||||||
ADD_TENANT_SET_STORAGE_CLASSES_LIST,
|
ADD_TENANT_SET_STORAGE_CLASSES_LIST,
|
||||||
@@ -59,17 +58,22 @@ import {
|
|||||||
ADD_POOL_REMOVE_TOLERATION_ROW,
|
ADD_POOL_REMOVE_TOLERATION_ROW,
|
||||||
ADD_POOL_SET_KEY_PAIR_VALUE,
|
ADD_POOL_SET_KEY_PAIR_VALUE,
|
||||||
POOL_DETAILS_SET_SELECTED_POOL,
|
POOL_DETAILS_SET_SELECTED_POOL,
|
||||||
|
POOL_DETAILS_SET_OPEN_DETAILS,
|
||||||
|
EDIT_POOL_SET_INITIAL_INFO,
|
||||||
|
EDIT_POOL_SET_LOADING,
|
||||||
|
EDIT_POOL_RESET_FORM,
|
||||||
|
IEditPoolFields,
|
||||||
|
EDIT_POOL_SET_VALUE,
|
||||||
|
EDIT_POOL_SET_PAGE_VALID,
|
||||||
|
EDIT_POOL_SET_POOL_STORAGE_CLASSES,
|
||||||
|
EDIT_POOL_SET_TOLERATION_VALUE,
|
||||||
|
EDIT_POOL_ADD_NEW_TOLERATION,
|
||||||
|
EDIT_POOL_REMOVE_TOLERATION_ROW,
|
||||||
|
EDIT_POOL_SET_KEY_PAIR_VALUE,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { ITolerationModel } from "../../../common/types";
|
import { ITolerationModel } from "../../../common/types";
|
||||||
|
|
||||||
// Basic actions
|
// Basic actions
|
||||||
export const setWizardPage = (page: number) => {
|
|
||||||
return {
|
|
||||||
type: ADD_TENANT_SET_CURRENT_PAGE,
|
|
||||||
page,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateAddField = (
|
export const updateAddField = (
|
||||||
pageName: string,
|
pageName: string,
|
||||||
fieldName: string,
|
fieldName: string,
|
||||||
@@ -409,9 +413,99 @@ export const setPoolKeyValuePairs = (newArray: LabelKeyPair[]) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Pool Details
|
||||||
|
|
||||||
export const setSelectedPool = (newPool: string | null) => {
|
export const setSelectedPool = (newPool: string | null) => {
|
||||||
return {
|
return {
|
||||||
type: POOL_DETAILS_SET_SELECTED_POOL,
|
type: POOL_DETAILS_SET_SELECTED_POOL,
|
||||||
pool: newPool,
|
pool: newPool,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setOpenPoolDetails = (state: boolean) => {
|
||||||
|
return {
|
||||||
|
type: POOL_DETAILS_SET_OPEN_DETAILS,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Edit Pool
|
||||||
|
|
||||||
|
export const setInitialPoolDetails = (pool: IPool) => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_SET_INITIAL_INFO,
|
||||||
|
pool,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setEditPoolLoading = (state: boolean) => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_SET_LOADING,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resetEditPoolForm = () => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_RESET_FORM,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setEditPoolField = (
|
||||||
|
page: keyof IEditPoolFields,
|
||||||
|
field: string,
|
||||||
|
value: any
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_SET_VALUE,
|
||||||
|
page,
|
||||||
|
field,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isEditPoolPageValid = (page: string, status: boolean) => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_SET_PAGE_VALID,
|
||||||
|
page,
|
||||||
|
status,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setEditPoolStorageClasses = (storageClasses: Opts[]) => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_SET_POOL_STORAGE_CLASSES,
|
||||||
|
storageClasses,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setEditPoolTolerationInfo = (
|
||||||
|
index: number,
|
||||||
|
tolerationValue: ITolerationModel
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_SET_TOLERATION_VALUE,
|
||||||
|
index,
|
||||||
|
toleration: tolerationValue,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addNewEditPoolToleration = () => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_ADD_NEW_TOLERATION,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeEditPoolToleration = (index: number) => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_REMOVE_TOLERATION_ROW,
|
||||||
|
index,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setEditPoolKeyValuePairs = (newArray: LabelKeyPair[]) => {
|
||||||
|
return {
|
||||||
|
type: EDIT_POOL_SET_KEY_PAIR_VALUE,
|
||||||
|
newArray,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -16,6 +16,15 @@
|
|||||||
import has from "lodash/has";
|
import has from "lodash/has";
|
||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
import {
|
import {
|
||||||
|
ADD_POOL_ADD_NEW_TOLERATION,
|
||||||
|
ADD_POOL_REMOVE_TOLERATION_ROW,
|
||||||
|
ADD_POOL_RESET_FORM,
|
||||||
|
ADD_POOL_SET_KEY_PAIR_VALUE,
|
||||||
|
ADD_POOL_SET_LOADING,
|
||||||
|
ADD_POOL_SET_PAGE_VALID,
|
||||||
|
ADD_POOL_SET_POOL_STORAGE_CLASSES,
|
||||||
|
ADD_POOL_SET_TOLERATION_VALUE,
|
||||||
|
ADD_POOL_SET_VALUE,
|
||||||
ADD_TENANT_ADD_CA_KEYPAIR,
|
ADD_TENANT_ADD_CA_KEYPAIR,
|
||||||
ADD_TENANT_ADD_CONSOLE_CA_KEYPAIR,
|
ADD_TENANT_ADD_CONSOLE_CA_KEYPAIR,
|
||||||
ADD_TENANT_ADD_CONSOLE_CERT,
|
ADD_TENANT_ADD_CONSOLE_CERT,
|
||||||
@@ -42,27 +51,35 @@ import {
|
|||||||
ADD_TENANT_SET_STORAGE_TYPE,
|
ADD_TENANT_SET_STORAGE_TYPE,
|
||||||
ADD_TENANT_SET_TOLERATION_VALUE,
|
ADD_TENANT_SET_TOLERATION_VALUE,
|
||||||
ADD_TENANT_UPDATE_FIELD,
|
ADD_TENANT_UPDATE_FIELD,
|
||||||
|
EDIT_POOL_ADD_NEW_TOLERATION,
|
||||||
|
EDIT_POOL_REMOVE_TOLERATION_ROW,
|
||||||
|
EDIT_POOL_RESET_FORM,
|
||||||
|
EDIT_POOL_SET_INITIAL_INFO,
|
||||||
|
EDIT_POOL_SET_KEY_PAIR_VALUE,
|
||||||
|
EDIT_POOL_SET_LOADING,
|
||||||
|
EDIT_POOL_SET_PAGE_VALID,
|
||||||
|
EDIT_POOL_SET_POOL_STORAGE_CLASSES,
|
||||||
|
EDIT_POOL_SET_TOLERATION_VALUE,
|
||||||
|
EDIT_POOL_SET_VALUE,
|
||||||
|
IEditPoolFields,
|
||||||
|
ITenantState,
|
||||||
|
LabelKeyPair,
|
||||||
|
POOL_DETAILS_SET_OPEN_DETAILS,
|
||||||
|
POOL_DETAILS_SET_SELECTED_POOL,
|
||||||
TENANT_DETAILS_SET_CURRENT_TENANT,
|
TENANT_DETAILS_SET_CURRENT_TENANT,
|
||||||
TENANT_DETAILS_SET_LOADING,
|
TENANT_DETAILS_SET_LOADING,
|
||||||
TENANT_DETAILS_SET_TAB,
|
TENANT_DETAILS_SET_TAB,
|
||||||
TENANT_DETAILS_SET_TENANT,
|
TENANT_DETAILS_SET_TENANT,
|
||||||
ADD_POOL_SET_LOADING,
|
|
||||||
ADD_POOL_SET_VALUE,
|
|
||||||
ADD_POOL_RESET_FORM,
|
|
||||||
ITenantState,
|
|
||||||
TenantsManagementTypes,
|
TenantsManagementTypes,
|
||||||
ADD_POOL_SET_PAGE_VALID,
|
|
||||||
ADD_POOL_SET_POOL_STORAGE_CLASSES,
|
|
||||||
ADD_POOL_ADD_NEW_TOLERATION,
|
|
||||||
ADD_POOL_SET_TOLERATION_VALUE,
|
|
||||||
ADD_POOL_REMOVE_TOLERATION_ROW,
|
|
||||||
ADD_POOL_SET_KEY_PAIR_VALUE,
|
|
||||||
POOL_DETAILS_SET_SELECTED_POOL,
|
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { KeyPair } from "./ListTenants/utils";
|
import { KeyPair } from "./ListTenants/utils";
|
||||||
import { getRandomString } from "./utils";
|
import { getRandomString } from "./utils";
|
||||||
import { addTenantSetStorageTypeReducer } from "./reducers/add-tenant-reducer";
|
import { addTenantSetStorageTypeReducer } from "./reducers/add-tenant-reducer";
|
||||||
import { ITolerationEffect, ITolerationOperator } from "../../../common/types";
|
import {
|
||||||
|
ITolerationEffect,
|
||||||
|
ITolerationModel,
|
||||||
|
ITolerationOperator,
|
||||||
|
} from "../../../common/types";
|
||||||
|
|
||||||
const initialState: ITenantState = {
|
const initialState: ITenantState = {
|
||||||
createTenant: {
|
createTenant: {
|
||||||
@@ -365,6 +382,7 @@ const initialState: ITenantState = {
|
|||||||
tenantInfo: null,
|
tenantInfo: null,
|
||||||
currentTab: "summary",
|
currentTab: "summary",
|
||||||
selectedPool: null,
|
selectedPool: null,
|
||||||
|
poolDetailsOpen: false,
|
||||||
},
|
},
|
||||||
addPool: {
|
addPool: {
|
||||||
addPoolLoading: false,
|
addPoolLoading: false,
|
||||||
@@ -404,6 +422,44 @@ const initialState: ITenantState = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
editPool: {
|
||||||
|
editPoolLoading: false,
|
||||||
|
validPages: ["setup", "affinity", "configure"],
|
||||||
|
storageClasses: [],
|
||||||
|
limitSize: {},
|
||||||
|
fields: {
|
||||||
|
setup: {
|
||||||
|
numberOfNodes: 0,
|
||||||
|
storageClass: "",
|
||||||
|
volumeSize: 0,
|
||||||
|
volumesPerServer: 0,
|
||||||
|
},
|
||||||
|
affinity: {
|
||||||
|
nodeSelectorLabels: "",
|
||||||
|
podAffinity: "default",
|
||||||
|
withPodAntiAffinity: true,
|
||||||
|
},
|
||||||
|
configuration: {
|
||||||
|
securityContextEnabled: false,
|
||||||
|
securityContext: {
|
||||||
|
runAsUser: "1000",
|
||||||
|
runAsGroup: "1000",
|
||||||
|
fsGroup: "1000",
|
||||||
|
runAsNonRoot: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodeSelectorPairs: [{ key: "", value: "" }],
|
||||||
|
tolerations: [
|
||||||
|
{
|
||||||
|
key: "",
|
||||||
|
tolerationSeconds: { seconds: 0 },
|
||||||
|
value: "",
|
||||||
|
effect: ITolerationEffect.NoSchedule,
|
||||||
|
operator: ITolerationOperator.Equal,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function tenantsReducer(
|
export function tenantsReducer(
|
||||||
@@ -1165,14 +1221,6 @@ export function tenantsReducer(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case POOL_DETAILS_SET_SELECTED_POOL:
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
tenantDetails: {
|
|
||||||
...state.tenantDetails,
|
|
||||||
selectedPool: action.pool,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
case ADD_POOL_RESET_FORM:
|
case ADD_POOL_RESET_FORM:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -1215,6 +1263,281 @@ export function tenantsReducer(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
case POOL_DETAILS_SET_SELECTED_POOL:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
tenantDetails: {
|
||||||
|
...state.tenantDetails,
|
||||||
|
selectedPool: action.pool,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case POOL_DETAILS_SET_OPEN_DETAILS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
tenantDetails: {
|
||||||
|
...state.tenantDetails,
|
||||||
|
poolDetailsOpen: action.state,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case EDIT_POOL_SET_INITIAL_INFO:
|
||||||
|
let podAffinity: "default" | "nodeSelector" | "none" = "none";
|
||||||
|
let withPodAntiAffinity = false;
|
||||||
|
let nodeSelectorLabels = "";
|
||||||
|
let tolerations: ITolerationModel[] = [
|
||||||
|
{
|
||||||
|
key: "",
|
||||||
|
tolerationSeconds: { seconds: 0 },
|
||||||
|
value: "",
|
||||||
|
effect: ITolerationEffect.NoSchedule,
|
||||||
|
operator: ITolerationOperator.Equal,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let nodeSelectorPairs: LabelKeyPair[] = [{ key: "", value: "" }];
|
||||||
|
|
||||||
|
if (action.pool.affinity?.nodeAffinity) {
|
||||||
|
podAffinity = "nodeSelector";
|
||||||
|
if (action.pool.affinity?.podAntiAffinity) {
|
||||||
|
withPodAntiAffinity = true;
|
||||||
|
}
|
||||||
|
} else if (action.pool.affinity?.podAntiAffinity) {
|
||||||
|
podAffinity = "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.pool.affinity?.nodeAffinity) {
|
||||||
|
let labelItems: string[] = [];
|
||||||
|
nodeSelectorPairs = [];
|
||||||
|
|
||||||
|
action.pool.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.forEach(
|
||||||
|
(labels) => {
|
||||||
|
labels.matchExpressions.forEach((exp) => {
|
||||||
|
labelItems.push(`${exp.key}=${exp.values.join(",")}`);
|
||||||
|
nodeSelectorPairs.push({
|
||||||
|
key: exp.key,
|
||||||
|
value: exp.values.join(", "),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
nodeSelectorLabels = labelItems.join("&");
|
||||||
|
}
|
||||||
|
|
||||||
|
let securityContextOption = false;
|
||||||
|
|
||||||
|
if (action.pool.securityContext) {
|
||||||
|
securityContextOption =
|
||||||
|
!!action.pool.securityContext.runAsUser ||
|
||||||
|
!!action.pool.securityContext.runAsGroup ||
|
||||||
|
!!action.pool.securityContext.fsGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.pool.tolerations) {
|
||||||
|
tolerations = action.pool.tolerations?.map((toleration) => {
|
||||||
|
const tolerationItem: ITolerationModel = {
|
||||||
|
key: toleration.key,
|
||||||
|
tolerationSeconds: toleration.tolerationSeconds,
|
||||||
|
value: toleration.value,
|
||||||
|
effect: toleration.effect,
|
||||||
|
operator: toleration.operator,
|
||||||
|
};
|
||||||
|
return tolerationItem;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const volSizeVars = action.pool.volume_configuration.size / 1073741824;
|
||||||
|
|
||||||
|
const newPoolInfoFields: IEditPoolFields = {
|
||||||
|
setup: {
|
||||||
|
numberOfNodes: action.pool.servers,
|
||||||
|
storageClass: action.pool.volume_configuration.storage_class_name,
|
||||||
|
volumeSize: volSizeVars,
|
||||||
|
volumesPerServer: action.pool.volumes_per_server,
|
||||||
|
},
|
||||||
|
configuration: {
|
||||||
|
securityContextEnabled: securityContextOption,
|
||||||
|
securityContext: {
|
||||||
|
runAsUser: action.pool.securityContext?.runAsUser || "",
|
||||||
|
runAsGroup: action.pool.securityContext?.runAsGroup || "",
|
||||||
|
fsGroup: action.pool.securityContext?.fsGroup || "",
|
||||||
|
runAsNonRoot: !!action.pool.securityContext?.runAsNonRoot,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
affinity: {
|
||||||
|
podAffinity,
|
||||||
|
withPodAntiAffinity,
|
||||||
|
nodeSelectorLabels,
|
||||||
|
},
|
||||||
|
tolerations,
|
||||||
|
nodeSelectorPairs,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
editPool: {
|
||||||
|
...state.editPool,
|
||||||
|
fields: {
|
||||||
|
...state.editPool.fields,
|
||||||
|
...newPoolInfoFields,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
case EDIT_POOL_SET_LOADING:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
editPool: {
|
||||||
|
...state.editPool,
|
||||||
|
editPoolLoading: action.state,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case EDIT_POOL_SET_VALUE:
|
||||||
|
if (has(newState.editPool.fields, `${action.page}.${action.field}`)) {
|
||||||
|
const originPageNameItems = get(
|
||||||
|
newState.editPool.fields,
|
||||||
|
`${action.page}`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
let newValue: any = {};
|
||||||
|
newValue[action.field] = action.value;
|
||||||
|
|
||||||
|
const joinValue = { ...originPageNameItems, ...newValue };
|
||||||
|
|
||||||
|
newState.editPool.fields[action.page] = { ...joinValue };
|
||||||
|
|
||||||
|
return { ...newState };
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
case EDIT_POOL_SET_PAGE_VALID:
|
||||||
|
const edPoolPV = [...state.editPool.validPages];
|
||||||
|
|
||||||
|
if (action.status) {
|
||||||
|
if (!edPoolPV.includes(action.page)) {
|
||||||
|
edPoolPV.push(action.page);
|
||||||
|
|
||||||
|
newState.editPool.validPages = [...edPoolPV];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const newSetOfPages = edPoolPV.filter((elm) => elm !== action.page);
|
||||||
|
|
||||||
|
newState.editPool.validPages = [...newSetOfPages];
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...newState };
|
||||||
|
case EDIT_POOL_SET_POOL_STORAGE_CLASSES:
|
||||||
|
return {
|
||||||
|
...newState,
|
||||||
|
editPool: {
|
||||||
|
...newState.editPool,
|
||||||
|
storageClasses: action.storageClasses,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case EDIT_POOL_SET_TOLERATION_VALUE:
|
||||||
|
const editPoolTolerationValue = [...state.editPool.fields.tolerations];
|
||||||
|
|
||||||
|
editPoolTolerationValue[action.index] = action.toleration;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
editPool: {
|
||||||
|
...state.editPool,
|
||||||
|
fields: {
|
||||||
|
...state.editPool.fields,
|
||||||
|
tolerations: [...editPoolTolerationValue],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case EDIT_POOL_ADD_NEW_TOLERATION:
|
||||||
|
const editPoolTolerationArray = [
|
||||||
|
...state.editPool.fields.tolerations,
|
||||||
|
{
|
||||||
|
key: "",
|
||||||
|
tolerationSeconds: { seconds: 0 },
|
||||||
|
value: "",
|
||||||
|
effect: ITolerationEffect.NoSchedule,
|
||||||
|
operator: ITolerationOperator.Equal,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
editPool: {
|
||||||
|
...state.editPool,
|
||||||
|
fields: {
|
||||||
|
...state.editPool.fields,
|
||||||
|
tolerations: [...editPoolTolerationArray],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case EDIT_POOL_REMOVE_TOLERATION_ROW:
|
||||||
|
const removePoolTolerationArray =
|
||||||
|
state.editPool.fields.tolerations.filter(
|
||||||
|
(_, index) => index !== action.index
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
editPool: {
|
||||||
|
...state.editPool,
|
||||||
|
fields: {
|
||||||
|
...state.editPool.fields,
|
||||||
|
tolerations: [...removePoolTolerationArray],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case EDIT_POOL_SET_KEY_PAIR_VALUE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
editPool: {
|
||||||
|
...state.editPool,
|
||||||
|
fields: {
|
||||||
|
...state.editPool.fields,
|
||||||
|
nodeSelectorPairs: action.newArray,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case EDIT_POOL_RESET_FORM:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
editPool: {
|
||||||
|
editPoolLoading: false,
|
||||||
|
validPages: ["setup", "affinity", "configure"],
|
||||||
|
storageClasses: [],
|
||||||
|
limitSize: {},
|
||||||
|
fields: {
|
||||||
|
setup: {
|
||||||
|
numberOfNodes: 0,
|
||||||
|
storageClass: "",
|
||||||
|
volumeSize: 0,
|
||||||
|
volumesPerServer: 0,
|
||||||
|
},
|
||||||
|
affinity: {
|
||||||
|
nodeSelectorLabels: "",
|
||||||
|
podAffinity: "default",
|
||||||
|
withPodAntiAffinity: true,
|
||||||
|
},
|
||||||
|
configuration: {
|
||||||
|
securityContextEnabled: false,
|
||||||
|
securityContext: {
|
||||||
|
runAsUser: "1000",
|
||||||
|
runAsGroup: "1000",
|
||||||
|
fsGroup: "1000",
|
||||||
|
runAsNonRoot: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nodeSelectorPairs: [{ key: "", value: "" }],
|
||||||
|
tolerations: [
|
||||||
|
{
|
||||||
|
key: "",
|
||||||
|
tolerationSeconds: { seconds: 0 },
|
||||||
|
value: "",
|
||||||
|
effect: ITolerationEffect.NoSchedule,
|
||||||
|
operator: ITolerationOperator.Equal,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
IGemaltoCredentials,
|
IGemaltoCredentials,
|
||||||
ITolerationModel,
|
ITolerationModel,
|
||||||
} from "../../../common/types";
|
} from "../../../common/types";
|
||||||
import { IResourcesSize, ITenant } from "./ListTenants/types";
|
import { IPool, IResourcesSize, ITenant } from "./ListTenants/types";
|
||||||
import { KeyPair, Opts } from "./ListTenants/utils";
|
import { KeyPair, Opts } from "./ListTenants/utils";
|
||||||
import { IntegrationConfiguration } from "./AddTenant/Steps/TenantResources/utils";
|
import { IntegrationConfiguration } from "./AddTenant/Steps/TenantResources/utils";
|
||||||
|
|
||||||
@@ -99,8 +99,23 @@ export const ADD_POOL_ADD_NEW_TOLERATION = "ADD_POOL/ADD_NEW_TOLERATION";
|
|||||||
export const ADD_POOL_REMOVE_TOLERATION_ROW = "ADD_POOL/REMOVE_TOLERATION_ROW";
|
export const ADD_POOL_REMOVE_TOLERATION_ROW = "ADD_POOL/REMOVE_TOLERATION_ROW";
|
||||||
|
|
||||||
// Pool Details
|
// Pool Details
|
||||||
|
export const POOL_DETAILS_SET_OPEN_DETAILS = "POOL_DETAILS/SET_OPEN_DETAILS";
|
||||||
export const POOL_DETAILS_SET_SELECTED_POOL = "POOL_DETAILS/SET_SELECTED_POOL";
|
export const POOL_DETAILS_SET_SELECTED_POOL = "POOL_DETAILS/SET_SELECTED_POOL";
|
||||||
|
|
||||||
|
// Edit Pool
|
||||||
|
export const EDIT_POOL_SET_INITIAL_INFO = "EDIT_POOL/SET_INITIAL_INFO";
|
||||||
|
export const EDIT_POOL_SET_POOL_STORAGE_CLASSES =
|
||||||
|
"EDIT_POOL/SET_POOL_STORAGE_CLASSES";
|
||||||
|
export const EDIT_POOL_SET_PAGE_VALID = "EDIT_POOL/SET_PAGE_VALID";
|
||||||
|
export const EDIT_POOL_SET_VALUE = "EDIT_POOL/SET_VALUE";
|
||||||
|
export const EDIT_POOL_SET_LOADING = "EDIT_POOL/SET_LOADING";
|
||||||
|
export const EDIT_POOL_RESET_FORM = "EDIT_POOL/RESET_FORM";
|
||||||
|
export const EDIT_POOL_SET_KEY_PAIR_VALUE = "EDIT_POOL/SET_KEY_PAIR_VALUE";
|
||||||
|
export const EDIT_POOL_SET_TOLERATION_VALUE = "EDIT_POOL/SET_TOLERATION_VALUE";
|
||||||
|
export const EDIT_POOL_ADD_NEW_TOLERATION = "EDIT_POOL/ADD_NEW_TOLERATION";
|
||||||
|
export const EDIT_POOL_REMOVE_TOLERATION_ROW =
|
||||||
|
"EDIT_POOL/REMOVE_TOLERATION_ROW";
|
||||||
|
|
||||||
export interface ICertificateInfo {
|
export interface ICertificateInfo {
|
||||||
name: string;
|
name: string;
|
||||||
serialNumber: string;
|
serialNumber: string;
|
||||||
@@ -367,6 +382,7 @@ export interface ITenantDetails {
|
|||||||
loadingTenant: boolean;
|
loadingTenant: boolean;
|
||||||
tenantInfo: ITenant | null;
|
tenantInfo: ITenant | null;
|
||||||
currentTab: string;
|
currentTab: string;
|
||||||
|
poolDetailsOpen: boolean;
|
||||||
selectedPool: string | null;
|
selectedPool: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,6 +390,7 @@ export interface ITenantState {
|
|||||||
createTenant: ICreateTenant;
|
createTenant: ICreateTenant;
|
||||||
tenantDetails: ITenantDetails;
|
tenantDetails: ITenantDetails;
|
||||||
addPool: IAddPool;
|
addPool: IAddPool;
|
||||||
|
editPool: IEditPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILabelKeyPair {
|
export interface ILabelKeyPair {
|
||||||
@@ -421,6 +438,29 @@ export interface IAddPool {
|
|||||||
fields: IAddPoolFields;
|
fields: IAddPoolFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IEditPoolSetup {
|
||||||
|
numberOfNodes: number;
|
||||||
|
volumeSize: number;
|
||||||
|
volumesPerServer: number;
|
||||||
|
storageClass: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEditPoolFields {
|
||||||
|
setup: IEditPoolSetup;
|
||||||
|
affinity: ITenantAffinity;
|
||||||
|
configuration: IPoolConfiguration;
|
||||||
|
tolerations: ITolerationModel[];
|
||||||
|
nodeSelectorPairs: LabelKeyPair[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEditPool {
|
||||||
|
editPoolLoading: boolean;
|
||||||
|
validPages: string[];
|
||||||
|
storageClasses: Opts[];
|
||||||
|
limitSize: any;
|
||||||
|
fields: IEditPoolFields;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ITenantIdentityProviderResponse {
|
export interface ITenantIdentityProviderResponse {
|
||||||
oidc?: {
|
oidc?: {
|
||||||
callback_url: string;
|
callback_url: string;
|
||||||
@@ -662,11 +702,68 @@ interface SetPoolSelectorKeyPairValueArray {
|
|||||||
newArray: LabelKeyPair[];
|
newArray: LabelKeyPair[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SetDetailsOpen {
|
||||||
|
type: typeof POOL_DETAILS_SET_OPEN_DETAILS;
|
||||||
|
state: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface SetSelectedPool {
|
interface SetSelectedPool {
|
||||||
type: typeof POOL_DETAILS_SET_SELECTED_POOL;
|
type: typeof POOL_DETAILS_SET_SELECTED_POOL;
|
||||||
pool: string | null;
|
pool: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EditPoolSetInitialInformation {
|
||||||
|
type: typeof EDIT_POOL_SET_INITIAL_INFO;
|
||||||
|
pool: IPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditPoolLoading {
|
||||||
|
type: typeof EDIT_POOL_SET_LOADING;
|
||||||
|
state: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResetEditPoolForm {
|
||||||
|
type: typeof EDIT_POOL_RESET_FORM;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SetEditPoolFieldValue {
|
||||||
|
type: typeof EDIT_POOL_SET_VALUE;
|
||||||
|
page: keyof IAddPoolFields;
|
||||||
|
field: string;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditPoolPageValid {
|
||||||
|
type: typeof EDIT_POOL_SET_PAGE_VALID;
|
||||||
|
page: string;
|
||||||
|
status: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditPoolStorageClasses {
|
||||||
|
type: typeof EDIT_POOL_SET_POOL_STORAGE_CLASSES;
|
||||||
|
storageClasses: Opts[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditPoolTolerationValue {
|
||||||
|
type: typeof EDIT_POOL_SET_TOLERATION_VALUE;
|
||||||
|
index: number;
|
||||||
|
toleration: ITolerationModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditPoolToleration {
|
||||||
|
type: typeof EDIT_POOL_ADD_NEW_TOLERATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditRemovePoolTolerationRow {
|
||||||
|
type: typeof EDIT_POOL_REMOVE_TOLERATION_ROW;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditPoolSelectorKeyPairValueArray {
|
||||||
|
type: typeof EDIT_POOL_SET_KEY_PAIR_VALUE;
|
||||||
|
newArray: LabelKeyPair[];
|
||||||
|
}
|
||||||
|
|
||||||
export type FieldsToHandle = INameTenantFields;
|
export type FieldsToHandle = INameTenantFields;
|
||||||
|
|
||||||
export type TenantsManagementTypes =
|
export type TenantsManagementTypes =
|
||||||
@@ -709,4 +806,15 @@ export type TenantsManagementTypes =
|
|||||||
| AddNewPoolToleration
|
| AddNewPoolToleration
|
||||||
| RemovePoolTolerationRow
|
| RemovePoolTolerationRow
|
||||||
| SetPoolSelectorKeyPairValueArray
|
| SetPoolSelectorKeyPairValueArray
|
||||||
| SetSelectedPool;
|
| SetSelectedPool
|
||||||
|
| SetDetailsOpen
|
||||||
|
| EditPoolLoading
|
||||||
|
| ResetEditPoolForm
|
||||||
|
| SetEditPoolFieldValue
|
||||||
|
| EditPoolPageValid
|
||||||
|
| EditPoolStorageClasses
|
||||||
|
| EditPoolTolerationValue
|
||||||
|
| EditPoolToleration
|
||||||
|
| EditRemovePoolTolerationRow
|
||||||
|
| EditPoolSelectorKeyPairValueArray
|
||||||
|
| EditPoolSetInitialInformation;
|
||||||
|
|||||||
Reference in New Issue
Block a user