mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Improve keystore CLI (#157359)
## Summary Relates to: #113217 - Add extra documentation to highlight behaviour of the kibana keystore (for #113217) - Fix/Tidy-up commands (`create`, `list`) where the extra unused arguments were preventing the `options` from being passed to the functions. Also remove unnecessary `async` keyword from the `remove` command. - Added new `show` command ``` Usage: bin/kibana-keystore show [options] <key> Displays the value of a single setting in the keystore. Pass the -o (or --output) parameter to write the setting to a file. Options: -s, --silent prevent all logging -o, --output <file> output value to a file -h, --help output usage information ``` ### Checklist Delete any items that are not applicable to this PR. - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Kaarina Tungseth <kaarina.tungseth@elastic.co>
This commit is contained in:
parent
8181597cc9
commit
6ebfb8aa3e
8 changed files with 188 additions and 5 deletions
|
@ -5,7 +5,12 @@ Some settings are sensitive, and relying on filesystem permissions to protect
|
|||
their values is not sufficient. For this use case, Kibana provides a
|
||||
keystore, and the `kibana-keystore` tool to manage the settings in the keystore.
|
||||
|
||||
NOTE: All commands here should be run as the user which will run Kibana.
|
||||
[NOTE]
|
||||
====
|
||||
* Run all commands as the user who runs {kib}.
|
||||
* Only the settings with the `(Secure)` qualifier should be stored in the keystore.
|
||||
Unsupported, extraneous or invalid JSON-string settings cause {kib} to fail to start up.
|
||||
====
|
||||
|
||||
[float]
|
||||
[[creating-keystore]]
|
||||
|
@ -36,7 +41,8 @@ bin/kibana-keystore list
|
|||
[[add-string-to-keystore]]
|
||||
=== Add string settings
|
||||
|
||||
NOTE: Your input will be JSON-parsed to allow for object/array input configurations. To enforce string values, use "double quotes" around your input.
|
||||
NOTE: Your input will be JSON-parsed to allow for object/array input configurations.
|
||||
To enforce string values, use "double quotes" around your input.
|
||||
|
||||
Sensitive string settings, like authentication credentials for Elasticsearch
|
||||
can be added using the `add` command:
|
||||
|
@ -75,3 +81,14 @@ To remove a setting from the keystore, use the `remove` command:
|
|||
----------------------------------------------------------------
|
||||
bin/kibana-keystore remove the.setting.name.to.remove
|
||||
----------------------------------------------------------------
|
||||
|
||||
[float]
|
||||
[[read-settings]]
|
||||
=== Read settings
|
||||
|
||||
To display the configured setting values, use the `show` command:
|
||||
|
||||
[source, sh]
|
||||
----------------------------------------------------------------
|
||||
bin/kibana-keystore show setting.key
|
||||
----------------------------------------------------------------
|
||||
|
|
|
@ -17,6 +17,7 @@ import { createCli } from './create';
|
|||
import { listCli } from './list';
|
||||
import { addCli } from './add';
|
||||
import { removeCli } from './remove';
|
||||
import { showCli } from './show';
|
||||
|
||||
const argv = process.argv.slice();
|
||||
const program = new Command('bin/kibana-keystore');
|
||||
|
@ -31,6 +32,7 @@ createCli(program, keystore);
|
|||
listCli(program, keystore);
|
||||
addCli(program, keystore);
|
||||
removeCli(program, keystore);
|
||||
showCli(program, keystore);
|
||||
|
||||
program
|
||||
.command('help <command>')
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import { Logger } from '../cli/logger';
|
||||
import { confirm } from './utils';
|
||||
|
||||
export async function create(keystore, command, options) {
|
||||
export async function create(keystore, options) {
|
||||
const logger = new Logger(options);
|
||||
|
||||
if (keystore.exists()) {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import { Logger } from '../cli/logger';
|
||||
|
||||
export function list(keystore, command, options = {}) {
|
||||
export function list(keystore, options = {}) {
|
||||
const logger = new Logger(options);
|
||||
|
||||
if (!keystore.exists()) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export async function remove(keystore, key) {
|
||||
export function remove(keystore, key) {
|
||||
keystore.remove(key);
|
||||
keystore.save();
|
||||
}
|
||||
|
|
101
src/cli_keystore/show.test.ts
Normal file
101
src/cli_keystore/show.test.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This data blob has 3 key/values set:
|
||||
* - foo: "turbo2000"
|
||||
* - bar: {"sub": 0}
|
||||
* - num: 12345
|
||||
*/
|
||||
const mockKeystoreData =
|
||||
'1:ae/OomiywlzhXnR8DnGLHheyAklj4WcvDUOzeIyeQIHEmrY' +
|
||||
'MIYOYHvduos7NDOgw3TFAuh7xs6z9i0juEo1zFeJeIr8yoyIxdGi1J8GUCO0/' +
|
||||
'OeaKxvLjTjczwoxiy34kM6CzlnJhjwnALAMiBvbehMUaCVzxf3Fu/3Gk2qeux0OPhidJ4Pn/RPjdMA==';
|
||||
|
||||
jest.mock('fs', () => ({
|
||||
readFileSync: jest.fn().mockImplementation(() => JSON.stringify(mockKeystoreData)),
|
||||
existsSync: jest.fn().mockImplementation((fileName) => {
|
||||
if (fileName === 'non-existent-file.txt') {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
writeFileSync: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../cli/logger');
|
||||
|
||||
import { Logger } from '../cli/logger';
|
||||
const mockLogFn = jest.fn();
|
||||
Logger.prototype.log = mockLogFn;
|
||||
const mockErrFn = jest.fn();
|
||||
Logger.prototype.error = mockErrFn;
|
||||
|
||||
import { Keystore } from '../cli/keystore';
|
||||
import { show } from './show';
|
||||
|
||||
describe('Kibana keystore: show', () => {
|
||||
const keystore = new Keystore('mock-path', '');
|
||||
|
||||
it('reads stored strings', () => {
|
||||
const exitCode = show(keystore, 'foo', {});
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(mockLogFn).toHaveBeenCalledWith('turbo2000');
|
||||
expect(mockErrFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('reads stored numbers', () => {
|
||||
const exitCode = show(keystore, 'num', {});
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(mockLogFn).toHaveBeenCalledWith('12345');
|
||||
expect(mockErrFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('reads stored objecs', () => {
|
||||
const exitCode = show(keystore, 'bar', {});
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(mockLogFn).toHaveBeenCalledWith(JSON.stringify({ sub: 0 }));
|
||||
expect(mockErrFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('outputs to a file when the arg is passed', () => {
|
||||
const exitCode = show(keystore, 'foo', { output: 'non-existent-file.txt' });
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(mockLogFn).toHaveBeenCalledWith('Writing output to file: non-existent-file.txt');
|
||||
expect(mockErrFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('logs and terminates with an error when output file exists', () => {
|
||||
const exitCode = show(keystore, 'foo', { output: 'existing-file.txt' });
|
||||
|
||||
expect(exitCode).toBe(-1);
|
||||
expect(mockErrFn).toHaveBeenCalledWith(
|
||||
'ERROR: Output file already exists. Remove it before retrying.'
|
||||
);
|
||||
expect(mockLogFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("logs and terminates with an error when the store doesn't have the key", () => {
|
||||
const exitCode = show(keystore, 'no-key');
|
||||
|
||||
expect(exitCode).toBe(-1);
|
||||
expect(mockErrFn).toHaveBeenCalledWith("ERROR: Kibana keystore doesn't have requested key.");
|
||||
expect(mockLogFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockLogFn.mockReset();
|
||||
mockErrFn.mockReset();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
});
|
62
src/cli_keystore/show.ts
Normal file
62
src/cli_keystore/show.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { writeFileSync, existsSync } from 'fs';
|
||||
|
||||
import { Keystore } from '../cli/keystore';
|
||||
import { Logger } from '../cli/logger';
|
||||
|
||||
interface ShowOptions {
|
||||
silent?: boolean;
|
||||
output?: string;
|
||||
}
|
||||
|
||||
export function show(keystore: Keystore, key: string, options: ShowOptions = {}): number | void {
|
||||
const { silent, output } = options;
|
||||
const logger = new Logger({ silent });
|
||||
|
||||
if (!keystore.exists()) {
|
||||
logger.error("ERROR: Kibana keystore not found. Use 'create' command to create one.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!keystore.has(key)) {
|
||||
logger.error("ERROR: Kibana keystore doesn't have requested key.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const value = keystore.data[key];
|
||||
const valueAsString = typeof value === 'string' ? value : JSON.stringify(value);
|
||||
|
||||
if (output) {
|
||||
if (existsSync(output)) {
|
||||
logger.error('ERROR: Output file already exists. Remove it before retrying.');
|
||||
return -1;
|
||||
} else {
|
||||
writeFileSync(output, valueAsString);
|
||||
logger.log('Writing output to file: ' + output);
|
||||
}
|
||||
} else {
|
||||
logger.log(valueAsString);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function showCli(program: any, keystore: Keystore) {
|
||||
program
|
||||
.command('show <key>')
|
||||
.description(
|
||||
'Displays the value of a single setting in the keystore. Pass the -o (or --output) parameter to write the setting to a file.'
|
||||
)
|
||||
.option('-s, --silent', 'prevent all logging')
|
||||
.option('-o, --output <file>', 'output value to a file')
|
||||
.action((key: string, options: ShowOptions) => {
|
||||
process.exitCode = show(keystore, key, options) || 0;
|
||||
});
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
"keystore/**/*",
|
||||
"utils/**/*",
|
||||
"*.js",
|
||||
"*.ts",
|
||||
],
|
||||
"kbn_references": [
|
||||
{ "path": "../setup_node_env/tsconfig.json" },
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue