Skip to content

Commit

Permalink
feat(deployment): --genesis=FILE and unique digitalocean SSH keys
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed Jun 3, 2021
1 parent 22a3f42 commit 00d69da
Show file tree
Hide file tree
Showing 13 changed files with 134 additions and 73 deletions.
2 changes: 1 addition & 1 deletion packages/deployment/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ RUN echo 'deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main' >> /e
# COPY --from=go-build /go/bin/journalbeat /usr/local/bin/

WORKDIR /usr/src/agoric-sdk/packages/deployment
RUN ln -sf $PWD/ag-setup-cosmos /usr/local/bin/
RUN ln -sf $PWD/src/entrypoint.cjs /usr/local/bin/ag-setup-cosmos

WORKDIR /data/chains

Expand Down
4 changes: 2 additions & 2 deletions packages/deployment/Dockerfile.sdk
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ RUN make GIT_REVISION="$GIT_REVISION" MOD_READONLY= compile-go

###############################
# The js build container
FROM node:12-buster AS build-js
FROM node:14.15-buster AS build-js
ARG MODDABLE_COMMIT_HASH
ARG MODDABLE_URL

Expand All @@ -39,7 +39,7 @@ RUN yarn install --frozen-lockfile --production

###############################
# The install container.
FROM node:12-buster AS install
FROM node:14.15-buster AS install

# Install some conveniences.
RUN apt-get update && apt-get install -y vim jq less && apt-get clean -y
Expand Down
36 changes: 0 additions & 36 deletions packages/deployment/ag-setup-cosmos

This file was deleted.

2 changes: 1 addition & 1 deletion packages/deployment/ansible/prepare-machine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
gather_facts: yes
strategy: free
vars:
- NODEJS_VERSION: 12
- NODEJS_VERSION: 14
roles:
- prereq
2 changes: 1 addition & 1 deletion packages/deployment/ansible/roles/copy/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

- name: Synchronize Agoric SDK
synchronize:
src: "/usr/src/agoric-sdk/"
src: "{{ AGORIC_SDK }}/"
dest: "/usr/src/agoric-sdk/"
dirs: yes
delete: yes
Expand Down
6 changes: 6 additions & 0 deletions packages/deployment/ansible/update_known_hosts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,9 @@
state: present
line: "{{ item }}"
with_items: "{{ ssh_keys.splitlines() }}"

