Skip to content

Commit a15d121

Browse files
committed
Merge branch 'master' into static-repository-factory
2 parents daeb57b + 86b3428 commit a15d121

File tree

66 files changed

+2661
-782
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+2661
-782
lines changed

src/cli/serve/integration_tests/invalid_config.test.js

Lines changed: 0 additions & 57 deletions
This file was deleted.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { spawnSync } from 'child_process';
21+
import { resolve } from 'path';
22+
23+
const ROOT_DIR = resolve(__dirname, '../../../../');
24+
const INVALID_CONFIG_PATH = resolve(__dirname, '__fixtures__/invalid_config.yml');
25+
26+
interface LogEntry {
27+
message: string;
28+
tags: string[];
29+
type: string;
30+
}
31+
32+
describe('cli invalid config support', function() {
33+
it(
34+
'exits with statusCode 64 and logs a single line when config is invalid',
35+
function() {
36+
// Unused keys only throw once LegacyService starts, so disable migrations so that Core
37+
// will finish the start lifecycle without a running Elasticsearch instance.
38+
const { error, status, stdout } = spawnSync(
39+
process.execPath,
40+
['src/cli', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'],
41+
{
42+
cwd: ROOT_DIR,
43+
}
44+
);
45+
46+
const [fatalLogLine] = stdout
47+
.toString('utf8')
48+
.split('\n')
49+
.filter(Boolean)
50+
.map(line => JSON.parse(line) as LogEntry)
51+
.filter(line => line.tags.includes('fatal'))
52+
.map(obj => ({
53+
...obj,
54+
pid: '## PID ##',
55+
'@timestamp': '## @timestamp ##',
56+
error: '## Error with stack trace ##',
57+
}));
58+
59+
expect(error).toBe(undefined);
60+
expect(status).toBe(64);
61+
expect(fatalLogLine.message).toContain(
62+
'Error: Unknown configuration key(s): "unknown.key", "other.unknown.key", "other.third", "some.flat.key", ' +
63+
'"some.array". Check for spelling errors and ensure that expected plugins are installed.'
64+
);
65+
expect(fatalLogLine.tags).toEqual(['fatal', 'root']);
66+
expect(fatalLogLine.type).toEqual('log');
67+
},
68+
20 * 1000
69+
);
70+
});

src/core/server/bootstrap.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { isMaster } from 'cluster';
2222
import { CliArgs, Env, RawConfigService } from './config';
2323
import { LegacyObjectToConfigAdapter } from './legacy';
2424
import { Root } from './root';
25+
import { CriticalError } from './errors';
2526

2627
interface KibanaFeatures {
2728
// Indicates whether we can run Kibana in a so called cluster mode in which
@@ -112,7 +113,9 @@ function onRootShutdown(reason?: any) {
112113
// mirror such fatal errors in standard output with `console.error`.
113114
// eslint-disable-next-line
114115
console.error(`\n${chalk.white.bgRed(' FATAL ')} ${reason}\n`);
116+
117+
process.exit(reason instanceof CriticalError ? reason.processExitCode : 1);
115118
}
116119

117-
process.exit(reason === undefined ? 0 : (reason as any).processExitCode || 1);
120+
process.exit(0);
118121
}

test/api_integration/apis/search/index.js renamed to src/core/server/errors.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@
1717
* under the License.
1818
*/
1919

20-
export default function ({ loadTestFile }) {
21-
describe('search', () => {
22-
loadTestFile(require.resolve('./count'));
23-
});
20+
export class CriticalError extends Error {
21+
constructor(
22+
message: string,
23+
public code: string,
24+
public processExitCode: number,
25+
public cause?: Error
26+
) {
27+
super(message);
28+
Object.setPrototypeOf(this, CriticalError.prototype);
29+
}
2430
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { ensureValidConfiguration } from './ensure_valid_configuration';
21+
import { getUnusedConfigKeys } from './get_unused_config_keys';
22+
import { configServiceMock } from '../../config/config_service.mock';
23+
24+
jest.mock('./get_unused_config_keys');
25+
26+
describe('ensureValidConfiguration', () => {
27+
let configService: ReturnType<typeof configServiceMock.create>;
28+
29+
beforeEach(() => {
30+
jest.clearAllMocks();
31+
configService = configServiceMock.create();
32+
configService.getUsedPaths.mockReturnValue(Promise.resolve(['core', 'elastic']));
33+
34+
(getUnusedConfigKeys as any).mockImplementation(() => []);
35+
});
36+
37+
it('calls getUnusedConfigKeys with correct parameters', async () => {
38+
await ensureValidConfiguration(
39+
configService as any,
40+
{
41+
settings: 'settings',
42+
pluginSpecs: 'pluginSpecs',
43+
disabledPluginSpecs: 'disabledPluginSpecs',
44+
pluginExtendedConfig: 'pluginExtendedConfig',
45+
uiExports: 'uiExports',
46+
} as any
47+
);
48+
expect(getUnusedConfigKeys).toHaveBeenCalledTimes(1);
49+
expect(getUnusedConfigKeys).toHaveBeenCalledWith({
50+
coreHandledConfigPaths: ['core', 'elastic'],
51+
pluginSpecs: 'pluginSpecs',
52+
disabledPluginSpecs: 'disabledPluginSpecs',
53+
inputSettings: 'settings',
54+
legacyConfig: 'pluginExtendedConfig',
55+
});
56+
});
57+
58+
it('returns normally when there is no unused keys', async () => {
59+
await expect(
60+
ensureValidConfiguration(configService as any, {} as any)
61+
).resolves.toBeUndefined();
62+
63+
expect(getUnusedConfigKeys).toHaveBeenCalledTimes(1);
64+
});
65+
66+
it('throws when there are some unused keys', async () => {
67+
(getUnusedConfigKeys as any).mockImplementation(() => ['some.key', 'some.other.key']);
68+
69+
await expect(
70+
ensureValidConfiguration(configService as any, {} as any)
71+
).rejects.toMatchInlineSnapshot(
72+
`[Error: Unknown configuration key(s): "some.key", "some.other.key". Check for spelling errors and ensure that expected plugins are installed.]`
73+
);
74+
});
75+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
import { getUnusedConfigKeys } from './get_unused_config_keys';
21+
import { ConfigService } from '../../config';
22+
import { LegacyServiceDiscoverPlugins } from '../legacy_service';
23+
import { CriticalError } from '../../errors';
24+
25+
export async function ensureValidConfiguration(
26+
configService: ConfigService,
27+
{ pluginSpecs, disabledPluginSpecs, pluginExtendedConfig, settings }: LegacyServiceDiscoverPlugins
28+
) {
29+
const unusedConfigKeys = await getUnusedConfigKeys({
30+
coreHandledConfigPaths: await configService.getUsedPaths(),
31+
pluginSpecs,
32+
disabledPluginSpecs,
33+
inputSettings: settings,
34+
legacyConfig: pluginExtendedConfig,
35+
});
36+
37+
if (unusedConfigKeys.length > 0) {
38+
const message = `Unknown configuration key(s): ${unusedConfigKeys
39+
.map(key => `"${key}"`)
40+
.join(', ')}. Check for spelling errors and ensure that expected plugins are installed.`;
41+
throw new InvalidConfigurationError(message);
42+
}
43+
}
44+
45+
class InvalidConfigurationError extends CriticalError {
46+
constructor(message: string) {
47+
super(message, 'InvalidConfig', 64);
48+
Object.setPrototypeOf(this, InvalidConfigurationError.prototype);
49+
}
50+
}

0 commit comments

Comments
 (0)