Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Ingest Manager] Don't retain POST /setup results. fixes #74587 #75372

Merged
merged 23 commits into from
Aug 20, 2020
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
bf2196d
add retries for registry requests.
Aug 6, 2020
73a7e04
Fix TS issue. Add link to node-fetch error docs
Aug 6, 2020
a565c02
Restore some accidentally deleted code.
Aug 6, 2020
877ecd7
Add more comments. Remove logging.
Aug 6, 2020
2a2fd17
Merge branch 'master' into registry-retry
elasticmachine Aug 7, 2020
bf973b7
Merge branch 'master' into registry-retry
elasticmachine Aug 10, 2020
3440dd7
Add tests for plugin setup service & handlers
Aug 11, 2020
f6a8272
Add tests for Registry retry logic
Aug 12, 2020
44ed63b
Extract setup retry logic to separate function/file
Aug 12, 2020
a077988
Add tests for setup retry logic
Aug 12, 2020
ab56cfa
Merge branch 'master' into registry-retry
elasticmachine Aug 12, 2020
85cf9b8
More straightforward(?) tests for setup caching
Aug 13, 2020
b7ce43e
Merge branch 'master' into registry-retry
elasticmachine Aug 13, 2020
27a0252
Revert cached setup. Still limit 1 call at a time
Aug 13, 2020
e220bbd
Merge branch 'master' into registry-retry
elasticmachine Aug 14, 2020
409ef85
Merge branch 'master' into registry-retry
elasticmachine Aug 17, 2020
a7d7261
New name & tests for one-at-a-time /setup behavior
Aug 18, 2020
a6bd2d2
More (better?) renaming
Aug 18, 2020
fe66914
Fix name in test description
Aug 18, 2020
3a89cde
Fix spelling typo.
Aug 18, 2020
deafa13
Remove registry retry code & tests
Aug 18, 2020
a913911
Use async fn's .catch to avoid unhandled rejection
Aug 19, 2020
55d7bba
Merge branch 'master' of github.com:elastic/kibana into 74587-retry-f…
Aug 20, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions x-pack/plugins/ingest_manager/server/routes/setup/handlers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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 { xpackMocks } from '../../../../../../x-pack/mocks';
import { httpServerMock } from 'src/core/server/mocks';
import { PostIngestSetupResponse } from '../../../common';
import { RegistryError } from '../../errors';
import { createAppContextStartContractMock } from '../../mocks';
import { ingestManagerSetupHandler } from './handlers';
import { appContextService } from '../../services/app_context';
import { setupIngestManager } from '../../services/setup';

jest.mock('../../services/setup', () => {
return {
setupIngestManager: jest.fn(),
};
});

const mockSetupIngestManager = setupIngestManager as jest.MockedFunction<typeof setupIngestManager>;

describe('ingestManagerSetupHandler', () => {
let context: ReturnType<typeof xpackMocks.createRequestHandlerContext>;
let response: ReturnType<typeof httpServerMock.createResponseFactory>;
let request: ReturnType<typeof httpServerMock.createKibanaRequest>;

beforeEach(async () => {
context = xpackMocks.createRequestHandlerContext();
response = httpServerMock.createResponseFactory();
request = httpServerMock.createKibanaRequest({
method: 'post',
path: '/api/ingest_manager/setup',
});
// prevents `Logger not set.` and other appContext errors
appContextService.start(createAppContextStartContractMock());
});

afterEach(async () => {
jest.clearAllMocks();
appContextService.stop();
});

it('POST /setup succeeds w/200 and body of resolved value', async () => {
mockSetupIngestManager.mockImplementation(() => Promise.resolve({ isIntialized: true }));
await ingestManagerSetupHandler(context, request, response);

const expectedBody: PostIngestSetupResponse = { isInitialized: true };
expect(response.customError).toHaveBeenCalledTimes(0);
expect(response.ok).toHaveBeenCalledWith({ body: expectedBody });
});

it('POST /setup fails w/500 on custom error', async () => {
mockSetupIngestManager.mockImplementation(() =>
Promise.reject(new Error('SO method mocked to throw'))
);
await ingestManagerSetupHandler(context, request, response);

expect(response.customError).toHaveBeenCalledTimes(1);
expect(response.customError).toHaveBeenCalledWith({
statusCode: 500,
body: {
message: 'SO method mocked to throw',
},
});
});

it('POST /setup fails w/502 on RegistryError', async () => {
mockSetupIngestManager.mockImplementation(() =>
Promise.reject(new RegistryError('Registry method mocked to throw'))
);

await ingestManagerSetupHandler(context, request, response);
expect(response.customError).toHaveBeenCalledTimes(1);
expect(response.customError).toHaveBeenCalledWith({
statusCode: 502,
body: {
message: 'Registry method mocked to throw',
},
});
});
});
40 changes: 26 additions & 14 deletions x-pack/plugins/ingest_manager/server/routes/setup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import {
} from './handlers';
import { PostFleetSetupRequestSchema } from '../../types';

