UNPKG

@maticnetwork/matic-cli

Version:

Testing toolkit to setup, manage and operate Polygon networks

436 lines (357 loc) 15.4 kB
// noinspection JSCheckFunctionSignatures,JSUnresolvedFunction,JSUnresolvedVariable import { validateConfigs, editMaticCliDockerYAMLConfig, editMaticCliRemoteYAMLConfig, getDevnetId, splitAndGetHostIp, splitToArray } from '../common/config-utils' import { maxRetries, runScpCommand, runSshCommand } from '../common/remote-worker' import { timer } from '../common/time-utils' import yaml from 'js-yaml' import fs from 'fs' const shell = require('shelljs') async function terraformApply(devnetId) { console.log('📍Executing terraform apply...') shell.exec( `terraform -chdir=../../deployments/devnet-${devnetId} apply -auto-approve -var-file=./secret.tfvars`, { env: { ...process.env } } ) } async function terraformOutput() { console.log('📍Executing terraform output...') const { stdout } = shell.exec('terraform output --json', { env: { ...process.env } }) return stdout } async function installRequiredSoftwareOnRemoteMachines( ips, devnetType, devnetId ) { const doc = await yaml.load( fs.readFileSync( `../../deployments/devnet-${devnetId}/${devnetType}-setup-config.yaml`, 'utf8' ) ) const ipsArray = splitToArray(ips) const borUsers = splitToArray(doc.devnetBorUsers.toString()) let user, ip const nodeIps = [] const isHostMap = new Map() for (let i = 0; i < ipsArray.length; i++) { i === 0 ? (user = `${doc.ethHostUser}`) : (user = `${borUsers[i]}`) ip = `${user}@${ipsArray[i]}` nodeIps.push(ip) i === 0 ? isHostMap.set(ip, true) : isHostMap.set(ip, false) } const requirementTasks = nodeIps.map(async (ip) => { user = splitAndGetHostIp(ip) await configureCertAndPermissions(user, ip) await installCommonPackages(ip) if (isHostMap.get(ip)) { // Install Host dependencies await installHostSpecificPackages(ip) if (process.env.TF_VAR_DOCKERIZED === 'yes') { await installDocker(ip, user) } } }) await Promise.all(requirementTasks) } async function configureCertAndPermissions(user, ip) { console.log('📍Allowing user not to use password...') let command = `echo "${user} ALL=(ALL) NOPASSWD:ALL" | sudo tee -a /etc/sudoers` await runSshCommand(ip, command, maxRetries) console.log('📍Give permissions to all users for root folder...') command = 'sudo chmod 755 -R ~/' await runSshCommand(ip, command, maxRetries) console.log('📍Copying certificate to ' + ip + ':~/cert.pem...') const src = `${process.env.PEM_FILE_PATH}` const dest = `${ip}:~/cert.pem` await runScpCommand(src, dest, maxRetries) console.log('📍Adding ssh for ' + ip + ':~/cert.pem...') command = 'sudo chmod 700 ~/cert.pem && eval "$(ssh-agent -s)" && ssh-add ~/cert.pem && sudo chmod -R 700 ~/.ssh' await runSshCommand(ip, command, maxRetries) } async function installCommonPackages(ip) { console.log('📍Installing required software on remote machine ' + ip + '...') console.log('📍Running apt update...') let command = 'sudo apt update -y' await runSshCommand(ip, command, maxRetries) console.log('📍Installing build-essential...') command = 'sudo apt install build-essential -y' await runSshCommand(ip, command, maxRetries) console.log('📍Installing go...') command = `wget -nc https://raw.githubusercontent.com/maticnetwork/node-ansible/master/go-install.sh && bash go-install.sh --remove && bash go-install.sh && source ~/.bashrc` await runSshCommand(ip, command, maxRetries) console.log('📍Creating symlink for go...') command = 'sudo ln -sf ~/.go/bin/go /usr/local/bin/go' await runSshCommand(ip, command, maxRetries) console.log('📍Installing rabbitmq...') command = 'sudo apt install rabbitmq-server -y' await runSshCommand(ip, command, maxRetries) } async function installHostSpecificPackages(ip) { console.log('📍Installing nvm...') let command = `curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash && export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \\. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/bash_completion" ] && \\. "$NVM_DIR/bash_completion" && nvm install 16.17.1` await runSshCommand(ip, command, maxRetries) console.log('📍Installing solc...') command = 'sudo snap install solc' await runSshCommand(ip, command, maxRetries) console.log('📍Installing python2...') command = 'sudo apt install python2 -y && alias python="/usr/bin/python2"' await runSshCommand(ip, command, maxRetries) console.log('📍Installing nodejs and npm...') command = 'sudo apt install nodejs npm -y' await runSshCommand(ip, command, maxRetries) console.log('📍Creating symlink for npm and node...') command = `sudo ln -sf ~/.nvm/versions/node/v16.17.1/bin/npm /usr/bin/npm && sudo ln -sf ~/.nvm/versions/node/v16.17.1/bin/node /usr/bin/node && sudo ln -sf ~/.nvm/versions/node/v16.17.1/bin/npx /usr/bin/npx` await runSshCommand(ip, command, maxRetries) console.log('📍Installing ganache...') command = 'sudo npm install -g ganache -y' await runSshCommand(ip, command, maxRetries) } export async function installDocker(ip, user) { console.log('📍Setting docker repository up...') let command = 'sudo apt-get update -y && sudo apt install apt-transport-https ca-certificates curl software-properties-common -y' await runSshCommand(ip, command, maxRetries) console.log('📍Installing docker...') command = 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -' await runSshCommand(ip, command, maxRetries) command = 'sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"' await runSshCommand(ip, command, maxRetries) command = 'sudo apt install docker-ce docker-ce-cli containerd.io -y' await runSshCommand(ip, command, maxRetries) command = 'sudo apt install docker-compose-plugin -y' await runSshCommand(ip, command, maxRetries) console.log('📍Adding user to docker group...') command = `sudo usermod -aG docker ${user}` await runSshCommand(ip, command, maxRetries) } async function prepareMaticCLI(ips, devnetType, devnetId) { const doc = await yaml.load( fs.readFileSync( `../../deployments/devnet-${devnetId}/${devnetType}-setup-config.yaml`, 'utf8' ) ) const ipsArray = splitToArray(ips) const ip = `${doc.ethHostUser}@${ipsArray[0]}` const maticCliRepo = process.env.MATIC_CLI_REPO const maticCliBranch = process.env.MATIC_CLI_BRANCH console.log('📍Git clone ' + maticCliRepo + ' if does not exist on ' + ip) let command = `cd ~ && git clone ${maticCliRepo} || (cd ~/matic-cli; git fetch)` await runSshCommand(ip, command, maxRetries) console.log( '📍Git checkout ' + maticCliBranch + ' and git pull on machine ' + ip ) command = `cd ~/matic-cli && git checkout ${maticCliBranch} && git pull || (cd ~/matic-cli && git stash && git stash drop && git pull)` await runSshCommand(ip, command, maxRetries) console.log('📍Installing matic-cli dependencies...') command = 'cd ~/matic-cli && npm i' await runSshCommand(ip, command, maxRetries) } async function eventuallyCleanupPreviousDevnet(ips, devnetType, devnetId) { const doc = await yaml.load( fs.readFileSync( `../../deployments/devnet-${devnetId}/${devnetType}-setup-config.yaml`, 'utf8' ) ) const ipsArray = splitToArray(ips) const borUsers = splitToArray(doc.devnetBorUsers.toString()) let user, ip const nodeIps = [] const isHostMap = new Map() for (let i = 0; i < ipsArray.length; i++) { i === 0 ? (user = `${doc.ethHostUser}`) : (user = `${borUsers[i]}`) ip = `${user}@${ipsArray[i]}` nodeIps.push(ip) i === 0 ? isHostMap.set(ip, true) : isHostMap.set(ip, false) } const cleanupTasks = nodeIps.map(async (ip) => { if (isHostMap.get(ip)) { // Cleanup Host console.log( '📍Removing old devnet (if present) on machine ' + ip + ' ...' ) let command = 'rm -rf ~/matic-cli/devnet' await runSshCommand(ip, command, maxRetries) console.log('📍Stopping ganache (if present) on machine ' + ip + ' ...') command = "sudo systemctl stop ganache.service || echo 'ganache not running on current machine...'" await runSshCommand(ip, command, maxRetries) } console.log('📍Stopping heimdall (if present) on machine ' + ip + ' ...') let command = "sudo systemctl stop heimdalld.service || echo 'heimdall not running on current machine...'" await runSshCommand(ip, command, maxRetries) console.log('📍Stopping bor (if present) on machine ' + ip + ' ...') command = "sudo systemctl stop bor.service || echo 'bor not running on current machine...'" await runSshCommand(ip, command, maxRetries) console.log('📍Removing .bor folder (if present) on machine ' + ip + ' ...') command = 'rm -rf ~/.bor' await runSshCommand(ip, command, maxRetries) console.log( '📍Removing .heimdalld folder (if present) on machine ' + ip + ' ...' ) command = 'rm -rf ~/.heimdalld' await runSshCommand(ip, command, maxRetries) console.log('📍Removing data folder (if present) on machine ' + ip + ' ...') command = 'rm -rf ~/data' await runSshCommand(ip, command, maxRetries) console.log('📍Removing node folder (if present) on machine ' + ip + ' ...') command = 'rm -rf ~/node' await runSshCommand(ip, command, maxRetries) }) await Promise.all(cleanupTasks) } async function runDockerSetupWithMaticCLI(ips, devnetId) { const doc = await yaml.load( fs.readFileSync( `../../deployments/devnet-${devnetId}/docker-setup-config.yaml`, 'utf8' ) ) const ipsArray = splitToArray(ips) const ip = `${doc.ethHostUser}@${ipsArray[0]}` console.log('📍Creating devnet and removing default configs...') let command = 'cd ~/matic-cli && mkdir -p devnet && rm configs/devnet/docker-setup-config.yaml' await runSshCommand(ip, command, maxRetries) console.log('📍Copying docker matic-cli configurations...') const src = `../../deployments/devnet-${devnetId}/docker-setup-config.yaml` const dest = `${doc.ethHostUser}@${ipsArray[0]}:~/matic-cli/configs/devnet/docker-setup-config.yaml` await runScpCommand(src, dest, maxRetries) console.log('📍Executing docker setup with matic-cli...') command = 'cd ~/matic-cli/devnet && ../bin/matic-cli setup devnet -c ../configs/devnet/docker-setup-config.yaml' await runSshCommand(ip, command, maxRetries) console.log('📍Starting ganache...') command = 'cd ~/matic-cli/devnet && bash docker-ganache-start.sh' await runSshCommand(ip, command, maxRetries) console.log('📍Starting heimdall...') command = 'cd ~/matic-cli/devnet && bash docker-heimdall-start-all.sh' await runSshCommand(ip, command, maxRetries) console.log('📍Setting up bor...') command = 'cd ~/matic-cli/devnet && bash docker-bor-setup.sh' await runSshCommand(ip, command, maxRetries) console.log('📍Starting bor...') command = 'cd ~/matic-cli/devnet && bash docker-bor-start-all.sh' await runSshCommand(ip, command, maxRetries) await timer(60000) console.log('📍Deploying contracts for bor...') command = 'cd ~/matic-cli/devnet && bash ganache-deployment-bor.sh' await runSshCommand(ip, command, maxRetries) await timer(60000) console.log('📍Deploying state-sync contracts...') command = 'cd ~/matic-cli/devnet && bash ganache-deployment-sync.sh' await runSshCommand(ip, command, maxRetries) await timer(60000) console.log('📍Executing bor ipc tests...') console.log('📍1. Fetching admin.peers...') command = 'cd ~/matic-cli/devnet && docker exec bor0 bash -c "bor attach /root/.bor/data/bor.ipc -exec \'admin.peers\'"' await runSshCommand(ip, command, maxRetries) console.log('📍2. Fetching eth.blockNumber...') command = 'cd ~/matic-cli/devnet && docker exec bor0 bash -c "bor attach /root/.bor/data/bor.ipc -exec \'eth.blockNumber\'"' await runSshCommand(ip, command, maxRetries) console.log('📍bor ipc tests executed...') } async function runRemoteSetupWithMaticCLI(ips, devnetId) { const doc = await yaml.load( fs.readFileSync( `../../deployments/devnet-${devnetId}/remote-setup-config.yaml`, 'utf8' ) ) const ipsArray = splitToArray(ips) const ip = `${doc.ethHostUser}@${ipsArray[0]}` console.log('📍Creating devnet and removing default configs...') let command = 'cd ~/matic-cli && mkdir -p devnet && rm configs/devnet/remote-setup-config.yaml' await runSshCommand(ip, command, maxRetries) console.log('📍Copying remote matic-cli configurations...') const src = `../../deployments/devnet-${devnetId}/remote-setup-config.yaml` const dest = `${doc.ethHostUser}@${ipsArray[0]}:~/matic-cli/configs/devnet/remote-setup-config.yaml` await runScpCommand(src, dest, maxRetries) console.log('📍Executing remote setup with matic-cli...') command = 'cd ~/matic-cli/devnet && ../bin/matic-cli setup devnet -c ../configs/devnet/remote-setup-config.yaml' await runSshCommand(ip, command, maxRetries) console.log('📍Deploying contracts for bor on machine ' + ip + ' ...') await timer(60000) command = 'cd ~/matic-cli/devnet && bash ganache-deployment-bor.sh' await runSshCommand(ip, command, maxRetries) console.log('📍Deploying state-sync contracts on machine ' + ip + ' ...') await timer(60000) command = 'cd ~/matic-cli/devnet && bash ganache-deployment-sync.sh' await runSshCommand(ip, command, maxRetries) } export async function start() { const devnetId = getDevnetId() require('dotenv').config({ path: `${process.cwd()}/.env` }) shell.exec(`terraform workspace select devnet-${devnetId}`) const devnetType = process.env.TF_VAR_DOCKERIZED === 'yes' ? 'docker' : 'remote' await terraformApply(devnetId) const tfOutput = await terraformOutput() const dnsIps = JSON.parse(tfOutput).instance_dns_ips.value.toString() const ids = JSON.parse(tfOutput).instance_ids.value.toString() process.env.DEVNET_BOR_HOSTS = dnsIps process.env.INSTANCES_IDS = ids await validateConfigs() shell.exec( `cp ../../configs/devnet/${devnetType}-setup-config.yaml ../../deployments/devnet-${devnetId}` ) shell.exec( `cp ../../configs/devnet/openmetrics-conf.yaml ../../deployments/devnet-${devnetId}` ) shell.exec( `cp ../../configs/devnet/otel-config-dd.yaml ../../deployments/devnet-${devnetId}` ) if (devnetType === 'docker') { await editMaticCliDockerYAMLConfig() } else { await editMaticCliRemoteYAMLConfig() } console.log('📍Waiting 30s for the VMs to initialize...') await timer(30000) await installRequiredSoftwareOnRemoteMachines(dnsIps, devnetType, devnetId) await prepareMaticCLI(dnsIps, devnetType, devnetId) await eventuallyCleanupPreviousDevnet(dnsIps, devnetType, devnetId) if (devnetType === 'docker') { await runDockerSetupWithMaticCLI(dnsIps, devnetId) } else { await runRemoteSetupWithMaticCLI(dnsIps, devnetId) } }