Move EditPool redux state to it's own slice (#2063)
Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
@@ -14,16 +14,14 @@
|
||||
// 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 React, { Fragment, useEffect } from "react";
|
||||
import { useDispatch, useSelector } 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";
|
||||
@@ -33,30 +31,18 @@ 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 { niceBytes } from "../../../../../../common/utils";
|
||||
import {
|
||||
formFieldStyles,
|
||||
modalStyleUtils,
|
||||
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||
import { IEditPoolItem, IEditPoolRequest } from "../../../ListTenants/types";
|
||||
|
||||
import { AppState } from "../../../../../../store";
|
||||
import { ErrorResponseHandler } from "../../../../../../common/types";
|
||||
import { getDefaultAffinity, getNodeSelector } from "../../utils";
|
||||
import { setErrorSnackMessage } from "../../../../../../systemSlice";
|
||||
import {
|
||||
resetPoolForm,
|
||||
setInitialPoolDetails,
|
||||
setTenantDetailsLoad,
|
||||
} from "../../../tenantsSlice";
|
||||
import { resetEditPoolForm, setInitialPoolDetails } from "./editPoolSlice";
|
||||
import EditPoolButton from "./EditPoolButton";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
|
||||
interface IEditPoolProps {
|
||||
classes: any;
|
||||
open: boolean;
|
||||
match: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
bottomContainer: {
|
||||
display: "flex",
|
||||
@@ -81,12 +67,12 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
...formFieldStyles,
|
||||
...modalStyleUtils,
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
const requiredPages = ["setup", "affinity", "configure"];
|
||||
|
||||
const EditPool = ({ classes, open }: IEditPoolProps) => {
|
||||
const EditPool = () => {
|
||||
const dispatch = useDispatch();
|
||||
const classes = useStyles();
|
||||
|
||||
const tenant = useSelector(
|
||||
(state: AppState) => state.tenants.tenantDetails.tenantInfo
|
||||
@@ -94,47 +80,11 @@ const EditPool = ({ classes, open }: IEditPoolProps) => {
|
||||
const selectedPool = useSelector(
|
||||
(state: AppState) => state.tenants.tenantDetails.selectedPool
|
||||
);
|
||||
const selectedStorageClass = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.setup.storageClass
|
||||
);
|
||||
const validPages = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.validPages
|
||||
);
|
||||
const numberOfNodes = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.setup.numberOfNodes
|
||||
);
|
||||
const volumeSize = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.setup.volumeSize
|
||||
);
|
||||
const volumesPerServer = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.setup.volumesPerServer
|
||||
);
|
||||
const affinityType = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.affinity.podAffinity
|
||||
);
|
||||
const nodeSelectorLabels = useSelector(
|
||||
(state: AppState) =>
|
||||
state.tenants.editPool.fields.affinity.nodeSelectorLabels
|
||||
);
|
||||
const withPodAntiAffinity = useSelector(
|
||||
(state: AppState) =>
|
||||
state.tenants.editPool.fields.affinity.withPodAntiAffinity
|
||||
);
|
||||
const tolerations = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.tolerations
|
||||
);
|
||||
const securityContextEnabled = useSelector(
|
||||
(state: AppState) =>
|
||||
state.tenants.editPool.fields.configuration.securityContextEnabled
|
||||
);
|
||||
|
||||
const securityContext = useSelector(
|
||||
(state: AppState) =>
|
||||
state.tenants.editPool.fields.configuration.securityContext
|
||||
const editSending = useSelector(
|
||||
(state: AppState) => state.editPool.editSending
|
||||
);
|
||||
|
||||
const [editSending, setEditSending] = useState<boolean>(false);
|
||||
|
||||
const poolsURL = `/namespaces/${tenant?.namespace || ""}/tenants/${
|
||||
tenant?.name || ""
|
||||
}/pools`;
|
||||
@@ -153,131 +103,18 @@ const EditPool = ({ classes, open }: IEditPoolProps) => {
|
||||
}
|
||||
}, [selectedPool, dispatch, 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);
|
||||
dispatch(resetPoolForm());
|
||||
dispatch(setTenantDetailsLoad(true));
|
||||
history.push(poolsURL);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setEditSending(false);
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
});
|
||||
}
|
||||
}, [
|
||||
selectedPool,
|
||||
dispatch,
|
||||
editSending,
|
||||
poolsURL,
|
||||
affinityType,
|
||||
nodeSelectorLabels,
|
||||
numberOfNodes,
|
||||
securityContext,
|
||||
securityContextEnabled,
|
||||
selectedStorageClass,
|
||||
tenant,
|
||||
tolerations,
|
||||
volumeSize,
|
||||
volumesPerServer,
|
||||
withPodAntiAffinity,
|
||||
]);
|
||||
|
||||
const cancelButton = {
|
||||
label: "Cancel",
|
||||
type: "other",
|
||||
enabled: true,
|
||||
action: () => {
|
||||
dispatch(resetPoolForm());
|
||||
dispatch(resetEditPoolForm());
|
||||
history.push(poolsURL);
|
||||
},
|
||||
};
|
||||
|
||||
const createButton = {
|
||||
label: "Update",
|
||||
type: "submit",
|
||||
enabled:
|
||||
!editSending &&
|
||||
selectedStorageClass !== "" &&
|
||||
requiredPages.every((v) => validPages.includes(v)),
|
||||
action: () => {
|
||||
setEditSending(true);
|
||||
},
|
||||
componentRender: <EditPoolButton />,
|
||||
};
|
||||
|
||||
const wizardSteps: IWizardElement[] = [
|
||||
@@ -339,4 +176,4 @@ const EditPool = ({ classes, open }: IEditPoolProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(EditPool);
|
||||
export default EditPool;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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 { Button } from "@mui/material";
|
||||
import React from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppState } from "../../../../../../store";
|
||||
import { editPoolAsync } from "./thunks/editPoolAsync";
|
||||
|
||||
const EditPoolButton = () => {
|
||||
const dispatch = useDispatch();
|
||||
const requiredPages = ["setup", "affinity", "configure"];
|
||||
|
||||
const selectedStorageClass = useSelector(
|
||||
(state: AppState) => state.editPool.fields.setup.storageClass
|
||||
);
|
||||
const validPages = useSelector(
|
||||
(state: AppState) => state.editPool.validPages
|
||||
);
|
||||
|
||||
const editSending = useSelector(
|
||||
(state: AppState) => state.editPool.editSending
|
||||
);
|
||||
|
||||
const enabled =
|
||||
!editSending &&
|
||||
selectedStorageClass !== "" &&
|
||||
requiredPages.every((v) => validPages.includes(v));
|
||||
|
||||
return (
|
||||
<Button
|
||||
id={"wizard-button-Update"}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
dispatch(editPoolAsync());
|
||||
}}
|
||||
disabled={!enabled}
|
||||
key={`button-EditPool-Update`}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditPoolButton;
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
} from "../../../../../../utils/validationFunctions";
|
||||
import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import { isEditPoolPageValid, setEditPoolField } from "../../../tenantsSlice";
|
||||
import { isEditPoolPageValid, setEditPoolField } from "./editPoolSlice";
|
||||
|
||||
interface IConfigureProps {
|
||||
classes: any;
|
||||
@@ -83,11 +83,10 @@ const PoolConfiguration = ({ classes }: IConfigureProps) => {
|
||||
|
||||
const securityContextEnabled = useSelector(
|
||||
(state: AppState) =>
|
||||
state.tenants.editPool.fields.configuration.securityContextEnabled
|
||||
state.editPool.fields.configuration.securityContextEnabled
|
||||
);
|
||||
const securityContext = useSelector(
|
||||
(state: AppState) =>
|
||||
state.tenants.editPool.fields.configuration.securityContext
|
||||
(state: AppState) => state.editPool.fields.configuration.securityContext
|
||||
);
|
||||
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
|
||||
@@ -48,7 +48,7 @@ import {
|
||||
setEditPoolField,
|
||||
setEditPoolKeyValuePairs,
|
||||
setEditPoolTolerationInfo,
|
||||
} from "../../../tenantsSlice";
|
||||
} from "./editPoolSlice";
|
||||
|
||||
interface IAffinityProps {
|
||||
classes: any;
|
||||
@@ -119,21 +119,19 @@ const Affinity = ({ classes }: IAffinityProps) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const podAffinity = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.affinity.podAffinity
|
||||
(state: AppState) => state.editPool.fields.affinity.podAffinity
|
||||
);
|
||||
const nodeSelectorLabels = useSelector(
|
||||
(state: AppState) =>
|
||||
state.tenants.editPool.fields.affinity.nodeSelectorLabels
|
||||
(state: AppState) => state.editPool.fields.affinity.nodeSelectorLabels
|
||||
);
|
||||
const withPodAntiAffinity = useSelector(
|
||||
(state: AppState) =>
|
||||
state.tenants.editPool.fields.affinity.withPodAntiAffinity
|
||||
(state: AppState) => state.editPool.fields.affinity.withPodAntiAffinity
|
||||
);
|
||||
const keyValuePairs = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.nodeSelectorPairs
|
||||
(state: AppState) => state.editPool.fields.nodeSelectorPairs
|
||||
);
|
||||
const tolerations = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.tolerations
|
||||
(state: AppState) => state.editPool.fields.tolerations
|
||||
);
|
||||
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
isEditPoolPageValid,
|
||||
setEditPoolField,
|
||||
setEditPoolStorageClasses,
|
||||
} from "../../../tenantsSlice";
|
||||
} from "./editPoolSlice";
|
||||
|
||||
interface IPoolResourcesProps {
|
||||
classes: any;
|
||||
@@ -58,7 +58,7 @@ const styles = (theme: Theme) =>
|
||||
margin: "auto",
|
||||
justifyContent: "center",
|
||||
"& div": {
|
||||
width: 150,
|
||||
width: 200,
|
||||
"@media (max-width: 900px)": {
|
||||
flexFlow: "column",
|
||||
},
|
||||
@@ -90,19 +90,19 @@ const PoolResources = ({ classes }: IPoolResourcesProps) => {
|
||||
(state: AppState) => state.tenants.tenantDetails.tenantInfo
|
||||
);
|
||||
const storageClasses = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.storageClasses
|
||||
(state: AppState) => state.editPool.storageClasses
|
||||
);
|
||||
const numberOfNodes = useSelector((state: AppState) =>
|
||||
state.tenants.editPool.fields.setup.numberOfNodes.toString()
|
||||
state.editPool.fields.setup.numberOfNodes.toString()
|
||||
);
|
||||
const storageClass = useSelector(
|
||||
(state: AppState) => state.tenants.editPool.fields.setup.storageClass
|
||||
(state: AppState) => state.editPool.fields.setup.storageClass
|
||||
);
|
||||
const volumeSize = useSelector((state: AppState) =>
|
||||
state.tenants.editPool.fields.setup.volumeSize.toString()
|
||||
state.editPool.fields.setup.volumeSize.toString()
|
||||
);
|
||||
const volumesPerServer = useSelector((state: AppState) =>
|
||||
state.tenants.editPool.fields.setup.volumesPerServer.toString()
|
||||
state.editPool.fields.setup.volumesPerServer.toString()
|
||||
);
|
||||
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
// 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 { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { IEditPool, IEditPoolFields, PageFieldValue } from "./types";
|
||||
import {
|
||||
ITolerationEffect,
|
||||
ITolerationModel,
|
||||
ITolerationOperator,
|
||||
} from "../../../../../../common/types";
|
||||
import { IPool } from "../../../ListTenants/types";
|
||||
import { LabelKeyPair } from "../../../types";
|
||||
import { has } from "lodash";
|
||||
import get from "lodash/get";
|
||||
import { Opts } from "../../../ListTenants/utils";
|
||||
import { editPoolAsync } from "./thunks/editPoolAsync";
|
||||
|
||||
const initialState: IEditPool = {
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
editSending: false,
|
||||
};
|
||||
|
||||
export const editPoolSlice = createSlice({
|
||||
name: "editPool",
|
||||
initialState,
|
||||
reducers: {
|
||||
setInitialPoolDetails: (state, action: PayloadAction<IPool>) => {
|
||||
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.payload.affinity?.nodeAffinity) {
|
||||
podAffinity = "nodeSelector";
|
||||
if (action.payload.affinity?.podAntiAffinity) {
|
||||
withPodAntiAffinity = true;
|
||||
}
|
||||
} else if (action.payload.affinity?.podAntiAffinity) {
|
||||
podAffinity = "default";
|
||||
}
|
||||
|
||||
if (action.payload.affinity?.nodeAffinity) {
|
||||
let labelItems: string[] = [];
|
||||
nodeSelectorPairs = [];
|
||||
|
||||
action.payload.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.payload.securityContext) {
|
||||
securityContextOption =
|
||||
!!action.payload.securityContext.runAsUser ||
|
||||
!!action.payload.securityContext.runAsGroup ||
|
||||
!!action.payload.securityContext.fsGroup;
|
||||
}
|
||||
|
||||
if (action.payload.tolerations) {
|
||||
tolerations = action.payload.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.payload.volume_configuration.size / 1073741824;
|
||||
|
||||
const newPoolInfoFields: IEditPoolFields = {
|
||||
setup: {
|
||||
numberOfNodes: action.payload.servers,
|
||||
storageClass: action.payload.volume_configuration.storage_class_name,
|
||||
volumeSize: volSizeVars,
|
||||
volumesPerServer: action.payload.volumes_per_server,
|
||||
},
|
||||
configuration: {
|
||||
securityContextEnabled: securityContextOption,
|
||||
securityContext: {
|
||||
runAsUser: action.payload.securityContext?.runAsUser || "",
|
||||
runAsGroup: action.payload.securityContext?.runAsGroup || "",
|
||||
fsGroup: action.payload.securityContext?.fsGroup || "",
|
||||
runAsNonRoot: !!action.payload.securityContext?.runAsNonRoot,
|
||||
},
|
||||
},
|
||||
affinity: {
|
||||
podAffinity,
|
||||
withPodAntiAffinity,
|
||||
nodeSelectorLabels,
|
||||
},
|
||||
tolerations,
|
||||
nodeSelectorPairs,
|
||||
};
|
||||
|
||||
state.fields = {
|
||||
...state.fields,
|
||||
...newPoolInfoFields,
|
||||
};
|
||||
},
|
||||
setEditPoolLoading: (state, action: PayloadAction<boolean>) => {
|
||||
state.editPoolLoading = action.payload;
|
||||
},
|
||||
setEditPoolField: (state, action: PayloadAction<PageFieldValue>) => {
|
||||
if (has(state.fields, `${action.payload.page}.${action.payload.field}`)) {
|
||||
const originPageNameItems = get(
|
||||
state.fields,
|
||||
`${action.payload.page}`,
|
||||
{}
|
||||
);
|
||||
|
||||
let newValue: any = {};
|
||||
newValue[action.payload.field] = action.payload.value;
|
||||
|
||||
const joinValue = { ...originPageNameItems, ...newValue };
|
||||
|
||||
state.fields[action.payload.page] = { ...joinValue };
|
||||
}
|
||||
},
|
||||
isEditPoolPageValid: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
page: string;
|
||||
status: boolean;
|
||||
}>
|
||||
) => {
|
||||
const edPoolPV = [...state.validPages];
|
||||
|
||||
if (action.payload.status) {
|
||||
if (!edPoolPV.includes(action.payload.page)) {
|
||||
edPoolPV.push(action.payload.page);
|
||||
|
||||
state.validPages = [...edPoolPV];
|
||||
}
|
||||
} else {
|
||||
const newSetOfPages = edPoolPV.filter(
|
||||
(elm) => elm !== action.payload.page
|
||||
);
|
||||
|
||||
state.validPages = [...newSetOfPages];
|
||||
}
|
||||
},
|
||||
setEditPoolStorageClasses: (state, action: PayloadAction<Opts[]>) => {
|
||||
state.storageClasses = action.payload;
|
||||
},
|
||||
setEditPoolTolerationInfo: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
index: number;
|
||||
tolerationValue: ITolerationModel;
|
||||
}>
|
||||
) => {
|
||||
const editPoolTolerationValue = [...state.fields.tolerations];
|
||||
|
||||
editPoolTolerationValue[action.payload.index] =
|
||||
action.payload.tolerationValue;
|
||||
state.fields.tolerations = editPoolTolerationValue;
|
||||
},
|
||||
addNewEditPoolToleration: (state) => {
|
||||
state.fields.tolerations.push({
|
||||
key: "",
|
||||
tolerationSeconds: { seconds: 0 },
|
||||
value: "",
|
||||
effect: ITolerationEffect.NoSchedule,
|
||||
operator: ITolerationOperator.Equal,
|
||||
});
|
||||
},
|
||||
removeEditPoolToleration: (state, action: PayloadAction<number>) => {
|
||||
state.fields.tolerations = state.fields.tolerations.filter(
|
||||
(_, index) => index !== action.payload
|
||||
);
|
||||
},
|
||||
setEditPoolKeyValuePairs: (
|
||||
state,
|
||||
action: PayloadAction<LabelKeyPair[]>
|
||||
) => {
|
||||
state.fields.nodeSelectorPairs = action.payload;
|
||||
},
|
||||
resetEditPoolForm: () => initialState,
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(editPoolAsync.pending, (state, action) => {
|
||||
state.editSending = true;
|
||||
})
|
||||
.addCase(editPoolAsync.rejected, (state, action) => {
|
||||
state.editSending = false;
|
||||
})
|
||||
.addCase(editPoolAsync.fulfilled, (state, action) => {
|
||||
state.editSending = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
setInitialPoolDetails,
|
||||
setEditPoolLoading,
|
||||
resetEditPoolForm,
|
||||
setEditPoolField,
|
||||
isEditPoolPageValid,
|
||||
setEditPoolStorageClasses,
|
||||
setEditPoolTolerationInfo,
|
||||
addNewEditPoolToleration,
|
||||
removeEditPoolToleration,
|
||||
setEditPoolKeyValuePairs,
|
||||
} = editPoolSlice.actions;
|
||||
|
||||
export default editPoolSlice.reducer;
|
||||
@@ -0,0 +1,139 @@
|
||||
// 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 { createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import { AppState } from "../../../../../../../store";
|
||||
import api from "../../../../../../../common/api";
|
||||
import { ErrorResponseHandler } from "../../../../../../../common/types";
|
||||
import { setErrorSnackMessage } from "../../../../../../../systemSlice";
|
||||
import { generatePoolName } from "../../../../../../../common/utils";
|
||||
import { getDefaultAffinity, getNodeSelector } from "../../../utils";
|
||||
import { IEditPoolItem, IEditPoolRequest } from "../../../../ListTenants/types";
|
||||
import history from "../../../../../../../history";
|
||||
import { resetEditPoolForm } from "../editPoolSlice";
|
||||
import { setTenantDetailsLoad } from "../../../../tenantsSlice";
|
||||
|
||||
export const editPoolAsync = createAsyncThunk(
|
||||
"editPool/editPoolAsync",
|
||||
async (_, { getState, rejectWithValue, dispatch }) => {
|
||||
const state = getState() as AppState;
|
||||
|
||||
const tenant = state.tenants.tenantDetails.tenantInfo;
|
||||
const selectedPool = state.tenants.tenantDetails.selectedPool;
|
||||
const selectedStorageClass = state.editPool.fields.setup.storageClass;
|
||||
const numberOfNodes = state.editPool.fields.setup.numberOfNodes;
|
||||
const volumeSize = state.editPool.fields.setup.volumeSize;
|
||||
const volumesPerServer = state.editPool.fields.setup.volumesPerServer;
|
||||
const affinityType = state.editPool.fields.affinity.podAffinity;
|
||||
const nodeSelectorLabels =
|
||||
state.editPool.fields.affinity.nodeSelectorLabels;
|
||||
const withPodAntiAffinity =
|
||||
state.editPool.fields.affinity.withPodAntiAffinity;
|
||||
const tolerations = state.editPool.fields.tolerations;
|
||||
const securityContextEnabled =
|
||||
state.editPool.fields.configuration.securityContextEnabled;
|
||||
const securityContext = state.editPool.fields.configuration.securityContext;
|
||||
if (!tenant) {
|
||||
return;
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
};
|
||||
const poolsURL = `/namespaces/${tenant?.namespace || ""}/tenants/${
|
||||
tenant?.name || ""
|
||||
}/pools`;
|
||||
|
||||
return api
|
||||
.invoke(
|
||||
"PUT",
|
||||
`/api/v1/namespaces/${tenant.namespace}/tenants/${tenant.name}/pools`,
|
||||
data
|
||||
)
|
||||
.then(() => {
|
||||
dispatch(resetEditPoolForm());
|
||||
dispatch(setTenantDetailsLoad(true));
|
||||
history.push(poolsURL);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
dispatch(setErrorSnackMessage(err));
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 { ITolerationModel } from "../../../../../../common/types";
|
||||
import { Opts } from "../../../ListTenants/utils";
|
||||
import {
|
||||
IPoolConfiguration,
|
||||
ITenantAffinity,
|
||||
LabelKeyPair,
|
||||
} from "../../../types";
|
||||
|
||||
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;
|
||||
editSending: boolean;
|
||||
}
|
||||
|
||||
export interface PageFieldValue {
|
||||
page: keyof IEditPoolFields;
|
||||
field: string;
|
||||
value: any;
|
||||
}
|
||||
@@ -15,12 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import {
|
||||
IAddPoolFields,
|
||||
IEditPoolFields,
|
||||
ITenantState,
|
||||
LabelKeyPair,
|
||||
} from "./types";
|
||||
import { IAddPoolFields, ITenantState, LabelKeyPair } from "./types";
|
||||
import {
|
||||
ITolerationEffect,
|
||||
ITolerationModel,
|
||||
@@ -29,7 +24,7 @@ import {
|
||||
import get from "lodash/get";
|
||||
import { has } from "lodash";
|
||||
import { Opts } from "./ListTenants/utils";
|
||||
import { IPool, ITenant } from "./ListTenants/types";
|
||||
import { ITenant } from "./ListTenants/types";
|
||||
|
||||
export interface FileValue {
|
||||
fileName: string;
|
||||
@@ -49,12 +44,6 @@ export interface CertificateFile {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface PageFieldValue {
|
||||
page: keyof IEditPoolFields;
|
||||
field: string;
|
||||
value: any;
|
||||
}
|
||||
|
||||
const initialState: ITenantState = {
|
||||
tenantDetails: {
|
||||
currentTenant: "",
|
||||
@@ -103,44 +92,6 @@ 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 const tenantSlice = createSlice({
|
||||
@@ -295,226 +246,6 @@ export const tenantSlice = createSlice({
|
||||
setOpenPoolDetails: (state, action: PayloadAction<boolean>) => {
|
||||
state.tenantDetails.poolDetailsOpen = action.payload;
|
||||
},
|
||||
setInitialPoolDetails: (state, action: PayloadAction<IPool>) => {
|
||||
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.payload.affinity?.nodeAffinity) {
|
||||
podAffinity = "nodeSelector";
|
||||
if (action.payload.affinity?.podAntiAffinity) {
|
||||
withPodAntiAffinity = true;
|
||||
}
|
||||
} else if (action.payload.affinity?.podAntiAffinity) {
|
||||
podAffinity = "default";
|
||||
}
|
||||
|
||||
if (action.payload.affinity?.nodeAffinity) {
|
||||
let labelItems: string[] = [];
|
||||
nodeSelectorPairs = [];
|
||||
|
||||
action.payload.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.payload.securityContext) {
|
||||
securityContextOption =
|
||||
!!action.payload.securityContext.runAsUser ||
|
||||
!!action.payload.securityContext.runAsGroup ||
|
||||
!!action.payload.securityContext.fsGroup;
|
||||
}
|
||||
|
||||
if (action.payload.tolerations) {
|
||||
tolerations = action.payload.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.payload.volume_configuration.size / 1073741824;
|
||||
|
||||
const newPoolInfoFields: IEditPoolFields = {
|
||||
setup: {
|
||||
numberOfNodes: action.payload.servers,
|
||||
storageClass: action.payload.volume_configuration.storage_class_name,
|
||||
volumeSize: volSizeVars,
|
||||
volumesPerServer: action.payload.volumes_per_server,
|
||||
},
|
||||
configuration: {
|
||||
securityContextEnabled: securityContextOption,
|
||||
securityContext: {
|
||||
runAsUser: action.payload.securityContext?.runAsUser || "",
|
||||
runAsGroup: action.payload.securityContext?.runAsGroup || "",
|
||||
fsGroup: action.payload.securityContext?.fsGroup || "",
|
||||
runAsNonRoot: !!action.payload.securityContext?.runAsNonRoot,
|
||||
},
|
||||
},
|
||||
affinity: {
|
||||
podAffinity,
|
||||
withPodAntiAffinity,
|
||||
nodeSelectorLabels,
|
||||
},
|
||||
tolerations,
|
||||
nodeSelectorPairs,
|
||||
};
|
||||
|
||||
state.editPool.fields = {
|
||||
...state.editPool.fields,
|
||||
...newPoolInfoFields,
|
||||
};
|
||||
},
|
||||
setEditPoolLoading: (state, action: PayloadAction<boolean>) => {
|
||||
state.editPool.editPoolLoading = action.payload;
|
||||
},
|
||||
setEditPoolField: (state, action: PayloadAction<PageFieldValue>) => {
|
||||
if (
|
||||
has(
|
||||
state.editPool.fields,
|
||||
`${action.payload.page}.${action.payload.field}`
|
||||
)
|
||||
) {
|
||||
const originPageNameItems = get(
|
||||
state.editPool.fields,
|
||||
`${action.payload.page}`,
|
||||
{}
|
||||
);
|
||||
|
||||
let newValue: any = {};
|
||||
newValue[action.payload.field] = action.payload.value;
|
||||
|
||||
const joinValue = { ...originPageNameItems, ...newValue };
|
||||
|
||||
state.editPool.fields[action.payload.page] = { ...joinValue };
|
||||
}
|
||||
},
|
||||
isEditPoolPageValid: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
page: string;
|
||||
status: boolean;
|
||||
}>
|
||||
) => {
|
||||
const edPoolPV = [...state.editPool.validPages];
|
||||
|
||||
if (action.payload.status) {
|
||||
if (!edPoolPV.includes(action.payload.page)) {
|
||||
edPoolPV.push(action.payload.page);
|
||||
|
||||
state.editPool.validPages = [...edPoolPV];
|
||||
}
|
||||
} else {
|
||||
const newSetOfPages = edPoolPV.filter(
|
||||
(elm) => elm !== action.payload.page
|
||||
);
|
||||
|
||||
state.editPool.validPages = [...newSetOfPages];
|
||||
}
|
||||
},
|
||||
setEditPoolStorageClasses: (state, action: PayloadAction<Opts[]>) => {
|
||||
state.editPool.storageClasses = action.payload;
|
||||
},
|
||||
setEditPoolTolerationInfo: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
index: number;
|
||||
tolerationValue: ITolerationModel;
|
||||
}>
|
||||
) => {
|
||||
const editPoolTolerationValue = [...state.editPool.fields.tolerations];
|
||||
|
||||
editPoolTolerationValue[action.payload.index] =
|
||||
action.payload.tolerationValue;
|
||||
state.editPool.fields.tolerations = editPoolTolerationValue;
|
||||
},
|
||||
addNewEditPoolToleration: (state) => {
|
||||
state.editPool.fields.tolerations.push({
|
||||
key: "",
|
||||
tolerationSeconds: { seconds: 0 },
|
||||
value: "",
|
||||
effect: ITolerationEffect.NoSchedule,
|
||||
operator: ITolerationOperator.Equal,
|
||||
});
|
||||
},
|
||||
removeEditPoolToleration: (state, action: PayloadAction<number>) => {
|
||||
state.editPool.fields.tolerations =
|
||||
state.editPool.fields.tolerations.filter(
|
||||
(_, index) => index !== action.payload
|
||||
);
|
||||
},
|
||||
setEditPoolKeyValuePairs: (
|
||||
state,
|
||||
action: PayloadAction<LabelKeyPair[]>
|
||||
) => {
|
||||
state.editPool.fields.nodeSelectorPairs = action.payload;
|
||||
},
|
||||
resetEditPoolForm: (state) => {
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -535,16 +266,6 @@ export const {
|
||||
setPoolKeyValuePairs,
|
||||
setSelectedPool,
|
||||
setOpenPoolDetails,
|
||||
setInitialPoolDetails,
|
||||
setEditPoolLoading,
|
||||
resetEditPoolForm,
|
||||
setEditPoolField,
|
||||
isEditPoolPageValid,
|
||||
setEditPoolStorageClasses,
|
||||
setEditPoolTolerationInfo,
|
||||
addNewEditPoolToleration,
|
||||
removeEditPoolToleration,
|
||||
setEditPoolKeyValuePairs,
|
||||
} = tenantSlice.actions;
|
||||
|
||||
export default tenantSlice.reducer;
|
||||
|
||||
@@ -291,7 +291,6 @@ export interface ITenantDetails {
|
||||
export interface ITenantState {
|
||||
tenantDetails: ITenantDetails;
|
||||
addPool: IAddPool;
|
||||
editPool: IEditPool;
|
||||
}
|
||||
|
||||
export interface ILabelKeyPair {
|
||||
@@ -339,29 +338,6 @@ export interface IAddPool {
|
||||
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 {
|
||||
oidc?: {
|
||||
callback_url: string;
|
||||
|
||||
@@ -27,6 +27,7 @@ import tenantsReducer from "./screens/Console/Tenants/tenantsSlice";
|
||||
import dashboardReducer from "./screens/Console/Dashboard/dashboardSlice";
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import createTenantReducer from "./screens/Console/Tenants/AddTenant/createTenantSlice";
|
||||
import editPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/EditPool/editPoolSlice";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
@@ -43,12 +44,12 @@ export const store = configureStore({
|
||||
// Operator Reducers
|
||||
tenants: tenantsReducer,
|
||||
createTenant: createTenantReducer,
|
||||
editPool: editPoolReducer,
|
||||
},
|
||||
});
|
||||
|
||||
export type AppState = ReturnType<typeof store.getState>;
|
||||
|
||||
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
|
||||
export default store;
|
||||
|
||||
Reference in New Issue
Block a user