Move trace and logs UI to Operator Console (#375)

Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
This commit is contained in:
Cesar N
2020-11-04 21:45:48 -08:00
committed by GitHub
parent 3cd024ea2c
commit 06f333395e
23 changed files with 256 additions and 222 deletions

2
go.mod
View File

@@ -19,7 +19,7 @@ require (
github.com/minio/mc v0.0.0-20201001165056-7f2df96e4821
github.com/minio/minio v0.0.0-20200927172404-27d9bd04e544
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c
github.com/minio/operator v0.0.0-20201022162018-527e5c32132b
github.com/mitchellh/go-homedir v1.1.0
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/secure-io/sio-go v0.3.1

11
go.sum
View File

@@ -60,10 +60,12 @@ github.com/Azure/azure-storage-blob-go v0.10.0/go.mod h1:ep1edmW+kNQx4UfWM9heESN
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v14.1.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
github.com/Azure/go-autorest/autorest v0.10.2 h1:NuSF3gXetiHyUbVdneJMEVyPUYAe5wh+aN08JYAf1tI=
github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
@@ -72,11 +74,13 @@ github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMl
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
github.com/Azure/go-autorest/autorest/adal v0.9.1 h1:xjPqigMQe2+0DAJ5A6MLUPp5D2r2Io8qHCuCMMI/yJU=
github.com/Azure/go-autorest/autorest/adal v0.9.1/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
@@ -86,8 +90,10 @@ github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocm
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8=
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -540,6 +546,7 @@ github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV
github.com/googleapis/gnostic v0.2.2 h1:DcFegQ7+ECdmkJMfVwWlC+89I4esJ7p8nkGt9ainGDk=
github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -798,8 +805,8 @@ github.com/minio/minio-go/v7 v7.0.2/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6J
github.com/minio/minio-go/v7 v7.0.5-0.20200811211821-14ed05478889/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089 h1:9DDs/Gc3fNHOQxQmwIFWs7YDLMTBh59r2XQ6RqEUA1I=
github.com/minio/minio-go/v7 v7.0.6-0.20200923173112-bc846cb9b089/go.mod h1:CSt2ETZNs+bIIhWTse0mcZKZWMGrFU7Er7RR0TmkDYk=
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c h1:OIKdzEJDFmUokbJ1rIdlr3kcfsBfXelYgSCTN/+Ppec=
github.com/minio/operator v0.0.0-20200930213302-ab2bbdfae96c/go.mod h1:6lavbNo2YuJWeQR5bZYsEWdbpRCO2KrTyfQ0PtC/AN4=
github.com/minio/operator v0.0.0-20201022162018-527e5c32132b h1:ggfD6V3nodTzhHJHCYIT1F897gscrz+hHsan0a2Wtqw=
github.com/minio/operator v0.0.0-20201022162018-527e5c32132b/go.mod h1:At+++4/6W5BEXK11tN5DKIvkJKhYBZybbb5zmxb0LQI=
github.com/minio/selfupdate v0.3.1 h1:BWEFSNnrZVMUWXbXIgLDNDjbejkmpAmZvy/nCz1HlEs=
github.com/minio/selfupdate v0.3.1/go.mod h1:b8ThJzzH7u2MkF6PcIra7KaXO9Khf6alWPvMSyTDCFM=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=

View File

@@ -93,9 +93,6 @@ type CreateTenantRequest struct {
// secret key
SecretKey string `json:"secret_key,omitempty"`
// service name
ServiceName string `json:"service_name,omitempty"`
// tls
TLS *TLSConfiguration `json:"tls,omitempty"`

View File

@@ -28,8 +28,6 @@ var (
iamPolicies = "/policies"
dashboard = "/dashboard"
profiling = "/profiling"
trace = "/trace"
logs = "/logs"
watch = "/watch"
notifications = "/notification-endpoints"
buckets = "/buckets"
@@ -61,16 +59,6 @@ var configurationActionSet = ConfigurationActionSet{
),
}
// logsActionSet contains the list of admin actions required for this endpoint to work
var logsActionSet = ConfigurationActionSet{
actionTypes: iampolicy.NewActionSet(
iampolicy.AllAdminActions,
),
actions: iampolicy.NewActionSet(
iampolicy.ConsoleLogAdminAction,
),
}
// dashboardActionSet contains the list of admin actions required for this endpoint to work
var dashboardActionSet = ConfigurationActionSet{
actionTypes: iampolicy.NewActionSet(
@@ -119,16 +107,6 @@ var profilingActionSet = ConfigurationActionSet{
),
}
// traceActionSet contains the list of admin actions required for this endpoint to work
var traceActionSet = ConfigurationActionSet{
actionTypes: iampolicy.NewActionSet(
iampolicy.AllAdminActions,
),
actions: iampolicy.NewActionSet(
iampolicy.TraceAdminAction,
),
}
// usersActionSet contains the list of admin actions required for this endpoint to work
var usersActionSet = ConfigurationActionSet{
actionTypes: iampolicy.NewActionSet(
@@ -252,8 +230,6 @@ var endpointRules = map[string]ConfigurationActionSet{
iamPolicies: iamPoliciesActionSet,
dashboard: dashboardActionSet,
profiling: profilingActionSet,
trace: traceActionSet,
logs: logsActionSet,
watch: watchActionSet,
notifications: notificationsActionSet,
buckets: bucketsActionSet,

View File

@@ -72,7 +72,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
"admin:*",
},
},
want: 17,
want: 15,
},
{
name: "all s3 endpoints",
@@ -91,7 +91,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
"s3:*",
},
},
want: 20,
want: 18,
},
{
name: "no endpoints",

View File

@@ -42,8 +42,6 @@ import ListNotificationEndpoints from "./NotificationEndopoints/ListNotification
import ConfigurationsList from "./Configurations/ConfigurationPanels/ConfigurationsList";
import { Button, LinearProgress } from "@material-ui/core";
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
import Trace from "./Trace/Trace";
import Logs from "./Logs/Logs";
import Heal from "./Heal/Heal";
import Watch from "./Watch/Watch";
import ListTenants from "./Tenants/ListTenants/ListTenants";
@@ -256,14 +254,6 @@ const Console = ({
component: Policies,
path: "/policies",
},
{
component: Trace,
path: "/trace",
},
{
component: Logs,
path: "/logs",
},
{
component: Heal,
path: "/heal",

View File

@@ -43,13 +43,11 @@ import {
LambdaNotificationsIcon,
MirroringIcon,
ServiceAccountsIcon,
TraceIcon,
UsersIcon,
WarpIcon,
} from "../../../icons";
import { clearSession } from "../../../common/utils";
import HealIcon from "../../../icons/HealIcon";
import ConsoleIcon from "../../../icons/ConsoleIcon";
import LicenseIcon from "../../../icons/LicenseIcon";
import LogoutIcon from "../../../icons/LogoutIcon";
@@ -242,14 +240,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
name: "IAM Policies",
icon: <IAMPoliciesIcon />,
},
{
group: "Tools",
type: "item",
component: NavLink,
to: "/logs",
name: "Logs",
icon: <ConsoleIcon />,
},
{
group: "Tools",
type: "item",
@@ -258,14 +248,6 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
name: "Watch",
icon: <WatchIcon />,
},
{
group: "Tools",
type: "item",
component: NavLink,
to: "/trace",
name: "Trace",
icon: <TraceIcon />,
},
{
group: "Tools",
type: "item",

View File

@@ -15,20 +15,19 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useState, useEffect } from "react";
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
import { AppState } from "../../../store";
import { AppState } from "../../../../../store";
import { connect } from "react-redux";
import { logMessageReceived, logResetMessages } from "./actions";
import { LogMessage } from "./types";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { timeFromDate } from "../../../common/utils";
import { wsProtocol } from "../../../utils/wsUtils";
import { timeFromDate } from "../../../../../common/utils";
import { wsProtocol } from "../../../../../utils/wsUtils";
import {
actionsTray,
containerForHeader,
searchField,
} from "../Common/FormComponents/common/styleLibrary";
} from "../../../Common/FormComponents/common/styleLibrary";
import { Grid } from "@material-ui/core";
import PageHeader from "../Common/PageHeader/PageHeader";
import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment";
import SearchIcon from "@material-ui/icons/Search";
@@ -73,6 +72,8 @@ interface ILogs {
logMessageReceived: typeof logMessageReceived;
logResetMessages: typeof logResetMessages;
messages: LogMessage[];
namespace: string;
tenant: string;
}
const Logs = ({
@@ -80,6 +81,8 @@ const Logs = ({
logMessageReceived,
logResetMessages,
messages,
namespace,
tenant,
}: ILogs) => {
const [highlight, setHighlight] = useState("");
@@ -92,7 +95,7 @@ const Logs = ({
const wsProt = wsProtocol(url.protocol);
const c = new W3CWebSocket(
`${wsProt}://${url.hostname}:${port}/ws/console`
`${wsProt}://${url.hostname}:${port}/ws/console/${namespace}/${tenant}`
);
let interval: any | null = null;
@@ -321,38 +324,33 @@ const Logs = ({
});
return (
<React.Fragment>
<PageHeader label="Logs" />
<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12} className={classes.actionsTray}>
<TextField
placeholder="Highlight Line"
className={classes.searchField}
id="search-resource"
label=""
onChange={(val) => {
setHighlight(val.target.value);
}}
InputProps={{
disableUnderline: true,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<div className={classes.logList}>{renderLines}</div>
</Grid>
</Grid>
<Grid item xs={12}>
<Grid item xs={12} className={classes.actionsTray}>
<TextField
placeholder="Highlight Line"
className={classes.searchField}
id="search-resource"
label=""
onChange={(val) => {
setHighlight(val.target.value);
}}
InputProps={{
disableUnderline: true,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
</Grid>
</React.Fragment>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<div className={classes.logList}>{renderLines}</div>
</Grid>
</Grid>
);
};

View File

@@ -32,6 +32,8 @@ import AddBucket from "../../Buckets/ListBuckets/AddBucket";
import ReplicationSetup from "./ReplicationSetup";
import api from "../../../../common/api";
import { ITenant, IZone } from "../ListTenants/types";
import Logs from "./Logs/Logs";
import Trace from "./Trace/Trace";
interface ITenantDetailsProps {
classes: any;
@@ -237,6 +239,8 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
aria-label="tenant-tabs"
>
<Tab label="Zones" />
<Tab label="Logs" />
<Tab label="Trace" />
</Tabs>
</Grid>
<Grid item xs={6} className={classes.actionsTray}>
@@ -255,42 +259,50 @@ const TenantDetails = ({ classes, match }: ITenantDetailsProps) => {
<br />
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={[
{
type: "delete",
onClick: (element) => {
console.log(element);
{selectedTab === 0 && (
<TableWrapper
itemActions={[
{
type: "delete",
onClick: (element) => {
console.log(element);
},
sendOnlyId: true,
},
sendOnlyId: true,
},
]}
columns={[
{ label: "Name", elementKey: "name" },
{ label: "Capacity", elementKey: "capacity" },
{ label: "# of Instances", elementKey: "servers" },
{ label: "# of Drives", elementKey: "volumes" },
]}
isLoading={false}
records={zones}
entityName="Zones"
idField="name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: zoneCount,
rowsPerPage: 10,
page: 0,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
ActionsComponent: MinTablePaginationActions,
onChangePage: () => {},
onChangeRowsPerPage: () => {},
}}
/>
]}
columns={[
{ label: "Name", elementKey: "name" },
{ label: "Capacity", elementKey: "capacity" },
{ label: "# of Instances", elementKey: "servers" },
{ label: "# of Drives", elementKey: "volumes" },
]}
isLoading={false}
records={zones}
entityName="Zones"
idField="name"
paginatorConfig={{
rowsPerPageOptions: [5, 10, 25],
colSpan: 3,
count: zoneCount,
rowsPerPage: 10,
page: 0,
SelectProps: {
inputProps: { "aria-label": "rows per page" },
native: true,
},
ActionsComponent: MinTablePaginationActions,
onChangePage: () => {},
onChangeRowsPerPage: () => {},
}}
/>
)}
</Grid>
{selectedTab === 1 && tenant !== null && (
<Logs namespace={tenant.namespace} tenant={tenant.name} />
)}
{selectedTab === 2 && tenant !== null && (
<Trace namespace={tenant.namespace} tenant={tenant.name} />
)}
</Grid>
</React.Fragment>
);

View File

@@ -16,17 +16,17 @@
import React, { useEffect } from "react";
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
import { AppState } from "../../../store";
import { AppState } from "../../../../../store";
import { connect } from "react-redux";
import { traceMessageReceived, traceResetMessages } from "./actions";
import { TraceMessage } from "./types";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { niceBytes, timeFromDate } from "../../../common/utils";
import { wsProtocol } from "../../../utils/wsUtils";
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
import PageHeader from "../Common/PageHeader/PageHeader";
import { niceBytes, timeFromDate } from "../../../../../common/utils";
import { wsProtocol } from "../../../../../utils/wsUtils";
import { containerForHeader } from "../../../Common/FormComponents/common/styleLibrary";
import PageHeader from "../../../Common/PageHeader/PageHeader";
import { Grid } from "@material-ui/core";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import TableWrapper from "../../../Common/TableWrapper/TableWrapper";
const styles = (theme: Theme) =>
createStyles({
@@ -59,6 +59,8 @@ interface ITrace {
traceMessageReceived: typeof traceMessageReceived;
traceResetMessages: typeof traceResetMessages;
messages: TraceMessage[];
namespace: string;
tenant: string;
}
const Trace = ({
@@ -66,6 +68,8 @@ const Trace = ({
traceMessageReceived,
traceResetMessages,
messages,
namespace,
tenant,
}: ITrace) => {
useEffect(() => {
traceResetMessages();
@@ -74,7 +78,9 @@ const Trace = ({
const port = isDev ? "9090" : url.port;
const wsProt = wsProtocol(url.protocol);
const c = new W3CWebSocket(`${wsProt}://${url.hostname}:${port}/ws/trace`);
const c = new W3CWebSocket(
`${wsProt}://${url.hostname}:${port}/ws/trace/${namespace}/${tenant}`
);
let interval: any | null = null;
if (c !== null) {
@@ -104,64 +110,59 @@ const Trace = ({
}, [traceMessageReceived, traceResetMessages]);
return (
<React.Fragment>
<PageHeader label={"Trace"} />
<Grid container>
<Grid item xs={12} className={classes.container}>
<TableWrapper
itemActions={[]}
columns={[
{
label: "Time",
elementKey: "time",
renderFunction: (time: Date) => {
const timeParse = new Date(time);
return timeFromDate(timeParse);
},
globalClass: classes.timeItem,
},
{ label: "Name", elementKey: "api" },
{
label: "Status",
elementKey: "",
renderFunction: (fullElement: TraceMessage) =>
`${fullElement.statusCode} ${fullElement.statusMsg}`,
renderFullObject: true,
},
{
label: "Location",
elementKey: "configuration_id",
renderFunction: (fullElement: TraceMessage) =>
`${fullElement.host} ${fullElement.client}`,
renderFullObject: true,
},
{
label: "Load Time",
elementKey: "callStats.duration",
globalClass: classes.timeItem,
},
{
label: "Upload",
elementKey: "callStats.rx",
renderFunction: niceBytes,
globalClass: classes.sizeItem,
},
{
label: "Download",
elementKey: "callStats.tx",
renderFunction: niceBytes,
globalClass: classes.sizeItem,
},
]}
isLoading={false}
records={messages}
entityName="Traces"
idField="api"
customEmptyMessage="There are no traced Elements yet"
/>
</Grid>
</Grid>
</React.Fragment>
<Grid item xs={12}>
<TableWrapper
itemActions={[]}
columns={[
{
label: "Time",
elementKey: "time",
renderFunction: (time: Date) => {
const timeParse = new Date(time);
return timeFromDate(timeParse);
},
globalClass: classes.timeItem,
},
{ label: "Name", elementKey: "api" },
{
label: "Status",
elementKey: "",
renderFunction: (fullElement: TraceMessage) =>
`${fullElement.statusCode} ${fullElement.statusMsg}`,
renderFullObject: true,
},
{
label: "Location",
elementKey: "configuration_id",
renderFunction: (fullElement: TraceMessage) =>
`${fullElement.host} ${fullElement.client}`,
renderFullObject: true,
},
{
label: "Load Time",
elementKey: "callStats.duration",
globalClass: classes.timeItem,
},
{
label: "Upload",
elementKey: "callStats.rx",
renderFunction: niceBytes,
globalClass: classes.sizeItem,
},
{
label: "Download",
elementKey: "callStats.tx",
renderFunction: niceBytes,
globalClass: classes.sizeItem,
},
]}
isLoading={false}
records={messages}
entityName="Traces"
idField="api"
customEmptyMessage="There are no traced Elements yet"
/>
</Grid>
);
};

View File

@@ -17,8 +17,8 @@
import { applyMiddleware, combineReducers, compose, createStore } from "redux";
import thunk from "redux-thunk";
import { systemReducer } from "./reducer";
import { traceReducer } from "./screens/Console/Trace/reducers";
import { logReducer } from "./screens/Console/Logs/reducers";
import { traceReducer } from "./screens/Console/Tenants/TenantDetails/Trace/reducers";
import { logReducer } from "./screens/Console/Tenants/TenantDetails/Logs/reducers";
import { watchReducer } from "./screens/Console/Watch/reducers";
import { consoleReducer } from "./screens/Console/reducer";
import { bucketsReducer } from "./screens/Console/Buckets/reducers";

View File

@@ -21,6 +21,8 @@ import (
"encoding/json"
"fmt"
"log"
"net/http"
"regexp"
"strings"
"time"
@@ -93,3 +95,25 @@ func getLogTime(lt string) string {
}
return tm.Format(logTimeFormat)
}
// getConsoleLogOptionsFromReq return tenant name from url
// path come as : `/console/<namespace>/<tenantName>`
func getConsoleLogOptionsFromReq(req *http.Request) (namespace, tenant string) {
re := regexp.MustCompile(`(/console/)(.*?)/(.*?)(\?.*?$|$)`)
matches := re.FindAllSubmatch([]byte(req.URL.Path), -1)
// len matches is always 4
namespace = strings.TrimSpace(string(matches[0][2]))
tenant = strings.TrimSpace(string(matches[0][3]))
return namespace, tenant
}
// getTraceOptionsFromReq return tenant name from url
// path come as : `/trace/<namespace>/<tenantName>`
func getTraceOptionsFromReq(req *http.Request) (namespace, tenant string) {
re := regexp.MustCompile(`(/trace/)(.*?)/(.*?)(\?.*?$|$)`)
matches := re.FindAllSubmatch([]byte(req.URL.Path), -1)
// len matches is always 4
namespace = strings.TrimSpace(string(matches[0][2]))
tenant = strings.TrimSpace(string(matches[0][3]))
return namespace, tenant
}

View File

@@ -576,14 +576,16 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
if tenantReq.EnableTLS != nil && *tenantReq.EnableTLS {
// If user request autoCert, Operator will generate certificate keypair for MinIO (server), Console (server) and KES (server and app mTLS)
isEncryptionEnabled = true
minInst.Spec.RequestAutoCert = *tenantReq.EnableTLS
minInst.Spec.RequestAutoCert = tenantReq.EnableTLS
}
if !minInst.Spec.RequestAutoCert && tenantReq.TLS != nil && len(tenantReq.TLS.Minio) > 0 {
if (minInst.Spec.RequestAutoCert == nil || (minInst.Spec.RequestAutoCert != nil && !*minInst.Spec.RequestAutoCert)) &&
tenantReq.TLS != nil &&
len(tenantReq.TLS.Minio) > 0 {
// User provided TLS certificates for MinIO
isEncryptionEnabled = true
// disable autoCert
minInst.Spec.RequestAutoCert = false
minInst.Spec.RequestAutoCert = swag.Bool(false)
// Certificates used by the MinIO instance
externalCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
externalCertSecret, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, tenantReq.TLS.Minio, externalCertSecretName, tenantName)
@@ -600,7 +602,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
Value: "on",
})
// KES client mTLSCertificates used by MinIO instance, only if autoCert is not enabled
if !minInst.Spec.RequestAutoCert {
if minInst.Spec.RequestAutoCert == nil || (minInst.Spec.RequestAutoCert != nil && !*minInst.Spec.RequestAutoCert) {
tenantExternalClientCertSecretName := fmt.Sprintf("%s-tenant-external-client-cert", secretName)
certificates := []*models.KeyPairConfiguration{tenantReq.Encryption.Client}
certificateSecrets, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, certificates, tenantExternalClientCertSecretName, tenantName)
@@ -664,7 +666,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
instanceSecret.Data["CONSOLE_IDP_SECRET"] = []byte(secretID)
consoleScheme := "http"
consolePort := 9090
if minInst.Spec.RequestAutoCert {
if minInst.Spec.RequestAutoCert != nil && *minInst.Spec.RequestAutoCert {
consoleScheme = "https"
consolePort = 9443
}
@@ -690,7 +692,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
},
},
}
if !minInst.Spec.RequestAutoCert && tenantReq.TLS != nil && tenantReq.TLS.Console != nil {
if (minInst.Spec.RequestAutoCert == nil || (minInst.Spec.RequestAutoCert != nil && !*minInst.Spec.RequestAutoCert)) && tenantReq.TLS != nil && tenantReq.TLS.Console != nil {
// Certificates used by the console instance
externalCertSecretName := fmt.Sprintf("%s-console-external-certificates", secretName)
certificates := []*models.KeyPairConfiguration{tenantReq.TLS.Console}
@@ -711,10 +713,6 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
}
}
// set the service name if provided
if tenantReq.ServiceName != "" {
minInst.Spec.ServiceName = tenantReq.ServiceName
}
// add annotations
var annotations map[string]string

View File

@@ -185,7 +185,7 @@ func getTenantUpdateEncryptionResponse(session *models.Principal, params admin_a
// getKESConfiguration will generate the KES server certificate secrets, the tenant client secrets for mTLS authentication between MinIO and KES and the
// kes-configuration.yaml file used by the KES service (how to connect to the external KMS, eg: Vault, AWS, Gemalto, etc)
func getKESConfiguration(ctx context.Context, clientSet K8sClientI, ns string, encryptionCfg *models.EncryptionConfiguration, secretName, tenantName string, autoCert bool) (kesConfiguration *operator.KESConfig, err error) {
func getKESConfiguration(ctx context.Context, clientSet K8sClientI, ns string, encryptionCfg *models.EncryptionConfiguration, secretName, tenantName string, autoCert *bool) (kesConfiguration *operator.KESConfig, err error) {
// Secrets used by the KES service
//
// kesExternalCertSecretName is the name of the secret that will store the certificates for TLS in the KES server, eg: server.key and server.crt
@@ -204,7 +204,7 @@ func getKESConfiguration(ctx context.Context, clientSet K8sClientI, ns string, e
kesConfiguration.Image = encryptionCfg.Image
}
// Generate server certificates for KES only if autoCert is disabled
if !autoCert {
if autoCert == nil || (autoCert != nil && !*autoCert) {
certificates := []*models.KeyPairConfiguration{encryptionCfg.Server}
certificateSecrets, err := createOrReplaceExternalCertSecrets(ctx, clientSet, ns, certificates, kesExternalCertSecretName, tenantName)
if err != nil {

View File

@@ -3264,9 +3264,6 @@ func init() {
"secret_key": {
"type": "string"
},
"service_name": {
"type": "string"
},
"tls": {
"type": "object",
"$ref": "#/definitions/tlsConfiguration"
@@ -8656,9 +8653,6 @@ func init() {
"secret_key": {
"type": "string"
},
"service_name": {
"type": "string"
},
"tls": {
"type": "object",
"$ref": "#/definitions/tlsConfiguration"

View File

@@ -22,9 +22,11 @@ import (
"net"
"net/http"
"strings"
"time"
"github.com/go-openapi/errors"
"github.com/gorilla/websocket"
"github.com/minio/console/cluster"
"github.com/minio/console/models"
"github.com/minio/console/pkg/auth"
)
@@ -118,15 +120,19 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
wsPath := strings.TrimPrefix(req.URL.Path, wsBasePath)
switch {
case wsPath == "/trace":
wsAdminClient, err := newWebSocketAdminClient(conn, session)
case strings.HasPrefix(wsPath, `/trace`):
// Trace api only for operator Console
namespace, tenant := getTraceOptionsFromReq(req)
wsAdminClient, err := newWebSocketTenantAdminClient(conn, session, namespace, tenant)
if err != nil {
closeWsConn(conn)
return
}
go wsAdminClient.trace()
case wsPath == "/console":
wsAdminClient, err := newWebSocketAdminClient(conn, session)
case strings.HasPrefix(wsPath, `/console`):
// Trace api only for operator Console
namespace, tenant := getConsoleLogOptionsFromReq(req)
wsAdminClient, err := newWebSocketTenantAdminClient(conn, session, namespace, tenant)
if err != nil {
closeWsConn(conn)
return
@@ -159,6 +165,57 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
}
}
// newWebSocketTenantAdminClient creates a ws Client with a k8s tenant client
// this is to be used for a kubernetes environment and for a particular tenant
// in a defined namespace
func newWebSocketTenantAdminClient(conn *websocket.Conn, session *models.Principal, namespace, tenant string) (*wsAdminClient, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
opClientClientSet, err := cluster.OperatorClient(session.SessionToken)
if err != nil {
return nil, err
}
clientSet, err := cluster.K8sClient(session.SessionToken)
if err != nil {
return nil, err
}
opClient := &operatorClient{
client: opClientClientSet,
}
k8sClient := &k8sClient{
client: clientSet,
}
minTenant, err := getTenant(ctx, opClient, namespace, tenant)
if err != nil {
return nil, err
}
minTenant.EnsureDefaults()
svcURL := GetTenantServiceURL(minTenant)
mAdmin, err := getTenantAdminClient(
ctx,
k8sClient,
minTenant,
svcURL,
true)
if err != nil {
return nil, err
}
// create a websocket connection interface implementation
// defining the connection to be used
wsConnection := wsConn{conn: conn}
// create a minioClient interface implementation
// defining the client to be used
adminClient := adminClient{client: mAdmin}
// create websocket client and handle request
wsAdminClient := &wsAdminClient{conn: wsConnection, client: adminClient}
return wsAdminClient, nil
}
// newWebSocketAdminClient returns a wsAdminClient authenticated as an admin user
func newWebSocketAdminClient(conn *websocket.Conn, autClaims *models.Principal) (*wsAdminClient, error) {
// Only start Websocket Interaction after user has been

View File

@@ -2697,8 +2697,6 @@ definitions:
type: string
console_image:
type: string
service_name:
type: string
zones:
type: array
items: