diff --git a/CHANGELOG.md b/CHANGELOG.md index 0042a0349..41041f081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan - [#59](https://github.com/kobsio/kobs/pull/59): Add support for Templates via the new Templates CRD. Templates allows a user to reuse plugin definitions accross Applications, Teams and Kubernetes resources. - [#60](https://github.com/kobsio/kobs/pull/60): Add support for additional Pod annotations and labels in the Helm chart via the new `podAnnotations` and `podLabels` values. - [#63](https://github.com/kobsio/kobs/pull/63): Add Kiali plugin (in the current version the Kiali plugin only supports the graph feature from Kiali). +- [#66](https://github.com/kobsio/kobs/pull/66): Add edge metrics for Kiali plugin. ### Fixed diff --git a/app/src/components/applications/applications.css b/app/src/components/applications/applications.css index d0fe5ccc8..ef23a1262 100644 --- a/app/src/components/applications/applications.css +++ b/app/src/components/applications/applications.css @@ -1,3 +1,5 @@ +/* kobsio-application-topology + * The kobsio-application-topology classes are used for the node labels in the topology graph. */ .kobsio-application-topology-label { border-radius: 3px; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 2px 8px 0 rgba(0, 0, 0, 0.19); @@ -31,7 +33,7 @@ } .kobsio-application-topology-label-badge { - background-color: var(--pf-global--palette--blue-200); + background-color: var(--pf-global--primary-color--100); margin-right: 5px; min-width: 24px; padding-left: 0px; diff --git a/app/src/plugins/kiali/KialiChart.tsx b/app/src/plugins/kiali/KialiChart.tsx new file mode 100644 index 000000000..99eb6e393 --- /dev/null +++ b/app/src/plugins/kiali/KialiChart.tsx @@ -0,0 +1,85 @@ +import { + Chart, + ChartArea, + ChartAxis, + ChartGroup, + ChartLegendTooltip, + ChartThemeColor, + createContainer, +} from '@patternfly/react-charts'; +import React, { useEffect, useRef, useState } from 'react'; + +import { Data, Metric } from 'proto/kiali_grpc_web_pb'; +import { formatTime } from 'utils/helpers'; + +interface ILabels { + datum: Data.AsObject; +} + +export interface IKialiChartProps { + unit: string; + metrics: Metric.AsObject[]; +} + +const KialiChart: React.FunctionComponent = ({ unit, metrics }: IKialiChartProps) => { + const refChart = useRef(null); + const [width, setWidth] = useState(0); + const [height, setHeight] = useState(0); + + // useEffect is executed on every render of this component. This is needed, so that we are able to use a width of 100% + // and a static height for the chart. + useEffect(() => { + if (refChart && refChart.current) { + setWidth(refChart.current.getBoundingClientRect().width); + setHeight(refChart.current.getBoundingClientRect().height); + } + }, []); + + // In the following we are creating the container for the cursor container, we are generating the data for the legend + // and we are creating the series component for each metric. + const CursorVoronoiContainer = createContainer('voronoi', 'cursor'); + const legendData = metrics.map((metric, index) => ({ + childName: `index${index}`, + name: metric.stat ? metric.stat : metric.name, + })); + const series = metrics.map((metric, index) => ( + + )); + + return ( + +
+ (datum.y ? `${datum.y} ${unit}` : null)} + labelComponent={ + formatTime(Math.floor(point.x / 1000))} + /> + } + mouseFollowTooltips + voronoiDimension="x" + voronoiPadding={{ bottom: 60, left: 60, right: 0, top: 0 }} + /> + } + height={height} + legendData={legendData} + legendPosition="bottom" + padding={{ bottom: 60, left: 60, right: 0, top: 0 }} + scale={{ x: 'time', y: 'linear' }} + themeColor={ChartThemeColor.multiOrdered} + width={width} + > + + + {series} + +
+
+ ); +}; + +export default KialiChart; diff --git a/app/src/plugins/kiali/KialiDetailsEdge.tsx b/app/src/plugins/kiali/KialiDetailsEdge.tsx new file mode 100644 index 000000000..65067e936 --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsEdge.tsx @@ -0,0 +1,119 @@ +import { + Badge, + DrawerActions, + DrawerCloseButton, + DrawerHead, + DrawerPanelBody, + DrawerPanelContent, + Tab, + TabTitleText, + Tabs, +} from '@patternfly/react-core'; +import React, { useState } from 'react'; + +import { Edge, NodeWrapper } from 'proto/kiali_grpc_web_pb'; +import KialiDetailsEdgeFlags from 'plugins/kiali/KialiDetailsEdgeFlags'; +import KialiDetailsEdgeHosts from 'plugins/kiali/KialiDetailsEdgeHosts'; +import KialiDetailsEdgeMetrics from 'plugins/kiali/KialiDetailsEdgeMetrics'; +import KialiDetailsEdgeTrafficGRPC from 'plugins/kiali/KialiDetailsEdgeTrafficGRPC'; +import KialiDetailsEdgeTrafficHTTP from 'plugins/kiali/KialiDetailsEdgeTrafficHTTP'; +import { getTitle } from 'plugins/kiali/helpers'; + +interface IKialiDetailsEdgeProps { + name: string; + duration: number; + edge: Edge.AsObject; + nodes: NodeWrapper.AsObject[]; + close: () => void; +} + +// KialiDetailsEdge is used as the drawer panel component to display the details about a selected edge. +const KialiDetailsEdge: React.FunctionComponent = ({ + name, + duration, + edge, + nodes, + close, +}: IKialiDetailsEdgeProps) => { + const [activeTab, setActiveTab] = useState( + edge.traffic?.protocol === 'http' ? 'trafficHTTP' : edge.traffic?.protocol === 'grpc' ? 'trafficGRPC' : 'flags', + ); + + // To display the edge details like metrics, we have to get the source and target node of the edge. After that we, + // generate the title for both nodes and display them in the format "From: ... To: ...". + const sourceNode = nodes.filter((node) => node.data?.id === edge.source); + const sourceTitle = + sourceNode.length === 1 && sourceNode[0].data ? getTitle(sourceNode[0].data) : { badge: 'U', title: 'Unknown' }; + const targetNode = nodes.filter((node) => node.data?.id === edge.target); + const targetTitle = + targetNode.length === 1 && targetNode[0].data ? getTitle(targetNode[0].data) : { badge: 'U', title: 'Unknown' }; + + return ( + + + + From: + + {sourceTitle.badge} + {sourceTitle.title} + + To: + + {targetTitle.badge} + {targetTitle.title} + + + + + + + + + setActiveTab(tabIndex.toString())} + className="pf-u-mt-md" + isFilled={true} + mountOnEnter={true} + > + {edge.traffic?.protocol === 'http' ? ( + Traffic}> +
+ +
+
+ ) : null} + {edge.traffic?.protocol === 'grpc' ? ( + Traffic}> +
+ +
+
+ ) : null} + Flags}> +
+ +
+
+ Hosts}> +
+ +
+
+
+ + {sourceNode.length === 1 && sourceNode[0].data && targetNode.length === 1 && targetNode[0].data ? ( + + ) : null} +
+
+ ); +}; + +export default KialiDetailsEdge; diff --git a/app/src/plugins/kiali/KialiDetailsEdgeFlags.tsx b/app/src/plugins/kiali/KialiDetailsEdgeFlags.tsx new file mode 100644 index 000000000..a9c283024 --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsEdgeFlags.tsx @@ -0,0 +1,45 @@ +import { Card, CardBody, CardTitle } from '@patternfly/react-core'; +import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; +import React from 'react'; + +import { Edge } from 'proto/kiali_grpc_web_pb'; + +interface IKialiDetailsEdgeFlagsProps { + edge: Edge.AsObject; +} + +// KialiDetailsEdgeFlags is the tab content for the flags tab of an edge. It is used to display a table of the responses +// grouped by the flag. +const KialiDetailsEdgeFlags: React.FunctionComponent = ({ + edge, +}: IKialiDetailsEdgeFlagsProps) => { + return ( + + Response flags by HTTP code + + + + + Code + Flags + Req (%) + + + + {edge.traffic?.responsesMap.map((response, i) => + response[1].flagsMap.map((flag, j) => ( + + {response[0]} + {flag[0]} + {flag[1]} + + )), + )} + + + + + ); +}; + +export default KialiDetailsEdgeFlags; diff --git a/app/src/plugins/kiali/KialiDetailsEdgeHosts.tsx b/app/src/plugins/kiali/KialiDetailsEdgeHosts.tsx new file mode 100644 index 000000000..8107de327 --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsEdgeHosts.tsx @@ -0,0 +1,45 @@ +import { Card, CardBody, CardTitle } from '@patternfly/react-core'; +import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; +import React from 'react'; + +import { Edge } from 'proto/kiali_grpc_web_pb'; + +interface IKialiDetailsEdgeHostsProps { + edge: Edge.AsObject; +} + +// KialiDetailsEdgeHosts is the tab content for the hosts tab of an edge. It is used to display a table of the responses +// grouped by the host. +const KialiDetailsEdgeHosts: React.FunctionComponent = ({ + edge, +}: IKialiDetailsEdgeHostsProps) => { + return ( + + Hosts by HTTP code + + + + + Code + Host + Req (%) + + + + {edge.traffic?.responsesMap.map((response, i) => + response[1].hostsMap.map((host, j) => ( + + {response[0]} + {host[0]} + {host[1]} + + )), + )} + + + + + ); +}; + +export default KialiDetailsEdgeHosts; diff --git a/app/src/plugins/kiali/KialiDetailsEdgeMetrics.tsx b/app/src/plugins/kiali/KialiDetailsEdgeMetrics.tsx new file mode 100644 index 000000000..685cbaaf2 --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsEdgeMetrics.tsx @@ -0,0 +1,57 @@ +import React from 'react'; + +import { Edge, Node } from 'proto/kiali_grpc_web_pb'; +import KialiDetailsEdgeMetricsGRPC from 'plugins/kiali/KialiDetailsEdgeMetricsGRPC'; +import KialiDetailsEdgeMetricsHTTP from 'plugins/kiali/KialiDetailsEdgeMetricsHTTP'; +import KialiDetailsEdgeMetricsTCP from 'plugins/kiali/KialiDetailsEdgeMetricsTCP'; + +interface IKialiDetailsEdgeMetricsProps { + name: string; + duration: number; + edge: Edge.AsObject; + sourceNode: Node.AsObject; + targetNode: Node.AsObject; +} + +// KialiDetailsEdgeMetrics is a wrapper component for the metrics of an edge. We use this component to decide if we have +// to display the http, tcp or grpc metrics for the edge. +// This component is displayed for all tabs, below the actual tab content. +const KialiDetailsEdgeMetrics: React.FunctionComponent = ({ + name, + duration, + edge, + sourceNode, + targetNode, +}: IKialiDetailsEdgeMetricsProps) => { + return ( +
+ {edge.traffic?.protocol === 'tcp' ? ( + + ) : edge.traffic?.protocol === 'http' ? ( + + ) : edge.traffic?.protocol === 'grpc' ? ( + + ) : null} +
+ ); +}; + +export default KialiDetailsEdgeMetrics; diff --git a/app/src/plugins/kiali/KialiDetailsEdgeMetricsGRPC.tsx b/app/src/plugins/kiali/KialiDetailsEdgeMetricsGRPC.tsx new file mode 100644 index 000000000..bd67706fd --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsEdgeMetricsGRPC.tsx @@ -0,0 +1,145 @@ +import { Alert, AlertVariant, Card, CardBody, CardTitle, Spinner } from '@patternfly/react-core'; +import React, { useCallback, useEffect, useState } from 'react'; + +import { Edge, GetMetricsRequest, GetMetricsResponse, KialiPromiseClient, Metric, Node } from 'proto/kiali_grpc_web_pb'; +import KialiChart from 'plugins/kiali/KialiChart'; +import { apiURL } from 'utils/constants'; + +// kialiService is the gRPC service to get the metrics for the selected edge. +const kialiService = new KialiPromiseClient(apiURL, null, null); + +interface IDataState { + error: string; + isLoading: boolean; + responseTime: Metric.AsObject[]; + traffic: Metric.AsObject[]; +} + +interface IKialiDetailsEdgeMetricsGRPCProps { + name: string; + duration: number; + edge: Edge.AsObject; + sourceNode: Node.AsObject; + targetNode: Node.AsObject; +} + +// KialiDetailsEdgeMetricsGRPC is used to get the the data for the grpc metrics (request traffic and request response +// time). +const KialiDetailsEdgeMetricsGRPC: React.FunctionComponent = ({ + name, + duration, + edge, + sourceNode, + targetNode, +}: IKialiDetailsEdgeMetricsGRPCProps) => { + const [data, setData] = useState({ error: '', isLoading: false, responseTime: [], traffic: [] }); + + const fetchGraph = useCallback(async (): Promise => { + try { + setData({ error: '', isLoading: true, responseTime: [], traffic: [] }); + + let nodeType = 'workloads'; + let nodeName = targetNode.workload; + let byLabels = ['destination_service_name']; + let reporter = 'destination'; + + if (targetNode.nodetype === 'service') { + nodeType = 'services'; + nodeName = targetNode.service; + byLabels = ['source_workload']; + reporter = 'source'; + } + + if (sourceNode.nodetype === 'unknown') { + reporter = 'destination'; + } + + let filterName = sourceNode.workload; + + if (sourceNode.nodetype === 'service') { + filterName = sourceNode.service; + } + + const getMetricsRequest = new GetMetricsRequest(); + getMetricsRequest.setName(name); + getMetricsRequest.setNamespace(targetNode.namespace); + getMetricsRequest.setNodetype(nodeType); + getMetricsRequest.setNodename(nodeName); + getMetricsRequest.setQuerytime(Math.floor(Date.now() / 1000)); + getMetricsRequest.setDuration(duration); + getMetricsRequest.setStep(30); + getMetricsRequest.setRateinterval('30s'); + getMetricsRequest.setFiltersList(['request_count', 'request_duration_millis', 'request_error_count']); + getMetricsRequest.setBylabelsList(byLabels); + getMetricsRequest.setDirection('inbound'); + getMetricsRequest.setReporter(reporter); + getMetricsRequest.setRequestprotocol('grpc'); + + const getMetricsResponse: GetMetricsResponse = await kialiService.getMetrics(getMetricsRequest, null); + const metrics = getMetricsResponse.toObject().metricsList.filter((metric) => metric.label === filterName); + + const traffic = metrics.filter((metric) => metric.name === 'request_count'); + traffic.push(...metrics.filter((metric) => metric.name === 'request_error_count')); + + setData({ + error: '', + isLoading: false, + responseTime: metrics.filter((metric) => metric.name === 'request_duration_millis'), + traffic: traffic, + }); + } catch (err) { + setData({ error: err.message, isLoading: false, responseTime: [], traffic: [] }); + } + }, [name, duration, sourceNode, targetNode]); + + useEffect(() => { + fetchGraph(); + }, [fetchGraph]); + + if (data.isLoading) { + return ( +
+ +
+ ); + } + + if (data.error) { + return ( + +

{data.error}

+
+ ); + } + + return ( + + {data.traffic.length === 0 ? ( + + ) : ( + + GRPC Request Traffic + + + + + )} + +

 

+

 

+ + {data.responseTime.length === 0 ? ( + + ) : ( + + GRPC Request Response Time + + + + + )} +
+ ); +}; + +export default KialiDetailsEdgeMetricsGRPC; diff --git a/app/src/plugins/kiali/KialiDetailsEdgeMetricsHTTP.tsx b/app/src/plugins/kiali/KialiDetailsEdgeMetricsHTTP.tsx new file mode 100644 index 000000000..bc286bd44 --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsEdgeMetricsHTTP.tsx @@ -0,0 +1,143 @@ +import { Alert, AlertVariant, Card, CardBody, CardTitle, Spinner } from '@patternfly/react-core'; +import React, { useCallback, useEffect, useState } from 'react'; + +import { Edge, GetMetricsRequest, GetMetricsResponse, KialiPromiseClient, Metric, Node } from 'proto/kiali_grpc_web_pb'; +import KialiChart from 'plugins/kiali/KialiChart'; +import { apiURL } from 'utils/constants'; + +// kialiService is the gRPC service to get the metrics for the selected edge. +const kialiService = new KialiPromiseClient(apiURL, null, null); + +interface IDataState { + error: string; + isLoading: boolean; + responseTime: Metric.AsObject[]; + traffic: Metric.AsObject[]; +} + +interface IKialiDetailsEdgeMetricsHTTPProps { + name: string; + duration: number; + edge: Edge.AsObject; + sourceNode: Node.AsObject; + targetNode: Node.AsObject; +} + +const KialiDetailsEdgeMetricsHTTP: React.FunctionComponent = ({ + name, + duration, + edge, + sourceNode, + targetNode, +}: IKialiDetailsEdgeMetricsHTTPProps) => { + const [data, setData] = useState({ error: '', isLoading: false, responseTime: [], traffic: [] }); + + const fetchGraph = useCallback(async (): Promise => { + try { + setData({ error: '', isLoading: true, responseTime: [], traffic: [] }); + + let nodeType = 'workloads'; + let nodeName = targetNode.workload; + let byLabels = ['destination_service_name']; + let reporter = 'destination'; + + if (targetNode.nodetype === 'service') { + nodeType = 'services'; + nodeName = targetNode.service; + byLabels = ['source_workload']; + reporter = 'source'; + } + + if (sourceNode.nodetype === 'unknown') { + reporter = 'destination'; + } + + let filterName = sourceNode.workload; + + if (sourceNode.nodetype === 'service') { + filterName = sourceNode.service; + } + + const getMetricsRequest = new GetMetricsRequest(); + getMetricsRequest.setName(name); + getMetricsRequest.setNamespace(targetNode.namespace); + getMetricsRequest.setNodetype(nodeType); + getMetricsRequest.setNodename(nodeName); + getMetricsRequest.setQuerytime(Math.floor(Date.now() / 1000)); + getMetricsRequest.setDuration(duration); + getMetricsRequest.setStep(30); + getMetricsRequest.setRateinterval('30s'); + getMetricsRequest.setFiltersList(['request_count', 'request_duration_millis', 'request_error_count']); + getMetricsRequest.setBylabelsList(byLabels); + getMetricsRequest.setDirection('inbound'); + getMetricsRequest.setReporter(reporter); + getMetricsRequest.setRequestprotocol('http'); + + const getMetricsResponse: GetMetricsResponse = await kialiService.getMetrics(getMetricsRequest, null); + const metrics = getMetricsResponse.toObject().metricsList.filter((metric) => metric.label === filterName); + + const traffic = metrics.filter((metric) => metric.name === 'request_count'); + traffic.push(...metrics.filter((metric) => metric.name === 'request_error_count')); + + setData({ + error: '', + isLoading: false, + responseTime: metrics.filter((metric) => metric.name === 'request_duration_millis'), + traffic: traffic, + }); + } catch (err) { + setData({ error: err.message, isLoading: false, responseTime: [], traffic: [] }); + } + }, [name, duration, sourceNode, targetNode]); + + useEffect(() => { + fetchGraph(); + }, [fetchGraph]); + + if (data.isLoading) { + return ( +
+ +
+ ); + } + + if (data.error) { + return ( + +

{data.error}

+
+ ); + } + + return ( + + {data.traffic.length === 0 ? ( + + ) : ( + + HTTP Request Traffic + + + + + )} + +

 

+

 

+ + {data.responseTime.length === 0 ? ( + + ) : ( + + HTTP Request Response Time + + + + + )} +
+ ); +}; + +export default KialiDetailsEdgeMetricsHTTP; diff --git a/app/src/plugins/kiali/KialiDetailsEdgeMetricsTCP.tsx b/app/src/plugins/kiali/KialiDetailsEdgeMetricsTCP.tsx new file mode 100644 index 000000000..35d3451f9 --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsEdgeMetricsTCP.tsx @@ -0,0 +1,116 @@ +import { Alert, AlertVariant, Card, CardBody, CardTitle, Spinner } from '@patternfly/react-core'; +import React, { useCallback, useEffect, useState } from 'react'; + +import { Edge, GetMetricsRequest, GetMetricsResponse, KialiPromiseClient, Metric, Node } from 'proto/kiali_grpc_web_pb'; +import KialiChart from 'plugins/kiali/KialiChart'; +import { apiURL } from 'utils/constants'; + +// kialiService is the gRPC service to get the metrics for the selected edge. +const kialiService = new KialiPromiseClient(apiURL, null, null); + +interface IDataState { + error: string; + isLoading: boolean; + metrics: Metric.AsObject[]; +} + +interface IKialiDetailsEdgeMetricsTCPProps { + name: string; + duration: number; + edge: Edge.AsObject; + sourceNode: Node.AsObject; + targetNode: Node.AsObject; +} + +const KialiDetailsEdgeMetricsTCP: React.FunctionComponent = ({ + name, + duration, + edge, + sourceNode, + targetNode, +}: IKialiDetailsEdgeMetricsTCPProps) => { + const [data, setData] = useState({ error: '', isLoading: false, metrics: [] }); + + const fetchGraph = useCallback(async (): Promise => { + try { + setData({ error: '', isLoading: true, metrics: [] }); + + let nodeType = 'workloads'; + let nodeName = targetNode.workload; + let byLabels = ['destination_service_name']; + let direction = 'inbound'; + + if (targetNode.nodetype === 'service') { + nodeType = 'services'; + nodeName = targetNode.service; + byLabels = ['source_workload']; + } else if (targetNode.nodetype === 'serviceentry') { + nodeType = 'workloads'; + nodeName = sourceNode.app; + direction = 'outbound'; + } + + let filterName = sourceNode.workload; + + if (sourceNode.nodetype === 'service') { + filterName = sourceNode.service; + } + + const getMetricsRequest = new GetMetricsRequest(); + getMetricsRequest.setName(name); + getMetricsRequest.setNamespace(targetNode.namespace); + getMetricsRequest.setNodetype(nodeType); + getMetricsRequest.setNodename(nodeName); + getMetricsRequest.setQuerytime(Math.floor(Date.now() / 1000)); + getMetricsRequest.setDuration(duration); + getMetricsRequest.setStep(30); + getMetricsRequest.setRateinterval('30s'); + getMetricsRequest.setFiltersList(['tcp_sent', 'tcp_received']); + getMetricsRequest.setBylabelsList(byLabels); + getMetricsRequest.setDirection(direction); + getMetricsRequest.setReporter('source'); + + const getMetricsResponse: GetMetricsResponse = await kialiService.getMetrics(getMetricsRequest, null); + const metrics = getMetricsResponse.toObject().metricsList.filter((metric) => metric.label === filterName); + + setData({ error: '', isLoading: false, metrics: metrics }); + } catch (err) { + setData({ error: err.message, isLoading: false, metrics: [] }); + } + }, [name, duration, sourceNode, targetNode]); + + useEffect(() => { + fetchGraph(); + }, [fetchGraph]); + + if (data.isLoading) { + return ( +
+ +
+ ); + } + + if (data.error) { + return ( + +

{data.error}

+
+ ); + } + + if (data.metrics.length === 0) { + return ; + } + + return ( + + TCP Traffic + + + + + ); +}; + +export default KialiDetailsEdgeMetricsTCP; diff --git a/app/src/plugins/kiali/KialiDetailsEdgeTrafficGRPC.tsx b/app/src/plugins/kiali/KialiDetailsEdgeTrafficGRPC.tsx new file mode 100644 index 000000000..47c0ce15c --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsEdgeTrafficGRPC.tsx @@ -0,0 +1,73 @@ +import { Card, CardBody, CardTitle } from '@patternfly/react-core'; +import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; +import React from 'react'; + +import { Edge } from 'proto/kiali_grpc_web_pb'; + +interface IRates { + grpc: number; + grpcPercentErr: number; + grpcPercentSuc: number; +} + +// getTableDataFromRates is used to generate the data for the traffic table. For that we have to get the rate and errors +// from the "grpc" and "grpcPercentErr" field in the rates map. The percent value for successfull calls is generated, by +// subtracting the percent value for the errors. +const getTableDataFromRates = (ratesMap: [string, string][]): IRates => { + let grpc = 0; + let grpcPercentErr = 0; + + for (const rate of ratesMap) { + if (rate[0] === 'grpc') { + grpc = parseFloat(rate[1]); + } else if (rate[0] === 'grpcPercentErr') { + grpcPercentErr = parseFloat(rate[1]); + } + } + + return { + grpc: grpc, + grpcPercentErr: grpcPercentErr, + grpcPercentSuc: 100 - grpcPercentErr, + }; +}; + +interface IKialiDetailsEdgeTrafficGRPCProps { + edge: Edge.AsObject; +} + +// KialiDetailsEdgeTrafficGRPC is the component to display the traffic details for a grpc edge. This includes the rate +// and the percentage of successful and error calls. +const KialiDetailsEdgeTrafficGRPC: React.FunctionComponent = ({ + edge, +}: IKialiDetailsEdgeTrafficGRPCProps) => { + const rates = edge.traffic?.ratesMap ? getTableDataFromRates(edge.traffic?.ratesMap) : undefined; + + return ( + + GRPC requests per second + + {rates ? ( + + + + Total + Success (%) + Error (%) + + + + + {rates.grpc} + {rates.grpcPercentSuc} + {rates.grpcPercentErr} + + + + ) : null} + + + ); +}; + +export default KialiDetailsEdgeTrafficGRPC; diff --git a/app/src/plugins/kiali/KialiDetailsEdgeTrafficHTTP.tsx b/app/src/plugins/kiali/KialiDetailsEdgeTrafficHTTP.tsx new file mode 100644 index 000000000..4ba7fd991 --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsEdgeTrafficHTTP.tsx @@ -0,0 +1,159 @@ +import { Card, CardBody, CardTitle } from '@patternfly/react-core'; +import { ChartBullet, ChartThemeColor } from '@patternfly/react-charts'; +import React, { useEffect, useRef, useState } from 'react'; +import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; + +import { Edge, Response } from 'proto/kiali_grpc_web_pb'; + +interface IRates { + http: number; + httpPercentErr: number; + httpPercentSuc: number; +} + +// getTableDataFromRates is used to generate the data for the traffic table. For that we have to get the rate and errors +// from the "http" and "httpPercentErr" field in the rates map. The percent value for successfull requests is generated, +// by subtracting the percent value for the errors. +const getTableDataFromRates = (ratesMap: [string, string][]): IRates => { + let http = 0; + let httpPercentErr = 0; + + for (const rate of ratesMap) { + if (rate[0] === 'http') { + http = parseFloat(rate[1]); + } else if (rate[0] === 'httpPercentErr') { + httpPercentErr = parseFloat(rate[1]); + } + } + + return { + http: http, + httpPercentErr: httpPercentErr, + httpPercentSuc: 100 - httpPercentErr, + }; +}; + +// IPercentag is the interface for the bullet chart data. It contains the name of the data point, a value with the real +// percentage for a status code and a y value which is used for rendering. The y value is the value + the value of all +// percentages, which are rendered befor. +interface IPercentag { + name: string; + value: number; + y: number; +} + +const sumFlagPercentages = (flagsMap: [string, string][]): number => { + let sum = 0; + + for (const flag of flagsMap) { + sum = sum + parseFloat(flag[1]); + } + + return sum; +}; + +// getGraphDataFromResponses is used to generate the data for the bullet chart. It contains the distribution of the +// requests by there status code. To be able to render the chart we also have to add a value, which also contains the +// sum of all values, which are rendered befor. +const getGraphDataFromResponses = (responsesMap: [string, Response.AsObject][]): IPercentag[] => { + let http3xxResponse = 0; + let http4xxResponse = 0; + let http5xxResponse = 0; + + for (const response of responsesMap) { + if (response[0].startsWith('3')) { + http3xxResponse = sumFlagPercentages(response[1].flagsMap); + } else if (response[0].startsWith('4')) { + http4xxResponse = sumFlagPercentages(response[1].flagsMap); + } else if (response[0].startsWith('5')) { + http5xxResponse = sumFlagPercentages(response[1].flagsMap); + } + } + + const http2xxResponse = 100 - http3xxResponse - http4xxResponse - http5xxResponse; + + const percentages: IPercentag[] = []; + percentages.push({ name: '2xx', value: http2xxResponse, y: http2xxResponse }); + percentages.push({ name: '3xx', value: http3xxResponse, y: percentages[0].y + http3xxResponse }); + percentages.push({ name: '4xx', value: http4xxResponse, y: percentages[1].y + http4xxResponse }); + percentages.push({ name: '5xx', value: http5xxResponse, y: percentages[2].y + http5xxResponse }); + + return percentages; +}; + +interface IKialiDetailsEdgeTrafficHTTPProps { + edge: Edge.AsObject; +} + +// KialiDetailsEdgeTrafficHTTP is the component for the tabs content for the traffic details of an http edge. It +// displays a table (rate, success, error) and bullet chart with the percent values for the different status codes (2xx, +// 3xx, 4xx, 5xx). +const KialiDetailsEdgeTrafficHTTP: React.FunctionComponent = ({ + edge, +}: IKialiDetailsEdgeTrafficHTTPProps) => { + const refChart = useRef(null); + const [width, setWidth] = useState(0); + const [height, setHeight] = useState(0); + + const rates = edge.traffic?.ratesMap ? getTableDataFromRates(edge.traffic?.ratesMap) : undefined; + const percentages = edge.traffic?.responsesMap ? getGraphDataFromResponses(edge.traffic?.responsesMap) : undefined; + + // useEffect is executed on every render of this component. This is needed, so that we are able to use a width of 100% + // and a static height for the chart. + useEffect(() => { + if (refChart && refChart.current) { + setWidth(refChart.current.getBoundingClientRect().width); + setHeight(refChart.current.getBoundingClientRect().height); + } + }, []); + + return ( + + HTTP requests per second + + {rates ? ( + + + + Total + Success (%) + Error (%) + + + + + {rates.http} + {rates.httpPercentSuc} + {rates.httpPercentErr} + + + + ) : null} +
+ {percentages ? ( + `${datum.name}: ${datum.value.toFixed(2)}`} + maxDomain={{ y: 100 }} + padding={{ + bottom: 10, + left: 10, + right: 10, + top: 0, + }} + primarySegmentedMeasureData={percentages} + primarySegmentedMeasureLegendData={percentages.map((percentage) => { + return { name: percentage.name }; + })} + themeColor={ChartThemeColor.multiOrdered} + width={width} + /> + ) : null} +
+
+
+ ); +}; + +export default KialiDetailsEdgeTrafficHTTP; diff --git a/app/src/plugins/kiali/KialiDetailsNode.tsx b/app/src/plugins/kiali/KialiDetailsNode.tsx new file mode 100644 index 000000000..270e8d187 --- /dev/null +++ b/app/src/plugins/kiali/KialiDetailsNode.tsx @@ -0,0 +1,52 @@ +import { + Alert, + AlertVariant, + Badge, + DrawerActions, + DrawerCloseButton, + DrawerHead, + DrawerPanelBody, + DrawerPanelContent, +} from '@patternfly/react-core'; +import React from 'react'; + +import { Node } from 'proto/kiali_grpc_web_pb'; +import { getTitle } from 'plugins/kiali/helpers'; + +interface IKialiDetailsNodeProps { + name: string; + duration: number; + node: Node.AsObject; + close: () => void; +} + +const KialiDetailsNode: React.FunctionComponent = ({ + name, + duration, + node, + close, +}: IKialiDetailsNodeProps) => { + const title = getTitle(node); + + return ( + + + + + {title.badge} + {title.title} + + + + + + + + + + + + ); +}; + +export default KialiDetailsNode; diff --git a/app/src/plugins/kiali/KialiGraph.tsx b/app/src/plugins/kiali/KialiGraph.tsx index 2a44106e8..4b8e3b3a9 100644 --- a/app/src/plugins/kiali/KialiGraph.tsx +++ b/app/src/plugins/kiali/KialiGraph.tsx @@ -4,7 +4,9 @@ import cytoscape from 'cytoscape'; // eslint-disable-next-line @typescript-eslint/no-explicit-any import dagre from 'cytoscape-dagre'; -import { Node } from 'proto/kiali_grpc_web_pb'; +import { Node, NodeWrapper } from 'proto/kiali_grpc_web_pb'; +import KialiDetailsEdge from 'plugins/kiali/KialiDetailsEdge'; +import KialiDetailsNode from 'plugins/kiali/KialiDetailsNode'; import 'plugins/kiali/kiali.css'; @@ -100,6 +102,13 @@ const styleSheet: cytoscape.Stylesheet[] = [ 'target-arrow-color': '#0066cc', }, }, + { + selector: 'edge[edgetype="grpc"]', + style: { + 'line-color': '#009596', + 'target-arrow-color': '#009596', + }, + }, { selector: 'edge[edgetype="http"]', style: { @@ -171,22 +180,53 @@ const nodeLabel = (node: Node.AsObject): string => { }; interface IKialiGraphProps { + name: string; + duration: number; edges: cytoscape.ElementDefinition[]; nodes: cytoscape.ElementDefinition[]; + setDetails?: (details: React.ReactNode) => void; } -const KialiGraph: React.FunctionComponent = ({ edges, nodes }: IKialiGraphProps) => { +const KialiGraph: React.FunctionComponent = ({ + name, + duration, + edges, + nodes, + setDetails, +}: IKialiGraphProps) => { const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); const containerRef = useRef(null); const graphRef = useRef(null); - const onTap = useCallback((event: cytoscape.EventObject): void => { - const ele = event.target; - const data = ele.data(); - // TODO: Show drawer with metrics for the selected edge / node. - console.log(data); - }, []); + // onTap is used to display the details drawer for a selected edge/node. When the user clicks on a edge/node the onTap + // function is called. We then check, which type the clicked element has and display the corresponding details + // component in the drawer panel. + const onTap = useCallback( + (event: cytoscape.EventObject): void => { + const ele = event.target; + const data = ele.data(); + + if (data.nodetype && setDetails) { + setDetails( + setDetails(undefined)} />, + ); + } + + if (data.edgetype && setDetails) { + setDetails( + setDetails(undefined)} + />, + ); + } + }, + [name, duration, nodes, setDetails], + ); const cyCallback = useCallback( (cy: cytoscape.Core): void => { diff --git a/app/src/plugins/kiali/KialiGraphWrapper.tsx b/app/src/plugins/kiali/KialiGraphWrapper.tsx index 4ee37d880..d23afc171 100644 --- a/app/src/plugins/kiali/KialiGraphWrapper.tsx +++ b/app/src/plugins/kiali/KialiGraphWrapper.tsx @@ -26,12 +26,14 @@ interface IKialiGraphWrapperProps { name: string; namespaces: string[]; duration: number; + setDetails?: (details: React.ReactNode) => void; } const KialiGraphWrapper: React.FunctionComponent = ({ name, namespaces, duration, + setDetails, }: IKialiGraphWrapperProps) => { const [data, setData] = useState({ edges: [], error: '', isLoading: false, nodes: [] }); @@ -89,8 +91,11 @@ const KialiGraphWrapper: React.FunctionComponent = ({ return (
); diff --git a/app/src/plugins/kiali/KialiPage.tsx b/app/src/plugins/kiali/KialiPage.tsx index f9ae77412..6d9341ce2 100644 --- a/app/src/plugins/kiali/KialiPage.tsx +++ b/app/src/plugins/kiali/KialiPage.tsx @@ -18,6 +18,7 @@ const KialiPage: React.FunctionComponent = ({ name, descriptio const history = useHistory(); const location = useLocation(); const [options, setOptions] = useState(getOptionsFromSearch(location.search)); + const [details, setDetails] = useState(undefined); const changeOptions = (opts: IKialiOptions): void => { const namespaces = opts.namespaces ? opts.namespaces.map((field) => `&namespace=${field}`) : undefined; @@ -47,12 +48,17 @@ const KialiPage: React.FunctionComponent = ({ name, descriptio /> - - + + {options.namespaces.length > 0 ? ( - + ) : null} diff --git a/app/src/plugins/kiali/helpers.ts b/app/src/plugins/kiali/helpers.ts index 24d343ce0..d5927edfd 100644 --- a/app/src/plugins/kiali/helpers.ts +++ b/app/src/plugins/kiali/helpers.ts @@ -1,3 +1,5 @@ +import { Node } from 'proto/kiali_grpc_web_pb'; + // IKialiOptions is the interface for all available options to get the graph from Kiali. export interface IKialiOptions { duration: number; @@ -15,3 +17,20 @@ export const getOptionsFromSearch = (search: string): IKialiOptions => { namespaces: namespaces, }; }; + +export interface ITitle { + badge: string; + title: string; +} + +// getTitle returns the title of a node for the details view. The title contains a name (title) and a badge, which is +// used to display the node type. The node type can be SE (ServiceEntry), S (Service) or A (Application). +export const getTitle = (node: Node.AsObject): ITitle => { + if (node.nodetype === 'serviceentry') { + return { badge: 'SE', title: node.nodelabel }; + } else if (node.nodetype === 'service') { + return { badge: 'S', title: node.service }; + } + + return { badge: 'A', title: node.app }; +}; diff --git a/app/src/plugins/kiali/kiali.css b/app/src/plugins/kiali/kiali.css index dad09a287..15ffe977c 100644 --- a/app/src/plugins/kiali/kiali.css +++ b/app/src/plugins/kiali/kiali.css @@ -1,3 +1,5 @@ +/* kobsio-kiali-label + * The kobsio-kiali-label classes are used for the node labels in the Kiali graph. */ .kobsio-kiali-label { border-radius: 3px; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 2px 8px 0 rgba(0, 0, 0, 0.19); @@ -39,7 +41,7 @@ } .kobsio-kiali-label-badge { - background-color: var(--pf-global--palette--blue-200); + background-color: var(--pf-global--primary-color--100); margin-right: 5px; min-width: 24px; padding-left: 0px; diff --git a/app/src/proto/kiali_grpc_web_pb.js b/app/src/proto/kiali_grpc_web_pb.js index eee675bed..3aac39208 100644 --- a/app/src/proto/kiali_grpc_web_pb.js +++ b/app/src/proto/kiali_grpc_web_pb.js @@ -231,5 +231,85 @@ proto.plugins.kiali.KialiPromiseClient.prototype.getGraph = }; +/** + * @const + * @type {!grpc.web.MethodDescriptor< + * !proto.plugins.kiali.GetMetricsRequest, + * !proto.plugins.kiali.GetMetricsResponse>} + */ +const methodDescriptor_Kiali_GetMetrics = new grpc.web.MethodDescriptor( + '/plugins.kiali.Kiali/GetMetrics', + grpc.web.MethodType.UNARY, + proto.plugins.kiali.GetMetricsRequest, + proto.plugins.kiali.GetMetricsResponse, + /** + * @param {!proto.plugins.kiali.GetMetricsRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.plugins.kiali.GetMetricsResponse.deserializeBinary +); + + +/** + * @const + * @type {!grpc.web.AbstractClientBase.MethodInfo< + * !proto.plugins.kiali.GetMetricsRequest, + * !proto.plugins.kiali.GetMetricsResponse>} + */ +const methodInfo_Kiali_GetMetrics = new grpc.web.AbstractClientBase.MethodInfo( + proto.plugins.kiali.GetMetricsResponse, + /** + * @param {!proto.plugins.kiali.GetMetricsRequest} request + * @return {!Uint8Array} + */ + function(request) { + return request.serializeBinary(); + }, + proto.plugins.kiali.GetMetricsResponse.deserializeBinary +); + + +/** + * @param {!proto.plugins.kiali.GetMetricsRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @param {function(?grpc.web.Error, ?proto.plugins.kiali.GetMetricsResponse)} + * callback The callback function(error, response) + * @return {!grpc.web.ClientReadableStream|undefined} + * The XHR Node Readable Stream + */ +proto.plugins.kiali.KialiClient.prototype.getMetrics = + function(request, metadata, callback) { + return this.client_.rpcCall(this.hostname_ + + '/plugins.kiali.Kiali/GetMetrics', + request, + metadata || {}, + methodDescriptor_Kiali_GetMetrics, + callback); +}; + + +/** + * @param {!proto.plugins.kiali.GetMetricsRequest} request The + * request proto + * @param {?Object} metadata User defined + * call metadata + * @return {!Promise} + * Promise that resolves to the response + */ +proto.plugins.kiali.KialiPromiseClient.prototype.getMetrics = + function(request, metadata) { + return this.client_.unaryCall(this.hostname_ + + '/plugins.kiali.Kiali/GetMetrics', + request, + metadata || {}, + methodDescriptor_Kiali_GetMetrics); +}; + + module.exports = proto.plugins.kiali; diff --git a/app/src/proto/kiali_pb.d.ts b/app/src/proto/kiali_pb.d.ts index 021402c5a..122a486b1 100644 --- a/app/src/proto/kiali_pb.d.ts +++ b/app/src/proto/kiali_pb.d.ts @@ -507,3 +507,155 @@ export namespace Response { } } +export class GetMetricsRequest extends jspb.Message { + getName(): string; + setName(value: string): void; + + getNamespace(): string; + setNamespace(value: string): void; + + getNodetype(): string; + setNodetype(value: string): void; + + getNodename(): string; + setNodename(value: string): void; + + getQuerytime(): number; + setQuerytime(value: number): void; + + getDuration(): number; + setDuration(value: number): void; + + getStep(): number; + setStep(value: number): void; + + getRateinterval(): string; + setRateinterval(value: string): void; + + clearFiltersList(): void; + getFiltersList(): Array; + setFiltersList(value: Array): void; + addFilters(value: string, index?: number): string; + + clearBylabelsList(): void; + getBylabelsList(): Array; + setBylabelsList(value: Array): void; + addBylabels(value: string, index?: number): string; + + getDirection(): string; + setDirection(value: string): void; + + getReporter(): string; + setReporter(value: string): void; + + getRequestprotocol(): string; + setRequestprotocol(value: string): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetMetricsRequest.AsObject; + static toObject(includeInstance: boolean, msg: GetMetricsRequest): GetMetricsRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetMetricsRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetMetricsRequest; + static deserializeBinaryFromReader(message: GetMetricsRequest, reader: jspb.BinaryReader): GetMetricsRequest; +} + +export namespace GetMetricsRequest { + export type AsObject = { + name: string, + namespace: string, + nodetype: string, + nodename: string, + querytime: number, + duration: number, + step: number, + rateinterval: string, + filtersList: Array, + bylabelsList: Array, + direction: string, + reporter: string, + requestprotocol: string, + } +} + +export class GetMetricsResponse extends jspb.Message { + clearMetricsList(): void; + getMetricsList(): Array; + setMetricsList(value: Array): void; + addMetrics(value?: Metric, index?: number): Metric; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): GetMetricsResponse.AsObject; + static toObject(includeInstance: boolean, msg: GetMetricsResponse): GetMetricsResponse.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: GetMetricsResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): GetMetricsResponse; + static deserializeBinaryFromReader(message: GetMetricsResponse, reader: jspb.BinaryReader): GetMetricsResponse; +} + +export namespace GetMetricsResponse { + export type AsObject = { + metricsList: Array, + } +} + +export class Metric extends jspb.Message { + getName(): string; + setName(value: string): void; + + getLabel(): string; + setLabel(value: string): void; + + getStat(): string; + setStat(value: string): void; + + clearDataList(): void; + getDataList(): Array; + setDataList(value: Array): void; + addData(value?: Data, index?: number): Data; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): Metric.AsObject; + static toObject(includeInstance: boolean, msg: Metric): Metric.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: Metric, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): Metric; + static deserializeBinaryFromReader(message: Metric, reader: jspb.BinaryReader): Metric; +} + +export namespace Metric { + export type AsObject = { + name: string, + label: string, + stat: string, + dataList: Array, + } +} + +export class Data extends jspb.Message { + getX(): number; + setX(value: number): void; + + getY(): number; + setY(value: number): void; + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): Data.AsObject; + static toObject(includeInstance: boolean, msg: Data): Data.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: Data, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): Data; + static deserializeBinaryFromReader(message: Data, reader: jspb.BinaryReader): Data; +} + +export namespace Data { + export type AsObject = { + x: number, + y: number, + } +} + diff --git a/app/src/proto/kiali_pb.js b/app/src/proto/kiali_pb.js index 76035e7c9..fed876802 100644 --- a/app/src/proto/kiali_pb.js +++ b/app/src/proto/kiali_pb.js @@ -14,13 +14,17 @@ var jspb = require('google-protobuf'); var goog = jspb; var global = Function('return this')(); +goog.exportSymbol('proto.plugins.kiali.Data', null, global); goog.exportSymbol('proto.plugins.kiali.Edge', null, global); goog.exportSymbol('proto.plugins.kiali.EdgeWrapper', null, global); goog.exportSymbol('proto.plugins.kiali.Elements', null, global); goog.exportSymbol('proto.plugins.kiali.GetGraphRequest', null, global); goog.exportSymbol('proto.plugins.kiali.GetGraphResponse', null, global); +goog.exportSymbol('proto.plugins.kiali.GetMetricsRequest', null, global); +goog.exportSymbol('proto.plugins.kiali.GetMetricsResponse', null, global); goog.exportSymbol('proto.plugins.kiali.GetNamespacesRequest', null, global); goog.exportSymbol('proto.plugins.kiali.GetNamespacesResponse', null, global); +goog.exportSymbol('proto.plugins.kiali.Metric', null, global); goog.exportSymbol('proto.plugins.kiali.Namespace', null, global); goog.exportSymbol('proto.plugins.kiali.Node', null, global); goog.exportSymbol('proto.plugins.kiali.NodeWrapper', null, global); @@ -322,6 +326,90 @@ if (goog.DEBUG && !COMPILED) { */ proto.plugins.kiali.Response.displayName = 'proto.plugins.kiali.Response'; } +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.plugins.kiali.GetMetricsRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.plugins.kiali.GetMetricsRequest.repeatedFields_, null); +}; +goog.inherits(proto.plugins.kiali.GetMetricsRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.plugins.kiali.GetMetricsRequest.displayName = 'proto.plugins.kiali.GetMetricsRequest'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.plugins.kiali.GetMetricsResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.plugins.kiali.GetMetricsResponse.repeatedFields_, null); +}; +goog.inherits(proto.plugins.kiali.GetMetricsResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.plugins.kiali.GetMetricsResponse.displayName = 'proto.plugins.kiali.GetMetricsResponse'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.plugins.kiali.Metric = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.plugins.kiali.Metric.repeatedFields_, null); +}; +goog.inherits(proto.plugins.kiali.Metric, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.plugins.kiali.Metric.displayName = 'proto.plugins.kiali.Metric'; +} +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.plugins.kiali.Data = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.plugins.kiali.Data, jspb.Message); +if (goog.DEBUG && !COMPILED) { + /** + * @public + * @override + */ + proto.plugins.kiali.Data.displayName = 'proto.plugins.kiali.Data'; +} @@ -4002,4 +4090,1109 @@ proto.plugins.kiali.Response.prototype.clearHostsMap = function() { return this;}; + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.plugins.kiali.GetMetricsRequest.repeatedFields_ = [9,10]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.toObject = function(opt_includeInstance) { + return proto.plugins.kiali.GetMetricsRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.plugins.kiali.GetMetricsRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.plugins.kiali.GetMetricsRequest.toObject = function(includeInstance, msg) { + var f, obj = { + name: jspb.Message.getFieldWithDefault(msg, 1, ""), + namespace: jspb.Message.getFieldWithDefault(msg, 2, ""), + nodetype: jspb.Message.getFieldWithDefault(msg, 3, ""), + nodename: jspb.Message.getFieldWithDefault(msg, 4, ""), + querytime: jspb.Message.getFieldWithDefault(msg, 5, 0), + duration: jspb.Message.getFieldWithDefault(msg, 6, 0), + step: jspb.Message.getFieldWithDefault(msg, 7, 0), + rateinterval: jspb.Message.getFieldWithDefault(msg, 8, ""), + filtersList: (f = jspb.Message.getRepeatedField(msg, 9)) == null ? undefined : f, + bylabelsList: (f = jspb.Message.getRepeatedField(msg, 10)) == null ? undefined : f, + direction: jspb.Message.getFieldWithDefault(msg, 11, ""), + reporter: jspb.Message.getFieldWithDefault(msg, 12, ""), + requestprotocol: jspb.Message.getFieldWithDefault(msg, 13, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.plugins.kiali.GetMetricsRequest} + */ +proto.plugins.kiali.GetMetricsRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.plugins.kiali.GetMetricsRequest; + return proto.plugins.kiali.GetMetricsRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.plugins.kiali.GetMetricsRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.plugins.kiali.GetMetricsRequest} + */ +proto.plugins.kiali.GetMetricsRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setName(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setNamespace(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setNodetype(value); + break; + case 4: + var value = /** @type {string} */ (reader.readString()); + msg.setNodename(value); + break; + case 5: + var value = /** @type {number} */ (reader.readInt64()); + msg.setQuerytime(value); + break; + case 6: + var value = /** @type {number} */ (reader.readInt64()); + msg.setDuration(value); + break; + case 7: + var value = /** @type {number} */ (reader.readInt64()); + msg.setStep(value); + break; + case 8: + var value = /** @type {string} */ (reader.readString()); + msg.setRateinterval(value); + break; + case 9: + var value = /** @type {string} */ (reader.readString()); + msg.addFilters(value); + break; + case 10: + var value = /** @type {string} */ (reader.readString()); + msg.addBylabels(value); + break; + case 11: + var value = /** @type {string} */ (reader.readString()); + msg.setDirection(value); + break; + case 12: + var value = /** @type {string} */ (reader.readString()); + msg.setReporter(value); + break; + case 13: + var value = /** @type {string} */ (reader.readString()); + msg.setRequestprotocol(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.plugins.kiali.GetMetricsRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.plugins.kiali.GetMetricsRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.plugins.kiali.GetMetricsRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getName(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getNamespace(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getNodetype(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } + f = message.getNodename(); + if (f.length > 0) { + writer.writeString( + 4, + f + ); + } + f = message.getQuerytime(); + if (f !== 0) { + writer.writeInt64( + 5, + f + ); + } + f = message.getDuration(); + if (f !== 0) { + writer.writeInt64( + 6, + f + ); + } + f = message.getStep(); + if (f !== 0) { + writer.writeInt64( + 7, + f + ); + } + f = message.getRateinterval(); + if (f.length > 0) { + writer.writeString( + 8, + f + ); + } + f = message.getFiltersList(); + if (f.length > 0) { + writer.writeRepeatedString( + 9, + f + ); + } + f = message.getBylabelsList(); + if (f.length > 0) { + writer.writeRepeatedString( + 10, + f + ); + } + f = message.getDirection(); + if (f.length > 0) { + writer.writeString( + 11, + f + ); + } + f = message.getReporter(); + if (f.length > 0) { + writer.writeString( + 12, + f + ); + } + f = message.getRequestprotocol(); + if (f.length > 0) { + writer.writeString( + 13, + f + ); + } +}; + + +/** + * optional string name = 1; + * @return {string} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setName = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string namespace = 2; + * @return {string} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getNamespace = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setNamespace = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional string nodeType = 3; + * @return {string} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getNodetype = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setNodetype = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); +}; + + +/** + * optional string nodeName = 4; + * @return {string} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getNodename = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setNodename = function(value) { + return jspb.Message.setProto3StringField(this, 4, value); +}; + + +/** + * optional int64 queryTime = 5; + * @return {number} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getQuerytime = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setQuerytime = function(value) { + return jspb.Message.setProto3IntField(this, 5, value); +}; + + +/** + * optional int64 duration = 6; + * @return {number} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getDuration = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setDuration = function(value) { + return jspb.Message.setProto3IntField(this, 6, value); +}; + + +/** + * optional int64 step = 7; + * @return {number} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getStep = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setStep = function(value) { + return jspb.Message.setProto3IntField(this, 7, value); +}; + + +/** + * optional string rateInterval = 8; + * @return {string} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getRateinterval = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 8, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setRateinterval = function(value) { + return jspb.Message.setProto3StringField(this, 8, value); +}; + + +/** + * repeated string filters = 9; + * @return {!Array} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getFiltersList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 9)); +}; + + +/** + * @param {!Array} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setFiltersList = function(value) { + return jspb.Message.setField(this, 9, value || []); +}; + + +/** + * @param {string} value + * @param {number=} opt_index + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.addFilters = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 9, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.clearFiltersList = function() { + return this.setFiltersList([]); +}; + + +/** + * repeated string byLabels = 10; + * @return {!Array} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getBylabelsList = function() { + return /** @type {!Array} */ (jspb.Message.getRepeatedField(this, 10)); +}; + + +/** + * @param {!Array} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setBylabelsList = function(value) { + return jspb.Message.setField(this, 10, value || []); +}; + + +/** + * @param {string} value + * @param {number=} opt_index + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.addBylabels = function(value, opt_index) { + return jspb.Message.addToRepeatedField(this, 10, value, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.clearBylabelsList = function() { + return this.setBylabelsList([]); +}; + + +/** + * optional string direction = 11; + * @return {string} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getDirection = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 11, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setDirection = function(value) { + return jspb.Message.setProto3StringField(this, 11, value); +}; + + +/** + * optional string reporter = 12; + * @return {string} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getReporter = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 12, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setReporter = function(value) { + return jspb.Message.setProto3StringField(this, 12, value); +}; + + +/** + * optional string requestProtocol = 13; + * @return {string} + */ +proto.plugins.kiali.GetMetricsRequest.prototype.getRequestprotocol = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 13, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.GetMetricsRequest} returns this + */ +proto.plugins.kiali.GetMetricsRequest.prototype.setRequestprotocol = function(value) { + return jspb.Message.setProto3StringField(this, 13, value); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.plugins.kiali.GetMetricsResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.plugins.kiali.GetMetricsResponse.prototype.toObject = function(opt_includeInstance) { + return proto.plugins.kiali.GetMetricsResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.plugins.kiali.GetMetricsResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.plugins.kiali.GetMetricsResponse.toObject = function(includeInstance, msg) { + var f, obj = { + metricsList: jspb.Message.toObjectList(msg.getMetricsList(), + proto.plugins.kiali.Metric.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.plugins.kiali.GetMetricsResponse} + */ +proto.plugins.kiali.GetMetricsResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.plugins.kiali.GetMetricsResponse; + return proto.plugins.kiali.GetMetricsResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.plugins.kiali.GetMetricsResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.plugins.kiali.GetMetricsResponse} + */ +proto.plugins.kiali.GetMetricsResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.plugins.kiali.Metric; + reader.readMessage(value,proto.plugins.kiali.Metric.deserializeBinaryFromReader); + msg.addMetrics(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.plugins.kiali.GetMetricsResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.plugins.kiali.GetMetricsResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.plugins.kiali.GetMetricsResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.plugins.kiali.GetMetricsResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMetricsList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 1, + f, + proto.plugins.kiali.Metric.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated Metric metrics = 1; + * @return {!Array} + */ +proto.plugins.kiali.GetMetricsResponse.prototype.getMetricsList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.plugins.kiali.Metric, 1)); +}; + + +/** + * @param {!Array} value + * @return {!proto.plugins.kiali.GetMetricsResponse} returns this +*/ +proto.plugins.kiali.GetMetricsResponse.prototype.setMetricsList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 1, value); +}; + + +/** + * @param {!proto.plugins.kiali.Metric=} opt_value + * @param {number=} opt_index + * @return {!proto.plugins.kiali.Metric} + */ +proto.plugins.kiali.GetMetricsResponse.prototype.addMetrics = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.plugins.kiali.Metric, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.plugins.kiali.GetMetricsResponse} returns this + */ +proto.plugins.kiali.GetMetricsResponse.prototype.clearMetricsList = function() { + return this.setMetricsList([]); +}; + + + +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.plugins.kiali.Metric.repeatedFields_ = [4]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.plugins.kiali.Metric.prototype.toObject = function(opt_includeInstance) { + return proto.plugins.kiali.Metric.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.plugins.kiali.Metric} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.plugins.kiali.Metric.toObject = function(includeInstance, msg) { + var f, obj = { + name: jspb.Message.getFieldWithDefault(msg, 1, ""), + label: jspb.Message.getFieldWithDefault(msg, 2, ""), + stat: jspb.Message.getFieldWithDefault(msg, 3, ""), + dataList: jspb.Message.toObjectList(msg.getDataList(), + proto.plugins.kiali.Data.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.plugins.kiali.Metric} + */ +proto.plugins.kiali.Metric.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.plugins.kiali.Metric; + return proto.plugins.kiali.Metric.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.plugins.kiali.Metric} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.plugins.kiali.Metric} + */ +proto.plugins.kiali.Metric.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {string} */ (reader.readString()); + msg.setName(value); + break; + case 2: + var value = /** @type {string} */ (reader.readString()); + msg.setLabel(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setStat(value); + break; + case 4: + var value = new proto.plugins.kiali.Data; + reader.readMessage(value,proto.plugins.kiali.Data.deserializeBinaryFromReader); + msg.addData(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.plugins.kiali.Metric.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.plugins.kiali.Metric.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.plugins.kiali.Metric} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.plugins.kiali.Metric.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getName(); + if (f.length > 0) { + writer.writeString( + 1, + f + ); + } + f = message.getLabel(); + if (f.length > 0) { + writer.writeString( + 2, + f + ); + } + f = message.getStat(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } + f = message.getDataList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 4, + f, + proto.plugins.kiali.Data.serializeBinaryToWriter + ); + } +}; + + +/** + * optional string name = 1; + * @return {string} + */ +proto.plugins.kiali.Metric.prototype.getName = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.Metric} returns this + */ +proto.plugins.kiali.Metric.prototype.setName = function(value) { + return jspb.Message.setProto3StringField(this, 1, value); +}; + + +/** + * optional string label = 2; + * @return {string} + */ +proto.plugins.kiali.Metric.prototype.getLabel = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.Metric} returns this + */ +proto.plugins.kiali.Metric.prototype.setLabel = function(value) { + return jspb.Message.setProto3StringField(this, 2, value); +}; + + +/** + * optional string stat = 3; + * @return {string} + */ +proto.plugins.kiali.Metric.prototype.getStat = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** + * @param {string} value + * @return {!proto.plugins.kiali.Metric} returns this + */ +proto.plugins.kiali.Metric.prototype.setStat = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); +}; + + +/** + * repeated Data data = 4; + * @return {!Array} + */ +proto.plugins.kiali.Metric.prototype.getDataList = function() { + return /** @type{!Array} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.plugins.kiali.Data, 4)); +}; + + +/** + * @param {!Array} value + * @return {!proto.plugins.kiali.Metric} returns this +*/ +proto.plugins.kiali.Metric.prototype.setDataList = function(value) { + return jspb.Message.setRepeatedWrapperField(this, 4, value); +}; + + +/** + * @param {!proto.plugins.kiali.Data=} opt_value + * @param {number=} opt_index + * @return {!proto.plugins.kiali.Data} + */ +proto.plugins.kiali.Metric.prototype.addData = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 4, opt_value, proto.plugins.kiali.Data, opt_index); +}; + + +/** + * Clears the list making it empty but non-null. + * @return {!proto.plugins.kiali.Metric} returns this + */ +proto.plugins.kiali.Metric.prototype.clearDataList = function() { + return this.setDataList([]); +}; + + + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * Optional fields that are not set will be set to undefined. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * net/proto2/compiler/js/internal/generator.cc#kKeyword. + * @param {boolean=} opt_includeInstance Deprecated. whether to include the + * JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @return {!Object} + */ +proto.plugins.kiali.Data.prototype.toObject = function(opt_includeInstance) { + return proto.plugins.kiali.Data.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Deprecated. Whether to include + * the JSPB instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.plugins.kiali.Data} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.plugins.kiali.Data.toObject = function(includeInstance, msg) { + var f, obj = { + x: jspb.Message.getFieldWithDefault(msg, 1, 0), + y: jspb.Message.getFloatingPointFieldWithDefault(msg, 2, 0.0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.plugins.kiali.Data} + */ +proto.plugins.kiali.Data.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.plugins.kiali.Data; + return proto.plugins.kiali.Data.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.plugins.kiali.Data} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.plugins.kiali.Data} + */ +proto.plugins.kiali.Data.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt64()); + msg.setX(value); + break; + case 2: + var value = /** @type {number} */ (reader.readDouble()); + msg.setY(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.plugins.kiali.Data.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.plugins.kiali.Data.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.plugins.kiali.Data} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.plugins.kiali.Data.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getX(); + if (f !== 0) { + writer.writeInt64( + 1, + f + ); + } + f = message.getY(); + if (f !== 0.0) { + writer.writeDouble( + 2, + f + ); + } +}; + + +/** + * optional int64 x = 1; + * @return {number} + */ +proto.plugins.kiali.Data.prototype.getX = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** + * @param {number} value + * @return {!proto.plugins.kiali.Data} returns this + */ +proto.plugins.kiali.Data.prototype.setX = function(value) { + return jspb.Message.setProto3IntField(this, 1, value); +}; + + +/** + * optional double y = 2; + * @return {number} + */ +proto.plugins.kiali.Data.prototype.getY = function() { + return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 2, 0.0)); +}; + + +/** + * @param {number} value + * @return {!proto.plugins.kiali.Data} returns this + */ +proto.plugins.kiali.Data.prototype.setY = function(value) { + return jspb.Message.setProto3FloatField(this, 2, value); +}; + + goog.object.extend(exports, proto.plugins.kiali); diff --git a/app/src/proto/kiali_pb_service.d.ts b/app/src/proto/kiali_pb_service.d.ts index 6d49bba78..394afe29a 100644 --- a/app/src/proto/kiali_pb_service.d.ts +++ b/app/src/proto/kiali_pb_service.d.ts @@ -22,10 +22,20 @@ type KialiGetGraph = { readonly responseType: typeof kiali_pb.GetGraphResponse; }; +type KialiGetMetrics = { + readonly methodName: string; + readonly service: typeof Kiali; + readonly requestStream: false; + readonly responseStream: false; + readonly requestType: typeof kiali_pb.GetMetricsRequest; + readonly responseType: typeof kiali_pb.GetMetricsResponse; +}; + export class Kiali { static readonly serviceName: string; static readonly GetNamespaces: KialiGetNamespaces; static readonly GetGraph: KialiGetGraph; + static readonly GetMetrics: KialiGetMetrics; } export type ServiceError = { message: string, code: number; metadata: grpc.Metadata } @@ -78,5 +88,14 @@ export class KialiClient { requestMessage: kiali_pb.GetGraphRequest, callback: (error: ServiceError|null, responseMessage: kiali_pb.GetGraphResponse|null) => void ): UnaryResponse; + getMetrics( + requestMessage: kiali_pb.GetMetricsRequest, + metadata: grpc.Metadata, + callback: (error: ServiceError|null, responseMessage: kiali_pb.GetMetricsResponse|null) => void + ): UnaryResponse; + getMetrics( + requestMessage: kiali_pb.GetMetricsRequest, + callback: (error: ServiceError|null, responseMessage: kiali_pb.GetMetricsResponse|null) => void + ): UnaryResponse; } diff --git a/app/src/proto/kiali_pb_service.js b/app/src/proto/kiali_pb_service.js index 8d1269425..7f6af3003 100644 --- a/app/src/proto/kiali_pb_service.js +++ b/app/src/proto/kiali_pb_service.js @@ -28,6 +28,15 @@ Kiali.GetGraph = { responseType: kiali_pb.GetGraphResponse }; +Kiali.GetMetrics = { + methodName: "GetMetrics", + service: Kiali, + requestStream: false, + responseStream: false, + requestType: kiali_pb.GetMetricsRequest, + responseType: kiali_pb.GetMetricsResponse +}; + exports.Kiali = Kiali; function KialiClient(serviceHost, options) { @@ -97,5 +106,36 @@ KialiClient.prototype.getGraph = function getGraph(requestMessage, metadata, cal }; }; +KialiClient.prototype.getMetrics = function getMetrics(requestMessage, metadata, callback) { + if (arguments.length === 2) { + callback = arguments[1]; + } + var client = grpc.unary(Kiali.GetMetrics, { + request: requestMessage, + host: this.serviceHost, + metadata: metadata, + transport: this.options.transport, + debug: this.options.debug, + onEnd: function (response) { + if (callback) { + if (response.status !== grpc.Code.OK) { + var err = new Error(response.statusMessage); + err.code = response.status; + err.metadata = response.trailers; + callback(err, null); + } else { + callback(null, response.message); + } + } + } + }); + return { + cancel: function () { + callback = null; + client.close(); + } + }; +}; + exports.KialiClient = KialiClient; diff --git a/deploy/docker/kobs/config.yaml b/deploy/docker/kobs/config.yaml index 3b6251ebc..bb5b368f8 100644 --- a/deploy/docker/kobs/config.yaml +++ b/deploy/docker/kobs/config.yaml @@ -36,12 +36,11 @@ kiali: password: token: traffic: - enabled: true - failure: 95 - degraded: 99 + degraded: 1 + failure: 5 -opsgenie: - - name: Opsgenie - description: On-call and alert management to keep services always on. - apiUrl: api.eu.opsgenie.com - apiKey: ${OPSGENIE_API_KEY} +# opsgenie: +# - name: Opsgenie +# description: On-call and alert management to keep services always on. +# apiUrl: api.eu.opsgenie.com +# apiKey: ${OPSGENIE_API_KEY} diff --git a/docs/configuration/plugins.md b/docs/configuration/plugins.md index 07691142d..8c665f4cf 100644 --- a/docs/configuration/plugins.md +++ b/docs/configuration/plugins.md @@ -63,9 +63,8 @@ kiali: description: Service mesh management for Istio. address: https://kiali.kobs.io traffic: - enabled: true - failure: 95 - degraded: 99 + degraded: 1 + failure: 5 ``` | Field | Type | Description | Required | @@ -76,9 +75,8 @@ kiali: | username | string | Username to access a Kiali instance via basic authentication. | No | | password | string | Password to access a Kiali instance via basic authentication. | No | | token | string | Token to access a Kiali instance via token based authentication. | No | -| traffic.enabled | boolean | Enable traffic visualization in the Kiali graph. | No | -| traffic.failure | number | Threshold to mark edges with failures. This must be a number between 0 and 100. | No | -| traffic.degraded | number | Threshold to mark edges with degraded performance. This must be a number between 0 and 100. | No | +| traffic.failure | number | Threshold to mark edges with failures. This must be a number between 0 and 100. The default value is 5. | No | +| traffic.degraded | number | Threshold to mark edges with degraded performance. This must be a number between 0 and 100. The default value is 1. | No | ## Opsgenie diff --git a/pkg/api/plugins/kiali/kiali.go b/pkg/api/plugins/kiali/kiali.go index 65c4fca88..aea2dcee0 100644 --- a/pkg/api/plugins/kiali/kiali.go +++ b/pkg/api/plugins/kiali/kiali.go @@ -32,9 +32,15 @@ type Config struct { } type Traffic struct { - Enabled bool `yaml:"enabled"` - Failure float64 `yaml:"failure"` Degraded float64 `yaml:"degraded"` + Failure float64 `yaml:"failure"` +} + +type Metric struct { + Labels map[string]string `json:"labels"` + Datapoints [][]interface{} `json:"datapoints"` + Stat string `json:"stat"` + Name string `json:"name"` } type Kiali struct { @@ -170,26 +176,36 @@ func (k *Kiali) GetGraph(ctx context.Context, getGraphRequest *kialiProto.GetGra edgeLabel = httpRate + "req/s" } - if httpPercent, ok := response.Elements.Edges[i].Data.Traffic.Rates["httpPercentReq"]; ok { + if httpPercentErr, ok := response.Elements.Edges[i].Data.Traffic.Rates["httpPercentErr"]; ok { if edgeLabel == "" { - edgeLabel = httpPercent + edgeLabel = httpPercentErr + "%" } else { - edgeLabel = edgeLabel + "\n" + httpPercent + "%" + edgeLabel = edgeLabel + "\n" + httpPercentErr + "%" } - if instance.traffic.Enabled { - httpPercentFloat, err := strconv.ParseFloat(httpPercent, 64) - if err == nil { - if httpPercentFloat <= instance.traffic.Failure { - edgeType = "httpfailure" - } else if httpPercentFloat <= instance.traffic.Degraded { - edgeType = "httpdegraded" - } else { - edgeType = "httphealthy" - } + httpPercentErrFloat, err := strconv.ParseFloat(httpPercentErr, 64) + if err == nil { + if httpPercentErrFloat >= instance.traffic.Failure { + edgeType = "httpfailure" + } else if httpPercentErrFloat >= instance.traffic.Degraded { + edgeType = "httpdegraded" + } else { + edgeType = "httphealthy" } + } + } + } else if response.Elements.Edges[i].Data.Traffic.Protocol == "grpc" { + edgeType = "grpc" + + if grpcRate, ok := response.Elements.Edges[i].Data.Traffic.Rates["grpc"]; ok { + edgeLabel = grpcRate + "req/s" + } + + if grpcPercentErr, ok := response.Elements.Edges[i].Data.Traffic.Rates["grpcPercentErr"]; ok { + if edgeLabel == "" { + edgeLabel = grpcPercentErr + "%" } else { - edgeType = "httphealthy" + edgeLabel = edgeLabel + "\n" + grpcPercentErr + "%" } } } else if response.Elements.Edges[i].Data.Traffic.Protocol == "tcp" && response.Elements.Edges[i].Data.Traffic.Rates != nil { @@ -240,7 +256,85 @@ func (k *Kiali) GetGraph(ctx context.Context, getGraphRequest *kialiProto.GetGra log.WithFields(logrus.Fields{"nodes": len(response.Elements.Nodes), "edges": len(response.Elements.Edges)}).Debugf("Results.") return response, nil +} + +// GetMetrics returns the Prometheus metrics for the given workload/service from the Kiali API. We transform the API +// response into our protobuf message format, so that we can handle the datapoints better in the React UI. +func (k *Kiali) GetMetrics(ctx context.Context, getMetricsRequest *kialiProto.GetMetricsRequest) (*kialiProto.GetMetricsResponse, error) { + if getMetricsRequest == nil { + return nil, fmt.Errorf("request data is missing") + } + + instance := k.getInstace(getMetricsRequest.Name) + if instance == nil { + return nil, fmt.Errorf("invalid name for Kiali plugin") + } + + quantiles := "&quantiles[]=0.5&quantiles[]=0.95&quantiles[]=0.99" + filters := strings.Join(getMetricsRequest.Filters, "&filters[]=") + byLabels := strings.Join(getMetricsRequest.ByLabels, "&byLabels[]=") + + var requestProtocol string + if getMetricsRequest.RequestProtocol != "" { + requestProtocol = "&requestProtocol=" + getMetricsRequest.RequestProtocol + } + + url := fmt.Sprintf( + "/kiali/api/namespaces/%s/%s/%s/metrics?queryTime=%d&duration=%d&step=%d&rateInterval=%s%s&filters[]=%s&byLabels[]=%s&direction=%s&reporter=%s%s", + getMetricsRequest.Namespace, + getMetricsRequest.NodeType, + getMetricsRequest.NodeName, + getMetricsRequest.QueryTime, + getMetricsRequest.Duration, + getMetricsRequest.Step, + getMetricsRequest.RateInterval, + quantiles, + filters, + byLabels, + getMetricsRequest.Direction, + getMetricsRequest.Reporter, + requestProtocol, + ) + log.WithFields(logrus.Fields{"url": url}).Debugf("GetMetrics.") + + body, err := instance.doRequest(ctx, url) + if err != nil { + return nil, err + } + + var response map[string][]Metric + err = json.Unmarshal(body, &response) + if err != nil { + return nil, err + } + var metrics []*kialiProto.Metric + + for metricName, metricsList := range response { + for _, metric := range metricsList { + var data []*kialiProto.Data + for _, point := range metric.Datapoints { + value, err := strconv.ParseFloat(point[1].(string), 64) + if err == nil { + data = append(data, &kialiProto.Data{ + X: int64(point[0].(float64)) * 1000, + Y: value, + }) + } + } + + metrics = append(metrics, &kialiProto.Metric{ + Name: metricName, + Label: metric.Labels[getMetricsRequest.ByLabels[0]], + Stat: metric.Stat, + Data: data, + }) + } + } + + return &kialiProto.GetMetricsResponse{ + Metrics: metrics, + }, nil } // Register is used to register the Kiali plugin at our gRPC server, with all configured instances. @@ -269,14 +363,12 @@ func Register(cfg []Config, grpcServer *grpc.Server) ([]*pluginsProto.PluginShor } traffic := config.Traffic - if traffic.Enabled { - if traffic.Failure <= 0 || traffic.Failure >= 100 || traffic.Degraded < traffic.Failure { - traffic.Failure = 95 - } + if traffic.Degraded <= 0 || traffic.Degraded >= 100 || traffic.Degraded > traffic.Failure { + traffic.Degraded = 1 + } - if traffic.Degraded <= 0 || traffic.Degraded >= 100 || traffic.Degraded < traffic.Failure { - traffic.Degraded = 99 - } + if traffic.Failure <= 0 || traffic.Failure >= 100 || traffic.Degraded > traffic.Failure { + traffic.Failure = 5 } pluginDetails = append(pluginDetails, &pluginsProto.PluginShort{ diff --git a/pkg/api/plugins/kiali/proto/kiali.pb.go b/pkg/api/plugins/kiali/proto/kiali.pb.go index d3e4a2517..27c5032d6 100644 --- a/pkg/api/plugins/kiali/proto/kiali.pb.go +++ b/pkg/api/plugins/kiali/proto/kiali.pb.go @@ -1099,6 +1099,329 @@ func (x *Response) GetHosts() map[string]string { return nil } +// GetMetricsRequest it the request format to get the metrics for a workload/service (nodeType). For the request we also +// need the name and namespace of the workload/service and some other properties, which are used to filter the metrics. +type GetMetricsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` + NodeType string `protobuf:"bytes,3,opt,name=nodeType,proto3" json:"nodeType,omitempty"` + NodeName string `protobuf:"bytes,4,opt,name=nodeName,proto3" json:"nodeName,omitempty"` + QueryTime int64 `protobuf:"varint,5,opt,name=queryTime,proto3" json:"queryTime,omitempty"` + Duration int64 `protobuf:"varint,6,opt,name=duration,proto3" json:"duration,omitempty"` + Step int64 `protobuf:"varint,7,opt,name=step,proto3" json:"step,omitempty"` + RateInterval string `protobuf:"bytes,8,opt,name=rateInterval,proto3" json:"rateInterval,omitempty"` + Filters []string `protobuf:"bytes,9,rep,name=filters,proto3" json:"filters,omitempty"` + ByLabels []string `protobuf:"bytes,10,rep,name=byLabels,proto3" json:"byLabels,omitempty"` + Direction string `protobuf:"bytes,11,opt,name=direction,proto3" json:"direction,omitempty"` + Reporter string `protobuf:"bytes,12,opt,name=reporter,proto3" json:"reporter,omitempty"` + RequestProtocol string `protobuf:"bytes,13,opt,name=requestProtocol,proto3" json:"requestProtocol,omitempty"` +} + +func (x *GetMetricsRequest) Reset() { + *x = GetMetricsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_kiali_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMetricsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMetricsRequest) ProtoMessage() {} + +func (x *GetMetricsRequest) ProtoReflect() protoreflect.Message { + mi := &file_kiali_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMetricsRequest.ProtoReflect.Descriptor instead. +func (*GetMetricsRequest) Descriptor() ([]byte, []int) { + return file_kiali_proto_rawDescGZIP(), []int{14} +} + +func (x *GetMetricsRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetMetricsRequest) GetNamespace() string { + if x != nil { + return x.Namespace + } + return "" +} + +func (x *GetMetricsRequest) GetNodeType() string { + if x != nil { + return x.NodeType + } + return "" +} + +func (x *GetMetricsRequest) GetNodeName() string { + if x != nil { + return x.NodeName + } + return "" +} + +func (x *GetMetricsRequest) GetQueryTime() int64 { + if x != nil { + return x.QueryTime + } + return 0 +} + +func (x *GetMetricsRequest) GetDuration() int64 { + if x != nil { + return x.Duration + } + return 0 +} + +func (x *GetMetricsRequest) GetStep() int64 { + if x != nil { + return x.Step + } + return 0 +} + +func (x *GetMetricsRequest) GetRateInterval() string { + if x != nil { + return x.RateInterval + } + return "" +} + +func (x *GetMetricsRequest) GetFilters() []string { + if x != nil { + return x.Filters + } + return nil +} + +func (x *GetMetricsRequest) GetByLabels() []string { + if x != nil { + return x.ByLabels + } + return nil +} + +func (x *GetMetricsRequest) GetDirection() string { + if x != nil { + return x.Direction + } + return "" +} + +func (x *GetMetricsRequest) GetReporter() string { + if x != nil { + return x.Reporter + } + return "" +} + +func (x *GetMetricsRequest) GetRequestProtocol() string { + if x != nil { + return x.RequestProtocol + } + return "" +} + +// GetMetricsResponse is the response format for a GetMetrics call. It contains all the metrics for the requested +// workload/service. +type GetMetricsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metrics []*Metric `protobuf:"bytes,1,rep,name=metrics,proto3" json:"metrics,omitempty"` +} + +func (x *GetMetricsResponse) Reset() { + *x = GetMetricsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_kiali_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetMetricsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetMetricsResponse) ProtoMessage() {} + +func (x *GetMetricsResponse) ProtoReflect() protoreflect.Message { + mi := &file_kiali_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetMetricsResponse.ProtoReflect.Descriptor instead. +func (*GetMetricsResponse) Descriptor() ([]byte, []int) { + return file_kiali_proto_rawDescGZIP(), []int{15} +} + +func (x *GetMetricsResponse) GetMetrics() []*Metric { + if x != nil { + return x.Metrics + } + return nil +} + +// Metric is a single metrics for a workload/service. Each metric can be identified by the name, label and stat +// parameter. The metric also contains all datapoints. +type Metric struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"` + Stat string `protobuf:"bytes,3,opt,name=stat,proto3" json:"stat,omitempty"` + Data []*Data `protobuf:"bytes,4,rep,name=data,proto3" json:"data,omitempty"` +} + +func (x *Metric) Reset() { + *x = Metric{} + if protoimpl.UnsafeEnabled { + mi := &file_kiali_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Metric) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Metric) ProtoMessage() {} + +func (x *Metric) ProtoReflect() protoreflect.Message { + mi := &file_kiali_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Metric.ProtoReflect.Descriptor instead. +func (*Metric) Descriptor() ([]byte, []int) { + return file_kiali_proto_rawDescGZIP(), []int{16} +} + +func (x *Metric) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Metric) GetLabel() string { + if x != nil { + return x.Label + } + return "" +} + +func (x *Metric) GetStat() string { + if x != nil { + return x.Stat + } + return "" +} + +func (x *Metric) GetData() []*Data { + if x != nil { + return x.Data + } + return nil +} + +// Data is a single datapoint for a metric. +type Data struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + X int64 `protobuf:"varint,1,opt,name=x,proto3" json:"x,omitempty"` + Y float64 `protobuf:"fixed64,2,opt,name=y,proto3" json:"y,omitempty"` +} + +func (x *Data) Reset() { + *x = Data{} + if protoimpl.UnsafeEnabled { + mi := &file_kiali_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Data) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Data) ProtoMessage() {} + +func (x *Data) ProtoReflect() protoreflect.Message { + mi := &file_kiali_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Data.ProtoReflect.Descriptor instead. +func (*Data) Descriptor() ([]byte, []int) { + return file_kiali_proto_rawDescGZIP(), []int{17} +} + +func (x *Data) GetX() int64 { + if x != nil { + return x.X + } + return 0 +} + +func (x *Data) GetY() float64 { + if x != nil { + return x.Y + } + return 0 +} + var File_kiali_proto protoreflect.FileDescriptor var file_kiali_proto_rawDesc = []byte{ @@ -1285,22 +1608,66 @@ var file_kiali_proto_rawDesc = []byte{ 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x32, 0xb4, 0x01, 0x0a, 0x05, 0x4b, 0x69, 0x61, 0x6c, 0x69, 0x12, 0x5c, 0x0a, 0x0d, - 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x23, 0x2e, - 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, 0x47, 0x65, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x6b, 0x69, 0x61, - 0x6c, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x08, 0x47, 0x65, - 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, - 0x2e, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, - 0x2e, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x6f, 0x62, 0x73, 0x69, 0x6f, 0x2f, 0x6b, - 0x6f, 0x62, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x73, 0x2f, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x38, 0x01, 0x22, 0x89, 0x03, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x6f, + 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, + 0x64, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x74, 0x65, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, + 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, + 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1a, + 0x0a, 0x08, 0x62, 0x79, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x08, 0x62, 0x79, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x45, + 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, + 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, + 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x6f, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x61, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x61, 0x74, 0x12, 0x27, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x22, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0c, + 0x0a, 0x01, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x79, 0x32, 0x89, 0x02, 0x0a, 0x05, 0x4b, + 0x69, 0x61, 0x6c, 0x69, 0x12, 0x5c, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, + 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1e, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, 0x47, + 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, 0x47, + 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x53, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, + 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2e, + 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x2e, 0x6b, 0x69, 0x61, 0x6c, + 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x6f, 0x62, 0x73, 0x69, 0x6f, 0x2f, 0x6b, 0x6f, 0x62, 0x73, + 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, + 0x2f, 0x6b, 0x69, 0x61, 0x6c, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1315,7 +1682,7 @@ func file_kiali_proto_rawDescGZIP() []byte { return file_kiali_proto_rawDescData } -var file_kiali_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_kiali_proto_msgTypes = make([]protoimpl.MessageInfo, 24) var file_kiali_proto_goTypes = []interface{}{ (*GetNamespacesRequest)(nil), // 0: plugins.kiali.GetNamespacesRequest (*GetNamespacesResponse)(nil), // 1: plugins.kiali.GetNamespacesResponse @@ -1331,16 +1698,20 @@ var file_kiali_proto_goTypes = []interface{}{ (*ProtocolTraffic)(nil), // 11: plugins.kiali.ProtocolTraffic (*SEInfo)(nil), // 12: plugins.kiali.SEInfo (*Response)(nil), // 13: plugins.kiali.Response - nil, // 14: plugins.kiali.Namespace.LabelsEntry - nil, // 15: plugins.kiali.Node.HasHealthConfigEntry - nil, // 16: plugins.kiali.ProtocolTraffic.RatesEntry - nil, // 17: plugins.kiali.ProtocolTraffic.ResponsesEntry - nil, // 18: plugins.kiali.Response.FlagsEntry - nil, // 19: plugins.kiali.Response.HostsEntry + (*GetMetricsRequest)(nil), // 14: plugins.kiali.GetMetricsRequest + (*GetMetricsResponse)(nil), // 15: plugins.kiali.GetMetricsResponse + (*Metric)(nil), // 16: plugins.kiali.Metric + (*Data)(nil), // 17: plugins.kiali.Data + nil, // 18: plugins.kiali.Namespace.LabelsEntry + nil, // 19: plugins.kiali.Node.HasHealthConfigEntry + nil, // 20: plugins.kiali.ProtocolTraffic.RatesEntry + nil, // 21: plugins.kiali.ProtocolTraffic.ResponsesEntry + nil, // 22: plugins.kiali.Response.FlagsEntry + nil, // 23: plugins.kiali.Response.HostsEntry } var file_kiali_proto_depIdxs = []int32{ 2, // 0: plugins.kiali.GetNamespacesResponse.namespaces:type_name -> plugins.kiali.Namespace - 14, // 1: plugins.kiali.Namespace.labels:type_name -> plugins.kiali.Namespace.LabelsEntry + 18, // 1: plugins.kiali.Namespace.labels:type_name -> plugins.kiali.Namespace.LabelsEntry 5, // 2: plugins.kiali.GetGraphResponse.elements:type_name -> plugins.kiali.Elements 6, // 3: plugins.kiali.Elements.nodes:type_name -> plugins.kiali.NodeWrapper 7, // 4: plugins.kiali.Elements.edges:type_name -> plugins.kiali.EdgeWrapper @@ -1348,23 +1719,27 @@ var file_kiali_proto_depIdxs = []int32{ 9, // 6: plugins.kiali.EdgeWrapper.data:type_name -> plugins.kiali.Edge 10, // 7: plugins.kiali.Node.destServices:type_name -> plugins.kiali.ServiceName 11, // 8: plugins.kiali.Node.traffic:type_name -> plugins.kiali.ProtocolTraffic - 15, // 9: plugins.kiali.Node.hasHealthConfig:type_name -> plugins.kiali.Node.HasHealthConfigEntry + 19, // 9: plugins.kiali.Node.hasHealthConfig:type_name -> plugins.kiali.Node.HasHealthConfigEntry 12, // 10: plugins.kiali.Node.isServiceEntry:type_name -> plugins.kiali.SEInfo 11, // 11: plugins.kiali.Edge.traffic:type_name -> plugins.kiali.ProtocolTraffic - 16, // 12: plugins.kiali.ProtocolTraffic.rates:type_name -> plugins.kiali.ProtocolTraffic.RatesEntry - 17, // 13: plugins.kiali.ProtocolTraffic.responses:type_name -> plugins.kiali.ProtocolTraffic.ResponsesEntry - 18, // 14: plugins.kiali.Response.flags:type_name -> plugins.kiali.Response.FlagsEntry - 19, // 15: plugins.kiali.Response.hosts:type_name -> plugins.kiali.Response.HostsEntry - 13, // 16: plugins.kiali.ProtocolTraffic.ResponsesEntry.value:type_name -> plugins.kiali.Response - 0, // 17: plugins.kiali.Kiali.GetNamespaces:input_type -> plugins.kiali.GetNamespacesRequest - 3, // 18: plugins.kiali.Kiali.GetGraph:input_type -> plugins.kiali.GetGraphRequest - 1, // 19: plugins.kiali.Kiali.GetNamespaces:output_type -> plugins.kiali.GetNamespacesResponse - 4, // 20: plugins.kiali.Kiali.GetGraph:output_type -> plugins.kiali.GetGraphResponse - 19, // [19:21] is the sub-list for method output_type - 17, // [17:19] is the sub-list for method input_type - 17, // [17:17] is the sub-list for extension type_name - 17, // [17:17] is the sub-list for extension extendee - 0, // [0:17] is the sub-list for field type_name + 20, // 12: plugins.kiali.ProtocolTraffic.rates:type_name -> plugins.kiali.ProtocolTraffic.RatesEntry + 21, // 13: plugins.kiali.ProtocolTraffic.responses:type_name -> plugins.kiali.ProtocolTraffic.ResponsesEntry + 22, // 14: plugins.kiali.Response.flags:type_name -> plugins.kiali.Response.FlagsEntry + 23, // 15: plugins.kiali.Response.hosts:type_name -> plugins.kiali.Response.HostsEntry + 16, // 16: plugins.kiali.GetMetricsResponse.metrics:type_name -> plugins.kiali.Metric + 17, // 17: plugins.kiali.Metric.data:type_name -> plugins.kiali.Data + 13, // 18: plugins.kiali.ProtocolTraffic.ResponsesEntry.value:type_name -> plugins.kiali.Response + 0, // 19: plugins.kiali.Kiali.GetNamespaces:input_type -> plugins.kiali.GetNamespacesRequest + 3, // 20: plugins.kiali.Kiali.GetGraph:input_type -> plugins.kiali.GetGraphRequest + 14, // 21: plugins.kiali.Kiali.GetMetrics:input_type -> plugins.kiali.GetMetricsRequest + 1, // 22: plugins.kiali.Kiali.GetNamespaces:output_type -> plugins.kiali.GetNamespacesResponse + 4, // 23: plugins.kiali.Kiali.GetGraph:output_type -> plugins.kiali.GetGraphResponse + 15, // 24: plugins.kiali.Kiali.GetMetrics:output_type -> plugins.kiali.GetMetricsResponse + 22, // [22:25] is the sub-list for method output_type + 19, // [19:22] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_kiali_proto_init() } @@ -1541,6 +1916,54 @@ func file_kiali_proto_init() { return nil } } + file_kiali_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMetricsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kiali_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetMetricsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kiali_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Metric); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kiali_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Data); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1548,7 +1971,7 @@ func file_kiali_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_kiali_proto_rawDesc, NumEnums: 0, - NumMessages: 20, + NumMessages: 24, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/api/plugins/kiali/proto/kiali_deepcopy.gen.go b/pkg/api/plugins/kiali/proto/kiali_deepcopy.gen.go index 79c6e2947..da34d57bc 100644 --- a/pkg/api/plugins/kiali/proto/kiali_deepcopy.gen.go +++ b/pkg/api/plugins/kiali/proto/kiali_deepcopy.gen.go @@ -307,3 +307,87 @@ func (in *Response) DeepCopy() *Response { func (in *Response) DeepCopyInterface() interface{} { return in.DeepCopy() } + +// DeepCopyInto supports using GetMetricsRequest within kubernetes types, where deepcopy-gen is used. +func (in *GetMetricsRequest) DeepCopyInto(out *GetMetricsRequest) { + p := proto.Clone(in).(*GetMetricsRequest) + *out = *p +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GetMetricsRequest. Required by controller-gen. +func (in *GetMetricsRequest) DeepCopy() *GetMetricsRequest { + if in == nil { + return nil + } + out := new(GetMetricsRequest) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new GetMetricsRequest. Required by controller-gen. +func (in *GetMetricsRequest) DeepCopyInterface() interface{} { + return in.DeepCopy() +} + +// DeepCopyInto supports using GetMetricsResponse within kubernetes types, where deepcopy-gen is used. +func (in *GetMetricsResponse) DeepCopyInto(out *GetMetricsResponse) { + p := proto.Clone(in).(*GetMetricsResponse) + *out = *p +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GetMetricsResponse. Required by controller-gen. +func (in *GetMetricsResponse) DeepCopy() *GetMetricsResponse { + if in == nil { + return nil + } + out := new(GetMetricsResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new GetMetricsResponse. Required by controller-gen. +func (in *GetMetricsResponse) DeepCopyInterface() interface{} { + return in.DeepCopy() +} + +// DeepCopyInto supports using Metric within kubernetes types, where deepcopy-gen is used. +func (in *Metric) DeepCopyInto(out *Metric) { + p := proto.Clone(in).(*Metric) + *out = *p +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Metric. Required by controller-gen. +func (in *Metric) DeepCopy() *Metric { + if in == nil { + return nil + } + out := new(Metric) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Metric. Required by controller-gen. +func (in *Metric) DeepCopyInterface() interface{} { + return in.DeepCopy() +} + +// DeepCopyInto supports using Data within kubernetes types, where deepcopy-gen is used. +func (in *Data) DeepCopyInto(out *Data) { + p := proto.Clone(in).(*Data) + *out = *p +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Data. Required by controller-gen. +func (in *Data) DeepCopy() *Data { + if in == nil { + return nil + } + out := new(Data) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInterface is an autogenerated deepcopy function, copying the receiver, creating a new Data. Required by controller-gen. +func (in *Data) DeepCopyInterface() interface{} { + return in.DeepCopy() +} diff --git a/pkg/api/plugins/kiali/proto/kiali_grpc.pb.go b/pkg/api/plugins/kiali/proto/kiali_grpc.pb.go index 3f033199e..eef17b710 100644 --- a/pkg/api/plugins/kiali/proto/kiali_grpc.pb.go +++ b/pkg/api/plugins/kiali/proto/kiali_grpc.pb.go @@ -20,6 +20,7 @@ const _ = grpc.SupportPackageIsVersion7 type KialiClient interface { GetNamespaces(ctx context.Context, in *GetNamespacesRequest, opts ...grpc.CallOption) (*GetNamespacesResponse, error) GetGraph(ctx context.Context, in *GetGraphRequest, opts ...grpc.CallOption) (*GetGraphResponse, error) + GetMetrics(ctx context.Context, in *GetMetricsRequest, opts ...grpc.CallOption) (*GetMetricsResponse, error) } type kialiClient struct { @@ -48,12 +49,22 @@ func (c *kialiClient) GetGraph(ctx context.Context, in *GetGraphRequest, opts .. return out, nil } +func (c *kialiClient) GetMetrics(ctx context.Context, in *GetMetricsRequest, opts ...grpc.CallOption) (*GetMetricsResponse, error) { + out := new(GetMetricsResponse) + err := c.cc.Invoke(ctx, "/plugins.kiali.Kiali/GetMetrics", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // KialiServer is the server API for Kiali service. // All implementations must embed UnimplementedKialiServer // for forward compatibility type KialiServer interface { GetNamespaces(context.Context, *GetNamespacesRequest) (*GetNamespacesResponse, error) GetGraph(context.Context, *GetGraphRequest) (*GetGraphResponse, error) + GetMetrics(context.Context, *GetMetricsRequest) (*GetMetricsResponse, error) mustEmbedUnimplementedKialiServer() } @@ -67,6 +78,9 @@ func (UnimplementedKialiServer) GetNamespaces(context.Context, *GetNamespacesReq func (UnimplementedKialiServer) GetGraph(context.Context, *GetGraphRequest) (*GetGraphResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetGraph not implemented") } +func (UnimplementedKialiServer) GetMetrics(context.Context, *GetMetricsRequest) (*GetMetricsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMetrics not implemented") +} func (UnimplementedKialiServer) mustEmbedUnimplementedKialiServer() {} // UnsafeKialiServer may be embedded to opt out of forward compatibility for this service. @@ -116,6 +130,24 @@ func _Kiali_GetGraph_Handler(srv interface{}, ctx context.Context, dec func(inte return interceptor(ctx, in, info, handler) } +func _Kiali_GetMetrics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetMetricsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KialiServer).GetMetrics(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/plugins.kiali.Kiali/GetMetrics", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KialiServer).GetMetrics(ctx, req.(*GetMetricsRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Kiali_ServiceDesc is the grpc.ServiceDesc for Kiali service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -131,6 +163,10 @@ var Kiali_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetGraph", Handler: _Kiali_GetGraph_Handler, }, + { + MethodName: "GetMetrics", + Handler: _Kiali_GetMetrics_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "kiali.proto", diff --git a/proto/kiali.proto b/proto/kiali.proto index c0b3c19ec..2fb0135ec 100644 --- a/proto/kiali.proto +++ b/proto/kiali.proto @@ -7,6 +7,7 @@ option go_package = "github.com/kobsio/kobs/pkg/api/plugins/kiali/proto"; service Kiali { rpc GetNamespaces(GetNamespacesRequest) returns (GetNamespacesResponse) {} rpc GetGraph(GetGraphRequest) returns (GetGraphResponse) {} + rpc GetMetrics(GetMetricsRequest) returns (GetMetricsResponse) {} } // GetNamespacesRequest is the request format to get all namespace. It just requires the name of the Kiali instance. @@ -128,3 +129,42 @@ message Response { map flags = 1; map hosts = 2; } + +// GetMetricsRequest it the request format to get the metrics for a workload/service (nodeType). For the request we also +// need the name and namespace of the workload/service and some other properties, which are used to filter the metrics. +message GetMetricsRequest { + string name = 1; + string namespace = 2; + string nodeType = 3; + string nodeName = 4; + int64 queryTime = 5; + int64 duration = 6; + int64 step = 7; + string rateInterval = 8; + repeated string filters = 9; + repeated string byLabels = 10; + string direction = 11; + string reporter = 12; + string requestProtocol = 13; +} + +// GetMetricsResponse is the response format for a GetMetrics call. It contains all the metrics for the requested +// workload/service. +message GetMetricsResponse { + repeated Metric metrics = 1; +} + +// Metric is a single metrics for a workload/service. Each metric can be identified by the name, label and stat +// parameter. The metric also contains all datapoints. +message Metric { + string name = 1; + string label = 2; + string stat = 3; + repeated Data data = 4; +} + +// Data is a single datapoint for a metric. +message Data { + int64 x = 1; + double y = 2; +}