Last active
November 6, 2023 21:17
-
-
Save wileyj/9922af657ae8048cb67ba9621141e7e1 to your computer and use it in GitHub Desktop.
Script to setup and run a node as a Stacks bootstrap instance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -eo pipefail | |
set -Eo functrace | |
shopt -s expand_aliases | |
## | |
## Meant to be run manually or via cron | |
## ex cron: | |
## 00 * * * * /usr/local/bin/seed >> /tmp/seed-log | |
## | |
## set some defaults | |
## Optional: use verbose logging | |
VERBOSE=true | |
## Optional: use an auth user/key to bypass API rate limits in the format of "-u user:key" | |
AUTH="" | |
## Local file paths | |
LOCAL_VERSION="/stacks_version" | |
LOCAL_KEYFILE="/stacks_key" | |
LOCAL_PUBLIC_IPV4="/stacks_ipv4" | |
LOCAL_BINARY_DIR="/usr/local/bin" | |
LOCAL_BINARY_NAME="stacks-node" | |
LOCAL_BINARY="${LOCAL_BINARY_DIR}/${LOCAL_BINARY_NAME}" | |
LOCAL_KEY_SCRIPT="${LOCAL_BINARY_DIR}/key" | |
## stacks specific values | |
STACKS_SERVICE_USER="stacks" | |
STACKS_SERVICE_NAME="stacks" | |
STACKS_CONFIG_DIR="/etc/stacks-blockchain" | |
STACKS_CHAINSTATE_DIR="/stacks-blockchain" | |
STACKS_CONFIG_FILE="${STACKS_CONFIG_DIR}/Config.toml" | |
STACKS_UNIT_FILE="/etc/systemd/system/${STACKS_SERVICE_NAME}.service" | |
STACKS_RPC_PORT=20443 | |
STACKS_P2P_PORT=20444 | |
## Optional but recommended for keeping the keypair consistent (can be retrieved after 1st run from /stacks_key) | |
STACKS_PRIVATE_KEY="" | |
STACKS_PUBLIC_KEY="" | |
STACKS_RELEASE_URL="https://api.github.com/repos/stacks-network/stacks-blockchain/releases" | |
## configure logging format | |
alias log="logger" | |
alias log_error='logger "${ERROR}"' | |
alias log_warn='logger "${WARN}"' | |
alias log_info='logger "${INFO}"' | |
alias log_exit='exit_error "${EXIT_MSG}"' | |
if ${VERBOSE}; then | |
alias log='logger "$(date "+%D %H:%m:%S")" "Func:${FUNCNAME:-main}" "Line:${LINENO:-null}"' | |
alias log_info='logger "$(date "+%D %H:%m:%S")" "Func:${FUNCNAME:-main}" "Line:${LINENO:-null}" "${INFO}"' | |
alias log_warn='logger "$(date "+%D %H:%m:%S")" "Func:${FUNCNAME:-main}" "Line:${LINENO:-null}" "${WARN}"' | |
alias log_error='logger "$(date "+%D %H:%m:%S")" "Func:${FUNCNAME:-main}" "Line:${LINENO:-null}" "${ERROR}"' | |
alias log_exit='exit_error "$(date "+%D %H:%m:%S")" "Func:${FUNCNAME:-main}" "Line:${LINENO:-null}" "${EXIT_MSG}"' | |
fi | |
logger() { | |
if ${VERBOSE}; then | |
printf "%s %-30s %-10s %-10s %-25s %s\\n" "${1}" "${2}" "${3}" "${DEBUG}" "${4}" "${5}" | |
else | |
printf "%-25s %s\\n" "${1}" "${2}" | |
fi | |
} | |
exit_error() { | |
if ${VERBOSE}; then | |
printf "%s %-25s %-10s %-10s %-25s %s\\n\\n" "${1}" "${2}" "${DEBUG}" "${3}" "${4}" "${5}" | |
else | |
printf "%-25s %s\\n\\n" "${1}" "${2}" | |
fi | |
exit 1 | |
} | |
if ! (sudo test -f "${LOCAL_VERSION}" && sudo test -f "${LOCAL_KEYFILE}" && sudo test -f "${LOCAL_PUBLIC_IPV4}" ); then | |
${VERBOSE} && log "Local state files are missing. Trying to install dependencies" | |
log "Installing system dependencies" | |
## install required packages | |
sudo apt-get install -y curl unzip nodejs npm jq sed > /dev/null 2>&1 || log_exit "Error installing required packages" | |
## install required npm packages | |
sudo npm install -g @stacks/encryption elliptic > /dev/null 2>&1 || log_exit "Error installing required npm modules" | |
fi | |
# Check for required binaries, exit if missing | |
for cmd in curl node file unzip sed; do | |
command -v "${cmd}" > /dev/null 2>&1 || log_exit "Missing command: ${cmd}" | |
done | |
## Only set these values after dependencies are installed and we've checked the binaries are in $PATH | |
NODE_PATH="$(npm root -g)" | |
## Retrieve the latest release version from github | |
REMOTE_VERSION=$(curl -sL ${AUTH} -H "Accept: application/vnd.github+json" ${STACKS_RELEASE_URL}/latest | jq -r .tag_name) | |
if [ "${REMOTE_VERSION}" == "" ];then | |
log "Error retrieving the remote version" | |
log_exit "Exiting" | |
fi | |
LOCAL_BINARY_ARCHIVE="/tmp/stacks-blockchain-${REMOTE_VERSION}.zip" ## this has to be defined after we have the remote_version | |
## if verbose is set, print out the variables we'll be using throughout the script | |
${VERBOSE} && log "NODE_PATH: ${NODE_PATH}" | |
${VERBOSE} && log "STACKS_RELEASE_URL: ${STACKS_RELEASE_URL}" | |
${VERBOSE} && log "REMOTE_VERSION: ${REMOTE_VERSION}" | |
${VERBOSE} && log "LOCAL_TAG: ${LOCAL_VERSION}" | |
${VERBOSE} && log "LOCAL_KEYFILE: ${LOCAL_KEYFILE}" | |
${VERBOSE} && log "LOCAL_BINARY_ARCHIVE: ${LOCAL_BINARY_ARCHIVE}" | |
${VERBOSE} && log "LOCAL_BINARY_DIR: ${LOCAL_BINARY_DIR}" | |
${VERBOSE} && log "LOCAL_BINARY_NAME: ${LOCAL_BINARY_NAME}" | |
${VERBOSE} && log "LOCAL_BINARY: ${LOCAL_BINARY}" | |
${VERBOSE} && log "SERVICE_USER: ${STACKS_SERVICE_USER}" | |
${VERBOSE} && log "SERVICE_NAME: ${STACKS_SERVICE_NAME}" | |
## Install a simple node script to create a stacks keypair | |
install_seed_script() { | |
## create/recreate the script every runtime | |
log "Creating file: ${LOCAL_KEY_SCRIPT}" | |
sudo bash -c 'cat <<-EOF> '"${LOCAL_KEY_SCRIPT}"' | |
#!/usr/bin/env node | |
const EC = require("elliptic").ec; | |
const secp256k1 = new EC("secp256k1"); | |
const enc = require("@stacks/encryption"); | |
const process = require("process"); | |
const key = secp256k1.genKeyPair(); | |
while (true) { | |
const key_hex = key.getPrivate().toString("hex"); | |
if (key_hex.length != 64) { | |
continue; | |
} | |
break; | |
} | |
try { | |
const privateKey = key.getPrivate().toString("hex"); | |
const publicKey = enc.getPublicKeyFromPrivate(privateKey); | |
process.stdout.write([publicKey, ":", privateKey].join("")); | |
} catch { | |
return process.exit(1); | |
} | |
return process.exit(0); | |
EOF' | |
## set the script as executable | |
${VERBOSE} && log "Setting file permissions on ${LOCAL_KEY_SCRIPT}" | |
sudo chmod 755 "${LOCAL_KEY_SCRIPT}" || log_exit "Error creating script: ${LOCAL_KEY_SCRIPT}" | |
return 0 | |
} | |
## Install the systemd unit for stacks | |
install_unit_file() { | |
## create/recreate the unit file every runtime | |
log "Creating unit file: ${STACKS_UNIT_FILE}" | |
sudo bash -c 'cat <<-EOF> '"${STACKS_UNIT_FILE}"' | |
## Modeled after https://github.com/bitcoin/bitcoin/blob/master/contrib/init/bitcoind.service | |
[Unit] | |
Description=Stacks Blockchain | |
# https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/ | |
After=network-online.target | |
Wants=network-online.target | |
ConditionFileIsExecutable=LOCAL_BINARY | |
ConditionPathExists=STACKS_CONFIG_FILE | |
ConditionPathIsDirectory=STACKS_CHAINSTATE_DIR | |
[Service] | |
ExecStart=LOCAL_BINARY start --config=STACKS_CONFIG_FILE | |
# Make sure the config directory is readable by the service user | |
PermissionsStartOnly=true | |
ExecStartPre=/bin/chgrp stacks STACKS_CONFIG_DIR | |
# Process management | |
#################### | |
PIDFile=/run/stacks-blockchain/stacks-blockchain.pid | |
Restart=no | |
TimeoutStopSec=900 | |
KillSignal=SIGINT | |
SendSIGKILL=no | |
# Directory creation and permissions | |
#################################### | |
# Run as SERVICE_USER:SERVICE_USER | |
User=STACKS_SERVICE_USER | |
Group=STACKS_SERVICE_USER | |
# /run/stacks-blockchain | |
RuntimeDirectory=stacks-blockchain | |
RuntimeDirectoryMode=0710 | |
# /etc/stacks-blockchain | |
ConfigurationDirectory=stacks-blockchain | |
ConfigurationDirectoryMode=0710 | |
# Hardening measures | |
#################### | |
# Provide a private /tmp and /var/tmp. | |
PrivateTmp=true | |
# Mount /usr, /boot/ and /etc read-only for the process. | |
ProtectSystem=full | |
# Deny access to /home, /root and /run/user | |
ProtectHome=true | |
# Disallow the process and all of its children to gain | |
# new privileges through execve(). | |
NoNewPrivileges=true | |
# Use a new /dev namespace only populated with API pseudo devices | |
# such as /dev/null, /dev/zero and /dev/random. | |
PrivateDevices=true | |
# Deny the creation of writable and executable memory mappings. | |
MemoryDenyWriteExecute=true | |
SystemCallArchitectures=native | |
[Install] | |
WantedBy=multi-user.target | |
EOF' | |
if ! sudo test -f "${STACKS_UNIT_FILE}";then | |
log_exit "Error creating unit file: ${STACKS_UNIT_FILE}" | |
fi | |
## replace some hardcoded values in the above file with variables from this script | |
${VERBOSE} && log "Substitute unit file strings with variables" | |
sudo sed -i -e "s|STACKS_SERVICE_USER|${STACKS_SERVICE_USER}|g" "${STACKS_UNIT_FILE}" || log_exit "Error sed'ing 'SERVICE_USER' in ${STACKS_UNIT_FILE}" | |
sudo sed -i -e "s|STACKS_CONFIG_DIR|${STACKS_CONFIG_DIR}|g" "${STACKS_UNIT_FILE}" || log_exit "Error sed'ing 'STACKS_CONFIG_DIR' in ${STACKS_UNIT_FILE}" | |
sudo sed -i -e "s|STACKS_CHAINSTATE_DIR|${STACKS_CHAINSTATE_DIR}|g" "${STACKS_UNIT_FILE}" || log_exit "Error sed'ing 'STACKS_CHAINSTATE_DIR' in ${STACKS_UNIT_FILE}" | |
sudo sed -i -e "s|STACKS_CONFIG_FILE|${STACKS_CONFIG_FILE}|g" "${STACKS_UNIT_FILE}" || log_exit "Error sed'ing 'STACKS_CONFIG_FILE' in ${STACKS_UNIT_FILE}" | |
sudo sed -i -e "s|LOCAL_BINARY|${LOCAL_BINARY}|g" "${STACKS_UNIT_FILE}" || log_exit "Error sed'ing 'LOCAL_BINARY' in ${STACKS_UNIT_FILE}" | |
## set the unit's file permissions | |
${VERBOSE} && log "Setting file permissions on ${STACKS_UNIT_FILE}" | |
sudo chmod 644 "${STACKS_UNIT_FILE}" || log_exit "Error setting permissions on ${STACKS_UNIT_FILE}" | |
## reload systemd and enable the unit | |
${VERBOSE} && log "Reloading systemd" | |
sudo systemctl daemon-reload > /dev/null 2>&1 || log_exit "Error reloading systemd" | |
${VERBOSE} && log "Enabling ${STACKS_SERVICE_NAME} on boot" | |
sudo systemctl enable ${STACKS_SERVICE_NAME} > /dev/null 2>&1 || log_exit "Error enabling ${STACKS_SERVICE_NAME} on boot" | |
log "Installed and enabled systemd unit" | |
return 0 | |
} | |
## Install the stacks Config.toml | |
install_stacks_config() { | |
private_key="" | |
public_key="" | |
local_public_ipv4="" | |
## check for an existing keyfile to use values from | |
if ! sudo test -f "${LOCAL_KEYFILE}"; then | |
if [ "${STACKS_PRIVATE_KEY}" != "" -a "${STACKS_PUBLIC_KEY}" != "" ];then | |
## use hard-coded values if they are defined and local keyfile is missing | |
log "Creating keyfile from hardcoded stacks keypair" | |
sudo sh -c "echo -n ${STACKS_PUBLIC_KEY}:${STACKS_PRIVATE_KEY} > ${LOCAL_KEYFILE}" || log_exit "Error storing version in: ${LOCAL_KEYFILE}" | |
else | |
## create a new keyfile with new keypair from script in install_seed_script | |
log "Creating new stacks keypair" | |
sudo sh -c "NODE_PATH=${NODE_PATH} node ${LOCAL_KEY_SCRIPT} > ${LOCAL_KEYFILE}" || log_exit "Error saving key to: ${LOCAL_KEYFILE}" | |
fi | |
fi | |
## ensure the keyfile has the correct permissions | |
sudo chmod 600 ${LOCAL_KEYFILE} || log_exit "Error setting permissions: ${LOCAL_KEYFILE}" | |
## retrieve the keys from the (now) existing file | |
public_key=$(sudo cat ${LOCAL_KEYFILE} | cut -f1 -d ":") | |
private_key=$(sudo cat ${LOCAL_KEYFILE} | cut -f2 -d ":") | |
if [ "${private_key}" == "" ]; then | |
log_exit "Error retrieving private key from: ${LOCAL_KEYFILE}" | |
fi | |
${VERBOSE} && log "using private_key: ${private_key}" | |
${VERBOSE} && log "using public_key: ${public_key}" | |
## retrieve the host's public IP | |
## public_ipv4=$(curl -sL https://ipinfo.io/ip) ## non-aws hosts | |
public_ipv4=$(curl -sL http://169.254.169.254/latest/meta-data/public-ipv4) ## aws hosts, http only | |
if [ -f "${LOCAL_PUBLIC_IPV4}" ]; then | |
local_public_ipv4=$(sudo cat ${LOCAL_PUBLIC_IPV4}) | |
fi | |
if [ "${public_ipv4}" != "${local_public_ipv4}" ];then | |
${VERBOSE} && log "Public IPv4 do not match" | |
fi | |
## if the config file does not exist or the public ip has changed, (over)write the config | |
if [ "${public_ipv4}" != "${local_public_ipv4}" ] || ! (sudo test -f "${STACKS_CONFIG_FILE}" ); then | |
log "Creating config file: ${STACKS_CONFIG_FILE}" | |
sudo bash -c 'cat <<-EOF> '"${STACKS_CONFIG_FILE}"' | |
[node] | |
working_dir = "STACKS_CHAINSTATE_DIR" | |
rpc_bind = "0.0.0.0:STACKS_RPC_PORT" | |
p2p_bind = "0.0.0.0:STACKS_P2P_PORT" | |
data_url = "http://PUBLIC_IPV4:STACKS_RPC_PORT" | |
p2p_address = "PUBLIC_IPV4:STACKS_P2P_PORT" | |
local_peer_seed = "PRIVATE_KEY" | |
bootstrap_node = "02196f005965cebe6ddc3901b7b1cc1aa7a88f305bb8c5893456b8f9a605923893@seed.mainnet.hiro.so:20444,02539449ad94e6e6392d8c1deb2b4e61f80ae2a18964349bc14336d8b903c46a8c@cet.stacksnodes.org:20444,02ececc8ce79b8adf813f13a0255f8ae58d4357309ba0cedd523d9f1a306fcfb79@sgt.stacksnodes.org:20444,0303144ba518fe7a0fb56a8a7d488f950307a4330f146e1e1458fc63fb33defe96@est.stacksnodes.org:20444" | |
name = "Seed" | |
[burnchain] | |
chain = "bitcoin" | |
mode = "mainnet" | |
peer_host = "bitcoin.mainnet.stacks.org" | |
username = "stacks" | |
password = "foundation" | |
rpc_port = 8332 | |
peer_port = 8333 | |
[connection_options] | |
public_ip_address = "PUBLIC_IPV4:STACKS_P2P_PORT" | |
read_only_call_limit_write_length = 0 | |
read_only_call_limit_read_length = 100000 | |
read_only_call_limit_write_count = 0 | |
read_only_call_limit_read_count = 30 | |
read_only_call_limit_runtime = 1000000000 | |
EOF' | |
## replace some hardcoded values in the above file with variables from this script | |
${VERBOSE} && log "Substitute config file strings with variables" | |
sudo sed -i -e "s|STACKS_RPC_PORT|${STACKS_RPC_PORT}|g" "${STACKS_CONFIG_FILE}" || log_exit "Error sed'ing 'RPC_PORT' in ${STACKS_CONFIG_FILE}" | |
sudo sed -i -e "s|STACKS_P2P_PORT|${STACKS_P2P_PORT}|g" "${STACKS_CONFIG_FILE}" || log_exit "Error sed'ing 'P2P_PORT' in ${STACKS_CONFIG_FILE}" | |
sudo sed -i -e "s|STACKS_CHAINSTATE_DIR|${STACKS_CHAINSTATE_DIR}|g" "${STACKS_CONFIG_FILE}" || log_exit "Error sed'ing 'STACKS_CHAINSTATE_DIR' in ${STACKS_CONFIG_FILE}" | |
sudo sed -i -e "s|PUBLIC_IPV4|${public_ipv4}|g" "${STACKS_CONFIG_FILE}" || log_exit "Error sed'ing 'PUBLIC_IPV4' in ${STACKS_CONFIG_FILE}" | |
sudo sed -i -e "s|PRIVATE_KEY|${private_key}|g" "${STACKS_CONFIG_FILE}" || log_exit "Error sed'ing 'PRIVATE_KEY' in ${STACKS_CONFIG_FILE}" | |
if ! sudo test -f "${STACKS_CONFIG_FILE}";then | |
log_exit "Error creating config file: ${STACKS_CONFIG_FILE}" | |
fi | |
sudo sh -c "echo ${public_ipv4} > ${LOCAL_PUBLIC_IPV4}" || log_exit "Error storing public ipv4 in: ${LOCAL_PUBLIC_IPV4}" | |
if is_stacks_running; then | |
${VERBOSE} && log "Stopping ${STACKS_SERVICE_NAME} after config change" | |
## The stacks binary is running, stop it. the last step in the script will restart the pid, no need to do it here | |
run_systemctl "stop" | |
fi | |
fi | |
## change the group to the service user (will need to have read access to this file when binary is started) | |
sudo chgrp "${STACKS_SERVICE_USER}" "${STACKS_CONFIG_FILE}" || log_exit "Error setting group on: ${STACKS_CONFIG_FILE}" | |
return 0 | |
} | |
## Create the user to own the pid and files/dirs | |
create_user(){ | |
## check if the user exists | |
if [ ! $(getent passwd ${STACKS_SERVICE_USER}) ]; then | |
## create the service user with a bash shell using the chainstate dir as $HOME | |
sudo useradd "${STACKS_SERVICE_USER}" -s /usr/bin/bash -m -d "${STACKS_CHAINSTATE_DIR}" || log_exit "Error creating user: ${STACKS_SERVICE_USER}" | |
log "Created user: ${STACKS_SERVICE_USER}" | |
else | |
${VERBOSE} && log "User already exists: ${STACKS_SERVICE_USER}" | |
fi | |
return 0 | |
} | |
## create the expected dirs if they don't exist | |
create_dirs() { | |
dirs=( | |
${STACKS_CONFIG_DIR} | |
${STACKS_CHAINSTATE_DIR} | |
) | |
for dir in "${dirs[@]}"; do | |
## if the dir is missing, create it | |
if ! sudo test -d "${dir}";then | |
log "Creating missing dir: $dir" | |
sudo mkdir -p "${dir}" || log_exit "Error creating dir: ${dir}" | |
## set the ownership only if we're createing the dir | |
sudo chown -R "${STACKS_SERVICE_USER}" "${dir}" || log_exit "Error setting ownership on: ${dir}" | |
fi | |
## Set permissions on the dirs (regardless if they were just created) | |
log "Setting permissions on: ${dir}" | |
if [ "${dir}" == "${STACKS_CONFIG_DIR}" ];then | |
${VERBOSE} && log "Setting permissions on: ${dir}" | |
sudo chmod 710 "${dir}" || log_exit "Error setting permissions on: ${dir}" | |
fi | |
## set the group ownership of the dir (less relevant for the chainstate, but important for the config dir/file) | |
log "Setting ownership on: ${dir}" | |
sudo chgrp "${STACKS_SERVICE_USER}" "${dir}" || log_exit "Error setting group on: ${dir}" | |
done | |
return 0 | |
} | |
## Compare the remote version to a recorded local version file | |
check_version() { | |
## Check that the local version file exists | |
if [ ! -f "${LOCAL_VERSION}" ]; then | |
log "Version file missing: ${LOCAL_VERSION}" | |
return 0 | |
fi | |
## compare the contents of the local version file vs the remote version | |
if [ "$(cat ${LOCAL_VERSION})" != "${REMOTE_VERSION}" ];then | |
# tags do not match | |
log "Versions do not match" | |
${VERBOSE} && log " local version: $(cat ${LOCAL_VERSION})" | |
${VERBOSE} && log " remote version: ${REMOTE_VERSION}" | |
return 0 | |
else | |
log "Local and remote versions match" | |
${VERBOSE} && log " local version: $(cat ${LOCAL_VERSION})" | |
${VERBOSE} && log " remote version: ${REMOTE_VERSION}" | |
fi | |
return 1 | |
} | |
## Check if the binary is running via systemd | |
is_stacks_running() { | |
sudo systemctl status "${STACKS_SERVICE_NAME}" > /dev/null 2>&1 | |
if [[ "${?}" -eq 0 ]]; then | |
## service is running | |
return 0 | |
fi | |
${VERBOSE} && log "${STACKS_SERVICE_NAME} service is not running" | |
return 1 | |
} | |
## Execute systemcl command | |
run_systemctl(){ | |
cmd="${1}" | |
${VERBOSE} && log "Running: systemctl sudo systemctl ${cmd} ${STACKS_SERVICE_NAME}" | |
sudo systemctl "${cmd}" "${STACKS_SERVICE_NAME}" > /dev/null 2>&1 || log_exit "Error Running: sudo systemctl ${cmd} ${STACKS_SERVICE_NAME}" | |
return 0 | |
} | |
## Confirm if a filetype matches what is expected (uses an arg for matching file types) | |
check_filetype() { | |
file="${1}" | |
type="${2}" | |
if [ -f "${file}" ]; then | |
## check the filetype using the provided args | |
file -zEb "${file}" | grep "${type}" > /dev/null 2>&1 | |
if [ "${?}" == "0" ]; then | |
${VERBOSE} && log "File matches expected type: ${file} (${type})" | |
return 0 | |
fi | |
## File did not match what was expected in $2 | |
${VERBOSE} && log "File does not match expected type: ${file} (${type})" | |
return 1 | |
fi | |
## filetype matched what was expected | |
${VERBOSE} && log "File not found: ${file}" | |
return 1 | |
} | |
## Download the stacks binary if the local version doesn't match the remote version (or the local version file is missing) | |
download_binary() { | |
## check if the file exists and is a zip archive | |
if ! check_filetype "${LOCAL_BINARY_ARCHIVE}" "Zip archive data"; then | |
${VERBOSE} && log "Downloading binary archive" | |
archive_url=$(curl -sL ${AUTH} ${STACKS_RELEASE_URL}/latest | jq -r '.assets[] | { name, browser_download_url } | select((.name == "linux-glibc-x64.zip")) | {browser_download_url}| .[]') | |
## download the zip archive since it does not exist, or it is not a zip file | |
curl -L ${AUTH} -# "${archive_url}" -o "${LOCAL_BINARY_ARCHIVE}" || log_exit "Error downloading binary archive: ${LOCAL_BINARY_ARCHIVE}" | |
## check the downloaded file | |
if ! check_filetype "${LOCAL_BINARY_ARCHIVE}" "Zip archive data"; then | |
## archive either does not exist or is not a zip file | |
log_exit "Error downloading archive: ${LOCAL_BINARY_ARCHIVE}" | |
else | |
log "Archive downloaded: ${LOCAL_BINARY_ARCHIVE}" | |
fi | |
fi | |
# Archive already exists and is a zipfile. no need to re-download it | |
return 0 | |
} | |
## Extract the binary from the zip archive to /usr/local/bin | |
extract_binary() { | |
## extract the binary from the zip archive to LOCAL_BINARY_DIR/LOCAL_BINARY_NAME | |
sudo unzip -Djqo "${LOCAL_BINARY_ARCHIVE}" "${LOCAL_BINARY_NAME}" -d "${LOCAL_BINARY_DIR}" || log_exit "Error extracting archive: ${LOCAL_BINARY_ARCHIVE}" | |
## confirm that the extracted file is the expected filetype | |
if ! check_filetype "${LOCAL_BINARY}" "ELF 64-bit LSB pie executable, x86-64"; then | |
log_exit "Exiting - File (${LOCAL_BINARY}) does not match expected type" | |
fi | |
${VERBOSE} && log "Binary extracted to: ${LOCAL_BINARY}" | |
## save the version to a local file for future comparison | |
sudo sh -c "echo ${REMOTE_VERSION} > ${LOCAL_VERSION}" || log_exit "Error storing version in: ${LOCAL_VERSION}" | |
return 0 | |
} | |
## Run these functions every time (only `create_user` and `create_dirs` has to be run in order) | |
create_user ## create the user binary will run as | |
create_dirs ## create the required dirs | |
install_seed_script ## install the script to generate a public key | |
install_unit_file ## install the systemd unit | |
install_stacks_config ## create the stacks config | |
## check if the remote version matches the local version file | |
if check_version; then | |
## if there is no match, download the binary archive | |
if download_binary; then | |
## check if binary is running | |
if is_stacks_running; then | |
## The stacks binary is running, stop it | |
run_systemctl "stop" | |
fi | |
## Extract the stacks-node binary file | |
if extract_binary && ! is_stacks_running; then | |
## Start the binary | |
run_systemctl "start" | |
fi | |
fi | |
fi | |
if ! is_stacks_running; then | |
## Binary is not running, start it. This will work to start a dead pid if something unexpectedly killed it | |
run_systemctl "start" | |
fi | |
if is_stacks_running; then | |
## Confirm the binary is running before exiting | |
log "Successfully exiting" | |
exit 0 | |
else | |
log "service not running" | |
fi | |
## Fail with an error if the binary is still not running | |
exit 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Some notes:
/stacks_key
su - stacks
or usesudo
to manually edit configs etc. just use sudo (this also applies tocat
andls
).00 * * * * /usr/local/bin/seed >> /tmp/seed-log