Merge branch 'main' into beta1

This commit is contained in:
chalith
2022-07-04 17:33:53 +05:30
19 changed files with 2834 additions and 54 deletions

View File

@@ -35,9 +35,12 @@ if [ "$max_swap_kbytes" != "" ] && [ ! ${#max_swap_kbytes} -eq 0 ] && [ "$max_sw
! instance_swap_kbytes=$(expr $instance_mem_kbytes + $max_swap_kbytes / $max_instance_count) && echo "Max swap memory limit calculation error." && exit 1
fi
instance_cpu_us=0
instance_cpu_quota=0
# In the Sashimono configuration, CPU time is 1000000us Sashimono is given max_cpu_us out of it.
# Instance allocation is multiplied by number of cores to determined the number of cores per instance and devided by 10 since cfs_period_us is set to 100000us
if [ "$max_cpu_us" != "" ] && [ ! ${#max_cpu_us} -eq 0 ] && [ "$max_cpu_us" -gt 0 ]; then
! instance_cpu_us=$(expr $max_cpu_us / $max_instance_count) && echo "Max cpu limit calculation error." && exit 1
cores=$(grep -c ^processor /proc/cpuinfo)
! instance_cpu_quota=$(expr $(expr $cores \* $max_cpu_us) / $(expr $max_instance_count \* 10)) && echo "Max cpu limit calculation error." && exit 1
fi
prefix="sashi"
@@ -53,18 +56,18 @@ done
has_err=0
for user in "${validusers[@]}"; do
# Setup user cgroup.
if [ $instance_cpu_us -gt 0 ] &&
if [ $instance_cpu_quota -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 "100000" >/sys/fs/cgroup/cpu/$user$cgroupsuffix/cpu.cfs_period_us &&
echo "$instance_cpu_quota" >/sys/fs/cgroup/cpu/$user$cgroupsuffix/cpu.cfs_quota_us); then
echo "CPU cgroup creation for $user failed."
has_err=1
fi
if [ $instance_mem_kbytes -gt 0 ] &&
! (cgcreate -g memory:$user$cgroupsuffix &&
echo "${instance_mem_kbytes}K" > /sys/fs/cgroup/memory/$user$cgroupsuffix/memory.limit_in_bytes &&
echo "${instance_swap_kbytes}K" > /sys/fs/cgroup/memory/$user$cgroupsuffix/memory.memsw.limit_in_bytes); then
echo "${instance_mem_kbytes}K" >/sys/fs/cgroup/memory/$user$cgroupsuffix/memory.limit_in_bytes &&
echo "${instance_swap_kbytes}K" >/sys/fs/cgroup/memory/$user$cgroupsuffix/memory.memsw.limit_in_bytes); then
echo "Memory cgroup creation for $user failed."
has_err=1
fi

View File

@@ -209,10 +209,14 @@ WantedBy=default.target" >"$user_dir"/.config/systemd/user/ledger_fs.service
sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user daemon-reload
# In the Sashimono configuration, CPU time is 1000000us Sashimono is given max_cpu_us out of it.
# Instance allocation is multiplied by number of cores to determined the number of cores per instance and devided by 10 since cfs_period_us is set to 100000us
cores=$(grep -c ^processor /proc/cpuinfo)
cpu_quota=$(expr $(expr $cores \* $cpu) / 10)
echo "Setting up user cgroup resources."
! (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"
echo "100000" >/sys/fs/cgroup/cpu/$user$cgroupsuffix/cpu.cfs_period_us &&
echo "$cpu_quota" >/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 &&
echo "${swapmem}K" >/sys/fs/cgroup/memory/$user$cgroupsuffix/memory.memsw.limit_in_bytes) && rollback "CGROUP_MEM_CREAT"

2
examples/crawler/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules
dist

137
examples/crawler/app.js Normal file
View File

@@ -0,0 +1,137 @@
const { ContractInstanceManager } = require('./contract-instance-manager');
const evernode = require("evernode-js-client");
const process = require('process');
const fs = require('fs');
const { v4: uuidv4 } = require('uuid');
const args = process.argv.slice(2);
const tenantAddress = args[0] || "rKaXLGujsf8LokeQxG6TsoKVq8XWEMKpH";
const tenantSecret = args[1] || "ssorCEAHJFhZihUjCbx55j1mP6RnZ";
const intervalSec = (args[2] && parseInt(args[2])) || 60;
console.log(tenantAddress, intervalSec);
const registryAddress = "r3cNR2bdao1NyvQ5ZuQvCUgqkoWGmgF34E";
const evrIssuerAddress = "rfxLPXCcSmwR99dV97yFozBzrzpvCa2VCf";
const foundationAddress = "rppVLpTDks7tjAGw9TRcwqMGzuDvzV72Vh";
const foundationSecret = "shdAf9oUv1TLVTTR26Ke7w7Gv44HN";
const contractOwnerPrivateKey = "edfbbf5e66101cbf443c137b66c6b379bd4dfb8274015f7a0accd5cf3f4c640aa65cb83404120ac759609819591ef839b7d222c84f1f08b3012f490586159d2b50";
const contractOwnerPublicKey = "ed5cb83404120ac759609819591ef839b7d222c84f1f08b3012f490586159d2b50";
const contractBundle = 'contract-bundle.zip';
const logFile = 'crawler.log';
const statsFile = 'stats.log';
let instancesCreated = 0;
async function fundTenant(tenant) {
// Send evers to tenant if needed.
const lines = await tenant.xrplAcc.getTrustLines('EVR', evrIssuerAddress);
if (lines.length === 0 || parseInt(lines[0].balance) < 1) {
await tenant.xrplAcc.setTrustLine('EVR', evrIssuerAddress, "99999999");
await new evernode.XrplAccount(foundationAddress, foundationSecret).makePayment(tenantAddress, "100000", 'EVR', evrIssuerAddress);
}
}
function appendLog(type, msg) {
const str = '\n' + new Date().toUTCString() + '\n' + type + '\n' + JSON.stringify(msg) + '\n';
fs.appendFileSync(logFile, str);
}
function updateStats(instancesCreated) {
fs.writeFileSync(statsFile, JSON.stringify({
instancesCreated: instancesCreated
}))
}
async function acquireLease(tenant, host, instanceId, contractId, ownerPubKey) {
if ((host.maxInstances - host.activeInstances) > 0) {
try {
console.log(`Acquiring lease in Host ${host.address} (currently ${host.activeInstances} instances)`);
const result = await tenant.acquireLease(host.address, {
container_name: instanceId,
owner_pubkey: ownerPubKey,
contract_id: contractId,
image: "hp.latest-ubt.20.04-njs.16",
config: {}
}, { timeout: 60000 });
console.log(`Tenant received instance '${result.instance.name}'`);
return result.instance;
}
catch (err) {
console.log("Tenant recieved acquire error: ", err)
appendLog('AcquireError ' + host.address, err);
}
}
else {
console.log(`Host ${host.address} full.`);
}
return null;
}
async function spawnOnRandomHost(registry, tenant) {
console.log("------------------------------------------");
console.log(new Date().toUTCString());
const hosts = await registry.getActiveHosts();
if (hosts.length > 0) {
const randomIndex = Math.floor(Math.random() * hosts.length);
const host = hosts[randomIndex];
const contractId = uuidv4();
const instance = await acquireLease(tenant, host, contractId, contractId, contractOwnerPublicKey)
if (instance) {
console.log(`Received instance at ${new Date().toUTCString()}`, instance);
const instanceMgr = new ContractInstanceManager(contractOwnerPrivateKey, instance.pubkey, instance.ip, instance.user_port, instance.contractId, contractBundle);
try {
await instanceMgr.deployContract();
console.log('Instance deployed at', new Date().toUTCString());
updateStats(++instancesCreated);
}
catch (err) {
console.log("Contract deployment error.", err);
appendLog('DeployError ' + instance.ip, err);
}
}
else {
console.log('Spawning skipped.');
}
}
else {
console.log("No active hosts.");
}
// Reschedule same call.
setTimeout(() => {
spawnOnRandomHost(registry, tenant);
}, intervalSec * 1000);
}
async function crawler() {
const xrplApi = new evernode.XrplApi('wss://hooks-testnet-v2.xrpl-labs.com');
evernode.Defaults.set({
registryAddress: registryAddress,
xrplApi: xrplApi
})
await xrplApi.connect();
const tenant = new evernode.TenantClient(tenantAddress, tenantSecret);
await tenant.connect();
await tenant.prepareAccount();
await fundTenant(tenant);
const registry = new evernode.RegistryClient();
await registry.connect()
// Recursive scheduled call.
spawnOnRandomHost(registry, tenant);
}
crawler();

View File

@@ -0,0 +1,117 @@
const fs = require('fs').promises;
const os = require('os');
const path = require('path');
const bson = require('bson');
const HotPocket = require('hotpocket-js-client');
const uploadTimeout = 30000;
class ContractInstanceManager {
#ownerPrivKeyHex;
#instancePubKeyHex;
#ip;
#userPort;
#contractId;
#contractBundle;
constructor(ownerPrivKeyHex, instancePubKeyHex, ip, userPort, contractId, contractBundle) {
this.#ownerPrivKeyHex = ownerPrivKeyHex;
this.#instancePubKeyHex = instancePubKeyHex;
this.#ip = ip;
this.#userPort = userPort;
this.#contractId = contractId;
this.#contractBundle = contractBundle;
}
async deployContract() {
const tmpdir = await fs.mkdtemp(path.join(os.tmpdir(), 'evncrawler'));
try {
const hpc = await this.#getHotPocketConnection();
await this.#uploadBundle(hpc, this.#contractBundle);
await hpc.close();
}
catch (e) {
throw e;
}
finally {
await fs.rm(tmpdir, { recursive: true, force: true });
}
}
async #getHotPocketConnection() {
const server = `wss://${this.#ip}:${this.#userPort}`
const keys = await HotPocket.generateKeys(this.#ownerPrivKeyHex);
const hpc = await HotPocket.createClient([server], keys, {
contractId: this.#contractId,
trustedServerKeys: [this.#instancePubKeyHex],
protocol: HotPocket.protocols.bson
});
// Establish HotPocket connection.
if (!await hpc.connect()) {
throw `${server} connection failed.`
}
return hpc;
}
async #uploadBundle(hpc, bundleZipFile) {
return new Promise(async (resolve, reject) => {
const uploadTimer = setTimeout(() => reject("Upload timeout."), uploadTimeout);
const failure = (e) => {
clearTimeout(uploadTimer);
reject(e);
}
const success = () => {
console.log("Upload complete");
clearTimeout(uploadTimer);
resolve();
}
// This will get fired when contract sends an output.
hpc.on(HotPocket.events.contractOutput, (r) => {
r.outputs.forEach(output => {
let result;
try {
result = bson.deserialize(output);
}
catch (e) {
failure(e);
}
if (result.type == "uploadResult") {
if (result.status == "ok")
success();
else
failure(`Zip upload failed. reason: ${result.status}`);
}
else {
console.log("Unknown contract output.");
}
});
});
const fileContent = await fs.readFile(bundleZipFile);
console.log("Uploading");
const input = await hpc.submitContractInput(bson.serialize({
type: "upload",
content: fileContent
}));
const submission = await input.submissionStatus;
console.log(submission.status);
if (submission.status != "accepted")
failure("Upload submission failed. reason: " + submission.reason);
})
}
}
module.exports = {
ContractInstanceManager
}

View File

@@ -0,0 +1,5 @@
{
"version": "1.0",
"bin_path": "/usr/bin/node",
"bin_args": "index.js"
}

View File

@@ -0,0 +1,64 @@
const HotPocket = require("hotpocket-nodejs-contract");
const fs = require('fs');
const exectsFile = "exects.txt";
const maxFileRead = 3 * 1024 * 1024;
function getFileData() {
// Read entire file to memory (intentional).
let data = fs.readFileSync("exects.txt").toString();
// Limit max file data returned.
if (data.length > maxFileRead) {
data = data.substring(data.length - maxFileRead);
}
return data;
}
// HP smart contract is defined as a function which takes HP ExecutionContext as an argument.
// HP considers execution as complete, when this function completes and all the NPL message callbacks are complete.
const echoContract = async (ctx) => {
// We just save execution timestamp as an example state file change.
if (!ctx.readonly) {
fs.appendFileSync(exectsFile, "ts:" + ctx.timestamp + "\n");
const stats = fs.statSync(exectsFile);
if (stats.size > 300 * 1024 * 1024) // If more than 300 MB, empty the file.
fs.truncateSync(exectsFile);
}
// Collection of per-user promises to wait for. Each promise completes when inputs for that user is processed.
const userHandlers = [];
for (const user of ctx.users.list()) {
// This user's hex pubkey can be accessed from 'user.pubKey'
// For each user we add a promise to list of promises.
userHandlers.push(new Promise(async (resolve) => {
// The contract need to ensure that all outputs for a particular user is emitted
// in deterministic order. Hence, we are processing all inputs for each user sequentially.
for (const input of user.inputs) {
const buf = await ctx.users.read(input);
const msg = buf.toString();
const output = (msg == "ts") ? getFileData() : ("Echoing: " + msg);
await user.send(output);
}
// The promise gets completed when all inputs for this user are processed.
resolve();
}));
}
// Wait until all user promises are complete.
await Promise.all(userHandlers);
}
const hpc = new HotPocket.Contract();
hpc.init(echoContract);

View File

@@ -0,0 +1,24 @@
{
"name": "contract",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"hotpocket-nodejs-contract": "0.5.3"
}
},
"node_modules/hotpocket-nodejs-contract": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/hotpocket-nodejs-contract/-/hotpocket-nodejs-contract-0.5.3.tgz",
"integrity": "sha512-IMyH6noQB1w6dcPTLxdrArMpfeT9d9VJPEQ5bxubDi9Gqeu+s+0E2tKP7wDY85ie7cG/IzC9v0TCbTggp2u12w=="
}
},
"dependencies": {
"hotpocket-nodejs-contract": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/hotpocket-nodejs-contract/-/hotpocket-nodejs-contract-0.5.3.tgz",
"integrity": "sha512-IMyH6noQB1w6dcPTLxdrArMpfeT9d9VJPEQ5bxubDi9Gqeu+s+0E2tKP7wDY85ie7cG/IzC9v0TCbTggp2u12w=="
}
}
}

