generator-living-atlas
Version:
A Yeoman Generator for Living Atlas Ansible Inventories
1,394 lines (1,281 loc) • 65.2 kB
JavaScript
/* eslint camelcase: ["error", {properties: "never"}], no-warning-comments: 0 */
// For Prompt options:
// https://github.com/SBoudrias/Inquirer.js
'use strict';
const Generator = require('yeoman-generator');
const yosay = require('onionsay');
const niceware = require('niceware');
const fsN = require('fs');
const {v4: uuidv4} = require('uuid');
const bcrypt = require('bcrypt');
const Base64 = require('js-base64');
const crypto = require('crypto');
const {
defUseSubdomain,
defUseSubdomainPrompt,
isCorrectHostname,
isDefined,
em,
validateDomain,
addOnce,
removeOnce, additionalToolkitPrompts
} = require('./libs.js');
const {services, servicesDesc, serviceUseVar} = require('./services.js');
let defaultStore = false;
let logger;
let debug = false;
let replay = false;
let dontAsk = false;
let previousConfig;
let firstRun;
let useJenkins = false;
/*
Set of used servers
*/
const servers = new Set();
let serversSorted = [];
/*
We store used `context` like this:
serversAndPaths["server"]["url"]["path"] = true;
so we can check that in a same server we don't configure two servers
with the same domain and the same path
*/
const serversAndPaths = {};
let servicesInUse = [];
let groupsAndServers = {};
let groupsChildren = {};
/* To create this hierarchy:
[childgroup2]
host1
host2
[childgroup1]
host2
host3
[parent1:children]
childgroup1
childgroup2
*/
const vhostsSet = new Set(); // use for nginx_vhost_fragments_to_clear
function storeGroupServer(name, server) {
let desc = servicesDesc[name];
let group = desc.group;
if (server != null) {
if (groupsAndServers[group] == null) {
groupsAndServers[group] = [server];
} else {
if (!groupsAndServers[group].includes(server)) {
groupsAndServers[group] = [...groupsAndServers[group], server];
}
}
let serviceInUse = {
service: name,
map: desc,
};
// Don't add twice
if (servicesInUse.filter(e => e.service === name).length === 0) {
servicesInUse.push(serviceInUse);
}
}
if (name === 'biocache_backend') {
storeGroupServer('biocache_cli', server);
storeGroupServer('nameindexer', server);
}
if (name === 'pipelines') {
// Add child services
storeGroupServer('spark', server);
storeGroupServer('hadoop', server);
if (useJenkins) storeGroupServer('pipelines_jenkins', server);
}
if (name === 'docker_swarm') {
// Add child services
storeGroupServer('docker_common', server);
}
}
const hostSepRegexp = /[,\s]+/;
const validateAndStoreServer = (name, serversOrServersList) =>
new Promise((resolve, reject) => {
if (serversOrServersList == null || serversOrServersList === '') {
reject(new Error(`Please select some server for deploy '${name}'`));
}
// if (debug) logger(`store: ${serversOrServersList} typeof ${typeof serversOrServersList}`);
let groupServers = typeof serversOrServersList === 'string' ? serversOrServersList.split(hostSepRegexp) : serversOrServersList;
// if (debug) logger(`store: ${groupServers} (${groupServers.length}) typeof ${typeof groupServers}`);
if (name === 'pipelines' && groupServers.length < 3) {
logger.error(`You'll need at least 3 servers to use pipelines with a spark cluster`);
}
if (name === 'pipelines' && groupServers.length < 3) {
logger.error(`You'll need at least 3 servers to use pipelines with a spark cluster`);
}
// solrcloud hosts > 1 and odd, so >= 3
if (name === 'solrcloud' && groupServers.length < 3) {
logger.error(`You'll need at least 3 servers to use a redundant solr cluster`);
}
if (name === 'zookeeper' && groupServers.length < 3) {
logger.error(`You'll need at least 3 servers to use a redundant zookeeper cluster`);
}
for (let server of groupServers) {
if (debug) logger(`Store: ${name} -> ${server}`);
if (isCorrectHostname(server) === true) {
if (debug) logger('We add to the first position');
servers.add(server);
if (!serversSorted.includes(server)) serversSorted.unshift(server);
// add this server for its groups (we can add several to allow redundancy of services)
storeGroupServer(name, server);
} else {
reject(new Error(`Invalid hostname '${server}' for '${name}'`));
}
}
if (debug) logger(servers);
if (debug) logger(serversSorted)
resolve(true);
});
function PromptSubdomainFor(name, subdomain, when, force) {
this.store = defaultStore;
this.type = 'confirm';
const varName = `LA_${name}_uses_subdomain`;
this.name = varName;
this.message = (a) => defUseSubdomainPrompt(a, name);
this.default = (a) => defUseSubdomain(a);
if (force) {
this.when = (a) =>
new Promise((resolve) => {
a[varName] = true;
resolve(false);
});
} else {
if (when) {
this.when = when;
}
}
}
function urlDefValue(a, varUsesSubdomain, subdomain, varName) {
const hasPrevious =
typeof previousConfig !== 'undefined' &&
typeof previousConfig[varName] !== 'undefined';
if (replay && hasPrevious) {
return previousConfig[varName];
}
if (typeof varUsesSubdomain !== 'undefined' && a[varUsesSubdomain]) {
return `${subdomain}.${a['LA_domain']}`;
}
return `${a['LA_domain']}`;
}
function hostChoices(a, varUsesSubdomain, subdomain, multi, name) {
let serverList = a['LA_hostnames'].split(hostSepRegexp)
let group = servicesDesc[name].group;
let choices = [];
if (!multi) {
choices = serverList;
// Add previous selected hostname at the start of the array
const hasPrevious = groupsAndServers[group] != null && groupsAndServers[group].length > 0;
const previousHostname = hasPrevious ? groupsAndServers[group][0] : null;
if (debug) logger(`hasPrevious: ${hasPrevious} ${previousHostname}`);
if (replay && hasPrevious && choices.includes(previousHostname)) {
if (choices.includes(previousHostname)) {
// Remove and later put in the first position
for (let i = 0; i < choices.length; i++) {
if (choices[i] === previousHostname) {
choices.splice(i, 1);
}
}
}
choices.unshift(previousHostname);
}
} else {
// multi select
for (let host of serverList) {
let checked = groupsAndServers[group] != null && groupsAndServers[group].length > 0 && groupsAndServers[group].includes(host);
choices.push({name: host, checked: checked});
}
}
if (debug) logger(choices);
return choices;
}
function PromptHostnameFor(name, subdomain, when) {
this.store = defaultStore;
// if (debug) logger(`prompt hostname for ${name}: ${subdomain}`);
const multi = servicesDesc[name].allowMultipleDeploys;
this.type = multi ? 'checkbox' : 'list'; // checkbox
const varName = `LA_${name}_hostname`;
const varUsesSubdomain = `LA_${name}_uses_subdomain`;
this.name = varName;
this.message = `LA ${em(
name
)} hostname (like myvm1, somehost.amazonaws.com, ...) ?`;
this.choices = (a) => hostChoices(a, varUsesSubdomain, subdomain, multi, name);
if (when) {
this.when = when;
}
this.filter = async (input, a) => {
if (this.type == 'list') {
await validateAndStoreServer(name, input);
}
return typeof input === 'string' ? input : input.join(', ');
}
if (this.type == 'checkbox') {
this.validate = (input, a) =>
validateAndStoreServer(name, input);
}
// Validation does not work with list, so we add to the filter
// this.validate = input => validateAndStoreServer(name, input);
// https://github.com/SBoudrias/Inquirer.js/issues/383
}
function PromptUrlFor(name, subdomain, when) {
this.store = defaultStore;
this.type = 'input';
const varName = `LA_${name}_url`;
const varUsesSubdomain = `LA_${name}_uses_subdomain`;
this.name = varName;
this.message = `LA ${em(name)} base url ?`;
if (when) {
this.when = when;
}
this.default = (a) => urlDefValue(a, varUsesSubdomain, subdomain, varName);
this.validate = async (input, a) => {
// Store previous hostname from choices or input request for later use
const varHostName = `LA_${name}_hostname`;
const varUrl = `LA_${name}_url`;
vhostsSet.add(input);
// Now we verify the url
return validateDomain(input, name, logger);
};
}
function PromptPathFor(name, path, when) {
this.store = defaultStore;
const varName = `LA_${name}_path`;
const varUsesSubdomain = `LA_${name}_uses_subdomain`;
const varHostname = `LA_${name}_hostname`;
const varUrl = `LA_${name}_url`;
this.type = 'input';
this.name = varName;
this.message = (a) => {
// Warn: dup code below
const samplePath = a[varUsesSubdomain]
? '/'
: `/${typeof path === 'undefined' ? '' : path}`;
return `Which context ${em('/path')} you wanna use for this service (like ${
a[varUrl]
}${em(samplePath)}) ?`;
};
this.default = (a) => {
if (a[varUsesSubdomain]) {
const hostname = a[varHostname];
const url = a[varUrl];
const rootUsed =
typeof serversAndPaths[hostname] !== 'undefined' &&
typeof serversAndPaths[hostname][url] !== 'undefined' &&
serversAndPaths[hostname][url]['/'];
return rootUsed ? `/${typeof path === 'undefined' ? '' : path}` : '/';
}
return typeof path === 'undefined' ? '' : `/${path}`;
};
if (when) {
this.when = when;
}
this.validate = (path, answers) =>
new Promise((resolve) => {
// Check if in this server this path is already used
// logger(`path '${path}'`);
if (!path.startsWith('/')) {
resolve("Please enter something like '/path'");
}
// FIXME check better url here
const hostname = answers[`LA_${name}_hostname`];
const url = answers[`LA_${name}_url`];
const used =
serversAndPaths[hostname] &&
serversAndPaths[hostname][url] &&
serversAndPaths[hostname][url][path];
if (used) {
resolve(`This context /path is already in use in this server`);
}
if (!serversAndPaths[hostname]) {
serversAndPaths[hostname] = {};
}
if (!serversAndPaths[hostname][url]) {
serversAndPaths[hostname][url] = {};
}
serversAndPaths[hostname][url][path] = true;
resolve(true);
});
}
function replaceLine(filePath, oldContent, newContent) {
let content = this.fs.read(this.destinationPath(filePath));
const regexp = new RegExp(`^[ \\t]*${oldContent}.+$`, 'm');
content = content.replace(regexp, newContent);
this.fs.write(this.destinationPath(filePath), content);
}
function commentLine(filePath, line) {
let content = this.fs.read(this.destinationPath(filePath));
const regexp = new RegExp(`^${line}`, 'm');
content = content.replace(regexp, `# ${line}`);
this.fs.write(this.destinationPath(filePath), content);
}
function isPasswordNotDefined(filePath, pass_variable) {
let content = this.fs.read(this.destinationPath(filePath));
const regexp = new RegExp(`^[ \\t]*${pass_variable}.+$`, 'm');
return !content.match(regexp);
}
function generateBranding(conf, brandDest) {
const brandSettings = `${brandDest}/app/js/settings.js`;
const brandDeployDest = `${brandDest}/deploy.sh`;
// noinspection JSUnresolvedFunction
this.fs.copyTpl(
this.templatePath('deploy-branding.sh'),
this.destinationPath(brandDeployDest),
conf
);
if (this.fs.exists(brandSettings)) {
// noinspection JSUnresolvedFunction
this.fs.copyTpl(
this.templatePath('base-branding-settings.ejs'),
this.destinationPath(`${brandSettings}`),
conf
);
if (conf['LA_theme'] != null) {
// try to change the theme in the settings
logger('INFO: trying to setup the theme of current branding settings');
replaceLine.call(
this,
brandSettings,
'theme:',
` theme: '${conf['LA_theme']}',`
);
}
} else {
logger(
`INFO: Generating a sample branding in '${brandDest}' directory. Please wait`
);
// noinspection JSCheckFunctionSignatures
let cmdResult = this.spawnCommandSync(
'git',
[
'clone',
'--depth=1',
'https://github.com/living-atlases/base-branding.git',
brandDest,
],
{
// accessing this._destinationRoot
cwd: this.destinationRoot(),
shell: true,
stdio: 'ignore',
// stdio: "inherit",
}
);
if (cmdResult.exitCode === 0) {
// Async update submodules
logger(
`INFO: Do a "git submodule update --init --recursive --depth=1" in '${brandDest}' directory later`
);
// we remove the default branding settings
this.spawnCommandSync('rm', ['-f', brandSettings], {
cwd: this.destinationPath(),
shell: true,
stdio: 'ignore',
});
// noinspection JSUnresolvedFunction
this.fs.copyTpl(
this.templatePath('base-branding-settings.ejs'),
this.destinationPath(brandSettings),
conf
);
} else {
logger(
`Error: Failed to clone base-branding, error: ${JSON.stringify(
cmdResult
)}`
);
}
}
}
function handleClientKeysGeneration(localPassDest, clientType, servicesDesc) {
if (isPasswordNotDefined.call(this, localPassDest, clientType)) {
const skipOidcServices = new Set([
'biocache_backend', 'biocache_cli', 'nameindexer', 'webapi', 'branding', 'pipelines',
'solr', 'solrcloud', 'zookeeper', 'cas', 'spark', 'hadoop', 'jenkins', 'pipelines_jenkins',
'namematching_service', 'sensitive_data_service', 'pdfgen', 'ecodata_reporting', 'events',
'events_elasticsearch', 'docker_swarm', 'docker_common', 'gatus', 'portainer'
]);
Object.keys(servicesDesc).forEach(service => {
if (skipOidcServices.has(service)) {
return;
}
const clientIdName = `${service}_client_id`;
const clientSecretName = `${service}_client_secret`;
const clientId = crypto.randomBytes(18).toString('hex');
const clientSecret = crypto.randomBytes(18).toString('hex');
replaceLine.call(
this,
localPassDest,
'# External old',
`${clientIdName} = ${clientId}\n# External old API access to collectory to lookup collections/institutions, etc`
);
replaceLine.call(
this,
localPassDest,
'# External old',
`${clientSecretName} = ${clientSecret}\n# External old API access to collectory to lookup collections/institutions, etc`
);
});
}
}
function generateAnsiblew(conf, dest) {
// noinspection JSUnresolvedFunction
this.fs.copyTpl(
this.templatePath('ansiblew'),
this.destinationPath(`${dest}/ansiblew`),
conf
);
}
function generate(conf, dest, filePrefix) {
const useBranding = conf['LA_generate_branding'];
if (useBranding) {
const brandDest = `${conf['LA_pkg_name']}-branding`;
generateBranding.call(this, conf, brandDest);
}
if (conf['LA_additionalVariables'] != null) {
let additionalVariables = Base64.decode(conf['LA_additionalVariables']);
additionalVariables =
'\n' +
'#'.repeat(80) +
'\n' +
'# #\n' +
'# GENERATED BY THE la-toolkit #\n' +
"# Please don't edit this file #\n" +
'# use the la-toolkit tune tool #\n' +
'# #\n' +
'#'.repeat(80) +
'\n\n\n\n' +
additionalVariables;
this.fs.write(
this.destinationPath(`${dest}/${filePrefix}-toolkit.ini`),
additionalVariables
);
}
const preDeployDest = `${conf['LA_pkg_name']}-pre-deploy`;
const postDeployDest = `${conf['LA_pkg_name']}-post-deploy`;
// noinspection JSUnresolvedFunction
this.fs.copyTpl(
this.templatePath('pre-deploy'),
this.destinationPath(preDeployDest),
conf
);
// noinspection JSUnresolvedFunction
this.fs.copyTpl(
this.templatePath('post-deploy'),
this.destinationPath(postDeployDest),
conf
);
}
// noinspection JSUnusedGlobalSymbols
module.exports = class extends Generator {
// noinspection JSUnresolvedVariable
constructor(args, opts) {
super(args, opts);
this.argument('debug', {type: Boolean, required: false});
this.argument('replay', {type: Boolean, required: false});
this.argument('replay-dont-ask', {type: Boolean, required: false});
debug = this.options.debug;
replay = this.options['replay'] || this.options['replay-dont-ask'];
dontAsk = this.options['replay-dont-ask'];
// https://github.com/yeoman/environment/issues/298
this.options.skipLocalCache = false;
const previousConfigAll = this.config.getAll();
previousConfig =
typeof previousConfigAll === 'undefined'
? []
: previousConfigAll['promptValues'];
firstRun = previousConfigAll.firstRun !== false;
logger = this.log;
if (firstRun) {
// Set firstRun so in the future we can check it
this.config.set('firstRun', false);
}
let cmdResult = this.spawnCommandSync('which', ['git'], {
shell: true,
stdio: 'ignore',
});
if (cmdResult.exitCode !== 0) {
logger(
`${em('Error')}: Please install git before running this generator`
);
process.exit(1);
}
// We always store in the first run
if (debug && firstRun) {
logger('Running generator for the first time in this location');
}
defaultStore = firstRun || this.options['replay'];
if (debug) logger('Current config:');
if (debug) logger(previousConfig);
if (typeof previousConfig === 'undefined' && replay) {
logger(
`${em('Error')}: We cannot replay without a previous configuration`
);
process.exit(1);
}
if (replay) {
useJenkins = serviceUseVar('pipelines_jenkins', previousConfig);
Object.keys(servicesDesc).forEach((service) => {
const hostVar = `LA_${service}_hostname`;
const groupServers = previousConfig[hostVar] == null ? null : previousConfig[hostVar].split(hostSepRegexp);
if (serviceUseVar(service, previousConfig)) {
if (groupServers != null) {
groupServers.forEach(servers.add, servers);
for (let server of groupServers) {
storeGroupServer(service, server);
}
} else if (service === 'branding') {
// We fallback to the domain host if replay without defining correctly the branding hostname
if (previousConfig['LA_main_hostname'] != null)
storeGroupServer(service, previousConfig['LA_main_hostname']);
}
}
});
serversSorted = [...servers];
if (debug) logger(groupsAndServers);
if (debug) logger(servers);
if (debug) logger(serversSorted)
}
}
async prompting() {
// Have Yeoman greet the user.
logger(
yosay(
`Welcome to the ${em('Living Atlas')} Quick-Start Ansible Generator`
)
);
this.answers = dontAsk
? previousConfig
: await this.prompt([...[
{
store: true,
type: 'input',
name: 'LA_project_name',
message: `Your LA Project ${em('Long Name')}:`,
default: 'Living Atlas of Wakanda',
},
{
store: true,
type: 'input',
name: 'LA_project_shortname',
message: `Your LA Project ${em('Shortname')}:`,
default: (answers) =>
answers['LA_project_name'].replace(/Living Atlas of /g, 'LA '),
},
{
store: true,
type: 'input',
name: 'LA_pkg_name',
message: `Your LA ${em(
'short-lowercase-name'
)} (we'll put your inventories in that directory):`,
default: (answers) =>
answers['LA_project_shortname'].toLowerCase().replace(/ /g, '-'),
validate: (input) =>
new Promise((resolve) => {
if (input.match(/^[a-z0-9-]+$/g)) {
resolve(true);
} else {
resolve('You need to provide some-example-short-name');
}
}),
},
{
store: true,
type: 'input',
name: 'LA_domain',
message: `What is your LA node ${em('main domain')}?`,
default: (answers) => `${answers['LA_pkg_name']}.org`,
validate: (input) => validateDomain(input, 'branding', logger),
},
{
store: true,
type: 'input',
name: 'LA_hostnames',
message: 'Names of the servers you will use (comma or space separated)',
default: (a) => firstRun ? 'vm1, vm2, vm3' : previousConfig['LA_hostnames'] == null && servers.size > 0 ? [...servers].join(',') : previousConfig['LA_hostnames'],
validate: (input) => /^[._\-a-z0-9A-Z, ]+$/.test(input),
// filter: (input, a) => input.split(hostSepRegexp).join('|')
},
{
store: true,
type: 'confirm',
name: 'LA_use_species',
message: `Use ${em('species')} service?`,
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_species_lists',
message: `Use ${em('specieslists')} service?`,
when: (a) => a['LA_use_species'],
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_images',
message: `Use ${em('images')} service?`,
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_spatial',
message: `Use ${em('spatial')} service?`,
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_regions',
message: `Use ${em('regions')} service?`,
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_webapi',
message: `Use ${em(
'webapi'
)} an API documentation service (similar to api.ala.org.au but empty)?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_dashboard',
message: `Use ${em(
'dashboard'
)} view stats service (similar to dashboard.ala.org.au)?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_alerts',
message: `Use ${em('alerts')} service?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_doi',
message: `Use ${em('doi')} service?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_sds',
message: `Use ${em(
'sds'
)} sensitive data service (similar to sds.ala.org.au)?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_data_quality',
message: `Use ${em(
'data-quality service'
)} (similar to data-quality-service.ala.org.au)?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_namematching_service',
message: `Use ${em(
'namematching service'
)} (similar to namematching-ws.ala.org.au)?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_sensitive_data_service',
message: `Use ${em(
'sensitive_data service'
)} (similar to sensitive-ws-test.ala.org.au)?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_biocollect',
message: `Use ${em(
'biocollect'
)} data collection tool (similar to biocollect.ala.org.au/acsa)?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_CAS',
message: `Use ${em('CAS')} Auth service?`,
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_biocache_store',
message: `Use ${em(
'biocache-store'
)} backend for data ingestion? (now replaced by pipelines)`,
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_pipelines',
message: `Use ${em(
'pipelines'
)} backend for data ingestion? (biocache-store replacement)`,
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_pipelines_jenkins',
message: `Use ${em(
'jenkins'
)} with pipelines?`,
when: (a) => a['LA_use_pipelines'],
default: true,
validate: (input) =>
new Promise((resolve) => {
useJenkins = input;
resolve(true);
}),
},
{
store: true,
type: 'confirm',
name: 'LA_use_solrcloud',
message: `Use ${em(
'solrcloud'
)} cluster instead of a single solr server standalone`,
default: (a) => a['LA_use_pipelines'],
validate: (input, a) =>
new Promise((resolve, reject) => {
// logger(`input ${input} ${a['LA_use_pipelines']}`);
if (!input && a['LA_use_pipelines']) {
resolve('You need to use solrcloud with pipelines');
} else {
resolve(true);
}
}),
},
{
store: true,
type: 'confirm',
name: 'LA_use_events',
message: `Use ${em(
'events'
)} extended data model?`,
default: false,
},
{
store: true,
type: 'confirm',
name: 'LA_use_docker_swarm',
message: `Use ${em(
'docker swarm'
)}?`,
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_gatus',
message: `Use ${em(
'gatus'
)} as status service?`,
default: (a) => a['LA_use_docker_swarm'],
},
{
store: true,
type: 'confirm',
name: 'LA_use_portainer',
message: `Use ${em(
'portainer'
)} as docker management platform?`,
default: (a) => a['LA_use_docker_swarm'],
},
{
store: true,
type: 'confirm',
name: 'LA_enable_ssl',
message: `Enable ${em('SSL')}?`,
default: true,
},
{
store: defaultStore,
type: 'input',
name: 'check-ssl',
message: '',
default: '',
when: (a) =>
new Promise((resolve) => {
// noinspection HttpUrlsUsage
a['LA_urls_prefix'] = a['LA_enable_ssl']
? 'https://'
: 'http://';
resolve(false);
}),
},
{
store: defaultStore,
type: 'list',
name: 'LA_collectory_uses_subdomain',
message: (a) =>
`Will the ${em('collectory')} service use a ${
a['LA_urls_prefix']
}${em('subdomain')}.${a['LA_domain']} or a ${
a['LA_urls_prefix']
}${a['LA_domain']}${em('/service-path')} ?`,
choices: [
{name: 'subdomain', value: true},
{name: 'service-path', value: false},
],
},
new PromptHostnameFor('collectory', 'collections'),
new PromptUrlFor('collectory', 'collections'),
new PromptPathFor('collectory', 'collections'),
new PromptSubdomainFor('ala_hub', 'records'),
new PromptHostnameFor('ala_hub', 'records'),
new PromptUrlFor('ala_hub', 'records'),
new PromptPathFor('ala_hub', 'records'),
new PromptSubdomainFor('biocache_service', 'records-ws'),
new PromptHostnameFor('biocache_service', 'records-ws'),
new PromptUrlFor('biocache_service', 'records-ws'),
new PromptPathFor('biocache_service', 'records-ws'),
new PromptSubdomainFor(
'ala_bie',
'species',
(a) => a['LA_use_species']
),
new PromptHostnameFor(
'ala_bie',
'species',
(a) => a['LA_use_species']
),
new PromptUrlFor('ala_bie', 'species', (a) => a['LA_use_species']),
new PromptPathFor('ala_bie', 'species', (a) => a['LA_use_species']),
new PromptSubdomainFor(
'bie_index',
'species-service',
(a) => a['LA_use_species']
),
new PromptHostnameFor(
'bie_index',
'species-ws',
(a) => a['LA_use_species']
),
new PromptUrlFor(
'bie_index',
'species-ws',
(a) => a['LA_use_species']
),
new PromptPathFor(
'bie_index',
'species-ws',
(a) => a['LA_use_species']
),
new PromptSubdomainFor('images', 'images', (a) => a['LA_use_images']),
new PromptHostnameFor('images', 'images', (a) => a['LA_use_images']),
new PromptUrlFor('images', 'images', (a) => a['LA_use_images']),
new PromptPathFor('images', 'images', (a) => a['LA_use_images']),
new PromptSubdomainFor(
'lists',
'specieslists',
(a) => a['LA_use_species_lists']
),
new PromptHostnameFor(
'lists',
'lists',
(a) => a['LA_use_species_lists']
),
new PromptUrlFor('lists', 'lists', (a) => a['LA_use_species_lists']),
new PromptPathFor(
'lists',
'specieslists',
(a) => a['LA_use_species_lists']
),
new PromptSubdomainFor(
'regions',
'regions',
(a) => a['LA_use_regions']
),
new PromptHostnameFor(
'regions',
'regions',
(a) => a['LA_use_regions']
),
new PromptUrlFor('regions', 'regions', (a) => a['LA_use_regions']),
new PromptPathFor('regions', 'regions', (a) => a['LA_use_regions']),
new PromptSubdomainFor('logger', 'logger'),
new PromptHostnameFor('logger', 'logger'),
new PromptUrlFor('logger', 'logger'),
new PromptPathFor('logger', 'logger-service'),
new PromptSubdomainFor('webapi', 'webapi', (a) => a['LA_use_webapi']),
new PromptHostnameFor('webapi', 'api', (a) => a['LA_use_webapi']),
new PromptUrlFor('webapi', 'api', (a) => a['LA_use_webapi']),
new PromptPathFor('webapi', 'webapi', (a) => a['LA_use_webapi']),
new PromptSubdomainFor(
'dashboard',
'dashboard',
(a) => a['LA_use_dashboard']
),
new PromptHostnameFor(
'dashboard',
'dashboard',
(a) => a['LA_use_dashboard']
),
new PromptUrlFor(
'dashboard',
'dashboard',
(a) => a['LA_use_dashboard']
),
new PromptPathFor(
'dashboard',
'dashboard',
(a) => a['LA_use_dashboard']
),
new PromptSubdomainFor('alerts', 'alerts', (a) => a['LA_use_alerts']),
new PromptHostnameFor('alerts', 'alerts', (a) => a['LA_use_alerts']),
new PromptUrlFor('alerts', 'alerts', (a) => a['LA_use_alerts']),
new PromptPathFor('alerts', 'alerts', (a) => a['LA_use_alerts']),
new PromptSubdomainFor('doi', 'doi', (a) => a['LA_use_doi']),
new PromptHostnameFor('doi', 'doi', (a) => a['LA_use_doi']),
new PromptUrlFor('doi', 'doi', (a) => a['LA_use_doi']),
new PromptPathFor('doi', 'doi', (a) => a['LA_use_doi']),
new PromptSubdomainFor('sds', 'sds', (a) => a['LA_use_sds']),
new PromptHostnameFor('sds', 'sds', (a) => a['LA_use_sds']), new PromptUrlFor('sds', 'sds', (a) => a['LA_use_sds']),
new PromptPathFor('sds', 'sds', (a) => a['LA_use_sds']),
new PromptSubdomainFor('namematching_service', 'namematching_service', (a) => a['LA_use_namematching_service']),
new PromptHostnameFor('namematching_service', 'namematching_service', (a) => a['LA_use_namematching_service']), new PromptUrlFor('namematching_service', 'namematching_service', (a) => a['LA_use_namematching_service']),
new PromptPathFor('namematching_service', 'namematching_service', (a) => a['LA_use_namematching_service']),
new PromptSubdomainFor('sensitive_data_service', 'sensitive_data_service', (a) => a['LA_use_sensitive_data_service']),
new PromptHostnameFor('sensitive_data_service', 'sensitive_data_service', (a) => a['LA_use_sensitive_data_service']), new PromptUrlFor('sensitive_data_service', 'sensitive_data_service', (a) => a['LA_use_sensitive_data_service']),
new PromptPathFor('sensitive_data_service', 'sensitive_data_service', (a) => a['LA_use_sensitive_data_service']),
new PromptSubdomainFor('data_quality', 'data_quality', (a) => a['LA_use_data_quality']),
new PromptHostnameFor('data_quality', 'data_quality', (a) => a['LA_use_data_quality']), new PromptUrlFor('data_quality', 'data_quality', (a) => a['LA_use_data_quality']),
new PromptPathFor('data_quality', 'data_quality', (a) => a['LA_use_data_quality']),
new PromptSubdomainFor('biocollect', 'biocollect', (a) => a['LA_use_biocollect']),
new PromptHostnameFor('biocollect', 'biocollect', (a) => a['LA_use_biocollect'] ),
new PromptUrlFor('biocollect', 'biocollect', (a) => a['LA_use_biocollect']),
new PromptPathFor('biocollect', 'biocollect', (a) => a['LA_use_biocollect']),
new PromptSubdomainFor('pdfgen', 'pdfgen', (a) => a['LA_use_biocollect']),
new PromptHostnameFor('pdfgen', 'pdfgen', (a) => a['LA_use_biocollect'] ),
new PromptUrlFor('pdfgen', 'pdfgen', (a) => a['LA_use_biocollect']),
new PromptPathFor('pdfgen', 'pdfgen', (a) => a['LA_use_biocollect']),
new PromptSubdomainFor('ecodata', 'ecodata', (a) => a['LA_use_biocollect']),
new PromptHostnameFor('ecodata', 'ecodata', (a) => a['LA_use_biocollect'] ),
new PromptUrlFor('ecodata', 'ecodata', (a) => a['LA_use_biocollect']),
new PromptPathFor('ecodata', 'ecodata', (a) => a['LA_use_biocollect']),
new PromptSubdomainFor('ecodata_reporting', 'ecodata-reporting', (a) => a['LA_use_biocollect']),
new PromptHostnameFor('ecodata_reporting', 'ecodata-reporting', (a) => a['LA_use_biocollect'] ),
new PromptUrlFor('ecodata_reporting', 'ecodata-reporting', (a) => a['LA_use_biocollect']),
new PromptPathFor('ecodata_reporting', 'ecodata-reporting', (a) => a['LA_use_biocollect']),
// In the future, try to use solrcloud (ALA uses solr-standalone for bie, (a) => !a['LA_use_solrcloud']),
new PromptSubdomainFor('solr', 'solr'),
new PromptHostnameFor('solr', 'index'),
new PromptUrlFor('solr', 'index'),
new PromptPathFor('solr', 'solr'),
new PromptHostnameFor('solrcloud', 'solr', (a) => a['LA_use_solrcloud']),
new PromptHostnameFor('zookeeper', 'zoo', (a) => a['LA_use_solrcloud']),
new PromptSubdomainFor('cas', 'auth', true, true),
new PromptHostnameFor('cas', 'auth'),
new PromptUrlFor('cas', 'auth'),
new PromptHostnameFor('biocache_backend', 'biocache_backend', (a) => a['LA_use_biocache_store']),
new PromptHostnameFor('pipelines', 'pipelines', (a) => a['LA_use_pipelines']),
new PromptSubdomainFor('events', 'events', (a) => a['LA_use_events']),
new PromptHostnameFor('events', 'events', (a) => a['LA_use_events'] ),
new PromptUrlFor('events', 'events', (a) => a['LA_use_events']),
new PromptPathFor('events', 'events', (a) => a['LA_use_events']),
new PromptHostnameFor('events_elasticsearch', 'events_elasticsearch', (a) => a['LA_use_events']),
new PromptHostnameFor('docker_swarm', 'docker_swarm', (a) => a['LA_use_docker_swarm']),
new PromptSubdomainFor('gatus', 'gatus', (a) => a['LA_use_gatus']),
new PromptHostnameFor('gatus', 'gatus', (a) => a['LA_use_gatus'] ),
new PromptUrlFor('gatus', 'gatus', (a) => a['LA_use_gatus']),
new PromptPathFor('gatus', 'gatus', (a) => a['LA_use_gatus']),
new PromptSubdomainFor('portainer', 'portainer', (a) => a['LA_use_portainer']),
new PromptHostnameFor('portainer', 'portainer', (a) => a['LA_use_portainer'] ),
new PromptUrlFor('portainer', 'portainer', (a) => a['LA_use_portainer']),
new PromptPathFor('portainer', 'portainer', (a) => a['LA_use_portainer']),
{
store: true,
type: 'list',
name: 'LA_pipelines_master',
when: (a) => a['LA_use_pipelines'],
message: `${em('pipelines')} cluster master:`,
choices: () => {
let choices = [];
for (let host of groupsAndServers['pipelines']) {
choices.push({name: host, checked: false});
}
return choices;
},
},
new PromptSubdomainFor(
'spatial',
'spatial',
(a) => a['LA_use_spatial'],
true
),
new PromptHostnameFor(
'spatial',
'spatial',
(a) => a['LA_use_spatial']
),
new PromptUrlFor('spatial', 'spatial', (a) => a['LA_use_spatial']),
new PromptSubdomainFor('branding', 'branding'),
new PromptHostnameFor('branding', 'branding'),
new PromptUrlFor('branding', 'branding'),
new PromptPathFor('branding', 'branding'),
{
store: true,
type: 'confirm',
name: 'LA_generate_branding',
message: `Do you want to generate also a sample compatible ${em(
'LA branding'
)}? (Recommended to start, later you can improve it with your site style)`,
default: true,
},
{
store: true,
type: 'confirm',
name: 'LA_use_git',
message: `Use ${em(
'git'
)} in your generated inventories to track their changes? (Very recommended)`,
default: true,
},
], ...additionalToolkitPrompts()]);
// For back-compatibility
if (typeof this.answers['LA_main_hostname'] === 'undefined') {
this.answers['LA_main_hostname'] = this.answers['LA_domain'];
}
this.answers['LA_biocache_cli_hostname'] = this.answers[
'LA_biocache_backend_hostname'
];
this.answers['LA_nameindexer_hostname'] = this.answers[
'LA_biocache_backend_hostname'
];
if (typeof this.answers['LA_spatial_uses_subdomain'] === 'undefined')
this.answers['LA_spatial_uses_subdomain'] = true;
if (typeof this.answers['LA_cas_uses_subdomain'] === 'undefined')
this.answers['LA_cas_uses_subdomain'] = true;
if (typeof this.answers['LA_use_images'] === 'undefined')
this.answers['LA_use_images'] = true;
if (typeof this.answers['LA_use_species_lists'] === 'undefined')
this.answers['LA_use_species_lists'] = false;
if (typeof this.answers['LA_use_species'] === 'undefined')
this.answers['LA_use_species'] = true;
if (typeof this.answers['LA_use_pipelines'] === 'undefined')
this.answers['LA_use_pipelines'] = false;
if (typeof this.answers['LA_use_biocache_store'] === 'undefined')
this.answers['LA_use_biocache_store'] = true;
if (typeof this.answers['LA_use_pipelines_jenkins'] === 'undefined')
this.answers['LA_use_pipelines_jenkins'] = false;
vhostsSet.add(this.answers['LA_domain']);
useJenkins = serviceUseVar('pipelines_jenkins', this.answers)
if (dontAsk) {
// Compatible with old generated inventories and don-ask
if (typeof this.answers['LA_use_webapi'] === 'undefined')
this.answers['LA_use_webapi'] = false;
if (typeof this.answers['LA_use_alerts'] === 'undefined')
this.answers['LA_use_alerts'] = false;
if (typeof this.answers['LA_use_doi'] === 'undefined')
this.answers['LA_use_doi'] = false;
if (typeof this.answers['LA_use_dashboard'] === 'undefined')
this.answers['LA_use_dashboard'] = false;
if (typeof this.answers['LA_use_sds'] === 'undefined')
this.answers['LA_use_sds'] = false;
if (typeof this.answers['LA_use_biocollect'] === 'undefined')
this.answers['LA_use_biocollect'] = false;
if (typeof this.answers['LA_generate_branding'] === 'undefined')
this.answers['LA_generate_branding'] = false;
if (typeof this.answers['LA_branding_hostname'] === 'undefined')
this.answers['LA_branding_hostname'] = this.answers['LA_domain'];
if (typeof this.answers['LA_branding_path'] === 'undefined')
this.answers['LA_branding_path'] = '/';
if (typeof this.answers['LA_branding_url'] === 'undefined')
this.answers['LA_branding_url'] = this.answers['LA_domain'];
if (typeof this.answers['LA_use_pipelines'] === 'undefined') {
this.answers['LA_use_pipelines'] = false;
}
if (typeof this.answers['LA_use_biocache_store'] === 'undefined') {
this.answers['LA_use_biocache_store'] = true;
}
if (typeof this.answers['LA_use_solrcloud'] === 'undefined') {
this.answers['LA_use_solrcloud'] = false;
}
if (typeof this.answers['LA_use_data_quality'] === 'undefined') {
this.answers['LA_use_data_quality'] = false;
}
if (typeof this.answers['LA_use_namematching_service'] === 'undefined') {
this.answers['LA_use_namematching_service'] = false;
}
if (typeof this.answers['LA_use_sensitive_data_service'] === 'undefined') {
this.answers['LA_use_sensitive_data_service'] = false;
}
if (typeof this.answers['LA_use_events'] === 'undefined') {
this.answers['LA_use_events'] = false;
}
if (typeof this.answers['LA_use_docker_swarm'] === 'undefined') {
this.answers['LA_use_docker_swarm'] = false;
}
if (typeof this.answers['LA_use_gatus'] === 'undefined') {
this.answers['LA_use_gatus'] = false;
}
if (typeof this.answers['LA_use_portainer'] === 'undefined') {
this.answers['LA_use_portainer'] = false;
}
if (typeof this.answers['LA_use_cassandra'] === 'undefined') {
this.answers['LA_use_cassandra'] = this.answers['LA_use_pipelines'];
}
// noinspection HttpUrlsUsage
this.answers['LA_urls_prefix'] = this.answers['LA_enable_ssl']
? 'https://'
: 'http://';
if (debug) logger(this.answers);
// TODO Rewrite this
Object.keys(servicesDesc).forEach((service) => {
if (service === 'spatial' && !this.answers['LA_use_spatial']) return;
if (service === 'regions' && !this.answers['LA_use_regions']) return;
if (service === 'ala_bie' && !this.answers['LA_use_species']) return;
if (service === 'bie_index' && !this.answers['LA_use_species']) return;
if (service === 'lists' && !this.answers['LA_use_species_lists'])
return;
if (service === 'images' && !this.answers['LA_use_images']) return;
if (service === 'webapi' && !this.answers['LA_use_webapi']) return;
if (service === 'alerts' && !this.answers['LA_use_alerts']) return;
if (service === 'doi' && !this.answers['LA_use_doi']) return;
if (service === 'dashboard' && !this.answers['LA_use_dashboard'])
return;
if (service === 'sds' && !this.answers['LA_use_sds']) return;
if (service === 'biocollect' && !this.answers['LA_use_biocollect'])
return;
if (service === 'events' && !this.answers['LA_use_events'])
return;
if (service === 'events_elasticsearch' && !this.answers['LA_use_events'])
return;
if (service === 'docker_swarm' && !this.answers['LA_use_docker_swarm'])
return;
if (service === 'docker_common' && !this.answers['LA_use_docker_swarm'])
return;
if (service === 'gatus' && !this.answers['LA_use_gatus'])
return;
if (service === 'portainer' && !this.answers['LA_use_portainer'])
return;
if (service === 'biocache_backend' && !this.answers['LA_use_biocache_store'])
return;
if (service === 'pipelines' && !this.answers['LA_use_pipelines'])
return;
if (service === 'data_quality' && !this.answers['LA_use_data_quality']) return;
if (service === 'namematching_service' && !this.answers['LA_use_namematching_service']) return;
if (service === 'sensitive_data_service' && !this.answers['LA_use_sensitive_data_service']) return;
const hostVar = `LA_${service}_hostname`;
const serviceUrl = this.answers[`LA_${service}_url`];
const hostname = this.answers[hostVar];
if (debug)
logger(`${service}: ${hostVar} -> ${hostname}, url: ${serviceUrl}`);
if (typeof serviceUrl !== 'undefined') vhostsSet.add(serviceUrl);
});
if (debug) logger(servers);
if (debug) logger(servicesInUse);
}
if (this.answers['LA_use_pipelines']) {
if (isDefined(this.answers['LA_variable_pipelines_master'])) {
// configured from the toolkit
this.answers["LA_pipelines_master"] = this.answers['LA_variable_pipelines_master'];
}
if (this.answers["LA_pipelines_master"] == null) {
// Set master to first one as last attempt to set this
this.answers["LA_pipelines_master"] = groupsAndServers['pipelines'][0];
}
const pipelinesMaster = this.answers["LA_pipelines_master"];
if (useJenkins) {
// we only store the jenkins master when we know it
storeGroupServer('jenkins', pipelinesMaster);
for (let s of groupsAndServers['pipelines']) {
storeGroupServer('pipelines_jenkins', s);
}
}
groupsChildren['spark'] = {cluster_master: [pipelinesMaster], cluster_nodes: [...groupsAndServers['pipelines']]};
if (useJenkins) groupsChildren['pipelines_jenkins'] = {jenkins_master: [pipelinesMaster], jenkins_slaves: [...groupsAndServers['pipelines']]};
// remove master from slaves
removeOnce(groupsChildren['spark']['cluster_nodes'], pipelinesMaster);
// remove master from slaves
if (useJenkins) removeOnce(groupsChildren['pipelines_jenkins']['jenkins_slaves'], pipelinesMaster);
}
this.answers["LA_groups_and_servers"] = groupsAndServers;
this.answers["LA_groups_children"] = groupsChildren;
// remove these services included in others playbooks (like spark, etc in pipelines)
servicesInUse = servicesInUse.filter(x => ['spark', 'pipelines_jenkins', 'jenkins', 'hadoop', 'zookeeper'].indexOf(x.service) === -1);
// Sort services to avoid other orders caused by the use of sets and unnecessary differences in inventories
let sorted = Object.keys(servicesDesc);
servicesInUse.sort(function (a, b) {
return (sorted.indexOf(a) < sorted.indexOf(b));
});
if (debug) logger(JSON.stringify(groupsChildren));
}
writing() {
const conf = this.answers;
let dest = conf['LA_pkg_name'];
// Old destination check, for back compatibility
// For now we use with "LA_pkg_name-inventories"
if (!fsN.existsSync(dest)) dest = `${conf['LA_pkg_name']}-inventories`;
const isHub = isDefined(conf['LA_is_hub']);
const hasHubs = isDefined(conf['LA_hubs']);
const cmdOpts = {
cwd: this.destinationPath(dest),
shell: true,
stdio: 'inherit',
};
// accessing this._destinationRoot
if (debug) {
// noinspection JSCheckFunctionSignatures
logger(`Destination root: ${this.destinationRoot()}`);
}
if (debug) logger(`cmdOpts: ${JSON.stringify(cmdOpts)}`);
services.forEach((service) => {
const path = conf[`LA_${service}_path`];
if (path === '/') {
conf[`LA_${service}_path`] = '';
}
// url var is new, so we use hostname for old generated inventories
if (typeof conf[`LA_${service}_url`] === 'undefined') {
conf[`LA_${service}_url`] = conf[`LA_${service}_hostname`];
}
});
if (conf['LA_solr_uses_subdomain']) {
conf['LA_solr_path'] = '';
}
// Backward compatibility
if (typeof conf['LA_solr_url'] === 'undefined') {
conf['LA_solr_url'] = conf['LA_solr_hostname'];
}
if (typeof conf['LA_cas_url'] === 'undefined') {
conf['LA_cas_url'] = conf['LA_cas_hostname'];
}
if (typeof conf['LA_spatial_url'] === 'undefined') {
conf['LA_spatial_url'] = conf['LA_spatial_hostname'];
}
if (typeof conf['LA_generate_branding'] === 'undefined') {
conf['LA_generate_branding'] = false;
}
conf['LA_servers'] = servers;
conf['LA_services_in_use'] = servicesInUse;
conf['LA_groups_and_servers'] = groupsAndServers;
const filePrefix = conf['LA_pkg_name'];
const useCAS = conf['LA_use_CAS'];
const useSpatial = conf['LA_use_spatial'];
// logger(`vhost: ${vhostsSet.size}`);
conf['LA_nginx_vhosts'] = [...vho