Fix NodeSelector Screen (#732)

This makes it so the node selector logic is different from podAffinity

Signed-off-by: Daniel Valdivia <hola@danielvaldivia.com>
This commit is contained in:
Daniel Valdivia
2021-05-10 17:34:54 -07:00
committed by GitHub
parent 4f0752c9fc
commit 3a7da72919
14 changed files with 127 additions and 82 deletions

View File

@@ -1,8 +1,8 @@
{
"files": {
"main.css": "/static/css/main.a19f3d53.chunk.css",
"main.js": "/static/js/main.d6310664.chunk.js",
"main.js.map": "/static/js/main.d6310664.chunk.js.map",
"main.js": "/static/js/main.b74c83f1.chunk.js",
"main.js.map": "/static/js/main.b74c83f1.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.f324abd6.chunk.css": "/static/css/2.f324abd6.chunk.css",
@@ -20,6 +20,6 @@
"static/css/2.f324abd6.chunk.css",
"static/js/2.26f9f812.chunk.js",
"static/css/main.a19f3d53.chunk.css",
"static/js/main.d6310664.chunk.js"
"static/js/main.b74c83f1.chunk.js"
]
}

View File

@@ -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.f324abd6.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.26f9f812.chunk.js"></script><script src="/static/js/main.d6310664.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.f324abd6.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.26f9f812.chunk.js"></script><script src="/static/js/main.b74c83f1.chunk.js"></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -99,7 +99,7 @@ export interface ITenantUsage {
export interface IAffinityModel {
podAntiAffinity?: IPodAntiAffinityModel;
podAffinity?: IPodAffinityModel;
nodeAffinity?: INodeAffinityModel;
}
export interface IPodAntiAffinityModel {
@@ -115,17 +115,16 @@ export interface IPodAffinityTermLabelSelector {
matchExpressions: IMatchExpressionItem[];
}
export interface IPodAffinityModel {
requiredDuringSchedulingIgnoredDuringExecution: IPodAffinityTerms[];
export interface INodeAffinityModel {
requiredDuringSchedulingIgnoredDuringExecution: INodeAffinityTerms;
}
export interface IPodAffinityTerms {
labelSelector: IPodAffinityLabelsSelector;
topologyKey: string;
export interface INodeAffinityTerms {
nodeSelectorTerms: INodeAffinityLabelsSelector[];
}
export interface IPodAffinityLabelsSelector {
matchLabels: object;
export interface INodeAffinityLabelsSelector {
matchExpressions: object[];
}
export interface IMatchExpressionItem {
@@ -377,4 +376,5 @@ export interface PrometheusConfiguration {
export interface AffinityConfiguration {
affinityType: "default" | "nodeSelector" | "none";
nodeSelectorLabels?: ILabelKeyPair[];
withPodAntiAffinity?: boolean;
}

View File

@@ -168,7 +168,8 @@ const AddTenant = ({
fields.configure.prometheusSelectedStorageClass;
const prometheusVolumeSize = fields.configure.prometheusVolumeSize;
const affinityType = fields.affinity.podAffinity;
const affinityLabels = fields.affinity.affinityLabels;
const nodeSelectorLabels = fields.affinity.nodeSelectorLabels;
const withPodAntiAffinity = fields.affinity.withPodAntiAffinity;
if (addSending) {
const poolName = generatePoolName([]);
@@ -182,7 +183,14 @@ const AddTenant = ({
};
break;
case "nodeSelector":
affinityObject = { affinity: getNodeSelector(affinityLabels) };
affinityObject = {
affinity: getNodeSelector(
nodeSelectorLabels,
withPodAntiAffinity,
tenantName,
poolName
),
};
break;
}

View File

@@ -31,11 +31,13 @@ import {
} from "../../../../../utils/validationFunctions";
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import QueryMultiSelector from "../../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector";
import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
interface IAffinityProps {
classes: any;
podAffinity: string;
affinityLabels: string;
nodeSelectorLabels: string;
withPodAntiAffinity: boolean;
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
updateAddField: typeof updateAddField;
isPageValid: typeof isPageValid;
@@ -53,7 +55,8 @@ const styles = (theme: Theme) =>
const Affinity = ({
classes,
podAffinity,
affinityLabels,
nodeSelectorLabels,
withPodAntiAffinity,
setModalErrorSnackMessage,
updateAddField,
isPageValid,
@@ -75,7 +78,7 @@ const Affinity = ({
if (podAffinity === "nodeSelector") {
let valid = true;
const splittedLabels = affinityLabels.split("&");
const splittedLabels = nodeSelectorLabels.split("&");
if (splittedLabels.length === 1 && splittedLabels[0] === "") {
valid = false;
@@ -100,7 +103,7 @@ const Affinity = ({
{
fieldKey: "labels",
required: true,
value: affinityLabels,
value: nodeSelectorLabels,
customValidation: !valid,
customValidationMessage:
"You need to add at least one label key-pair",
@@ -113,7 +116,7 @@ const Affinity = ({
isPageValid("affinity", Object.keys(commonVal).length === 0);
setValidationErrors(commonVal);
}, [isPageValid, podAffinity, affinityLabels]);
}, [isPageValid, podAffinity, nodeSelectorLabels]);
return (
<Fragment>
@@ -143,13 +146,28 @@ const Affinity = ({
{podAffinity === "nodeSelector" && (
<Fragment>
<br />
<Grid item xs={12}>
<FormSwitchWrapper
value="with_pod_anti_affinity"
id="with_pod_anti_affinity"
name="with_pod_anti_affinity"
checked={withPodAntiAffinity}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
updateField("withPodAntiAffinity", checked);
}}
label={"With Pod Anti-Affinity"}
/>
</Grid>
<Grid item xs={12}>
<QueryMultiSelector
name="labels"
label="Labels"
elements={affinityLabels}
elements={nodeSelectorLabels}
onChange={(vl: string) => {
updateField("affinityLabels", vl);
updateField("nodeSelectorLabels", vl);
}}
keyPlaceholder="Label Key"
valuePlaceholder="Label Value"
@@ -166,7 +184,10 @@ const Affinity = ({
const mapState = (state: AppState) => ({
podAffinity: state.tenants.createTenant.fields.affinity.podAffinity,
affinityLabels: state.tenants.createTenant.fields.affinity.affinityLabels,
nodeSelectorLabels:
state.tenants.createTenant.fields.affinity.nodeSelectorLabels,
withPodAntiAffinity:
state.tenants.createTenant.fields.affinity.withPodAntiAffinity,
});
const connector = connect(mapState, {

View File

@@ -115,44 +115,46 @@ const TenantYAML = ({
title={`YAML`}
>
{loading && <LinearProgress />}
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
updateTenant(e);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
<Grid item xs={12}>
<br />
{!loading && (
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
updateTenant(e);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
<Grid item xs={12}>
<br />
</Grid>
<CodeMirrorWrapper
label={`Tenant Specification`}
value={tenantYaml}
mode={"yaml"}
onBeforeChange={(editor, data, value) => {
setTenantYaml(value);
}}
/>
</Grid>
<CodeMirrorWrapper
label={`Tenant Specification`}
value={tenantYaml}
mode={"yaml"}
onBeforeChange={(editor, data, value) => {
setTenantYaml(value);
}}
/>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading || !validSave}
>
Save
</Button>
</Grid>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading || !validSave}
>
Save
</Button>
</Grid>
)}
</Grid>
</form>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
)}
</ModalWrapper>
);
};

View File

@@ -42,32 +42,42 @@ export const getDefaultAffinity = (tenantName: string, poolName: string) => {
return defaultAffinity;
};
export const getNodeSelector = (labels: string) => {
export const getNodeSelector = (
labels: string,
withPodAntiAffinity: boolean,
tenantName: string,
poolName: string
) => {
// Labels in the form of key1=value1&key2=value2&key3=value3...
const splittedLabels = labels.split("&");
let labelItems: any = {};
let matchExpressions: any = [];
splittedLabels.forEach((label: string) => {
const splitKeyValue = label.split("=");
if (splitKeyValue.length === 2) {
labelItems = {
...labelItems,
[`${splitKeyValue[0]}`]: splitKeyValue[1],
};
matchExpressions.push({
key: splitKeyValue[0],
operator: "In",
values: [splitKeyValue[1]],
});
}
});
const nodeSelector: IAffinityModel = {
podAffinity: {
requiredDuringSchedulingIgnoredDuringExecution: [
{
labelSelector: {
matchLabels: { ...labelItems },
nodeAffinity: {
requiredDuringSchedulingIgnoredDuringExecution: {
nodeSelectorTerms: [
{
matchExpressions: matchExpressions,
},
topologyKey: "kubernetes.io/hostname",
},
],
],
},
},
};
if (withPodAntiAffinity) {
const def = getDefaultAffinity(tenantName, poolName);
nodeSelector.podAntiAffinity = def.podAntiAffinity;
}
console.log(nodeSelector);
return nodeSelector;
};

View File

@@ -78,8 +78,8 @@ const initialState: ITenantState = {
},
identityProvider: {
idpSelection: "Built-in",
accessKeys: [""],
secretKeys: [""],
accessKeys: [getRandomString(16)],
secretKeys: [getRandomString(32)],
openIDURL: "",
openIDClientID: "",
openIDSecretID: "",
@@ -159,8 +159,9 @@ const initialState: ITenantState = {
limitSize: {},
},
affinity: {
affinityLabels: "",
nodeSelectorLabels: "",
podAffinity: "default",
withPodAntiAffinity: true,
},
},
certificates: {
@@ -553,8 +554,9 @@ export function tenantsReducer(
limitSize: {},
},
affinity: {
affinityLabels: "",
nodeSelectorLabels: "",
podAffinity: "default",
withPodAntiAffinity: true,
},
},
certificates: {

View File

@@ -183,7 +183,8 @@ export interface ITenantSizeFields {
export interface ITenantAffinity {
podAffinity: "default" | "nodeSelector" | "none";
affinityLabels: string;
nodeSelectorLabels: string;
withPodAntiAffinity: boolean;
}
export interface ITenantState {

View File

@@ -704,10 +704,11 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
// Create the secret any built-in user passed if no external IDP was configured
if tenantReq.Idp != nil && len(tenantReq.Idp.Keys) > 0 && tenantReq.Idp.ActiveDirectory == nil && tenantReq.Idp.Oidc == nil {
for i := 0; i < len(tenantReq.Idp.Keys); i++ {
users = append(users, &corev1.LocalObjectReference{Name: fmt.Sprintf("%s-user-%d", tenantName, i)})
userSecretName := fmt.Sprintf("%s-user-%d", tenantName, i)
users = append(users, &corev1.LocalObjectReference{Name: userSecretName})
userSecret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s%d", secretName, i),
Name: userSecretName,
Labels: map[string]string{
miniov2.TenantLabel: tenantName,
},