Skip to content

Commit fcb74ae

Browse files
authored
Fix Node Number validation (#918)
Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
1 parent 7e4d349 commit fcb74ae

File tree

4 files changed

+44
-41
lines changed

4 files changed

+44
-41
lines changed

operatorapi/error.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ var (
3838
errLicenseNotFound = errors.New("license not found")
3939
errAvoidSelfAccountDelete = errors.New("logged in user cannot be deleted by itself")
4040
errAccessDenied = errors.New("access denied")
41-
errTooFewNodes = errors.New("at least 4 nodes are required in cluster")
42-
errTooFewSchedulableNodes = errors.New("at least 4 schedulable nodes are required in cluster")
43-
errFewerThanFourNodes = errors.New("at least 4 nodes are required in request")
41+
errTooManyNodes = errors.New("cannot request more nodes than what is available in the cluster")
42+
errTooFewNodes = errors.New("there are not enough nodes in the cluster to support this tenant")
43+
errTooFewSchedulableNodes = errors.New("there is not enough schedulable nodes to satisfy this requirement")
44+
errFewerThanFourNodes = errors.New("at least 4 nodes are required for a tenant")
4445
)
4546

4647
// prepareError receives an error object and parse it against k8sErrors, returns the right error code paired with a generic error message

operatorapi/operator_nodes.go

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ import (
2626

2727
"github.com/minio/console/cluster"
2828

29-
"errors"
30-
3129
"github.com/go-openapi/runtime/middleware"
3230
"github.com/minio/console/models"
3331
"github.com/minio/console/operatorapi/operations"
@@ -56,25 +54,42 @@ func registerNodesHandlers(api *operations.OperatorAPI) {
5654

5755
// getMaxAllocatableMemory get max allocatable memory given a desired number of nodes
5856
func getMaxAllocatableMemory(ctx context.Context, clientset v1.CoreV1Interface, numNodes int32) (*models.MaxAllocatableMemResponse, error) {
57+
// can't request less than 4 nodes
5958
if numNodes < 4 {
60-
return nil, errors.New("error NumNodes must be at least 4")
59+
return nil, errFewerThanFourNodes
6160
}
6261

6362
// get all nodes from cluster
6463
nodes, err := clientset.Nodes().List(ctx, metav1.ListOptions{})
6564
if err != nil {
6665
return nil, err
6766
}
68-
if len(nodes.Items) < int(numNodes) {
69-
return nil, errTooFewNodes
70-
}
71-
activeNodes := 0
72-
for i := 0; i < len(nodes.Items); i++ {
73-
if !nodes.Items[i].Spec.Unschedulable {
74-
activeNodes++
67+
68+
// requesting more nodes than are schedulable in the cluster
69+
schedulableNodes := len(nodes.Items)
70+
nonMasterNodes := len(nodes.Items)
71+
for _, node := range nodes.Items {
72+
// check taints to check if node is schedulable
73+
for _, taint := range node.Spec.Taints {
74+
if taint.Effect == corev1.TaintEffectNoSchedule {
75+
schedulableNodes--
76+
}
77+
// check if the node is a master
78+
if taint.Key == "node-role.kubernetes.io/master" {
79+
nonMasterNodes--
80+
}
7581
}
7682
}
77-
if activeNodes < int(numNodes) {
83+
// requesting more nodes than schedulable and less than total number of workers
84+
if int(numNodes) > schedulableNodes && int(numNodes) < nonMasterNodes {
85+
return nil, errTooManyNodes
86+
}
87+
if nonMasterNodes < int(numNodes) {
88+
return nil, errTooFewNodes
89+
}
90+
91+
// not enough schedulable nodes
92+
if schedulableNodes < int(numNodes) {
7893
return nil, errTooFewSchedulableNodes
7994
}
8095

portal-ui/src/common/utils.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -224,16 +224,6 @@ export const calculateDistribution = (
224224
};
225225
}
226226

227-
if (forcedNodes < 4) {
228-
return {
229-
error: "Number of nodes cannot be less than 4",
230-
nodes: 0,
231-
persistentVolumes: 0,
232-
disks: 0,
233-
pvSize: 0,
234-
};
235-
}
236-
237227
if (drivesPerServer <= 0) {
238228
return {
239229
error: "Number of drives must be at least 1",

portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantSize.tsx

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,26 @@
1414
// You should have received a copy of the GNU Affero General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17-
import React, { Fragment, useState, useEffect, useCallback } from "react";
17+
import React, { Fragment, useCallback, useEffect, useState } from "react";
1818
import { connect } from "react-redux";
1919
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
2020
import { AppState } from "../../../../../store";
21-
import { updateAddField, isPageValid } from "../../actions";
21+
import { isPageValid, updateAddField } from "../../actions";
2222
import {
23-
wizardCommon,
2423
modalBasic,
24+
wizardCommon,
2525
} from "../../../Common/FormComponents/common/styleLibrary";
2626
import Grid from "@material-ui/core/Grid";
2727
import Table from "@material-ui/core/Table";
2828
import TableBody from "@material-ui/core/TableBody";
2929
import TableCell from "@material-ui/core/TableCell";
3030
import TableRow from "@material-ui/core/TableRow";
3131
import {
32+
calculateDistribution,
33+
erasureCodeCalc,
3234
getBytes,
3335
k8sfactorForDropdown,
3436
niceBytes,
35-
calculateDistribution,
36-
erasureCodeCalc,
3737
setMemoryResource,
3838
} from "../../../../../common/utils";
3939
import { clearValidationError } from "../../utils";
@@ -138,6 +138,7 @@ const TenantSize = ({
138138

139139
const getMaxAllocableMemory = (nodes: string) => {
140140
if (nodes !== "" && !isNaN(parseInt(nodes))) {
141+
setNodeError("");
141142
api
142143
.invoke(
143144
"GET",
@@ -147,10 +148,9 @@ const TenantSize = ({
147148
const maxMemory = res.max_memory ? res.max_memory : 0;
148149
updateField("maxAllocableMemo", maxMemory);
149150
})
150-
.catch((err: any) => {
151+
.catch((err: ErrorResponseHandler) => {
151152
setErrorFlag(true);
152153
setNodeError(err.errorMessage);
153-
console.error(err);
154154
});
155155
}
156156
};
@@ -230,13 +230,6 @@ const TenantSize = ({
230230
useEffect(() => {
231231
const parsedSize = getBytes(volumeSize, sizeFactor, true);
232232
const commonValidation = commonFormValidation([
233-
{
234-
fieldKey: "nodes",
235-
required: true,
236-
value: nodes,
237-
customValidation: parseInt(nodes) < 4,
238-
customValidationMessage: "Number of nodes cannot be less than 4",
239-
},
240233
{
241234
fieldKey: "nodes",
242235
required: true,
@@ -297,7 +290,7 @@ const TenantSize = ({
297290
selectedStorageClass,
298291
isPageValid,
299292
errorFlag,
300-
nodeError
293+
nodeError,
301294
]);
302295

303296
/* End Validation of pages */
@@ -310,8 +303,12 @@ const TenantSize = ({
310303
Please select the desired capacity
311304
</span>
312305
</div>
313-
<span className={classes.error}>{distribution.error}</span>
314-
<span className={classes.error}>{memorySize.error}</span>
306+
{distribution.error !== "" && (
307+
<div className={classes.error}>{distribution.error}</div>
308+
)}
309+
{memorySize.error !== "" && (
310+
<div className={classes.error}>{memorySize.error}</div>
311+
)}
315312
<Grid item xs={12}>
316313
<InputBoxWrapper
317314
id="nodes"

0 commit comments

Comments
 (0)