Fix Tenant Details Bugs (#589)
This fixes #584 by making the expand set a name for the pool This fixes #585 by making the expand set an affinity for the pool This fixes #586 by generating a pool name if it's not indicated only
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -15,12 +15,8 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import storage from "local-storage-fallback";
|
||||
import {
|
||||
ICapacity,
|
||||
IErasureCodeCalc,
|
||||
IPoolModel,
|
||||
IStorageFactors,
|
||||
} from "./types";
|
||||
import { ICapacity, IErasureCodeCalc, IStorageFactors } from "./types";
|
||||
import { IPool } from "../screens/Console/Tenants/ListTenants/types";
|
||||
|
||||
const minStReq = 1073741824; // Minimal Space required for MinIO
|
||||
const minMemReq = 2147483648; // Minimal Memory required for MinIO in bytes
|
||||
@@ -419,7 +415,7 @@ export const erasureCodeCalc = (
|
||||
};
|
||||
|
||||
// Pool Name Generator
|
||||
export const generatePoolName = (pools: IPoolModel[]) => {
|
||||
export const generatePoolName = (pools: IPool[]) => {
|
||||
const poolCounter = pools.length;
|
||||
|
||||
return `pool-${poolCounter}`;
|
||||
|
||||
@@ -187,7 +187,7 @@ const Menu = ({ userLoggedIn, classes, pages, operatorMode }: IMenuProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
const menuItems = [
|
||||
let menuItems = [
|
||||
{
|
||||
group: "common",
|
||||
type: "item",
|
||||
@@ -316,14 +316,6 @@ const Menu = ({ userLoggedIn, classes, pages, operatorMode }: IMenuProps) => {
|
||||
name: "Warp",
|
||||
icon: <WarpIcon />,
|
||||
},
|
||||
{
|
||||
group: "License",
|
||||
type: "item",
|
||||
component: NavLink,
|
||||
to: "/license",
|
||||
name: "License",
|
||||
icon: <LicenseIcon />,
|
||||
},
|
||||
];
|
||||
|
||||
const allowedPages = pages.reduce((result: any, item: any, index: any) => {
|
||||
@@ -331,6 +323,27 @@ const Menu = ({ userLoggedIn, classes, pages, operatorMode }: IMenuProps) => {
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
// Append the license page according to the allowedPages
|
||||
if (allowedPages.hasOwnProperty("/tenants")) {
|
||||
menuItems.push({
|
||||
group: "Operator",
|
||||
type: "item",
|
||||
component: NavLink,
|
||||
to: "/license",
|
||||
name: "License",
|
||||
icon: <LicenseIcon />,
|
||||
});
|
||||
} else {
|
||||
menuItems.push({
|
||||
group: "License",
|
||||
type: "item",
|
||||
component: NavLink,
|
||||
to: "/license",
|
||||
name: "License",
|
||||
icon: <LicenseIcon />,
|
||||
});
|
||||
}
|
||||
|
||||
const allowedItems = menuItems.filter(
|
||||
(item: any) =>
|
||||
allowedPages[item.to] || item.forceDisplay || item.type !== "item"
|
||||
|
||||
@@ -19,6 +19,6 @@ export const menuGroups = [
|
||||
{ label: "User", group: "User", collapsible: true },
|
||||
{ label: "Admin", group: "Admin", collapsible: true },
|
||||
{ label: "Tools", group: "Tools", collapsible: true },
|
||||
{ label: "Operator", group: "Operator", collapsible: true },
|
||||
{ label: "Operator", group: "Operator", collapsible: false },
|
||||
{ label: "", group: "License", collapsible: false },
|
||||
];
|
||||
|
||||
@@ -66,6 +66,7 @@ import { IMemorySize } from "./types";
|
||||
import Divider from "@material-ui/core/Divider";
|
||||
import { connect } from "react-redux";
|
||||
import { setModalErrorSnackMessage } from "../../../../actions";
|
||||
import { getHardcodedAffinity } from "../TenantDetails/utils";
|
||||
|
||||
interface IAddTenantProps {
|
||||
open: boolean;
|
||||
@@ -917,29 +918,10 @@ const AddTenant = ({
|
||||
if (addSending) {
|
||||
const poolName = generatePoolName([]);
|
||||
|
||||
const hardCodedAffinity: IAffinityModel = {
|
||||
podAntiAffinity: {
|
||||
requiredDuringSchedulingIgnoredDuringExecution: [
|
||||
{
|
||||
labelSelector: {
|
||||
matchExpressions: [
|
||||
{
|
||||
key: "v1.min.io/tenant",
|
||||
operator: "In",
|
||||
values: [tenantName],
|
||||
},
|
||||
{
|
||||
key: "v1.min.io/pool",
|
||||
operator: "In",
|
||||
values: [poolName],
|
||||
},
|
||||
],
|
||||
},
|
||||
topologyKey: "kubernetes.io/hostname",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const hardCodedAffinity: IAffinityModel = getHardcodedAffinity(
|
||||
tenantName,
|
||||
poolName
|
||||
);
|
||||
|
||||
const erasureCode = ecParity.split(":")[1];
|
||||
|
||||
|
||||
@@ -257,6 +257,7 @@ const ListTenants = ({ classes, setErrorSnackMessage }: ITenantsList) => {
|
||||
itemActions={tableActions}
|
||||
columns={[
|
||||
{ label: "Name", elementKey: "name" },
|
||||
{ label: "Namespace", elementKey: "namespace" },
|
||||
{ label: "Capacity", elementKey: "capacity" },
|
||||
{ label: "# of Pools", elementKey: "pool_count" },
|
||||
{ label: "State", elementKey: "currentState" },
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { LicenseInfo } from "../../License/types";
|
||||
import { IAffinityModel } from "../../../../common/types";
|
||||
|
||||
export interface IPool {
|
||||
name: string;
|
||||
@@ -24,6 +25,7 @@ export interface IPool {
|
||||
// computed
|
||||
capacity: string;
|
||||
volumes: number;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export interface IAddPoolRequest {
|
||||
@@ -31,6 +33,7 @@ export interface IAddPoolRequest {
|
||||
servers: number;
|
||||
volumes_per_server: number;
|
||||
volume_configuration: IVolumeConfiguration;
|
||||
affinity?: IAffinityModel;
|
||||
}
|
||||
|
||||
export interface IVolumeConfiguration {
|
||||
|
||||
@@ -4,10 +4,12 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { modalBasic } from "../../Common/FormComponents/common/styleLibrary";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { niceBytes } from "../../../../common/utils";
|
||||
import { generatePoolName, niceBytes } from "../../../../common/utils";
|
||||
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";
|
||||
|
||||
interface IAddPoolProps {
|
||||
tenant: ITenant;
|
||||
@@ -81,8 +83,16 @@ const AddPoolModal = ({
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setAddSending(true);
|
||||
|
||||
const poolName = generatePoolName(tenant.pools);
|
||||
|
||||
const hardCodedAffinity: IAffinityModel = getHardcodedAffinity(
|
||||
tenant.name,
|
||||
poolName
|
||||
);
|
||||
|
||||
const data: IAddPoolRequest = {
|
||||
name: "",
|
||||
name: poolName,
|
||||
servers: numberOfNodes,
|
||||
volumes_per_server: volumesPerServer,
|
||||
volume_configuration: {
|
||||
@@ -90,7 +100,9 @@ const AddPoolModal = ({
|
||||
storage_class: "",
|
||||
labels: null,
|
||||
},
|
||||
affinity: hardCodedAffinity,
|
||||
};
|
||||
|
||||
api
|
||||
.invoke(
|
||||
"POST",
|
||||
|
||||
@@ -115,6 +115,9 @@ const styles = (theme: Theme) =>
|
||||
noUnderLine: {
|
||||
textDecoration: "none",
|
||||
},
|
||||
poolLabel: {
|
||||
color: "#666666",
|
||||
},
|
||||
...modalBasic,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
@@ -127,7 +130,7 @@ const TenantDetails = ({
|
||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||
const [capacity, setCapacity] = useState<number>(0);
|
||||
const [poolCount, setPoolCount] = useState<number>(0);
|
||||
const [serverSets, setServerSets] = useState<IPool[]>([]);
|
||||
const [pools, setPools] = useState<IPool[]>([]);
|
||||
const [instances, setInstances] = useState<number>(0);
|
||||
const [volumes, setVolumes] = useState<number>(0);
|
||||
const [addPoolOpen, setAddPool] = useState<boolean>(false);
|
||||
@@ -199,25 +202,28 @@ const TenantDetails = ({
|
||||
|
||||
let totalInstances = 0;
|
||||
let totalVolumes = 0;
|
||||
let count = 1;
|
||||
let poolNamedIndex = 0;
|
||||
for (let pool of resPools) {
|
||||
const cap =
|
||||
pool.volumes_per_server *
|
||||
pool.servers *
|
||||
pool.volume_configuration.size;
|
||||
pool.name = `pool-${count}`;
|
||||
pool.label = `pool-${poolNamedIndex}`;
|
||||
if (pool.name === undefined || pool.name === "") {
|
||||
pool.name = pool.label;
|
||||
}
|
||||
pool.capacity = niceBytes(cap + "");
|
||||
pool.volumes = pool.servers * pool.volumes_per_server;
|
||||
totalInstances += pool.servers;
|
||||
totalVolumes += pool.volumes;
|
||||
count += 1;
|
||||
poolNamedIndex += 1;
|
||||
}
|
||||
setCapacity(res.total_size);
|
||||
setPoolCount(resPools.length);
|
||||
setVolumes(totalVolumes);
|
||||
setInstances(totalInstances);
|
||||
|
||||
setServerSets(resPools);
|
||||
setPools(resPools);
|
||||
|
||||
setTenant(res);
|
||||
})
|
||||
@@ -415,7 +421,7 @@ const TenantDetails = ({
|
||||
{ label: "# of Drives", elementKey: "volumes" },
|
||||
]}
|
||||
isLoading={false}
|
||||
records={serverSets}
|
||||
records={pools}
|
||||
entityName="Servers"
|
||||
idField="name"
|
||||
/>
|
||||
|
||||
43
portal-ui/src/screens/Console/Tenants/TenantDetails/utils.ts
Normal file
43
portal-ui/src/screens/Console/Tenants/TenantDetails/utils.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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 { IAffinityModel } from "../../../../common/types";
|
||||
|
||||
export const getHardcodedAffinity = (tenantName: string, poolName: string) => {
|
||||
const hardCodedAffinity: IAffinityModel = {
|
||||
podAntiAffinity: {
|
||||
requiredDuringSchedulingIgnoredDuringExecution: [
|
||||
{
|
||||
labelSelector: {
|
||||
matchExpressions: [
|
||||
{
|
||||
key: "v1.min.io/tenant",
|
||||
operator: "In",
|
||||
values: [tenantName],
|
||||
},
|
||||
{
|
||||
key: "v1.min.io/pool",
|
||||
operator: "In",
|
||||
values: [poolName],
|
||||
},
|
||||
],
|
||||
},
|
||||
topologyKey: "kubernetes.io/hostname",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
return hardCodedAffinity;
|
||||
};
|
||||
@@ -381,12 +381,14 @@ func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInf
|
||||
//minio service
|
||||
minSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.MinIOCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
// we can tolerate this error
|
||||
log.Println(err)
|
||||
}
|
||||
//console service
|
||||
conSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.ConsoleCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
// we can tolerate this error
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
schema := "http"
|
||||
@@ -401,10 +403,10 @@ func getTenantInfoResponse(session *models.Principal, params admin_api.TenantInf
|
||||
}
|
||||
var minioEndpoint string
|
||||
var consoleEndpoint string
|
||||
if len(minSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
if minSvc != nil && len(minSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
minioEndpoint = fmt.Sprintf("%s://%s", schema, minSvc.Status.LoadBalancer.Ingress[0].IP)
|
||||
}
|
||||
if len(conSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
if conSvc != nil && len(conSvc.Status.LoadBalancer.Ingress) > 0 {
|
||||
consoleEndpoint = fmt.Sprintf("%s://%s%s", consoleSchema, conSvc.Status.LoadBalancer.Ingress[0].IP, consolePort)
|
||||
}
|
||||
if minioEndpoint != "" || consoleEndpoint != "" {
|
||||
|
||||
Reference in New Issue
Block a user