diff --git a/packages/agoric-cli/lib/chain-config.js b/packages/agoric-cli/lib/chain-config.js index 2c87eac404b..909c6674aa6 100644 --- a/packages/agoric-cli/lib/chain-config.js +++ b/packages/agoric-cli/lib/chain-config.js @@ -56,6 +56,8 @@ export function finishTendermintConfig({ exportMetrics, portNum = `${DEFAULT_RPC_PORT}`, persistentPeers = '', + seeds = '', + unconditionalPeerIds = '', }) { const rpcPort = Number(portNum); @@ -70,6 +72,8 @@ export function finishTendermintConfig({ // Update addresses in the config. config.p2p.laddr = `tcp://0.0.0.0:${rpcPort - 1}`; config.p2p.persistent_peers = persistentPeers; + config.p2p.unconditional_peer_ids = unconditionalPeerIds; + config.p2p.seeds = seeds; config.rpc.laddr = `tcp://0.0.0.0:${rpcPort}`; config.rpc.max_body_bytes = 15 * 10 ** 6; diff --git a/packages/agoric-cli/lib/main.js b/packages/agoric-cli/lib/main.js index 1fbbf3e3528..fb4b177b503 100644 --- a/packages/agoric-cli/lib/main.js +++ b/packages/agoric-cli/lib/main.js @@ -120,6 +120,12 @@ const main = async (progname, rawArgs, powers) => { 'set the config.toml p2p.persistent_peers value', '', ) + .option('--seeds ', 'set the config.toml p2p.seeds value', '') + .option( + '--unconditional-peer-ids ', + 'set the config.toml p2p.unconditional_peer_ids value', + '', + ) .option( '--export-metrics', 'open ports to export Prometheus metrics', diff --git a/packages/agoric-cli/lib/set-defaults.js b/packages/agoric-cli/lib/set-defaults.js index 0792085ea9c..d04d97931ac 100644 --- a/packages/agoric-cli/lib/set-defaults.js +++ b/packages/agoric-cli/lib/set-defaults.js @@ -58,12 +58,14 @@ export default async function setDefaultsMain(progname, rawArgs, powers, opts) { if (configFile) { log(`read ${configFile}`); - const { persistentPeers } = opts; + const { persistentPeers, seeds, unconditionalPeerIds } = opts; const configToml = await fs.readFile(configFile, 'utf-8'); const newConfigToml = finishTendermintConfig({ configToml, persistentPeers, + seeds, + unconditionalPeerIds, exportMetrics, }); await create(configFile, newConfigToml); diff --git a/packages/deployment/ansible/cosmos-genesis.yml b/packages/deployment/ansible/cosmos-genesis.yml index 1c913bc43df..c0dd7b7b400 100644 --- a/packages/deployment/ansible/cosmos-genesis.yml +++ b/packages/deployment/ansible/cosmos-genesis.yml @@ -12,6 +12,6 @@ - STAKER: ag-staker - STAKER_TOKENS: 10000000000000000000000000uagstake - STAKER_AMOUNT: 50000000uagstake - - STAKER_NODE: node0 + - STAKER_NODE: validator0 roles: - cosmos-genesis diff --git a/packages/deployment/ansible/cosmos-validators.yml b/packages/deployment/ansible/cosmos-validators.yml index 4ae2f42d5d3..f6c01a30fde 100644 --- a/packages/deployment/ansible/cosmos-validators.yml +++ b/packages/deployment/ansible/cosmos-validators.yml @@ -1,6 +1,6 @@ --- -- hosts: "{{ service }}" +- hosts: validator user: root #any_errors_fatal: true gather_facts: yes @@ -10,7 +10,7 @@ - data: "{{ SETUP_HOME }}/{{ service }}/data" - CHAIN_NAME: "{{ lookup('file', SETUP_HOME + '/' + service + '/chain-name.txt') }}" - STAKER: ag-staker - - STAKER_NODE: node0 + - STAKER_NODE: validator0 - STAKER_AMOUNT: 50000000uagstake roles: - cosmos-validators diff --git a/packages/deployment/init.js b/packages/deployment/init.js index 896b82b75b9..331dbb09469 100644 --- a/packages/deployment/init.js +++ b/packages/deployment/init.js @@ -4,6 +4,8 @@ import { assert, details as X } from '@agoric/assert'; import { PLAYBOOK_WRAPPER, SSH_TYPE } from './setup'; import { shellEscape } from './run'; +export const AVAILABLE_ROLES = ['validator', 'peer', 'seed']; + const calculateTotal = placement => (placement ? Object.values(placement) : []).reduce( (prior, cur) => prior + cur, @@ -42,7 +44,6 @@ const tfStringify = obj => { return ret; }; -/** @param {Powers} arg0 */ const genericAskApiKey = ({ env, inquirer }) => async (provider, myDetails) => { const questions = [ { @@ -149,6 +150,7 @@ module "${PLACEMENT}" { CLUSTER_NAME = "${PREFIX}\${var.NETWORK_NAME}-${PLACEMENT}" OFFSET = "\${var.OFFSETS["${PLACEMENT}"]}" SSH_KEY_FILE = "\${var.SSH_KEY_FILE}" + ROLE = "\${var.ROLES["${PLACEMENT}"]}" SERVERS = "\${length(var.DATACENTERS["${PLACEMENT}"])}" VOLUMES = "\${var.VOLUMES["${PLACEMENT}"]}" } @@ -184,6 +186,7 @@ module "${PLACEMENT}" { CLUSTER_NAME = "${PREFIX}\${var.NETWORK_NAME}-${PLACEMENT}" OFFSET = "\${var.OFFSETS["${PLACEMENT}"]}" REGIONS = "\${var.DATACENTERS["${PLACEMENT}"]}" + ROLE = "\${var.ROLES["${PLACEMENT}"]}" SSH_KEY_FILE = "\${var.SSH_KEY_FILE}" DO_API_TOKEN = "\${var.API_KEYS["${PLACEMENT}"]}" SERVERS = "\${length(var.DATACENTERS["${PLACEMENT}"])}" @@ -193,7 +196,7 @@ module "${PLACEMENT}" { }, }); -const askPlacement = ({ inquirer }) => PLACEMENTS => { +const askPlacement = ({ inquirer }) => async (PLACEMENTS, ROLES) => { let total = 0; PLACEMENTS.forEach( ([_PLACEMENT, placement]) => (total += calculateTotal(placement)), @@ -211,13 +214,28 @@ const askPlacement = ({ inquirer }) => PLACEMENTS => { DONE, NEW, ...PLACEMENTS.map(([place, placement]) => ({ - name: `${place}${nodeCount(calculateTotal(placement))}`, + name: `${ROLES[place]} - ${place}${nodeCount( + calculateTotal(placement), + )}`, value: place, })), ], }, ]; - return inquirer.prompt(questions); + + const first = await inquirer.prompt(questions); + + const roleQuestions = [ + { + name: 'ROLE', + type: 'list', + message: `Role for the ${first.PLACEMENT} placement?`, + choices: AVAILABLE_ROLES, + }, + ]; + const second = first.PLACEMENT ? await inquirer.prompt(roleQuestions) : {}; + + return { ...first, ...second }; }; const askProvider = ({ inquirer }) => PROVIDERS => { @@ -280,23 +298,27 @@ const doInit = ({ env, rd, wr, running, setup, inquirer, fetch }) => async ( SSH_PRIVATE_KEY_FILE: `id_${SSH_TYPE}`, DETAILS: {}, OFFSETS: {}, + ROLES: {}, DATACENTERS: {}, PROVIDER_NEXT_INDEX: {}, }; config.NETWORK_NAME = overrideNetworkName; - let instance = 0; - // eslint-disable-next-line no-constant-condition while (true) { // eslint-disable-next-line no-await-in-loop - let { PLACEMENT } = await askPlacement({ inquirer })(config.PLACEMENTS); + const { ROLE, ...rest } = await askPlacement({ inquirer })( + config.PLACEMENTS, + config.ROLES, + ); + let { PLACEMENT } = rest; if (!PLACEMENT) { break; } let provider; let myDetails = {}; if (PLACEMENT !== 'NEW') { + config.ROLES[PLACEMENT] = ROLE; const PROVIDER = config.PLACEMENT_PROVIDER[PLACEMENT]; provider = PROVIDERS[PROVIDER]; } else { @@ -320,6 +342,7 @@ const doInit = ({ env, rd, wr, running, setup, inquirer, fetch }) => async ( } idx[PROVIDER] += 1; PLACEMENT = `${PROVIDER}${idx[PROVIDER]}`; + config.ROLES[PLACEMENT] = ROLE; config.PLACEMENT_PROVIDER[PLACEMENT] = PROVIDER; }; @@ -354,6 +377,7 @@ const doInit = ({ env, rd, wr, running, setup, inquirer, fetch }) => async ( provider.datacenters && // eslint-disable-next-line no-await-in-loop (await provider.datacenters(provider, PLACEMENT, myDetails)); + config.ROLES; const [_p, placement] = config.PLACEMENTS.find( ([p]) => p === PLACEMENT, ) || [PLACEMENT, {}]; @@ -412,7 +436,12 @@ const doInit = ({ env, rd, wr, running, setup, inquirer, fetch }) => async ( } // Collate the placement information. + const ROLE_INSTANCE = {}; + Object.values(config.ROLES).forEach(role => { + ROLE_INSTANCE[role] = 0; + }); for (const [PLACEMENT, placement] of config.PLACEMENTS) { + let instance = ROLE_INSTANCE[config.ROLES[PLACEMENT]]; const offset = instance; config.DATACENTERS[PLACEMENT] = []; for (const dc of Object.keys(placement).sort()) { @@ -433,10 +462,14 @@ const doInit = ({ env, rd, wr, running, setup, inquirer, fetch }) => async ( } // Commit the final details. + ROLE_INSTANCE[config.ROLES[PLACEMENT]] = instance; config.OFFSETS[PLACEMENT] = offset; } - assert(instance !== 0, X`Aborting due to no nodes configured!`); + assert( + Object.values(ROLE_INSTANCE).some(i => i > 0), + X`Aborting due to no nodes configured!`, + ); await wr.createFile( `vars.tf`, @@ -459,6 +492,10 @@ variable "OFFSETS" { default = ${tfStringify(config.OFFSETS)} } +variable "ROLES" { + default = ${tfStringify(config.ROLES)} +} + ${Object.keys(config.DETAILS) .sort() .map( @@ -473,7 +510,7 @@ variable ${JSON.stringify(vname)} { ); // Go and create the specific files. - const clusterPrefix = 'ag-chain-cosmos-'; + const clusterPrefix = 'ag-'; for (const PLACEMENT of Object.keys(config.PLACEMENT_PROVIDER).sort()) { const PROVIDER = config.PLACEMENT_PROVIDER[PLACEMENT]; const provider = PROVIDERS[PROVIDER]; @@ -493,6 +530,10 @@ ${Object.keys(config.DATACENTERS) } } +output "roles" { + value = "\${var.ROLES}" +} + output "offsets" { value = "\${var.OFFSETS}" } @@ -535,4 +576,4 @@ pipelining = True await wr.createFile(networkTxt, config.NETWORK_NAME); }; -export default doInit; +export { doInit }; diff --git a/packages/deployment/main.js b/packages/deployment/main.js index 873d1b1b57c..3338ce0d5bc 100644 --- a/packages/deployment/main.js +++ b/packages/deployment/main.js @@ -5,18 +5,30 @@ import { createHash } from 'crypto'; import chalk from 'chalk'; import parseArgs from 'minimist'; import { assert, details as X } from '@agoric/assert'; -import doInit from './init'; +import { doInit } from './init'; import { shellMetaRegexp, shellEscape } from './run'; import { streamFromString } from './files'; import { SSH_TYPE, DEFAULT_BOOT_TOKENS } from './setup'; const PROVISION_DIR = 'provision'; -const PROVISIONER_NODE = 'node0'; // FIXME: Allow configuration. const COSMOS_DIR = 'ag-chain-cosmos'; const DWEB_DIR = 'dweb'; const SECONDS_BETWEEN_BLOCKS = 5; const DEFAULT_SWINGSET_PROMETHEUS_PORT = 9464; +// We now use Cloudflare, not dweb. +const ALL_NODES_DWEB = false; + +const isPublicRpc = (roles, cluster) => { + if (Object.values(roles).includes('peer')) { + // We have some marked as "peer", so only advertise them. + return roles[cluster] === 'peer'; + } + // Advertise all validators. + return roles[cluster] === 'validator'; +}; +const isPersistentPeer = isPublicRpc; + const makeGuardFile = ({ rd, wr }) => async (file, maker) => { if (await rd.exists(file)) { return 0; @@ -55,7 +67,7 @@ const waitForStatus = ({ setup, running }) => async ( await doRetry(retryNum); let buf = ''; // eslint-disable-next-line no-await-in-loop - const code = await running.needDoRun( + const code = await running.doRun( setup.playbook('status', `-euser=${user}`, ...hostArgs, ...serviceArgs), undefined, chunk => { @@ -368,6 +380,10 @@ show-config display the client connection parameters const files = await rd.readdir(`${COSMOS_DIR}/data`); const dsts = files.filter(fname => fname.endsWith('.dst')); const peers = await needBacktick(`${shellEscape(progname)} show-peers`); + const seeds = await needBacktick(`${shellEscape(progname)} show-seeds`); + const allIds = await needBacktick( + `${shellEscape(progname)} show-all-ids`, + ); await Promise.all( dsts.map(async (dst, i) => { // Update the config.toml and genesis.json. @@ -376,6 +392,8 @@ show-config display the client connection parameters `set-defaults`, `ag-chain-cosmos`, `--persistent-peers=${peers}`, + `--seeds=${seeds}`, + `--unconditional-peer-ids=${allIds}`, ...importFlags, `${COSMOS_DIR}/data/${dst}`, ]); @@ -543,12 +561,13 @@ ${chalk.yellow.bold(`ag-setup-solo --netconfig='${dwebHost}/network-config'`)} setSilent(true); await chdir(setup.SETUP_HOME); await inited(); - const [chainName, gci, peers, rpcAddrs] = await Promise.all( + const [chainName, gci, peers, rpcAddrs, seeds] = await Promise.all( [ 'show-chain-name', 'show-gci', 'show-peers', 'show-rpcaddrs', + 'show-seeds', ].map(subcmd => needBacktick([progname, subcmd].map(shellEscape).join(' ')), ), @@ -558,6 +577,7 @@ ${chalk.yellow.bold(`ag-setup-solo --netconfig='${dwebHost}/network-config'`)} gci, peers: peers.split(','), rpcAddrs: rpcAddrs.split(','), + seeds: seeds.split(','), }; stdout.write(`${JSON.stringify(obj, undefined, 2)}\n`); break; @@ -572,20 +592,18 @@ ${chalk.yellow.bold(`ag-setup-solo --netconfig='${dwebHost}/network-config'`)} } // Expand the hosts into nodes. - const nodeMap = {}; + const nodeSet = new Set(); for (const host of hosts) { const hostLines = await needBacktick( `ansible --list-hosts ${shellEscape(host)}`, ); - for (const line of hostLines.split('\n')) { - const match = line.match(/^\s*(node\d+)/); - if (match) { - nodeMap[match[1]] = true; - } - } + hostLines + .split('\n') + .slice(1) + .forEach(h => nodeSet.add(h.trim())); } - const nodes = Object.keys(nodeMap).sort(); + const nodes = [...nodeSet.keys()].sort(); assert(nodes.length > 0, X`Need at least one node`); for (const node of nodes) { @@ -602,7 +620,7 @@ ${chalk.yellow.bold(`ag-setup-solo --netconfig='${dwebHost}/network-config'`)} await inited(); if (!host) { - host = 'ag-chain-cosmos'; + host = 'validator'; } // Detect when blocks are being produced. @@ -615,7 +633,11 @@ ${chalk.yellow.bold(`ag-setup-solo --netconfig='${dwebHost}/network-config'`)} SECONDS_BETWEEN_BLOCKS + 1, `to check if ${chalk.underline(host)} has committed a block`, ), - buf => { + (buf, code) => { + if (buf === '' && code === 1) { + // No status information. Probably an empty inventory label. + return true; + } const match = buf.match( /( block-manager: block ([1-9]\d*) commit|Committed state.*module=state.*height=([1-9]\d*))/, ); @@ -653,6 +675,10 @@ ${chalk.yellow.bold(`ag-setup-solo --netconfig='${dwebHost}/network-config'`)} let rpcaddrs = ''; let sep = ''; for (const CLUSTER of Object.keys(prov.public_ips.value)) { + if (!isPublicRpc(prov.roles.value, CLUSTER)) { + // eslint-disable-next-line no-continue + continue; + } const ips = prov.public_ips.value[CLUSTER]; const PORT = 26657; for (const IP of ips) { @@ -665,32 +691,55 @@ ${chalk.yellow.bold(`ag-setup-solo --netconfig='${dwebHost}/network-config'`)} break; } - case 'show-peers': { + case 'show-seeds': + case 'show-peers': + case 'show-all-ids': { await inited(); + const prov = await provisionOutput({ rd, wr, running }); const publicIps = []; const publicPorts = []; + const hosts = []; + let selector; + switch (cmd) { + case 'show-seeds': { + selector = placement => prov.roles.value[placement] === 'seed'; + break; + } + case 'show-peers': { + selector = placement => isPersistentPeer(prov.roles.value, placement); + break; + } + case 'show-all-ids': { + selector = _placement => true; + break; + } + default: { + assert.fail(X`Unrecognized show command ${cmd}`); + } + } + for (const CLUSTER of Object.keys(prov.public_ips.value)) { + if (!selector(CLUSTER)) { + // eslint-disable-next-line no-continue + continue; + } const ips = prov.public_ips.value[CLUSTER]; const offset = Number(prov.offsets.value[CLUSTER]); for (let i = 0; i < ips.length; i += 1) { - publicIps[offset + i] = ips[i]; + publicIps.push(ips[i]); + hosts.push(`${prov.roles.value[CLUSTER]}${offset + i}`); } } const DEFAULT_PORT = 26656; - let peers = ''; + let ret = ''; let sep = ''; - let idPath; - let i = 0; - // eslint-disable-next-line no-constant-condition - while (true) { + for (let i = 0; i < hosts.length; i += 1) { // Read the node-id file for this node. - idPath = `${COSMOS_DIR}/data/node${i}/node-id`; - if (!(await rd.exists(idPath))) { - break; - } + const host = hosts[i]; + const idPath = `${COSMOS_DIR}/data/${host}/node-id`; const raw = await trimReadFile(idPath); const ID = String(raw); @@ -700,15 +749,17 @@ ${chalk.yellow.bold(`ag-setup-solo --netconfig='${dwebHost}/network-config'`)} ID.match(/^[a-f0-9]+/), X`${idPath} contains an invalid ID ${ID}`, ); - const IP = publicIps[i]; - assert(IP, X`${idPath} does not correspond to a Terraform public IP`); - const PORT = publicPorts[i] || DEFAULT_PORT; - peers += `${sep}${ID}@${IP}:${PORT}`; + if (cmd.endsWith('-ids')) { + ret += `${sep}${ID}`; + } else { + const IP = publicIps[i]; + assert(IP, X`${idPath} does not correspond to a Terraform public IP`); + const PORT = publicPorts[i] || DEFAULT_PORT; + ret += `${sep}${ID}@${IP}:${PORT}`; + } sep = ','; - i += 1; } - assert(i !== 0, X`No ${idPath} file found`); - stdout.write(peers); + stdout.write(ret); break; } @@ -801,39 +852,46 @@ ${name}: const addAll = makeGroup('all'); const addChainCosmos = makeGroup('ag-chain-cosmos', 4); const addDweb = makeGroup('dweb', 4); + const addRole = {}; for (const provider of Object.keys(prov.public_ips.value).sort()) { const addProvider = makeGroup(provider, 4); const ips = prov.public_ips.value[provider]; const offset = Number(prov.offsets.value[provider]); + const role = prov.roles.value[provider]; + if (!addRole[role]) { + addRole[role] = makeGroup(role, 4); + } for (let instance = 0; instance < ips.length; instance += 1) { const ip = ips[instance]; - const node = `node${offset + instance}`; - const units = - node === PROVISIONER_NODE - ? `\ - units: - - ag-chain-cosmos.service -` - : ''; - const host = `\ -${node}: + const node = `${role}${offset + instance}`; + let roleParams = ''; + if (role === 'validator') { + // These are the validator params. + roleParams = ` moniker: Agoric${offset + instance} website: https://testnet.agoric.com - identity: https://keybase.io/team/agoric.testnet.validators + identity: https://keybase.io/team/agoric.testnet.validators`; + } + const host = `\ +${node}:${roleParams} ansible_host: ${ip} ansible_ssh_user: root ansible_ssh_private_key_file: '${SSH_PRIVATE_KEY_FILE}' - ansible_python_interpreter: /usr/bin/python -${units}`; + ansible_python_interpreter: /usr/bin/python`; addProvider(host); addAll(host); - // We add all the nodes to ag-chain-cosmos. + // We add all the cosmos nodes to ag-chain-cosmos. addChainCosmos(host); - // Install the decentralised web on all the hosts. - addDweb(host); + if (ALL_NODES_DWEB) { + // Install the decentralised web on all the hosts. + addDweb(host); + } + + // Add the role-specific group. + addRole[role](host); } } out.write(byGroup.all); diff --git a/packages/deployment/terraform/digitalocean/cluster/main.tf b/packages/deployment/terraform/digitalocean/cluster/main.tf index dac026f8ab3..d7f5c85aeef 100644 --- a/packages/deployment/terraform/digitalocean/cluster/main.tf +++ b/packages/deployment/terraform/digitalocean/cluster/main.tf @@ -8,7 +8,7 @@ resource "digitalocean_ssh_key" "cluster" { } resource "digitalocean_droplet" "cluster" { - name = "${var.name}-node${var.offset + count.index}" + name = "${var.name}-${var.role}${var.offset + count.index}" image = "debian-10-x64" size = "${var.instance_size}" region = "${element(var.regions, count.index)}" diff --git a/packages/deployment/terraform/digitalocean/cluster/variables.tf b/packages/deployment/terraform/digitalocean/cluster/variables.tf index 4c54281fe49..df6784a4082 100644 --- a/packages/deployment/terraform/digitalocean/cluster/variables.tf +++ b/packages/deployment/terraform/digitalocean/cluster/variables.tf @@ -8,6 +8,11 @@ variable "regions" { default = ["AMS3", "FRA1", "LON1", "NYC3", "SFO2", "SGP1", "TOR1"] } +variable "role" { + description = "Role of this cluster" + default = "node" +} + variable "offset" { description = "Offset of node id" default = 0 @@ -20,7 +25,7 @@ variable "ssh_key" { variable "instance_size" { description = "The instance size to use" - default = "s-2vcpu-4gb" + default = "s-8vcpu-16gb" } variable "volume_size" { diff --git a/packages/deployment/terraform/digitalocean/main.tf b/packages/deployment/terraform/digitalocean/main.tf index caf020ae9e9..f16b9dc124f 100644 --- a/packages/deployment/terraform/digitalocean/main.tf +++ b/packages/deployment/terraform/digitalocean/main.tf @@ -14,6 +14,11 @@ variable "SSH_KEY_FILE" { type = "string" } +variable "ROLE" { + description = "Role of this cluster" + default = "node" +} + variable "SERVERS" { description = "Number of nodes in cluster" default = "5" @@ -40,6 +45,7 @@ module "cluster" { name = "${var.CLUSTER_NAME}" offset = "${var.OFFSET}" regions = "${var.REGIONS}" + role = "${var.ROLE}" ssh_key = "${var.SSH_KEY_FILE}" servers = "${var.SERVERS}" } diff --git a/packages/deployment/terraform/docker/cluster/main.tf b/packages/deployment/terraform/docker/cluster/main.tf index 25900eeaaba..6c6e8dcc40a 100644 --- a/packages/deployment/terraform/docker/cluster/main.tf +++ b/packages/deployment/terraform/docker/cluster/main.tf @@ -1,5 +1,5 @@ resource "docker_container" "cluster" { - name = "${var.name}-node${var.offset + count.index}" + name = "${var.name}-${var.role}${var.offset + count.index}" count = "${var.servers}" image = "agoric/deployment:latest" diff --git a/packages/deployment/terraform/docker/cluster/variables.tf b/packages/deployment/terraform/docker/cluster/variables.tf index 164d1eab99c..d365d2d63ef 100644 --- a/packages/deployment/terraform/docker/cluster/variables.tf +++ b/packages/deployment/terraform/docker/cluster/variables.tf @@ -7,6 +7,12 @@ variable "offset" { default = 0 } +variable "role" { + description = "Role of the nodes in this cluster" + type = "string" + default = "node" +} + variable "ssh_key" { description = "SSH key filename to copy to the nodes" type = "string" diff --git a/packages/deployment/terraform/docker/main.tf b/packages/deployment/terraform/docker/main.tf index c9f54dcf3f3..7e674856954 100644 --- a/packages/deployment/terraform/docker/main.tf +++ b/packages/deployment/terraform/docker/main.tf @@ -13,6 +13,11 @@ variable "SSH_KEY_FILE" { type = "string" } +variable "ROLE" { + description = "Role of this cluster" + default = "node" +} + variable "SERVERS" { description = "Number of nodes in cluster" default = "5" @@ -42,6 +47,7 @@ module "cluster" { name = "${var.CLUSTER_NAME}" offset = "${var.OFFSET}" ssh_key = "${var.SSH_KEY_FILE}" + role = "${var.ROLE}" servers = "${var.SERVERS}" volumes = "${var.VOLUMES}" }