- name: Set master authorized SSH key
authorized_key:
user: root
state: present
key: "{{ lookup('file', SETUP_HOME + '/id_ecdsa.pub') }}"
5 changes: 4 additions & 1 deletion packages/deployment/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
"js": "mjs"
},
"private": true,
"main": "main.js",
"main": "src/main.js",
"bin": {
"ag-setup-cosmos": "src/entrypoint.cjs"
},
"scripts": {
"test": "exit 0",
"test:xs": "exit 0",
Expand Down
34 changes: 34 additions & 0 deletions packages/deployment/src/entrypoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#! /usr/bin/env node
/* global setInterval */

import '@agoric/install-ses';

import fs from 'fs';
import path from 'path';
import temp from 'temp';
import process from 'process';
import { exec, spawn } from 'child_process';
import inquirer from 'inquirer';
import fetch from 'node-fetch';

import { running } from './run';
import { setup } from './setup';
import * as files from './files';
import deploy from './main.js';

process.on('SIGINT', () => process.exit(-1));
deploy(process.argv[1], process.argv.splice(2), {
env: process.env,
rd: files.reading(fs, path),
wr: files.writing(fs, path, temp),
setup: setup({ resolve: path.resolve, env: process.env, setInterval }),
running: running(process, { exec, process, spawn }),
inquirer,
fetch,
}).then(
res => process.exit(res || 0),
rej => {
console.error(`error running ag-setup-cosmos:`, rej);
process.exit(1);
},
);
File renamed without changes.
49 changes: 35 additions & 14 deletions packages/deployment/init.js → packages/deployment/src/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ const nodeCount = (count, force) => {
const tfStringify = obj => {
let ret = '';
if (Array.isArray(obj)) {
let sep = '[';
ret += '[';
let sep = '';
for (const el of obj) {
ret += sep + tfStringify(el);
sep = ',';
}
ret += ']';
} else if (Object(obj) === obj) {
let sep = '{';
ret += '{';
let sep = '';
for (const key of Object.keys(obj).sort()) {
ret += `${sep}${JSON.stringify(key)}=${tfStringify(obj[key])}`;
sep = ',';
Expand Down Expand Up @@ -149,7 +151,7 @@ module "${PLACEMENT}" {
source = "${setup.SETUP_DIR}/terraform/${provider.value}"
CLUSTER_NAME = "${PREFIX}\${var.NETWORK_NAME}-${PLACEMENT}"
OFFSET = "\${var.OFFSETS["${PLACEMENT}"]}"
SSH_KEY_FILE = "\${var.SSH_KEY_FILE}"
SSH_KEY_FILE = "${PLACEMENT}-\${var.SSH_KEY_FILE}"
ROLE = "\${var.ROLES["${PLACEMENT}"]}"
SERVERS = "\${length(var.DATACENTERS["${PLACEMENT}"])}"
VOLUMES = "\${var.VOLUMES["${PLACEMENT}"]}"
Expand Down Expand Up @@ -187,7 +189,8 @@ module "${PLACEMENT}" {
OFFSET = "\${var.OFFSETS["${PLACEMENT}"]}"
REGIONS = "\${var.DATACENTERS["${PLACEMENT}"]}"
ROLE = "\${var.ROLES["${PLACEMENT}"]}"
SSH_KEY_FILE = "\${var.SSH_KEY_FILE}"
# TODO: DigitalOcean provider module doesn't allow reuse of SSH public keys.
SSH_KEY_FILE = "${PLACEMENT}-\${var.SSH_KEY_FILE}"
DO_API_TOKEN = "\${var.API_KEYS["${PLACEMENT}"]}"
SERVERS = "\${length(var.DATACENTERS["${PLACEMENT}"])}"
}
Expand Down Expand Up @@ -292,16 +295,23 @@ const doInit = ({ env, rd, wr, running, setup, inquirer, fetch }) => async (
const deploymentJson = `deployment.json`;
const config = (await rd.exists(deploymentJson))
? JSON.parse(await rd.readFile(deploymentJson, 'utf-8'))
: {
PLACEMENTS: [],
PLACEMENT_PROVIDER: {},
SSH_PRIVATE_KEY_FILE: `id_${SSH_TYPE}`,
DETAILS: {},
OFFSETS: {},
ROLES: {},
DATACENTERS: {},
PROVIDER_NEXT_INDEX: {},
};
: {};

const defaultConfigs = {
PLACEMENTS: [],
PLACEMENT_PROVIDER: {},
SSH_PRIVATE_KEY_FILE: `id_${SSH_TYPE}`,
DETAILS: {},
OFFSETS: {},
ROLES: {},
DATACENTERS: {},
PROVIDER_NEXT_INDEX: {},
};
Object.entries(defaultConfigs).forEach(([key, dflt]) => {
if (!(key in config)) {
config[key] = dflt;
}
});
config.NETWORK_NAME = overrideNetworkName;

// eslint-disable-next-line no-constant-condition
Expand Down Expand Up @@ -514,6 +524,16 @@ variable ${JSON.stringify(vname)} {
for (const PLACEMENT of Object.keys(config.PLACEMENT_PROVIDER).sort()) {
const PROVIDER = config.PLACEMENT_PROVIDER[PLACEMENT];
const provider = PROVIDERS[PROVIDER];

// Create a placement-specific key file.
const keyFile = `${PLACEMENT}-${config.SSH_PRIVATE_KEY_FILE}`;
// eslint-disable-next-line no-await-in-loop
if (!(await rd.exists(keyFile))) {
// Set empty password.
// eslint-disable-next-line no-await-in-loop
await needDoRun(['ssh-keygen', '-N', '', '-t', SSH_TYPE, '-f', keyFile]);
}

// eslint-disable-next-line no-await-in-loop
await provider.createPlacementFiles(provider, PLACEMENT, clusterPrefix);
}
Expand Down Expand Up @@ -552,6 +572,7 @@ output "offsets" {
#! /bin/sh
exec ansible-playbook -f10 \\
-eSETUP_HOME=${shellEscape(cwd())} \\
-eAGORIC_SDK=${shellEscape(setup.AGORIC_SDK)} \\
-eNETWORK_NAME=\`cat ${shellEscape(rd.resolve('network.txt'))}\` \\
\${1+"$@"}
`,
Expand Down
64 changes: 48 additions & 16 deletions packages/deployment/main.js → packages/deployment/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { createHash } from 'crypto';
import chalk from 'chalk';
import parseArgs from 'minimist';
import { assert, details as X } from '@agoric/assert';
import { dirname, basename } from 'path';
import { doInit } from './init';
import { shellMetaRegexp, shellEscape } from './run';
import { streamFromString } from './files';
Expand Down Expand Up @@ -200,6 +201,7 @@ show-config display the client connection parameters
default: {
'boot-tokens': DEFAULT_BOOT_TOKENS,
},
string: ['bump', 'import-from', 'genesis'],
stopEarly: true,
},
);
Expand Down Expand Up @@ -246,7 +248,7 @@ show-config display the client connection parameters
case undefined: {
await wr.createFile('boot-tokens.txt', bootTokens);
const bootOpts = [];
for (const propagate of ['bump', 'import-from']) {
for (const propagate of ['bump', 'import-from', 'genesis']) {
const val = subOpts[propagate];
if (val !== undefined) {
bootOpts.push(`--${propagate}=${val}`);
Expand Down Expand Up @@ -309,7 +311,7 @@ show-config display the client connection parameters
await inited();
// eslint-disable-next-line no-unused-vars
const { _: subArgs, ...subOpts } = parseArgs(args.slice(1), {
string: ['bump', 'import-from'],
string: ['bump', 'import-from', 'genesis'],
stopEarly: true,
});

Expand Down Expand Up @@ -339,9 +341,18 @@ show-config display the client connection parameters
await guardFile(`chain-version.txt`, makeFile => makeFile('1'));

// Assign the chain name.
const networkName = await trimReadFile('network.txt');
const chainVersion = await trimReadFile('chain-version.txt');
const chainName = `${networkName}-${chainVersion}`;
let chainName;
let genJSON;
if (subOpts.genesis) {
// Fetch the specified genesis, don't generate it.
genJSON = await trimReadFile(subOpts.genesis);
const genesis = JSON.parse(genJSON);
chainName = genesis.chain_id;
} else {
const networkName = await trimReadFile('network.txt');
const chainVersion = await trimReadFile('chain-version.txt');
chainName = `${networkName}-${chainVersion}`;
}
const currentChainName = await trimReadFile(
`${COSMOS_DIR}/chain-name.txt`,
).catch(_ => undefined);
Expand All @@ -363,14 +374,25 @@ show-config display the client connection parameters
await guardFile(`${COSMOS_DIR}/prepare.stamp`, () =>
needReMain(['play', 'prepare-cosmos']),
);
await guardFile(`${COSMOS_DIR}/genesis.stamp`, () =>
needReMain(['play', 'cosmos-genesis']),
);

// If the canonical genesis exists, use it.
await guardFile(`${COSMOS_DIR}/genesis.stamp`, async () => {
await wr.mkdir(`${COSMOS_DIR}/data`, { recursive: true });
if (genJSON) {
await wr.createFile(`${COSMOS_DIR}/data/genesis.json`, genJSON);
} else {
await guardFile(`${COSMOS_DIR}/data/genesis.json`, async () => {
await needReMain(['play', 'cosmos-genesis']);
// Don't overwrite the data/genesis.json.
return true;
});
}
});

await guardFile(`${COSMOS_DIR}/set-defaults.stamp`, async () => {
await needReMain(['play', 'cosmos-clone-config']);

const agoricCli = rd.resolve(__dirname, `../agoric-cli/bin/agoric`);
const agoricCli = rd.resolve(__dirname, `../../agoric-cli/bin/agoric`);

// Apply the Agoric set-defaults to all the .dst dirs.
const files = await rd.readdir(`${COSMOS_DIR}/data`);
Expand All @@ -397,13 +419,19 @@ show-config display the client connection parameters
...importFlags,
`${COSMOS_DIR}/data/${dst}`,
]);
if (i === 0) {
// Make a canonical copy of the genesis.json.
const data = await rd.readFile(
`${COSMOS_DIR}/data/${dst}/genesis.json`,
);
await wr.createFile(`${COSMOS_DIR}/data/genesis.json`, data);
if (i !== 0) {
return;
}
await guardFile(
`${COSMOS_DIR}/data/genesis.json`,
async makeGenesis => {
// Make a canonical copy of the genesis.json if there isn't one.
const data = await rd.readFile(
`${COSMOS_DIR}/data/${dst}/genesis.json`,
);
await makeGenesis(data);
},
);
}),
);
});
Expand Down Expand Up @@ -862,6 +890,10 @@ ${name}:
if (!addRole[role]) {
addRole[role] = makeGroup(role, 4);
}
const keyFile = rd.resolve(
dirname(SSH_PRIVATE_KEY_FILE),
`${provider}-${basename(SSH_PRIVATE_KEY_FILE)}`,
);
for (let instance = 0; instance < ips.length; instance += 1) {
const ip = ips[instance];
const node = `${role}${offset + instance}`;
Expand All @@ -877,7 +909,7 @@ ${name}:
${node}:${roleParams}
ansible_host: ${ip}
ansible_ssh_user: root
ansible_ssh_private_key_file: '${SSH_PRIVATE_KEY_FILE}'
ansible_ssh_private_key_file: '${keyFile}'
ansible_python_interpreter: /usr/bin/python`;
addProvider(host);

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export const SSH_TYPE = 'ecdsa';

export const setup = ({ resolve, env, setInterval }) => {
const it = harden({
SETUP_DIR: __dirname,
AGORIC_SDK: resolve(__dirname, '../../..'),
SETUP_DIR: resolve(__dirname, '..'),
SETUP_HOME: env.AG_SETUP_COSMOS_HOME
? resolve(env.AG_SETUP_COSMOS_HOME)
: resolve('.'),
Expand Down

0 comments on commit 00d69da

Please sign in to comment.