Tenant log config screen (#2142)

* Created file for auditLogsScreen, connected to link in TenantDetails Audit Log tab
* Fixed input title formatting, confirmation modal logic
This commit is contained in:
jinapurapu
2022-06-23 16:43:23 -07:00
committed by GitHub
parent ba4103e03f
commit 2830022ede
8 changed files with 796 additions and 1099 deletions

View File

@@ -1639,7 +1639,6 @@ func enableTenantLoggingResponse(session *models.Principal, params operator_api.
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
}
minTenant.EnsureDefaults()
// Default class name for Log search
diskSpaceFromAPI := int64(5) * humanize.GiByte // Default is 5Gi
logSearchStorageClass := "standard"

View File

@@ -220,6 +220,7 @@ export interface ITenantMonitoringStruct {
}
export interface ITenantLogsStruct {
auditLoggingEnabled: boolean;
image: string;
labels: IKeyValue[];
annotations: IKeyValue[];

View File

@@ -1,597 +0,0 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 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 } from "react";
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Theme } from "@mui/material/styles";
import {
formFieldStyles,
modalBasic,
modalStyleUtils,
} from "../../Common/FormComponents/common/styleLibrary";
import { Button, Grid } from "@mui/material";
import api from "../../../../common/api";
import { IKeyValue, ITenant } from "../ListTenants/types";
import { ErrorResponseHandler } from "../../../../common/types";
import KeyPairEdit from "./KeyPairEdit";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import {
commonFormValidation,
IValidation,
} from "../../../../utils/validationFunctions";
import { clearValidationError } from "../utils";
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import { setModalErrorSnackMessage } from "../../../../systemSlice";
import { useAppDispatch } from "../../../../store";
interface IEditTenantLogsProps {
tenant: ITenant;
classes: any;
open: boolean;
onClose: (shouldReload: boolean) => void;
image: string;
labels: IKeyValue[];
annotations: IKeyValue[];
nodeSelector: IKeyValue[];
diskCapacityGB: number;
serviceAccountName: string;
dbImage: string;
dbInitImage: string;
dbLabels: IKeyValue[];
dbAnnotations: IKeyValue[];
dbNodeSelector: IKeyValue[];
dbServiceAccountName: string;
cpuRequest: string;
memRequest: string;
dbCPURequest: string;
dbMemRequest: string;
}
const styles = (theme: Theme) =>
createStyles({
buttonContainer: {
textAlign: "right",
},
bottomContainer: {
display: "flex",
flexGrow: 1,
alignItems: "center",
"& div": {
flexGrow: 1,
width: "100%",
},
},
...modalBasic,
...modalStyleUtils,
...formFieldStyles,
});
const EditTenantLogsModal = ({
tenant,
classes,
open,
onClose,
image,
labels,
annotations,
nodeSelector,
diskCapacityGB,
serviceAccountName,
dbLabels,
dbAnnotations,
dbNodeSelector,
dbImage,
dbInitImage,
dbServiceAccountName,
cpuRequest,
memRequest,
dbCPURequest,
dbMemRequest,
}: IEditTenantLogsProps) => {
const dispatch = useAppDispatch();
const [validationErrors, setValidationErrors] = useState<any>({});
const [newLabels, setNewLabels] = useState<IKeyValue[]>(
labels.length > 0 ? [...labels] : [{ key: "", value: "" }]
);
const [newAnnotations, setNewAnnotations] = useState<IKeyValue[]>(
annotations.length > 0 ? [...annotations] : [{ key: "", value: "" }]
);
const [newNodeSelector, setNewNodeSelector] = useState<IKeyValue[]>(
nodeSelector.length > 0 ? [...nodeSelector] : [{ key: "", value: "" }]
);
const [newImage, setNewImage] = useState<string>(image);
const [newDiskCapacityGB, setNewDiskCapacityGB] =
useState<number>(diskCapacityGB);
const [newServiceAccountName, setNewServiceAccountName] = useState<string>(
serviceAccountName != null ? serviceAccountName : ""
);
const [newDbLabels, setNewDbLabels] = useState<IKeyValue[]>(
dbLabels.length > 0 ? [...dbLabels] : [{ key: "", value: "" }]
);
const [newDbAnnotations, setNewDbAnnotations] = useState<IKeyValue[]>(
dbAnnotations.length > 0 ? [...dbAnnotations] : [{ key: "", value: "" }]
);
const [newDbNodeSelector, setNewDbNodeSelector] = useState<IKeyValue[]>(
dbNodeSelector.length > 0 ? [...dbNodeSelector] : [{ key: "", value: "" }]
);
const [newDbImage, setNewDbImage] = useState<string>(dbImage);
const [newDbInitImage, setNewDbInitImage] = useState<string>(dbInitImage);
const [newDbServiceAccountName, setNewDbServiceAccountName] =
useState<string>(dbServiceAccountName != null ? dbServiceAccountName : "");
const [labelsError, setLabelsError] = useState<any>({});
const [annotationsError, setAnnotationsError] = useState<any>({});
const [nodeSelectorError, setNodeSelectorError] = useState<any>({});
const [dbLabelsError, setDbLabelsError] = useState<any>({});
const [dbAnnotationsError, setDbAnnotationsError] = useState<any>({});
const [dbNodeSelectorError, setDbNodeSelectorError] = useState<any>({});
const [newCPURequest, setNewCPURequest] = useState<string>(cpuRequest);
const [newMemRequest, setNewMemRequest] = useState<string>(
memRequest
? Math.floor(parseInt(memRequest, 10) / 1000000000).toString()
: "0"
);
const [newDBCPURequest, setNewDBCPURequest] = useState<string>(dbCPURequest);
const [newDBMemRequest, setNewDBMemRequest] = useState<string>(
dbMemRequest
? Math.floor(parseInt(dbMemRequest, 10) / 1000000000).toString()
: "0"
);
const trim = (x: IKeyValue[]): IKeyValue[] => {
let retval: IKeyValue[] = [];
for (let i = 0; i < x.length; i++) {
if (x[i].key !== "") {
retval.push(x[i]);
}
}
return retval;
};
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
useEffect(() => {
let tenantLogValidation: IValidation[] = [];
tenantLogValidation.push({
fieldKey: `image`,
required: false,
value: newImage,
pattern:
/^([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9]?)+(\/(([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9])?)+)*:([a-zA-Z0-9])[a-zA-Z0-9-.]{0,127}$/,
customPatternMessage: "Invalid image",
});
tenantLogValidation.push({
fieldKey: `dbImage`,
required: false,
value: newDbImage,
pattern:
/^([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9]?)+(\/(([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9])?)+)*:([a-zA-Z0-9])[a-zA-Z0-9-.]{0,127}$/,
customPatternMessage: "Invalid image",
});
tenantLogValidation.push({
fieldKey: `dbInitImage`,
required: false,
value: newDbInitImage,
pattern:
/^([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9]?)+(\/(([a-zA-Z0-9])([a-zA-Z0-9-._])*([a-zA-Z0-9])?)+)*:([a-zA-Z0-9])[a-zA-Z0-9-.]{0,127}$/,
customPatternMessage: "Invalid image",
});
tenantLogValidation.push({
fieldKey: `diskCapacityGB`,
required: true,
value: newDiskCapacityGB as any as string,
pattern: /^[0-9]*$/,
customPatternMessage: "Must be an integer between 0 and 10",
});
tenantLogValidation.push({
fieldKey: `serviceAccountName`,
required: false,
value: newServiceAccountName,
pattern: /^[a-zA-Z0-9-.]{1,253}$/,
customPatternMessage: "Invalid service account name",
});
tenantLogValidation.push({
fieldKey: `dbServiceAccountName`,
required: false,
value: newDbServiceAccountName,
pattern: /^[a-zA-Z0-9-.]{1,253}$/,
customPatternMessage: "Invalid service account name",
});
tenantLogValidation.push({
fieldKey: `cpuRequest`,
required: true,
value: newCPURequest as any as string,
pattern: /^[0-9]*$/,
customPatternMessage:
"Please enter an integer value for number of CPUs requested",
});
tenantLogValidation.push({
fieldKey: `memRequest`,
required: true,
value: newMemRequest as any as string,
pattern: /^[0-9]*$/,
customPatternMessage:
"Please enter an integer value (Gi) for memory requested",
});
tenantLogValidation.push({
fieldKey: `dbCPURequest`,
required: true,
value: newDBCPURequest as any as string,
pattern: /^[0-9]*$/,
customPatternMessage:
"Please enter an integer value for number of DB CPUs requested",
});
tenantLogValidation.push({
fieldKey: `dbMemRequest`,
required: true,
value: newDBMemRequest as any as string,
pattern: /^[0-9]*$/,
customPatternMessage:
"Please enter an integer value (Gi) for DB memory requested",
});
const commonVal = commonFormValidation(tenantLogValidation);
setValidationErrors(commonVal);
}, [
newImage,
newDbImage,
newDbInitImage,
newDiskCapacityGB,
newServiceAccountName,
newDbServiceAccountName,
newCPURequest,
newMemRequest,
newDBCPURequest,
newDBMemRequest,
setValidationErrors,
]);
const checkValid = (): boolean => {
if (
Object.keys(validationErrors).length !== 0 ||
Object.keys(labelsError).length !== 0 ||
Object.keys(annotationsError).length !== 0 ||
Object.keys(nodeSelectorError).length !== 0 ||
Object.keys(dbLabelsError).length !== 0 ||
Object.keys(dbAnnotationsError).length !== 0 ||
Object.keys(dbNodeSelectorError).length !== 0
) {
return false;
} else {
return true;
}
};
return (
<ModalWrapper
onClose={() => onClose(true)}
modalOpen={open}
title="Edit Logging"
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!checkValid()) {
dispatch(
setModalErrorSnackMessage({
errorMessage: "Some fields have invalid values",
detailedError: "",
})
);
} else {
api
.invoke(
"PUT",
`/api/v1/namespaces/${tenant.namespace}/tenants/${tenant.name}/log`,
{
labels: trim(newLabels),
annotations: trim(newAnnotations),
nodeSelector: trim(newNodeSelector),
image: newImage,
diskCapacityGB: newDiskCapacityGB,
serviceAccountName: newServiceAccountName,
dbLabels: trim(newDbLabels),
dbAnnotations: trim(newDbAnnotations),
dbNodeSelector: trim(newDbNodeSelector),
dbImage: newDbImage,
dbInitImage: newDbInitImage,
dbServiceAccountName: newDbServiceAccountName,
logCPURequest: newCPURequest,
logMemRequest: newMemRequest + "Gi",
logDBCPURequest: newDBCPURequest,
logDBMemRequest: newDBMemRequest + "Gi",
}
)
.then(() => {
onClose(true);
})
.catch((err: ErrorResponseHandler) => {});
}
}}
>
<Grid container>
<Grid xs={12} className={classes.modalFormScrollable}>
<Grid item xs={12} className={classes.formFieldRow}>
<h4>Logging API </h4>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`image`}
label={"Image"}
placeholder={"minio/operator:v4.4.22"}
name={`image`}
value={newImage}
onChange={(e) => {
setNewImage(e.target.value);
cleanValidation(`image`);
}}
key={`image`}
error={validationErrors[`image`] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`diskCapacityGB`}
label={"Disk Capacity"}
placeholder={"Disk Capacity"}
name={`diskCapacityGB`}
value={newDiskCapacityGB as any as string}
onChange={(e) => {
setNewDiskCapacityGB(e.target.value as any as number);
cleanValidation(`diskCapacityGB`);
}}
key={`diskCapacityGB`}
error={validationErrors[`diskCapacityGB`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`serviceAccountName`}
label={"Service Account"}
placeholder={"Service Account Name"}
name={`serviceAccountName`}
value={newServiceAccountName}
onChange={(e) => {
setNewServiceAccountName(e.target.value);
cleanValidation(`serviceAccountName`);
}}
key={`serviceAccountName`}
error={validationErrors[`serviceAccountName`] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`cpuRequest`}
label={"CPU Request"}
placeholder={"CPU Request"}
name={`cpuRequest`}
value={newCPURequest as any as string}
onChange={(e) => {
setNewCPURequest(e.target.value as any as string);
cleanValidation(`cpuRequest`);
}}
key={`cpuRequest`}
error={validationErrors[`cpuRequest`] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`memRequest`}
label={"Memory request"}
placeholder={"Memory request"}
name={`memRequest`}
value={newMemRequest}
onChange={(e) => {
setNewMemRequest(e.target.value as any as string);
cleanValidation(`memRequest`);
}}
key={`memRequest`}
error={validationErrors[`memRequest`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Labels</span>
<KeyPairEdit
newValues={newLabels}
setNewValues={setNewLabels}
paramName={"Labels"}
error={labelsError}
setError={setLabelsError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Annotations</span>
<KeyPairEdit
newValues={newAnnotations}
setNewValues={setNewAnnotations}
paramName={"Annotations"}
error={annotationsError}
setError={setAnnotationsError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Node Selector</span>
<KeyPairEdit
newValues={newNodeSelector}
setNewValues={setNewNodeSelector}
paramName={"Node Selector"}
error={nodeSelectorError}
setError={setNodeSelectorError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<h4>Database Configuration </h4>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`dbImage`}
label={"Postgres Image"}
placeholder={"library/postgres:13"}
name={`dbImage`}
value={newDbImage}
onChange={(e) => {
setNewDbImage(e.target.value);
cleanValidation(`dbImage`);
}}
key={`dbImage`}
error={validationErrors[`dbImage`] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`dbInitImage`}
label={"Postgres Init Image"}
placeholder={"library/busybox:1.33.1"}
name={`dbInitImage`}
value={newDbInitImage}
onChange={(e) => {
setNewDbInitImage(e.target.value);
cleanValidation(`dbInitImage`);
}}
key={`dbInitImage`}
error={validationErrors[`dbInitImage`] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`dbServiceAccountName`}
label={"Service Account"}
placeholder={"Db Service Account Name"}
name={`dbServiceAccountName`}
value={newDbServiceAccountName}
onChange={(e) => {
setNewDbServiceAccountName(e.target.value);
cleanValidation(`dbServiceAccountName`);
}}
key={`dbServiceAccountName`}
error={validationErrors[`dbServiceAccountName`] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`dbCpuRequest`}
label={"DB CPU Request"}
placeholder={"DB CPU Request"}
name={`dbCpuRequest`}
value={newDBCPURequest as any as string}
onChange={(e) => {
setNewDBCPURequest(e.target.value as any as string);
cleanValidation(`dbCpuRequest`);
}}
key={`dbCpuRequest`}
error={validationErrors[`dbCpuRequest`] || ""}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<InputBoxWrapper
id={`dbMemRequest`}
label={"DB Memory request"}
placeholder={"DB Memory request"}
name={`dbMemRequest`}
value={newDBMemRequest}
onChange={(e) => {
setNewDBMemRequest(e.target.value as any as string);
cleanValidation(`dbMemRequest`);
}}
key={`dbMemRequest`}
error={validationErrors[`dbMemRequest`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Labels</span>
<KeyPairEdit
newValues={newDbLabels}
setNewValues={setNewDbLabels}
paramName={"Db Labels"}
error={dbLabelsError}
setError={setDbLabelsError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Annotations</span>
<KeyPairEdit
newValues={newDbAnnotations}
setNewValues={setNewDbAnnotations}
paramName={"Db Annotations"}
error={dbAnnotationsError}
setError={setDbAnnotationsError}
/>
</Grid>
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Node Selector</span>
<KeyPairEdit
newValues={newDbNodeSelector}
setNewValues={setNewDbNodeSelector}
paramName={"DbNode Selector"}
error={dbNodeSelectorError}
setError={setDbNodeSelectorError}
/>
</Grid>
</Grid>
<Grid xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={!checkValid()}
>
Save
</Button>
</Grid>
</Grid>
</form>
</ModalWrapper>
);
};
export default withStyles(styles)(EditTenantLogsModal);

View File

@@ -0,0 +1,655 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 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 { ISecurityContext} from "../types";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
containerForHeader,
createTenantCommon,
formFieldStyles,
modalBasic,
spacingUtils,
tenantDetailsStyles,
wizardCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import React, { Fragment, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { AppState, useAppDispatch } from "../../../../store";
import api from "../../../../common/api";
import { ErrorResponseHandler } from "../../../../common/types";
import { useParams } from "react-router-dom";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import Grid from "@mui/material/Grid";
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import { Button, DialogContentText } from "@mui/material";
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
import {
setErrorSnackMessage,
setSnackBarMessage,
} from "../../../../systemSlice";
import { IKeyValue } from "../ListTenants/types";
import KeyPairEdit from "./KeyPairEdit";
import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import { ITenantLogsStruct } from "../ListTenants/types";
import {
setAuditLoggingEnabled,
setImage,
setDBImage,
setDBInitImage,
setDiskCapacityGB,
setServiceAccountName,
setDBServiceAccountName,
setCPURequest,
setMemRequest,
setDBCPURequest,
setDBMemRequest,
} from "../TenantDetails/tenantAuditLogSlice";
import { clearValidationError } from "../utils";
interface ITenantAuditLogs {
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
...tenantDetailsStyles,
...spacingUtils,
bold: { fontWeight: "bold" },
italic: { fontStyle: "italic" },
fileItem: {
marginRight: 10,
display: "flex",
"& div label": {
minWidth: 50,
},
"@media (max-width: 900px)": {
flexFlow: "column",
},
},
...containerForHeader(theme.spacing(4)),
...createTenantCommon,
...formFieldStyles,
...modalBasic,
...wizardCommon,
});
const TenantAuditLogging = ({ classes }: ITenantAuditLogs) => {
const dispatch = useAppDispatch();
const { tenantName, tenantNamespace } = useParams();
const auditLoggingEnabled = useSelector(
(state: AppState) => state.editTenantLogging.auditLoggingEnabled
);
const image = useSelector(
(state: AppState) => state.editTenantLogging.image
);
const dbImage = useSelector(
(state: AppState) => state.editTenantLogging.dbImage
);
const dbInitImage = useSelector(
(state: AppState) => state.editTenantLogging.dbInitImage
);
const diskCapacityGB = useSelector(
(state: AppState) => state.editTenantLogging.diskCapacityGB
);
const cpuRequest = useSelector(
(state: AppState) => state.editTenantLogging.cpuRequest
);
const memRequest = useSelector(
(state: AppState) => state.editTenantLogging.memRequest
);
const dbCpuRequest = useSelector(
(state: AppState) => state.editTenantLogging.dbCPURequest
);
const dbMemRequest = useSelector(
(state: AppState) => state.editTenantLogging.dbMemRequest
);
const serviceAccountName = useSelector(
(state: AppState) => state.editTenantLogging.serviceAccountName
);
const dbServiceAccountName = useSelector(
(state: AppState) => state.editTenantLogging.dbServiceAccountName
);
const [validationErrors, setValidationErrors] = useState<any>({});
const [toggleConfirmOpen, setToggleConfirmOpen] = useState<boolean>(false);
const [labels, setLabels] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const [annotations, setAnnotations] = useState<IKeyValue[]>([
{ key: "", value: "" },
]);
const [nodeSelector, setNodeSelector] = useState<IKeyValue[]>([
{ key: "", value: "" },
]);
const [dbLabels, setDBLabels] = useState<IKeyValue[]>([{ key: "", value: "" }]);
const [dbAnnotations, setDBAnnotations] = useState<IKeyValue[]>([
{ key: "", value: "" },
]);
const [dbNodeSelector, setDBNodeSelector] = useState<IKeyValue[]>([
{ key: "", value: "" },
]);
const [refreshLoggingInfo, setRefreshLoggingInfo] =
useState<boolean>(true);
const [labelsError, setLabelsError] = useState<any>({});
const [annotationsError, setAnnotationsError] = useState<any>({});
const [nodeSelectorError, setNodeSelectorError] = useState<any>({});
const [dbLabelsError, setDBLabelsError] = useState<any>({});
const [dbAnnotationsError, setDBAnnotationsError] = useState<any>({});
const [dbNodeSelectorError, setDBNodeSelectorError] = useState<any>({});
const cleanValidation = (fieldName: string) => {
setValidationErrors(clearValidationError(validationErrors, fieldName));
};
const setLoggingInfo = (res: ITenantLogsStruct) => {
dispatch(setAuditLoggingEnabled(!res.disabled))
dispatch(setImage(res.image));
dispatch(setServiceAccountName(res.serviceAccountName));
dispatch(setDBServiceAccountName(res.dbServiceAccountName));
dispatch(setDBImage(res.dbImage));
dispatch(setDBInitImage(res.dbInitImage));
dispatch(setCPURequest(res.logCPURequest));
dispatch(setDBCPURequest(res.logDBCPURequest));
if (res.logMemRequest) {
dispatch(
setMemRequest(
Math.floor(
parseInt(res.logMemRequest, 10) / 1000000000
).toString()
)
);
} else {
dispatch(setMemRequest("0"));
}
if (res.logDBMemRequest) {
dispatch(
setDBMemRequest(
Math.floor(
parseInt(res.logDBMemRequest, 10) / 1000000000
).toString()
)
);
} else {
dispatch(setDBMemRequest("0"));
}
dispatch(setDiskCapacityGB(res.diskCapacityGB));
res.labels.length > 0 ? setLabels(res.labels) : setLabels([{ key: "test", value: "test" }]);
res.annotations.length > 0 ? setAnnotations(res.annotations) : setAnnotations([{ key: "", value: "" }]);
res.nodeSelector.length > 0 ? setNodeSelector(res.nodeSelector) : setNodeSelector([{ key: "", value: "" }]);
res.dbLabels.length > 0 ? setDBLabels(res.dbLabels) : setDBLabels([{ key: "", value: "" }]);
res.dbAnnotations.length > 0 ? setDBAnnotations(res.dbAnnotations) : setDBAnnotations([{ key: "", value: "" }]);
res.dbNodeSelector.length > 0 ? setDBNodeSelector(res.dbNodeSelector) : setDBNodeSelector([{ key: "", value: "" }]);
};
const trim = (x: IKeyValue[]): IKeyValue[] => {
let retval: IKeyValue[] = [];
for (let i = 0; i < x.length; i++) {
if (x[i].key !== "") {
retval.push(x[i]);
}
}
return retval;
};
const checkValid = (): boolean => {
if (
Object.keys(validationErrors).length !== 0 ||
Object.keys(labelsError).length !== 0 ||
Object.keys(annotationsError).length !== 0 ||
Object.keys(nodeSelectorError).length !== 0 ||
Object.keys(dbNodeSelectorError).length !== 0 ||
Object.keys(dbAnnotationsError).length !== 0 ||
Object.keys(dbLabelsError).length !== 0
) {
let err: ErrorResponseHandler = {
errorMessage: "Invalid entry",
detailedError: "",
};
dispatch(setErrorSnackMessage(err));
return false;
} else {
return true;
}
};
useEffect(() => {
if (refreshLoggingInfo) {
api
.invoke(
"GET",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/log`
)
.then((res: ITenantLogsStruct) => {
dispatch(setAuditLoggingEnabled(res.auditLoggingEnabled));
setLoggingInfo(res);
setRefreshLoggingInfo(false);
})
.catch((err: ErrorResponseHandler) => {
dispatch(setErrorSnackMessage(err));
setRefreshLoggingInfo(false);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [refreshLoggingInfo]);
const submitLoggingInfo = () => {
if (checkValid()) {
api
.invoke(
"PUT",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/log`,
{
labels: trim(labels),
annotations: trim(annotations),
nodeSelector: trim(nodeSelector),
image: image,
diskCapacityGB: diskCapacityGB.toString(),
serviceAccountName: serviceAccountName,
dbLabels: trim(dbLabels),
dbAnnotations: trim(dbAnnotations),
dbNodeSelector: trim(dbNodeSelector),
dbImage: dbImage,
dbInitImage: dbInitImage,
dbServiceAccountName: dbServiceAccountName,
logCPURequest: cpuRequest,
logMemRequest: memRequest + "Gi",
logDBCPURequest: dbCpuRequest,
logDBMemRequest: dbMemRequest + "Gi",
}
)
.then(() => {
setRefreshLoggingInfo(true);
dispatch(setSnackBarMessage(`Audit Log configuration updated.`));
})
.catch((err: ErrorResponseHandler) => {
setErrorSnackMessage(err);
});
}
};
const toggleLogging = () => {
if(!auditLoggingEnabled) {
api
.invoke(
"POST",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/enable-logging`
)
.then(() => {
setRefreshLoggingInfo(true);
setToggleConfirmOpen(false);
})
.catch((err: ErrorResponseHandler) => {
dispatch(
setErrorSnackMessage({
errorMessage: "Error enabling logging",
detailedError: err.detailedError,
})
);
});
} else {
api
.invoke(
"POST",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/disable-logging`
)
.then(() => {
setRefreshLoggingInfo(true);
setToggleConfirmOpen(false);
})
.catch((err: ErrorResponseHandler) => {
dispatch(
setErrorSnackMessage({
errorMessage: "Error disabling logging",
detailedError: err.detailedError,
})
);
});
};
};
return (
<Fragment>
{toggleConfirmOpen && (
<ConfirmDialog
isOpen={toggleConfirmOpen}
title={
!auditLoggingEnabled
? "Enable Audit Logging for this tenant?"
: "Disable Audit Logging for this tenant?"
}
confirmText={!auditLoggingEnabled ? "Enable" : "Disable"}
cancelText="Cancel"
onClose={() => setToggleConfirmOpen(false)}
onConfirm={toggleLogging}
confirmationContent={
<DialogContentText>
{!auditLoggingEnabled
? "A small Postgres server will be started per the configuration provided, which will collect the audit logs for your tenant."
: " Current configuration will be lost, and defaults reset if reenabled."}
</DialogContentText>
}
/>
)}
<Grid container spacing={1}>
<Grid item xs>
<h1 className={classes.sectionTitle}>Audit Logs</h1>
</Grid>
<Grid item xs={7} justifyContent={"end"} textAlign={"right"}>
<FormSwitchWrapper
label={""}
indicatorLabels={["Enabled", "Disabled"]}
checked={auditLoggingEnabled}
value={"tenant_logging"}
id="tenant_logging"
name="tenant_logging"
onChange={() => {
setToggleConfirmOpen(true);
}}
description=""
/>
</Grid>
<Grid xs={12}>
<hr className={classes.hrClass} />
</Grid>
</Grid>
{auditLoggingEnabled && (
<Fragment>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`image`}
label={"Image"}
placeholder={"minio/operator:v4.4.22"}
name={`image`}
value={image}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setImage(event.target.value));
}
cleanValidation(`image`);
}}
key={`image`}
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
error={validationErrors[`image`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbImage`}
label={"DB Postgres Image"}
placeholder={"library/postgres:13"}
name={`dbImage`}
value={dbImage}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBImage(event.target.value));
}
cleanValidation(`dbImage`);
}}
key={`dbImage`}
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
error={validationErrors[`dbImage`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbInitImage`}
label={"DB Init Image"}
placeholder={"library/busybox:1.33.1"}
name={`dbInitImage`}
value={dbInitImage}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBInitImage(event.target.value));
}
cleanValidation(`dbInitImage`);
}}
key={`dbInitImage`}
pattern={"^[a-zA-Z0-9-./:]{1,253}$"}
error={validationErrors[`dbInitImage`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`diskCapacityGB`}
label={"Disk Capacity"}
placeholder={"Disk Capacity"}
name={`diskCapacityGB`}
value={!isNaN(diskCapacityGB) ? diskCapacityGB.toString() : "0"}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDiskCapacityGB(parseInt(event.target.value)));
}
cleanValidation(`diskCapacityGB`);
}}
key={`diskCapacityGB`}
pattern={"[0-9]*"}
error={validationErrors[`diskCapacityGB`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`cpuRequest`}
label={"CPU Request"}
placeholder={"CPU Request"}
name={`cpuRequest`}
value={cpuRequest}
pattern={"[0-9]*"}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setCPURequest(event.target.value));
}
cleanValidation(`cpuRequest`);
}}
key={`cpuRequest`}
error={validationErrors[`cpuRequest`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbCPURequest`}
label={"DB CPU Request"}
placeholder={"DB CPU Request"}
name={`dbCPURequest`}
value={dbCpuRequest}
pattern={"[0-9]*"}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBCPURequest(event.target.value));
}
cleanValidation(`dbCPURequest`);
}}
key={`dbCPURequest`}
error={validationErrors[`dbCPURequest`] || ""}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`memRequest`}
label={"Memory Request"}
placeholder={"Memory request"}
name={`memRequest`}
value={memRequest}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setMemRequest(event.target.value));
}
cleanValidation(`memRequest`);
}}
pattern={"[0-9]*"}
key={`memRequest`}
error={validationErrors[`memRequest`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`dbMemRequest`}
label={"DB Memory Request"}
placeholder={"DB Memory request"}
name={`dbMemRequest`}
value={dbMemRequest}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setDBMemRequest(event.target.value));
}
cleanValidation(`dbMemRequest`);
}}
pattern={"[0-9]*"}
key={`dbMemRequest`}
error={validationErrors[`dbMemRequest`] || ""}
overlayObject={
<InputUnitMenu
id={"size-unit"}
onUnitChange={() => {}}
unitSelected={"Gi"}
unitsList={[{ label: "Gi", value: "Gi" }]}
disabled={true}
/>
}
/>
</Grid>
<Grid item xs={12} paddingBottom={2}>
<InputBoxWrapper
id={`serviceAccountName`}
label={"Service Account"}
placeholder={"Service Account Name"}
name={`serviceAccountName`}
value={serviceAccountName}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.validity.valid) {
dispatch(setServiceAccountName(event.target.value));
}
cleanValidation(`serviceAccountName`);
}}
key={`serviceAccountName`}
pattern={"^[a-zA-Z0-9-.]{1,253}$"}
error={validationErrors[`serviceAccountName`] || ""}
/>
</Grid>
{labels !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Labels</span>
<KeyPairEdit
newValues={labels}
setNewValues={setLabels}
paramName={"Labels"}
error={labelsError}
setError={setLabelsError}
/>
</Grid>
)}
{annotations !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Annotations</span>
<KeyPairEdit
newValues={annotations}
setNewValues={setAnnotations}
paramName={"Annotations"}
error={annotationsError}
setError={setAnnotationsError}
/>
</Grid>
)}
{nodeSelector !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>Node Selector</span>
<KeyPairEdit
newValues={nodeSelector}
setNewValues={setNodeSelector}
paramName={"Node Selector"}
error={nodeSelectorError}
setError={setNodeSelectorError}
/>
</Grid>
)}
{dbLabels !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>DB Labels</span>
<KeyPairEdit
newValues={dbLabels}
setNewValues={setDBLabels}
paramName={"dbLabels"}
error={dbLabelsError}
setError={setDBLabelsError}
/>
</Grid>
)}
{dbAnnotations !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>DB Annotations</span>
<KeyPairEdit
newValues={dbAnnotations}
setNewValues={setDBAnnotations}
paramName={"dbAnnotations"}
error={dbAnnotationsError}
setError={setDBAnnotationsError}
/>
</Grid>
)}
{dbNodeSelector !== null && (
<Grid item xs={12} className={classes.formFieldRow}>
<span className={classes.inputLabel}>DB Node Selector</span>
<KeyPairEdit
newValues={dbNodeSelector}
setNewValues={setDBNodeSelector}
paramName={"DB Node Selector"}
error={dbNodeSelectorError}
setError={setDBNodeSelectorError}
/>
</Grid>
)}
<Grid item xs={12} textAlign={"right"}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={!checkValid()}
onClick={() => submitLoggingInfo()}
>
Save
</Button>
</Grid>
</Fragment>
)}
</Fragment>
);
};
export default withStyles(styles)(TenantAuditLogging);

View File

@@ -59,7 +59,7 @@ const TenantSummary = withSuspense(React.lazy(() => import("./TenantSummary")));
const TenantLicense = withSuspense(React.lazy(() => import("./TenantLicense")));
const PoolsSummary = withSuspense(React.lazy(() => import("./PoolsSummary")));
const PodsSummary = withSuspense(React.lazy(() => import("./PodsSummary")));
const TenantLogging = withSuspense(React.lazy(() => import("./TenantLogging")));
const TenantLogging = withSuspense(React.lazy(() => import("./TenantAuditLogsScreen")));
const TenantEvents = withSuspense(React.lazy(() => import("./TenantEvents")));
const TenantCSR = withSuspense(React.lazy(() => import("./TenantCSR")));
const VolumesSummary = withSuspense(

View File

@@ -1,500 +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, useEffect, useState } from "react";
import { connect, useSelector } from "react-redux";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Theme } from "@mui/material/styles";
import {
actionsTray,
containerForHeader,
searchField,
tenantDetailsStyles,
} from "../../Common/FormComponents/common/styleLibrary";
import Grid from "@mui/material/Grid";
import { DialogContentText } from "@mui/material";
import Paper from "@mui/material/Paper";
import api from "../../../../common/api";
import { ITenantLogsStruct } from "../ListTenants/types";
import { AppState, useAppDispatch } from "../../../../store";
import { ErrorResponseHandler } from "../../../../common/types";
import { EditIcon } from "../../../../icons";
import EditTenantLogsModal from "./EditTenantLogsModal";
import KeyPairView from "./KeyPairView";
import ConfirmDialog from "../../Common/ModalWrapper/ConfirmDialog";
import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import RBIconButton from "../../Buckets/BucketDetails/SummaryItems/RBIconButton";
import { niceBytes } from "../../../../common/utils";
import Loader from "../../Common/Loader/Loader";
import { setErrorSnackMessage } from "../../../../systemSlice";
import { useParams } from "react-router-dom";
interface ITenantLogs {
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
...tenantDetailsStyles,
paperContainer: {
padding: "15px 15px 15px 50px",
},
...actionsTray,
...searchField,
...containerForHeader(theme.spacing(4)),
});
const TenantLogging = ({ classes }: ITenantLogs) => {
const dispatch = useAppDispatch();
const params = useParams();
const loadingTenant = useSelector(
(state: AppState) => state.tenants.loadingTenant
);
const tenant = useSelector((state: AppState) => state.tenants.tenantInfo);
const [loadingTenantLogs, setLoadingTenantLogs] = useState<boolean>(true);
const [logInfo, setLogInfo] = useState<ITenantLogsStruct>();
const [edit, setEdit] = useState<boolean>(false);
const [disabled, setDisabled] = useState<boolean>(false);
const [preDisabled, setPreDisabled] = useState<boolean>(false);
const [disableDialogOpen, setDisableDialogOpen] = useState<boolean>(false);
const [enableDialogOpen, setEnableDialogOpen] = useState<boolean>(false);
const tenantName = params.tenantName;
const tenantNamespace = params.tenantNamespace;
useEffect(() => {
if (loadingTenantLogs) {
api
.invoke(
"GET",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/log`
)
.then((result: ITenantLogsStruct) => {
setLogInfo(result);
setPreDisabled(result.disabled);
setDisabled(result.disabled);
setLoadingTenantLogs(false);
})
.catch((err: ErrorResponseHandler) => {
dispatch(
setErrorSnackMessage({
errorMessage: "Error getting tenant logs",
detailedError: err.detailedError,
})
);
});
}
}, [
tenantName,
tenantNamespace,
loadingTenantLogs,
setDisabled,
disabled,
dispatch,
]);
const onCloseEditAndRefresh = () => {
setDisableDialogOpen(false);
setEdit(false);
setLoadingTenantLogs(true);
};
const onCloseEnableAndRefresh = () => {
setEnableDialogOpen(false);
setDisabled(false);
setLoadingTenantLogs(true);
};
return (
<Fragment>
<ConfirmDialog
title="Disable Logging?"
confirmText="Disable"
isOpen={disableDialogOpen}
onConfirm={() => {
api
.invoke(
"POST",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/disable-logging`
)
.then(() => {
setPreDisabled(true);
setDisabled(true);
})
.catch((err: ErrorResponseHandler) => {
dispatch(
setErrorSnackMessage({
errorMessage: "Error disabling logging",
detailedError: err.detailedError,
})
);
});
onCloseEditAndRefresh();
}}
onClose={() => setDisableDialogOpen(false)}
confirmationContent={
<DialogContentText>
Disabling logging will erase any custom values you have used to
configure logging
</DialogContentText>
}
/>
<ConfirmDialog
title="Enable Logging?"
confirmText="Enable"
isOpen={enableDialogOpen}
onConfirm={() => {
api
.invoke(
"POST",
`/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/enable-logging`
)
.then(() => {
setPreDisabled(false);
})
.catch((err: ErrorResponseHandler) => {
dispatch(
setErrorSnackMessage({
errorMessage: "Error enabling logging",
detailedError: err.detailedError,
})
);
});
onCloseEnableAndRefresh();
}}
onClose={() => setEnableDialogOpen(false)}
confirmationContent={
<DialogContentText>
Logging will be enabled with default values
</DialogContentText>
}
/>
{edit && tenant !== null && logInfo != null && !disabled && (
<EditTenantLogsModal
open={edit}
onClose={onCloseEditAndRefresh}
tenant={tenant}
image={logInfo.image}
labels={logInfo.labels}
annotations={logInfo.annotations}
nodeSelector={logInfo.nodeSelector}
diskCapacityGB={logInfo.diskCapacityGB}
serviceAccountName={logInfo.serviceAccountName}
dbImage={logInfo.dbImage}
dbInitImage={logInfo.dbInitImage}
dbLabels={logInfo.dbLabels}
dbAnnotations={logInfo.dbAnnotations}
dbNodeSelector={logInfo.dbNodeSelector}
dbServiceAccountName={logInfo.dbServiceAccountName}
cpuRequest={logInfo.logCPURequest}
memRequest={logInfo.logMemRequest}
dbCPURequest={logInfo.logDBCPURequest}
dbMemRequest={logInfo.logDBMemRequest}
/>
)}
<Grid container alignItems={"center"}>
<Grid item xs>
<h1 className={classes.sectionTitle}>Audit Log</h1>
</Grid>
<Grid item xs={4}>
<FormSwitchWrapper
value="enableLogging"
id="enableLogging"
name="enableLogging"
checked={!preDisabled}
onChange={(e) => {
const targetD = e.target;
const checked = targetD.checked;
if (checked) {
setEnableDialogOpen(true);
} else {
setDisableDialogOpen(true);
}
}}
indicatorLabels={["Enabled", "Disabled"]}
/>
</Grid>
</Grid>
{!disabled && !loadingTenantLogs && (
<Paper className={classes.paperContainer}>
<Grid container>
<Grid item xs={12}>
<Grid container alignItems={"center"}>
<Grid xs={8}>
<h3>Configuration</h3>
</Grid>
<Grid xs={4} justifyContent={"end"} textAlign={"right"}>
<RBIconButton
tooltip={"Edit Logging configuration"}
text={"Edit"}
onClick={() => {
setEdit(true);
}}
icon={<EditIcon />}
color="primary"
variant={"contained"}
/>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<hr className={classes.hrClass} />
<table width={"100%"}>
<tbody>
{loadingTenant ? (
<tr>
<td className={classes.centerAlign} colSpan={4}>
<Loader />
</td>
</tr>
) : (
<Fragment>
{logInfo?.logCPURequest != null && (
<tr>
<td className={classes.titleCol}>CPU Request:</td>
<td>{logInfo?.logCPURequest}</td>
</tr>
)}
{logInfo?.logMemRequest != null && (
<tr>
<td className={classes.titleCol}>Memory Request:</td>
<td>{niceBytes(logInfo?.logMemRequest, true)}</td>
</tr>
)}
{logInfo?.image != null && (
<tr>
<td className={classes.titleCol}>Image:</td>
<td>{logInfo?.image}</td>
</tr>
)}
{logInfo?.diskCapacityGB != null && (
<tr>
<td className={classes.titleCol}>
Disk Capacity (GB):
</td>
<td>{logInfo?.diskCapacityGB}</td>
</tr>
)}
{logInfo?.serviceAccountName != null && (
<tr>
<td className={classes.titleCol}>Service Account:</td>
<td>{logInfo?.serviceAccountName}</td>
</tr>
)}
{logInfo?.labels != null && logInfo.labels.length > 0 && (
<Fragment>
<tr>
<td>
<h4>Labels</h4>
</td>
</tr>
<tr>
<td>
<KeyPairView
records={
logInfo != null && logInfo.labels.length > 0
? logInfo.labels
: []
}
recordName="Labels"
/>
</td>
</tr>
</Fragment>
)}
{logInfo?.annotations != null &&
logInfo.annotations.length > 0 && (
<Fragment>
<tr>
<td>
<h4>Annotations</h4>
</td>
</tr>
<tr>
<td>
<KeyPairView
records={
logInfo != null &&
logInfo.annotations.length > 0
? logInfo.annotations
: []
}
recordName="Annotations"
/>
</td>
</tr>
</Fragment>
)}
{logInfo?.nodeSelector != null &&
logInfo.nodeSelector.length > 0 && (
<Fragment>
<tr>
<td>
<h4>Node Selector</h4>
</td>
</tr>
<tr>
<td>
<KeyPairView
records={
logInfo != null &&
logInfo.nodeSelector.length > 0
? logInfo.nodeSelector
: []
}
recordName="Node Selector"
/>
</td>
</tr>
</Fragment>
)}
</Fragment>
)}
</tbody>
</table>
<h2>Database Details</h2>
<hr className={classes.hrClass} />
<table width={"100%"}>
<tbody>
{loadingTenant ? (
<tr>
<td className={classes.centerAlign} colSpan={4}>
<Loader />
</td>
</tr>
) : (
<Fragment>
{logInfo?.logDBCPURequest != null && (
<tr>
<td className={classes.titleCol}>DB CPU Request:</td>
<td>{logInfo?.logDBCPURequest}</td>
</tr>
)}
{logInfo?.logDBMemRequest != null && (
<tr>
<td className={classes.titleCol}>
DB Memory Request:
</td>
<td>{niceBytes(logInfo?.logDBMemRequest, true)}</td>
</tr>
)}
{logInfo?.dbImage != null && (
<tr>
<td className={classes.titleCol}>Postgres Image:</td>
<td>{logInfo?.dbImage}</td>
</tr>
)}
{logInfo?.dbServiceAccountName != null && (
<tr>
<td className={classes.titleCol}>Service Account:</td>
<td>{logInfo?.dbServiceAccountName}</td>
</tr>
)}
{logInfo?.dbLabels != null &&
logInfo.dbLabels.length > 0 && (
<Fragment>
<tr>
<td>
<h4>Labels</h4>
</td>
</tr>
<tr>
<td>
<KeyPairView
records={
logInfo != null &&
logInfo.dbLabels?.length > 0
? logInfo.dbLabels
: []
}
recordName="labels"
/>
</td>
</tr>
</Fragment>
)}
{logInfo?.annotations != null &&
logInfo.dbAnnotations.length > 0 && (
<Fragment>
<tr>
<td>
<h4>Annotations</h4>
</td>
</tr>
<tr>
<td>
<KeyPairView
records={
logInfo != null &&
logInfo.dbAnnotations?.length > 0
? logInfo.dbAnnotations
: []
}
recordName="annotations"
/>
</td>
</tr>
</Fragment>
)}
{logInfo?.nodeSelector != null &&
logInfo.dbNodeSelector.length > 0 && (
<Fragment>
<tr>
<td>
<h4>Node Selector </h4>
</td>
</tr>
<tr>
<td>
<KeyPairView
records={
logInfo != null &&
logInfo.dbNodeSelector?.length > 0
? logInfo.dbNodeSelector
: []
}
recordName="node selectors"
/>
</td>
</tr>
</Fragment>
)}
</Fragment>
)}
</tbody>
</table>
</Grid>
</Grid>
</Paper>
)}
</Fragment>
);
};
const mapState = (state: AppState) => ({
loadingTenant: state.tenants.loadingTenant,
selectedTenant: state.tenants.currentTenant,
tenant: state.tenants.tenantInfo,
});
const connector = connect(mapState, null);
export default withStyles(styles)(connector(TenantLogging));

View File

@@ -0,0 +1,137 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 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 { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { IKeyValue } from "../ListTenants/types";
export interface IEditTenantAuditLogging {
auditLoggingEnabled: boolean;
image: string;
labels: IKeyValue[];
annotations: IKeyValue[];
nodeSelector: IKeyValue[];
diskCapacityGB: number;
serviceAccountName: string;
dbImage: string;
dbInitImage: string;
dbLabels: IKeyValue[];
dbAnnotations: IKeyValue[];
dbNodeSelector: IKeyValue[];
dbServiceAccountName: string;
cpuRequest: string;
memRequest: string;
dbCPURequest: string;
dbMemRequest: string;
}
const initialState: IEditTenantAuditLogging = {
auditLoggingEnabled: false,
image: "",
labels: [{ key: " ", value: " " }],
annotations: [{ key: " ", value: " " }],
nodeSelector: [{ key: " ", value: " " }],
diskCapacityGB: 0,
serviceAccountName: "",
dbCPURequest: "",
dbMemRequest: "",
dbImage: "",
dbInitImage: "",
dbLabels: [{ key: " ", value: " " }],
dbAnnotations: [{ key: " ", value: " " }],
dbNodeSelector: [{ key: " ", value: " " }],
dbServiceAccountName: "",
cpuRequest: "",
memRequest: "",
};
export const editTenantAuditLoggingSlice = createSlice({
name: "editTenantAuditLogging",
initialState,
reducers: {
setAuditLoggingEnabled: (state, action: PayloadAction<boolean>) => {
state.auditLoggingEnabled = action.payload;
},
setImage: (state, action: PayloadAction<string>) => {
state.image = action.payload;
},
setDBImage: (state, action: PayloadAction<string>) => {
state.dbImage = action.payload;
},
setDBInitImage: (state, action: PayloadAction<string>) => {
state.dbInitImage = action.payload;
},
setLabels: (state, action: PayloadAction<IKeyValue[]>) => {
state.labels = action.payload;
},
setAnnotations: (state, action: PayloadAction<IKeyValue[]>) => {
state.annotations = action.payload;
},
setNodeSelector: (state, action: PayloadAction<IKeyValue[]>) => {
state.nodeSelector = action.payload;
},
setDBLabels: (state, action: PayloadAction<IKeyValue[]>) => {
state.dbLabels = action.payload;
},
setDBAnnotations: (state, action: PayloadAction<IKeyValue[]>) => {
state.dbAnnotations = action.payload;
},
setDBNodeSelector: (state, action: PayloadAction<IKeyValue[]>) => {
state.dbNodeSelector = action.payload;
},
setDiskCapacityGB: (state, action: PayloadAction<number>) => {
state.diskCapacityGB = action.payload;
},
setServiceAccountName: (state, action: PayloadAction<string>) => {
state.serviceAccountName = action.payload;
},
setDBServiceAccountName: (state, action: PayloadAction<string>) => {
state.dbServiceAccountName = action.payload;
},
setCPURequest: (state, action: PayloadAction<string>) => {
state.cpuRequest = action.payload;
},
setMemRequest: (state, action: PayloadAction<string>) => {
state.memRequest = action.payload;
},
setDBCPURequest: (state, action: PayloadAction<string>) => {
state.dbCPURequest = action.payload;
},
setDBMemRequest: (state, action: PayloadAction<string>) => {
state.dbMemRequest = action.payload;
},
},
});
export const {
setAuditLoggingEnabled,
setImage,
setDBImage,
setDBInitImage,
setLabels,
setAnnotations,
setNodeSelector,
setDBLabels,
setDBAnnotations,
setDBNodeSelector,
setDiskCapacityGB,
setServiceAccountName,
setDBServiceAccountName,
setCPURequest,
setMemRequest,
setDBCPURequest,
setDBMemRequest,
} = editTenantAuditLoggingSlice.actions;
export default editTenantAuditLoggingSlice.reducer;

View File

@@ -32,6 +32,7 @@ import createUserReducer from "./screens/Console/Users/AddUsersSlice";
import addPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/AddPool/addPoolSlice";
import editPoolReducer from "./screens/Console/Tenants/TenantDetails/Pools/EditPool/editPoolSlice";
import editTenantMonitoringReducer from "./screens/Console/Tenants/TenantDetails/tenantMonitoringSlice";
import editTenantAuditLoggingReducer from "./screens/Console/Tenants/TenantDetails/tenantAuditLogSlice";
const rootReducer = combineReducers({
system: systemReducer,
@@ -51,6 +52,7 @@ const rootReducer = combineReducers({
addPool: addPoolReducer,
editPool: editPoolReducer,
editTenantMonitoring: editTenantMonitoringReducer,
editTenantLogging: editTenantAuditLoggingReducer,
});
export const store = configureStore({