View File

@@ -0,0 +1,11 @@
{
"scripts": {
"build": "ncc build echo_contract.js --minify -o dist",
"cpconf": "cp contract.config dist/",
"zip": "mkdir -p ../dist && cd dist && zip -r ../../dist/contract-bundle.zip *",
"bundle": "npm run build && npm run cpconf && npm run zip"
},
"dependencies": {
"hotpocket-nodejs-contract": "0.5.3"
}
}

2294
examples/crawler/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{
"dependencies": {
"evernode-js-client": "0.4.40",
"hotpocket-js-client": "0.5.3",
"bson": "4.6.1",
"uuid": "8.3.2"
},
"scripts": {
"bundle-contract": "npm --prefix ./contract run bundle",
"build": "ncc build app.js --minify -o dist",
"bundle": "npm run build && npm run bundle-contract"
}
}

View File

@@ -48,11 +48,17 @@ else
fi
fi
# Install iptables
if ! command -v iptables &>/dev/null; then
stage "Installing iptables"
apt-get install -y iptables
fi
# Load br_netfilter kernel module on startup (if not loaded already).
if [[ -z "$(lsmod | grep br_netfilter)" ]]; then
echo "Adding br_netfilter"
modprobe br_netfilter
echo "br_netfilter" > /etc/modules-load.d/br_netfilter.conf
echo "br_netfilter" >/etc/modules-load.d/br_netfilter.conf
fi
# -------------------------------
@@ -92,10 +98,10 @@ if [ $updated -eq 1 ]; then
# Create a backup of original, if remount failed replace updated with backup.
cp $originalfstab $backup
mv "$tmpfstab" $originalfstab
if ! mount -o remount / 2>&1 ; then
if ! mount -o remount / 2>&1; then
mv $backup $originalfstab
echo "Re mounting error." && exit 1
fi
fi
echo "Updated fstab."
else
echo "fstab already configured."
@@ -134,7 +140,7 @@ if [ $res -eq 0 ]; then
res=$?
updated=1
elif [ $res -eq 0 ]; then
echo "user_allow_other" >> "$tmpconf"
echo "user_allow_other" >>"$tmpconf"
res=$?
updated=1
fi
@@ -236,7 +242,7 @@ if [ $res -eq 100 ]; then
fi
fi
elif [ $res -eq 0 ]; then
echo "GRUB_CMDLINE_LINUX=\"cgroup_enable=memory swapaccount=1\"" >> "$tmpgrub"
echo "GRUB_CMDLINE_LINUX=\"cgroup_enable=memory swapaccount=1\"" >>"$tmpgrub"
res=$?
updated=1
fi
@@ -251,7 +257,7 @@ if [ $updated -eq 1 ]; then
cp /etc/default/grub $grub_backup
mv "$tmpgrub" /etc/default/grub
rm -r "$tmp"
if ! update-grub >/dev/null 2>&1 ; then
if ! update-grub >/dev/null 2>&1; then
mv $grub_backup /etc/default/grub
echo "Grub update failed."
exit 1
@@ -268,4 +274,4 @@ else
echo "Grub already configured."
fi
exit 0
exit 0

