Skip to content

Commit

Permalink
Do not write UUID file during optimize process
Browse files Browse the repository at this point in the history
  • Loading branch information
joshdover committed Feb 28, 2020
1 parent d474ccf commit 1054bd4
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 76 deletions.
174 changes: 115 additions & 59 deletions src/core/server/uuid/resolve_uuid.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,58 +97,96 @@ describe('resolveInstanceUuid', () => {
});

describe('when file is present and config property is set', () => {
it('writes to file and returns the config uuid if they mismatch', async () => {
const uuid = await resolveInstanceUuid(configService, logger);
expect(uuid).toEqual(DEFAULT_CONFIG_UUID);
expect(writeFile).toHaveBeenCalledWith(
join('data-folder', 'uuid'),
DEFAULT_CONFIG_UUID,
expect.any(Object)
);
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Updating Kibana instance UUID to: CONFIG_UUID (was: FILE_UUID)",
]
`);
describe('when they mismatch', () => {
describe('when syncToFile is true', () => {
it('writes to file and returns the config uuid', async () => {
const uuid = await resolveInstanceUuid({ configService, logger, syncToFile: true });
expect(uuid).toEqual(DEFAULT_CONFIG_UUID);
expect(writeFile).toHaveBeenCalledWith(
join('data-folder', 'uuid'),
DEFAULT_CONFIG_UUID,
expect.any(Object)
);
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Updating Kibana instance UUID to: CONFIG_UUID (was: FILE_UUID)",
]
`);
});
});

describe('when syncTofile is false', () => {
it('does not write to file and returns the config uuid', async () => {
const uuid = await resolveInstanceUuid({ configService, logger, syncToFile: false });
expect(uuid).toEqual(DEFAULT_CONFIG_UUID);
expect(writeFile).not.toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Updating Kibana instance UUID to: CONFIG_UUID (was: FILE_UUID)",
]
`);
});
});
});
it('does not write to file if they match', async () => {
mockReadFile({ uuid: DEFAULT_CONFIG_UUID });
const uuid = await resolveInstanceUuid(configService, logger);
expect(uuid).toEqual(DEFAULT_CONFIG_UUID);
expect(writeFile).not.toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Kibana instance UUID: CONFIG_UUID",
]
`);

describe('when they match', () => {
it('does not write to file', async () => {
mockReadFile({ uuid: DEFAULT_CONFIG_UUID });
const uuid = await resolveInstanceUuid({ configService, logger, syncToFile: true });
expect(uuid).toEqual(DEFAULT_CONFIG_UUID);
expect(writeFile).not.toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Kibana instance UUID: CONFIG_UUID",
]
`);
});
});
});

describe('when file is not present and config property is set', () => {
it('writes the uuid to file and returns the config uuid', async () => {
mockReadFile({ error: fileNotFoundError });
const uuid = await resolveInstanceUuid(configService, logger);
expect(uuid).toEqual(DEFAULT_CONFIG_UUID);
expect(writeFile).toHaveBeenCalledWith(
join('data-folder', 'uuid'),
DEFAULT_CONFIG_UUID,
expect.any(Object)
);
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Setting new Kibana instance UUID: CONFIG_UUID",
]
`);
describe('when syncToFile is true', () => {
it('writes the uuid to file and returns the config uuid', async () => {
mockReadFile({ error: fileNotFoundError });
const uuid = await resolveInstanceUuid({ configService, logger, syncToFile: true });
expect(uuid).toEqual(DEFAULT_CONFIG_UUID);
expect(writeFile).toHaveBeenCalledWith(
join('data-folder', 'uuid'),
DEFAULT_CONFIG_UUID,
expect.any(Object)
);
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Setting new Kibana instance UUID: CONFIG_UUID",
]
`);
});
});

describe('when syncToFile is false', () => {
it('does not write the uuid to file and returns the config uuid', async () => {
mockReadFile({ error: fileNotFoundError });
const uuid = await resolveInstanceUuid({ configService, logger, syncToFile: false });
expect(uuid).toEqual(DEFAULT_CONFIG_UUID);
expect(writeFile).not.toHaveBeenCalledWith();
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Setting new Kibana instance UUID: CONFIG_UUID",
]
`);
});
});
});

