Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions portal-ui/build/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
{
"files": {
"main.css": "/static/css/main.a19f3d53.chunk.css",
"main.js": "/static/js/main.b4a5fae7.chunk.js",
"main.js.map": "/static/js/main.b4a5fae7.chunk.js.map",
"main.js": "/static/js/main.8671089f.chunk.js",
"main.js.map": "/static/js/main.8671089f.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.f48e99e5.js",
"runtime-main.js.map": "/static/js/runtime-main.f48e99e5.js.map",
"static/css/2.32daf8f7.chunk.css": "/static/css/2.32daf8f7.chunk.css",
"static/js/2.e5e999f3.chunk.js": "/static/js/2.e5e999f3.chunk.js",
"static/js/2.e5e999f3.chunk.js.map": "/static/js/2.e5e999f3.chunk.js.map",
"static/js/2.095198e4.chunk.js": "/static/js/2.095198e4.chunk.js",
"static/js/2.095198e4.chunk.js.map": "/static/js/2.095198e4.chunk.js.map",
"index.html": "/index.html",
"static/css/2.32daf8f7.chunk.css.map": "/static/css/2.32daf8f7.chunk.css.map",
"static/css/main.a19f3d53.chunk.css.map": "/static/css/main.a19f3d53.chunk.css.map",
"static/js/2.e5e999f3.chunk.js.LICENSE.txt": "/static/js/2.e5e999f3.chunk.js.LICENSE.txt",
"static/js/2.095198e4.chunk.js.LICENSE.txt": "/static/js/2.095198e4.chunk.js.LICENSE.txt",
"static/media/minio_console_logo.0837460e.svg": "/static/media/minio_console_logo.0837460e.svg",
"static/media/minio_operator_logo.1312b7c9.svg": "/static/media/minio_operator_logo.1312b7c9.svg"
},
"entrypoints": [
"static/js/runtime-main.f48e99e5.js",
"static/css/2.32daf8f7.chunk.css",
"static/js/2.e5e999f3.chunk.js",
"static/js/2.095198e4.chunk.js",
"static/css/main.a19f3d53.chunk.css",
"static/js/main.b4a5fae7.chunk.js"
"static/js/main.8671089f.chunk.js"
]
}
2 changes: 1 addition & 1 deletion portal-ui/build/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.32daf8f7.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.e5e999f3.chunk.js"></script><script src="/static/js/main.b4a5fae7.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="MinIO Console"/><link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap" rel="stylesheet"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json"/><link rel="mask-icon" href="/safari-pinned-tab.svg" color="#3a4e54"/><title>MinIO Console</title><link href="/static/css/2.32daf8f7.chunk.css" rel="stylesheet"><link href="/static/css/main.a19f3d53.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,l,i=r[0],a=r[1],p=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var a=t[i];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={1:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonpportal-ui"]=this["webpackJsonpportal-ui"]||[],a=i.push.bind(i);i.push=r,i=i.slice();for(var p=0;p<i.length;p++)r(i[p]);var f=a;t()}([])</script><script src="/static/js/2.095198e4.chunk.js"></script><script src="/static/js/main.8671089f.chunk.js"></script></body></html>

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions portal-ui/build/static/js/main.8671089f.chunk.js.map

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion portal-ui/build/static/js/main.b4a5fae7.chunk.js.map

This file was deleted.

24 changes: 23 additions & 1 deletion portal-ui/src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// 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 { ILabelKeyPair } from "../screens/Console/Tenants/types";

export interface ITenantsObject {
tenants: ITenant[];
}
Expand Down Expand Up @@ -70,6 +72,7 @@ export interface ITenantCreator {
image_registry?: ImageRegistry;
logSearchConfiguration?: LogSearchConfiguration;
prometheusConfiguration?: PrometheusConfiguration;
affinity?: AffinityConfiguration;
}

