gce-elastic-docker
Version:
A package to help setup Elasticsearch / Kibana clusters on Google Compute Engine.
319 lines (282 loc) • 8.57 kB
text/typescript
import { m_types, registries, short_regions, zones } from '../gce';
import { kibana_users_env_var } from '../image';
import { Utils } from '../utils';
const ged_label = 'ged';
export { ged_label };
export interface IReservedEnv {
[kibana_users_env_var]: any;
[ged_label]: any;
'bootstrap.memory_lock': any;
'cluster.name': any;
'ES_JAVA_OPTS': any;
'network.host': any;
'node.data': any;
'node.ingest': any;
'node.master': any;
'node.name': any;
'NODE_OPTIONS': any;
}
export interface IReservedLabels {
[ged_label]: any;
}
export interface IBaseNode {
cluster_name: string;
data?: boolean;
dsize: number;
dtype: 'pd-standard' | 'pd-ssd';
env?: {};
hsize: number;
image: string;
ingest?: boolean;
khsize?: number;
kibana?: boolean;
labels?: {};
master?: boolean;
max_map_count?: number;
mtype: string;
name: string;
region?: string;
service_account: string;
short_region?: string;
zone: string;
}
export class BaseNode implements IBaseNode {
cluster_name: string;
data: boolean;
dsize: number;
dtype: 'pd-standard' | 'pd-ssd';
env: {};
hsize: number;
image: string;
ingest: boolean;
khsize: number;
kibana: boolean;
labels: {};
master: boolean;
max_map_count: number;
mtype: string;
name: string;
region: string;
service_account: string;
short_region: string;
zone: string;
constructor(v: IBaseNode) {
this._set_cluster_name(v);
this._set_data(v);
this._set_dsize(v);
this._set_dtype(v);
this.env = {};
this.set_env(v.env);
this.set_hsize(v.hsize);
this.set_khsize(v.khsize);
this._set_image(v);
this._set_ingest(v);
this._set_kibana(v);
this._set_labels(v.labels);
this._set_master(v);
this._set_max_map_count(v);
this._set_zone(v); // must set zone b4 mtype.
this._set_mtype(v);
this._set_name(v);
this._set_service_account(v);
this.region = this.zone.slice(0, -2);
this.short_region = short_regions[this.region];
}
// returns the keys of all env vars to remove aka that are null.
get_env_to_remove(): string[] {
return Object.keys(this.env).filter(e => this.env[e] === null);
}
// returns all creatable or updatable env keys/values
get_merged_env(): { [key: string]: any } {
const copy: BaseNode = JSON.parse(JSON.stringify(this));
// remove keys set to null from the copies env.
for (const k in copy.env) {
if (copy.env[k] === null) {
delete copy.env[k];
}
}
const base: IReservedEnv = {
ES_JAVA_OPTS: `-Xms${this.hsize}m -Xmx${this.hsize}m`,
NODE_OPTIONS: `--max-old-space-size=${this.khsize}`,
'bootstrap.memory_lock': true,
'cluster.name': this.cluster_name,
ged: Buffer.from(JSON.stringify(copy)).toString('base64'),
kibana_users: undefined, // set when creating a node.
'network.host': '0.0.0.0',
'node.data': this.data,
'node.ingest': this.ingest,
'node.master': this.master,
'node.name': this.name
};
delete base[kibana_users_env_var];
Object.keys(copy.env).forEach(k => base[k] = `${copy.env[k]}`);
return base;
}
get_merged_labels(): { [key: string]: any } {
const base: IReservedLabels = {
ged: 'true'
};
Object.keys(this.labels).forEach(k => base[k] = this.labels[k]);
return base;
}
set_env(v?: {}) {
const reserved: IReservedEnv = {
ES_JAVA_OPTS: true,
NODE_OPTIONS: true,
'bootstrap.memory_lock': true,
'cluster.name': true,
ged: true,
kibana_users: true,
'network.host': true,
'node.data': true,
'node.ingest': true,
'node.master': true,
'node.name': true
};
if (v) {
Object.keys(v).map(k => {
if (!Utils.is_string(v[k]) && !Utils.is_null(v[k]) &&
!Utils.is_number(v[k]) && !Utils.is_bool(v[k])) {
throw Error('an environment value must be a string|null|number|bool.');
} else if (reserved[k]) {
throw Error(`${k} is a reserved env variable this package sets.`);
} else {
this.env[k] = v[k];
}
});
}
}
set_hsize(v: number) {
if (!Utils.is_integer(v) || (v < 100) || (v > 31000)) {
throw Error(`es heap size of ${v} must be an integer from [100, 31000]`);
}
this.hsize = v;
}
set_khsize(v?: number) {
const val: any = v;
if (Utils.is_defined(val)) {
if (!Utils.is_integer(val)) {
throw Error('kibana heap size not an integer');
} else if (val < 100) {
throw Error('kibana heap size too small.');
}
}
this.khsize = val ? val : 512;
}
private _set_cluster_name(v: IBaseNode) {
if (!Utils.is_string(v.cluster_name) || !v.cluster_name || / /.test(v.cluster_name)) {
throw Error(`${v.cluster_name} is not a valid cluster name`);
}
this.cluster_name = v.cluster_name;
}
private _set_data(v: IBaseNode) {
if (Utils.is_bool(v.data)) {
this.data = <boolean> v.data;
} else if (Utils.is_defined(v.data)) {
throw Error('not a boolean');
} else {
this.data = true;
}
}
private _set_dsize(v: IBaseNode) {
if (!Utils.is_integer(v.dsize) || (v.dsize < 10) || (v.dsize > 6400)) {
throw Error(`disk size of ${v.dsize} must be an integer from [10, 6400]`);
}
this.dsize = v.dsize;
}
private _set_dtype(v: IBaseNode) {
if ((v.dtype !== 'pd-ssd') && v.dtype !== ('pd-standard')) {
throw Error('disk type must be pd-ssd|pd-standard');
}
this.dtype = v.dtype;
}
private _set_image(v: IBaseNode) {
if (!Utils.is_valid_image_name(v.image)) {
throw Error(`${v.image} is an invalid image name. Make sure it looks like: ` +
`{${registries.join(' | ')}}/{gcloud-project-id}/{image_name}`);
}
this.image = v.image;
}
private _set_ingest(v: IBaseNode) {
if (Utils.is_bool(v.ingest)) {
this.ingest = <boolean> v.ingest;
} else if (Utils.is_defined(v.ingest)) {
throw Error('not a boolean');
} else {
this.ingest = false;
}
}
private _set_kibana(v: IBaseNode) {
if (Utils.is_bool(v.kibana)) {
this.kibana = <boolean> v.kibana;
} else if (Utils.is_defined(v.kibana)) {
throw Error('not a boolean');
} else {
this.kibana = false;
}
}
private _set_labels(v?: {}) {
const reserved: IReservedLabels = {
ged: true
};
this.labels = {};
if (v) {
Object.keys(v).map(k => {
if (!Utils.is_string(v[k]) || !v[k]) {
throw Error('a labels value must be a nonempty string.');
} else if (!/^[a-z]/.test(k)) {
throw Error('a label must start with lowercase letter.');
} else if (!/^[a-z0-9_-]*$/.test(k)) {
throw Error('a label can contain only a-z0-9_-');
} else if (!/^[a-z0-9_-]*$/.test(v[k])) {
throw Error('a labels value can contain only a-z0-9_-');
} else if (reserved[k]) {
throw Error(`${k} is a reserved label this package sets.`);
} else {
this.labels[k] = v[k];
}
});
}
}
private _set_master(v: IBaseNode) {
if (Utils.is_bool(v.master)) {
this.master = <boolean> v.master;
} else if (Utils.is_defined(v.master)) {
throw Error('not a boolean');
} else {
this.master = true;
}
}
private _set_max_map_count(v: IBaseNode) {
if (Utils.is_defined(v.max_map_count) && (!Utils.is_integer(v.max_map_count) ||
(<number> v.max_map_count <= 0))) {
throw Error('max map count must be an integer > 0');
}
this.max_map_count = v.max_map_count ? v.max_map_count : 262144;
}
private _set_mtype(v: IBaseNode) {
if (m_types[this.zone].indexOf(v.mtype) === -1) {
throw Error(`mtype of ${v.mtype} is an invalid gce machine ` +
`type for zone ${this.zone}`);
}
this.mtype = v.mtype;
}
private _set_name(v: IBaseNode) {
if (!Utils.is_string(v.name) || !v.name || / /.test(v.name)) {
throw Error(`${v.name} is not a valid name.`);
}
this.name = v.name;
}
private _set_service_account(v: IBaseNode) {
if (!Utils.is_string(v.service_account) || !/compute@developer/.test(v.service_account)) {
throw Error('must provide a valid default gce service account.');
}
this.service_account = v.service_account;
}
private _set_zone(v: IBaseNode) {
if (zones.indexOf(v.zone) === -1) {
throw Error(`${v} is an invalid gce zone.`);
}
this.zone = v.zone;
}
}