AWS Marketplace Integration Updates (#1740)

Makes the create tenant for MK AWS updated with new recommendations
Fixes Back Link icon alignment and color
Adds a helpbox for MK AWS

Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
Daniel Valdivia
2022-03-21 10:21:39 -07:00
committed by GitHub
parent 58c53cbe0a
commit ce4d9310aa
10 changed files with 356 additions and 62 deletions

View File

@@ -19,43 +19,47 @@ import { Link } from "react-router-dom";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { BackIcon } from "../icons";
import { BackSettingsIcon } from "../icons";
import { Box } from "@mui/material";
const styles = (theme: Theme) =>
createStyles({
link: {
display: "inline-block",
alignItems: "center",
justifyContent: "center",
display: "block",
textDecoration: "none",
maxWidth: "40px",
"&:active": {
color: theme.palette.primary.light,
},
},
icon: {
marginRight: "11px",
iconBox: {
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "35px",
width: "35px",
borderRadius: "2px",
flexDirection: "row",
"&:hover": {
background: "rgba(234,237,238)",
},
"& svg.min-icon": {
width: "18px",
height: "12px",
height: "30px",
paddingBottom: 4,
paddingTop: 8,
paddingRight: 16,
paddingLeft: 0,
borderRadius: 4,
},
icon: {
lineHeight: 1,
marginRight: "14px",
alignItems: "center",
width: "22px",
"& .min-icon": {
color: theme.palette.primary.light,
width: "16px",
height: "16px",
},
},
label: {
display: "flex",
lineHeight: 1,
alignItems: "center",
height: "35px",
padding: "0 0px 0 5px",
fontSize: "18px",
paddingTop: 1,
fontSize: "14px",
fontWeight: 600,
color: theme.palette.primary.light,
},
@@ -92,11 +96,13 @@ const BackLink = ({
}
}}
>
<div className={classes.icon}>
<BackIcon />
<div className={classes.iconBox}>
<div className={classes.icon}>
<BackSettingsIcon />
</div>
<div className={classes.label}>{label}</div>
</div>
</Link>
<div className={classes.label}>{label}</div>
</Box>
);
};

View File

@@ -22,6 +22,12 @@ import {
IStorageFactors,
} from "./types";
import { IPool } from "../screens/Console/Tenants/ListTenants/types";
import {
IMkEnvs,
IntegrationConfiguration,
mkPanelConfigurations,
} from "../screens/Console/Tenants/AddTenant/Steps/TenantResources/utils";
import get from "lodash/get";
const minStReq = 1073741824; // Minimal Space required for MinIO
const minMemReq = 2147483648; // Minimal Memory required for MinIO in bytes
@@ -114,12 +120,21 @@ export const k8sScalarUnitsExcluding = (exclude?: string[]) => {
});
};
//getBytes, converts from a value and a unit from units array to bytes
//getBytes, converts from a value and a unit from units array to bytes as a string
export const getBytes = (
value: string,
unit: string,
fromk8s: boolean = false
) => {
): string => {
return getBytesNumber(value, unit, fromk8s).toString(10);
};
//getBytesNumber, converts from a value and a unit from units array to bytes
export const getBytesNumber = (
value: string,
unit: string,
fromk8s: boolean = false
): number => {
const vl: number = parseFloat(value);
const unitsTake = fromk8s ? k8sCalcUnits : units;
@@ -127,12 +142,12 @@ export const getBytes = (
const powFactor = unitsTake.findIndex((element) => element === unit);
if (powFactor === -1) {
return "0";
return 0;
}
const factor = Math.pow(1024, powFactor);
const total = vl * factor;
return total.toString(10);
return total;
};
//getTotalSize gets the total size of a value & unit
@@ -218,7 +233,9 @@ export const calculateDistribution = (
capacityToUse: ICapacity,
forcedNodes: number = 0,
limitSize: number = 0,
drivesPerServer: number = 0
drivesPerServer: number = 0,
marketplaceIntegration?: IMkEnvs,
selectedStorageType?: string
): IStorageDistribution => {
const requestedSizeBytes = getBytes(
capacityToUse.value,
@@ -250,7 +267,9 @@ export const calculateDistribution = (
requestedSizeBytes,
forcedNodes,
limitSize,
drivesPerServer
drivesPerServer,
marketplaceIntegration,
selectedStorageType
);
return numberOfNodes;
@@ -260,7 +279,9 @@ const calculateStorage = (
requestedBytes: string,
forcedNodes: number,
limitSize: number,
drivesPerServer: number
drivesPerServer: number,
marketplaceIntegration?: IMkEnvs,
selectedStorageType?: string
): IStorageDistribution => {
// Size validation
const intReqBytes = parseInt(requestedBytes, 10);
@@ -272,7 +293,9 @@ const calculateStorage = (
intReqBytes,
maxDiskSize,
limitSize,
drivesPerServer
drivesPerServer,
marketplaceIntegration,
selectedStorageType
);
};
@@ -281,7 +304,9 @@ const structureCalc = (
desiredCapacity: number,
maxDiskSize: number,
maxClusterSize: number,
disksPerNode: number = 0
disksPerNode: number = 0,
marketplaceIntegration?: IMkEnvs,
selectedStorageType?: string
): IStorageDistribution => {
if (
isNaN(nodes) ||
@@ -350,6 +375,48 @@ const structureCalc = (
pvSize: 0,
}; // Cannot allocate this volume size
}
// validate for integrations
if (marketplaceIntegration !== undefined) {
const setConfigs = mkPanelConfigurations[marketplaceIntegration];
const keyCount = Object.keys(setConfigs).length;
//Configuration is filled
if (keyCount > 0) {
const configs: IntegrationConfiguration[] = get(
setConfigs,
"configurations",
[]
);
const mainSelection = configs.find(
(item) => item.typeSelection === selectedStorageType
);
if (mainSelection !== undefined && mainSelection.minimumVolumeSize) {
const minimumPvSize = getBytesNumber(
mainSelection.minimumVolumeSize?.driveSize,
mainSelection.minimumVolumeSize?.sizeUnit,
true
);
const storageTypeLabel = setConfigs.variantSelectorValues!.find(
(item) => item.value === selectedStorageType
);
if (persistentVolumeSize < minimumPvSize) {
return {
error: `For the ${
storageTypeLabel!.label
} storage type the mininum volume size is ${
mainSelection.minimumVolumeSize.driveSize
}${mainSelection.minimumVolumeSize.sizeUnit}`,
nodes: 0,
persistentVolumes: 0,
disks: 0,
pvSize: 0,
};
}
}
}
}
return {
error: "",

View File

@@ -55,6 +55,12 @@ import BackLink from "../../../../common/BackLink";
import TenantResources from "./Steps/TenantResources/TenantResources";
import ConfigLogSearch from "./Steps/ConfigLogSearch";
import ConfigPrometheus from "./Steps/ConfigPrometheus";
import {
IMkEnvs,
resourcesConfigurations,
} from "./Steps/TenantResources/utils";
import HelpBox from "../../../../common/HelpBox";
import { StorageIcon } from "../../../../icons";
interface IAddTenantProps {
setErrorSnackMessage: typeof setErrorSnackMessage;
@@ -66,6 +72,7 @@ interface IAddTenantProps {
namespace: string;
validPages: string[];
classes: any;
features?: string[];
}
const styles = (theme: Theme) =>
@@ -87,6 +94,7 @@ const AddTenant = ({
validPages,
setErrorSnackMessage,
resetAddTenantForm,
features,
}: IAddTenantProps) => {
// Modals
const [showNewCredentials, setShowNewCredentials] = useState<boolean>(false);
@@ -95,6 +103,27 @@ const AddTenant = ({
// Fields
const [addSending, setAddSending] = useState<boolean>(false);
const [formRender, setFormRender] = useState<IMkEnvs | null>(null);
useEffect(() => {
let setConfiguration = IMkEnvs.default;
if (features && features.length !== 0) {
const possibleVariables = Object.keys(resourcesConfigurations);
possibleVariables.forEach((element) => {
if (features.includes(element)) {
setConfiguration = get(
resourcesConfigurations,
element,
IMkEnvs.default
);
}
});
}
setFormRender(setConfiguration);
}, [features]);
/* Send Information to backend */
useEffect(() => {
@@ -764,20 +793,47 @@ const AddTenant = ({
/>
)}
<PageHeader label={"Create New Tenant"} />
<BackLink
to={"/tenants"}
label={"Tenant List"}
executeOnClick={resetAddTenantForm}
/>
<PageLayout>
{addSending && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
<Grid item xs={12}>
<BackLink
to={"/tenants"}
label={"Tenant List"}
executeOnClick={resetAddTenantForm}
/>
</Grid>
<Grid item xs={12} className={classes.pageBox}>
<GenericWizard wizardSteps={filteredWizardSteps} />
</Grid>
{formRender === IMkEnvs.aws && (
<Grid item xs={12} style={{ marginTop: 16 }}>
<HelpBox
title={"EBS Volume Configuration."}
iconComponent={<StorageIcon />}
help={
<Fragment>
<b>Performance Optimized</b>: Uses the <i>gp3</i> EBS storage
class class configured at 1,000Mi/s throughput and 16,000
IOPS, however the minimum volume size for this type of EBS
volume is <b>32Gi</b>.
<br />
<br />
<b>Storage Optimized</b>: Uses the <i>sc1</i> EBS storage
class, however the minimum volume size for this type of EBS
volume is &nbsp;
<b>16Ti</b> to unlock their maximum throughput speed of
250Mi/s.
</Fragment>
}
/>
</Grid>
)}
</PageLayout>
</Fragment>
);
@@ -790,6 +846,7 @@ const mapState = (state: AppState) => ({
certificates: state.tenants.createTenant.certificates,
selectedStorageClass:
state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
features: state.console.session.features,
});
const connector = connect(mapState, {

View File

@@ -37,6 +37,7 @@ import { setModalErrorSnackMessage } from "../../../../../../actions";
import {
isPageValid,
setLimitSize,
setStorageType,
setStorageClassesList,
updateAddField,
} from "../../../actions";
@@ -87,6 +88,8 @@ interface INameTenantMainScreen {
selectedStorageClass: string;
selectedStorageType: string;
formToRender?: IMkEnvs;
features?: string[];
setStorageType: typeof setStorageType;
}
const NameTenantMain = ({
@@ -102,6 +105,8 @@ const NameTenantMain = ({
setLimitSize,
isPageValid,
setModalErrorSnackMessage,
features,
setStorageType,
}: INameTenantMainScreen) => {
const [validationErrors, setValidationErrors] = useState<any>({});
const [emptyNamespace, setEmptyNamespace] = useState<boolean>(true);
@@ -350,10 +355,7 @@ const NameTenantMain = ({
id="storage_type"
name="storage_type"
onChange={(e: SelectChangeEvent<string>) => {
updateField(
"selectedStorageType",
e.target.value as string
);
setStorageType(e.target.value as string, features);
}}
label={get(
mkPanelConfigurations,
@@ -399,6 +401,7 @@ const mapState = (state: AppState) => ({
selectedStorageType:
state.tenants.createTenant.fields.nameTenant.selectedStorageType,
storageClasses: state.tenants.createTenant.storageClasses,
features: state.console.session.features,
});
const connector = connect(mapState, {
@@ -407,6 +410,7 @@ const connector = connect(mapState, {
setStorageClassesList,
setLimitSize,
isPageValid,
setStorageType,
});
export default withStyles(styles)(connector(NameTenantMain));

View File

@@ -45,6 +45,7 @@ import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/I
import SelectWrapper from "../../../../Common/FormComponents/SelectWrapper/SelectWrapper";
import TenantSizeResources from "./TenantSizeResources";
import InputUnitMenu from "../../../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import { IMkEnvs } from "./utils";
interface ITenantSizeProps {
classes: any;
@@ -64,6 +65,8 @@ interface ITenantSizeProps {
limitSize: any;
selectedStorageClass: string;
untouchedECField: boolean;
formToRender?: IMkEnvs;
selectedStorageType: string;
}
const styles = (theme: Theme) =>
@@ -106,6 +109,8 @@ const TenantSize = ({
limitSize,
selectedStorageClass,
untouchedECField,
formToRender,
selectedStorageType,
}: ITenantSizeProps) => {
const [validationErrors, setValidationErrors] = useState<any>({});
const [errorFlag, setErrorFlag] = useState<boolean>(false);
@@ -174,7 +179,7 @@ const TenantSize = ({
//Validate Cluster Size
const size = volumeSize;
const factor = sizeFactor;
const limitSize = getBytes("12", "Ti", true);
const limitSize = getBytes("16", "Ti", true);
const clusterCapacity: ICapacity = {
unit: factor,
@@ -185,13 +190,23 @@ const TenantSize = ({
clusterCapacity,
parseInt(nodes),
parseInt(limitSize),
parseInt(drivesPerServer)
parseInt(drivesPerServer),
formToRender,
selectedStorageType
);
updateField("distribution", distrCalculate);
setErrorFlag(false);
setNodeError("");
}, [nodes, volumeSize, sizeFactor, updateField, drivesPerServer]);
}, [
nodes,
volumeSize,
sizeFactor,
updateField,
drivesPerServer,
selectedStorageType,
formToRender,
]);
/*Calculate Allocation End*/
@@ -406,6 +421,8 @@ const mapState = (state: AppState) => {
limitSize: state.tenants.createTenant.limitSize,
selectedStorageClass:
state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
selectedStorageType:
state.tenants.createTenant.fields.nameTenant.selectedStorageType,
};
};

View File

@@ -17,6 +17,7 @@
import React from "react";
import { Opts } from "../../../ListTenants/utils";
import TenantSizeMK from "./TenantSizeMK";
import TenantSize from "./TenantSize";
export enum IMkEnvs {
"aws",
@@ -38,11 +39,12 @@ export interface IntegrationConfiguration {
memory: number;
drivesPerServer: number;
driveSize: IDriveSizing;
minimumVolumeSize?: IDriveSizing;
}
export const AWSStorageTypes: Opts[] = [
{ label: "NVME", value: "nvme" },
{ label: "HDD", value: "hdd" },
{ label: "Performance Optimized", value: "performance" },
{ label: "Capacity Optimized", value: "capacity" },
];
export const AzureStorageTypes: Opts[] = [
@@ -59,20 +61,22 @@ export const resourcesConfigurations = {
export const AWSConfigurations: IntegrationConfiguration[] = [
{
typeSelection: "nvme",
storageClass: "nvme-i3en-12xlarge",
CPU: 48,
memory: 384,
driveSize: { driveSize: "7500", sizeUnit: "Gi" },
typeSelection: "performance",
storageClass: "performance-c6gn-16xlarge",
CPU: 64,
memory: 128,
driveSize: { driveSize: "32", sizeUnit: "Gi" },
drivesPerServer: 4,
minimumVolumeSize: { driveSize: "32", sizeUnit: "Gi" },
},
{
typeSelection: "hdd",
storageClass: "hdd-d3en-12xlarge",
CPU: 8,
memory: 32,
driveSize: { driveSize: "12.7", sizeUnit: "Ti" },
drivesPerServer: 4,
typeSelection: "capacity",
storageClass: "capacity-c6gn-16xlarge",
CPU: 64,
memory: 128,
driveSize: { driveSize: "16", sizeUnit: "Ti" },
drivesPerServer: 18,
minimumVolumeSize: { driveSize: "16", sizeUnit: "Ti" },
},
];
@@ -132,12 +136,19 @@ export const GCPConfigurations: IntegrationConfiguration[] = [
},
];
export const mkPanelConfigurations = {
interface mkConfiguration {
variantSelectorLabel?: string;
variantSelectorValues?: Opts[];
configurations?: IntegrationConfiguration[];
sizingComponent?: JSX.Element;
}
export const mkPanelConfigurations: { [index: number]: mkConfiguration } = {
[IMkEnvs.aws]: {
variantSelectorLabel: "Storage Type",
variantSelectorValues: AWSStorageTypes,
configurations: AWSConfigurations,
sizingComponent: <TenantSizeMK formToRender={IMkEnvs.aws} />,
sizingComponent: <TenantSize formToRender={IMkEnvs.aws} />,
},
[IMkEnvs.azure]: {
variantSelectorLabel: "VM Size",

View File

@@ -37,6 +37,7 @@ import {
ADD_TENANT_SET_LIMIT_SIZE,
ADD_TENANT_SET_PAGE_VALID,
ADD_TENANT_SET_STORAGE_CLASSES_LIST,
ADD_TENANT_SET_STORAGE_TYPE,
ADD_TENANT_UPDATE_FIELD,
TENANT_DETAILS_SET_CURRENT_TENANT,
TENANT_DETAILS_SET_LOADING,
@@ -84,6 +85,14 @@ export const setStorageClassesList = (storageClasses: Opts[]) => {
};
};
export const setStorageType = (storageType: string, features?: string[]) => {
return {
type: ADD_TENANT_SET_STORAGE_TYPE,
storageType,
features,
};
};
export const setLimitSize = (limitSize: any) => {
return {
type: ADD_TENANT_SET_LIMIT_SIZE,

View File

@@ -36,6 +36,7 @@ import {
ADD_TENANT_SET_KEY_PAIR_VALUE,
ADD_TENANT_SET_LIMIT_SIZE,
ADD_TENANT_SET_PAGE_VALID,
ADD_TENANT_SET_STORAGE_TYPE,
ADD_TENANT_SET_STORAGE_CLASSES_LIST,
ADD_TENANT_UPDATE_FIELD,
ITenantState,
@@ -47,6 +48,7 @@ import {
} from "./types";
import { KeyPair } from "./ListTenants/utils";
import { getRandomString } from "./utils";
import { addTenantSetStorageTypeReducer } from "./reducers/add-tenant-reducer";
const initialState: ITenantState = {
createTenant: {
@@ -189,9 +191,9 @@ const initialState: ITenantState = {
},
},
tenantSize: {
volumeSize: "100",
volumeSize: "1024",
sizeFactor: "Gi",
drivesPerServer: "1",
drivesPerServer: "4",
nodes: "4",
memoryNode: "2",
ecParity: "",
@@ -401,6 +403,8 @@ export function tenantsReducer(
},
};
return { ...changeCL };
case ADD_TENANT_SET_STORAGE_TYPE:
return addTenantSetStorageTypeReducer(action, state);
case ADD_TENANT_SET_LIMIT_SIZE:
const changeSizeLimit = {
...state,
@@ -739,9 +743,9 @@ export function tenantsReducer(
},
},
tenantSize: {
volumeSize: "100",
volumeSize: "1024",
sizeFactor: "Gi",
drivesPerServer: "1",
drivesPerServer: "4",
nodes: "4",
memoryNode: "2",
ecParity: "",

View File

@@ -0,0 +1,110 @@
// 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 { ITenantState, SetStorageType } from "../types";
import {
IMkEnvs,
IntegrationConfiguration,
mkPanelConfigurations,
resourcesConfigurations,
} from "../AddTenant/Steps/TenantResources/utils";
import get from "lodash/get";
import { getBytesNumber } from "../../../../common/utils";
export const addTenantSetStorageTypeReducer = (
action: SetStorageType,
state: ITenantState
) => {
let size = state.createTenant.fields.tenantSize.volumeSize;
let sizeFactor = state.createTenant.fields.tenantSize.sizeFactor;
let volumeSize = state.createTenant.fields.tenantSize.volumeSize;
// for the aws marketplace integration we have some constraints
// on the minimum cluster size
if (action.features !== undefined && action.features.length > 0) {
let formToRender = IMkEnvs.default;
const possibleVariables = Object.keys(resourcesConfigurations);
possibleVariables.forEach((element) => {
if (action.features !== undefined && action.features.includes(element)) {
formToRender = get(resourcesConfigurations, element, IMkEnvs.default);
}
});
// if the size is less than the minimum for the selected storage type
// we will override the current total storage entered amount with the minimum
if (formToRender !== undefined) {
const setConfigs = mkPanelConfigurations[formToRender];
const keyCount = Object.keys(setConfigs).length;
//Configuration is filled
if (keyCount > 0) {
const configs: IntegrationConfiguration[] = get(
setConfigs,
"configurations",
[]
);
const mainSelection = configs.find(
(item) => item.typeSelection === action.storageType
);
if (mainSelection !== undefined && mainSelection.minimumVolumeSize) {
const minimumSize = getBytesNumber(
mainSelection.minimumVolumeSize?.driveSize,
mainSelection.minimumVolumeSize?.sizeUnit,
true
);
const drivesPerServer =
state.createTenant.fields.tenantSize.drivesPerServer;
const nodes = state.createTenant.fields.tenantSize.drivesPerServer;
const currentSize = getBytesNumber(size.toString(), sizeFactor, true);
if (currentSize < minimumSize) {
size = minimumSize.toString(10);
const totalSize =
parseInt(nodes) *
parseInt(drivesPerServer) *
parseInt(mainSelection.minimumVolumeSize.driveSize);
volumeSize = totalSize.toString(10);
sizeFactor = mainSelection.minimumVolumeSize.sizeUnit;
}
}
}
}
}
const newstate = {
...state,
createTenant: {
...state.createTenant,
fields: {
...state.createTenant.fields,
nameTenant: {
...state.createTenant.fields.nameTenant,
selectedStorageType: action.storageType,
},
tenantSize: {
...state.createTenant.fields.tenantSize,
size: size,
volumeSize: volumeSize,
sizeFactor: sizeFactor,
},
},
},
};
return { ...newstate };
};

View File

@@ -34,6 +34,8 @@ export const ADD_TENANT_RESET_FORM = "ADD_TENANT/RESET_FORM";
export const ADD_TENANT_SET_STORAGE_CLASSES_LIST =
"ADD_TENANT/SET_STORAGE_CLASSES_LIST";
export const ADD_TENANT_SET_LIMIT_SIZE = "ADD_TENANT/SET_LIMIT_SIZE";
export const ADD_TENANT_SET_STORAGE_TYPE =
"ADD_TENANT/ADD_TENANT_SET_STORAGE_TYPE";
// Security
export const ADD_TENANT_ADD_MINIO_KEYPAIR = "ADD_TENANT/ADD_MINIO_KEYPAIR";
@@ -390,6 +392,12 @@ interface SetLimitSize {
limitSize: any;
}
export interface SetStorageType {
type: typeof ADD_TENANT_SET_STORAGE_TYPE;
storageType: string;
features?: string[];
}
interface AddMinioKeyPair {
type: typeof ADD_TENANT_ADD_MINIO_KEYPAIR;
}
@@ -519,6 +527,7 @@ export type TenantsManagementTypes =
| UpdateATField
| SetPageValid
| SetStorageClassesList
| SetStorageType
| SetLimitSize
| AddMinioKeyPair
| DeleteMinioKeyPair