Add feature hide-menu for embedded screens on Operator UI (#1604)

* Add feature hide-menu for embedded screens on Operator UI

Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
Daniel Valdivia
2022-02-21 21:42:18 -08:00
committed by GitHub
parent 28dcd19dd9
commit 56c4311a6b
27 changed files with 381 additions and 166 deletions

View File

@@ -129,8 +129,7 @@ export const IAM_PAGES = {
TOOLS_LOGS: "/tools/logs",
TOOLS_AUDITLOGS: "/tools/audit-logs",
TOOLS_TRACE: "/tools/trace",
METRICS: "/tools/metrics",
DASHBOARD: "/tools/dashboard",
DASHBOARD: "/tools/metrics",
TOOLS_HEAL: "/tools/heal",
TOOLS_WATCH: "/tools/watch",
/* Health */
@@ -178,6 +177,8 @@ export const IAM_PAGES = {
"/namespaces/:tenantNamespace/tenants/:tenantName/summary",
NAMESPACE_TENANT_METRICS:
"/namespaces/:tenantNamespace/tenants/:tenantName/metrics",
NAMESPACE_TENANT_TRACE:
"/namespaces/:tenantNamespace/tenants/:tenantName/trace",
NAMESPACE_TENANT_POOLS:
"/namespaces/:tenantNamespace/tenants/:tenantName/pools",
NAMESPACE_TENANT_VOLUMES:
@@ -297,9 +298,6 @@ export const IAM_PAGES_PERMISSIONS = {
[IAM_PAGES.DASHBOARD]: [
IAM_SCOPES.ADMIN_SERVER_INFO, // displays dashboard information
],
[IAM_PAGES.METRICS]: [
IAM_SCOPES.ADMIN_SERVER_INFO, // displays dashboard information
],
[IAM_PAGES.POLICIES_VIEW]: [
IAM_SCOPES.ADMIN_DELETE_POLICY,
IAM_SCOPES.ADMIN_LIST_GROUPS,

View File

@@ -14,7 +14,7 @@
// 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 from "react";
import React, { Fragment } from "react";
import { Theme } from "@mui/material/styles";
import { connect } from "react-redux";
import Grid from "@mui/material/Grid";
@@ -38,6 +38,7 @@ interface IPageHeader {
managerObjects?: IFileItem[];
toggleList: typeof toggleList;
middleComponent?: React.ReactNode;
features: string[];
}
const styles = (theme: Theme) =>
@@ -88,7 +89,11 @@ const PageHeader = ({
managerObjects,
toggleList,
middleComponent,
features,
}: IPageHeader) => {
if (features.includes("hide-menu")) {
return <Fragment />;
}
return (
<Grid
container
@@ -157,6 +162,7 @@ const mapState = (state: AppState) => ({
sidebarOpen: state.system.sidebarOpen,
operatorMode: state.system.operatorMode,
managerObjects: state.objectBrowser.objectManager.objectsToManage,
features: state.console.session.features,
});
const mapDispatchToProps = {

View File

@@ -56,7 +56,6 @@ const Heal = React.lazy(() => import("./Heal/Heal"));
const Watch = React.lazy(() => import("./Watch/Watch"));
const HealthInfo = React.lazy(() => import("./HealthInfo/HealthInfo"));
const Storage = React.lazy(() => import("./Storage/Storage"));
const Metrics = React.lazy(() => import("./Dashboard/Metrics"));
const Hop = React.lazy(() => import("./Tenants/TenantDetails/hop/Hop"));
const AddTenant = React.lazy(() => import("./Tenants/AddTenant/AddTenant"));
@@ -211,10 +210,6 @@ const Console = ({
component: Dashboard,
path: IAM_PAGES.DASHBOARD,
},
{
component: Metrics,
path: IAM_PAGES.METRICS,
},
{
component: Buckets,
path: IAM_PAGES.ADD_BUCKETS,
@@ -433,6 +428,11 @@ const Console = ({
path: IAM_PAGES.NAMESPACE_TENANT_METRICS,
forceDisplay: true,
},
{
component: TenantDetails,
path: IAM_PAGES.NAMESPACE_TENANT_TRACE,
forceDisplay: true,
},
{
component: TenantDetails,
path: IAM_PAGES.NAMESPACE_TENANT_PODS_LIST,
@@ -513,7 +513,7 @@ const Console = ({
const location = useLocation();
let hideMenu = false;
if (location.pathname === IAM_PAGES.METRICS) {
if (features?.includes("hide-menu")) {
hideMenu = true;
} else if (location.pathname.endsWith("/hop")) {
hideMenu = true;

View File

@@ -84,6 +84,10 @@ const ConsoleKBar = ({
operatorMode: boolean;
features: string[] | null;
}) => {
if (features?.includes("hide-menu")) {
return <Console />;
}
const allowedMenuItems = validRoutes(features, operatorMode);
const initialActions = [];

View File

@@ -1,91 +0,0 @@
// 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, { Fragment, useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";
import get from "lodash/get";
import Grid from "@mui/material/Grid";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { LinearProgress } from "@mui/material";
import api from "../../../common/api";
import { Usage } from "./types";
import { setErrorSnackMessage } from "../../../actions";
import { ErrorResponseHandler } from "../../../common/types";
import PrDashboard from "./Prometheus/PrDashboard";
import BasicDashboard from "./BasicDashboard/BasicDashboard";
interface IMetricsSimple {
classes: any;
displayErrorMessage: typeof setErrorSnackMessage;
}
const styles = (theme: Theme) => createStyles({});
const Metrics = ({ classes, displayErrorMessage }: IMetricsSimple) => {
const [loading, setLoading] = useState<boolean>(true);
const [basicResult, setBasicResult] = useState<Usage | null>(null);
const fetchUsage = useCallback(() => {
api
.invoke("GET", `/api/v1/admin/info`)
.then((res: Usage) => {
setBasicResult(res);
setLoading(false);
})
.catch((err: ErrorResponseHandler) => {
displayErrorMessage(err);
setLoading(false);
});
}, [setBasicResult, setLoading, displayErrorMessage]);
useEffect(() => {
if (loading) {
fetchUsage();
}
}, [loading, fetchUsage]);
const widgets = get(basicResult, "widgets", null);
return (
<Fragment>
<Grid container>
{loading ? (
<Grid item xs={12}>
<LinearProgress />
</Grid>
) : (
<Fragment>
{widgets !== null ? (
<Grid container>
<PrDashboard />
</Grid>
) : (
<BasicDashboard usage={basicResult} />
)}
</Fragment>
)}
</Grid>
</Fragment>
);
};
const connector = connect(null, {
displayErrorMessage: setErrorSnackMessage,
});
export default withStyles(styles)(connector(Metrics));

View File

@@ -50,7 +50,6 @@ import BackLink from "../../../../common/BackLink";
import VerticalTabs from "../../Common/VerticalTabs/VerticalTabs";
import BoxIconButton from "../../Common/BoxIconButton/BoxIconButton";
import withSuspense from "../../Common/Components/withSuspense";
import PVCDetails from "./pvcs/PVCDetails";
const TenantYAML = withSuspense(React.lazy(() => import("./TenantYAML")));
const TenantSummary = withSuspense(React.lazy(() => import("./TenantSummary")));
@@ -63,6 +62,10 @@ const VolumesSummary = withSuspense(
React.lazy(() => import("./VolumesSummary"))
);
const TenantMetrics = withSuspense(React.lazy(() => import("./TenantMetrics")));
const TenantTrace = withSuspense(React.lazy(() => import("./TenantTrace")));
const TenantVolumes = withSuspense(
React.lazy(() => import("./pvcs/TenantVolumes"))
);
const TenantSecurity = withSuspense(
React.lazy(() => import("./TenantSecurity"))
);
@@ -424,6 +427,10 @@ const TenantDetails = ({
path="/namespaces/:tenantNamespace/tenants/:tenantName/metrics"
component={TenantMetrics}
/>
<Route
path="/namespaces/:tenantNamespace/tenants/:tenantName/trace"
component={TenantTrace}
/>
<Route
path="/namespaces/:tenantNamespace/tenants/:tenantName/security"
component={TenantSecurity}
@@ -442,7 +449,7 @@ const TenantDetails = ({
/>
<Route
path="/namespaces/:tenantNamespace/tenants/:tenantName/pvcs/:PVCName"
component={PVCDetails}
component={TenantVolumes}
/>
<Route
path="/namespaces/:tenantNamespace/tenants/:tenantName/volumes"

View File

@@ -28,6 +28,7 @@ import { ITenant } from "../ListTenants/types";
import { setErrorSnackMessage } from "../../../../actions";
import { AppState } from "../../../../store";
import { LinearProgress } from "@mui/material";
import { IAM_PAGES } from "../../../../common/SecureComponent/permissions";
interface ITenantMetrics {
classes: any;
@@ -66,7 +67,7 @@ const TenantMetrics = ({ classes, match }: ITenantMetrics) => {
<iframe
className={classes.iframeStyle}
title={"metrics"}
src={`/api/proxy/${tenantNamespace}/${tenantName}/metrics?cp=y`}
src={`/api/proxy/${tenantNamespace}/${tenantName}${IAM_PAGES.DASHBOARD}?cp=y`}
onLoad={() => {
setLoading(false);
}}

View File

@@ -0,0 +1,106 @@
// 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, { useState } from "react";
import { connect } from "react-redux";
import get from "lodash/get";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
containerForHeader,
tenantDetailsStyles,
} from "../../Common/FormComponents/common/styleLibrary";
import { ITenant } from "../ListTenants/types";
import { setErrorSnackMessage } from "../../../../actions";
import { AppState } from "../../../../store";
import { LinearProgress } from "@mui/material";
import { IAM_PAGES } from "../../../../common/SecureComponent/permissions";
interface ITenantTrace {
classes: any;
match: any;
tenant: ITenant | null;
setErrorSnackMessage: typeof setErrorSnackMessage;
}
const styles = (theme: Theme) =>
createStyles({
...tenantDetailsStyles,
iframeStyle: {
border: "0px",
flex: "1 1 auto",
minHeight: "800px",
width: "100%",
},
...containerForHeader(theme.spacing(4)),
});
const TenantTrace = ({ classes, match }: ITenantTrace) => {
const tenantName = match.params["tenantName"];
const tenantNamespace = match.params["tenantNamespace"];
const [loading, setLoading] = useState<boolean>(true);
return (
<React.Fragment>
<h1 className={classes.sectionTitle}>Metrics</h1>
{loading && (
<div style={{ marginTop: "80px" }}>
<LinearProgress />
</div>
)}
<iframe
className={classes.iframeStyle}
title={"metrics"}
src={`/api/proxy/${tenantNamespace}/${tenantName}${IAM_PAGES.TOOLS_TRACE}?cp=y`}
onLoad={() => {
setLoading(false);
}}
/>
</React.Fragment>
);
};
const mapState = (state: AppState) => ({
loadingTenant: state.tenants.tenantDetails.loadingTenant,
selectedTenant: state.tenants.tenantDetails.currentTenant,
tenant: state.tenants.tenantDetails.tenantInfo,
logEnabled: get(state.tenants.tenantDetails.tenantInfo, "logEnabled", false),
monitoringEnabled: get(
state.tenants.tenantDetails.tenantInfo,
"monitoringEnabled",
false
),
encryptionEnabled: get(
state.tenants.tenantDetails.tenantInfo,
"encryptionEnabled",
false
),
adEnabled: get(state.tenants.tenantDetails.tenantInfo, "idpAdEnabled", false),
oidcEnabled: get(
state.tenants.tenantDetails.tenantInfo,
"idpOidcEnabled",
false
),
});
const connector = connect(mapState, {
setErrorSnackMessage,
});
export default withStyles(styles)(connector(TenantTrace));

View File

@@ -43,7 +43,7 @@ const styles = (theme: Theme) =>
...containerForHeader(theme.spacing(4)),
});
const PVCDetails = ({
const TenantVolumes = ({
classes,
match,
setErrorSnackMessage,
@@ -98,4 +98,4 @@ const PVCDetails = ({
);
};
export default withStyles(styles)(PVCDetails);
export default withStyles(styles)(TenantVolumes);

View File

@@ -23,6 +23,6 @@ fixture("For user with default permissions").page("http://localhost:9090");
test("Login to Operator Web Page", async (t) => {
await t
.navigateTo("http://localhost:9090/login")
.typeText("#jwt","anyrandompasswordwillwork")
.click("button.MuiButton-root")
});
.typeText("#jwt", "anyrandompasswordwillwork")
.click("button.MuiButton-root");
});

View File

@@ -16,6 +16,7 @@
import * as constants from "./constants";
import { Selector } from "testcafe";
import { IAM_PAGES } from "../../src/common/SecureComponent/permissions";
//----------------------------------------------------
// General sidebar element
@@ -32,7 +33,7 @@ export const monitoringElement = Selector(".MuiPaper-root")
export const monitoringChildren = Selector("#tools-children");
export const dashboardElement = monitoringChildren
.find("a")
.withAttribute("href", "/tools/dashboard");
.withAttribute("href", IAM_PAGES.DASHBOARD);
export const logsElement = monitoringChildren
.find("a")
.withAttribute("href", "/tools/logs");