Skip to content

Commit

Permalink
[Infra UI] Adding links from Infra UI to Uptime (elastic#35993)
Browse files Browse the repository at this point in the history
* [Infra UI] Adding from Infra UI to Uptime

* Removed unused variable

* Adding tests for link generation

* Sometimes the IP address will be an array with [IPv4, IPv6]

* Ensuring only IPv4 addresses are returned; adding tests;

* only showing uptime link for host's with ip addresses
  • Loading branch information
simianhacker committed May 10, 2019
1 parent 581d499 commit 471aba0
Show file tree
Hide file tree
Showing 16 changed files with 311 additions and 4 deletions.
4 changes: 4 additions & 0 deletions x-pack/plugins/infra/common/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ export interface InfraSnapshotNodePath {
value: string;

label: string;

ip?: string | null;
}

export interface InfraSnapshotNodeMetric {
Expand Down Expand Up @@ -868,6 +870,8 @@ export namespace WaffleNodesQuery {
value: string;

label: string;

ip?: string | null;
};

export type Metric = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { createUptimeLink } from './create_uptime_link';
import {
InfraWaffleMapOptions,
InfraWaffleMapLegendMode,
InfraFormatterType,
} from '../../../lib/lib';
import { InfraNodeType } from '../../../../common/graphql/types';
import { InfraSnapshotMetricType } from '../../../graphql/types';

const options: InfraWaffleMapOptions = {
fields: {
container: 'container.id',
pod: 'kubernetes.pod.uid',
host: 'host.name',
message: ['@message'],
timestamp: '@timestanp',
tiebreaker: '@timestamp',
},
formatter: InfraFormatterType.percent,
formatTemplate: '{{value}}',
metric: { type: InfraSnapshotMetricType.cpu },
groupBy: [],
legend: {
type: InfraWaffleMapLegendMode.gradient,
rules: [],
},
};

describe('createUptimeLink()', () => {
it('should work for hosts with ip', () => {
const node = {
pathId: 'host-01',
id: 'host-01',
name: 'host-01',
ip: '10.0.1.2',
path: [],
metric: {
name: InfraSnapshotMetricType.cpu,
value: 0.5,
max: 0.8,
avg: 0.6,
},
};
expect(createUptimeLink(options, InfraNodeType.host, node)).toBe(
'../app/uptime#/?search=host.ip:"10.0.1.2"'
);
});

it('should work for hosts without ip', () => {
const node = {
pathId: 'host-01',
id: 'host-01',
name: 'host-01',
path: [],
metric: {
name: InfraSnapshotMetricType.cpu,
value: 0.5,
max: 0.8,
avg: 0.6,
},
};
expect(createUptimeLink(options, InfraNodeType.host, node)).toBe(
'../app/uptime#/?search=host.name:"host-01"'
);
});

it('should work for pods', () => {
const node = {
pathId: 'pod-01',
id: '29193-pod-02939',
name: 'pod-01',
path: [],
metric: {
name: InfraSnapshotMetricType.cpu,
value: 0.5,
max: 0.8,
avg: 0.6,
},
};
expect(createUptimeLink(options, InfraNodeType.pod, node)).toBe(
'../app/uptime#/?search=kubernetes.pod.uid:"29193-pod-02939"'
);
});

it('should work for container', () => {
const node = {
pathId: 'docker-01',
id: 'docker-1234',
name: 'docker-01',
path: [],
metric: {
name: InfraSnapshotMetricType.cpu,
value: 0.5,
max: 0.8,
avg: 0.6,
},
};
expect(createUptimeLink(options, InfraNodeType.container, node)).toBe(
'../app/uptime#/?search=container.id:"docker-1234"'
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { get } from 'lodash';
import { InfraNodeType } from '../../../graphql/types';
import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../lib/lib';

const BASE_URL = '../app/uptime#/?search=';

export const createUptimeLink = (
options: InfraWaffleMapOptions,
nodeType: InfraNodeType,
node: InfraWaffleMapNode
) => {
if (nodeType === InfraNodeType.host && node.ip) {
return `${BASE_URL}host.ip:"${node.ip}"`;
}
const field = get(options, ['fields', nodeType], '');
return `${BASE_URL}${field ? field + ':' : ''}"${node.id}"`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { injectUICapabilities } from 'ui/capabilities/react';
import { InfraNodeType, InfraTimerangeInput } from '../../graphql/types';
import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib';
import { getNodeDetailUrl, getNodeLogsUrl } from '../../pages/link_to';
import { createUptimeLink } from './lib/create_uptime_link';

interface Props {
options: InfraWaffleMapOptions;
Expand Down Expand Up @@ -89,6 +90,19 @@ export const NodeContextMenu = injectUICapabilities(
}
: undefined;

const uptimeUrl = node.ip
? {
name: intl.formatMessage(
{
id: 'xpack.infra.nodeContextMenu.viewUptimeLink',
defaultMessage: 'View {nodeType} in Uptime',
},
{ nodeType }
),
href: createUptimeLink(options, nodeType, node),
}
: undefined;

const panels: EuiContextMenuPanelDescriptor[] = [
{
id: 0,
Expand Down Expand Up @@ -118,6 +132,7 @@ export const NodeContextMenu = injectUICapabilities(
]
: []),
...(apmTracesUrl ? [apmTracesUrl] : []),
...(uptimeUrl ? [uptimeUrl] : []),
],
},
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export function createWaffleMapNode(node: InfraSnapshotNode): InfraWaffleMapNode
pathId: node.path.map(p => p.value).join('/'),
path: node.path,
id: nodePathItem.value,
ip: nodePathItem.ip,
name: nodePathItem.label || nodePathItem.value,
metric: node.metric,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const waffleNodesQuery = gql`
path {
value
label
ip
}
metric {
name
Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/infra/public/graphql/introspection.json
Original file line number Diff line number Diff line change
Expand Up @@ -2006,6 +2006,14 @@
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "ip",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/infra/public/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ export interface InfraSnapshotNodePath {
value: string;

label: string;

ip?: string | null;
}

export interface InfraSnapshotNodeMetric {
Expand Down Expand Up @@ -868,6 +870,8 @@ export namespace WaffleNodesQuery {
value: string;

label: string;

ip?: string | null;
};

export type Metric = {
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/infra/public/lib/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export interface InfraWaffleMapNode {
pathId: string;
id: string;
name: string;
ip?: string | null;
path: InfraSnapshotNodePath[];
metric: InfraSnapshotNodeMetric;
}
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/infra/server/graphql/snapshot/schema.gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const snapshotSchema: any = gql`
type InfraSnapshotNodePath {
value: String!
label: String!
ip: String
}
type InfraSnapshotNode {
Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/infra/server/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ export interface InfraSnapshotNodePath {
value: string;

label: string;

ip?: string | null;
}

export interface InfraSnapshotNodeMetric {
Expand Down Expand Up @@ -1455,6 +1457,8 @@ export namespace InfraSnapshotNodePathResolvers {
value?: ValueResolver<string, TypeParent, Context>;

label?: LabelResolver<string, TypeParent, Context>;

ip?: IpResolver<string | null, TypeParent, Context>;
}

export type ValueResolver<
Expand All @@ -1467,6 +1471,11 @@ export namespace InfraSnapshotNodePathResolvers {
Parent = InfraSnapshotNodePath,
Context = InfraContext
> = Resolver<R, Parent, Context>;
export type IpResolver<
R = string | null,
Parent = InfraSnapshotNodePath,
Context = InfraContext
> = Resolver<R, Parent, Context>;
}

export namespace InfraSnapshotNodeMetricResolvers {
Expand Down
5 changes: 5 additions & 0 deletions x-pack/plugins/infra/server/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ export const NAME_FIELDS = {
[InfraNodeType.pod]: 'kubernetes.pod.name',
[InfraNodeType.container]: 'container.name',
};
export const IP_FIELDS = {
[InfraNodeType.host]: 'host.ip',
[InfraNodeType.pod]: 'kubernetes.pod.ip',
[InfraNodeType.container]: 'container.ip_address',
};
4 changes: 3 additions & 1 deletion x-pack/plugins/infra/server/lib/snapshot/query_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export const getGroupedNodesSources = (options: InfraSnapshotRequestOptions) =>
return { [`${gb.field}`]: { terms: { field: gb.field } } };
});
sources.push({
id: { terms: { field: options.sourceConfiguration.fields[options.nodeType] } },
id: {
terms: { field: options.sourceConfiguration.fields[options.nodeType] },
},
});
sources.push({
name: { terms: { field: NAME_FIELDS[options.nodeType] } },
Expand Down
78 changes: 78 additions & 0 deletions x-pack/plugins/infra/server/lib/snapshot/response_helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { isIPv4, getIPFromBucket, InfraSnapshotNodeGroupByBucket } from './response_helpers';
import { InfraNodeType } from '../../graphql/types';

describe('InfraOps ResponseHelpers', () => {
describe('isIPv4', () => {
it('should return true for IPv4', () => {
expect(isIPv4('192.168.2.4')).toBe(true);
});
it('should return false for anything else', () => {
expect(isIPv4('0:0:0:0:0:0:0:1')).toBe(false);
});
});

describe('getIPFromBucket', () => {
it('should return IPv4 address', () => {
const bucket: InfraSnapshotNodeGroupByBucket = {
key: {
id: 'example-01',
name: 'example-01',
},
ip: {
hits: {
total: { value: 1 },
hits: [
{
_index: 'metricbeat-2019-01-01',
_type: '_doc',
_id: '29392939',
_score: null,
sort: [],
_source: {
host: {
ip: ['2001:db8:85a3::8a2e:370:7334', '192.168.1.4'],
},
},
},
],
},
},
};
expect(getIPFromBucket(InfraNodeType.host, bucket)).toBe('192.168.1.4');
});
it('should NOT return ipv6 address', () => {
const bucket: InfraSnapshotNodeGroupByBucket = {
key: {
id: 'example-01',
name: 'example-01',
},
ip: {
hits: {
total: { value: 1 },
hits: [
{
_index: 'metricbeat-2019-01-01',
_type: '_doc',
_id: '29392939',
_score: null,
sort: [],
_source: {
host: {
ip: ['2001:db8:85a3::8a2e:370:7334'],
},
},
},
],
},
},
};
expect(getIPFromBucket(InfraNodeType.host, bucket)).toBe(null);
});
});
});
Loading

0 comments on commit 471aba0

Please sign in to comment.