View File

@@ -43,6 +43,46 @@ function set_cpu_info() {
[ -z $cpu_mhz ] && cpu_mhz=$(lscpu | grep -i "^CPU MHz:" | sed 's/CPU MHz://g' | sed 's/\.[0-9]*//g' | xargs)
}
function enable_evernode_auto_updater() {
# Create the service.
echo "[Unit]
Description=Service for the Evernode auto-update.
After=network.target
[Service]
User=root
Group=root
Type=oneshot
ExecStart=/usr/bin/evernode update -q
[Install]
WantedBy=multi-user.target" >/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.service
# Create a timer for the service (every two hours).
echo "[Unit]
Description=Timer for the Evernode auto-update.
# Allow manual starts
RefuseManualStart=no
# Allow manual stops
RefuseManualStop=no
[Timer]
Unit=$EVERNODE_AUTO_UPDATE_SERVICE.service
OnCalendar=0/2:00:00
# Execute job if it missed a run due to machine being off
Persistent=true
[Install]
WantedBy=timers.target" >/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.timer
# Reload the systemd daemon.
systemctl daemon-reload
echo "Enabling Evernode auto update service..."
systemctl enable $EVERNODE_AUTO_UPDATE_SERVICE.service
echo "Enabling Evernode auto update timer..."
systemctl enable $EVERNODE_AUTO_UPDATE_SERVICE.timer
echo "Starting Evernode auto update timer..."
systemctl start $EVERNODE_AUTO_UPDATE_SERVICE.timer
}
# Check cgroup rule config exists.
[ ! -f /etc/cgred.conf ] && echo "cgroups is not configured. Make sure you've installed and configured cgroup-tools." && exit 1
@@ -269,4 +309,8 @@ if [ ! -f /run/reboot-required.pkgs ] || [ ! -n "$(grep sashimono /run/reboot-re
fi
echo "Sashimono installed successfully."
# Enable the Evernode Auto Updater Service.
[ "$UPGRADE" == "0" ] && enable_evernode_auto_updater
exit 0

View File

@@ -28,6 +28,24 @@ function cgrulesengd_servicename() {
fi
}
function remove_evernode_auto_updater() {
echo "Removing Evernode auto update timer..."
systemctl stop $EVERNODE_AUTO_UPDATE_SERVICE.timer
systemctl disable $EVERNODE_AUTO_UPDATE_SERVICE.timer
service_path="/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.timer"
rm $service_path
echo "Removing Evernode auto update service..."
systemctl stop $EVERNODE_AUTO_UPDATE_SERVICE.service
systemctl disable $EVERNODE_AUTO_UPDATE_SERVICE.service
service_path="/etc/systemd/system/$EVERNODE_AUTO_UPDATE_SERVICE.service"
rm $service_path
# Reload the systemd daemon.
systemctl daemon-reload
}
[ ! -d $SASHIMONO_BIN ] && echo "$SASHIMONO_BIN does not exist. Aborting uninstall." && exit 1
# Message board---------------------
@@ -87,8 +105,10 @@ rm $service_path
# Reload the systemd daemon after removing the service
systemctl daemon-reload
echo "Removing Sashimono private docker registry..."
$SASHIMONO_BIN/docker-registry-uninstall.sh
if [ -f $SASHIMONO_BIN/docker-registry-uninstall.sh ]; then
echo "Removing Sashimono private docker registry..."
$SASHIMONO_BIN/docker-registry-uninstall.sh
fi
# Delete binaries except message board and sashimnono uninstall script.
# We keep uninstall script so user can uninstall again if error occured at later steps.
@@ -154,4 +174,7 @@ groupdel $SASHIADMIN_GROUP
[ "$UPGRADE" == "0" ] && echo "Sashimono uninstalled successfully." || echo "Sashimono uninstalled successfully. Your data has been preserved."
# Remove the Evernode Auto Updater Service.
[ "$UPGRADE" == "0" ] && remove_evernode_auto_updater
exit 0

View File

@@ -12,10 +12,13 @@ instances_per_core=3
evernode_alias=/usr/bin/evernode
log_dir=/tmp/evernode-beta
cloud_storage="https://stevernode.blob.core.windows.net/evernode-beta"
script_url="$cloud_storage/setup.sh"
setup_script_url="$cloud_storage/setup.sh"
installer_url="$cloud_storage/installer.tar.gz"
licence_url="$cloud_storage/licence.txt"
version_timestamp_file="version.timestamp"
installer_version_timestamp_file="installer.version.timestamp"
setup_version_timestamp_file="setup.version.timestamp"
# export vars used by Sashimono installer.
export USER_BIN=/usr/bin
@@ -33,6 +36,7 @@ export SASHIUSER_PREFIX="sashi"
export MB_XRPL_USER="sashimbxrpl"
export CG_SUFFIX="-cg"
export EVERNODE_REGISTRY_ADDRESS="r3cNR2bdao1NyvQ5ZuQvCUgqkoWGmgF34E"
export EVERNODE_AUTO_UPDATE_SERVICE="evernode-auto-update"
# Private docker registry (not used for now)
export DOCKER_REGISTRY_USER="sashidockerreg"
@@ -96,7 +100,7 @@ function confirm() {
echo -en $1" [Y/n] "
local yn=""
read yn </dev/tty
# Default choice is 'y'
[ -z $yn ] && yn="y"
while ! [[ $yn =~ ^[Yy|Nn]$ ]]; do
@@ -346,16 +350,18 @@ function uninstall_failure() {
}
function online_version_timestamp() {
# Send HTTP HEAD request and get last modified timestamp of the installer package.
curl --silent --head $installer_url | grep 'Last-Modified:' | sed 's/[^ ]* //'
# Send HTTP HEAD request and get last modified timestamp of the installer package or setup.sh.
curl --silent --head $1 | grep 'Last-Modified:' | sed 's/[^ ]* //'
}
function install_evernode() {
local upgrade=$1
# Get installer version (timestamp). We use this later to check for Evernode software updates.
local version_timestamp=$(online_version_timestamp)
[ -z "$version_timestamp" ] && echo "Online installer not found." && exit 1
local installer_version_timestamp=$(online_version_timestamp $installer_url)
[ -z "$installer_version_timestamp" ] && echo "Online installer not found." && exit 1
# Get setup version (timestamp).
local setup_version_timestamp=$(online_version_timestamp $setup_script_url)
local tmp=$(mktemp -d)
cd $tmp
@@ -375,7 +381,7 @@ function install_evernode() {
# Create evernode cli alias at the begining.
# So, if the installation attempt failed user can uninstall the failed installation using evernode commands.
create_evernode_alias
! create_evernode_alias && install_failure
# Adding ip address as the host description.
description=$inetaddr
@@ -393,7 +399,8 @@ function install_evernode() {
rm -r $tmp
# Write the verison timestamp to a file for later updated version comparison.
echo $version_timestamp > $SASHIMONO_DATA/$version_timestamp_file
echo $installer_version_timestamp > $SASHIMONO_DATA/$installer_version_timestamp_file
echo $setup_version_timestamp > $SASHIMONO_DATA/$setup_version_timestamp_file
}
function uninstall_evernode() {
@@ -425,18 +432,30 @@ function uninstall_evernode() {
function update_evernode() {
echo "Checking for updates..."
local latest=$(online_version_timestamp)
[ -z "$latest" ] && echo "Could not check for updates. Online installer not found." && exit 1
local latest_installer_script_version=$(online_version_timestamp $installer_url)
local latest_setup_script_version=$(online_version_timestamp $setup_script_url)
[ -z "$latest_installer_script_version" ] && echo "Could not check for updates. Online installer not found." && exit 1
local current=$(cat $SASHIMONO_DATA/$version_timestamp_file)
[ "$latest" == "$current" ] && echo "Your $evernode installation is up to date." && exit 0
local current_installer_script_version=$(cat $SASHIMONO_DATA/$installer_version_timestamp_file)
local current_setup_script_version=$(cat $SASHIMONO_DATA/$setup_version_timestamp_file)
[ "$latest_installer_script_version" == "$current_installer_script_version" ] && [ "$latest_setup_script_version" == "$current_setup_script_version" ] && echo "Your $evernode installation is up to date." && exit 0
echo "New $evernode update available. Setup will re-install $evernode with updated software. Your account and contract instances will be preserved."
$interactive && ! confirm "\nDo you want to install the update?" && exit 1
uninstall_evernode 1
echo "Starting upgrade..."
install_evernode 1
# Alias for setup.sh is created during 'install_evernode' too.
# If only the setup.sh is updated but not the installer, then the alias should be created again.
if [ "$latest_installer_script_version" != "$current_installer_script_version" ] ; then
uninstall_evernode 1
install_evernode 1
elif [ "$latest_setup_script_version" != "$current_setup_script_version" ] ; then
[ -d $log_dir ] || mkdir -p $log_dir
logfile="$log_dir/installer-$(date +%s).log"
! create_evernode_alias && echo "Alias creation failed."
echo $latest_setup_script_version > $SASHIMONO_DATA/$setup_version_timestamp_file
fi
echo "Upgrade complete."
}
@@ -464,8 +483,9 @@ function create_log() {
# 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
! curl -fsSL $setup_script_url --output $evernode_alias >> $logfile 2>&1 && echo "Error in creating alias." && return 1
! chmod +x $evernode_alias >> $logfile 2>&1 && echo "Error in changing permission for the alias." && return 1
return 0
}
function remove_evernode_alias() {
@@ -498,6 +518,7 @@ function reg_info() {
fi
}
# Begin setup execution flow --------------------
echo "Thank you for trying out $evernode!"

View File

@@ -20,7 +20,7 @@ appenv = {
ACQUIRE_LEASE_TIMEOUT_THRESHOLD: 0.8,
ACQUIRE_LEASE_WAIT_TIMEOUT_THRESHOLD: 0.4,
SASHI_CLI_PATH: appenv.IS_DEV_MODE ? "../build/sashi" : "/usr/bin/sashi",
MB_VERSION: '0.5.2',
MB_VERSION: '0.5.3',
TOS_HASH: '757A0237B44D8B2BBB04AE2BAD5813858E0AECD2F0B217075E27E0630BA74314' // This is the sha256 hash of TOS text.
}
Object.freeze(appenv);

View File

@@ -107,14 +107,15 @@ class MessageBoard {
console.log(`Moments exceeded (current ledger:${e.ledger_index}, expiry ledger:${x.expiryLedger}). Destroying ${x.containerName}`);
// Expire the current lease agreement (Burn the instance NFT) and re-minting and creating sell offer for the same lease index.
const nft = (await (new evernode.XrplAccount(x.tenant)).getNfts())?.find(n => n.NFTokenID == x.containerName);
// If there's no nft for this record it should be already burned and instance is destroyed, So we only delete the record.
if (!nft)
throw `Cannot find a NFT for ${x.containerName}`;
console.log(`Cannot find a NFT for ${x.containerName}`);
else {
const uriInfo = evernode.UtilHelpers.decodeLeaseNftUri(nft.URI);
await this.destroyInstance(x.containerName, x.tenant, uriInfo.leaseIndex, true);
}
const uriInfo = evernode.UtilHelpers.decodeLeaseNftUri(nft.URI);
await this.destroyInstance(x.containerName, x.tenant, uriInfo.leaseIndex, true);
this.activeInstanceCount--;
await this.hostClient.updateRegInfo(this.activeInstanceCount);
/**
* Soft deletion for debugging purpose.
*/
@@ -122,6 +123,8 @@ class MessageBoard {
// Delete the lease record related to this instance (Permanent Delete).
await this.deleteLeaseRecord(x.txHash);
await this.hostClient.updateRegInfo(this.activeInstanceCount);
console.log(`Destroyed ${x.containerName}`);
}
catch (e) {

View File

@@ -42,7 +42,16 @@ namespace hp
constexpr const char *CHOWN_DIR = "chown -R %s:%s %s";
// Error codes used in create and initiate instance.
constexpr const char *INTERNAL_ERROR = "internal_error";
constexpr const char *DB_READ_ERROR = "db_read_error";
constexpr const char *DB_WRITE_ERROR = "db_write_error";
constexpr const char *USER_INSTALL_ERROR = "user_install_error";
constexpr const char *INSTANCE_ERROR = "instance_error";
constexpr const char *CONF_READ_ERROR = "conf_read_error";
constexpr const char *CONTAINER_CONF_ERROR = "container_conf_error";
constexpr const char *CONTAINER_START_ERROR = "container_start_error";
constexpr const char *CONTAINER_UPDATE_ERROR = "container_update_error";
constexpr const char *NO_CONTAINER = "no_container";
constexpr const char *DUP_CONTAINER = "dup_container";
constexpr const char *MAX_ALLOCATION_REACHED = "max_alloc_reached";
constexpr const char *CONTRACT_ID_INVALID = "contractid_bad_format";
constexpr const char *DOCKER_IMAGE_INVALID = "docker_image_invalid";
@@ -115,7 +124,7 @@ namespace hp
const int allocated_count = sqlite::get_allocated_instance_count(db);
if (allocated_count == -1)
{
error_msg = INTERNAL_ERROR;
error_msg = DB_READ_ERROR;
LOG_ERROR << "Error getting allocated instance count from db.";
return -1;
}
@@ -166,7 +175,7 @@ namespace hp
std::string username;
if (install_user(user_id, username, instance_resources.cpu_us, instance_resources.mem_kbytes, instance_resources.swap_kbytes, instance_resources.storage_kbytes, container_name, instance_ports, image_name) == -1)
{
error_msg = INTERNAL_ERROR;
error_msg = USER_INSTALL_ERROR;
return -1;
}
@@ -175,7 +184,7 @@ namespace hp
if (create_contract(username, owner_pubkey, contract_id, contract_dir, instance_ports, info) == -1 ||
create_container(username, image_name, container_name, contract_dir, instance_ports, info) == -1)
{
error_msg = INTERNAL_ERROR;
error_msg = INSTANCE_ERROR;
LOG_ERROR << "Error creating hp instance for " << owner_pubkey;
// Remove user if instance creation failed.
uninstall_user(username, instance_ports, container_name);
@@ -184,7 +193,7 @@ namespace hp
if (sqlite::insert_hp_instance_row(db, info) == -1)
{
error_msg = INTERNAL_ERROR;
error_msg = DB_WRITE_ERROR;
LOG_ERROR << "Error inserting instance data into db for " << owner_pubkey;
// Remove container and uninstall user if database update failed.
docker_remove(username, container_name);
@@ -213,13 +222,13 @@ namespace hp
const int res = sqlite::is_container_exists(db, container_name, info);
if (res == 0)
{
error_msg = INTERNAL_ERROR;
error_msg = NO_CONTAINER;
LOG_ERROR << "Given container not found. name: " << container_name;
return -1;
}
else if (info.status != CONTAINER_STATES[STATES::CREATED])
{
error_msg = INTERNAL_ERROR;
error_msg = DUP_CONTAINER;
LOG_ERROR << "Given container is already initiated. name: " << container_name;
return -1;
}
@@ -231,7 +240,7 @@ namespace hp
const int config_fd = open(config_file_path.data(), O_RDWR, FILE_PERMS);
if (config_fd == -1)
{
error_msg = INTERNAL_ERROR;
error_msg = CONF_READ_ERROR;
LOG_ERROR << errno << ": Error opening config file " << config_file_path;
return -1;
}
@@ -246,7 +255,7 @@ namespace hp
hpfs::update_service_conf(info.username, hpfs_log_level, is_full_history) == -1 ||
hpfs::start_hpfs_systemd(info.username) == -1)
{
error_msg = INTERNAL_ERROR;
error_msg = CONTAINER_CONF_ERROR;
LOG_ERROR << "Error when setting up container. name: " << container_name;
close(config_fd);
return -1;
@@ -255,7 +264,7 @@ namespace hp
if (docker_start(info.username, container_name) == -1)
{
error_msg = INTERNAL_ERROR;
error_msg = CONTAINER_START_ERROR;
LOG_ERROR << "Error when starting container. name: " << container_name;
// Stop started hpfs processes if starting instance failed.
hpfs::stop_hpfs_systemd(info.username);
@@ -264,8 +273,8 @@ namespace hp
if (sqlite::update_status_in_container(db, container_name, CONTAINER_STATES[STATES::RUNNING]) == -1)
{
error_msg = INTERNAL_ERROR;
LOG_ERROR << "Error when starting container. name: " << container_name;
error_msg = CONTAINER_UPDATE_ERROR;
LOG_ERROR << "Error when updating container status. name: " << container_name;
// Stop started docker and hpfs processes if database update fails.
docker_stop(info.username, container_name);
hpfs::stop_hpfs_systemd(info.username);

View File

@@ -6,7 +6,7 @@
namespace version
{
// Sashimono agent version. Written to new configs.
constexpr const char *AGENT_VERSION = "0.5.2";
constexpr const char *AGENT_VERSION = "0.5.3";
// Minimum compatible config version (this will be used to validate configs).
constexpr const char *MIN_CONFIG_VERSION = "0.5.0";