Skip to content

Commit

Permalink
Broker/Server separate configs (#166)
Browse files Browse the repository at this point in the history
Add automatic recovery for api/dual node
Merged both start scripts.
Replaced mustache for basic script that could be used when not running compose.

Co-authored-by: wayonb <[email protected]>
  • Loading branch information
fboucquez and Wayonb authored Mar 2, 2021
1 parent 89e0931 commit 35a2f97
Show file tree
Hide file tree
Showing 33 changed files with 272 additions and 180 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ The changelog format is based on [Keep a Changelog](https://keepachangelog.com/e
- Public keys can be used in custom presets in addition to encrypted private keys. If public keys are used, Bootstrap will prompt for the private keys when required.
- Added `encrypt` and `decrypt` commands to encrypt custom presets and decrypt generated `target/addresses.yml` files:
- The `--upgrade` param can be used to change the server keys without dropping the data.
- Splitting `userconfig` into `server-config` and `broker-config` for each service.
- Fixed recovery process.

## [0.4.4] - Feb-24-2021

Expand Down
16 changes: 0 additions & 16 deletions config/docker/server/runServerRecover.sh.mustache

This file was deleted.

125 changes: 125 additions & 0 deletions config/docker/server/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/bin/bash
set -e

function try()
{
[[ $- = *e* ]]; SAVED_OPT_E=$?
set +e
}

function throw()
{
exit $1
}

function catch()
{
export ex_code=$?
(( $SAVED_OPT_E )) && set +e
return $ex_code
}

catapultAppFolder=$1
dataDirectory=$2
application=$3
theOtherApplication=$4
mode=$5
name=$6
waitForOther=$7
config="./$application-config"

echo "RUNNING $application $name $mode"
export LD_LIBRARY_PATH=$catapultAppFolder/lib:$catapultAppFolder/deps

ulimit -c unlimited

rm -f "$dataDirectory/$application.started"
rm -f "$dataDirectory/$application-recovery.started"

if [ "$mode" == "DEBUG" ]; then
echo "Setting up core dump..."
mkdir -p ./logs
echo "./logs/$application.%e.%t" >/proc/sys/kernel/core_pattern
fi

otherApplicationRecoveryFile="$dataDirectory/$theOtherApplication-recovery.started"
while [ -f $otherApplicationRecoveryFile ] ;
do
echo "Waiting for $theOtherApplication recovery to finish"
sleep 1
done


if [ -e "$dataDirectory/$application.lock" ]; then
echo "!!!! Have lock file present, going to run recovery in $application mode...."

touch "$dataDirectory/$application-recovery.started"

while [ -f "$dataDirectory/$theOtherApplication.started" ] ;
do
echo "Waiting for $theOtherApplication to exit"
sleep 1
done

try
(
set -e
$catapultAppFolder/bin/catapult.recovery "$config"
echo "!!!! Finished running recovery, should be moving on to start $application..."
)
# directly after closing the subshell you need to connect a group to the catch using ||
catch || {
echo "!!!! $application recovery has CRASHED!"
rm -f "$dataDirectory/$application-recovery.started"
throw $ex_code # you can rethrow the "exception" causing the script to exit if not caught
}
fi

rm -f "$dataDirectory/$application-recovery.started"

if [ "$waitForOther" == "true" ]; then
while [ ! -f "$dataDirectory/$theOtherApplication.started" ] ;
do
echo "Waiting for $theOtherApplication to start"
sleep 1
done
fi

touch "$dataDirectory/$application.started"

processName="catapult.$application"
echo "!!!! Starting $application...."
$catapultAppFolder/bin/$processName "$config" &
export applicationPid=$!

cleanup() {
echo "Shutting down $application"
kill -s SIGTERM $applicationPid
wait $applicationPid
exitStatus=$?
rm -f "./data/$application.started"
exit $exitStatus
}

#Trap SIGTERM
trap 'cleanup' SIGTERM SIGINT

while sleep 5; do
ps aux |grep $processName |grep -q -v grep
processStatus=$?
if [ $processStatus -ne 0 ]; then
echo "The process $processName has already exited."
wait $applicationPid
exitStatus=$?
rm -f "./data/$application.started"
exit $exitStatus
fi

if [ -e $otherApplicationRecoveryFile ]; then
echo "Other process waiting for recovery. Shutting down $application"
kill -s SIGTERM $applicationPid
wait $applicationPid
rm -f "./data/$application.started"
exit 1
fi
done
19 changes: 0 additions & 19 deletions config/docker/server/startBroker.sh.mustache

This file was deleted.

17 changes: 0 additions & 17 deletions config/docker/server/startServer.sh.mustache

This file was deleted.

6 changes: 0 additions & 6 deletions config/docker/server/wait.sh

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[extensions]

# addressextraction must be first because mongo and zeromq depend on extracted addresses
extension.addressextraction = {{addressextraction}}
extension.mongo = {{mongo}}
extension.zeromq = {{zeromq}}
extension.addressextraction = {{addressextractionRecovery}}
extension.mongo = {{mongoRecovery}}
extension.zeromq = {{zeromqRecovery}}

extension.hashcache = true
extension.filespooling = {{filespoolingRecovery}}
extension.hashcache = {{hashcacheRecovery}}
1 change: 1 addition & 0 deletions src/model/ConfigPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ export interface ConfigPreset {
sinkAddress?: string;
epochAdjustment: string;
catapultAppFolder: string;
dataDirectory: string;
subnet?: string;
transactionsDirectory: string;
faucetUrl?: string;
Expand Down
27 changes: 11 additions & 16 deletions src/service/ComposeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,21 +193,17 @@ export class ComposeService {
(presetData.nodes || [])
.filter((d) => !d.excludeDockerService)
.map(async (n) => {
const debugFlag = ' DEBUG';
const serverDebugMode = presetData.dockerComposeDebugMode || n.dockerComposeDebugMode ? debugFlag : '';
const brokerDebugMode = presetData.dockerComposeDebugMode || n.brokerDockerComposeDebugMode ? debugFlag : '';
const waitForBroker = `/bin/bash ${nodeCommandsDirectory}/wait.sh ./data/broker.started`;
const recoverServerCommand = `/bin/bash ${nodeCommandsDirectory}/runServerRecover.sh ${n.name}`;
const serverCommand = `/bin/bash ${nodeCommandsDirectory}/startServer.sh ${n.name}${serverDebugMode}`;
const brokerCommand = `/bin/bash ${nodeCommandsDirectory}/startBroker.sh ${n.brokerName || 'broker'}${brokerDebugMode}`;
const recoverBrokerCommand = `/bin/bash ${nodeCommandsDirectory}/runServerRecover.sh ${n.brokerName || ''}`;
const debugFlag = 'DEBUG';
const serverDebugMode = presetData.dockerComposeDebugMode || n.dockerComposeDebugMode ? debugFlag : 'NORMAL';
const brokerDebugMode = presetData.dockerComposeDebugMode || n.brokerDockerComposeDebugMode ? debugFlag : 'NORMAL';
const serverCommand = `/bin/bash ${nodeCommandsDirectory}/start.sh ${presetData.catapultAppFolder} ${
presetData.dataDirectory
} server broker ${n.name} ${serverDebugMode} ${!!n.brokerName}`;
const brokerCommand = `/bin/bash ${nodeCommandsDirectory}/start.sh ${presetData.catapultAppFolder} ${
presetData.dataDirectory
} broker server ${n.brokerName || 'broker'} ${brokerDebugMode}`;
const portConfigurations = [{ internalPort: 7900, openPort: n.openPort }];

const serverServiceCommands = n.brokerName ? [waitForBroker, serverCommand] : [recoverServerCommand, serverCommand];

const serverServiceCommand = `bash -c "${serverServiceCommands.join(' && ')}"`;
const brokerServiceCommand = `bash -c "${[recoverBrokerCommand, brokerCommand].join(' && ')}"`;

const serverDependsOn: string[] = [];
const brokerDependsOn: string[] = [];

Expand All @@ -218,7 +214,6 @@ export class ComposeService {
if (n.brokerName) {
serverDependsOn.push(n.brokerName);
}

const volumes = [
vol(`../${targetNodesFolder}/${n.name}`, nodeWorkingDirectory, false),
vol(`./server`, nodeCommandsDirectory, true),
Expand All @@ -227,7 +222,7 @@ export class ComposeService {
user: serverDebugMode === debugFlag ? undefined : user, // if debug on, run as root
container_name: n.name,
image: presetData.symbolServerImage,
command: serverServiceCommand,
command: serverCommand,
stop_signal: 'SIGINT',
working_dir: nodeWorkingDirectory,
restart: restart,
Expand All @@ -253,7 +248,7 @@ export class ComposeService {
container_name: n.brokerName,
image: nodeService.image,
working_dir: nodeWorkingDirectory,
command: brokerServiceCommand,
command: brokerCommand,
ports: resolvePorts([{ internalPort: 7902, openPort: n.brokerOpenPort }]),
stop_signal: 'SIGINT',
restart: restart,
Expand Down
66 changes: 59 additions & 7 deletions src/service/ConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ export class ConfigService {
const copyFrom = join(this.root, 'config', 'node');
const name = account.name;

const outputFolder = BootstrapUtils.getTargetNodesFolder(this.params.target, false, name, 'userconfig');
const serverConfig = BootstrapUtils.getTargetNodesFolder(this.params.target, false, name, 'server-config');
const brokerConfig = BootstrapUtils.getTargetNodesFolder(this.params.target, false, name, 'broker-config');
const nodePreset = (presetData.nodes || [])[index];

const nodePrivateKey = await CommandUtils.resolvePrivateKey(
Expand Down Expand Up @@ -325,23 +326,67 @@ export class ConfigService {
await BootstrapUtils.generateConfiguration(templateContext, copyFrom, agentConfig, []);
}

await BootstrapUtils.generateConfiguration(templateContext, copyFrom, outputFolder, excludeFiles);
const serverRecoveryConfig = {
addressextractionRecovery: false,
mongoRecovery: false,
zeromqRecovery: false,
filespoolingRecovery: true,
hashcacheRecovery: true,
};

const brokerRecoveryConfig = {
addressextractionRecovery: true,
mongoRecovery: true,
zeromqRecovery: true,
filespoolingRecovery: false,
hashcacheRecovery: true,
};

logger.info(`Generating ${name} server configuration`);
await BootstrapUtils.generateConfiguration({ ...serverRecoveryConfig, ...templateContext }, copyFrom, serverConfig, excludeFiles);
await this.generateP2PFile(
presetData,
addresses,
outputFolder,
serverConfig,
NodeType.PEER_NODE,
(nodePresetData) => nodePresetData.harvesting,
'peers-p2p.json',
);
await this.generateP2PFile(
presetData,
addresses,
outputFolder,
serverConfig,
NodeType.API_NODE,
(nodePresetData) => nodePresetData.api,
'peers-api.json',
);

if (nodePreset.brokerName) {
logger.info(`Generating ${nodePreset.brokerName} broker configuration`);
await BootstrapUtils.generateConfiguration(
{ ...brokerRecoveryConfig, ...templateContext },
copyFrom,
brokerConfig,
excludeFiles,
);
await this.generateP2PFile(
presetData,
addresses,
brokerConfig,
NodeType.PEER_NODE,
(nodePresetData) => nodePresetData.harvesting,
'peers-p2p.json',
);
await this.generateP2PFile(
presetData,
addresses,
brokerConfig,
NodeType.API_NODE,
(nodePresetData) => nodePresetData.api,
'peers-api.json',
);
}

await new VotingService(this.params).run(presetData, account, nodePreset);
}

Expand Down Expand Up @@ -389,7 +434,7 @@ export class ConfigService {
const transactionsDirectory = join(nemesisWorkingDir, presetData.nemesis.transactionsDirectory || presetData.transactionsDirectory);
await BootstrapUtils.mkdir(transactionsDirectory);
const copyFrom = join(this.root, `config`, `nemesis`);
const moveTo = join(nemesisWorkingDir, `userconfig`);
const moveTo = join(nemesisWorkingDir, `server-config`);
const templateContext = { ...(presetData as any), addresses };
await Promise.all(
(addresses.nodes || []).filter((n) => n.vrf).map((n) => this.createVrfTransaction(transactionsDirectory, presetData, n)),
Expand Down Expand Up @@ -558,7 +603,7 @@ export class ConfigService {
this.params.target,
false,
gatewayPreset.apiNodeName,
'userconfig',
'server-config',
'resources',
);
const apiNodeCertFolder = BootstrapUtils.getTargetNodesFolder(this.params.target, false, gatewayPreset.apiNodeName, 'cert');
Expand Down Expand Up @@ -633,9 +678,16 @@ export class ConfigService {
private cleanUpConfiguration(presetData: ConfigPreset) {
const target = this.params.target;
(presetData.nodes || []).forEach(({ name }) => {
const serverConfigFolder = BootstrapUtils.getTargetNodesFolder(target, false, name, 'userconfig');
const serverConfigFolder = BootstrapUtils.getTargetNodesFolder(target, false, name, 'server-config');
BootstrapUtils.deleteFolder(serverConfigFolder);

const brokerConfigFolder = BootstrapUtils.getTargetNodesFolder(target, false, name, 'broker-config');
BootstrapUtils.deleteFolder(brokerConfigFolder);

// Remove old user configs when upgrading.
const userConfigFolder = BootstrapUtils.getTargetNodesFolder(target, false, name, 'userconfig');
BootstrapUtils.deleteFolder(userConfigFolder);

const seedFolder = BootstrapUtils.getTargetNodesFolder(target, false, name, 'seed');
BootstrapUtils.deleteFolder(seedFolder);
});
Expand Down
Loading

0 comments on commit 35a2f97

Please sign in to comment.