Skip to content

Commit

Permalink
feat: use new kibana api for pushing monitors (#649)
Browse files Browse the repository at this point in the history
* feat: use new kibana api for pushing monitors

Co-authored-by: Andrew Cholakian <andrewvc@elastic.co>
Co-authored-by: shahzad31 <shahzad31comp@gmail.com>
Co-authored-by: Dominique Clarke <dominique.clarke@elastic.co>
  • Loading branch information
4 people authored Dec 5, 2022
1 parent 051dda8 commit 8e0f4e3
Show file tree
Hide file tree
Showing 17 changed files with 688 additions and 349 deletions.
18 changes: 0 additions & 18 deletions __tests__/push/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,23 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Push API handle streamed response 1`] = `
"> preparing all monitors
> creating all monitors
> 2 monitors created successfully
> journey 2 monitor updated successfully
> deleting all stale monitors
✓ Pushed: http://localhost:54455/stream/app/uptime/manage-monitors/all
"
`;

exports[`Push API handle sync response 1`] = `
"> preparing all monitors
> creating all monitors
> deleting all stale monitors
✓ Pushed: http://localhost:54455/sync/app/uptime/manage-monitors/all
"
`;

exports[`Push error on empty project id 1`] = `
"Aborted. Invalid synthetics project settings.
Expand Down
2 changes: 1 addition & 1 deletion __tests__/push/__snapshots__/request.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Push api request format 404 error 1`] = `"✖ Please check your kibana url and try again - 404:Not Found"`;
exports[`Push api request format 404 error 1`] = `"✖ Please check your kibana url: http://foo and try again - 404:Not Found"`;

exports[`Push api request format api error 1`] = `
"✖ Error
Expand Down
182 changes: 55 additions & 127 deletions __tests__/push/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { mkdir, rm, writeFile } from 'fs/promises';
import { join } from 'path';
import { Monitor } from '../../src/dsl/monitor';
import { formatDuplicateError } from '../../src/push';
import { APISchema } from '../../src/push/monitor';
import { createKibanaTestServer } from '../utils/kibana-test-server';
import { Server } from '../utils/server';
import { CLIMock } from '../utils/test-config';

Expand Down Expand Up @@ -69,7 +69,7 @@ describe('Push', () => {
it('error when project is not setup', async () => {
const output = await runPush();
expect(output).toContain(
'Aborted (missing synthetics config file), Project not set up corrrectly.'
'Aborted (missing synthetics config file), Project not set up correctly.'
);
});

Expand Down Expand Up @@ -130,27 +130,6 @@ journey('journey 1', () => monitor.use({ id: 'j1', schedule: 8 }));`
await rm(testJourney, { force: true });
});

it('push with different id when overriden', async () => {
await fakeProjectSetup(
{ id: 'test-project', space: 'dummy', url: 'http://localhost:8080' },
{ locations: ['test-loc'], schedule: 3 }
);
const testJourney = join(PROJECT_DIR, 'test.journey.ts');
await writeFile(
testJourney,
`import {journey, monitor} from '../../../src';
journey('journey 1', () => monitor.use({ id: 'j1' }));`
);
const output = await runPush(
[...DEFAULT_ARGS, '-y', '--id', 'new-project'],
{
TEST_OVERRIDE: true,
}
);
expect(output).toContain('preparing all monitors');
await rm(testJourney, { force: true });
});

it('errors on duplicate browser monitors', async () => {
await fakeProjectSetup(
{ id: 'test-project' },
Expand Down Expand Up @@ -214,110 +193,59 @@ heartbeat.monitors:
expect(formatDuplicateError(duplicates as Set<Monitor>)).toMatchSnapshot();
});

it('abort when delete is skipped', async () => {
await fakeProjectSetup(
{ id: 'test-project' },
{ locations: ['test-loc'], schedule: 3 }
);
const output = await runPush([...DEFAULT_ARGS, '-y'], {
TEST_OVERRIDE: false,
});
expect(output).toContain('Push command Aborted');
});

it('delete entire project with --yes flag', async () => {
await fakeProjectSetup(
{ id: 'test-project', space: 'dummy', url: 'http://localhost:8080' },
{ locations: ['test-loc'], schedule: 3 }
);
const output = await runPush([...DEFAULT_ARGS, '-y']);
expect(output).toContain('deleting all stale monitors');
});

it('delete entire project with overrides', async () => {
await fakeProjectSetup(
{ id: 'test-project', space: 'dummy', url: 'http://localhost:8080' },
{ locations: ['test-loc'], schedule: 3 }
);
const output = await runPush([...DEFAULT_ARGS, '-y'], {
TEST_OVERRIDE: true,
});
expect(output).toContain('deleting all stale monitors');
});

describe('API', () => {
let server: Server;
beforeAll(async () => {
server = await Server.create({ port: 54455 });
const apiRes = { failedMonitors: [], failedStaleMonitors: [] };
server.route(
'/sync/s/dummy/api/synthetics/service/project/monitors',
(req, res) => {
res.end(JSON.stringify(apiRes));
}
);
server.route(
'/stream/s/dummy/api/synthetics/service/project/monitors',
async (req, res) => {
await new Promise(r => setTimeout(r, 20));
req.on('data', chunks => {
const schema = JSON.parse(chunks.toString()) as APISchema;
res.write(
JSON.stringify(
schema.monitors.length + ' monitors created successfully'
) + '\n'
);
res.write(
JSON.stringify(
`${schema.monitors[1].name} monitor updated successfully`
) + '\n'
);
if (!schema.keep_stale) {
// write more than the stream buffer to check the broken NDJSON data
res.write(
JSON.stringify(Buffer.from('a'.repeat(70000)).toString()) + '\n'
);
}
});
req.on('end', () => {
res.end(JSON.stringify(apiRes));
});
}
);
await fakeProjectSetup(
{
id: 'test-project',
space: 'dummy',
},
{ locations: ['test-loc'], schedule: 3 }
);

await writeFile(
join(PROJECT_DIR, 'test.journey.ts'),
`import {journey, monitor} from '../../../src/index';
journey('journey 1', () => monitor.use({ id: 'j1' }));
journey('journey 2', () => monitor.use({ id: 'j2' }));`
);
});
afterAll(async () => {
await server.close();
});

it('handle sync response', async () => {
const output = await runPush([
'--url',
server.PREFIX + '/sync',
...DEFAULT_ARGS,
]);
expect(output).toMatchSnapshot();
});
it('handle streamed response', async () => {
const output = await runPush([
'--url',
server.PREFIX + '/stream',
...DEFAULT_ARGS,
]);
expect(output).toMatchSnapshot();
['8.5.0', '8.6.0'].forEach(version => {
describe('API: ' + version, () => {
let server: Server;
const deleteProgress =
'8.5.0' === version
? 'deleting all stale monitors'
: 'deleting 2 monitors';
beforeAll(async () => {
server = await createKibanaTestServer(version);
await fakeProjectSetup(
{ id: 'test-project', space: 'dummy', url: server.PREFIX },
{ locations: ['test-loc'], schedule: 3 }
);
});
afterAll(async () => {
await server.close();
});

it('abort when delete is skipped', async () => {
const output = await runPush([...DEFAULT_ARGS, '-y'], {
TEST_OVERRIDE: false,
});
expect(output).toContain('Push command Aborted');
});

it('delete entire project with --yes flag', async () => {
const output = await runPush([...DEFAULT_ARGS, '-y']);
expect(output).toContain(deleteProgress);
});

it('delete entire project with prompt override', async () => {
const output = await runPush([...DEFAULT_ARGS, '-y'], {
TEST_OVERRIDE: true,
});
expect(output).toContain(deleteProgress);
});

it('push journeys', async () => {
const testJourney = join(PROJECT_DIR, 'test.journey.ts');
await writeFile(
testJourney,
`import {journey, monitor} from '../../../src/index';
journey('journey 1', () => monitor.use({ id: 'j1' }));
journey('journey 2', () => monitor.use({ id: 'j2' }));`
);
const output = await runPush();
expect(output).toContain('Pushing monitors for project: test-project');
expect(output).toContain('bundling 2 monitors');
expect(output).toContain('creating or updating 2 monitors');
expect(output).toContain(deleteProgress);
expect(output).toContain('✓ Pushed:');
await rm(testJourney, { force: true });
});
});
});
});
71 changes: 31 additions & 40 deletions __tests__/push/monitor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ import { join } from 'path';
import { generateTempPath } from '../../src/helpers';
import {
buildMonitorSchema,
createMonitors,
createLightweightMonitors,
diffMonitors,
parseSchedule,
} from '../../src/push/monitor';
import { Server } from '../utils/server';
import { createTestMonitor } from '../utils/test-config';

describe('Monitors', () => {
const monitor = createTestMonitor('example.journey.ts');
let server: Server;
beforeAll(async () => {
server = await Server.create();
Expand All @@ -48,29 +47,55 @@ describe('Monitors', () => {
process.env.NO_COLOR = '';
});

it('diff monitors', () => {
const local = [
{ journey_id: 'j1', hash: 'hash1' },
{ journey_id: 'j2', hash: 'hash2' },
{ journey_id: 'j3', hash: 'hash3' },
{ journey_id: 'j4', hash: 'hash4' },
];
const remote = [
{ journey_id: 'j1', hash: 'hash1' },
{ journey_id: 'j2', hash: 'hash2-changed' },
{ journey_id: 'j4', hash: '' }, // Hash reset in UI
{ journey_id: 'j5', hash: 'hash5' },
];
const result = diffMonitors(local, remote);
expect(Array.from(result.newIDs)).toEqual(['j3']);
expect(Array.from(result.changedIDs)).toEqual(['j2', 'j4']);
expect(Array.from(result.removedIDs)).toEqual(['j5']);
expect(Array.from(result.unchangedIDs)).toEqual(['j1']);
});

it('build lightweight monitor schema', async () => {
const schema = await buildMonitorSchema([
createTestMonitor('heartbeat.yml', 'http'),
]);
const schema = await buildMonitorSchema(
[createTestMonitor('heartbeat.yml', 'http')],
true
);
expect(schema[0]).toEqual({
id: 'test-monitor',
name: 'test',
schedule: 10,
type: 'http',
enabled: true,
hash: 'fS9hbiqcopwk3gMeDrqMgyp0mEpeyFWy6F/YFSnfWPE=',
locations: ['europe-west2-a', 'australia-southeast1-a'],
privateLocations: ['germany'],
});
});

it('build browser monitor schema', async () => {
const schema = await buildMonitorSchema([monitor]);
const schema = await buildMonitorSchema(
[createTestMonitor('example.journey.ts')],
true
);
expect(schema[0]).toEqual({
id: 'test-monitor',
name: 'test',
schedule: 10,
type: 'browser',
enabled: true,
hash: 'I+bYcE74C35IaKpHeN04wBfinO3qEiqlcaksKFAmkBg=',
locations: ['europe-west2-a', 'australia-southeast1-a'],
privateLocations: ['germany'],
content: expect.any(String),
Expand All @@ -97,40 +122,6 @@ describe('Monitors', () => {
expect(parseSchedule('@every 10h2m10s')).toBe(60);
});

it('api schema', async () => {
server.route(
'/s/dummy/api/synthetics/service/project/monitors',
(req, res) => {
let data = '';
req.on('data', chunks => {
data += chunks;
});
req.on('end', () => {
// Write the post data back
res.end(data.toString());
});
}
);
const schema = await buildMonitorSchema([monitor]);
const { statusCode, body } = await createMonitors(
schema,
{
url: `${server.PREFIX}`,
auth: 'apiKey',
id: 'blah',
space: 'dummy',
},
false
);

expect(statusCode).toBe(200);
expect(await body.json()).toEqual({
project: 'blah',
keep_stale: false,
monitors: schema,
});
});

describe('Lightweight monitors', () => {
const PROJECT_DIR = generateTempPath();
const HB_SOURCE = join(PROJECT_DIR, 'heartbeat.yml');
Expand Down
2 changes: 1 addition & 1 deletion __tests__/push/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ describe('Push api request', () => {

it('format 404 error', () => {
const message = 'Not Found';
expect(formatNotFoundError(message)).toMatchSnapshot();
expect(formatNotFoundError('http://foo', message)).toMatchSnapshot();
});

it('format failed monitors', () => {
Expand Down
Loading

0 comments on commit 8e0f4e3

Please sign in to comment.