From 94e419e09c2ea312cacb9aabf703d42e3174b829 Mon Sep 17 00:00:00 2001 From: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> Date: Thu, 2 Jun 2022 12:11:31 -0700 Subject: [PATCH] Add Pool Slice and Tenants Slice simplification (#2074) Add Pool Slice and Tenants Slice simplification Flatten Slice AddPool Thunk Return HMR support for Redux Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> --- .../Tenants/TenantDetails/KeyPairView.tsx | 38 +--- .../Tenants/TenantDetails/PodsSummary.tsx | 2 +- .../TenantDetails/Pools/AddPool/AddPool.tsx | 165 ++------------ .../Pools/AddPool/AddPoolCreateButton.tsx | 54 +++++ .../Pools/AddPool/PoolConfiguration.tsx | 8 +- .../Pools/AddPool/PoolPodPlacement.tsx | 14 +- .../Pools/AddPool/PoolResources.tsx | 16 +- .../Pools/AddPool/addPoolSlice.ts | 190 ++++++++++++++++ .../Pools/AddPool/addPoolThunks.ts | 107 +++++++++ .../Pools/Details/PoolDetails.tsx | 6 +- .../Pools/Details/PoolsListing.tsx | 6 +- .../TenantDetails/Pools/EditPool/EditPool.tsx | 6 +- .../Pools/EditPool/EditPoolResources.tsx | 4 +- .../Pools/EditPool/thunks/editPoolAsync.ts | 4 +- .../Tenants/TenantDetails/PoolsSummary.tsx | 4 +- .../Tenants/TenantDetails/TenantDetails.tsx | 10 +- .../TenantDetails/TenantEncryption.tsx | 4 +- .../Tenants/TenantDetails/TenantEvents.tsx | 2 +- .../TenantDetails/TenantIdentityProvider.tsx | 6 +- .../Tenants/TenantDetails/TenantLicense.tsx | 6 +- .../Tenants/TenantDetails/TenantLogging.tsx | 6 +- .../Tenants/TenantDetails/TenantSecurity.tsx | 6 +- .../Tenants/TenantDetails/TenantSummary.tsx | 16 +- .../Tenants/TenantDetails/VolumesSummary.tsx | 2 +- .../TenantDetails/pods/PodDescribe.tsx | 2 +- .../Tenants/TenantDetails/pods/PodEvents.tsx | 2 +- .../Tenants/TenantDetails/pods/PodLogs.tsx | 2 +- .../TenantDetails/pvcs/PVCDescribe.tsx | 2 +- .../screens/Console/Tenants/tenantsSlice.ts | 215 ++---------------- .../Tenants/thunks/tenantDetailsAsync.ts | 4 +- .../src/screens/Console/Tenants/types.ts | 24 +- portal-ui/src/store.ts | 46 ++-- 32 files changed, 484 insertions(+), 495 deletions(-) create mode 100644 portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/AddPoolCreateButton.tsx create mode 100644 portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolSlice.ts create mode 100644 portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolThunks.ts diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairView.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairView.tsx index 69cfb3959..a81b18bf9 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairView.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/KeyPairView.tsx @@ -63,33 +63,17 @@ const KeyPairView = ({ classes, records, recordName }: IKeyPairView) => { }; const mapState = (state: AppState) => ({ - loadingTenant: state.tenants.tenantDetails.loadingTenant, - selectedTenant: state.tenants.tenantDetails.currentTenant, - tenant: state.tenants.tenantDetails.tenantInfo, - logEnabled: get(state.tenants.tenantDetails.tenantInfo, "logEnabled", false), - monitoringEnabled: get( - state.tenants.tenantDetails.tenantInfo, - "monitoringEnabled", - false - ), - encryptionEnabled: get( - state.tenants.tenantDetails.tenantInfo, - "encryptionEnabled", - false - ), - minioTLS: get(state.tenants.tenantDetails.tenantInfo, "minioTLS", false), - consoleTLS: get(state.tenants.tenantDetails.tenantInfo, "consoleTLS", false), - consoleEnabled: get( - state.tenants.tenantDetails.tenantInfo, - "consoleEnabled", - false - ), - adEnabled: get(state.tenants.tenantDetails.tenantInfo, "idpAdEnabled", false), - oidcEnabled: get( - state.tenants.tenantDetails.tenantInfo, - "idpOidcEnabled", - false - ), + loadingTenant: state.tenants.loadingTenant, + selectedTenant: state.tenants.currentTenant, + tenant: state.tenants.tenantInfo, + logEnabled: get(state.tenants.tenantInfo, "logEnabled", false), + monitoringEnabled: get(state.tenants.tenantInfo, "monitoringEnabled", false), + encryptionEnabled: get(state.tenants.tenantInfo, "encryptionEnabled", false), + minioTLS: get(state.tenants.tenantInfo, "minioTLS", false), + consoleTLS: get(state.tenants.tenantInfo, "consoleTLS", false), + consoleEnabled: get(state.tenants.tenantInfo, "consoleEnabled", false), + adEnabled: get(state.tenants.tenantInfo, "idpAdEnabled", false), + oidcEnabled: get(state.tenants.tenantInfo, "idpOidcEnabled", false), }); const connector = connect(mapState, null); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/PodsSummary.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/PodsSummary.tsx index 7be5bad20..f34bc06ff 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/PodsSummary.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/PodsSummary.tsx @@ -53,7 +53,7 @@ const PodsSummary = ({ classes, match, history }: IPodsSummary) => { const dispatch = useDispatch(); const loadingTenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.loadingTenant + (state: AppState) => state.tenants.loadingTenant ); const [pods, setPods] = useState([]); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/AddPool.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/AddPool.tsx index af8159fce..30051426f 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/AddPool.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/AddPool.tsx @@ -14,18 +14,16 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -import React, { Fragment, useEffect, useState } from "react"; +import React, { Fragment } from "react"; import { Theme } from "@mui/material/styles"; import createStyles from "@mui/styles/createStyles"; -import withStyles from "@mui/styles/withStyles"; import { formFieldStyles, modalStyleUtils, } from "../../../../Common/FormComponents/common/styleLibrary"; import Grid from "@mui/material/Grid"; -import { generatePoolName, niceBytes } from "../../../../../../common/utils"; +import { niceBytes } from "../../../../../../common/utils"; import { LinearProgress } from "@mui/material"; -import { IAddPoolRequest } from "../../../ListTenants/types"; import PageHeader from "../../../../Common/PageHeader/PageHeader"; import PageLayout from "../../../../Common/Layout/PageLayout"; import GenericWizard from "../../../../Common/GenericWizard/GenericWizard"; @@ -39,20 +37,12 @@ import { AppState } from "../../../../../../store"; import { useDispatch, useSelector } from "react-redux"; import PoolConfiguration from "./PoolConfiguration"; import PoolPodPlacement from "./PoolPodPlacement"; -import { ErrorResponseHandler } from "../../../../../../common/types"; -import { getDefaultAffinity, getNodeSelector } from "../../utils"; -import api from "../../../../../../common/api"; import BackLink from "../../../../../../common/BackLink"; -import { setErrorSnackMessage } from "../../../../../../systemSlice"; -import { resetPoolForm, setTenantDetailsLoad } from "../../../tenantsSlice"; +import { resetPoolForm } from "./addPoolSlice"; +import AddPoolCreateButton from "./AddPoolCreateButton"; +import makeStyles from "@mui/styles/makeStyles"; -interface IAddPoolProps { - classes: any; - open: boolean; - match: any; -} - -const styles = (theme: Theme) => +const useStyles = makeStyles((theme: Theme) => createStyles({ bottomContainer: { display: "flex", @@ -77,136 +67,20 @@ const styles = (theme: Theme) => }, ...formFieldStyles, ...modalStyleUtils, - }); + }) +); -const requiredPages = ["setup", "affinity", "configure"]; - -const AddPool = ({ classes, open, match }: IAddPoolProps) => { +const AddPool = () => { const dispatch = useDispatch(); + const classes = useStyles(); - const tenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo - ); - const selectedStorageClass = useSelector( - (state: AppState) => state.tenants.addPool.fields.setup.storageClass - ); - const validPages = useSelector( - (state: AppState) => state.tenants.addPool.validPages - ); - const numberOfNodes = useSelector( - (state: AppState) => state.tenants.addPool.fields.setup.numberOfNodes - ); - const volumeSize = useSelector( - (state: AppState) => state.tenants.addPool.fields.setup.volumeSize - ); - const volumesPerServer = useSelector( - (state: AppState) => state.tenants.addPool.fields.setup.volumesPerServer - ); - const affinityType = useSelector( - (state: AppState) => state.tenants.addPool.fields.affinity.podAffinity - ); - const nodeSelectorLabels = useSelector( - (state: AppState) => - state.tenants.addPool.fields.affinity.nodeSelectorLabels - ); - const withPodAntiAffinity = useSelector( - (state: AppState) => - state.tenants.addPool.fields.affinity.withPodAntiAffinity - ); - const tolerations = useSelector( - (state: AppState) => state.tenants.addPool.fields.tolerations - ); - const securityContextEnabled = useSelector( - (state: AppState) => - state.tenants.addPool.fields.configuration.securityContextEnabled - ); - const securityContext = useSelector( - (state: AppState) => - state.tenants.addPool.fields.configuration.securityContext - ); - - const [addSending, setAddSending] = useState(false); + const tenant = useSelector((state: AppState) => state.tenants.tenantInfo); + const sending = useSelector((state: AppState) => state.addPool.sending); const poolsURL = `/namespaces/${tenant?.namespace || ""}/tenants/${ tenant?.name || "" }/pools`; - useEffect(() => { - if (addSending && 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 data: IAddPoolRequest = { - name: 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( - "POST", - `/api/v1/namespaces/${tenant.namespace}/tenants/${tenant.name}/pools`, - data - ) - .then(() => { - setAddSending(false); - dispatch(resetPoolForm()); - dispatch(setTenantDetailsLoad(true)); - history.push(poolsURL); - }) - .catch((err: ErrorResponseHandler) => { - setAddSending(false); - dispatch(setErrorSnackMessage(err)); - }); - } - }, [ - addSending, - poolsURL, - affinityType, - nodeSelectorLabels, - numberOfNodes, - securityContext, - securityContextEnabled, - selectedStorageClass, - tenant, - tolerations, - volumeSize, - volumesPerServer, - withPodAntiAffinity, - dispatch, - ]); - const cancelButton = { label: "Cancel", type: "other", @@ -218,15 +92,7 @@ const AddPool = ({ classes, open, match }: IAddPoolProps) => { }; const createButton = { - label: "Create", - type: "submit", - enabled: - !addSending && - selectedStorageClass !== "" && - requiredPages.every((v) => validPages.includes(v)), - action: () => { - setAddSending(true); - }, + componentRender: , }; const wizardSteps: IWizardElement[] = [ @@ -272,8 +138,7 @@ const AddPool = ({ classes, open, match }: IAddPoolProps) => { } /> - - {addSending && ( + {sending && ( @@ -287,4 +152,4 @@ const AddPool = ({ classes, open, match }: IAddPoolProps) => { ); }; -export default withStyles(styles)(AddPool); +export default AddPool; diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/AddPoolCreateButton.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/AddPoolCreateButton.tsx new file mode 100644 index 000000000..a6ca3a7c4 --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/AddPoolCreateButton.tsx @@ -0,0 +1,54 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { Button } from "@mui/material"; +import React from "react"; +import { addPoolAsync } from "./addPoolThunks"; +import { useDispatch, useSelector } from "react-redux"; +import { AppState } from "../../../../../../store"; + +const AddPoolCreateButton = () => { + const dispatch = useDispatch(); + + const selectedStorageClass = useSelector( + (state: AppState) => state.addPool.setup.storageClass + ); + const validPages = useSelector((state: AppState) => state.addPool.validPages); + + const sending = useSelector((state: AppState) => state.addPool.sending); + const requiredPages = ["setup", "affinity", "configure"]; + const enabled = + !sending && + selectedStorageClass !== "" && + requiredPages.every((v) => validPages.includes(v)); + return ( + + ); +}; + +export default AddPoolCreateButton; diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolConfiguration.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolConfiguration.tsx index 1ecf43c54..198601139 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolConfiguration.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolConfiguration.tsx @@ -33,7 +33,7 @@ import { } from "../../../../../../utils/validationFunctions"; import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper"; -import { isPoolPageValid, setPoolField } from "../../../tenantsSlice"; +import { isPoolPageValid, setPoolField } from "./addPoolSlice"; interface IConfigureProps { classes: any; @@ -81,12 +81,10 @@ const PoolConfiguration = ({ classes }: IConfigureProps) => { const dispatch = useDispatch(); const securityContextEnabled = useSelector( - (state: AppState) => - state.tenants.addPool.fields.configuration.securityContextEnabled + (state: AppState) => state.addPool.configuration.securityContextEnabled ); const securityContext = useSelector( - (state: AppState) => - state.tenants.addPool.fields.configuration.securityContext + (state: AppState) => state.addPool.configuration.securityContext ); const [validationErrors, setValidationErrors] = useState({}); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolPodPlacement.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolPodPlacement.tsx index fd7b6aa06..e18c0b31d 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolPodPlacement.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolPodPlacement.tsx @@ -48,7 +48,7 @@ import { setPoolField, setPoolKeyValuePairs, setPoolTolerationInfo, -} from "../../../tenantsSlice"; +} from "./addPoolSlice"; interface IAffinityProps { classes: any; @@ -119,21 +119,19 @@ const Affinity = ({ classes }: IAffinityProps) => { const dispatch = useDispatch(); const podAffinity = useSelector( - (state: AppState) => state.tenants.addPool.fields.affinity.podAffinity + (state: AppState) => state.addPool.affinity.podAffinity ); const nodeSelectorLabels = useSelector( - (state: AppState) => - state.tenants.addPool.fields.affinity.nodeSelectorLabels + (state: AppState) => state.addPool.affinity.nodeSelectorLabels ); const withPodAntiAffinity = useSelector( - (state: AppState) => - state.tenants.addPool.fields.affinity.withPodAntiAffinity + (state: AppState) => state.addPool.affinity.withPodAntiAffinity ); const keyValuePairs = useSelector( - (state: AppState) => state.tenants.addPool.fields.nodeSelectorPairs + (state: AppState) => state.addPool.nodeSelectorPairs ); const tolerations = useSelector( - (state: AppState) => state.tenants.addPool.fields.tolerations + (state: AppState) => state.addPool.tolerations ); const [validationErrors, setValidationErrors] = useState({}); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolResources.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolResources.tsx index 613ead20f..35cfcb1c8 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolResources.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/PoolResources.tsx @@ -43,7 +43,7 @@ import { isPoolPageValid, setPoolField, setPoolStorageClasses, -} from "../../../tenantsSlice"; +} from "./addPoolSlice"; interface IPoolResourcesProps { classes: any; @@ -86,23 +86,21 @@ const styles = (theme: Theme) => const PoolResources = ({ classes }: IPoolResourcesProps) => { const dispatch = useDispatch(); - const tenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo - ); + const tenant = useSelector((state: AppState) => state.tenants.tenantInfo); const storageClasses = useSelector( - (state: AppState) => state.tenants.addPool.storageClasses + (state: AppState) => state.addPool.storageClasses ); const numberOfNodes = useSelector((state: AppState) => - state.tenants.addPool.fields.setup.numberOfNodes.toString() + state.addPool.setup.numberOfNodes.toString() ); const storageClass = useSelector( - (state: AppState) => state.tenants.addPool.fields.setup.storageClass + (state: AppState) => state.addPool.setup.storageClass ); const volumeSize = useSelector((state: AppState) => - state.tenants.addPool.fields.setup.volumeSize.toString() + state.addPool.setup.volumeSize.toString() ); const volumesPerServer = useSelector((state: AppState) => - state.tenants.addPool.fields.setup.volumesPerServer.toString() + state.addPool.setup.volumesPerServer.toString() ); const [validationErrors, setValidationErrors] = useState({}); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolSlice.ts b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolSlice.ts new file mode 100644 index 000000000..253c5b0de --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolSlice.ts @@ -0,0 +1,190 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { + ITolerationEffect, + ITolerationModel, + ITolerationOperator, +} from "../../../../../../common/types"; +import { + IAddPoolSetup, + IPoolConfiguration, + ITenantAffinity, + LabelKeyPair, +} from "../../../types"; +import { has } from "lodash"; +import get from "lodash/get"; +import { Opts } from "../../../ListTenants/utils"; +import { addPoolAsync } from "./addPoolThunks"; + +export interface IAddPool { + addPoolLoading: boolean; + sending: boolean; + validPages: string[]; + storageClasses: Opts[]; + limitSize: any; + setup: IAddPoolSetup; + affinity: ITenantAffinity; + configuration: IPoolConfiguration; + tolerations: ITolerationModel[]; + nodeSelectorPairs: LabelKeyPair[]; +} + +const initialState: IAddPool = { + addPoolLoading: false, + sending: false, + validPages: ["affinity", "configure"], + storageClasses: [], + limitSize: {}, + 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 addPoolSlice = createSlice({ + name: "addPool", + initialState, + reducers: { + setPoolLoading: (state, action: PayloadAction) => { + state.addPoolLoading = action.payload; + }, + setPoolField: ( + state, + action: PayloadAction<{ + page: + | "setup" + | "affinity" + | "configuration" + | "tolerations" + | "nodeSelectorPairs"; + field: string; + value: any; + }> + ) => { + if (has(state, `${action.payload.page}.${action.payload.field}`)) { + const originPageNameItems = get(state, `${action.payload.page}`, {}); + + let newValue: any = {}; + newValue[action.payload.field] = action.payload.value; + + state[action.payload.page] = { + ...originPageNameItems, + ...newValue, + }; + } + }, + isPoolPageValid: ( + state, + action: PayloadAction<{ + page: string; + status: boolean; + }> + ) => { + if (action.payload.status) { + if (!state.validPages.includes(action.payload.page)) { + state.validPages.push(action.payload.page); + } + } else { + state.validPages = state.validPages.filter( + (elm) => elm !== action.payload.page + ); + } + }, + setPoolStorageClasses: (state, action: PayloadAction) => { + state.storageClasses = action.payload; + }, + setPoolTolerationInfo: ( + state, + action: PayloadAction<{ + index: number; + tolerationValue: ITolerationModel; + }> + ) => { + state.tolerations[action.payload.index] = action.payload.tolerationValue; + }, + addNewPoolToleration: (state) => { + state.tolerations.push({ + key: "", + tolerationSeconds: { seconds: 0 }, + value: "", + effect: ITolerationEffect.NoSchedule, + operator: ITolerationOperator.Equal, + }); + }, + removePoolToleration: (state, action: PayloadAction) => { + state.tolerations = state.tolerations.filter( + (_, index) => index !== action.payload + ); + }, + setPoolKeyValuePairs: (state, action: PayloadAction) => { + state.nodeSelectorPairs = action.payload; + }, + resetPoolForm: () => initialState, + }, + extraReducers: (builder) => { + builder + .addCase(addPoolAsync.pending, (state) => { + state.sending = true; + }) + .addCase(addPoolAsync.rejected, (state) => { + state.sending = false; + }) + .addCase(addPoolAsync.fulfilled, (state, action) => { + state.sending = false; + }); + }, +}); + +export const { + setPoolLoading, + resetPoolForm, + setPoolField, + isPoolPageValid, + setPoolStorageClasses, + setPoolTolerationInfo, + addNewPoolToleration, + removePoolToleration, + setPoolKeyValuePairs, +} = addPoolSlice.actions; + +export default addPoolSlice.reducer; diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolThunks.ts b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolThunks.ts new file mode 100644 index 000000000..54f91a9d3 --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolThunks.ts @@ -0,0 +1,107 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2022 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { AppState } from "../../../../../../store"; +import api from "../../../../../../common/api"; +import { IAddPoolRequest } from "../../../ListTenants/types"; +import { generatePoolName } from "../../../../../../common/utils"; +import { ErrorResponseHandler } from "../../../../../../common/types"; +import { setErrorSnackMessage } from "../../../../../../systemSlice"; +import { getDefaultAffinity, getNodeSelector } from "../../utils"; +import { resetPoolForm } from "./addPoolSlice"; +import { getTenantAsync } from "../../../thunks/tenantDetailsAsync"; +import history from "../../../../../../history"; + +export const addPoolAsync = createAsyncThunk( + "addPool/addPoolAsync", + async (_, { getState, rejectWithValue, dispatch }) => { + const state = getState() as AppState; + + const tenant = state.tenants.tenantInfo; + const selectedStorageClass = state.addPool.setup.storageClass; + const numberOfNodes = state.addPool.setup.numberOfNodes; + const volumeSize = state.addPool.setup.volumeSize; + const volumesPerServer = state.addPool.setup.volumesPerServer; + const affinityType = state.addPool.affinity.podAffinity; + const nodeSelectorLabels = state.addPool.affinity.nodeSelectorLabels; + const withPodAntiAffinity = state.addPool.affinity.withPodAntiAffinity; + const tolerations = state.addPool.tolerations; + const securityContextEnabled = + state.addPool.configuration.securityContextEnabled; + const securityContext = state.addPool.configuration.securityContext; + if (tenant === null) { + 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 data: IAddPoolRequest = { + name: 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( + "POST", + `/api/v1/namespaces/${tenant.namespace}/tenants/${tenant.name}/pools`, + data + ) + .then(() => { + dispatch(resetPoolForm()); + dispatch(getTenantAsync()); + history.push(poolsURL); + }) + .catch((err: ErrorResponseHandler) => { + dispatch(setErrorSnackMessage(err)); + }); + } +); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/Details/PoolDetails.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/Details/PoolDetails.tsx index d71e6cd89..149054931 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/Details/PoolDetails.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/Details/PoolDetails.tsx @@ -66,11 +66,9 @@ const twoColCssGridLayoutConfig = { }; const PoolDetails = ({ history }: IPoolDetails) => { - const tenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo - ); + const tenant = useSelector((state: AppState) => state.tenants.tenantInfo); const selectedPool = useSelector( - (state: AppState) => state.tenants.tenantDetails.selectedPool + (state: AppState) => state.tenants.selectedPool ); const poolInformation = diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/Details/PoolsListing.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/Details/PoolsListing.tsx index 0e3781975..ec767afb8 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/Details/PoolsListing.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/Details/PoolsListing.tsx @@ -59,11 +59,9 @@ const PoolsListing = ({ const dispatch = useDispatch(); const loadingTenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.loadingTenant - ); - const tenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo + (state: AppState) => state.tenants.loadingTenant ); + const tenant = useSelector((state: AppState) => state.tenants.tenantInfo); const [pools, setPools] = useState([]); const [filter, setFilter] = useState(""); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/EditPool.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/EditPool.tsx index 6b436dcfd..e35d3a908 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/EditPool.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/EditPool.tsx @@ -74,11 +74,9 @@ const EditPool = () => { const dispatch = useDispatch(); const classes = useStyles(); - const tenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo - ); + const tenant = useSelector((state: AppState) => state.tenants.tenantInfo); const selectedPool = useSelector( - (state: AppState) => state.tenants.tenantDetails.selectedPool + (state: AppState) => state.tenants.selectedPool ); const editSending = useSelector( diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/EditPoolResources.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/EditPoolResources.tsx index c0831ea8e..1ae1e498a 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/EditPoolResources.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/EditPoolResources.tsx @@ -86,9 +86,7 @@ const styles = (theme: Theme) => const PoolResources = ({ classes }: IPoolResourcesProps) => { const dispatch = useDispatch(); - const tenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo - ); + const tenant = useSelector((state: AppState) => state.tenants.tenantInfo); const storageClasses = useSelector( (state: AppState) => state.editPool.storageClasses ); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/thunks/editPoolAsync.ts b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/thunks/editPoolAsync.ts index 6752ce35b..11476aa3a 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/thunks/editPoolAsync.ts +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/Pools/EditPool/thunks/editPoolAsync.ts @@ -31,8 +31,8 @@ export const editPoolAsync = createAsyncThunk( async (_, { getState, rejectWithValue, dispatch }) => { const state = getState() as AppState; - const tenant = state.tenants.tenantDetails.tenantInfo; - const selectedPool = state.tenants.tenantDetails.selectedPool; + const tenant = state.tenants.tenantInfo; + const selectedPool = state.tenants.selectedPool; const selectedStorageClass = state.editPool.fields.setup.storageClass; const numberOfNodes = state.editPool.fields.setup.numberOfNodes; const volumeSize = state.editPool.fields.setup.volumeSize; diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/PoolsSummary.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/PoolsSummary.tsx index 3a9545a3f..97b5028bf 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/PoolsSummary.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/PoolsSummary.tsx @@ -52,10 +52,10 @@ const PoolsSummary = ({ classes, history, match }: IPoolsSummary) => { const dispatch = useDispatch(); const selectedPool = useSelector( - (state: AppState) => state.tenants.tenantDetails.selectedPool + (state: AppState) => state.tenants.selectedPool ); const poolDetailsOpen = useSelector( - (state: AppState) => state.tenants.tenantDetails.poolDetailsOpen + (state: AppState) => state.tenants.poolDetailsOpen ); return ( diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx index d94fc1298..651f3f4f8 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx @@ -161,17 +161,15 @@ const TenantDetails = ({ classes, match, history }: ITenantDetailsProps) => { const dispatch = useDispatch(); const loadingTenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.loadingTenant + (state: AppState) => state.tenants.loadingTenant ); const selectedTenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.currentTenant + (state: AppState) => state.tenants.currentTenant ); const selectedNamespace = useSelector( - (state: AppState) => state.tenants.tenantDetails.currentNamespace - ); - const tenantInfo = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo + (state: AppState) => state.tenants.currentNamespace ); + const tenantInfo = useSelector((state: AppState) => state.tenants.tenantInfo); const [yamlScreenOpen, setYamlScreenOpen] = useState(false); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEncryption.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEncryption.tsx index e7399cf08..621d95ebd 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEncryption.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEncryption.tsx @@ -87,9 +87,7 @@ const styles = (theme: Theme) => const TenantEncryption = ({ classes }: ITenantEncryption) => { const dispatch = useDispatch(); - const tenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo - ); + const tenant = useSelector((state: AppState) => state.tenants.tenantInfo); const [encryptionEnabled, setEncryptionEnabled] = useState(false); const [encryptionType, setEncryptionType] = useState("vault"); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEvents.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEvents.tsx index cb44940a8..ff525b15f 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEvents.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEvents.tsx @@ -98,7 +98,7 @@ const TenantEvents = ({ ); }; const mapState = (state: AppState) => ({ - loadingTenant: state.tenants.tenantDetails.loadingTenant, + loadingTenant: state.tenants.loadingTenant, }); const connector = connect(mapState, null); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantIdentityProvider.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantIdentityProvider.tsx index 48f22c65f..2dd067771 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantIdentityProvider.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantIdentityProvider.tsx @@ -594,9 +594,9 @@ const TenantIdentityProvider = ({ }; const mapState = (state: AppState) => ({ - loadingTenant: state.tenants.tenantDetails.loadingTenant, - selectedTenant: state.tenants.tenantDetails.currentTenant, - tenant: state.tenants.tenantDetails.tenantInfo, + loadingTenant: state.tenants.loadingTenant, + selectedTenant: state.tenants.currentTenant, + tenant: state.tenants.tenantInfo, }); const connector = connect(mapState, null); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantLicense.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantLicense.tsx index 7503b5fdd..69b2d9ecb 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantLicense.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantLicense.tsx @@ -50,11 +50,9 @@ const TenantLicense = ({ classes }: ITenantLicense) => { const dispatch = useDispatch(); const loadingTenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.loadingTenant - ); - const tenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo + (state: AppState) => state.tenants.loadingTenant ); + const tenant = useSelector((state: AppState) => state.tenants.tenantInfo); const [licenseInfo, setLicenseInfo] = useState(); const [loadingLicenseInfo, setLoadingLicenseInfo] = useState(true); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantLogging.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantLogging.tsx index 55031e1a2..5207e363f 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantLogging.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantLogging.tsx @@ -489,9 +489,9 @@ const TenantLogging = ({ }; const mapState = (state: AppState) => ({ - loadingTenant: state.tenants.tenantDetails.loadingTenant, - selectedTenant: state.tenants.tenantDetails.currentTenant, - tenant: state.tenants.tenantDetails.tenantInfo, + loadingTenant: state.tenants.loadingTenant, + selectedTenant: state.tenants.currentTenant, + tenant: state.tenants.tenantInfo, }); const connector = connect(mapState, null); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx index 348d72716..1df9b2430 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSecurity.tsx @@ -531,9 +531,9 @@ const TenantSecurity = ({ }; const mapState = (state: AppState) => ({ - loadingTenant: state.tenants.tenantDetails.loadingTenant, - selectedTenant: state.tenants.tenantDetails.currentTenant, - tenant: state.tenants.tenantDetails.tenantInfo, + loadingTenant: state.tenants.loadingTenant, + selectedTenant: state.tenants.currentTenant, + tenant: state.tenants.tenantInfo, }); const connector = connect(mapState, null); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSummary.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSummary.tsx index 9330fc434..7915d31fe 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSummary.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantSummary.tsx @@ -184,26 +184,24 @@ const featureItemStyleProps = { const TenantSummary = ({ classes, match }: ITenantsSummary) => { const dispatch = useDispatch(); - const tenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.tenantInfo - ); + const tenant = useSelector((state: AppState) => state.tenants.tenantInfo); const logEnabled = useSelector((state: AppState) => - get(state.tenants.tenantDetails.tenantInfo, "logEnabled", false) + get(state.tenants.tenantInfo, "logEnabled", false) ); const monitoringEnabled = useSelector((state: AppState) => - get(state.tenants.tenantDetails.tenantInfo, "monitoringEnabled", false) + get(state.tenants.tenantInfo, "monitoringEnabled", false) ); const encryptionEnabled = useSelector((state: AppState) => - get(state.tenants.tenantDetails.tenantInfo, "encryptionEnabled", false) + get(state.tenants.tenantInfo, "encryptionEnabled", false) ); const minioTLS = useSelector((state: AppState) => - get(state.tenants.tenantDetails.tenantInfo, "minioTLS", false) + get(state.tenants.tenantInfo, "minioTLS", false) ); const adEnabled = useSelector((state: AppState) => - get(state.tenants.tenantDetails.tenantInfo, "idpAdEnabled", false) + get(state.tenants.tenantInfo, "idpAdEnabled", false) ); const oidcEnabled = useSelector((state: AppState) => - get(state.tenants.tenantDetails.tenantInfo, "idpOidcEnabled", false) + get(state.tenants.tenantInfo, "idpOidcEnabled", false) ); const [poolCount, setPoolCount] = useState(0); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/VolumesSummary.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/VolumesSummary.tsx index 43a95d36e..8706d1716 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/VolumesSummary.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/VolumesSummary.tsx @@ -55,7 +55,7 @@ const TenantVolumes = ({ classes, history, match }: ITenantVolumesProps) => { const dispatch = useDispatch(); const loadingTenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.loadingTenant + (state: AppState) => state.tenants.loadingTenant ); const [records, setRecords] = useState([]); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDescribe.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDescribe.tsx index e99056a1f..219eefdd6 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDescribe.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodDescribe.tsx @@ -406,7 +406,7 @@ const PodDescribe = ({ }: IPodEventsProps) => { const dispatch = useDispatch(); const loadingTenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.loadingTenant + (state: AppState) => state.tenants.loadingTenant ); const [describeInfo, setDescribeInfo] = useState(); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodEvents.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodEvents.tsx index ee6bb55a5..c3fd5d507 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodEvents.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodEvents.tsx @@ -63,7 +63,7 @@ const PodEvents = ({ }: IPodEventsProps) => { const dispatch = useDispatch(); const loadingTenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.loadingTenant + (state: AppState) => state.tenants.loadingTenant ); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodLogs.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodLogs.tsx index aab37e4e3..7c63c891c 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodLogs.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/pods/PodLogs.tsx @@ -92,7 +92,7 @@ const PodLogs = ({ }: IPodLogsProps) => { const dispatch = useDispatch(); const loadingTenant = useSelector( - (state: AppState) => state.tenants.tenantDetails.loadingTenant + (state: AppState) => state.tenants.loadingTenant ); const [highlight, setHighlight] = useState(""); const [logLines, setLogLines] = useState([]); diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/pvcs/PVCDescribe.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/pvcs/PVCDescribe.tsx index 2a919251b..ec64b0ec5 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/pvcs/PVCDescribe.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/pvcs/PVCDescribe.tsx @@ -215,7 +215,7 @@ const PVCDescribe = ({ ); }; const mapState = (state: AppState) => ({ - loadingTenant: state.tenants.tenantDetails.loadingTenant, + loadingTenant: state.tenants.loadingTenant, }); const connector = connect(mapState, { setErrorSnackMessage, diff --git a/portal-ui/src/screens/Console/Tenants/tenantsSlice.ts b/portal-ui/src/screens/Console/Tenants/tenantsSlice.ts index ec171288c..0004669b8 100644 --- a/portal-ui/src/screens/Console/Tenants/tenantsSlice.ts +++ b/portal-ui/src/screens/Console/Tenants/tenantsSlice.ts @@ -15,15 +15,7 @@ // along with this program. If not, see . import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { IAddPoolFields, ITenantState, LabelKeyPair } from "./types"; -import { - ITolerationEffect, - ITolerationModel, - ITolerationOperator, -} from "../../../common/types"; -import get from "lodash/get"; -import { has } from "lodash"; -import { Opts } from "./ListTenants/utils"; +import { ITenantState } from "./types"; import { ITenant } from "./ListTenants/types"; import { getTenantAsync } from "./thunks/tenantDetailsAsync"; @@ -46,53 +38,13 @@ export interface CertificateFile { } const initialState: ITenantState = { - tenantDetails: { - currentTenant: "", - currentNamespace: "", - loadingTenant: false, - tenantInfo: null, - currentTab: "summary", - selectedPool: null, - poolDetailsOpen: false, - }, - addPool: { - addPoolLoading: false, - validPages: ["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, - }, - ], - }, - }, + currentTenant: "", + currentNamespace: "", + loadingTenant: false, + tenantInfo: null, + currentTab: "summary", + selectedPool: null, + poolDetailsOpen: false, }; export const tenantSlice = createSlice({ @@ -100,7 +52,7 @@ export const tenantSlice = createSlice({ initialState, reducers: { setTenantDetailsLoad: (state, action: PayloadAction) => { - state.tenantDetails.loadingTenant = action.payload; + state.loadingTenant = action.payload; }, setTenantName: ( state, @@ -109,156 +61,36 @@ export const tenantSlice = createSlice({ namespace: string; }> ) => { - state.tenantDetails.currentTenant = action.payload.name; - state.tenantDetails.currentNamespace = action.payload.namespace; + state.currentTenant = action.payload.name; + state.currentNamespace = action.payload.namespace; }, setTenantInfo: (state, action: PayloadAction) => { if (action.payload) { - state.tenantDetails.tenantInfo = action.payload; + state.tenantInfo = action.payload; } }, setTenantTab: (state, action: PayloadAction) => { - state.tenantDetails.currentTab = action.payload; + state.currentTab = action.payload; }, - setPoolLoading: (state, action: PayloadAction) => { - state.addPool.addPoolLoading = action.payload; - }, - setPoolField: ( - state, - action: PayloadAction<{ - page: keyof IAddPoolFields; - field: string; - value: any; - }> - ) => { - if ( - has( - state.addPool.fields, - `${action.payload.page}.${action.payload.field}` - ) - ) { - const originPageNameItems = get( - state.addPool.fields, - `${action.payload.page}`, - {} - ); - - let newValue: any = {}; - newValue[action.payload.field] = action.payload.value; - - state.addPool.fields[action.payload.page] = { - ...originPageNameItems, - ...newValue, - }; - } - }, - isPoolPageValid: ( - state, - action: PayloadAction<{ - page: string; - status: boolean; - }> - ) => { - if (action.payload.status) { - if (!state.addPool.validPages.includes(action.payload.page)) { - state.addPool.validPages.push(action.payload.page); - } - } else { - state.addPool.validPages = state.addPool.validPages.filter( - (elm) => elm !== action.payload.page - ); - } - }, - setPoolStorageClasses: (state, action: PayloadAction) => { - state.addPool.storageClasses = action.payload; - }, - setPoolTolerationInfo: ( - state, - action: PayloadAction<{ - index: number; - tolerationValue: ITolerationModel; - }> - ) => { - state.addPool.fields.tolerations[action.payload.index] = - action.payload.tolerationValue; - }, - addNewPoolToleration: (state) => { - state.addPool.fields.tolerations.push({ - key: "", - tolerationSeconds: { seconds: 0 }, - value: "", - effect: ITolerationEffect.NoSchedule, - operator: ITolerationOperator.Equal, - }); - }, - removePoolToleration: (state, action: PayloadAction) => { - state.addPool.fields.tolerations = - state.addPool.fields.tolerations.filter( - (_, index) => index !== action.payload - ); - }, - setPoolKeyValuePairs: (state, action: PayloadAction) => { - state.addPool.fields.nodeSelectorPairs = action.payload; - }, - resetPoolForm: (state) => { - state.addPool = { - addPoolLoading: false, - validPages: ["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, - }, - ], - }, - }; - }, setSelectedPool: (state, action: PayloadAction) => { - state.tenantDetails.selectedPool = action.payload; + state.selectedPool = action.payload; }, setOpenPoolDetails: (state, action: PayloadAction) => { - state.tenantDetails.poolDetailsOpen = action.payload; + state.poolDetailsOpen = action.payload; }, }, extraReducers: (builder) => { builder .addCase(getTenantAsync.pending, (state) => { - state.tenantDetails.loadingTenant = true; + state.loadingTenant = true; }) .addCase(getTenantAsync.rejected, (state) => { - state.tenantDetails.loadingTenant = false; + state.loadingTenant = false; }) .addCase(getTenantAsync.fulfilled, (state, action) => { - state.tenantDetails.loadingTenant = false; - state.tenantDetails.tenantInfo = action.payload; + state.loadingTenant = false; + state.tenantInfo = action.payload; }); }, }); @@ -269,15 +101,6 @@ export const { setTenantName, setTenantInfo, setTenantTab, - setPoolLoading, - resetPoolForm, - setPoolField, - isPoolPageValid, - setPoolStorageClasses, - setPoolTolerationInfo, - addNewPoolToleration, - removePoolToleration, - setPoolKeyValuePairs, setSelectedPool, setOpenPoolDetails, } = tenantSlice.actions; diff --git a/portal-ui/src/screens/Console/Tenants/thunks/tenantDetailsAsync.ts b/portal-ui/src/screens/Console/Tenants/thunks/tenantDetailsAsync.ts index 0982b50a8..bbdc18aac 100644 --- a/portal-ui/src/screens/Console/Tenants/thunks/tenantDetailsAsync.ts +++ b/portal-ui/src/screens/Console/Tenants/thunks/tenantDetailsAsync.ts @@ -27,8 +27,8 @@ export const getTenantAsync = createAsyncThunk( async (_, { getState, rejectWithValue, dispatch }) => { const state = getState() as AppState; - const currentNamespace = state.tenants.tenantDetails.currentNamespace; - const currentTenant = state.tenants.tenantDetails.currentTenant; + const currentNamespace = state.tenants.currentNamespace; + const currentTenant = state.tenants.currentTenant; return api .invoke( diff --git a/portal-ui/src/screens/Console/Tenants/types.ts b/portal-ui/src/screens/Console/Tenants/types.ts index c05db103d..5c4549701 100644 --- a/portal-ui/src/screens/Console/Tenants/types.ts +++ b/portal-ui/src/screens/Console/Tenants/types.ts @@ -20,7 +20,6 @@ import { IErasureCodeCalc, IGCPConfig, IGemaltoCredentials, - ITolerationModel, } from "../../../common/types"; import { IResourcesSize, ITenant } from "./ListTenants/types"; import { KeyPair, Opts } from "./ListTenants/utils"; @@ -278,7 +277,7 @@ export interface ITenantAffinity { withPodAntiAffinity: boolean; } -export interface ITenantDetails { +export interface ITenantState { currentTenant: string; currentNamespace: string; loadingTenant: boolean; @@ -288,11 +287,6 @@ export interface ITenantDetails { selectedPool: string | null; } -export interface ITenantState { - tenantDetails: ITenantDetails; - addPool: IAddPool; -} - export interface ILabelKeyPair { labelKey: string; labelValue: string; @@ -322,22 +316,6 @@ export interface IPoolConfiguration { securityContext: ISecurityContext; } -export interface IAddPoolFields { - setup: IAddPoolSetup; - affinity: ITenantAffinity; - configuration: IPoolConfiguration; - tolerations: ITolerationModel[]; - nodeSelectorPairs: LabelKeyPair[]; -} - -export interface IAddPool { - addPoolLoading: boolean; - validPages: string[]; - storageClasses: Opts[]; - limitSize: any; - fields: IAddPoolFields; -} - export interface ITenantIdentityProviderResponse { oidc?: { callback_url: string; diff --git a/portal-ui/src/store.ts b/portal-ui/src/store.ts index 01a271388..b3531cae1 100644 --- a/portal-ui/src/store.ts +++ b/portal-ui/src/store.ts @@ -25,29 +25,39 @@ import bucketDetailsReducer from "./screens/Console/Buckets/BucketDetails/bucket import objectBrowserReducer from "./screens/Console/ObjectBrowser/objectBrowserSlice"; import tenantsReducer from "./screens/Console/Tenants/tenantsSlice"; import dashboardReducer from "./screens/Console/Dashboard/dashboardSlice"; -import { configureStore } from "@reduxjs/toolkit"; +import { combineReducers, configureStore } from "@reduxjs/toolkit"; import createTenantReducer from "./screens/Console/Tenants/AddTenant/createTenantSlice"; +import addPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolSlice"; import editPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/EditPool/editPoolSlice"; -export const store = configureStore({ - reducer: { - system: systemReducer, - trace: traceReducer, - logs: logReducer, - watch: watchReducer, - console: consoleReducer, - buckets: bucketsReducer, - bucketDetails: bucketDetailsReducer, - objectBrowser: objectBrowserReducer, - healthInfo: healthInfoReducer, - dashboard: dashboardReducer, - // Operator Reducers - tenants: tenantsReducer, - createTenant: createTenantReducer, - editPool: editPoolReducer, - }, +const rootReducer = combineReducers({ + system: systemReducer, + trace: traceReducer, + logs: logReducer, + watch: watchReducer, + console: consoleReducer, + buckets: bucketsReducer, + bucketDetails: bucketDetailsReducer, + objectBrowser: objectBrowserReducer, + healthInfo: healthInfoReducer, + dashboard: dashboardReducer, + // Operator Reducers + tenants: tenantsReducer, + createTenant: createTenantReducer, + addPool: addPoolReducer, + editPool: editPoolReducer, }); +export const store = configureStore({ + reducer: rootReducer, +}); + +if (process.env.NODE_ENV !== "production" && module.hot) { + module.hot.accept(() => { + store.replaceReducer(rootReducer); + }); +} + export type AppState = ReturnType; export type AppDispatch = typeof store.dispatch;