describe('when file is present and config property is not set', () => {
it('does not write to file and returns the file uuid', async () => {
configService = getConfigService(undefined);
const uuid = await resolveInstanceUuid(configService, logger);
const uuid = await resolveInstanceUuid({ configService, logger, syncToFile: true });
expect(uuid).toEqual(DEFAULT_FILE_UUID);
expect(writeFile).not.toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalledTimes(1);
Expand All @@ -161,38 +199,56 @@ describe('resolveInstanceUuid', () => {
});

describe('when file is not present and config property is not set', () => {
it('generates a new uuid and write it to file', async () => {
configService = getConfigService(undefined);
mockReadFile({ error: fileNotFoundError });
const uuid = await resolveInstanceUuid(configService, logger);
expect(uuid).toEqual('NEW_UUID');
expect(writeFile).toHaveBeenCalledWith(
join('data-folder', 'uuid'),
'NEW_UUID',
expect.any(Object)
);
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Setting new Kibana instance UUID: NEW_UUID",
]
`);
describe('when syncToFile is true', () => {
it('generates a new uuid and write it to file', async () => {
configService = getConfigService(undefined);
mockReadFile({ error: fileNotFoundError });
const uuid = await resolveInstanceUuid({ configService, logger, syncToFile: true });
expect(uuid).toEqual('NEW_UUID');
expect(writeFile).toHaveBeenCalledWith(
join('data-folder', 'uuid'),
'NEW_UUID',
expect.any(Object)
);
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Setting new Kibana instance UUID: NEW_UUID",
]
`);
});
});

describe('when syncToFile is false', () => {
it('generates a new uuid and does not write it to file', async () => {
configService = getConfigService(undefined);
mockReadFile({ error: fileNotFoundError });
const uuid = await resolveInstanceUuid({ configService, logger, syncToFile: false });
expect(uuid).toEqual('NEW_UUID');
expect(writeFile).not.toHaveBeenCalledWith();
expect(logger.debug).toHaveBeenCalledTimes(1);
expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"Setting new Kibana instance UUID: NEW_UUID",
]
`);
});
});
});

describe('when file access error occurs', () => {
it('throws an explicit error for file read errors', async () => {
mockReadFile({ error: permissionError });
await expect(
resolveInstanceUuid(configService, logger)
resolveInstanceUuid({ configService, logger, syncToFile: true })
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to read Kibana UUID file, please check the uuid.server configuration value in kibana.yml and ensure Kibana has sufficient permissions to read / write to this file. Error was: EACCES"`
);
});
it('throws an explicit error for file write errors', async () => {
mockWriteFile(isDirectoryError);
await expect(
resolveInstanceUuid(configService, logger)
resolveInstanceUuid({ configService, logger, syncToFile: true })
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Unable to write Kibana UUID file, please check the uuid.server configuration value in kibana.yml and ensure Kibana has sufficient permissions to read / write to this file. Error was: EISDIR"`
);
Expand Down
23 changes: 16 additions & 7 deletions src/core/server/uuid/resolve_uuid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@ import { Logger } from '../logging';
const FILE_ENCODING = 'utf8';
const FILE_NAME = 'uuid';

export async function resolveInstanceUuid(
configService: IConfigService,
logger: Logger
): Promise<string> {
export async function resolveInstanceUuid({
configService,
syncToFile,
logger,
}: {
configService: IConfigService;
syncToFile: boolean;
logger: Logger;
}): Promise<string> {
const [pathConfig, serverConfig] = await Promise.all([
configService
.atPath<PathConfigType>(pathConfigDef.path)
Expand Down Expand Up @@ -61,15 +66,15 @@ export async function resolveInstanceUuid(
} else {
logger.debug(`Updating Kibana instance UUID to: ${uuidFromConfig} (was: ${uuidFromFile})`);
}
await writeUuidToFile(uuidFilePath, uuidFromConfig);
await writeUuidToFile(uuidFilePath, uuidFromConfig, syncToFile);
return uuidFromConfig;
}
}
if (uuidFromFile === undefined) {
const newUuid = uuid.v4();
// no uuid either in config or file, we need to generate and write it.
logger.debug(`Setting new Kibana instance UUID: ${newUuid}`);
await writeUuidToFile(uuidFilePath, newUuid);
await writeUuidToFile(uuidFilePath, newUuid, syncToFile);
return newUuid;
}

Expand All @@ -94,7 +99,11 @@ async function readUuidFromFile(filepath: string): Promise<string | undefined> {
}
}

async function writeUuidToFile(filepath: string, uuidValue: string) {
async function writeUuidToFile(filepath: string, uuidValue: string, syncToFile: boolean) {
if (!syncToFile) {
return;
}

try {
return await writeFile(filepath, uuidValue, { encoding: FILE_ENCODING });
} catch (e) {
Expand Down
36 changes: 28 additions & 8 deletions src/core/server/uuid/uuid_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { CoreContext } from '../core_context';

import { loggingServiceMock } from '../logging/logging_service.mock';
import { mockCoreContext } from '../core_context.mock';
import { Env } from '../config';
import { getEnvOptions } from '../config/__mocks__/env';

jest.mock('./resolve_uuid', () => ({
resolveInstanceUuid: jest.fn().mockResolvedValue('SOME_UUID'),
Expand All @@ -31,26 +33,44 @@ jest.mock('./resolve_uuid', () => ({
describe('UuidService', () => {
let logger: ReturnType<typeof loggingServiceMock.create>;
let coreContext: CoreContext;
let service: UuidService;

beforeEach(() => {
jest.clearAllMocks();
logger = loggingServiceMock.create();
coreContext = mockCoreContext.create({ logger });
service = new UuidService(coreContext);
});

describe('#setup()', () => {
it('calls manageInstanceUuid with core configuration service', async () => {
it('calls resolveInstanceUuid with core configuration service', async () => {
const service = new UuidService(coreContext);
await service.setup();
expect(resolveInstanceUuid).toHaveBeenCalledTimes(1);
expect(resolveInstanceUuid).toHaveBeenCalledWith(
coreContext.configService,
logger.get('uuid')
);
expect(resolveInstanceUuid).toHaveBeenCalledWith({
configService: coreContext.configService,
syncToFile: true,
logger: logger.get('uuid'),
});
});

it('returns the uuid resolved from manageInstanceUuid', async () => {
describe('when cliArgs.optimize is true', () => {
it('calls resolveInstanceUuid with syncToFile: false', async () => {
coreContext = mockCoreContext.create({
logger,
env: Env.createDefault(getEnvOptions({ cliArgs: { optimize: true } })),
});
const service = new UuidService(coreContext);
await service.setup();
expect(resolveInstanceUuid).toHaveBeenCalledTimes(1);
expect(resolveInstanceUuid).toHaveBeenCalledWith({
configService: coreContext.configService,
syncToFile: false,
logger: logger.get('uuid'),
});
});
});

it('returns the uuid resolved from resolveInstanceUuid', async () => {
const service = new UuidService(coreContext);
const setup = await service.setup();
expect(setup.getInstanceUuid()).toEqual('SOME_UUID');
});
Expand Down
10 changes: 8 additions & 2 deletions src/core/server/uuid/uuid_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { resolveInstanceUuid } from './resolve_uuid';
import { CoreContext } from '../core_context';
import { Logger } from '../logging';
import { IConfigService } from '../config';
import { IConfigService, CliArgs } from '../config';

/**
* APIs to access the application's instance uuid.
Expand All @@ -38,15 +38,21 @@ export interface UuidServiceSetup {
export class UuidService {
private readonly log: Logger;
private readonly configService: IConfigService;
private readonly cliArgs: CliArgs;
private uuid: string = '';

constructor(core: CoreContext) {
this.log = core.logger.get('uuid');
this.configService = core.configService;
this.cliArgs = core.env.cliArgs;
}

public async setup() {
this.uuid = await resolveInstanceUuid(this.configService, this.log);
this.uuid = await resolveInstanceUuid({
configService: this.configService,
syncToFile: !this.cliArgs.optimize,
logger: this.log,
});

return {
getInstanceUuid: () => this.uuid,
Expand Down

0 comments on commit 1054bd4

Please sign in to comment.