export interface ImageRegistry {
Expand All @@ -95,7 +98,8 @@ export interface ITenantUsage {
}

export interface IAffinityModel {
podAntiAffinity: IPodAntiAffinityModel;
podAntiAffinity?: IPodAntiAffinityModel;
podAffinity?: IPodAffinityModel;
}

export interface IPodAntiAffinityModel {
Expand All @@ -111,6 +115,19 @@ export interface IPodAffinityTermLabelSelector {
matchExpressions: IMatchExpressionItem[];
}

export interface IPodAffinityModel {
requiredDuringSchedulingIgnoredDuringExecution: IPodAffinityTerms[];
}

export interface IPodAffinityTerms {
labelSelector: IPodAffinityLabelsSelector;
topologyKey: string;
}

export interface IPodAffinityLabelsSelector {
matchLabels: object;
}

export interface IMatchExpressionItem {
key: string;
operator: string;
Expand Down Expand Up @@ -353,3 +370,8 @@ export interface PrometheusConfiguration {
storageClass: string;
storageSize: number;
}

export interface AffinityConfiguration {
affinityType: "default" | "nodeSelector" | "none";
nodeSelectorLabels?: ILabelKeyPair[];
}
39 changes: 32 additions & 7 deletions portal-ui/src/screens/Console/Tenants/AddTenant/AddTenant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,19 @@ import { IAffinityModel, ITenantCreator } from "../../../../common/types";
import { KeyPair } from "../ListTenants/utils";

import { setModalErrorSnackMessage } from "../../../../actions";
import { getHardcodedAffinity } from "../TenantDetails/utils";
import { getDefaultAffinity, getNodeSelector } from "../TenantDetails/utils";
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
import NameTenant from "./Steps/NameTenant";
import { AppState } from "../../../../store";
import { ICertificatesItems, IFieldStore } from "../types";
import { ICertificatesItems, IFieldStore, ILabelKeyPair } from "../types";
import { updateAddField } from "../actions";
import Configure from "./Steps/Configure";
import IdentityProvider from "./Steps/IdentityProvider";
import Security from "./Steps/Security";
import Encryption from "./Steps/Encryption";
import TenantSize from "./Steps/TenantSize";
import Preview from "./Steps/Preview";
import Affinity from "./Steps/Affinity";

interface IAddTenantProps {
closeAndRefresh: (reloadData: boolean) => any;
Expand Down Expand Up @@ -162,14 +163,24 @@ const AddTenant = ({
const prometheusSelectedStorageClass =
fields.configure.prometheusSelectedStorageClass;
const prometheusVolumeSize = fields.configure.prometheusVolumeSize;
const affinityType = fields.affinity.podAffinity;
const affinityLabels = fields.affinity.affinityLabels;

if (addSending) {
const poolName = generatePoolName([]);

const hardCodedAffinity: IAffinityModel = getHardcodedAffinity(
tenantName,
poolName
);
let affinityObject = {};

switch (affinityType) {
case "default":
affinityObject = {
affinity: getDefaultAffinity(tenantName, poolName),
};
break;
case "nodeSelector":
affinityObject = { affinity: getNodeSelector(affinityLabels) };
break;
}

const erasureCode = ecParity.split(":")[1];

Expand Down Expand Up @@ -205,7 +216,7 @@ const AddTenant = ({
memory: memorySize.limit,
},
},
affinity: hardCodedAffinity,
...affinityObject,
},
],
erasureCodingParity: parseInt(erasureCode, 10),
Expand Down Expand Up @@ -536,6 +547,20 @@ const AddTenant = ({
},
],
},
{
label: "Pod Affinity",
advancedOnly: true,
componentRender: <Affinity />,
buttons: [
cancelButton,
{ label: "Back", type: "back", enabled: true },
{
label: "Next",
type: "next",
enabled: validPages.includes("affinity"),
},
],
},
{
label: "Identity Provider",
advancedOnly: true,
Expand Down
178 changes: 178 additions & 0 deletions portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Affinity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

import React, { useEffect, useState, useCallback, Fragment } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { AppState } from "../../../../../store";
import { updateAddField, isPageValid } from "../../actions";
import { setModalErrorSnackMessage } from "../../../../../actions";
import {
modalBasic,
wizardCommon,
} from "../../../Common/FormComponents/common/styleLibrary";
import { Grid } from "@material-ui/core";
import {
commonFormValidation,
IValidation,
} from "../../../../../utils/validationFunctions";
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import QueryMultiSelector from "../../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";

interface IAffinityProps {
classes: any;
podAffinity: string;
affinityLabels: string;
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
updateAddField: typeof updateAddField;
isPageValid: typeof isPageValid;
}

const styles = (theme: Theme) =>
createStyles({
buttonContainer: {
textAlign: "right",
},
...modalBasic,
...wizardCommon,
});

const Affinity = ({
classes,
podAffinity,
affinityLabels,
setModalErrorSnackMessage,
updateAddField,
isPageValid,
}: IAffinityProps) => {
const [validationErrors, setValidationErrors] = useState<any>({});

// Common
const updateField = useCallback(
(field: string, value: any) => {
updateAddField("affinity", field, value);
},
[updateAddField]
);

// Validation
useEffect(() => {
let customAccountValidation: IValidation[] = [];

if (podAffinity === "nodeSelector") {
let valid = true;

const splittedLabels = affinityLabels.split("&");

if (splittedLabels.length === 1 && splittedLabels[0] === "") {
valid = false;
}

splittedLabels.forEach((item: string, index: number) => {
const splitItem = item.split("=");

if (splitItem.length !== 2) {
valid = false;
}

if (index + 1 !== splittedLabels.length) {
if (splitItem[0] === "" || splitItem[1] === "") {
valid = false;
}
}
});

customAccountValidation = [
...customAccountValidation,
{
fieldKey: "labels",
required: true,
value: affinityLabels,
customValidation: !valid,
customValidationMessage:
"You need to add at least one label key-pair",
},
];
}

const commonVal = commonFormValidation(customAccountValidation);

isPageValid("affinity", Object.keys(commonVal).length === 0);

setValidationErrors(commonVal);
}, [isPageValid, podAffinity, affinityLabels]);

return (
<Fragment>
<div className={classes.headerElement}>
<h3 className={classes.h3Section}>Pod Affinity</h3>
<span className={classes.descriptionText}>
Configure how pods will be assigned to nodes
</span>
</div>
<Grid item xs={12}>
<RadioGroupSelector
currentSelection={podAffinity}
id="affinity-options"
name="affinity-options"
label="Type"
onChange={(e) => {
updateField("podAffinity", e.target.value);
}}
selectorOptions={[
{ label: "None", value: "none" },
{ label: "Default (Pod Anti-afinnity)", value: "default" },
{ label: "Node Selector", value: "nodeSelector" },
]}
/>
MinIO supports multiple configurations for Pod Afinnity
</Grid>
{podAffinity === "nodeSelector" && (
<Fragment>
<br />
<Grid item xs={12}>
<QueryMultiSelector
name="labels"
label="Labels"
elements={affinityLabels}
onChange={(vl: string) => {
updateField("affinityLabels", vl);
}}
keyPlaceholder="Label Key"
valuePlaceholder="Label Value"
tooltip="Labels to be used in nodeSelector assignation. Invalid key-pairs will be ignored"
withBorder
/>
<span className={classes.error}>{validationErrors["labels"]}</span>
</Grid>
</Fragment>
)}
</Fragment>
);
};

const mapState = (state: AppState) => ({
podAffinity: state.tenants.createTenant.fields.affinity.podAffinity,
affinityLabels: state.tenants.createTenant.fields.affinity.affinityLabels,
});

const connector = connect(mapState, {
setModalErrorSnackMessage,
updateAddField,
isPageValid,
});

export default withStyles(styles)(connector(Affinity));
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { Button, LinearProgress } from "@material-ui/core";
import api from "../../../../common/api";
import { IAddPoolRequest, ITenant } from "../ListTenants/types";
import { IAffinityModel } from "../../../../common/types";
import { getHardcodedAffinity } from "./utils";
import { getDefaultAffinity } from "./utils";

import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
import { IQuotaElement, IQuotas, Opts } from "../ListTenants/utils";

Expand Down Expand Up @@ -121,7 +122,7 @@ const AddPoolModal = ({

const poolName = generatePoolName(tenant.pools);

const hardCodedAffinity: IAffinityModel = getHardcodedAffinity(
const defaultAffinity: IAffinityModel = getDefaultAffinity(
tenant.name,
poolName
);
Expand All @@ -135,7 +136,7 @@ const AddPoolModal = ({
storage_class_name: selectedStorageClass,
labels: null,
},
affinity: hardCodedAffinity,
affinity: defaultAffinity,
};

api
Expand Down
Loading