Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modifiable persistent deployment configuration #2613

Merged
merged 2 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 71 additions & 61 deletions packages/deployment/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import {
basename,
chmod,
createFile,
exists,
mkdir,
mustNotExist,
readFile,
resolve,
} from './files';
import { chdir, needDoRun, shellEscape } from './run';
Expand Down Expand Up @@ -198,9 +199,9 @@ module "${PLACEMENT}" {

const askPlacement = PLACEMENTS => {
let total = 0;
for (const placement of Object.values(PLACEMENTS)) {
total += calculateTotal(placement);
}
PLACEMENTS.forEach(
([_PLACEMENT, placement]) => (total += calculateTotal(placement)),
);
const count = nodeCount(total, true);
const DONE = { name: `Done with allocation${count}`, value: '' };
const NEW = { name: `Initialize new placement`, value: 'NEW' };
Expand All @@ -213,12 +214,10 @@ const askPlacement = PLACEMENTS => {
choices: [
DONE,
NEW,
...Object.keys(PLACEMENTS)
.sort()
.map(place => ({
name: `${place}${nodeCount(calculateTotal(PLACEMENTS[place]))}`,
value: place,
})),
...PLACEMENTS.map(([place, placement]) => ({
name: `${place}${nodeCount(calculateTotal(placement))}`,
value: place,
})),
],
},
];
Expand Down Expand Up @@ -250,28 +249,41 @@ const askProvider = () => {
};

const doInit = async (progname, args) => {
let [dir, NETWORK_NAME] = args.slice(1);
let [dir, overrideNetworkName] = args.slice(1);
if (!dir) {
dir = SETUP_HOME;
}
assert(dir, X`Need: [dir] [[network name]]`);
await mustNotExist(`${dir}/network.txt`);

const adir = resolve(process.cwd(), dir);
const SSH_PRIVATE_KEY_FILE = resolve(adir, `id_${SSH_TYPE}`);
if (!NETWORK_NAME) {
NETWORK_NAME = process.env.NETWORK_NAME;
const networkTxt = `${adir}/network.txt`;
if (await exists(networkTxt)) {
overrideNetworkName = (await readFile(networkTxt, 'utf-8')).trimEnd();
}

if (!overrideNetworkName) {
overrideNetworkName = process.env.NETWORK_NAME;
}
if (!NETWORK_NAME) {
NETWORK_NAME = basename(dir);
if (!overrideNetworkName) {
overrideNetworkName = basename(dir);
}

// TODO: Gather information and persist so they can change it before commit.
// Gather saved information.
const deploymentJson = `${adir}/deployment.json`;
const config = (await exists(deploymentJson))
? JSON.parse(await readFile(deploymentJson, 'utf-8'))
: {
PLACEMENTS: [],
PLACEMENT_PROVIDER: {},
SSH_PRIVATE_KEY_FILE: `id_${SSH_TYPE}`,
DETAILS: {},
OFFSETS: {},
DATACENTERS: {},
PROVIDER_NEXT_INDEX: {},
};
config.NETWORK_NAME = overrideNetworkName;

let instance = 0;
const PLACEMENTS = {};
const PLACEMENT_PROVIDER = {};
const lastPlacement = {};
const DETAILS = {};
try {
await mkdir(dir);
} catch (e) {
Expand All @@ -282,14 +294,14 @@ const doInit = async (progname, args) => {
// eslint-disable-next-line no-constant-condition
while (true) {
// eslint-disable-next-line no-await-in-loop
let { PLACEMENT } = await askPlacement(PLACEMENTS);
let { PLACEMENT } = await askPlacement(config.PLACEMENTS);
if (!PLACEMENT) {
break;
}
let provider;
let myDetails = {};
if (PLACEMENT !== 'NEW') {
const PROVIDER = PLACEMENT_PROVIDER[PLACEMENT];
const PROVIDER = config.PLACEMENT_PROVIDER[PLACEMENT];
provider = PROVIDERS[PROVIDER];
} else {
// eslint-disable-next-line no-await-in-loop
Expand All @@ -301,12 +313,13 @@ const doInit = async (progname, args) => {
provider = PROVIDERS[PROVIDER];

const setPlacement = () => {
if (!lastPlacement[PROVIDER]) {
lastPlacement[PROVIDER] = 0;
const idx = config.PROVIDER_NEXT_INDEX;
if (!idx[PROVIDER]) {
idx[PROVIDER] = 0;
}
lastPlacement[PROVIDER] += 1;
PLACEMENT = `${PROVIDER}${lastPlacement[PROVIDER]}`;
PLACEMENT_PROVIDER[PLACEMENT] = PROVIDER;
idx[PROVIDER] += 1;
PLACEMENT = `${PROVIDER}${idx[PROVIDER]}`;
config.PLACEMENT_PROVIDER[PLACEMENT] = PROVIDER;
};

if (provider.askDetails) {
Expand All @@ -322,14 +335,14 @@ const doInit = async (progname, args) => {
// Out with the old, in with the new.
setPlacement();
for (const vname of Object.keys(myDetails)) {
delete DETAILS[vname][PLACEMENT];
delete config.DETAILS[vname][PLACEMENT];
}
myDetails = PLACEMENT_DETAILS;
for (const vname of Object.keys(myDetails)) {
if (!DETAILS[vname]) {
DETAILS[vname] = {};
if (!config.DETAILS[vname]) {
config.DETAILS[vname] = {};
}
DETAILS[vname][PLACEMENT] = PLACEMENT_DETAILS[vname];
config.DETAILS[vname][PLACEMENT] = PLACEMENT_DETAILS[vname];
}
} else {
setPlacement();
Expand All @@ -340,7 +353,9 @@ const doInit = async (progname, args) => {
provider.datacenters &&
// eslint-disable-next-line no-await-in-loop
(await provider.datacenters(provider, PLACEMENT, myDetails));
const placement = PLACEMENTS[PLACEMENT] || {};
const [_p, placement] = config.PLACEMENTS.find(
([p]) => p === PLACEMENT,
) || [PLACEMENT, {}];
if (dcs) {
// Add our choices to the list.
const already = { ...placement };
Expand Down Expand Up @@ -390,23 +405,21 @@ const doInit = async (progname, args) => {
break;
}
}
PLACEMENTS[PLACEMENT] = placement;
config.PLACEMENTS.push([PLACEMENT, placement]);
}

// Collate the placement information.
const OFFSETS = {};
const DATACENTERS = {};
for (const [PLACEMENT, placement] of Object.entries(PLACEMENTS)) {
for (const [PLACEMENT, placement] of config.PLACEMENTS) {
const offset = instance;
DATACENTERS[PLACEMENT] = [];
config.DATACENTERS[PLACEMENT] = [];
for (const dc of Object.keys(placement).sort()) {
const nodes = [];
for (let i = 0; i < placement[dc]; i += 1) {
instance += 1;
nodes.push(dc);
}
if (nodes.length !== 0) {
DATACENTERS[PLACEMENT].push(...nodes);
config.DATACENTERS[PLACEMENT].push(...nodes);
}
}

Expand All @@ -417,7 +430,7 @@ const doInit = async (progname, args) => {
}

// Commit the final details.
OFFSETS[PLACEMENT] = offset;
config.OFFSETS[PLACEMENT] = offset;
}

assert(instance !== 0, X`Aborting due to no nodes configured!`);
Expand All @@ -428,27 +441,27 @@ const doInit = async (progname, args) => {
# Terraform configuration generated by "${progname} init"

variable "NETWORK_NAME" {
default = "${NETWORK_NAME}"
default = "${config.NETWORK_NAME}"
}

variable "SSH_KEY_FILE" {
default = "${SSH_PRIVATE_KEY_FILE}.pub"
default = "${config.SSH_PRIVATE_KEY_FILE}.pub"
}

variable "DATACENTERS" {
default = ${tfStringify(DATACENTERS)}
default = ${tfStringify(config.DATACENTERS)}
}

variable "OFFSETS" {
default = ${tfStringify(OFFSETS)}
default = ${tfStringify(config.OFFSETS)}
}

${Object.keys(DETAILS)
${Object.keys(config.DETAILS)
.sort()
.map(
vname => `\
variable ${JSON.stringify(vname)} {
default = ${tfStringify(DETAILS[vname])}
default = ${tfStringify(config.DETAILS[vname])}
}
`,
)
Expand All @@ -458,8 +471,8 @@ variable ${JSON.stringify(vname)} {

// Go and create the specific files.
const clusterPrefix = 'ag-chain-cosmos-';
for (const PLACEMENT of Object.keys(PLACEMENT_PROVIDER).sort()) {
const PROVIDER = PLACEMENT_PROVIDER[PLACEMENT];
for (const PLACEMENT of Object.keys(config.PLACEMENT_PROVIDER).sort()) {
const PROVIDER = config.PLACEMENT_PROVIDER[PLACEMENT];
const provider = PROVIDERS[PROVIDER];
// eslint-disable-next-line no-await-in-loop
await provider.createPlacementFiles(provider, PLACEMENT, clusterPrefix);
Expand All @@ -470,7 +483,7 @@ variable ${JSON.stringify(vname)} {
`\
output "public_ips" {
value = {
${Object.keys(DATACENTERS)
${Object.keys(config.DATACENTERS)
.sort()
.map(p => ` ${p} = "\${module.${p}.public_ips}"`)
.join('\n')}
Expand All @@ -483,16 +496,11 @@ output "offsets" {
`,
);

// Set empty password.
await needDoRun([
'ssh-keygen',
'-N',
'',
'-t',
SSH_TYPE,
'-f',
SSH_PRIVATE_KEY_FILE,
]);
const keyFile = resolve(adir, config.SSH_PRIVATE_KEY_FILE);
if (!(await exists(keyFile))) {
// Set empty password.
await needDoRun(['ssh-keygen', '-N', '', '-t', SSH_TYPE, '-f', keyFile]);
}

await createFile(
PLAYBOOK_WRAPPER,
Expand All @@ -519,7 +527,9 @@ pipelining = True
`,
);

await createFile(`network.txt`, NETWORK_NAME);
// Persist data for later.
await createFile(deploymentJson, JSON.stringify(config, undefined, 2));
await createFile(networkTxt, config.NETWORK_NAME);
};

export default doInit;
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ variable "ssh_key" {

variable "instance_size" {
description = "The instance size to use"
default = "2gb"
default = "s-2vcpu-4gb"
}

variable "volume_size" {
Expand Down