Skip to content

Commit

Permalink
[Uptime] Remove Scripted Metric Query (#69229) (#71242)
Browse files Browse the repository at this point in the history
Resolves #68096 , removing unnecessary scripted metric query from overview page and unifying the Check and Ping types.

This simplifies the types in a number of ways, and reduces the total quantity of code to execute the queries for the overview page. It also fixes the Tls and related types which were inconsistent and presented a problem here since they are used by this JS. There are now three stages where before there were four:

    Find potential matches: where we determine which monitor IDs are eligible for the overview page
    Refine potential matches: where we determine which ones actually match and return the summary documents for each location to build the MonitorSummary object
    Get monitor histograms: where we calculate the histograms for each monitor. In the future we might make this a separate API call.

This improves the overall code structure, and leaves the test coverage about the same depending on how you look at it. I think we can do more to improve the quality of code / tests here, but this seemed like a good initial place to draw the line for now.

In perfunctory testing on our internal observability clusters I saw perf improve from 2.5s to 1.1s on the Uptime homepage with no filters. So, it looks like there are potentially perf improvements (no real benchmarking was done).

Previously, this returned all pings from the latest check group. This was not actually used anywhere, only the summary pings are required for the current UI, so we now only return those from the list API as this saves a query.
  • Loading branch information
andrewvc authored Jul 9, 2020
1 parent ebfd803 commit aca9581
Show file tree
Hide file tree
Showing 49 changed files with 1,683 additions and 1,947 deletions.
91 changes: 27 additions & 64 deletions x-pack/plugins/uptime/common/runtime_types/monitor/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,68 +5,9 @@
*/

import * as t from 'io-ts';

export const CheckMonitorType = t.intersection([
t.partial({
name: t.string,
ip: t.union([t.array(t.union([t.string, t.null])), t.string, t.null]),
}),
t.type({
status: t.string,
}),
]);

export const CheckType = t.intersection([
t.partial({
agent: t.partial({
id: t.string,
}),
container: t.type({
id: t.string,
}),
kubernetes: t.type({
pod: t.type({
uid: t.string,
}),
}),
observer: t.type({
geo: t.partial({
name: t.string,
location: t.partial({
lat: t.number,
lon: t.number,
}),
}),
}),
}),
t.type({
monitor: CheckMonitorType,
timestamp: t.number,
}),
]);

export type Check = t.TypeOf<typeof CheckType>;
import { PingType } from '../ping/ping';

export const StateType = t.intersection([
t.partial({
checks: t.array(CheckType),
observer: t.partial({
geo: t.partial({
name: t.array(t.string),
}),
}),
summary: t.partial({
up: t.number,
down: t.number,
geo: t.partial({
name: t.string,
location: t.partial({
lat: t.number,
lon: t.number,
}),
}),
}),
}),
t.type({
timestamp: t.string,
url: t.partial({
Expand All @@ -76,9 +17,31 @@ export const StateType = t.intersection([
port: t.number,
scheme: t.string,
}),
summaryPings: t.array(PingType),
summary: t.partial({
status: t.string,
up: t.number,
down: t.number,
}),
monitor: t.partial({
name: t.string,
}),
}),
t.partial({
tls: t.partial({
not_after: t.union([t.string, t.null]),
not_before: t.union([t.string, t.null]),
}),
observer: t.type({
geo: t.type({
name: t.array(t.string),
}),
}),
}),
]);

export type MonitorSummaryState = t.TypeOf<typeof StateType>;

export const HistogramPointType = t.type({
timestamp: t.number,
up: t.union([t.number, t.undefined]),
Expand All @@ -105,18 +68,18 @@ export const MonitorSummaryType = t.intersection([

export type MonitorSummary = t.TypeOf<typeof MonitorSummaryType>;

export const MonitorSummaryResultType = t.intersection([
export const MonitorSummariesResultType = t.intersection([
t.partial({
summaries: t.array(MonitorSummaryType),
totalSummaryCount: t.number,
}),
t.type({
summaries: t.array(MonitorSummaryType),
prevPagePagination: t.union([t.string, t.null]),
nextPagePagination: t.union([t.string, t.null]),
totalSummaryCount: t.number,
}),
]);

export type MonitorSummaryResult = t.TypeOf<typeof MonitorSummaryResultType>;
export type MonitorSummariesResult = t.TypeOf<typeof MonitorSummariesResultType>;

export const FetchMonitorStatesQueryArgsType = t.intersection([
t.partial({
Expand Down
71 changes: 70 additions & 1 deletion x-pack/plugins/uptime/common/runtime_types/ping/ping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,51 @@ export const HttpResponseBodyType = t.partial({

export type HttpResponseBody = t.TypeOf<typeof HttpResponseBodyType>;

export const TlsType = t.partial({
const ECSDistinguishedName = t.type({
common_name: t.string,
distinguished_name: t.string,
});

export const X509ExpiryType = t.type({
not_after: t.string,
not_before: t.string,
});

export type X509Expiry = t.TypeOf<typeof X509ExpiryType>;

export const X509Type = t.intersection([
t.type({
issuer: ECSDistinguishedName,
subject: ECSDistinguishedName,
serial_number: t.string,
public_key_algorithm: t.string,
signature_algorithm: t.string,
}),
X509ExpiryType,
t.partial({
public_key_curve: t.string,
public_key_exponent: t.number,
public_key_size: t.number,
}),
]);

export type X509 = t.TypeOf<typeof X509Type>;

export const TlsType = t.partial({
// deprecated in favor of server.x509.not_after/not_before
certificate_not_valid_after: t.string,
certificate_not_valid_before: t.string,
cipher: t.string,
established: t.boolean,
server: t.partial({
hash: t.type({
sha256: t.string,
sha1: t.string,
}),
x509: X509Type,
}),
});

export type Tls = t.TypeOf<typeof TlsType>;

export const MonitorType = t.intersection([
Expand Down Expand Up @@ -123,6 +163,11 @@ export const PingType = t.intersection([
observer: t.partial({
geo: t.partial({
name: t.string,
location: t.union([
t.string,
t.partial({ lat: t.number, lon: t.number }),
t.partial({ lat: t.string, lon: t.string }),
]),
}),
}),
resolve: t.partial({
Expand Down Expand Up @@ -156,6 +201,30 @@ export const PingType = t.intersection([

export type Ping = t.TypeOf<typeof PingType>;

// Convenience function for tests etc that makes an empty ping
// object with the minimum of fields.
export const makePing = (f: {
docId?: string;
type?: string;
id?: string;
timestamp?: string;
ip?: string;
status?: string;
duration?: number;
}): Ping => {
return {
docId: f.docId || 'myDocId',
timestamp: f.timestamp || '2020-07-07T01:14:08Z',
monitor: {
id: f.id || 'myId',
type: f.type || 'myType',
ip: f.ip || '127.0.0.1',
status: f.status || 'up',
duration: { us: f.duration || 100000 },
},
};
};

export const PingsResponseType = t.type({
total: t.number,
locations: t.array(t.string),
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../common/constants';

describe('SSL Certificate component', () => {
let monitorTls: Tls;
const dateInTwoMonths = moment().add(2, 'month').toString();
const yesterday = moment().subtract(1, 'day').toString();

beforeEach(() => {
const dateInTwoMonths = moment().add(2, 'month').toString();

monitorTls = {
not_after: dateInTwoMonths,
certificate_not_valid_after: dateInTwoMonths,
certificate_not_valid_before: yesterday,
};

const useDispatchSpy = jest.spyOn(redux, 'useDispatch');
Expand All @@ -32,7 +33,8 @@ describe('SSL Certificate component', () => {

it('shallow renders', () => {
const monitorTls1 = {
not_after: '2020-04-24T11:41:38.200Z',
certificate_not_valid_after: '2020-04-24T11:41:38.200Z',
certificate_not_valid_before: '2019-04-24T11:41:38.200Z',
};
const component = shallowWithRouter(<MonitorSSLCertificate tls={monitorTls1} />);
expect(component).toMatchSnapshot();
Expand All @@ -45,7 +47,8 @@ describe('SSL Certificate component', () => {

it('renders null if invalid date', () => {
monitorTls = {
not_after: 'i am so invalid date',
certificate_not_valid_after: 'i am so invalid date',
certificate_not_valid_before: 'i am so invalid date',
};
const component = renderWithRouter(<MonitorSSLCertificate tls={monitorTls} />);
expect(component).toMatchSnapshot();
Expand All @@ -54,7 +57,8 @@ describe('SSL Certificate component', () => {
it('renders expiration date with a warning state if ssl expiry date is less than 5 days', () => {
const dateIn5Days = moment().add(5, 'day').toString();
monitorTls = {
not_after: dateIn5Days,
certificate_not_valid_after: dateIn5Days,
certificate_not_valid_before: yesterday,
};
const component = mountWithRouter(<MonitorSSLCertificate tls={monitorTls} />);

Expand All @@ -69,7 +73,8 @@ describe('SSL Certificate component', () => {
it('does not render the expiration date with a warning state if expiry date is greater than a month', () => {
const dateIn40Days = moment().add(40, 'day').toString();
monitorTls = {
not_after: dateIn40Days,
certificate_not_valid_after: dateIn40Days,
certificate_not_valid_before: yesterday,
};
const component = mountWithRouter(<MonitorSSLCertificate tls={monitorTls} />);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React from 'react';
import { Link } from 'react-router-dom';
import { EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { Tls } from '../../../../../common/runtime_types';
import { Tls, X509Expiry } from '../../../../../common/runtime_types';
import { CERTIFICATES_ROUTE } from '../../../../../common/constants';
import { MonListDescription, MonListTitle } from './status_bar';
import { CertStatusColumn } from '../../../overview/monitor_list/cert_status_column';
Expand All @@ -21,7 +21,21 @@ interface Props {
}

export const MonitorSSLCertificate = ({ tls }: Props) => {
return tls?.not_after ? (
let expiry: X509Expiry | null = null;
if (tls?.server?.x509) {
expiry = tls.server.x509;
} else if (tls?.certificate_not_valid_after && tls?.certificate_not_valid_before) {
expiry = {
not_after: tls.certificate_not_valid_after,
not_before: tls.certificate_not_valid_before,
};
}

if (!expiry) {
return null;
}

return (
<>
<MonListTitle>
<FormattedMessage
Expand All @@ -33,9 +47,9 @@ export const MonitorSSLCertificate = ({ tls }: Props) => {
<EuiSpacer size="s" />
<MonListDescription>
<Link to={CERTIFICATES_ROUTE} className="eui-displayInline">
<CertStatusColumn cert={tls} boldStyle={true} />
<CertStatusColumn expiry={expiry} boldStyle={true} />
</Link>
</MonListDescription>
</>
) : null;
);
};
Loading

0 comments on commit aca9581

Please sign in to comment.