Skip to content

Commit

Permalink
[eventLog] prevent log writing when initialization fails (#71339) (#7…
Browse files Browse the repository at this point in the history
…1662)

Previously, if the initialization of the elasticsearch resources failed
during initialization, the event logger would still try to write events.
Which is somewhat of a catastrophic failure, as typically the logger would
try writing to the alias name, but no alias exists, so a new index would
be created with the name of the alias.  Making it impossible to initialize
successfully later until that index was deleted.

The core initialization calls already returned success indicators, so this
PR just responds to those and prevents the logger from writing to the index
if initialization failed.
  • Loading branch information
pmuellr authored Jul 14, 2020
1 parent d1d3b84 commit 7dcb672
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 13 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/event_log/server/es/context.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const createContextMock = () => {
logger: loggingSystemMock.createLogger(),
esNames: namesMock.create(),
initialize: jest.fn(),
waitTillReady: jest.fn(),
waitTillReady: jest.fn(async () => true),
esAdapter: clusterClientAdapterMock.create(),
initialized: true,
};
Expand Down
19 changes: 15 additions & 4 deletions x-pack/plugins/event_log/server/es/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
import { createEsContext } from './context';
import { LegacyClusterClient, Logger } from '../../../../../src/core/server';
import { elasticsearchServiceMock, loggingSystemMock } from '../../../../../src/core/server/mocks';
jest.mock('../lib/../../../../package.json', () => ({
version: '1.2.3',
}));
export type EsClusterClient = Pick<LegacyClusterClient, 'callAsInternalUser' | 'asScoped'>;
jest.mock('../lib/../../../../package.json', () => ({ version: '1.2.3' }));
jest.mock('./init');
type EsClusterClient = Pick<jest.Mocked<LegacyClusterClient>, 'callAsInternalUser' | 'asScoped'>;

let logger: Logger;
let clusterClient: EsClusterClient;
Expand Down Expand Up @@ -92,4 +91,16 @@ describe('createEsContext', () => {
);
expect(doesIndexTemplateExist).toBeTruthy();
});

test('should handled failed initialization', async () => {
jest.requireMock('./init').initializeEs.mockResolvedValue(false);
const context = createEsContext({
logger,
clusterClientPromise: Promise.resolve(clusterClient),
indexNameRoot: 'test2',
});
context.initialize();
const success = await context.waitTillReady();
expect(success).toBe(false);
});
});
12 changes: 7 additions & 5 deletions x-pack/plugins/event_log/server/es/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,23 @@ class EsContextImpl implements EsContext {

setImmediate(async () => {
try {
await this._initialize();
this.logger.debug('readySignal.signal(true)');
this.readySignal.signal(true);
const success = await this._initialize();
this.logger.debug(`readySignal.signal(${success})`);
this.readySignal.signal(success);
} catch (err) {
this.logger.debug('readySignal.signal(false)');
this.readySignal.signal(false);
}
});
}

// waits till the ES initialization is done, returns true if it was successful,
// false if it was not successful
async waitTillReady(): Promise<boolean> {
return await this.readySignal.wait();
}

private async _initialize() {
await initializeEs(this);
private async _initialize(): Promise<boolean> {
return await initializeEs(this);
}
}
31 changes: 29 additions & 2 deletions x-pack/plugins/event_log/server/event_logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,52 @@ import { delay } from './lib/delay';
import { EVENT_LOGGED_PREFIX } from './event_logger';

const KIBANA_SERVER_UUID = '424-24-2424';
const WRITE_LOG_WAIT_MILLIS = 3000;

describe('EventLogger', () => {
let systemLogger: ReturnType<typeof loggingSystemMock.createLogger>;
let esContext: EsContext;
let esContext: jest.Mocked<EsContext>;
let service: IEventLogService;
let eventLogger: IEventLogger;

beforeEach(() => {
jest.resetAllMocks();
systemLogger = loggingSystemMock.createLogger();
esContext = contextMock.create();
service = new EventLogService({
esContext,
systemLogger,
config: { enabled: true, logEntries: true, indexEntries: false },
config: { enabled: true, logEntries: true, indexEntries: true },
kibanaUUID: KIBANA_SERVER_UUID,
});
eventLogger = service.getLogger({});
});

test('handles successful initialization', async () => {
service.registerProviderActions('test-provider', ['test-action-1']);
eventLogger = service.getLogger({
event: { provider: 'test-provider', action: 'test-action-1' },
});

eventLogger.logEvent({});
await waitForLogEvent(systemLogger);
delay(WRITE_LOG_WAIT_MILLIS); // sleep a bit since event logging is async
expect(esContext.esAdapter.indexDocument).toHaveBeenCalled();
});

test('handles failed initialization', async () => {
service.registerProviderActions('test-provider', ['test-action-1']);
eventLogger = service.getLogger({
event: { provider: 'test-provider', action: 'test-action-1' },
});
esContext.waitTillReady.mockImplementation(async () => false);

eventLogger.logEvent({});
await waitForLogEvent(systemLogger);
delay(WRITE_LOG_WAIT_MILLIS); // sleep a bit longer since event logging is async
expect(esContext.esAdapter.indexDocument).not.toHaveBeenCalled();
});

test('method logEvent() writes expected default values', async () => {
service.registerProviderActions('test-provider', ['test-action-1']);
eventLogger = service.getLogger({
Expand Down
7 changes: 6 additions & 1 deletion x-pack/plugins/event_log/server/event_logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,12 @@ function indexEventDoc(esContext: EsContext, doc: Doc): void {
// whew, the thing that actually writes the event log document!
async function indexLogEventDoc(esContext: EsContext, doc: unknown) {
esContext.logger.debug(`writing to event log: ${JSON.stringify(doc)}`);
await esContext.waitTillReady();
const success = await esContext.waitTillReady();
if (!success) {
esContext.logger.debug(`event log did not initialize correctly, event not written`);
return;
}

await esContext.esAdapter.indexDocument(doc);
esContext.logger.debug(`writing to event log complete`);
}
Expand Down

0 comments on commit 7dcb672

Please sign in to comment.