mirror of
https://github.com/EvernodeXRPL/sashimono.git
synced 2026-08-22 10:50:51 +00:00
Evernode setup installer. (#87)
This commit is contained in:
@@ -78,7 +78,7 @@ target_precompile_headers(sagent PUBLIC src/pchheader.hpp)
|
||||
add_custom_target(installer
|
||||
COMMAND mkdir -p ./build/sashimono-installer
|
||||
COMMAND bash -c "cp -r ./build/{sagent,sashi,hpfs,user-install.sh,user-uninstall.sh,contract_template} ./build/sashimono-installer/"
|
||||
COMMAND bash -c "cp -r ./installer/{docker-install.sh,registry-install.sh,registry-uninstall.sh,sashimono-install.sh,sashimono-uninstall.sh} ./build/sashimono-installer/"
|
||||
COMMAND bash -c "cp -r ./installer/{docker-install.sh,registry-install.sh,registry-uninstall.sh,prereq.sh,sashimono-install.sh,sashimono-uninstall.sh} ./build/sashimono-installer/"
|
||||
COMMAND bash -c "cp -r ./dependencies/{user-cgcreate.sh,libblake3.so} ./build/sashimono-installer/"
|
||||
COMMAND bash -c "cp -r ./mb-xrpl/dist ./build/sashimono-installer/mb-xrpl"
|
||||
COMMAND tar cfz ./build/sashimono-installer.tar.gz --directory=./build/ sashimono-installer
|
||||
|
||||
1
dependencies/user-cgcreate.sh
vendored
1
dependencies/user-cgcreate.sh
vendored
@@ -54,6 +54,7 @@ for user in "${validusers[@]}"; do
|
||||
# Setup user cgroup.
|
||||
if [ $instance_cpu_us -gt 0 ] &&
|
||||
! (cgcreate -g cpu:$user$cgroupsuffix &&
|
||||
echo "1000000" > /sys/fs/cgroup/cpu/$user$cgroupsuffix/cpu.cfs_period_us &&
|
||||
echo "$instance_cpu_us" > /sys/fs/cgroup/cpu/$user$cgroupsuffix/cpu.cfs_quota_us); then
|
||||
echo "CPU cgroup creation for $user failed."
|
||||
has_err=1
|
||||
|
||||
34
dependencies/user-install.sh
vendored
34
dependencies/user-install.sh
vendored
@@ -39,11 +39,24 @@ function rollback() {
|
||||
echo "$1,INST_ERR" && exit 1
|
||||
}
|
||||
|
||||
# Waits until a service becomes ready up to 3 seconds.
|
||||
function service_ready() {
|
||||
local svcstat=""
|
||||
for ((i = 0; i < 30; i++)); do
|
||||
sleep 0.1
|
||||
svcstat=$(sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user is-active $1)
|
||||
if [ "$svcstat" == "active" ] ; then
|
||||
return 0 # Success
|
||||
fi
|
||||
done
|
||||
return 1 # Error
|
||||
}
|
||||
|
||||
# Setup user and dockerd service.
|
||||
useradd --shell /usr/sbin/nologin -m "$user"
|
||||
usermod --lock "$user"
|
||||
usermod -a -G $group "$user"
|
||||
loginctl enable-linger "$user" # Enable lingering to support rootless dockerd service installation.
|
||||
useradd --shell /usr/sbin/nologin -m $user
|
||||
usermod --lock $user
|
||||
usermod -a -G $group $user
|
||||
loginctl enable-linger $user # Enable lingering to support rootless dockerd service installation.
|
||||
chmod o-rwx "$user_dir"
|
||||
echo "Created '$user' user."
|
||||
|
||||
@@ -68,6 +81,7 @@ dockerd_socket="unix://$user_runtime_dir/docker.sock"
|
||||
|
||||
# Setup user cgroup.
|
||||
! (cgcreate -g cpu:$user$cgroupsuffix &&
|
||||
echo "1000000" >/sys/fs/cgroup/cpu/$user$cgroupsuffix/cpu.cfs_period_us &&
|
||||
echo "$cpu" >/sys/fs/cgroup/cpu/$user$cgroupsuffix/cpu.cfs_quota_us) && rollback "CGROUP_CPU_CREAT"
|
||||
! (cgcreate -g memory:$user$cgroupsuffix &&
|
||||
echo "${memory}K" >/sys/fs/cgroup/memory/$user$cgroupsuffix/memory.limit_in_bytes &&
|
||||
@@ -97,11 +111,11 @@ done
|
||||
echo "Installing rootless dockerd for user."
|
||||
sudo -H -u "$user" PATH="$docker_bin":"$PATH" XDG_RUNTIME_DIR="$user_runtime_dir" "$docker_bin"/dockerd-rootless-setuptool.sh install
|
||||
|
||||
svcstat=$(sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user is-active $docker_service)
|
||||
[ "$svcstat" != "active" ] && rollback "NO_DOCKERSVC"
|
||||
|
||||
mkdir "$user_dir"/.config/systemd/user/$docker_service.d && echo "[Service]
|
||||
Environment='DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns'" >"$user_dir"/.config/systemd/user/$docker_service.d/override.conf
|
||||
mkdir "$user_dir"/.config/systemd/user/$docker_service.d
|
||||
echo "[Service]
|
||||
Environment='DOCKERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER=slirp4netns'" >"$user_dir"/.config/systemd/user/$docker_service.d/override.conf
|
||||
sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user restart $docker_service
|
||||
service_ready $docker_service || rollback "NO_DOCKERSVC"
|
||||
|
||||
echo "Installed rootless dockerd."
|
||||
|
||||
@@ -134,6 +148,6 @@ RestartSec=5
|
||||
WantedBy=default.target" >"$user_dir"/.config/systemd/user/ledger_fs.service
|
||||
|
||||
sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user daemon-reload
|
||||
sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user restart $docker_service
|
||||
|
||||
echo "$user_id,$user,$dockerd_socket,INST_SUC"
|
||||
exit 0
|
||||
|
||||
@@ -71,12 +71,13 @@ popd > /dev/null 2>&1
|
||||
popd > /dev/null 2>&1
|
||||
rm v2.0.0.tar.gz && sudo rm -r CLI11-2.0.0
|
||||
|
||||
# Library dependencies.
|
||||
# Library and tools dependencies.
|
||||
sudo apt-get install -y \
|
||||
libsodium-dev \
|
||||
sqlite3 libsqlite3-dev \
|
||||
libboost-stacktrace-dev \
|
||||
fuse3
|
||||
jq
|
||||
|
||||
sudo cp $scriptdir/dependencies/libblake3.so /usr/local/lib/
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Sashimono cloud installer script.
|
||||
# This will download, extract and install the Sashimono installer package.
|
||||
# -q for non-interactive (quiet) mode
|
||||
|
||||
package="https://sthotpocket.blob.core.windows.net/sashimono/sashimono-installer.tar.gz"
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
cd $tmp
|
||||
curl $package --output installer.tgz
|
||||
tar zxf $tmp/installer.tgz --strip-components=1
|
||||
rm installer.tgz
|
||||
|
||||
./sashimono-install.sh "$@"
|
||||
|
||||
rm -r $tmp
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Sashimono cloud uninstaller bootstrapper script.
|
||||
# This will download and extract the installer and then uninstall Sashimono.
|
||||
# -q for non-interactive (quiet) mode
|
||||
|
||||
package="https://sthotpocket.blob.core.windows.net/sashimono/sashimono-installer.tar.gz"
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
cd $tmp
|
||||
curl $package --output installer.tgz
|
||||
tar zxf $tmp/installer.tgz --strip-components=1
|
||||
rm installer.tgz
|
||||
|
||||
./sashimono-uninstall.sh "$@"
|
||||
|
||||
rm -r $tmp
|
||||
@@ -13,8 +13,8 @@ echo "Installing rootless docker packages into $docker_bin"
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
cd $tmp
|
||||
curl https://download.docker.com/linux/static/stable/x86_64/docker-20.10.7.tgz --output docker.tgz
|
||||
curl https://download.docker.com/linux/static/stable/x86_64/docker-rootless-extras-20.10.7.tgz --output rootless.tgz
|
||||
curl -s https://download.docker.com/linux/static/stable/x86_64/docker-20.10.7.tgz --output docker.tgz
|
||||
curl -s https://download.docker.com/linux/static/stable/x86_64/docker-rootless-extras-20.10.7.tgz --output rootless.tgz
|
||||
|
||||
cd $docker_bin
|
||||
tar zxf $tmp/docker.tgz --strip-components=1
|
||||
|
||||
@@ -1,25 +1,43 @@
|
||||
#!/bin/bash
|
||||
# Sashimono Ubuntu prerequisites installation script.
|
||||
# This must be executed with root privileges.
|
||||
# -q for non-interactive (quiet) mode
|
||||
|
||||
quiet=$1
|
||||
|
||||
# Adding user disk quota limitation capability
|
||||
# Enable user quota in fstab for root mount.
|
||||
# We do not edit original file, instead we create a temp file with original and edit it.
|
||||
# Replace temp file with original only if success.
|
||||
tmp=$(mktemp -d)
|
||||
# Enable cgroup memory and swapaccount capability.
|
||||
# Setup cgroups rules engine service.
|
||||
|
||||
echo "---Sashimono prerequisites installer---"
|
||||
|
||||
[ -z "$1" ] && echo "cgrules engine service name not specified." && exit 1
|
||||
|
||||
tmp=$(mktemp -d)
|
||||
tmpfstab=$tmp.tmp
|
||||
originalfstab=/etc/fstab
|
||||
cp $originalfstab "$tmpfstab"
|
||||
backup=$originalfstab.sashi.bk
|
||||
cgrulesengd_service=$1 # cgroups rules engine service name
|
||||
|
||||
function stage() {
|
||||
echo "STAGE $1" # This is picked up by the setup console output filter.
|
||||
}
|
||||
|
||||
stage "Installing dependencies"
|
||||
|
||||
apt-get update
|
||||
apt-get install uidmap -y
|
||||
apt-get install -y uidmap slirp4netns fuse3 cgroup-tools quota curl openssl jq
|
||||
# uidmap # Required for rootless docker.
|
||||
# slirp4netns # Required for high performance rootless networking.
|
||||
# fuse3 # Required for hpfs.
|
||||
# cgroup-tools # Required to setup contract instances resource limits.
|
||||
# quota # Required for disk space group quota.
|
||||
# curl # Required to download installation artifacts.
|
||||
# openssl # Required by Sashimono agent to create contract tls certs.
|
||||
# jq # Used for json config file manipulation.
|
||||
|
||||
# Install nodejs 14 if not exists.
|
||||
if ! command -v node &>/dev/null; then
|
||||
stage "Installing nodejs"
|
||||
apt-get -y install ca-certificates # In case nodejs package certitficates are renewed.
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | bash -
|
||||
apt-get -y install nodejs
|
||||
@@ -30,14 +48,15 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install slirp4netns if not exists (required for high performance rootless networking).
|
||||
if ! command -v slirp4netns &>/dev/null; then
|
||||
apt-get -y install slirp4netns
|
||||
fi
|
||||
# -------------------------------
|
||||
# fstab changes
|
||||
# We do not edit original file, instead we create a temp file with original and edit it.
|
||||
# Replace temp file with original only if success.
|
||||
|
||||
# Check for pattern <Not starting with a comment><Not whitespace(Device)><Whitespace></><Whitespace><Not whitespace(FS type)><Whitespace><No whitespace(Options)><Whitespace><Number(Dump)><Whitespace><Number(Pass)>
|
||||
# And whether Options is <Not whitespace>*grpjquota=aquota.group or jqfmt=vfsv0<Not whitespace>*
|
||||
# If not add groupquota to the options.
|
||||
stage "Configuring fstab"
|
||||
updated=0
|
||||
sed -n -r -e "/^[^#]\S+\s+\/\s+\S+\s+\S+\s+[0-9]+\s+[0-9]+\s*/{ /^\S+\s+\/\s+\S+\s+\S*grpjquota=aquota.group\S*/{q100} }" "$tmpfstab"
|
||||
res=$?
|
||||
@@ -47,7 +66,7 @@ if [ $res -eq 0 ]; then
|
||||
updated=1
|
||||
fi
|
||||
|
||||
# If the res is not success(0) or alredy exist(100).
|
||||
# If the res is not success(0) or already exist(100).
|
||||
[ ! $res -eq 0 ] && [ ! $res -eq 100 ] && echo "fstab update failed." && exit 1
|
||||
|
||||
sed -n -r -e "/^[^#]\S+\s+\/\s+\S+\s+\S+\s+[0-9]+\s+[0-9]+\s*/{ /^\S+\s+\/\s+\S+\s+\S*jqfmt=vfsv0\S*/{q100} }" "$tmpfstab"
|
||||
@@ -77,19 +96,15 @@ fi
|
||||
|
||||
# Check and turn on group quota if not enabled.
|
||||
if [ ! -f /aquota.group ]; then
|
||||
# quota package is not installed.
|
||||
if ! command -v quota &>/dev/null; then
|
||||
apt-get install -y quota
|
||||
fi
|
||||
quotacheck -ugm /
|
||||
quotaon -v /
|
||||
fi
|
||||
|
||||
# -------------------------------
|
||||
stage "Configuring fuse"
|
||||
|
||||
# Check fuse config exists.
|
||||
apt-get install -y fuse3
|
||||
[ ! -f /etc/fuse.conf ] && echo "Fuse config does not exist, Make sure you've installed fuse."
|
||||
[ ! -f /etc/fuse.conf ] && echo "Fuse config does not exist, Make sure you've installed fuse." && exit 1
|
||||
|
||||
# Set user_allow_other if not already configured
|
||||
# We create a temp of the config file and replace with original file only if success.
|
||||
@@ -135,11 +150,7 @@ else
|
||||
fi
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# Install cgroup-tools if not exists (required to setup resource control groups).
|
||||
if ! command -v /usr/sbin/cgconfigparser >/dev/null || ! command -v /usr/sbin/cgrulesengd >/dev/null; then
|
||||
apt-get install -y cgroup-tools
|
||||
fi
|
||||
stage "Configuring cgroup rules engine"
|
||||
|
||||
# Copy cgred.conf from examples if not exists to setup control groups.
|
||||
[ ! -f /etc/cgred.conf ] && cp /usr/share/doc/cgroup-tools/examples/cgred.conf /etc/
|
||||
@@ -150,29 +161,30 @@ fi
|
||||
# Create new cgrules.conf if not exists to setup control groups.
|
||||
[ ! -f /etc/cgrules.conf ] && : >/etc/cgrules.conf
|
||||
|
||||
# Setup a service to run cgroup rules generator.
|
||||
cgrulesengd_service=cgrulesengdsvc
|
||||
# Setup a service if not exists to run cgroup rules generator.
|
||||
cgrulesengd_file="/etc/systemd/system/$cgrulesengd_service.service"
|
||||
if ! [ -f "$cgrulesengd_file" ]; then
|
||||
echo "[Unit]
|
||||
Description=cgroups rules generator
|
||||
After=network.target
|
||||
|
||||
echo "[Unit]
|
||||
Description=cgroup rules generator
|
||||
After=network.target
|
||||
[Service]
|
||||
User=root
|
||||
Group=root
|
||||
Type=forking
|
||||
EnvironmentFile=-/etc/cgred.conf
|
||||
ExecStart=/usr/sbin/cgrulesengd
|
||||
Restart=on-failure
|
||||
|
||||
[Service]
|
||||
User=root
|
||||
Group=root
|
||||
Type=forking
|
||||
EnvironmentFile=-/etc/cgred.conf
|
||||
ExecStart=/usr/sbin/cgrulesengd
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target" >/etc/systemd/system/$cgrulesengd_service.service
|
||||
|
||||
systemctl daemon-reload
|
||||
[Install]
|
||||
WantedBy=multi-user.target" >$cgrulesengd_file
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
systemctl enable $cgrulesengd_service
|
||||
systemctl start $cgrulesengd_service
|
||||
|
||||
# -------------------------------
|
||||
stage "Configuring grub"
|
||||
|
||||
# Enable cgroup memory and swapaccount if not already configured
|
||||
# We create a temp of the grub file and replace with original file only if success.
|
||||
@@ -201,7 +213,7 @@ if [ $res -eq 100 ]; then
|
||||
sed -n -r -e "/^GRUB_CMDLINE_LINUX_DEFAULT=/{ /swapaccount=1/{q100}; }" "$tmpgrub"
|
||||
res=$?
|
||||
if [ $res -eq 0 ]; then
|
||||
# Check whether there's swapaccount value other that 1, If so replace value with 1.
|
||||
# Check whether there's swapaccount value other than 1, If so replace value with 1.
|
||||
# Otherwise add swapaccount=1 after cgroup_enable=memory.
|
||||
sed -n -r -e "/^GRUB_CMDLINE_LINUX_DEFAULT=/{ /swapaccount=/{q100}; }" "$tmpgrub"
|
||||
res=$?
|
||||
@@ -236,17 +248,14 @@ if [ $updated -eq 1 ]; then
|
||||
mv $grub_backup /etc/default/grub
|
||||
echo "Grub update failed."
|
||||
exit 1
|
||||
fi
|
||||
echo "Updated grub. System needs to be rebooted to apply grub changes."
|
||||
|
||||
if [ "$quiet" != "-q" ]; then
|
||||
read -p "Type 'yes' to reboot: " confirmation < /dev/tty
|
||||
[ "$confirmation" != "yes" ] && echo "Rebooting cancelled." && exit 0
|
||||
fi
|
||||
|
||||
echo "Rebooting..."
|
||||
reboot
|
||||
# Indicate pending reboot in the standard reboot required file.
|
||||
touch /run/reboot-required
|
||||
rebootpkgs=/run/reboot-required.pkgs
|
||||
(! [ -f $rebootpkgs ] || [ -z "$(grep sashimono $rebootpkgs)" ]) && echo "sashimono" >>$rebootpkgs
|
||||
|
||||
echo "Updated grub. System needs to be rebooted to apply grub changes."
|
||||
else
|
||||
rm -r "$tmp"
|
||||
echo "Grub already configured."
|
||||
@@ -8,6 +8,19 @@ hubacc="hotpocketdev"
|
||||
images=("sashimono:hp.latest-ubt.20.04" "sashimono:hp.latest-ubt.20.04-njs.14")
|
||||
user_dir=/home/$user
|
||||
|
||||
# Waits until a service becomes ready up to 3 seconds.
|
||||
function service_ready() {
|
||||
local svcstat=""
|
||||
for ((i = 0; i < 30; i++)); do
|
||||
sleep 0.1
|
||||
svcstat=$(sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user is-active $1)
|
||||
if [ "$svcstat" == "active" ] ; then
|
||||
return 0 # Success
|
||||
fi
|
||||
done
|
||||
return 1 # Error
|
||||
}
|
||||
|
||||
# Check if users already exists.
|
||||
[ "$(id -u "$user" 2>/dev/null || echo -1)" -ge 0 ] && echo "$user already exists." && exit 1
|
||||
|
||||
@@ -39,9 +52,7 @@ done
|
||||
|
||||
echo "Installing rootless dockerd for user."
|
||||
sudo -H -u "$user" PATH="$docker_bin":"$PATH" XDG_RUNTIME_DIR="$user_runtime_dir" "$docker_bin"/dockerd-rootless-setuptool.sh install
|
||||
|
||||
svcstat=$(sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user is-active docker.service)
|
||||
[ "$svcstat" != "active" ] && rollback "NO_DOCKERSVC"
|
||||
service_ready "docker.service" || rollback "NO_DOCKERSVC"
|
||||
|
||||
echo "Installed rootless dockerd for docker registry."
|
||||
|
||||
|
||||
@@ -1,181 +1,89 @@
|
||||
#!/bin/bash
|
||||
# Sashimono agent installation script.
|
||||
# This must be executed with root privileges.
|
||||
# -q for non-interactive (quiet) mode (This will skip the installation of xrpl message board)
|
||||
|
||||
user_bin=/usr/bin
|
||||
sashimono_bin=/usr/bin/sashimono-agent
|
||||
mb_xrpl_bin=$sashimono_bin/mb-xrpl
|
||||
docker_bin=$sashimono_bin/dockerbin
|
||||
sashimono_data=/etc/sashimono
|
||||
mb_xrpl_data=$sashimono_data/mb-xrpl
|
||||
sashimono_service="sashimono-agent"
|
||||
cgcreate_service="sashimono-cgcreate"
|
||||
mb_xrpl_service="sashimono-mb-xrpl"
|
||||
hook_address="rntPzkVidFxnymL98oF3RAFhhBSmsyB5HP"
|
||||
group="sashiuser"
|
||||
admin_group="sashiadmin"
|
||||
mb_user="sashimbxrpl"
|
||||
cgroupsuffix="-cg"
|
||||
registryuser="sashidockerreg"
|
||||
registryport=4444
|
||||
echo "---Sashimono installer---"
|
||||
|
||||
inetaddr=$1
|
||||
countrycode=$2
|
||||
inst_count=$3
|
||||
cpuMicroSec=$4
|
||||
ramKB=$5
|
||||
swapKB=$6
|
||||
diskKB=$7
|
||||
description=$8
|
||||
|
||||
script_dir=$(dirname "$(realpath "$0")")
|
||||
def_cgrulesengd_service="cgrulesengdsvc"
|
||||
quiet=$1
|
||||
|
||||
[ -d $sashimono_bin ] && [ -n "$(ls -A $sashimono_bin)" ] &&
|
||||
echo "Aborting installation. Previous Sashimono installation detected at $sashimono_bin" && exit 1
|
||||
function stage() {
|
||||
echo "STAGE $1" # This is picked up by the setup console output filter.
|
||||
}
|
||||
|
||||
# Check cgroup rule config exists.
|
||||
[ ! -f /etc/cgred.conf ] && echo "Cgroup is not configured. Make sure you've installed and configured cgroup-tools." && exit 1
|
||||
|
||||
# Create bin dirs first so it automatically checks for privileged access.
|
||||
mkdir -p $sashimono_bin
|
||||
[ "$?" == "1" ] && echo "Could not create '$sashimono_bin'. Make sure you are running as sudo." && exit 1
|
||||
mkdir -p $docker_bin
|
||||
[ "$?" == "1" ] && echo "Could not create '$docker_bin'. Make sure you are running as sudo." && exit 1
|
||||
mkdir -p $sashimono_data
|
||||
[ "$?" == "1" ] && echo "Could not create '$sashimono_data'. Make sure you are running as sudo." && exit 1
|
||||
|
||||
echo "Installing Sashimono..."
|
||||
|
||||
# Install curl if not exists (required to download installation artifacts).
|
||||
if ! command -v curl &>/dev/null; then
|
||||
apt-get install -y curl
|
||||
fi
|
||||
|
||||
# Install openssl if not exists (required by Sashimono agent to create contract tls certs).
|
||||
if ! command -v openssl &>/dev/null; then
|
||||
apt-get install -y openssl
|
||||
fi
|
||||
|
||||
# Blake3
|
||||
if [ ! -f /usr/local/lib/libblake3.so ]; then
|
||||
cp "$script_dir"/libblake3.so /usr/local/lib/
|
||||
fi
|
||||
|
||||
# jq command is used for json manipulation.
|
||||
if ! command -v jq &>/dev/null; then
|
||||
apt-get install -y jq
|
||||
fi
|
||||
|
||||
# Libfuse
|
||||
apt-get install -y fuse3
|
||||
|
||||
# Update linker library cache.
|
||||
sudo ldconfig
|
||||
|
||||
function rollback() {
|
||||
echo "Rolling back sashimono installation."
|
||||
"$script_dir"/sashimono-uninstall.sh -q # Quiet uninstall.
|
||||
"$script_dir"/sashimono-uninstall.sh
|
||||
echo "Rolled back the installation."
|
||||
exit 1
|
||||
}
|
||||
|
||||
mkdir -p $SASHIMONO_BIN
|
||||
mkdir -p $DOCKER_BIN
|
||||
mkdir -p $SASHIMONO_DATA
|
||||
|
||||
# Install Sashimono agent binaries into sashimono bin dir.
|
||||
cp "$script_dir"/{sagent,hpfs,user-cgcreate.sh,user-install.sh,user-uninstall.sh} $sashimono_bin
|
||||
chmod -R +x $sashimono_bin
|
||||
cp "$script_dir"/{sagent,hpfs,user-cgcreate.sh,user-install.sh,user-uninstall.sh,sashimono-uninstall.sh} $SASHIMONO_BIN
|
||||
chmod -R +x $SASHIMONO_BIN
|
||||
|
||||
# Blake3
|
||||
[ ! -f /usr/local/lib/libblake3.so ] && cp "$script_dir"/libblake3.so /usr/local/lib/
|
||||
# Update linker library cache.
|
||||
ldconfig
|
||||
|
||||
# Install Sashimono CLI binaries into user bin dir.
|
||||
cp "$script_dir"/sashi $user_bin
|
||||
cp "$script_dir"/sashi $USER_BIN
|
||||
|
||||
# Download and install rootless dockerd.
|
||||
"$script_dir"/docker-install.sh $docker_bin
|
||||
stage "Installing docker packages"
|
||||
"$script_dir"/docker-install.sh $DOCKER_BIN
|
||||
|
||||
# Check whether docker installation dir is still empty.
|
||||
[ -z "$(ls -A $docker_bin 2>/dev/null)" ] && echo "Rootless Docker installation failed." && rollback
|
||||
|
||||
# Detect self host address
|
||||
selfip=$(hostname -I)
|
||||
[ -z "$(ls -A $DOCKER_BIN 2>/dev/null)" ] && echo "Rootless Docker installation failed." && rollback
|
||||
|
||||
# Install private docker registry.
|
||||
# stage "Installing private docker registry"
|
||||
# (Disabled until secure registry configuration)
|
||||
# ./registry-install.sh $docker_bin $registryuser $registryport
|
||||
# ./registry-install.sh $DOCKER_BIN $REGISTRY_USER $REGISTRY_PORT
|
||||
# [ "$?" == "1" ] && rollback
|
||||
# registry_addr=$selfip:$registryport
|
||||
# registry_addr=$inetaddr:$REGISTRY_PORT
|
||||
|
||||
# Setting up Sashimono admin group.
|
||||
! groupadd $admin_group && echo "Admin group creation failed." && rollback
|
||||
! groupadd $SASHIADMIN_GROUP && echo "Admin group creation failed." && rollback
|
||||
# If installing with sudo, add current logged-in user to Sashimono admin group.
|
||||
[ -n "$SUDO_USER" ] && usermod -a -G $SASHIADMIN_GROUP $SUDO_USER
|
||||
|
||||
# Setup Sashimono data dir.
|
||||
cp -r "$script_dir"/contract_template $sashimono_data
|
||||
cp -r "$script_dir"/contract_template $SASHIMONO_DATA
|
||||
|
||||
if [ "$quiet" == "-q" ]; then
|
||||
stage "Configuring Sashimono services"
|
||||
|
||||
# We are in the quiet mode. Hence we auto-generate an XRPL test account and token details for the host.
|
||||
# (This is done for testing purposes during development)
|
||||
|
||||
xrpl_faucet_url="https://hooks-testnet.xrpl-labs.com/newcreds"
|
||||
hook_secret="shgNKT14iCV6S4HdT9r7mgqyx94Xt"
|
||||
func_url="https://func-hotpocket.azurewebsites.net/api/evrfaucet?code=pPUyV1q838ryrihA5NVlobVXj8ZGgn9HsQjGGjl6Vhgxlfha4/xCgQ=="
|
||||
|
||||
# Generate new fauset account.
|
||||
echo "Generating XRP faucet account..."
|
||||
new_acc=$(curl -X POST $xrpl_faucet_url)
|
||||
# If result is not a json, account generation failed.
|
||||
[[ ! "$new_acc" =~ \{.+\} ]] && echo "Xrpl faucet account generation failed." && rollback
|
||||
xrp_address=$(echo $new_acc | jq -r '.address')
|
||||
xrp_secret=$(echo $new_acc | jq -r '.secret')
|
||||
([ "$xrp_address" == "" ] || [ "$xrp_address" == "null" ] ||
|
||||
[ "$xrp_secret" == "" ] || [ "$xrp_secret" == "null" ]) && echo "Invalid generated xrpl account details: $new_acc" && rollback
|
||||
|
||||
# Wait a small interval so the XRP account gets replicated in the testnet (otherwise we may get 'Account not found' errors).
|
||||
sleep 4
|
||||
|
||||
# Setup the host xrpl account with an EVR balance and default rippling flag.
|
||||
echo "Setting up host XRP account..."
|
||||
acc_setup_func="$func_url&action=setuphost&hookaddr=$hook_address&hooksecret=$hook_secret&addr=$xrp_address&secret=$xrp_secret"
|
||||
func_code=$(curl -o /dev/null -s -w "%{http_code}\n" -d "" -X POST "$acc_setup_func")
|
||||
[ "$func_code" != "200" ] && echo "Host XRP account setup failed. code:$func_code" && rollback
|
||||
|
||||
# Generate random hosting token.
|
||||
token=$(tr -dc A-Z </dev/urandom | head -c 3)
|
||||
|
||||
echo "Auto-generated host information."
|
||||
|
||||
else
|
||||
|
||||
echo "Please answer following questions to setup Evernode xrpl message board."
|
||||
# Ask for input until a correct value is given
|
||||
while [[ ! "$token" =~ ^[A-Z]{3}$ ]]; do
|
||||
read -p "Token name? " token </dev/tty
|
||||
[[ ! "$token" =~ ^[A-Z]{3}$ ]] && echo "Token name should be 3 UPPERCASE letters."
|
||||
done
|
||||
while [[ -z "$xrp_address" ]]; do
|
||||
read -p "XRPL account address? " xrp_address </dev/tty
|
||||
done
|
||||
while [[ -z "$xrp_secret" ]]; do
|
||||
read -p "XRPL account secret? " xrp_secret </dev/tty
|
||||
done
|
||||
# Ask for cgroup rule generator service until a valid service provided.
|
||||
while true; do
|
||||
read -p "Enter your cgroup rule generator service name (default: $def_cgrulesengd_service)? " cgrulesengd_service </dev/tty
|
||||
# Set service name to default if user input is empty.
|
||||
[ -z "$cgrulesengd_service" ] && cgrulesengd_service="$def_cgrulesengd_service"
|
||||
# Remove '.service' if user has given the full name.
|
||||
cgrulesengd_service=$(echo $cgrulesengd_service | awk '{print tolower($0)}' | sed 's/\.service$//')
|
||||
# Break the loop if service is valid and exist.
|
||||
[ -f /etc/systemd/system/"$cgrulesengd_service".service ] && break
|
||||
echo "$cgrulesengd_service systemd service does not exist."
|
||||
done
|
||||
fi
|
||||
|
||||
# Set cgrulesengd_service to default if it's still empty.
|
||||
if [[ -z "$cgrulesengd_service" ]]; then
|
||||
cgrulesengd_service="$def_cgrulesengd_service"
|
||||
[ ! -f /etc/systemd/system/"$cgrulesengd_service".service ] && echo "$cgrulesengd_service systemd service does not exist." && rollback
|
||||
fi
|
||||
# Find the cgroups rules engine service.
|
||||
cgrulesengd_filename=$(basename $(grep "ExecStart.*=.*/cgrulesengd$" /etc/systemd/system/*.service | head -1 | awk -F : ' { print $1 } '))
|
||||
cgrulesengd_service="${cgrulesengd_filename%.*}"
|
||||
[ -z "$cgrulesengd_service" ] && echo "cgroups rules engine service does not exist." && rollback
|
||||
|
||||
# Setting up cgroup rules.
|
||||
echo "Creating cgroup rules..."
|
||||
! groupadd $group && echo "Group creation failed." && rollback
|
||||
! echo "@$group cpu,memory %u$cgroupsuffix" >>/etc/cgrules.conf && echo "Cgroup rule creation failed." && rollback
|
||||
! groupadd $SASHIUSER_GROUP && echo "Group creation failed." && rollback
|
||||
! echo "@$SASHIUSER_GROUP cpu,memory %u$CG_SUFFIX" >>/etc/cgrules.conf && echo "Cgroup rule creation failed." && rollback
|
||||
# Restart the service to apply the cgrules config.
|
||||
echo "Restarting the $cgrulesengd_service.service."
|
||||
echo "Restarting the '$cgrulesengd_service' service."
|
||||
systemctl restart $cgrulesengd_service || rollback
|
||||
|
||||
# Install Sashimono Agent cgcreate service.
|
||||
# This is a onshot service which runs only once.
|
||||
# This is a oneshot service which runs only once.
|
||||
echo "[Unit]
|
||||
Description=Sashimono cgroup creation service.
|
||||
After=network.target
|
||||
@@ -183,14 +91,14 @@ After=network.target
|
||||
User=root
|
||||
Group=root
|
||||
Type=oneshot
|
||||
ExecStart=$sashimono_bin/user-cgcreate.sh $sashimono_data
|
||||
ExecStart=$SASHIMONO_BIN/user-cgcreate.sh $SASHIMONO_DATA
|
||||
[Install]
|
||||
WantedBy=multi-user.target" >/etc/systemd/system/$cgcreate_service.service
|
||||
WantedBy=multi-user.target" >/etc/systemd/system/$CGCREATE_SERVICE.service
|
||||
|
||||
# Install xrpl message board systemd service.
|
||||
echo "Initiating the sashimono agent..."
|
||||
echo "Configuring sashimono agent service..."
|
||||
# Rollback if 'sagent new' failed.
|
||||
$sashimono_bin/sagent new $sashimono_data $cgrulesengd_service $selfip $registry_addr || rollback
|
||||
$SASHIMONO_BIN/sagent new $SASHIMONO_DATA $inetaddr $inst_count $cpuMicroSec $ramKB $swapKB $diskKB || rollback
|
||||
|
||||
# Install Sashimono Agent systemd service.
|
||||
# StartLimitIntervalSec=0 to make unlimited retries. RestartSec=5 is to keep 5 second gap between restarts.
|
||||
@@ -202,41 +110,40 @@ StartLimitIntervalSec=0
|
||||
User=root
|
||||
Group=root
|
||||
Type=simple
|
||||
WorkingDirectory=$sashimono_bin
|
||||
ExecStart=$sashimono_bin/sagent run $sashimono_data
|
||||
WorkingDirectory=$SASHIMONO_BIN
|
||||
ExecStart=$SASHIMONO_BIN/sagent run $SASHIMONO_DATA
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
[Install]
|
||||
WantedBy=multi-user.target" >/etc/systemd/system/$sashimono_service.service
|
||||
WantedBy=multi-user.target" >/etc/systemd/system/$SASHIMONO_SERVICE.service
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable $cgcreate_service
|
||||
systemctl start $cgcreate_service
|
||||
systemctl enable $sashimono_service
|
||||
systemctl start $sashimono_service
|
||||
systemctl enable $CGCREATE_SERVICE
|
||||
systemctl start $CGCREATE_SERVICE
|
||||
systemctl enable $SASHIMONO_SERVICE
|
||||
systemctl start $SASHIMONO_SERVICE
|
||||
# Both of these services needed to be restarted if sa.cfg max instance resources are manually changed.
|
||||
|
||||
# Install xrpl message board systemd service.
|
||||
echo "Installing Evernode xrpl message board..."
|
||||
|
||||
cp -r "$script_dir"/mb-xrpl $sashimono_bin
|
||||
|
||||
cp -r "$script_dir"/mb-xrpl $SASHIMONO_BIN
|
||||
|
||||
# Creating message board user.
|
||||
useradd --shell /usr/sbin/nologin -m "$mb_user"
|
||||
usermod --lock "$mb_user"
|
||||
usermod -a -G $admin_group "$mb_user"
|
||||
loginctl enable-linger "$mb_user" # Enable lingering to support service installation.
|
||||
useradd --shell /usr/sbin/nologin -m $MB_XRPL_USER
|
||||
usermod --lock $MB_XRPL_USER
|
||||
usermod -a -G $SASHIADMIN_GROUP $MB_XRPL_USER
|
||||
loginctl enable-linger $MB_XRPL_USER # Enable lingering to support service installation.
|
||||
|
||||
# First create the folder from root and then transfer ownership to the user
|
||||
# since the folder is created in /etc/sashimono directory.
|
||||
mkdir -p $mb_xrpl_data
|
||||
[ "$?" == "1" ] && echo "Could not create '$mb_xrpl_data'. Make sure you are running as sudo." && exit 1
|
||||
mkdir -p $MB_XRPL_DATA
|
||||
[ "$?" == "1" ] && echo "Could not create '$MB_XRPL_DATA'. Make sure you are running as sudo." && exit 1
|
||||
# Change ownership to message board user.
|
||||
chown "$mb_user":"$mb_user" $mb_xrpl_data
|
||||
chown "$MB_XRPL_USER":"$MB_XRPL_USER" $MB_XRPL_DATA
|
||||
|
||||
mb_user_dir=/home/"$mb_user"
|
||||
mb_user_id=$(id -u "$mb_user")
|
||||
mb_user_dir=/home/"$MB_XRPL_USER"
|
||||
mb_user_id=$(id -u "$MB_XRPL_USER")
|
||||
mb_user_runtime_dir="/run/user/$mb_user_id"
|
||||
|
||||
# Setup env variable for the message board user.
|
||||
@@ -247,17 +154,22 @@ echo "Updated mb user .bashrc."
|
||||
user_systemd=""
|
||||
for ((i = 0; i < 30; i++)); do
|
||||
sleep 0.1
|
||||
user_systemd=$(sudo -u "$mb_user" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user is-system-running 2>/dev/null)
|
||||
user_systemd=$(sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user is-system-running 2>/dev/null)
|
||||
[ "$user_systemd" == "running" ] && break
|
||||
done
|
||||
[ "$user_systemd" != "running" ] && echo "NO_MB_USER_SYSTEMD" && rollback
|
||||
|
||||
# Populate the message board config file.
|
||||
# Run as the message board user.
|
||||
sudo -u $mb_user MB_DATA_DIR=$mb_xrpl_data node $mb_xrpl_bin new "$xrp_address" "$xrp_secret" $hook_address "$token"
|
||||
# Generate beta host account.
|
||||
stage "Configuring host xrpl account"
|
||||
! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN betagen $HOOK_ADDRESS && echo "XRPLACC_FAILURE" && rollback
|
||||
# Register the host on Evernode.
|
||||
stage "Registering host on Evernode"
|
||||
! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN register \
|
||||
$countrycode $cpuMicroSec $ramKB $swapKB $diskKB $description && echo "REG_FAILURE" && rollback
|
||||
|
||||
! (sudo -u $mb_user mkdir -p "$mb_user_dir"/.config/systemd/user/) && echo "user systemd folder creation failed" && rollback
|
||||
! (sudo -u $MB_XRPL_USER mkdir -p "$mb_user_dir"/.config/systemd/user/) && echo "Message board user systemd folder creation failed" && rollback
|
||||
|
||||
stage "Configuring xrpl message board service"
|
||||
# StartLimitIntervalSec=0 to make unlimited retries. RestartSec=5 is to keep 5 second gap between restarts.
|
||||
echo "[Unit]
|
||||
Description=Running and monitoring evernode xrpl transactions.
|
||||
@@ -265,18 +177,18 @@ After=network.target
|
||||
StartLimitIntervalSec=0
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=$mb_xrpl_bin
|
||||
Environment=\"MB_DATA_DIR=$mb_xrpl_data\"
|
||||
ExecStart=/usr/bin/node $mb_xrpl_bin
|
||||
WorkingDirectory=$MB_XRPL_BIN
|
||||
Environment=\"MB_DATA_DIR=$MB_XRPL_DATA\"
|
||||
ExecStart=/usr/bin/node $MB_XRPL_BIN
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
[Install]
|
||||
WantedBy=default.target" | sudo -u $mb_user tee "$mb_user_dir"/.config/systemd/user/$mb_xrpl_service.service > /dev/null
|
||||
WantedBy=default.target" | sudo -u $MB_XRPL_USER tee "$mb_user_dir"/.config/systemd/user/$MB_XRPL_SERVICE.service > /dev/null
|
||||
|
||||
# This service needs to be restarted when mb-xrpl.cfg is changed.
|
||||
sudo -u "$mb_user" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user enable $mb_xrpl_service
|
||||
sudo -u "$mb_user" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user start $mb_xrpl_service
|
||||
sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user enable $MB_XRPL_SERVICE
|
||||
sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user start $MB_XRPL_SERVICE
|
||||
echo "Installed Evernode xrpl message board."
|
||||
|
||||
echo "Sashimono installed successfully."
|
||||
exit 0
|
||||
exit 0
|
||||
@@ -1,142 +1,91 @@
|
||||
#!/bin/bash
|
||||
# Sashimono agent uninstall script.
|
||||
# -q for non-interactive (quiet) mode
|
||||
# This must be executed with root privileges.
|
||||
|
||||
user_bin=/usr/bin
|
||||
sashimono_bin=/usr/bin/sashimono-agent
|
||||
mb_xrpl_bin=$sashimono_bin/mb-xrpl
|
||||
docker_bin=$sashimono_bin/dockerbin
|
||||
sashimono_data=/etc/sashimono
|
||||
sashimono_conf=$sashimono_data/sa.cfg
|
||||
mb_xrpl_data=$sashimono_data/mb-xrpl
|
||||
sashimono_service="sashimono-agent"
|
||||
cgcreate_service="sashimono-cgcreate"
|
||||
mb_xrpl_service="sashimono-mb-xrpl"
|
||||
registryuser="sashidockerreg"
|
||||
mb_user="sashimbxrpl"
|
||||
group="sashiuser"
|
||||
admin_group="sashiadmin"
|
||||
cgroupsuffix="-cg"
|
||||
quiet=$1
|
||||
echo "---Sashimono uninstaller---"
|
||||
|
||||
[ ! -d $sashimono_bin ] && echo "$sashimono_bin does not exist. Aborting uninstall." && exit 1
|
||||
[ ! -d $SASHIMONO_BIN ] && echo "$SASHIMONO_BIN does not exist. Aborting uninstall." && exit 1
|
||||
|
||||
if [ "$quiet" != "-q" ]; then
|
||||
echo "Are you sure you want to uninstall Sashimono?"
|
||||
read -p "Type 'yes' to confirm uninstall: " confirmation </dev/tty
|
||||
[ "$confirmation" != "yes" ] && echo "Uninstall cancelled." && exit 0
|
||||
fi
|
||||
|
||||
# Get the cgrules service from the config.
|
||||
cgrulesengd_service=$(jq -r '.service.cgrulesengd' $sashimono_conf | awk '{print tolower($0)}' | sed 's/\.service$//')
|
||||
[ ! -f /etc/systemd/system/"$cgrulesengd_service".service ] && echo "Warning: $cgrulesengd_service systemd service does not exist."
|
||||
# Find the cgroups rules engine service.
|
||||
cgrulesengd_filename=$(basename $(grep "ExecStart.*=.*/cgrulesengd$" /etc/systemd/system/*.service | head -1 | awk -F : ' { print $1 } '))
|
||||
cgrulesengd_service="${cgrulesengd_filename%.*}"
|
||||
[ -z "$cgrulesengd_service" ] && echo "Warning: cgroups rules engine service does not exist."
|
||||
|
||||
# Message board user.
|
||||
mb_user_dir=/home/"$mb_user"
|
||||
mb_user_id=$(id -u "$mb_user")
|
||||
mb_user_dir=/home/"$MB_XRPL_USER"
|
||||
mb_user_id=$(id -u "$MB_XRPL_USER")
|
||||
mb_user_runtime_dir="/run/user/$mb_user_id"
|
||||
# Remove xrpl message board service if exists.
|
||||
if [ -f "$mb_user_dir"/.config/systemd/user/$mb_xrpl_service.service ]; then
|
||||
sudo -u "$mb_user" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user stop $mb_xrpl_service
|
||||
sudo -u "$mb_user" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user disable $mb_xrpl_service
|
||||
fi
|
||||
if [ "$quiet" == "-q" ]; then
|
||||
# We only perform this for our testing setup during development.
|
||||
echo "Cleaning up host XRP account..."
|
||||
|
||||
hook_address="rntPzkVidFxnymL98oF3RAFhhBSmsyB5HP"
|
||||
func_url="https://func-hotpocket.azurewebsites.net/api/evrfaucet?code=pPUyV1q838ryrihA5NVlobVXj8ZGgn9HsQjGGjl6Vhgxlfha4/xCgQ=="
|
||||
|
||||
mb_xrpl_conf=$mb_xrpl_data/mb-xrpl.cfg
|
||||
xrp_address=$(jq -r '.xrpl.address' $mb_xrpl_conf)
|
||||
xrp_secret=$(jq -r '.xrpl.secret' $mb_xrpl_conf)
|
||||
|
||||
if [ "$xrp_address" != "" ] && [ "$xrp_secret" != "" ]; then
|
||||
acc_clean_func="$func_url&action=cleanhost&hookaddr=$hook_address&addr=$xrp_address&secret=$xrp_secret"
|
||||
func_code=$(curl -o /dev/null -s -w "%{http_code}\n" -d "" -X POST $acc_clean_func)
|
||||
[ "$func_code" != "200" ] && echo "Host XRP account cleanup failed. code:$func_code"
|
||||
[ "$func_code" == "200" ] && echo "Cleaned up host XRP account."
|
||||
fi
|
||||
if [ -f "$mb_user_dir"/.config/systemd/user/$MB_XRPL_SERVICE.service ]; then
|
||||
sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user stop $MB_XRPL_SERVICE
|
||||
sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user disable $MB_XRPL_SERVICE
|
||||
fi
|
||||
|
||||
# Deregister evernode message board host registration.
|
||||
echo "Attempting Evernode xrpl message board host deregistration..."
|
||||
sudo -u $mb_user MB_DATA_DIR=$mb_xrpl_data MB_LOG=1 MB_DEREGISTER=1 node $mb_xrpl_bin
|
||||
sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN deregister
|
||||
|
||||
echo "Deleting message board user..."
|
||||
killall -u $mb_user # Kill any running processes.
|
||||
userdel -f "$mb_user"
|
||||
rm -r /home/"${mb_user:?}"
|
||||
killall -u $MB_XRPL_USER # Kill any running processes.
|
||||
userdel -f "$MB_XRPL_USER"
|
||||
rm -r /home/"${MB_XRPL_USER:?}"
|
||||
|
||||
# Uninstall all contract instance users
|
||||
prefix="sashi"
|
||||
users=$(cut -d: -f1 /etc/passwd | grep "^$prefix" | sort)
|
||||
users=$(cut -d: -f1 /etc/passwd | grep "^$SASHIUSER_PREFIX" | sort)
|
||||
readarray -t userarr <<<"$users"
|
||||
validusers=()
|
||||
sashiusers=()
|
||||
for user in "${userarr[@]}"; do
|
||||
[ ${#user} -lt 24 ] || [ ${#user} -gt 32 ] || [[ ! "$user" =~ ^$prefix[0-9]+$ ]] && continue
|
||||
validusers+=("$user")
|
||||
[ ${#user} -lt 24 ] || [ ${#user} -gt 32 ] || [[ ! "$user" =~ ^$SASHIUSER_PREFIX[0-9]+$ ]] && continue
|
||||
sashiusers+=("$user")
|
||||
done
|
||||
|
||||
ucount=${#validusers[@]}
|
||||
ucount=${#sashiusers[@]}
|
||||
if [ $ucount -gt 0 ]; then
|
||||
|
||||
echo "Detected $ucount Sashimono contract instances."
|
||||
for user in "${validusers[@]}"; do
|
||||
echo "Detected $ucount contract instances."
|
||||
for user in "${sashiusers[@]}"; do
|
||||
echo "$user"
|
||||
done
|
||||
|
||||
if [ "$quiet" != "-q" ]; then
|
||||
echo "Are you sure you want to delete all $ucount Sashimono contract instances?"
|
||||
read -p "Type $ucount to confirm deletion:" confirmation </dev/tty
|
||||
else
|
||||
confirmation="$ucount"
|
||||
fi
|
||||
|
||||
if [ "$confirmation" == "$ucount" ]; then
|
||||
echo "Deleting $ucount contract instances..."
|
||||
for user in "${validusers[@]}"; do
|
||||
output=$($sashimono_bin/user-uninstall.sh $user | tee /dev/stderr)
|
||||
[ "${output: -10}" != "UNINST_SUC" ] && echo "Uninstall user '$user' failed. Aborting." && exit 1
|
||||
done
|
||||
else
|
||||
echo "Uninstall cancelled."
|
||||
exit 0
|
||||
fi
|
||||
echo "Deleting $ucount contract instances..."
|
||||
for user in "${sashiusers[@]}"; do
|
||||
output=$($SASHIMONO_BIN/user-uninstall.sh $user | tee /dev/stderr)
|
||||
[ "${output: -10}" != "UNINST_SUC" ] && echo "Uninstall user '$user' failed. Aborting." && exit 1
|
||||
done
|
||||
fi
|
||||
|
||||
echo "Removing Sashimono cgroup creation service..."
|
||||
systemctl stop $cgcreate_service
|
||||
systemctl disable $cgcreate_service
|
||||
rm /etc/systemd/system/$cgcreate_service.service
|
||||
systemctl stop $CGCREATE_SERVICE
|
||||
systemctl disable $CGCREATE_SERVICE
|
||||
rm /etc/systemd/system/$CGCREATE_SERVICE.service
|
||||
|
||||
echo "Removing Sashimono service..."
|
||||
systemctl stop $sashimono_service
|
||||
systemctl disable $sashimono_service
|
||||
rm /etc/systemd/system/$sashimono_service.service
|
||||
systemctl stop $SASHIMONO_SERVICE
|
||||
systemctl disable $SASHIMONO_SERVICE
|
||||
rm /etc/systemd/system/$SASHIMONO_SERVICE.service
|
||||
|
||||
# echo "Removing Sashimono private docker registry..."
|
||||
# ./registry-uninstall.sh $docker_bin $registryuser
|
||||
# ./registry-uninstall.sh $DOCKER_BIN $REGISTRY_USER
|
||||
|
||||
echo "Deleting binaries..."
|
||||
rm -r $sashimono_bin
|
||||
rm -r $SASHIMONO_BIN
|
||||
|
||||
echo "Deleting Sashimono CLI..."
|
||||
rm $user_bin/sashi
|
||||
rm $USER_BIN/sashi
|
||||
|
||||
echo "Deleting data folder..."
|
||||
rm -r $sashimono_data
|
||||
rm -r $SASHIMONO_DATA
|
||||
|
||||
# When removing the cgrule,
|
||||
# We first edit the config and restart the service to apply the config.
|
||||
# Then we remove the attached group.
|
||||
echo "Deleting cgroup rules..."
|
||||
sed -i -r "/^@$group\s+cpu,memory\s+%u$cgroupsuffix/d" /etc/cgrules.conf
|
||||
echo "Restarting the $cgrulesengd_service.service."
|
||||
sed -i -r "/^@$SASHIUSER_GROUP\s+cpu,memory\s+%u$CG_SUFFIX/d" /etc/cgrules.conf
|
||||
echo "Restarting the '$cgrulesengd_service' service..."
|
||||
systemctl restart $cgrulesengd_service
|
||||
groupdel $group
|
||||
groupdel $SASHIUSER_GROUP
|
||||
|
||||
groupdel $admin_group
|
||||
groupdel $SASHIADMIN_GROUP
|
||||
|
||||
echo "Sashimono uninstalled successfully."
|
||||
exit 0
|
||||
exit 0
|
||||
414
installer/setup.sh
Executable file
414
installer/setup.sh
Executable file
@@ -0,0 +1,414 @@
|
||||
#!/bin/bash
|
||||
# Evernode host setup tool to manage Sashimono installation and host registration.
|
||||
# usage: ./setup.sh install
|
||||
|
||||
evernode="Evernode beta"
|
||||
maxmind_creds="653000:0yB7wwsBqCiPO2m6"
|
||||
cgrulesengd_default="cgrulesengd"
|
||||
alloc_ratio=80
|
||||
memKB_per_instance=819200
|
||||
evernode_alias=/usr/bin/evernode
|
||||
log_dir=/tmp/evernode-beta
|
||||
script_url="https://sthotpocket.blob.core.windows.net/evernode/setup.sh"
|
||||
installer="https://sthotpocket.blob.core.windows.net/evernode/sashimono-installer.tar.gz"
|
||||
|
||||
# export vars used by Sashimono installer.
|
||||
export USER_BIN=/usr/bin
|
||||
export SASHIMONO_BIN=/usr/bin/sashimono
|
||||
export MB_XRPL_BIN=$SASHIMONO_BIN/mb-xrpl
|
||||
export DOCKER_BIN=$SASHIMONO_BIN/dockerbin
|
||||
export SASHIMONO_DATA=/etc/sashimono
|
||||
export MB_XRPL_DATA=$SASHIMONO_DATA/mb-xrpl
|
||||
export SASHIMONO_SERVICE="sashimono-agent"
|
||||
export CGCREATE_SERVICE="sashimono-cgcreate"
|
||||
export MB_XRPL_SERVICE="sashimono-mb-xrpl"
|
||||
export SASHIADMIN_GROUP="sashiadmin"
|
||||
export SASHIUSER_GROUP="sashiuser"
|
||||
export SASHIUSER_PREFIX="sashi"
|
||||
export MB_XRPL_USER="sashimbxrpl"
|
||||
export REGISTRY_USER="sashidockerreg"
|
||||
export CG_SUFFIX="-cg"
|
||||
export REGISTRY_PORT=4444
|
||||
export HOOK_ADDRESS="rKt4W57Rmh8k9HNCYLc2AvFD9rAEkF5RKP"
|
||||
|
||||
[ -f $SASHIMONO_DATA/sa.cfg ] && sashimono_installed=true || sashimono_installed=false
|
||||
|
||||
# Helper to print multi line text.
|
||||
# (When passed as a parameter, bash auto strips spaces and indentation which is what we want)
|
||||
function echomult() {
|
||||
echo -e $1
|
||||
}
|
||||
|
||||
# The set of commands supported differs based on whether Sashimono is installed or not.
|
||||
if ! $sashimono_installed ; then
|
||||
[ "$1" != "install" ] \
|
||||
&& echomult "$evernode host management tool
|
||||
\nYour system is not registered on $evernode.
|
||||
\nSupported commands:
|
||||
\ninstall - Install Sashimono and register on $evernode" \
|
||||
&& exit 1
|
||||
else
|
||||
[ "$1" != "uninstall" ] && [ "$1" != "status" ] && [ "$1" != "list" ] \
|
||||
&& echomult "$evernode host management tool
|
||||
\nYour system is registered on $evernode.
|
||||
\nSupported commands:
|
||||
\nstatus - View $evernode registration info
|
||||
\nlist - View contract instances running on this system
|
||||
\nuninstall - Uninstall and deregister from $evernode" \
|
||||
&& exit 1
|
||||
fi
|
||||
mode=$1
|
||||
|
||||
if [ "$mode" == "install" ] || [ "$mode" == "uninstall" ] ; then
|
||||
[ -n "$2" ] && [ "$2" != "-q" ] && [ "$2" != "-i" ] && echo "Second arg must be -q (Quiet) or -i (Interactive)" && exit 1
|
||||
[ "$2" == "-q" ] && interactive=false || interactive=true
|
||||
|
||||
[ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1
|
||||
fi
|
||||
|
||||
function confirm() {
|
||||
echo -en $1" [y/n] "
|
||||
local yn=""
|
||||
|
||||
read yn </dev/tty
|
||||
while ! [[ $yn =~ ^[Yy|Nn]$ ]]; do
|
||||
read -p "'y' or 'n' expected: " yn </dev/tty
|
||||
done
|
||||
|
||||
echo "" # Insert new line after answering.
|
||||
[[ $yn =~ ^[Yy]$ ]] && return 0 || return 1 # 0 means success.
|
||||
}
|
||||
|
||||
# Format the given KB number into GB units.
|
||||
function GB() {
|
||||
echo "$(bc <<<"scale=2; $1 / 1048576") GB"
|
||||
}
|
||||
|
||||
function check_sys_req() {
|
||||
|
||||
# Assign sys resource info to global vars since these will also be used for instance allocation later.
|
||||
ramKB=$(free | grep Mem | awk '{print $2}')
|
||||
swapKB=$(free | grep Swap | awk '{print $2}')
|
||||
diskKB=$(df | grep -w /home | head -1 | awk '{print $4}')
|
||||
[ -z "$diskKB" ] && diskKB=$(df | grep -w / | head -1 | awk '{print $4}')
|
||||
|
||||
[ "$SKIP_SYSREQ" == "1" ] && echo "System requirements check skipped." && return 0
|
||||
|
||||
local proc1=$(ps --no-headers -o comm 1)
|
||||
if [ "$proc1" != "systemd" ]; then
|
||||
echo "$evernode host installation requires systemd. Your system does not have systemd running. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local os=$(grep -ioP '^ID=\K.+' /etc/os-release)
|
||||
local osversion=$(grep -ioP '^VERSION_ID=\K.+' /etc/os-release)
|
||||
|
||||
if [ "$os" != "ubuntu" ] || [ "$osversion" != '"20.04"' ] || [ $ramKB -lt 2000000 ] || [ $swapKB -lt 2000000 ] || [ $diskKB -lt 4194304 ]; then
|
||||
echomult "Your system specs are:
|
||||
\n OS: $os $osversion
|
||||
\n RAM: $(GB $ramKB)
|
||||
\n Swap: $(GB $swapKB)
|
||||
\n Disk space (/home): $(GB $diskKB)
|
||||
\n$evernode host registration requires Ubuntu 20.04 with 2 GB RAM, 2 GB Swap and 4 GB free disk space for /home.
|
||||
\nYour system does not meet some of the requirements. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "System check complete. Your system is capable of becoming an $evernode host."
|
||||
}
|
||||
|
||||
function resolve_ip_addr() {
|
||||
# Attempt to resolve ip (in case inetaddr is a DNS address)
|
||||
# This will resolve correctly if inetaddr is a valid ip or dns address.
|
||||
ipaddr=$(getent hosts $inetaddr | head -1 | awk '{ print $1 }')
|
||||
|
||||
# If invalid, reset inetaddr and return with non-zero code.
|
||||
if [ -z "$ipaddr" ] ; then
|
||||
inetaddr=""
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
function set_inet_addr() {
|
||||
|
||||
# Attempt to auto-detect in interactive mode or if 'auto' is specified.
|
||||
([ "$inetaddr" == "auto" ] || $interactive) && inetaddr=$(hostname -I | awk '{print $1}')
|
||||
resolve_ip_addr
|
||||
|
||||
if $interactive ; then
|
||||
|
||||
if [ -n "$inetaddr" ] && ! confirm "Detected ip address '$inetaddr'. This will be used to reach contract instances running
|
||||
on your host. Do you want to specify a different IP or DNS address?" ; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
inetaddr=""
|
||||
while [ -z "$inetaddr" ]; do
|
||||
# This will be asked if auto-detection fails or if user wants to specify manually.
|
||||
read -p "Please specify the IP or DNS address your server is reachable at: " inetaddr </dev/tty
|
||||
resolve_ip_addr || echo "Invalid IP or DNS address."
|
||||
done
|
||||
|
||||
else
|
||||
[ -z "$inetaddr" ] && echo "Invalid IP or DNS address '$inetaddr'" && exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate country code and convert to uppercase if valid.
|
||||
function resolve_countrycode() {
|
||||
# If invalid, reset countrycode and return with non-zero code.
|
||||
if ! [[ $countrycode =~ ^[A-Za-z][A-Za-z]$ ]] ; then
|
||||
countrycode=""
|
||||
return 1
|
||||
else
|
||||
countrycode=$(echo $countrycode | tr 'a-z' 'A-Z')
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
function set_country_code() {
|
||||
|
||||
# Attempt to auto-detect in interactive mode or if 'auto' is specified.
|
||||
if [ "$countrycode" == "auto" ] || $interactive ; then
|
||||
echo "Checking country code..."
|
||||
echo "Using GeoLite2 data created by MaxMind, available from https://www.maxmind.com"
|
||||
|
||||
local detected=$(curl -s -u "$maxmind_creds" "https://geolite.info/geoip/v2.1/country/$ipaddr?pretty" | grep "iso_code" | head -1 | awk '{print $2}')
|
||||
countrycode=${detected:1:2}
|
||||
resolve_countrycode || echo "Could not detect country code."
|
||||
fi
|
||||
|
||||
if $interactive ; then
|
||||
|
||||
# Uncomment this if we want the user to manually change the auto-detected country code.
|
||||
# if [ -n "$countrycode" ] && ! confirm "Based on the internet address '$inetaddr' we have detected that your country
|
||||
# code is '$countrycode'. Do you want to specify a different country code" ; then
|
||||
# return 0
|
||||
# fi
|
||||
# countrycode=""
|
||||
|
||||
while [ -z "$countrycode" ]; do
|
||||
# This will be asked if auto-detection fails or if user wants to specify manually.
|
||||
read -p "Please specify the two-letter country code where your server is located in (eg. AU): " countrycode </dev/tty
|
||||
resolve_countrycode || echo "Invalid country code."
|
||||
done
|
||||
|
||||
else
|
||||
resolve_countrycode || (echo "Invalid country code '$countrycode'" && exit 1)
|
||||
fi
|
||||
}
|
||||
|
||||
function set_cgrules_svc() {
|
||||
local filename=$(basename $(grep "ExecStart.*=.*/cgrulesengd$" /etc/systemd/system/*.service | head -1 | awk -F : ' { print $1 } '))
|
||||
cgrulesengd_service="${filename%.*}"
|
||||
# If service not detected, use the default name.
|
||||
[ -z "$cgrulesengd_service" ] && cgrulesengd_service=$cgrulesengd_default || echo "cgroups rules engine service found: '$cgrulesengd_service'"
|
||||
}
|
||||
|
||||
function set_instance_alloc() {
|
||||
[ -z $alloc_ramKB ] && alloc_ramKB=$(( (ramKB / 100) * alloc_ratio ))
|
||||
[ -z $alloc_swapKB ] && alloc_swapKB=$(( (swapKB / 100) * alloc_ratio ))
|
||||
[ -z $alloc_diskKB ] && alloc_diskKB=$(( (diskKB / 100) * alloc_ratio ))
|
||||
[ -z $alloc_cpu ] && alloc_cpu=$(( (1000000 / 100) * alloc_ratio ))
|
||||
|
||||
# We decide instance count based on total memory (ram+swap)
|
||||
[ -z $alloc_instcount ] && alloc_instcount=$(( (alloc_ramKB + alloc_swapKB) / memKB_per_instance ))
|
||||
|
||||
if $interactive; then
|
||||
echomult "Based on your system resources, we have chosen the following allocation:\n
|
||||
$(GB $alloc_ramKB) RAM\n
|
||||
$(GB $alloc_swapKB) Swap\n
|
||||
$(GB $alloc_diskKB) disk space\n
|
||||
Distributed among $alloc_instcount contract instances"
|
||||
! confirm "Do you wish to change this allocation?" && return 0
|
||||
|
||||
local ramMB=0 swapMB=0 diskMB=0
|
||||
|
||||
while true ; do
|
||||
read -p "Specify the number of contract instances that you wish to host: " alloc_instcount </dev/tty
|
||||
! [[ $alloc_instcount -gt 0 ]] && echo "Invalid instance count." || break
|
||||
done
|
||||
|
||||
while true ; do
|
||||
read -p "Specify the total RAM in megabytes to distribute among all contract instances: " ramMB </dev/tty
|
||||
! [[ $ramMB -gt 0 ]] && echo "Invalid amount." || break
|
||||
done
|
||||
|
||||
while true ; do
|
||||
read -p "Specify the total Swap in megabytes to distribute among all contract instances: " swapMB </dev/tty
|
||||
! [[ $swapMB -gt 0 ]] && echo "Invalid amount." || break
|
||||
done
|
||||
|
||||
while true ; do
|
||||
read -p "Specify the total disk space in megabytes to distribute among all contract instances: " diskMB </dev/tty
|
||||
! [[ $diskMB -gt 0 ]] && echo "Invalid amount." || break
|
||||
done
|
||||
|
||||
alloc_ramKB=$(( ramMB * 1024 ))
|
||||
alloc_swapKB=$(( swapMB * 1024 ))
|
||||
alloc_diskKB=$(( diskMB * 1024 ))
|
||||
fi
|
||||
|
||||
if ! [[ $alloc_ramKB -gt 0 ]] || ! [[ $alloc_swapKB -gt 0 ]] || ! [[ $alloc_diskKB -gt 0 ]] ||
|
||||
! [[ $alloc_cpu -gt 0 ]] || ! [[ $alloc_instcount -gt 0 ]]; then
|
||||
echo "Invalid allocation." && exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function install_failure() {
|
||||
echo "There was an error during installation. Please provide the file $logfile to Evernode team. Thank you."
|
||||
exit 1
|
||||
}
|
||||
|
||||
function uninstall_failure() {
|
||||
echo "There was an error during uninstallation."
|
||||
exit 1
|
||||
}
|
||||
|
||||
function install_sashimono() {
|
||||
echo "Starting Sashimono installation..."
|
||||
|
||||
local tmp=$(mktemp -d)
|
||||
cd $tmp
|
||||
curl -s $installer --output installer.tgz
|
||||
tar zxf $tmp/installer.tgz --strip-components=1
|
||||
rm installer.tgz
|
||||
|
||||
set -o pipefail # We need installer exit code to detect failures (ignore the tee pipe exit code).
|
||||
mkdir -p $log_dir
|
||||
logfile="$log_dir/installer-$(date +%s).log"
|
||||
echo "Installing prerequisites..."
|
||||
! ./prereq.sh $cgrulesengd_service 2>&1 \
|
||||
| tee -a $logfile | stdbuf --output=L grep "STAGE" | cut -d ' ' -f 2- && install_failure
|
||||
echo "Installing Sashimono..."
|
||||
! ./sashimono-install.sh $inetaddr $countrycode $alloc_instcount \
|
||||
$alloc_cpu $alloc_ramKB $alloc_swapKB $alloc_diskKB $description 2>&1 \
|
||||
| tee -a $logfile | stdbuf --output=L grep "STAGE" | cut -d ' ' -f 2- && install_failure
|
||||
set +o pipefail
|
||||
|
||||
rm -r $tmp
|
||||
}
|
||||
|
||||
function uninstall_sashimono() {
|
||||
|
||||
# Check for existing contract instances.
|
||||
local users=$(cut -d: -f1 /etc/passwd | grep "^$SASHIUSER_PREFIX" | sort)
|
||||
readarray -t userarr <<<"$users"
|
||||
local sashiusers=()
|
||||
for user in "${userarr[@]}"; do
|
||||
[ ${#user} -lt 24 ] || [ ${#user} -gt 32 ] || [[ ! "$user" =~ ^$SASHIUSER_PREFIX[0-9]+$ ]] && continue
|
||||
sashiusers+=("$user")
|
||||
done
|
||||
local ucount=${#sashiusers[@]}
|
||||
|
||||
$interactive && [ $ucount -gt 0 ] && ! confirm "This will delete $ucount contract instances. Do you still want to uninstall?" && exit 1
|
||||
! $interactive && echo "$ucount contract instances will be deleted."
|
||||
|
||||
echo "Uninstalling Sashimono..."
|
||||
! $SASHIMONO_BIN/sashimono-uninstall.sh && uninstall_failure
|
||||
}
|
||||
|
||||
# Create a copy of this same script as a command.
|
||||
function create_evernode_alias() {
|
||||
! curl -fsSL $script_url --output $evernode_alias >> $logfile 2>&1 && install_failure
|
||||
! chmod +x $evernode_alias >> $logfile 2>&1 && install_failure
|
||||
}
|
||||
|
||||
function remove_evernode_alias() {
|
||||
rm $evernode_alias
|
||||
}
|
||||
|
||||
function check_installer_pending_finish() {
|
||||
if [ -f /run/reboot-required.pkgs ] && [ -n "$(grep sashimono /run/reboot-required.pkgs)" ]; then
|
||||
echo "Your system needs to be rebooted in order to complete Sashimono installation."
|
||||
$interactive && confirm "Reboot now?" && reboot
|
||||
! $interactive && echo "Rebooting..." && reboot
|
||||
return 0
|
||||
else
|
||||
# If reboot not required, check whether re-login is required in case the setup was run with sudo.
|
||||
# This is because the user account gets added to sashiadmin group and re-login is needed for group permission to apply.
|
||||
# without this, user cannot run "sashi" cli commands without sudo.
|
||||
if [ "$mode" == "install" ] && [ -n "$SUDO_USER" ] ; then
|
||||
echo "You need to logout and log back in, to complete Sashimono installation."
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function reg_info() {
|
||||
echo ""
|
||||
if sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN reginfo ; then
|
||||
echo -e "\nYou are receiving $evernode rewards to the Host account. The account secret is stored in $MB_XRPL_DATA/mb-xrpl.cfg"
|
||||
fi
|
||||
}
|
||||
|
||||
# Begin setup execution flow --------------------
|
||||
|
||||
echo "Thank you for trying out $evernode!"
|
||||
|
||||
if [ "$mode" == "install" ]; then
|
||||
|
||||
if ! $interactive ; then
|
||||
inetaddr=${3} # IP or DNS address.
|
||||
countrycode=${4} # 2-letter country code.
|
||||
alloc_cpu=${5} # CPU microsec to allocate for contract instances (max 1000000).
|
||||
alloc_ramKB=${6} # RAM to allocate for contract instances.
|
||||
alloc_swapKB=${7} # Swap to allocate for contract instances.
|
||||
alloc_diskKB=${8} # Disk space to allocate for contract instances.
|
||||
alloc_instcount=${9} # Total contract instance count.
|
||||
description=${10} # Registration description (underscore for spaces).
|
||||
else
|
||||
description="Evernode_host"
|
||||
fi
|
||||
|
||||
$interactive && ! confirm "This will install Sashimono, Evernode's contract instance management software,
|
||||
and register your system as an $evernode host on the public XRPL hooks testnet.\n
|
||||
\nThe setup will go through the following steps:\n
|
||||
- Check your system compatibility for $evernode.\n
|
||||
- Collect information about your system to be published to users.\n
|
||||
- Generate a testnet XRPL account to receive $evernode hosting rewards.\n
|
||||
\nContinue?" && exit 1
|
||||
|
||||
check_sys_req
|
||||
$interactive && ! confirm "Make sure your system does not currently contain any other workloads important
|
||||
to you since we will be making modifications to your system configuration.
|
||||
\nThis is beta software, so there's a chance things can go wrong. Continue?" && exit 1
|
||||
|
||||
set_inet_addr
|
||||
echo -e "Using '$inetaddr' as host internet address.\n"
|
||||
|
||||
set_country_code
|
||||
echo -e "Using '$countrycode' as country code.\n"
|
||||
|
||||
set_cgrules_svc
|
||||
echo -e "Using '$cgrulesengd_service' as cgroups rules engine service.\n"
|
||||
|
||||
set_instance_alloc
|
||||
echo -e "Using allocation $(GB $alloc_ramKB) RAM, $(GB $alloc_swapKB) Swap, $(GB $alloc_diskKB) disk space, $alloc_instcount contract instances.\n"
|
||||
|
||||
install_sashimono
|
||||
create_evernode_alias
|
||||
|
||||
echomult "Installation successful! Installation log can be found at $logfile
|
||||
\n\nYour system is now registered on $evernode. You can check your system status with 'evernode status' command."
|
||||
|
||||
elif [ "$mode" == "uninstall" ]; then
|
||||
|
||||
$interactive && ! confirm "Are you sure you want to uninstall Sashimono and deregister from $evernode?" && exit 1
|
||||
|
||||
uninstall_sashimono
|
||||
remove_evernode_alias
|
||||
echo "Uninstallation complete!"
|
||||
|
||||
elif [ "$mode" == "status" ]; then
|
||||
reg_info
|
||||
|
||||
elif [ "$mode" == "list" ]; then
|
||||
sashi list
|
||||
fi
|
||||
|
||||
[ "$mode" != "uninstall" ] && check_installer_pending_finish
|
||||
|
||||
exit 0
|
||||
@@ -5,13 +5,15 @@ const { exec } = require("child_process");
|
||||
const logger = require('./lib/logger');
|
||||
const evernode = require('evernode-js-client');
|
||||
const { SqliteDatabase, DataTypes } = require('./lib/sqlite-handler');
|
||||
const https = require('https');
|
||||
|
||||
// Environment variables.
|
||||
const IS_DEV_MODE = process.env.MB_DEV === "1";
|
||||
const FILE_LOG_ENABLED = process.env.MB_FILE_LOG === "1";
|
||||
const IS_DEREGISTER = process.env.MB_DEREGISTER === "1";
|
||||
const RIPPLED_URL = process.env.MB_RIPPLED_URL || "wss://hooks-testnet.xrpl-labs.com";
|
||||
const DATA_DIR = process.env.MB_DATA_DIR || __dirname;
|
||||
const FAUCET_URL = process.env.MB_FAUCET_URL || "https://hooks-testnet.xrpl-labs.com/newcreds"
|
||||
const EVR_SEND_URL = process.env.MB_EVR_SEND_URL || "https://func-hotpocket.azurewebsites.net/api/evrfaucet?code=pPUyV1q838ryrihA5NVlobVXj8ZGgn9HsQjGGjl6Vhgxlfha4/xCgQ==&action=fundhost&hostaddr="
|
||||
|
||||
const CONFIG_PATH = DATA_DIR + '/mb-xrpl.cfg';
|
||||
const LOG_PATH = DATA_DIR + '/log/mb-xrpl.log';
|
||||
@@ -48,19 +50,6 @@ class MessageBoard {
|
||||
this.db = new SqliteDatabase(dbPath);
|
||||
}
|
||||
|
||||
new(address = "", secret = "", hostAddress = "", token = "") {
|
||||
if (fs.existsSync(CONFIG_PATH))
|
||||
throw `Config file already exists at ${CONFIG_PATH}`;
|
||||
|
||||
const configJson = JSON.stringify({
|
||||
version: MB_VERSION,
|
||||
xrpl: { address: address, secret: secret, hookAddress: hostAddress, token: token, regFeeHash: "" }
|
||||
}, null, 2);
|
||||
fs.writeFileSync(CONFIG_PATH, configJson, { mode: 0o600 }); // Set file permission so only current user can read/write.
|
||||
|
||||
console.log(`Config file created at ${CONFIG_PATH}`);
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!fs.existsSync(this.configPath))
|
||||
throw `${this.configPath} does not exist.`;
|
||||
@@ -71,7 +60,6 @@ class MessageBoard {
|
||||
|
||||
console.log("Using hook " + this.cfg.xrpl.hookAddress);
|
||||
|
||||
|
||||
this.xrplApi = new evernode.XrplApi(this.rippledServer);
|
||||
evernode.Defaults.set({
|
||||
hookAddress: this.cfg.xrpl.hookAddress,
|
||||
@@ -84,25 +72,15 @@ class MessageBoard {
|
||||
await this.hostClient.connect();
|
||||
this.evernodeHookConf = this.hostClient.hookConfig;
|
||||
|
||||
if (IS_DEREGISTER) {
|
||||
await this.deregisterHost();
|
||||
await this.hostClient.disconnect();
|
||||
await this.xrplApi.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
this.hookClient = new evernode.HookClient();
|
||||
await this.hookClient.connect();
|
||||
|
||||
// Check whether registration fee is already paid and trustline is made.
|
||||
await this.checkForRegistration();
|
||||
|
||||
this.db.open();
|
||||
// Create redeem table if not exist.
|
||||
await this.createRedeemTableIfNotExists();
|
||||
await this.createUtilDataTableIfNotExists();
|
||||
|
||||
this.lastValidatedLedgerIndex = await this.xrplApi.ledgerIndex;
|
||||
this.lastValidatedLedgerIndex = this.xrplApi.ledgerIndex;
|
||||
|
||||
const redeems = await this.getRedeemedRecords();
|
||||
for (const redeem of redeems)
|
||||
@@ -119,7 +97,7 @@ class MessageBoard {
|
||||
if (currentMoment % this.hostClient.hookConfig.hostHeartbeatFreq === 0 && currentMoment !== this.lastRechargedMoment) {
|
||||
this.lastRechargedMoment = currentMoment;
|
||||
|
||||
console.log(`Recharding at Moment ${this.lastRechargedMoment}...`)
|
||||
console.log(`Recharging at Moment ${this.lastRechargedMoment}...`)
|
||||
|
||||
try {
|
||||
await this.hostClient.recharge();
|
||||
@@ -233,38 +211,6 @@ class MessageBoard {
|
||||
this.db.close();
|
||||
}
|
||||
|
||||
async checkForRegistration() {
|
||||
if (!this.cfg.xrpl.regFeeHash) {
|
||||
|
||||
try {
|
||||
console.log('Preparing host account...')
|
||||
await this.hostClient.prepareAccount();
|
||||
console.log('Registering host...')
|
||||
const tx = await this.hostClient.register(this.cfg.xrpl.token, "AU", 1000, 1024, 4096, "AUTO test Sashimono");
|
||||
|
||||
this.cfg.xrpl.regFeeHash = tx.id;
|
||||
this.persistConfig();
|
||||
console.log('Registration complete. Tx hash: ' + tx.id);
|
||||
}
|
||||
catch (err) {
|
||||
console.log("Registration failed.", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async deregisterHost() {
|
||||
// Sends evernode host de-registration transaction.
|
||||
console.log(`Performing Evernode host deregistration...`);
|
||||
|
||||
try {
|
||||
const tx = await this.hostClient.deregister();
|
||||
console.log('Deregistration complete. ' + tx.id);
|
||||
}
|
||||
catch (err) {
|
||||
console.log("Deregistration failed.", err)
|
||||
}
|
||||
}
|
||||
|
||||
addToExpiryList(txHash, containerName, expiryMoment) {
|
||||
this.expiryList.push({
|
||||
txHash: txHash,
|
||||
@@ -428,38 +374,262 @@ class SashiCLI {
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (process.argv.length === 3) {
|
||||
if (process.argv[2] === 'version') {
|
||||
console.log(MB_VERSION);
|
||||
process.exit(0);
|
||||
class Setup {
|
||||
|
||||
#httpPost(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = https.request(url, { method: 'POST' }, (resp) => {
|
||||
let data = '';
|
||||
resp.on('data', (chunk) => data += chunk);
|
||||
resp.on('end', () => {
|
||||
if (resp.statusCode == 200)
|
||||
resolve(data);
|
||||
else
|
||||
reject(data);
|
||||
});
|
||||
})
|
||||
|
||||
req.on("error", reject);
|
||||
req.on('timeout', () => reject('Request timed out.'))
|
||||
req.end()
|
||||
})
|
||||
}
|
||||
|
||||
async #generateFaucetAccount() {
|
||||
console.log("Generating faucet account...");
|
||||
const resp = await this.#httpPost(FAUCET_URL);
|
||||
const json = JSON.parse(resp);
|
||||
return {
|
||||
address: json.address,
|
||||
secret: json.secret
|
||||
};
|
||||
}
|
||||
|
||||
#getRandomToken() {
|
||||
const randomChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
let result = '';
|
||||
for (var i = 0; i < 3; i++) {
|
||||
result += randomChars.charAt(Math.floor(Math.random() * randomChars.length));
|
||||
}
|
||||
else if (process.argv[2] === 'help') {
|
||||
console.log(`Usage:
|
||||
return result;
|
||||
}
|
||||
|
||||
#getConfigAccount() {
|
||||
if (!fs.existsSync(CONFIG_PATH))
|
||||
throw `Config file does not exist at ${CONFIG_PATH}`;
|
||||
return JSON.parse(fs.readFileSync(CONFIG_PATH).toString()).xrpl;
|
||||
}
|
||||
|
||||
async #sendEversFromHook(hostAddress) {
|
||||
console.log("Sending EVRs...");
|
||||
|
||||
// Sometimes we may get error from func execution when some rippled servers in the testnet cluster
|
||||
// haven't still updated the ledger. In such cases, we retry several times before giving up.
|
||||
let attempts = 0;
|
||||
while (attempts >= 0) {
|
||||
try {
|
||||
await this.#httpPost(EVR_SEND_URL + hostAddress);
|
||||
break;
|
||||
}
|
||||
catch (err) {
|
||||
if (++attempts <= 5)
|
||||
continue;
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async generateBetaHostAccount(hookAddress) {
|
||||
|
||||
evernode.Defaults.set({
|
||||
hookAddress: hookAddress,
|
||||
rippledServer: RIPPLED_URL
|
||||
});
|
||||
|
||||
const acc = await this.#generateFaucetAccount();
|
||||
acc.token = this.#getRandomToken();
|
||||
|
||||
// Prepare host account.
|
||||
{
|
||||
console.log(`Preparing host account:${acc.address} (token:${acc.token} hook:${hookAddress})`);
|
||||
const hostClient = new evernode.HostClient(acc.address, acc.secret);
|
||||
await hostClient.connect();
|
||||
|
||||
// Sometimes we may get 'account not found' error from rippled when some servers in the testnet cluster
|
||||
// haven't still updated the ledger. In such cases, we retry several times before giving up.
|
||||
let attempts = 0;
|
||||
while (attempts >= 0) {
|
||||
try {
|
||||
await hostClient.prepareAccount();
|
||||
break;
|
||||
}
|
||||
catch (err) {
|
||||
if (err.data.error === 'actNotFound' && ++attempts <= 5) {
|
||||
console.log("actNotFound - retrying...")
|
||||
// Wait and retry.
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
await hostClient.disconnect();
|
||||
}
|
||||
|
||||
// Send EVRs from hook to host account.
|
||||
{
|
||||
await this.#sendEversFromHook(acc.address);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
newConfig(address = "", secret = "", hookAddress = "", token = "") {
|
||||
if (fs.existsSync(CONFIG_PATH))
|
||||
throw `Config file already exists at ${CONFIG_PATH}`;
|
||||
|
||||
const configJson = JSON.stringify({
|
||||
version: MB_VERSION,
|
||||
xrpl: { address: address, secret: secret, hookAddress: hookAddress, token: token }
|
||||
}, null, 2);
|
||||
fs.writeFileSync(CONFIG_PATH, configJson, { mode: 0o600 }); // Set file permission so only current user can read/write.
|
||||
}
|
||||
|
||||
async register(countryCode, cpuMicroSec, ramKb, swapKb, diskKb, description) {
|
||||
console.log("Registering host...");
|
||||
const acc = this.#getConfigAccount();
|
||||
evernode.Defaults.set({
|
||||
hookAddress: acc.hookAddress,
|
||||
rippledServer: RIPPLED_URL
|
||||
});
|
||||
|
||||
const hostClient = new evernode.HostClient(acc.address, acc.secret);
|
||||
await hostClient.connect();
|
||||
|
||||
// Sometimes we may get 'tecPATH_DRY' error from rippled when some servers in the testnet cluster
|
||||
// haven't still updated the ledger. In such cases, we retry several times before giving up.
|
||||
let attempts = 0;
|
||||
while (attempts >= 0) {
|
||||
try {
|
||||
await hostClient.register(acc.token, countryCode, cpuMicroSec,
|
||||
Math.floor((ramKb + swapKb) / 1024), Math.floor(diskKb / 1024), description.replace('_', ' '));
|
||||
break;
|
||||
}
|
||||
catch (err) {
|
||||
if (err.code === 'tecPATH_DRY' && ++attempts <= 5) {
|
||||
console.log("tecPATH_DRY - retrying...")
|
||||
// Wait and retry.
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
await hostClient.disconnect();
|
||||
}
|
||||
|
||||
async deregister() {
|
||||
console.log("Deregistering host...");
|
||||
const acc = this.#getConfigAccount();
|
||||
evernode.Defaults.set({
|
||||
hookAddress: acc.hookAddress,
|
||||
rippledServer: RIPPLED_URL
|
||||
});
|
||||
|
||||
const hostClient = new evernode.HostClient(acc.address, acc.secret);
|
||||
await hostClient.connect();
|
||||
await hostClient.deregister();
|
||||
await hostClient.disconnect();
|
||||
}
|
||||
|
||||
async regInfo() {
|
||||
const acc = this.#getConfigAccount();
|
||||
evernode.Defaults.set({
|
||||
hookAddress: acc.hookAddress,
|
||||
rippledServer: RIPPLED_URL
|
||||
});
|
||||
|
||||
console.log(`Host account address: ${acc.address}`);
|
||||
console.log(`Hosting token: ${acc.token}`);
|
||||
try {
|
||||
const hostClient = new evernode.HostClient(acc.address, acc.secret);
|
||||
console.log('Retrieving EVR balance...')
|
||||
await hostClient.connect();
|
||||
const evrBalance = await hostClient.getEVRBalance();
|
||||
console.log(`EVR balance: ${evrBalance}`);
|
||||
await hostClient.disconnect();
|
||||
}
|
||||
catch {
|
||||
console.log('EVR balance: [Error occured when retrieving EVR balance]');
|
||||
}
|
||||
console.log(`Hook address: ${acc.hookAddress}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
if (process.argv[2] === 'version') {
|
||||
console.log(MB_VERSION);
|
||||
}
|
||||
|
||||
try {
|
||||
if (process.argv.length >= 3) {
|
||||
if (process.argv.length >= 3 && process.argv[2] === 'new') {
|
||||
new Setup().newConfig(process.argv[3], process.argv[4], process.argv[5], process.argv[6]);
|
||||
}
|
||||
else if (process.argv.length === 4 && process.argv[2] === 'betagen') {
|
||||
const hookAddress = process.argv[3];
|
||||
const setup = new Setup();
|
||||
const acc = await setup.generateBetaHostAccount(hookAddress);
|
||||
setup.newConfig(acc.address, acc.secret, hookAddress, acc.token);
|
||||
}
|
||||
else if (process.argv.length === 9 && process.argv[2] === 'register') {
|
||||
await new Setup().register(process.argv[3], parseInt(process.argv[4]), parseInt(process.argv[5]),
|
||||
parseInt(process.argv[6]), parseInt(process.argv[7]), process.argv[8]);
|
||||
}
|
||||
else if (process.argv.length === 3 && process.argv[2] === 'deregister') {
|
||||
await new Setup().deregister();
|
||||
}
|
||||
else if (process.argv.length === 3 && process.argv[2] === 'reginfo') {
|
||||
await new Setup().regInfo();
|
||||
}
|
||||
else if (process.argv[2] === 'help') {
|
||||
console.log(`Usage:
|
||||
node index.js - Run message board.
|
||||
node index.js version - Print version.
|
||||
node index.js new [address] [secret] [hookAddress] [token] - Create new config file.
|
||||
node index.js betagen [hookAddress] - Generate beta host account and populate config.
|
||||
node index.js register [countryCode] [cpuMicroSec] [ramKb] [swapKb] [diskKb] [description] - Register the host on Evernode.
|
||||
node index.js deregister - Deregister the host from Evernode.
|
||||
node index.js reginfo - Display Evernode registration info.
|
||||
node index.js help - Print help.`);
|
||||
process.exit(0);
|
||||
}
|
||||
else {
|
||||
throw "Invalid args.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Logs are formatted with the timestamp and a log file will be created inside log directory.
|
||||
logger.init(LOG_PATH, FILE_LOG_ENABLED);
|
||||
|
||||
console.log('Starting the Evernode xrpl message board.' + (IS_DEV_MODE ? ' (in dev mode)' : ''));
|
||||
console.log('Data dir: ' + DATA_DIR);
|
||||
console.log('Rippled server: ' + RIPPLED_URL);
|
||||
console.log('Using Sashimono cli: ' + SASHI_CLI_PATH);
|
||||
|
||||
const mb = new MessageBoard(CONFIG_PATH, DB_PATH, SASHI_CLI_PATH, RIPPLED_URL);
|
||||
await mb.init();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mb = new MessageBoard(CONFIG_PATH, DB_PATH, SASHI_CLI_PATH, RIPPLED_URL);
|
||||
|
||||
if (process.argv.length >= 3 && process.argv[2] === 'new') {
|
||||
mb.new(process.argv[3], process.argv[4], process.argv[5], process.argv[6]);
|
||||
process.exit(0);
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
console.log("Evernode xrpl message board exiting with error.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Logs are formatted with the timestamp and a log file will be created inside log directory.
|
||||
logger.init(LOG_PATH, FILE_LOG_ENABLED);
|
||||
|
||||
console.log('Starting the Evernode xrpl message board.' + (IS_DEV_MODE ? ' (in dev mode)' : ''));
|
||||
console.log('Data dir: ' + DATA_DIR);
|
||||
console.log('Rippled server: ' + RIPPLED_URL);
|
||||
console.log('Using Sashimono cli: ' + SASHI_CLI_PATH);
|
||||
|
||||
await mb.init();
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
6
mb-xrpl/package-lock.json
generated
6
mb-xrpl/package-lock.json
generated
@@ -772,9 +772,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"evernode-js-client": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.4.0.tgz",
|
||||
"integrity": "sha512-vHmFq2NmifFcbBQnQFsebB8hKhDpc8h5HDI5MvQesPKLqZNhYlSaswrczP/SrucdkOQtg+0MahMtKAOMyGBxHg==",
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.4.2.tgz",
|
||||
"integrity": "sha512-Ewa0F85l/mUPVYW8arQcRxie73Gko+IfFn6UVxTL7PvA3UpiB11Z0v+h0H3iw78EqYZ+fSdptG/Px6NjQCSmyA==",
|
||||
"requires": {
|
||||
"elliptic": "6.5.4",
|
||||
"ripple-address-codec": "4.2.0",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"build": "npm run lint && ncc build mb-xrpl.js --minify -o dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"evernode-js-client": "0.4.0",
|
||||
"evernode-js-client": "0.4.2",
|
||||
"sqlite3": "5.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
42
src/conf.cpp
42
src/conf.cpp
@@ -33,7 +33,8 @@ namespace conf
|
||||
* Create config here.
|
||||
* @return 0 for success. -1 for failure.
|
||||
*/
|
||||
int create(std::string_view cgrulesengd_service, std::string_view host_addr, std::string_view registry_addr)
|
||||
int create(std::string_view host_addr, std::string_view registry_addr, const size_t inst_count,
|
||||
const size_t cpu_us, const size_t ram_kbytes, const size_t swap_kbytes, const size_t disk_kbytes)
|
||||
{
|
||||
if (util::is_file_exists(ctx.config_file))
|
||||
{
|
||||
@@ -42,7 +43,7 @@ namespace conf
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recursivly create contract directory. Return an error if unable to create
|
||||
// Recursively create contract directory. Return an error if unable to create
|
||||
if (util::create_dir_tree_recursive(ctx.log_dir) == -1 ||
|
||||
util::create_dir_tree_recursive(ctx.data_dir) == -1)
|
||||
{
|
||||
@@ -62,11 +63,11 @@ namespace conf
|
||||
cfg.hp.init_peer_port = 22861;
|
||||
cfg.hp.init_user_port = 8081;
|
||||
|
||||
cfg.system.max_instance_count = 3;
|
||||
cfg.system.max_mem_kbytes = 1048576; // Total 1GB RAM
|
||||
cfg.system.max_swap_kbytes = 3145728; // Total 3GB swap.
|
||||
cfg.system.max_cpu_us = 5000000; // CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000) per instance.
|
||||
cfg.system.max_storage_kbytes = 5242880; // Total 5GB disk storage.
|
||||
cfg.system.max_instance_count = !inst_count ? 3 : inst_count;
|
||||
cfg.system.max_mem_kbytes = !ram_kbytes ? 1048576 : ram_kbytes;
|
||||
cfg.system.max_swap_kbytes = !swap_kbytes ? 3145728 : swap_kbytes;
|
||||
cfg.system.max_cpu_us = !cpu_us ? 900000 : cpu_us; // Total CPU allocation out of 1000000 microsec (1 sec).
|
||||
cfg.system.max_storage_kbytes = !disk_kbytes ? 5242880 : disk_kbytes;
|
||||
|
||||
const std::string img_prefix = registry_addr.empty() ? "hotpocketdev" : std::string(registry_addr);
|
||||
cfg.docker.images["hp.latest-ubt.20.04"] = img_prefix + "/sashimono:hp.latest-ubt.20.04";
|
||||
@@ -77,8 +78,6 @@ namespace conf
|
||||
cfg.log.log_level = "inf";
|
||||
cfg.log.loggers.emplace("console");
|
||||
|
||||
cfg.service.cgrulesengd = cgrulesengd_service;
|
||||
|
||||
// We don't enable file logging by default because Sashimono running as a systemd service
|
||||
// would automatically log console output to journal log.
|
||||
// cfg.log.loggers.emplace("file");
|
||||
@@ -301,22 +300,6 @@ namespace conf
|
||||
}
|
||||
}
|
||||
|
||||
// service
|
||||
{
|
||||
jpath = "service";
|
||||
|
||||
try
|
||||
{
|
||||
const jsoncons::ojson &service = d["service"];
|
||||
cfg.service.cgrulesengd = service["cgrulesengd"].as<std::string>();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
print_missing_field_error(jpath, e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -383,15 +366,6 @@ namespace conf
|
||||
d.insert_or_assign("log", log_config);
|
||||
}
|
||||
|
||||
// Service configs.
|
||||
{
|
||||
jsoncons::ojson service_config;
|
||||
|
||||
service_config.insert_or_assign("cgrulesengd", cfg.service.cgrulesengd);
|
||||
|
||||
d.insert_or_assign("service", service_config);
|
||||
}
|
||||
|
||||
return write_json_file(ctx.config_file, d);
|
||||
}
|
||||
|
||||
|
||||
17
src/conf.hpp
17
src/conf.hpp
@@ -75,10 +75,10 @@ namespace conf
|
||||
|
||||
struct system_config
|
||||
{
|
||||
size_t max_cpu_us = 0; // Max CPU time the agent process can consume.
|
||||
size_t max_mem_kbytes = 0; // Max memory the agent process can allocate in KB.
|
||||
size_t max_swap_kbytes = 0; // Max swap memory the agent process can allocate in KB.
|
||||
size_t max_storage_kbytes = 0; // Max physical storage the agent process can allocate in KB.
|
||||
size_t max_cpu_us = 0; // Max CPU time allocated to all instances (out of 1000000 microsec).
|
||||
size_t max_mem_kbytes = 0; // Max memory allocated to all instances in KB.
|
||||
size_t max_swap_kbytes = 0; // Max swap memory allocated to all instances in KB.
|
||||
size_t max_storage_kbytes = 0; // Max physical storage allocated to all instances in KB.
|
||||
size_t max_instance_count = 0; // Max number of instances that can be created.
|
||||
};
|
||||
|
||||
@@ -87,11 +87,6 @@ namespace conf
|
||||
std::unordered_map<std::string, std::string> images;
|
||||
};
|
||||
|
||||
struct service_config
|
||||
{
|
||||
std::string cgrulesengd; // Cgroup rule generator service.
|
||||
};
|
||||
|
||||
struct sa_config
|
||||
{
|
||||
std::string version;
|
||||
@@ -99,7 +94,6 @@ namespace conf
|
||||
system_config system;
|
||||
docker_config docker;
|
||||
log_config log;
|
||||
service_config service;
|
||||
};
|
||||
|
||||
struct sa_context
|
||||
@@ -129,7 +123,8 @@ namespace conf
|
||||
|
||||
int init();
|
||||
|
||||
int create(std::string_view cgrulesengd_service, std::string_view host_addr, std::string_view registry_addr);
|
||||
int create(std::string_view host_addr, std::string_view registry_addr, const size_t inst_count,
|
||||
const size_t cpu_us, const size_t ram_kbytes, const size_t swap_kbytes, const size_t disk_kbytes);
|
||||
|
||||
void set_dir_paths(std::string exepath, std::string datadir);
|
||||
|
||||
|
||||
48
src/main.cpp
48
src/main.cpp
@@ -13,15 +13,15 @@
|
||||
#include "util/util.hpp"
|
||||
#include "killswitch/killswitch.h"
|
||||
|
||||
#define PARSE_ERROR \
|
||||
{ \
|
||||
std::cerr << "Arguments mismatch.\n"; \
|
||||
std::cerr << "Usage:\n"; \
|
||||
std::cerr << "sagent version\n"; \
|
||||
std::cerr << "sagent new [data_dir] [cgrulesengd_service] [host_addr] [registry_addr]\n"; \
|
||||
std::cerr << "sagent run [data_dir]\n"; \
|
||||
std::cerr << "Example: sagent run /etc/sashimono\n"; \
|
||||
return -1; \
|
||||
#define PARSE_ERROR \
|
||||
{ \
|
||||
std::cerr << "Arguments mismatch.\n"; \
|
||||
std::cerr << "Usage:\n"; \
|
||||
std::cerr << "sagent version\n"; \
|
||||
std::cerr << "sagent new [data_dir] [host_addr] [registry_addr] [inst_count] [cpu_us] [ram_kbytes] [swap_kbytes] [disk_kbytes]\n"; \
|
||||
std::cerr << "sagent run [data_dir]\n"; \
|
||||
std::cerr << "Example: sagent run /etc/sashimono\n"; \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,7 +36,7 @@ int parse_cmd(int argc, char **argv)
|
||||
{
|
||||
conf::ctx.command = argv[1];
|
||||
|
||||
if ((conf::ctx.command == "new" && argc >= 2 && argc <= 6) ||
|
||||
if ((conf::ctx.command == "new" && argc >= 2 && argc <= 10) ||
|
||||
(conf::ctx.command == "run" && argc >= 2 && argc <= 3) ||
|
||||
(conf::ctx.command == "version" && argc == 2))
|
||||
return 0;
|
||||
@@ -118,7 +118,7 @@ int main(int argc, char **argv)
|
||||
// Extract the CLI args
|
||||
// This call will populate conf::ctx
|
||||
if (parse_cmd(argc, argv) != 0)
|
||||
return -1;
|
||||
return 1;
|
||||
|
||||
if (conf::ctx.command == "version")
|
||||
{
|
||||
@@ -129,11 +129,19 @@ int main(int argc, char **argv)
|
||||
conf::set_dir_paths(argv[0], (argc >= 3) ? argv[2] : "");
|
||||
|
||||
// This will create a new config.
|
||||
const std::string cgrulesengd_service = (argc >= 4) ? argv[3] : "";
|
||||
const std::string host_addr = (argc >= 5) ? argv[4] : "";
|
||||
const std::string registry_addr = (argc >= 6) ? argv[5] : "";
|
||||
if (conf::create(cgrulesengd_service, host_addr, registry_addr) != 0)
|
||||
return -1;
|
||||
const std::string host_addr = (argc >= 4) ? argv[3] : "";
|
||||
size_t inst_count = 0, cpu_us = 0, ram_kbytes = 0, swap_kbytes = 0, disk_kbytes = 0;
|
||||
|
||||
if (((argc >= 5) && (util::stoull(argv[4], inst_count) != 0 || inst_count == 0)) ||
|
||||
((argc >= 6) && (util::stoull(argv[5], cpu_us) != 0 || cpu_us == 0)) ||
|
||||
((argc >= 7) && (util::stoull(argv[6], ram_kbytes) != 0 || ram_kbytes == 0)) ||
|
||||
((argc >= 8) && (util::stoull(argv[7], swap_kbytes) != 0 || swap_kbytes == 0)) ||
|
||||
((argc >= 9) && (util::stoull(argv[8], disk_kbytes) != 0 || disk_kbytes == 0)) ||
|
||||
conf::create(host_addr, "", inst_count, cpu_us, ram_kbytes, swap_kbytes, disk_kbytes) != 0)
|
||||
{
|
||||
std::cerr << "Invalid Sashimono Agent config creation args.\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (conf::ctx.command == "run")
|
||||
{
|
||||
@@ -142,16 +150,16 @@ int main(int argc, char **argv)
|
||||
if (kill_switch(util::get_epoch_milliseconds()))
|
||||
{
|
||||
std::cerr << "Sashimono Agent usage limit failure.\n";
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (conf::init() != 0)
|
||||
return -1;
|
||||
return 1;
|
||||
|
||||
salog::init(); // Initialize logger for SA.
|
||||
|
||||
if (crypto::init() == -1)
|
||||
return -1;
|
||||
return 1;
|
||||
|
||||
LOG_INFO << "Sashimono agent (version " << version::AGENT_VERSION << ")";
|
||||
LOG_INFO << "Log level: " << conf::cfg.log.log_level;
|
||||
@@ -160,7 +168,7 @@ int main(int argc, char **argv)
|
||||
if (comm::init() == -1 || hp::init() == -1)
|
||||
{
|
||||
deinit();
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// After initializing primary subsystems, register the exit handler.
|
||||
|
||||
@@ -279,6 +279,26 @@ namespace util
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts given string to a uint_64. A wrapper function for std::stoull.
|
||||
* @param str String variable.
|
||||
* @param result Variable to store the answer from the conversion.
|
||||
* @return Returns 0 in a successful conversion and -1 on error.
|
||||
*/
|
||||
int stoull(const std::string &str, uint64_t &result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = std::stoull(str);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
// Return -1 if any exceptions are captured.
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the user contract directory path when username is given.
|
||||
* @param username Username of the user.
|
||||
@@ -404,7 +424,8 @@ namespace util
|
||||
{
|
||||
params.append(*itr);
|
||||
if (std::next(itr) != input_params.end())
|
||||
params.append(" ");;
|
||||
params.append(" ");
|
||||
;
|
||||
}
|
||||
const int len = 23 + (file_name.length() * 2) + params.length();
|
||||
char command[len];
|
||||
|
||||
@@ -49,6 +49,8 @@ namespace util
|
||||
|
||||
int stoul(const std::string &str, uint16_t &result);
|
||||
|
||||
int stoull(const std::string &str, uint64_t &result);
|
||||
|
||||
const std::string get_user_contract_dir(const std::string &username, std::string_view container_name);
|
||||
|
||||
int get_system_user_info(std::string_view username, user_info &user_info);
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# lcl - Get lcl of the hosts.
|
||||
# peers - Get the cfg peer list of the hosts.
|
||||
# logs - Get the log lines grep by a given keywords.
|
||||
# replacebin - Replaces a given file to /usr/bin/sashimono-agent dir and keep a backup of existing file.
|
||||
# replacebin - Replaces a given file to /usr/bin/sashimono dir and keep a backup of existing file.
|
||||
|
||||
# create - Create new sashimono hotpocket instance in each node.
|
||||
# createall - Create sashimono hotpocket instances in all nodes parallely.
|
||||
@@ -59,11 +59,6 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# jq command is used for json manipulation.
|
||||
if ! command -v jq &>/dev/null; then
|
||||
sudo apt-get install -y jq
|
||||
fi
|
||||
|
||||
configfile=config.json
|
||||
if [ ! -f $configfile ]; then
|
||||
# Create default config file.
|
||||
@@ -186,12 +181,12 @@ if [ $mode == "reconfig" ]; then
|
||||
max_instance_count=-1
|
||||
fi
|
||||
|
||||
cgrulesengd_service="cgrulesengdsvc"
|
||||
cgrulesengd_service="cgrulesengd"
|
||||
sashimono_service="sashimono-agent"
|
||||
saconfig="/etc/sashimono/sa.cfg"
|
||||
|
||||
uninstall="curl -fsSL https://sthotpocket.blob.core.windows.net/sashimono/uninstall.sh | bash -s -- -q"
|
||||
install="curl -fsSL https://sthotpocket.blob.core.windows.net/sashimono/install.sh | bash -s -- -q"
|
||||
uninstall="evernode uninstall -q"
|
||||
install="curl -fsSL https://sthotpocket.blob.core.windows.net/evernode/setup.sh | cat | SKIP_SYSREQ=1 bash -s install -q auto auto 1000000 1000000 2097152 3145728 3 Auto_host"
|
||||
|
||||
restartcgrs="systemctl restart $cgrulesengd_service.service"
|
||||
restartsas="systemctl restart $sashimono_service.service"
|
||||
@@ -207,12 +202,12 @@ if [ $mode == "reconfig" ]; then
|
||||
|
||||
# Reinstall sashimono only if reinstall specified.
|
||||
if [ ! -z $reinstall ] && [ $reinstall == "R" ]; then
|
||||
command="$uninstall && $install && $changecfg && $restartcgrs && $restartsas"
|
||||
command="$uninstall &>/dev/null && echo 'Sashimono uninstalled.' && $install &>/dev/null && echo 'Sashimono installed.' && $changecfg && $restartcgrs && $restartsas"
|
||||
else
|
||||
command="$changecfg && $restartsas"
|
||||
fi
|
||||
|
||||
if ! sshskp $sshuser@$hostaddr $command &>/dev/null; then
|
||||
if ! sshskp $sshuser@$hostaddr $command ; then
|
||||
printf "$PRINTFORMAT" "$nodeno" "Error occured reconfiguring sashimono."
|
||||
else
|
||||
# Remove host info if reinstall.
|
||||
@@ -339,8 +334,8 @@ if [ $mode == "replacebin" ]; then
|
||||
nodeno=$(expr $1 + 1)
|
||||
replace=$2
|
||||
filename=$(basename $replace)
|
||||
original="/usr/bin/sashimono-agent/$filename"
|
||||
backup="/usr/bin/sashimono-agent/$filename.bk"
|
||||
original="/usr/bin/sashimono/$filename"
|
||||
backup="/usr/bin/sashimono/$filename.bk"
|
||||
sshskp $sshuser@$hostaddr "mv $original $backup" && scpskp -q $replace $sshuser@$hostaddr:$original
|
||||
echo "node$nodeno: Updated $original, Kept backup $backup"
|
||||
}
|
||||
@@ -359,7 +354,7 @@ if [ $mode == "replacebin" ]; then
|
||||
fi
|
||||
|
||||
if [ $mode == "docker-pull" ]; then
|
||||
dockerbin=/usr/bin/sashimono-agent/dockerbin/docker
|
||||
dockerbin=/usr/bin/sashimono/dockerbin/docker
|
||||
repo=$(echo $continfo | jq -r '.docker.repo')
|
||||
if [ "$repo" == "" ] || [ "$repo" == "null" ]; then
|
||||
echo "repo not specified."
|
||||
|
||||
@@ -168,7 +168,7 @@ async function initHosts() {
|
||||
async function initHostAccountData(host) {
|
||||
const output = await execSsh(host, "cat /etc/sashimono/mb-xrpl/mb-xrpl.cfg");
|
||||
if (!output || output.trim() === "") {
|
||||
console.log("ERROR: No output from mb-xrpl config read.")
|
||||
console.log(host, "ERROR: No output from mb-xrpl config read.")
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
6
test/vm-cluster/package-lock.json
generated
6
test/vm-cluster/package-lock.json
generated
@@ -258,9 +258,9 @@
|
||||
"integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw="
|
||||
},
|
||||
"evernode-js-client": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.3.4.tgz",
|
||||
"integrity": "sha512-uaGNP+IH4Gqw1j1gxlg5WUe2coSN85GJ65jnQDUT216oCHXnKeHCzWn/EUiCSd+0T8J5MReM0IVjV/7f1CwnMA==",
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/evernode-js-client/-/evernode-js-client-0.4.2.tgz",
|
||||
"integrity": "sha512-Ewa0F85l/mUPVYW8arQcRxie73Gko+IfFn6UVxTL7PvA3UpiB11Z0v+h0H3iw78EqYZ+fSdptG/Px6NjQCSmyA==",
|
||||
"requires": {
|
||||
"elliptic": "6.5.4",
|
||||
"ripple-address-codec": "4.2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "evcluster",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"evernode-js-client": "0.3.4",
|
||||
"evernode-js-client": "0.4.2",
|
||||
"node-fetch": "3.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user