* Adding id and name to metadata response * Adding name to response * update to types * Adding support for displayNames to waffle map * fixing a bug when _source is missing * Fixing tests * making the metadata response manditory * Fixes from PR review * Fixing typing errors related to displayName being required part of path * Changing 'Loading data for xxx' to 'Loading data' * Changing InfraNodePath.displayName to InfraNodePath.label * Change groups to use the label instead of value * Fixing merge changes
This commit is contained in:
parent
5425d289aa
commit
489119cbbe
|
@ -18,7 +18,8 @@ interface Props {
|
|||
metrics: InfraMetricData[];
|
||||
layouts: InfraMetricLayout[];
|
||||
loading: boolean;
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
label: string;
|
||||
onChangeRangeTime?: (time: metricTimeActions.MetricRangeTimeState) => void;
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
@ -41,15 +42,10 @@ export const Metrics = injectI18n(
|
|||
<InfraLoadingPanel
|
||||
height="100vh"
|
||||
width="auto"
|
||||
text={intl.formatMessage(
|
||||
{
|
||||
text={intl.formatMessage({
|
||||
id: 'xpack.infra.metrics.loadingNodeDataText',
|
||||
defaultMessage: 'Loading data for {nodeName}',
|
||||
},
|
||||
{
|
||||
nodeName: this.props.nodeName,
|
||||
}
|
||||
)}
|
||||
defaultMessage: 'Loading data',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ export const GroupOfNodes: React.SFC<Props> = ({
|
|||
<Nodes>
|
||||
{group.nodes.map(node => (
|
||||
<Node
|
||||
key={node.id}
|
||||
key={node.pathId}
|
||||
options={options}
|
||||
squareSize={group.squareSize}
|
||||
node={node}
|
||||
|
|
|
@ -25,18 +25,18 @@ interface Props {
|
|||
|
||||
export const NodeContextMenu = injectI18n(
|
||||
({ options, timeRange, children, node, isPopoverOpen, closePopover, nodeType, intl }: Props) => {
|
||||
const nodeName = node.path.length > 0 ? node.path[node.path.length - 1].value : undefined;
|
||||
const nodeLogsUrl = nodeName
|
||||
const nodeId = node.path.length > 0 ? node.path[node.path.length - 1].value : undefined;
|
||||
const nodeLogsUrl = nodeId
|
||||
? getNodeLogsUrl({
|
||||
nodeType,
|
||||
nodeName,
|
||||
nodeId,
|
||||
time: timeRange.to,
|
||||
})
|
||||
: undefined;
|
||||
const nodeDetailUrl = nodeName
|
||||
const nodeDetailUrl = nodeId
|
||||
? getNodeDetailUrl({
|
||||
nodeType,
|
||||
nodeName,
|
||||
nodeId,
|
||||
from: timeRange.from,
|
||||
to: timeRange.to,
|
||||
})
|
||||
|
@ -76,7 +76,7 @@ export const NodeContextMenu = injectI18n(
|
|||
return (
|
||||
<EuiPopover
|
||||
closePopover={closePopover}
|
||||
id={`${node.id}-popover`}
|
||||
id={`${node.pathId}-popover`}
|
||||
isOpen={isPopoverOpen}
|
||||
button={children}
|
||||
panelPaddingSize="none"
|
||||
|
|
|
@ -10,10 +10,13 @@ export const metadataQuery = gql`
|
|||
query MetadataQuery($sourceId: ID!, $nodeId: String!, $nodeType: InfraNodeType!) {
|
||||
source(id: $sourceId) {
|
||||
id
|
||||
metadataByNode(nodeName: $nodeId, nodeType: $nodeType) {
|
||||
metadataByNode(nodeId: $nodeId, nodeType: $nodeType) {
|
||||
name
|
||||
features {
|
||||
name
|
||||
source
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -21,6 +21,7 @@ interface WithMetadataProps {
|
|||
}
|
||||
|
||||
interface WithMetadataArgs {
|
||||
name: string;
|
||||
filteredLayouts: InfraMetricLayout[];
|
||||
error?: string | undefined;
|
||||
loading: boolean;
|
||||
|
@ -45,8 +46,9 @@ export const WithMetadata = ({
|
|||
>
|
||||
{({ data, error, loading }) => {
|
||||
const metadata = data && data.source && data.source.metadataByNode;
|
||||
const filteredLayouts = getFilteredLayouts(layouts, metadata);
|
||||
const filteredLayouts = (metadata && getFilteredLayouts(layouts, metadata.features)) || [];
|
||||
return children({
|
||||
name: (metadata && metadata.name) || '',
|
||||
filteredLayouts,
|
||||
error: error && error.message,
|
||||
loading,
|
||||
|
@ -58,7 +60,7 @@ export const WithMetadata = ({
|
|||
|
||||
const getFilteredLayouts = (
|
||||
layouts: InfraMetricLayout[],
|
||||
metadata: Array<MetadataQuery.MetadataByNode | null> | undefined
|
||||
metadata: Array<MetadataQuery.Features | null> | undefined
|
||||
): InfraMetricLayout[] => {
|
||||
if (!metadata) {
|
||||
return layouts;
|
||||
|
|
|
@ -51,7 +51,7 @@ function findOrCreateGroupWithNodes(
|
|||
? i18n.translate('xpack.infra.nodesToWaffleMap.groupsWithNodes.allName', {
|
||||
defaultMessage: 'All',
|
||||
})
|
||||
: last(path).value,
|
||||
: last(path).label,
|
||||
count: 0,
|
||||
width: 0,
|
||||
squareSize: 0,
|
||||
|
@ -75,7 +75,7 @@ function findOrCreateGroupWithGroups(
|
|||
? i18n.translate('xpack.infra.nodesToWaffleMap.groupsWithGroups.allName', {
|
||||
defaultMessage: 'All',
|
||||
})
|
||||
: last(path).value,
|
||||
: last(path).label,
|
||||
count: 0,
|
||||
width: 0,
|
||||
squareSize: 0,
|
||||
|
@ -84,10 +84,12 @@ function findOrCreateGroupWithGroups(
|
|||
}
|
||||
|
||||
function createWaffleMapNode(node: InfraNode): InfraWaffleMapNode {
|
||||
const nodePathItem = last(node.path);
|
||||
return {
|
||||
id: node.path.map(p => p.value).join('/'),
|
||||
pathId: node.path.map(p => p.value).join('/'),
|
||||
path: node.path,
|
||||
name: last(node.path).value,
|
||||
id: nodePathItem.value,
|
||||
name: nodePathItem.label,
|
||||
metric: node.metric,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ export const waffleNodesQuery = gql`
|
|||
nodes(path: $path, metric: $metric) {
|
||||
path {
|
||||
value
|
||||
label
|
||||
}
|
||||
metric {
|
||||
name
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
"description": "A hierarchy of metadata entries by node",
|
||||
"args": [
|
||||
{
|
||||
"name": "nodeName",
|
||||
"name": "nodeId",
|
||||
"description": "",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
|
@ -137,11 +137,7 @@
|
|||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": { "kind": "OBJECT", "name": "InfraNodeMetadata", "ofType": null }
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
|
@ -775,6 +771,61 @@
|
|||
"kind": "OBJECT",
|
||||
"name": "InfraNodeMetadata",
|
||||
"description": "One metadata entry for a node.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "SCALAR", "name": "ID", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "features",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "OBJECT", "name": "InfraNodeFeature", "ofType": null }
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "InfraNodeFeature",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "name",
|
||||
|
@ -1542,6 +1593,18 @@
|
|||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "displayName",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
|
|
|
@ -23,7 +23,7 @@ export interface InfraSource {
|
|||
/** The status of the source */
|
||||
status: InfraSourceStatus;
|
||||
/** A hierarchy of metadata entries by node */
|
||||
metadataByNode: (InfraNodeMetadata | null)[];
|
||||
metadataByNode: InfraNodeMetadata;
|
||||
/** A consecutive span of log entries surrounding a point in time */
|
||||
logEntriesAround: InfraLogEntryInterval;
|
||||
/** A consecutive span of log entries within an interval */
|
||||
|
@ -89,6 +89,14 @@ export interface InfraIndexField {
|
|||
}
|
||||
/** One metadata entry for a node. */
|
||||
export interface InfraNodeMetadata {
|
||||
id: string;
|
||||
|
||||
name: string;
|
||||
|
||||
features: InfraNodeFeature[];
|
||||
}
|
||||
|
||||
export interface InfraNodeFeature {
|
||||
name: string;
|
||||
|
||||
source: string;
|
||||
|
@ -175,6 +183,8 @@ export interface InfraNode {
|
|||
|
||||
export interface InfraNodePath {
|
||||
value: string;
|
||||
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface InfraNodeMetric {
|
||||
|
@ -252,7 +262,7 @@ export interface SourceQueryArgs {
|
|||
id: string;
|
||||
}
|
||||
export interface MetadataByNodeInfraSourceArgs {
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
|
||||
nodeType: InfraNodeType;
|
||||
}
|
||||
|
@ -416,7 +426,7 @@ export namespace MetadataQuery {
|
|||
|
||||
id: string;
|
||||
|
||||
metadataByNode: (MetadataByNode | null)[];
|
||||
metadataByNode: MetadataByNode;
|
||||
};
|
||||
|
||||
export type MetadataByNode = {
|
||||
|
@ -424,6 +434,14 @@ export namespace MetadataQuery {
|
|||
|
||||
name: string;
|
||||
|
||||
features: Features[];
|
||||
};
|
||||
|
||||
export type Features = {
|
||||
__typename?: 'InfraNodeFeature';
|
||||
|
||||
name: string;
|
||||
|
||||
source: string;
|
||||
};
|
||||
}
|
||||
|
@ -517,6 +535,8 @@ export namespace WaffleNodesQuery {
|
|||
__typename?: 'InfraNodePath';
|
||||
|
||||
value: string;
|
||||
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type Metric = {
|
||||
|
|
|
@ -100,6 +100,7 @@ export interface InfraField {
|
|||
export type InfraWaffleData = InfraWaffleMapGroup[];
|
||||
|
||||
export interface InfraWaffleMapNode {
|
||||
pathId: string;
|
||||
id: string;
|
||||
name: string;
|
||||
path: InfraNodePath[];
|
||||
|
|
|
@ -21,11 +21,11 @@ export class LinkToPage extends React.Component<LinkToPageProps> {
|
|||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
path={`${match.url}/:nodeType(host|container|pod)-logs/:nodeName`}
|
||||
path={`${match.url}/:nodeType(host|container|pod)-logs/:nodeId`}
|
||||
component={RedirectToNodeLogs}
|
||||
/>
|
||||
<Route
|
||||
path={`${match.url}/:nodeType(host|container|pod)-detail/:nodeName`}
|
||||
path={`${match.url}/:nodeType(host|container|pod)-detail/:nodeId`}
|
||||
component={RedirectToNodeDetail}
|
||||
/>
|
||||
<Redirect to="/home" />
|
||||
|
|
|
@ -12,13 +12,13 @@ import { InfraNodeType } from '../../graphql/types';
|
|||
import { getFromFromLocation, getToFromLocation } from './query_params';
|
||||
|
||||
type RedirectToNodeDetailProps = RouteComponentProps<{
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
nodeType: InfraNodeType;
|
||||
}>;
|
||||
|
||||
export const RedirectToNodeDetail = ({
|
||||
match: {
|
||||
params: { nodeName, nodeType },
|
||||
params: { nodeId, nodeType },
|
||||
},
|
||||
location,
|
||||
}: RedirectToNodeDetailProps) => {
|
||||
|
@ -27,20 +27,20 @@ export const RedirectToNodeDetail = ({
|
|||
getToFromLocation(location)
|
||||
)('');
|
||||
|
||||
return <Redirect to={`/metrics/${nodeType}/${nodeName}?${searchString}`} />;
|
||||
return <Redirect to={`/metrics/${nodeType}/${nodeId}?${searchString}`} />;
|
||||
};
|
||||
|
||||
export const getNodeDetailUrl = ({
|
||||
nodeType,
|
||||
nodeName,
|
||||
nodeId,
|
||||
to,
|
||||
from,
|
||||
}: {
|
||||
nodeType: InfraNodeType;
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
to?: number;
|
||||
from?: number;
|
||||
}) => {
|
||||
const args = to && from ? `?to=${to}&from=${from}` : '';
|
||||
return `#/link-to/${nodeType}-detail/${nodeName}${args}`;
|
||||
return `#/link-to/${nodeType}-detail/${nodeId}${args}`;
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ import { InfraNodeType } from '../../graphql/types';
|
|||
import { getTimeFromLocation } from './query_params';
|
||||
|
||||
type RedirectToNodeLogsType = RouteComponentProps<{
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
nodeType: InfraNodeType;
|
||||
}>;
|
||||
|
||||
|
@ -28,7 +28,7 @@ interface RedirectToNodeLogsProps extends RedirectToNodeLogsType {
|
|||
export const RedirectToNodeLogs = injectI18n(
|
||||
({
|
||||
match: {
|
||||
params: { nodeName, nodeType },
|
||||
params: { nodeId, nodeType },
|
||||
},
|
||||
location,
|
||||
intl,
|
||||
|
@ -52,7 +52,7 @@ export const RedirectToNodeLogs = injectI18n(
|
|||
}
|
||||
|
||||
const searchString = compose(
|
||||
replaceLogFilterInQueryString(`${configuredFields[nodeType]}: ${nodeName}`),
|
||||
replaceLogFilterInQueryString(`${configuredFields[nodeType]}: ${nodeId}`),
|
||||
replaceLogPositionInQueryString(getTimeFromLocation(location))
|
||||
)('');
|
||||
|
||||
|
@ -63,11 +63,11 @@ export const RedirectToNodeLogs = injectI18n(
|
|||
);
|
||||
|
||||
export const getNodeLogsUrl = ({
|
||||
nodeName,
|
||||
nodeId,
|
||||
nodeType,
|
||||
time,
|
||||
}: {
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
nodeType: InfraNodeType;
|
||||
time?: number;
|
||||
}) => [`#/link-to/${nodeType}-logs/`, nodeName, ...(time ? [`?time=${time}`] : [])].join('');
|
||||
}) => [`#/link-to/${nodeType}-logs/`, nodeId, ...(time ? [`?time=${time}`] : [])].join('');
|
||||
|
|
|
@ -63,7 +63,7 @@ export const MetricDetail = withTheme(
|
|||
|
||||
public render() {
|
||||
const { intl } = this.props;
|
||||
const nodeName = this.props.match.params.node;
|
||||
const nodeId = this.props.match.params.node;
|
||||
const nodeType = this.props.match.params.type as InfraNodeType;
|
||||
const layoutCreator = layoutCreators[nodeType];
|
||||
if (!layoutCreator) {
|
||||
|
@ -82,16 +82,8 @@ export const MetricDetail = withTheme(
|
|||
);
|
||||
}
|
||||
const layouts = layoutCreator(this.props.theme);
|
||||
const breadcrumbs = [{ text: nodeName }];
|
||||
|
||||
return (
|
||||
<ColumnarPage>
|
||||
<Header
|
||||
appendSections={<InfrastructureBetaBadgeHeaderSection />}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
<WithMetricsTimeUrlState />
|
||||
<DetailPageContent>
|
||||
<WithOptions>
|
||||
{({ sourceId }) => (
|
||||
<WithMetricsTime resetOnUnmount>
|
||||
|
@ -106,16 +98,24 @@ export const MetricDetail = withTheme(
|
|||
layouts={layouts}
|
||||
sourceId={sourceId}
|
||||
nodeType={nodeType}
|
||||
nodeId={nodeName}
|
||||
nodeId={nodeId}
|
||||
>
|
||||
{({ filteredLayouts, loading: metadataLoading }) => {
|
||||
{({ name, filteredLayouts, loading: metadataLoading }) => {
|
||||
const breadcrumbs = [{ text: name }];
|
||||
return (
|
||||
<ColumnarPage>
|
||||
<Header
|
||||
appendSections={<InfrastructureBetaBadgeHeaderSection />}
|
||||
breadcrumbs={breadcrumbs}
|
||||
/>
|
||||
<WithMetricsTimeUrlState />
|
||||
<DetailPageContent>
|
||||
<WithMetrics
|
||||
layouts={filteredLayouts}
|
||||
sourceId={sourceId}
|
||||
timerange={currentTimeRange as InfraTimerangeInput}
|
||||
nodeType={nodeType}
|
||||
nodeId={nodeName}
|
||||
nodeId={nodeId}
|
||||
>
|
||||
{({ metrics, error, loading }) => {
|
||||
if (error) {
|
||||
|
@ -126,7 +126,7 @@ export const MetricDetail = withTheme(
|
|||
<MetricsSideNav
|
||||
layouts={filteredLayouts}
|
||||
loading={metadataLoading}
|
||||
nodeName={nodeName}
|
||||
nodeName={name}
|
||||
handleClick={this.handleClick}
|
||||
/>
|
||||
<AutoSizer content={false} bounds detectAnyWindowResize>
|
||||
|
@ -139,7 +139,7 @@ export const MetricDetail = withTheme(
|
|||
<MetricsTitleTimeRangeContainer>
|
||||
<EuiHideFor sizes={['xs', 's']}>
|
||||
<EuiTitle size="m">
|
||||
<h1>{nodeName}</h1>
|
||||
<h1>{name}</h1>
|
||||
</EuiTitle>
|
||||
</EuiHideFor>
|
||||
<MetricsTimeControls
|
||||
|
@ -155,7 +155,8 @@ export const MetricDetail = withTheme(
|
|||
|
||||
<EuiPageContentWithRelative>
|
||||
<Metrics
|
||||
nodeName={nodeName}
|
||||
label={name}
|
||||
nodeId={nodeId}
|
||||
layouts={filteredLayouts}
|
||||
metrics={metrics}
|
||||
loading={
|
||||
|
@ -175,6 +176,8 @@ export const MetricDetail = withTheme(
|
|||
);
|
||||
}}
|
||||
</WithMetrics>
|
||||
</DetailPageContent>
|
||||
</ColumnarPage>
|
||||
);
|
||||
}}
|
||||
</WithMetadata>
|
||||
|
@ -182,8 +185,6 @@ export const MetricDetail = withTheme(
|
|||
</WithMetricsTime>
|
||||
)}
|
||||
</WithOptions>
|
||||
</DetailPageContent>
|
||||
</ColumnarPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ export const createMetadataResolvers = (libs: {
|
|||
} => ({
|
||||
InfraSource: {
|
||||
async metadataByNode(source, args, { req }) {
|
||||
const result = await libs.metadata.getMetadata(req, source.id, args.nodeName, args.nodeType);
|
||||
const result = await libs.metadata.getMetadata(req, source.id, args.nodeId, args.nodeType);
|
||||
return result;
|
||||
},
|
||||
},
|
||||
|
|
|
@ -9,12 +9,18 @@ import gql from 'graphql-tag';
|
|||
export const metadataSchema = gql`
|
||||
"One metadata entry for a node."
|
||||
type InfraNodeMetadata {
|
||||
id: ID!
|
||||
name: String!
|
||||
features: [InfraNodeFeature!]!
|
||||
}
|
||||
|
||||
type InfraNodeFeature {
|
||||
name: String!
|
||||
source: String!
|
||||
}
|
||||
|
||||
extend type InfraSource {
|
||||
"A hierarchy of metadata entries by node"
|
||||
metadataByNode(nodeName: String!, nodeType: InfraNodeType!): [InfraNodeMetadata]!
|
||||
metadataByNode(nodeId: String!, nodeType: InfraNodeType!): InfraNodeMetadata!
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -14,6 +14,7 @@ export const nodesSchema: any = gql`
|
|||
|
||||
type InfraNodePath {
|
||||
value: String!
|
||||
label: String
|
||||
}
|
||||
|
||||
type InfraNode {
|
||||
|
|
|
@ -51,7 +51,7 @@ export interface InfraSource {
|
|||
/** The status of the source */
|
||||
status: InfraSourceStatus;
|
||||
/** A hierarchy of metadata entries by node */
|
||||
metadataByNode: (InfraNodeMetadata | null)[];
|
||||
metadataByNode: InfraNodeMetadata;
|
||||
/** A consecutive span of log entries surrounding a point in time */
|
||||
logEntriesAround: InfraLogEntryInterval;
|
||||
/** A consecutive span of log entries within an interval */
|
||||
|
@ -117,6 +117,14 @@ export interface InfraIndexField {
|
|||
}
|
||||
/** One metadata entry for a node. */
|
||||
export interface InfraNodeMetadata {
|
||||
id: string;
|
||||
|
||||
name: string;
|
||||
|
||||
features: InfraNodeFeature[];
|
||||
}
|
||||
|
||||
export interface InfraNodeFeature {
|
||||
name: string;
|
||||
|
||||
source: string;
|
||||
|
@ -203,6 +211,8 @@ export interface InfraNode {
|
|||
|
||||
export interface InfraNodePath {
|
||||
value: string;
|
||||
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface InfraNodeMetric {
|
||||
|
@ -280,7 +290,7 @@ export interface SourceQueryArgs {
|
|||
id: string;
|
||||
}
|
||||
export interface MetadataByNodeInfraSourceArgs {
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
|
||||
nodeType: InfraNodeType;
|
||||
}
|
||||
|
@ -461,7 +471,7 @@ export namespace InfraSourceResolvers {
|
|||
/** The status of the source */
|
||||
status?: StatusResolver<InfraSourceStatus, TypeParent, Context>;
|
||||
/** A hierarchy of metadata entries by node */
|
||||
metadataByNode?: MetadataByNodeResolver<(InfraNodeMetadata | null)[], TypeParent, Context>;
|
||||
metadataByNode?: MetadataByNodeResolver<InfraNodeMetadata, TypeParent, Context>;
|
||||
/** A consecutive span of log entries surrounding a point in time */
|
||||
logEntriesAround?: LogEntriesAroundResolver<InfraLogEntryInterval, TypeParent, Context>;
|
||||
/** A consecutive span of log entries within an interval */
|
||||
|
@ -490,12 +500,12 @@ export namespace InfraSourceResolvers {
|
|||
Context = InfraContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type MetadataByNodeResolver<
|
||||
R = (InfraNodeMetadata | null)[],
|
||||
R = InfraNodeMetadata,
|
||||
Parent = InfraSource,
|
||||
Context = InfraContext
|
||||
> = Resolver<R, Parent, Context, MetadataByNodeArgs>;
|
||||
export interface MetadataByNodeArgs {
|
||||
nodeName: string;
|
||||
nodeId: string;
|
||||
|
||||
nodeType: InfraNodeType;
|
||||
}
|
||||
|
@ -746,6 +756,32 @@ export namespace InfraIndexFieldResolvers {
|
|||
/** One metadata entry for a node. */
|
||||
export namespace InfraNodeMetadataResolvers {
|
||||
export interface Resolvers<Context = InfraContext, TypeParent = InfraNodeMetadata> {
|
||||
id?: IdResolver<string, TypeParent, Context>;
|
||||
|
||||
name?: NameResolver<string, TypeParent, Context>;
|
||||
|
||||
features?: FeaturesResolver<InfraNodeFeature[], TypeParent, Context>;
|
||||
}
|
||||
|
||||
export type IdResolver<R = string, Parent = InfraNodeMetadata, Context = InfraContext> = Resolver<
|
||||
R,
|
||||
Parent,
|
||||
Context
|
||||
>;
|
||||
export type NameResolver<
|
||||
R = string,
|
||||
Parent = InfraNodeMetadata,
|
||||
Context = InfraContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type FeaturesResolver<
|
||||
R = InfraNodeFeature[],
|
||||
Parent = InfraNodeMetadata,
|
||||
Context = InfraContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
}
|
||||
|
||||
export namespace InfraNodeFeatureResolvers {
|
||||
export interface Resolvers<Context = InfraContext, TypeParent = InfraNodeFeature> {
|
||||
name?: NameResolver<string, TypeParent, Context>;
|
||||
|
||||
source?: SourceResolver<string, TypeParent, Context>;
|
||||
|
@ -753,12 +789,12 @@ export namespace InfraNodeMetadataResolvers {
|
|||
|
||||
export type NameResolver<
|
||||
R = string,
|
||||
Parent = InfraNodeMetadata,
|
||||
Parent = InfraNodeFeature,
|
||||
Context = InfraContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type SourceResolver<
|
||||
R = string,
|
||||
Parent = InfraNodeMetadata,
|
||||
Parent = InfraNodeFeature,
|
||||
Context = InfraContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
}
|
||||
|
@ -1012,6 +1048,8 @@ export namespace InfraNodeResolvers {
|
|||
export namespace InfraNodePathResolvers {
|
||||
export interface Resolvers<Context = InfraContext, TypeParent = InfraNodePath> {
|
||||
value?: ValueResolver<string, TypeParent, Context>;
|
||||
|
||||
label?: DisplayNameResolver<string, TypeParent, Context>;
|
||||
}
|
||||
|
||||
export type ValueResolver<R = string, Parent = InfraNodePath, Context = InfraContext> = Resolver<
|
||||
|
@ -1019,6 +1057,11 @@ export namespace InfraNodePathResolvers {
|
|||
Parent,
|
||||
Context
|
||||
>;
|
||||
export type DisplayNameResolver<
|
||||
R = string,
|
||||
Parent = InfraNodePath,
|
||||
Context = InfraContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
}
|
||||
|
||||
export namespace InfraNodeMetricResolvers {
|
||||
|
|
|
@ -7,17 +7,23 @@
|
|||
import { InfraSourceConfiguration } from '../../sources';
|
||||
import { InfraFrameworkRequest, InfraMetadataAggregationBucket } from '../framework';
|
||||
|
||||
export interface InfraMetricsAdapterResponse {
|
||||
id: string;
|
||||
name?: string;
|
||||
buckets: InfraMetadataAggregationBucket[];
|
||||
}
|
||||
|
||||
export interface InfraMetadataAdapter {
|
||||
getMetricMetadata(
|
||||
req: InfraFrameworkRequest,
|
||||
sourceConfiguration: InfraSourceConfiguration,
|
||||
nodeName: string,
|
||||
nodeId: string,
|
||||
nodeType: string
|
||||
): Promise<InfraMetadataAggregationBucket[]>;
|
||||
): Promise<InfraMetricsAdapterResponse>;
|
||||
getLogMetadata(
|
||||
req: InfraFrameworkRequest,
|
||||
sourceConfiguration: InfraSourceConfiguration,
|
||||
nodeName: string,
|
||||
nodeId: string,
|
||||
nodeType: string
|
||||
): Promise<InfraMetadataAggregationBucket[]>;
|
||||
): Promise<InfraMetricsAdapterResponse>;
|
||||
}
|
||||
|
|
|
@ -4,14 +4,15 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { first, get } from 'lodash';
|
||||
import { InfraSourceConfiguration } from '../../sources';
|
||||
import {
|
||||
InfraBackendFrameworkAdapter,
|
||||
InfraFrameworkRequest,
|
||||
InfraMetadataAggregationBucket,
|
||||
InfraMetadataAggregationResponse,
|
||||
} from '../framework';
|
||||
import { InfraMetadataAdapter } from './adapter_types';
|
||||
import { NAME_FIELDS } from '../nodes/constants';
|
||||
import { InfraMetadataAdapter, InfraMetricsAdapterResponse } from './adapter_types';
|
||||
|
||||
export class ElasticsearchMetadataAdapter implements InfraMetadataAdapter {
|
||||
private framework: InfraBackendFrameworkAdapter;
|
||||
|
@ -22,9 +23,9 @@ export class ElasticsearchMetadataAdapter implements InfraMetadataAdapter {
|
|||
public async getMetricMetadata(
|
||||
req: InfraFrameworkRequest,
|
||||
sourceConfiguration: InfraSourceConfiguration,
|
||||
nodeName: string,
|
||||
nodeId: string,
|
||||
nodeType: 'host' | 'container' | 'pod'
|
||||
): Promise<InfraMetadataAggregationBucket[]> {
|
||||
): Promise<InfraMetricsAdapterResponse> {
|
||||
const idFieldName = getIdFieldName(sourceConfiguration, nodeType);
|
||||
const metricQuery = {
|
||||
index: sourceConfiguration.metricAlias,
|
||||
|
@ -32,11 +33,12 @@ export class ElasticsearchMetadataAdapter implements InfraMetadataAdapter {
|
|||
query: {
|
||||
bool: {
|
||||
filter: {
|
||||
term: { [idFieldName]: nodeName },
|
||||
term: { [idFieldName]: nodeId },
|
||||
},
|
||||
},
|
||||
},
|
||||
size: 0,
|
||||
size: 1,
|
||||
_source: [NAME_FIELDS[nodeType]],
|
||||
aggs: {
|
||||
metrics: {
|
||||
terms: {
|
||||
|
@ -61,17 +63,26 @@ export class ElasticsearchMetadataAdapter implements InfraMetadataAdapter {
|
|||
{ metrics?: InfraMetadataAggregationResponse }
|
||||
>(req, 'search', metricQuery);
|
||||
|
||||
return response.aggregations && response.aggregations.metrics
|
||||
const buckets =
|
||||
response.aggregations && response.aggregations.metrics
|
||||
? response.aggregations.metrics.buckets
|
||||
: [];
|
||||
|
||||
const sampleDoc = first(response.hits.hits);
|
||||
|
||||
return {
|
||||
id: nodeId,
|
||||
name: get(sampleDoc, `_source.${NAME_FIELDS[nodeType]}`),
|
||||
buckets,
|
||||
};
|
||||
}
|
||||
|
||||
public async getLogMetadata(
|
||||
req: InfraFrameworkRequest,
|
||||
sourceConfiguration: InfraSourceConfiguration,
|
||||
nodeName: string,
|
||||
nodeId: string,
|
||||
nodeType: 'host' | 'container' | 'pod'
|
||||
): Promise<InfraMetadataAggregationBucket[]> {
|
||||
): Promise<InfraMetricsAdapterResponse> {
|
||||
const idFieldName = getIdFieldName(sourceConfiguration, nodeType);
|
||||
const logQuery = {
|
||||
index: sourceConfiguration.logAlias,
|
||||
|
@ -79,11 +90,12 @@ export class ElasticsearchMetadataAdapter implements InfraMetadataAdapter {
|
|||
query: {
|
||||
bool: {
|
||||
filter: {
|
||||
term: { [idFieldName]: nodeName },
|
||||
term: { [idFieldName]: nodeId },
|
||||
},
|
||||
},
|
||||
},
|
||||
size: 0,
|
||||
size: 1,
|
||||
_source: [NAME_FIELDS[nodeType]],
|
||||
aggs: {
|
||||
metrics: {
|
||||
terms: {
|
||||
|
@ -108,9 +120,18 @@ export class ElasticsearchMetadataAdapter implements InfraMetadataAdapter {
|
|||
{ metrics?: InfraMetadataAggregationResponse }
|
||||
>(req, 'search', logQuery);
|
||||
|
||||
return response.aggregations && response.aggregations.metrics
|
||||
const buckets =
|
||||
response.aggregations && response.aggregations.metrics
|
||||
? response.aggregations.metrics.buckets
|
||||
: [];
|
||||
|
||||
const sampleDoc = first(response.hits.hits);
|
||||
|
||||
return {
|
||||
id: nodeId,
|
||||
name: get(sampleDoc, `_source.${NAME_FIELDS[nodeType]}`),
|
||||
buckets,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { InfraNodeType } from '../../../graphql/types';
|
||||
// TODO: Make NODE_REQUEST_PARTITION_SIZE configurable from kibana.yml
|
||||
export const NODE_REQUEST_PARTITION_SIZE = 75;
|
||||
export const NODE_REQUEST_PARTITION_FACTOR = 1.2;
|
||||
export const NAME_FIELDS = {
|
||||
[InfraNodeType.host]: 'host.name',
|
||||
[InfraNodeType.pod]: 'kubernetes.pod.name',
|
||||
[InfraNodeType.container]: 'docker.container.name',
|
||||
};
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { last } from 'lodash';
|
||||
import { get, last } from 'lodash';
|
||||
import { isNumber } from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
import { InfraNode, InfraNodeMetric } from '../../../../graphql/types';
|
||||
import { InfraBucket, InfraNodeRequestOptions } from '../adapter_types';
|
||||
import { NAME_FIELDS } from '../constants';
|
||||
import { getBucketSizeInSeconds } from './get_bucket_size_in_seconds';
|
||||
|
||||
// TODO: Break these function into seperate files and expand beyond just documnet count
|
||||
|
@ -57,8 +58,9 @@ export function createNodeItem(
|
|||
node: InfraBucket,
|
||||
bucket: InfraBucket
|
||||
): InfraNode {
|
||||
const nodeDoc = get(node, ['nodeDetails', 'hits', 'hits', 0]);
|
||||
return {
|
||||
metric: createNodeMetrics(options, node, bucket),
|
||||
path: [{ value: node.key }],
|
||||
path: [{ value: node.key, label: get(nodeDoc, `_source.${NAME_FIELDS[options.nodeType]}`) }],
|
||||
} as InfraNode;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ export function extractGroupPaths(
|
|||
(b: InfraBucket): InfraNode => {
|
||||
const innerNode = createNodeItem(options, node, b);
|
||||
const nodePath = [
|
||||
{ value: bucket.key.toString() },
|
||||
{ value: b.key.toString() },
|
||||
{ value: bucket.key.toString(), label: bucket.key.toString() },
|
||||
{ value: b.key.toString(), label: b.key.toString() },
|
||||
].concat(innerNode.path);
|
||||
return {
|
||||
...innerNode,
|
||||
|
@ -40,7 +40,7 @@ export function extractGroupPaths(
|
|||
);
|
||||
}
|
||||
const nodeItem = createNodeItem(options, node, bucket);
|
||||
const path = [{ value: key }].concat(nodeItem.path);
|
||||
const path = [{ value: key, label: key }].concat(nodeItem.path);
|
||||
return acc.concat({
|
||||
...nodeItem,
|
||||
path,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { cloneDeep, set } from 'lodash';
|
||||
import { cloneDeep, get, set } from 'lodash';
|
||||
|
||||
import { InfraPathFilterInput, InfraPathInput } from '../../../../../graphql/types';
|
||||
import {
|
||||
|
@ -18,7 +18,7 @@ export const groupByProcessor = (options: InfraProcesorRequestOptions) => {
|
|||
return (doc: InfraESSearchBody) => {
|
||||
const result = cloneDeep(doc);
|
||||
const { groupBy } = options.nodeOptions;
|
||||
let aggs = {};
|
||||
let aggs = get(result, 'aggs.waffle.aggs.nodes.aggs', {});
|
||||
set(result, 'aggs.waffle.aggs.nodes.aggs', aggs);
|
||||
groupBy.forEach((grouping: InfraPathInput, index: number) => {
|
||||
if (isGroupByTerms(grouping)) {
|
||||
|
@ -49,7 +49,7 @@ export const groupByProcessor = (options: InfraProcesorRequestOptions) => {
|
|||
),
|
||||
},
|
||||
};
|
||||
set(aggs, `${grouping.id}`, filtersAgg);
|
||||
set(aggs, `path_${index}`, filtersAgg);
|
||||
aggs = filtersAgg.aggs;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
import { cloneDeep, set } from 'lodash';
|
||||
|
||||
import { InfraESSearchBody, InfraNodeType, InfraProcesorRequestOptions } from '../../adapter_types';
|
||||
import { NODE_REQUEST_PARTITION_FACTOR, NODE_REQUEST_PARTITION_SIZE } from '../../constants';
|
||||
import {
|
||||
NAME_FIELDS,
|
||||
NODE_REQUEST_PARTITION_FACTOR,
|
||||
NODE_REQUEST_PARTITION_SIZE,
|
||||
} from '../../constants';
|
||||
|
||||
const nodeTypeToField = (options: InfraProcesorRequestOptions): string => {
|
||||
const { fields } = options.nodeOptions.sourceConfiguration;
|
||||
|
@ -22,6 +26,7 @@ const nodeTypeToField = (options: InfraProcesorRequestOptions): string => {
|
|||
};
|
||||
|
||||
export const nodesProcessor = (options: InfraProcesorRequestOptions) => {
|
||||
const { fields } = options.nodeOptions.sourceConfiguration;
|
||||
return (doc: InfraESSearchBody) => {
|
||||
const result = cloneDeep(doc);
|
||||
const field = nodeTypeToField(options);
|
||||
|
@ -35,6 +40,16 @@ export const nodesProcessor = (options: InfraProcesorRequestOptions) => {
|
|||
order: { _key: 'asc' },
|
||||
size: NODE_REQUEST_PARTITION_SIZE * NODE_REQUEST_PARTITION_FACTOR,
|
||||
});
|
||||
|
||||
set(result, 'aggs.waffle.aggs.nodes.aggs', {
|
||||
nodeDetails: {
|
||||
top_hits: {
|
||||
size: 1,
|
||||
_source: { includes: [NAME_FIELDS[options.nodeType]] },
|
||||
sort: [{ [fields.timestamp]: { order: 'desc' } }],
|
||||
},
|
||||
},
|
||||
});
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -49,10 +49,10 @@ export class InfraConfigurationSourcesAdapter implements InfraSourcesAdapter {
|
|||
}
|
||||
|
||||
const DEFAULT_FIELDS = {
|
||||
container: 'docker.container.name',
|
||||
host: 'beat.hostname',
|
||||
container: 'docker.container.id',
|
||||
host: 'host.name',
|
||||
message: ['message', '@message'],
|
||||
pod: 'kubernetes.pod.name',
|
||||
pod: 'kubernetes.pod.uid',
|
||||
tiebreaker: '_doc',
|
||||
timestamp: '@timestamp',
|
||||
};
|
||||
|
|
|
@ -17,30 +17,32 @@ export class InfraMetadataDomain {
|
|||
public async getMetadata(
|
||||
req: InfraFrameworkRequest,
|
||||
sourceId: string,
|
||||
nodeName: string,
|
||||
nodeId: string,
|
||||
nodeType: string
|
||||
) {
|
||||
const sourceConfiguration = await this.libs.sources.getConfiguration(sourceId);
|
||||
const metricsPromise = this.adapter.getMetricMetadata(
|
||||
req,
|
||||
sourceConfiguration,
|
||||
nodeName,
|
||||
nodeId,
|
||||
nodeType
|
||||
);
|
||||
const logsPromise = this.adapter.getLogMetadata(req, sourceConfiguration, nodeName, nodeType);
|
||||
const logsPromise = this.adapter.getLogMetadata(req, sourceConfiguration, nodeId, nodeType);
|
||||
|
||||
const metrics = await metricsPromise;
|
||||
const logs = await logsPromise;
|
||||
|
||||
const metricMetadata = pickMetadata(metrics).map(entry => {
|
||||
const metricMetadata = pickMetadata(metrics.buckets).map(entry => {
|
||||
return { name: entry, source: 'metrics' };
|
||||
});
|
||||
|
||||
const logMetadata = pickMetadata(logs).map(entry => {
|
||||
const logMetadata = pickMetadata(logs.buckets).map(entry => {
|
||||
return { name: entry, source: 'logs' };
|
||||
});
|
||||
|
||||
return metricMetadata.concat(logMetadata);
|
||||
const id = metrics.id || logs.id;
|
||||
const name = metrics.name || logs.name || id;
|
||||
return { id, name, features: metricMetadata.concat(logMetadata) };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,12 @@ const metadataTests: KbnTestProvider = ({ getService }) => {
|
|||
})
|
||||
.then(resp => {
|
||||
const metadata = resp.data.source.metadataByNode;
|
||||
expect(metadata.length).to.be(14);
|
||||
if (metadata) {
|
||||
expect(metadata.features.length).to.be(14);
|
||||
expect(metadata.name).to.equal('demo-stack-nginx-01');
|
||||
} else {
|
||||
throw new Error('Metadata should never be empty');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -33,9 +33,9 @@ const sourcesTests: KbnTestProvider = ({ getService }) => {
|
|||
// shipped default values
|
||||
expect(sourceConfiguration.metricAlias).to.be('metricbeat-*');
|
||||
expect(sourceConfiguration.logAlias).to.be('filebeat-*');
|
||||
expect(sourceConfiguration.fields.container).to.be('docker.container.name');
|
||||
expect(sourceConfiguration.fields.host).to.be('beat.hostname');
|
||||
expect(sourceConfiguration.fields.pod).to.be('kubernetes.pod.name');
|
||||
expect(sourceConfiguration.fields.container).to.be('docker.container.id');
|
||||
expect(sourceConfiguration.fields.host).to.be('host.name');
|
||||
expect(sourceConfiguration.fields.pod).to.be('kubernetes.pod.uid');
|
||||
|
||||
// test data in x-pack/test/functional/es_archives/infra/data.json.gz
|
||||
expect(sourceStatus.indexFields.length).to.be(1765);
|
||||
|
|
Loading…
Reference in a new issue