Lens restructure (#540)

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman authored and GitHub committed 2020-06-30 14:35:16 +03:00
1 parent e810eab39f
commit b7974827d2
786 files changed
+14567 -24066

No files matched your search

@@ -0,0 +1,3 @@
export * from "./ingresses.route"
export * from "./ingresses"
export * from "./ingress-details"
@@ -0,0 +1,97 @@
import React, { useContext } from "react";
import { t } from "@lingui/macro";
import { observer } from "mobx-react";
import { ChartOptions, ChartPoint } from "chart.js";
import { IIngressMetrics, Ingress } from "../../api/endpoints";
import { BarChart, memoryOptions } from "../chart";
import { normalizeMetrics, isMetricsEmpty } from "../../api/endpoints/metrics.api";
import { NoMetrics } from "../resource-metrics/no-metrics";
import { ResourceMetricsContext, IResourceMetricsValue } from "../resource-metrics";
import { _i18n } from "../../i18n";
type IContext = IResourceMetricsValue<Ingress, { metrics: IIngressMetrics }>;
export const IngressCharts = observer(() => {
const { params: { metrics }, tabId, object } = useContext<IContext>(ResourceMetricsContext);
const id = object.getId();
if (!metrics) return null;
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
const values = Object.values(metrics)
.map(normalizeMetrics)
.map(({ data }) => data.result[0].values);
const [
bytesSentSuccess,
bytesSentFailure,
requestDurationSeconds,
responseDurationSeconds
] = values;
const datasets = [
// Network
[
{
id: `${id}-bytesSentSuccess`,
label: _i18n._(t`Bytes sent, status 2xx`),
tooltip: _i18n._(t`Bytes sent by Ingress controller with successful status`),
borderColor: "#46cd9e",
data: bytesSentSuccess.map(([x, y]) => ({ x, y }))
},
{
id: `${id}-bytesSentFailure`,
label: _i18n._(t`Bytes sent, status 5xx`),
tooltip: _i18n._(t`Bytes sent by Ingress controller with error status`),
borderColor: "#cd465a",
data: bytesSentFailure.map(([x, y]) => ({ x, y }))
},
],
// Duration
[
{
id: `${id}-requestDurationSeconds`,
label: _i18n._(t`Request`),
tooltip: _i18n._(t`Request duration in seconds`),
borderColor: "#48b18d",
data: requestDurationSeconds.map(([x, y]) => ({ x, y }))
},
{
id: `${id}-responseDurationSeconds`,
label: _i18n._(t`Response`),
tooltip: _i18n._(t`Response duration in seconds`),
borderColor: "#73ba3c",
data: responseDurationSeconds.map(([x, y]) => ({ x, y }))
},
]
];
const durationOptions: ChartOptions = {
scales: {
yAxes: [{
ticks: {
callback: value => value
}
}]
},
tooltips: {
callbacks: {
label: ({ datasetIndex, index }, { datasets }) => {
const { label, data } = datasets[datasetIndex];
const value = data[index] as ChartPoint;
const chartTooltipSec = _i18n._(t`sec`);
return `${label}: ${parseFloat(value.y as string).toFixed(3)} ${chartTooltipSec}`;
}
}
}
};
const options = [memoryOptions, durationOptions];
return (
<BarChart
name={`${object.getName()}-metric-${tabId}`}
options={options[tabId]}
data={{ datasets: datasets[tabId] }}
/>
);
})
@@ -0,0 +1,29 @@
.IngressDetails {
--titles-color: #{$textColorSecondary};
.rules {
margin-bottom: $margin * 2
}
.host-title {
color: var(--titles-color);
font-weight: bold;
margin-top: $margin * 2;
margin-bottom: $margin;
}
.Table {
margin-left: -$margin;
margin-right: -$margin;
.TableHead {
color: var(--titles-color);
}
.path {
&:empty:after {
content: '\2014';
}
}
}
}
@@ -0,0 +1,113 @@
import "./ingress-details.scss";
import React from "react";
import { disposeOnUnmount, observer } from "mobx-react";
import { reaction } from "mobx";
import { Trans } from "@lingui/macro";
import { DrawerItem, DrawerTitle } from "../drawer";
import { Ingress, ingressApi } from "../../api/endpoints";
import { Table, TableCell, TableHead, TableRow } from "../table";
import { KubeEventDetails } from "../+events/kube-event-details";
import { ingressStore } from "./ingress.store";
import { ResourceMetrics } from "../resource-metrics";
import { KubeObjectDetailsProps } from "../kube-object";
import { IngressCharts } from "./ingress-charts";
import { apiManager } from "../../api/api-manager";
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
interface Props extends KubeObjectDetailsProps<Ingress> {
}
@observer
export class IngressDetails extends React.Component<Props> {
@disposeOnUnmount
clean = reaction(() => this.props.object, () => {
ingressStore.reset();
});
componentWillUnmount() {
ingressStore.reset();
}
renderPaths(ingress: Ingress) {
const { spec: { rules } } = ingress
if (!rules || !rules.length) return null
return rules.map((rule, index) => {
return (
<div className="rules" key={index}>
{rule.host && (
<div className="host-title">
<Trans>Host: {rule.host}</Trans>
</div>
)}
{rule.http && (
<Table className="paths">
<TableHead>
<TableCell className="path"><Trans>Path</Trans></TableCell>
<TableCell className="backends"><Trans>Backends</Trans></TableCell>
</TableHead>
{
rule.http.paths.map((path, index) => {
const backend = `${path.backend.serviceName}:${path.backend.servicePort}`
return (
<TableRow key={index}>
<TableCell className="path">{path.path || ""}</TableCell>
<TableCell className="backends">
<p key={backend}>{backend}</p>
</TableCell>
</TableRow>
)
})
}
</Table>
)}
</div>
)
})
}
render() {
const { object: ingress } = this.props;
if (!ingress) {
return null;
}
const { spec } = ingress;
const { metrics } = ingressStore;
const metricTabs = [
<Trans>Network</Trans>,
<Trans>Duration</Trans>,
];
return (
<div className="IngressDetails">
<ResourceMetrics
loader={() => ingressStore.loadMetrics(ingress)}
tabs={metricTabs} object={ingress} params={{ metrics }}
>
<IngressCharts/>
</ResourceMetrics>
<KubeObjectMeta object={ingress}/>
<DrawerItem name={<Trans>Ports</Trans>}>
{ingress.getPorts()}
</DrawerItem>
{spec.tls &&
<DrawerItem name={<Trans>TLS</Trans>}>
{spec.tls.map((tls, index) => <p key={index}>{tls.secretName}</p>)}
</DrawerItem>
}
{spec.backend && spec.backend.serviceName && spec.backend.servicePort &&
<DrawerItem name={<Trans>Service</Trans>}>
{spec.backend.serviceName}:{spec.backend.servicePort}
</DrawerItem>
}
<DrawerTitle title={<Trans>Rules</Trans>}/>
{this.renderPaths(ingress)}
<KubeEventDetails object={ingress}/>
</div>
)
}
}
apiManager.registerViews(ingressApi, {
Details: IngressDetails,
})
@@ -0,0 +1,22 @@
import { observable } from "mobx";
import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils";
import { IIngressMetrics, Ingress, ingressApi } from "../../api/endpoints";
import { apiManager } from "../../api/api-manager";
@autobind()
export class IngressStore extends KubeObjectStore<Ingress> {
api = ingressApi;
@observable metrics: IIngressMetrics = null;
async loadMetrics(ingress: Ingress) {
this.metrics = await this.api.getMetrics(ingress.getName(), ingress.getNs());
}
reset() {
this.metrics = null;
}
}
export const ingressStore = new IngressStore();
apiManager.registerStore(ingressApi, ingressStore);
@@ -0,0 +1,11 @@
import { RouteProps } from "react-router"
import { buildURL } from "../../navigation";
export const ingressRoute: RouteProps = {
path: "/ingresses"
}
export interface IngressRouteParams {
}
export const ingressURL = buildURL<IngressRouteParams>(ingressRoute.path)
@@ -0,0 +1,7 @@
.Ingresses {
.TableCell {
&.rules {
flex-grow: 3.0;
}
}
}
@@ -0,0 +1,73 @@
import "./ingresses.scss"
import React from "react"
import { observer } from "mobx-react";
import { RouteComponentProps } from "react-router-dom"
import { IngressRouteParams } from "./ingresses.route"
import { Ingress, ingressApi } from "../../api/endpoints/ingress.api"
import { ingressStore } from "./ingress.store";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { KubeObjectListLayout } from "../kube-object";
import { Trans } from "@lingui/macro";
import { apiManager } from "../../api/api-manager";
enum sortBy {
name = "name",
namespace = "namespace",
age = "age",
}
interface Props extends RouteComponentProps<IngressRouteParams> {
}
@observer
export class Ingresses extends React.Component<Props> {
render() {
return (
<KubeObjectListLayout
className="Ingresses" store={ingressStore}
sortingCallbacks={{
[sortBy.name]: (ingress: Ingress) => ingress.getName(),
[sortBy.namespace]: (ingress: Ingress) => ingress.getNs(),
[sortBy.age]: (ingress: Ingress) => ingress.metadata.creationTimestamp,
}}
searchFilters={[
(ingress: Ingress) => ingress.getSearchFields(),
(ingress: Ingress) => ingress.getPorts(),
]}
renderHeaderTitle={<Trans>Ingresses</Trans>}
renderTableHeader={[
{ title: <Trans>Name</Trans>, className: "name", sortBy: sortBy.name },
{ title: <Trans>Namespace</Trans>, className: "namespace", sortBy: sortBy.namespace },
{ title: <Trans>Rules</Trans>, className: "rules" },
{ title: <Trans>Age</Trans>, className: "age", sortBy: sortBy.age },
]}
renderTableContents={(ingress: Ingress) => [
ingress.getName(),
ingress.getNs(),
ingress.getRoutes().map(route => <p key={route}>{route}</p>),
ingress.getAge(),
]}
renderItemMenu={(item: Ingress) => {
return <IngressMenu object={item}/>
}}
tableProps={{
customRowHeights: (item: Ingress, lineHeight, paddings) => {
const lines = item.getRoutes().length || 1;
return lines * lineHeight + paddings;
}
}}
/>
)
}
}
export function IngressMenu(props: KubeObjectMenuProps<Ingress>) {
return (
<KubeObjectMenu {...props}/>
)
}
apiManager.registerViews(ingressApi, {
Menu: IngressMenu
})