local-elastic-docker
Version:
A package for creating / tearing down local Elasticsearch / Kibana single node clusters via Docker.
208 lines (167 loc) • 5.77 kB
text/typescript
import { BaseContainer, IBaseContainer } from '../base-container';
import { elastic_image_label } from '../image';
import { Utils } from '../utils';
export interface IContainer extends IBaseContainer { }
export class Container extends BaseContainer implements IContainer {
static async fetch_all(verbose?: boolean) {
const names_cmd = `docker ps -a --filter "label=${elastic_image_label}" ` +
'--format " {{ .Names }}"';
const containers: Container[] = [];
if (verbose) {
console.log(`fetching all containers this package has created via\n${names_cmd}`);
}
let names = <string> await Utils.exec(names_cmd);
names = names.split('\n').join(' ');
if (names.length) {
const inspect_cmd = `docker inspect ${names}`;
const configs = JSON.parse(<string> await Utils.exec(inspect_cmd));
configs.forEach(config => {
const labels = config.Config.Labels;
const b64 = labels[elastic_image_label];
const decoded: IContainer = JSON.parse(Buffer.from(b64, 'base64').toString());
containers.push(new Container(decoded));
});
}
return containers;
}
constructor(v: IContainer) {
super(v);
}
// resolves w/ the standard elasticsearch json response for GET _cluster/health | undefined
async cluster_health(verbose?: boolean) {
if (verbose) {
console.log(`fetching cluster health for ${this.name}`);
}
try {
const cmd = 'curl -s localhost:9200/_cluster/health';
const res = await this.exec(cmd);
return JSON.parse(<string> res);
} catch (e) { }
}
// resolves w/ green | yellow | red | undefined
async cluster_state(verbose?: boolean) {
if (verbose) {
console.log(`fetching cluster state for ${this.name}`);
}
try {
return (await this.cluster_health()).status;
} catch (e) { }
}
async delete(verbose?: boolean) {
const cmd = `docker rm -f ${this.name}`;
if (verbose) {
console.log(`deleting container ${this.name} via ${cmd}`);
}
await Utils.exec(cmd, verbose);
}
// executes the given command in the container and returns its stdout.
// note that the cmd is base64 encoded so we dont have to worry about special
// characters like $ or quotes.
async exec(cmd: string, verbose?: boolean) {
if (!Utils.is_string(cmd) || !cmd) {
throw Error('command missing!');
}
const b64 = Buffer.from(cmd).toString('base64');
const wrapped_cmd = `docker exec -i ${this.name}` +
` bash -c 'eval $(echo ${b64} | base64 --decode)'`;
if (verbose) {
console.log(`executing the following command\n${wrapped_cmd}`);
}
return await Utils.exec(wrapped_cmd, verbose);
}
async kibana_saved_objects(verbose?: boolean): Promise<object[]> {
if (!this.kibana) {
throw Error(`${this.name} isnt a kibana node!`);
}
const major = this._get_major_version();
if (verbose) {
console.log(`fetching kibana saved objects for ${this.name}`);
}
if (major === 6) {
const cmd = 'curl -X GET "localhost:5601/api/saved_objects/_find?per_page=10000"';
const resp = <string> await this.exec(cmd);
return JSON.parse(resp).saved_objects;
} else if (major === 7) {
const types = [
'config',
'map',
'canvas-workpad',
'canvas-element',
'index-pattern',
'visualization',
'search',
'dashboard',
'url'
];
const cmd = 'curl -X GET "localhost:5601/api/saved_objects/_find?per_page=10000&' +
types.map(t => `type=${t}`).join('&') + '"';
const resp = <string> await this.exec(cmd);
return JSON.parse(resp).saved_objects;
} else {
throw Error('havent added support for es major version ' + major);
}
}
// resolves w/ number | undefined
async kibana_status(verbose?: boolean) {
if (!this.kibana) {
return;
} else if (verbose) {
console.log(`fetching kibana status for ${this.name}`);
}
try {
const cmd = 'curl -s -o /dev/null -w "%{http_code}" localhost:5601/status';
const res = await this.exec(cmd);
return res ? Number(res) : undefined;
} catch (e) { }
}
async restart(verbose?: boolean) {
await this.stop(verbose);
await this.start(verbose);
}
async start(verbose?: boolean) {
const cmd = `docker start ${this.name}`;
if (verbose) {
console.log(`starting container ${this.name} via ${cmd}`);
}
await Utils.exec(cmd, verbose);
}
async stop(verbose?: boolean) {
const cmd = `docker stop ${this.name}`;
if (verbose) {
console.log(`stopping container ${this.name} via ${cmd}`);
}
await Utils.exec(cmd, verbose);
}
async wait_for_elastic(verbose?: boolean) {
if (verbose) {
console.log(`waiting for state >= yellow from elastic for ${this.name}`);
}
await new Promise(resolve => {
const interval = 2000;
const again = () => {
setTimeout(async() => {
const state = await this.cluster_state(verbose);
/yellow|green/.test(state) ? resolve() : again();
}, interval);
};
again();
});
}
async wait_for_kibana(verbose?: boolean) {
if (!this.kibana) {
throw Error(`${this.name} isnt a kibana container! You\'ll never get a 200 response.`);
} else if (verbose) {
console.log(`waiting for status 200 from kibana for ${this.name}`);
}
await new Promise(resolve => {
const interval = 2000;
const again = () => {
setTimeout(async() => {
const status = await this.kibana_status(verbose);
status === 200 ? resolve() : again();
}, interval);
};
again();
});
}
}