export const registerRoutes = (router: IRouter, config: IngestManagerConfigType) => {
// Ingest manager setup
export const registerIngestManagerSetupRoute = (router: IRouter) => {
router.post(
{
path: SETUP_API_ROUTE,
Expand All @@ -26,12 +25,20 @@ export const registerRoutes = (router: IRouter, config: IngestManagerConfigType)
},
ingestManagerSetupHandler
);
};

if (!config.fleet.enabled) {
return;
}
export const registerCreateFleetSetupRoute = (router: IRouter) => {
router.post(
{
path: FLEET_SETUP_API_ROUTES.CREATE_PATTERN,
validate: PostFleetSetupRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
createFleetSetupHandler
);
};

// Get Fleet setup
export const registerGetFleetStatusRoute = (router: IRouter) => {
router.get(
{
path: FLEET_SETUP_API_ROUTES.INFO_PATTERN,
Expand All @@ -40,14 +47,19 @@ export const registerRoutes = (router: IRouter, config: IngestManagerConfigType)
},
getFleetStatusHandler
);
};

export const registerRoutes = (router: IRouter, config: IngestManagerConfigType) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I split this function up and exported the individual register*Route functions for some now-removed tests. I left it because it seems useful is common in other Kibana plugins.

#74507 (comment)

// Ingest manager setup
registerIngestManagerSetupRoute(router);

if (!config.fleet.enabled) {
return;
}

// Get Fleet setup
registerGetFleetStatusRoute(router);

// Create Fleet setup
router.post(
{
path: FLEET_SETUP_API_ROUTES.CREATE_PATTERN,
validate: PostFleetSetupRequestSchema,
options: { tags: [`access:${PLUGIN_ID}-all`] },
},
createFleetSetupHandler
);
registerCreateFleetSetupRoute(router);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export function bufferToStream(buffer: Buffer): PassThrough {
return stream;
}

export function streamToString(stream: NodeJS.ReadableStream): Promise<string> {
export function streamToString(stream: NodeJS.ReadableStream | Buffer): Promise<string> {
if (stream instanceof Buffer) return Promise.resolve(stream.toString());
return new Promise((resolve, reject) => {
const body: string[] = [];
stream.on('data', (chunk: string) => body.push(chunk));
Expand Down
76 changes: 47 additions & 29 deletions x-pack/plugins/ingest_manager/server/services/setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,59 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { xpackMocks } from '../../../../../x-pack/mocks';
import { createAppContextStartContractMock } from '../mocks';
import { appContextService } from './app_context';
import { setupIngestManager } from './setup';
import { savedObjectsClientMock } from 'src/core/server/mocks';

describe('setupIngestManager', () => {
it('returned promise should reject if errors thrown', async () => {
const { savedObjectsClient, callClusterMock } = makeErrorMocks();
const setupPromise = setupIngestManager(savedObjectsClient, callClusterMock);
await expect(setupPromise).rejects.toThrow('mocked');
const mockedMethodThrowsError = () =>
jest.fn().mockImplementation(() => {
throw new Error('SO method mocked to throw');
});
});

function makeErrorMocks() {
jest.mock('./app_context'); // else fails w/"Logger not set."
jest.mock('./epm/registry/registry_url', () => {
return {
fetchUrl: () => {
throw new Error('mocked registry#fetchUrl');
},
};
class CustomTestError extends Error {}
const mockedMethodThrowsCustom = () =>
jest.fn().mockImplementation(() => {
throw new CustomTestError('method mocked to throw');
});

const callClusterMock = jest.fn();
const savedObjectsClient = savedObjectsClientMock.create();
savedObjectsClient.find = jest.fn().mockImplementation(() => {
throw new Error('mocked SO#find');
});
savedObjectsClient.get = jest.fn().mockImplementation(() => {
throw new Error('mocked SO#get');
describe('setupIngestManager', () => {
let context: ReturnType<typeof xpackMocks.createRequestHandlerContext>;

beforeEach(async () => {
context = xpackMocks.createRequestHandlerContext();
// prevents `Logger not set.` and other appContext errors
appContextService.start(createAppContextStartContractMock());
});
savedObjectsClient.update = jest.fn().mockImplementation(() => {
throw new Error('mocked SO#update');

afterEach(async () => {
jest.clearAllMocks();
appContextService.stop();
});

return {
savedObjectsClient,
callClusterMock,
};
}
describe('should reject with any error thrown underneath', () => {
it('SO client throws plain Error', async () => {
const soClient = context.core.savedObjects.client;
soClient.create = mockedMethodThrowsError();
soClient.find = mockedMethodThrowsError();
soClient.get = mockedMethodThrowsError();
soClient.update = mockedMethodThrowsError();

const setupPromise = setupIngestManager(soClient, jest.fn());
await expect(setupPromise).rejects.toThrow('SO method mocked to throw');
await expect(setupPromise).rejects.toThrow(Error);
});

it('SO client throws other error', async () => {
const soClient = context.core.savedObjects.client;
soClient.create = mockedMethodThrowsCustom();
soClient.find = mockedMethodThrowsCustom();
soClient.get = mockedMethodThrowsCustom();
soClient.update = mockedMethodThrowsCustom();

const setupPromise = setupIngestManager(soClient, jest.fn());
await expect(setupPromise).rejects.toThrow('method mocked to throw');
await expect(setupPromise).rejects.toThrow(CustomTestError);
});
});
});
Loading