Skip to content

Commit 68e4c28

Browse files
authored
Merge pull request #159 from aws-samples/solana
Solana
2 parents eec97d7 + dea27ef commit 68e4c28

37 files changed

+892
-598
lines changed

lib/solana/README.md

Lines changed: 36 additions & 38 deletions
Large diffs are not rendered by default.

lib/solana/app.ts

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'dotenv/config'
33
import "source-map-support/register";
44
import * as cdk from "aws-cdk-lib";
55
import * as nag from "cdk-nag";
6-
import * as config from "./lib/config/solanaConfig";
6+
import * as config from "./lib/config/node-config";
77

88
import { SolanaSingleNodeStack } from "./lib/single-node-stack";
99
import { SolanaCommonStack } from "./lib/common-stack";
@@ -34,28 +34,23 @@ new SolanaSingleNodeStack(app, "solana-single-node", {
3434
registrationTransactionFundingAccountSecretARN: config.baseNodeConfig.registrationTransactionFundingAccountSecretARN,
3535
});
3636

37-
if (app.node.tryGetContext('deployHA') === 'true') {
38-
if (config.baseNodeConfig.nodeConfiguration !== "consensus") {
39-
new SolanaHANodesStack(app, "solana-ha-nodes", {
40-
stackName: `solana-ha-nodes-${config.baseNodeConfig.nodeConfiguration}`,
41-
env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
37+
new SolanaHANodesStack(app, "solana-ha-nodes", {
38+
stackName: `solana-ha-nodes-${config.baseNodeConfig.nodeConfiguration}`,
39+
env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
40+
41+
instanceType: config.baseNodeConfig.instanceType,
42+
instanceCpuType: config.baseNodeConfig.instanceCpuType,
43+
solanaCluster: config.baseNodeConfig.solanaCluster,
44+
solanaVersion: config.baseNodeConfig.solanaVersion,
45+
nodeConfiguration: config.baseNodeConfig.nodeConfiguration,
46+
dataVolume: config.baseNodeConfig.dataVolume,
47+
accountsVolume: config.baseNodeConfig.accountsVolume,
4248

43-
instanceType: config.baseNodeConfig.instanceType,
44-
instanceCpuType: config.baseNodeConfig.instanceCpuType,
45-
solanaCluster: config.baseNodeConfig.solanaCluster,
46-
solanaVersion: config.baseNodeConfig.solanaVersion,
47-
nodeConfiguration: config.baseNodeConfig.nodeConfiguration,
48-
dataVolume: config.baseNodeConfig.dataVolume,
49-
accountsVolume: config.baseNodeConfig.accountsVolume,
49+
albHealthCheckGracePeriodMin: config.haNodeConfig.albHealthCheckGracePeriodMin,
50+
heartBeatDelayMin: config.haNodeConfig.heartBeatDelayMin,
51+
numberOfNodes: config.haNodeConfig.numberOfNodes,
52+
});
5053

51-
albHealthCheckGracePeriodMin: config.haNodeConfig.albHealthCheckGracePeriodMin,
52-
heartBeatDelayMin: config.haNodeConfig.heartBeatDelayMin,
53-
numberOfNodes: config.haNodeConfig.numberOfNodes,
54-
});
55-
} else {
56-
throw new Error("Consensus node configuration is not yet supported for HA setup");
57-
}
58-
}
5954

6055
// Security Check
6156
cdk.Aspects.of(app).add(

lib/solana/lib/assets/build-binaries.sh

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/bash
2+
3+
if [ -n "$1" ]; then
4+
export STACK_ID=$1
5+
else
6+
echo "Error: No Stack ID is provided"
7+
echo "Usage: instance/cfn-hup/setup.sh <stack_id> <aws_region>"
8+
exit 1
9+
fi
10+
11+
if [ -n "$2" ]; then
12+
export AWS_REGION=$2
13+
else
14+
echo "Error: No AWS Region is provided"
15+
echo "Usage: instance/cfn-hup/setup.sh <stack_id> <aws_region>"
16+
exit 1
17+
fi
18+
19+
echo "Install CloudFormation helper scripts"
20+
mkdir -p /opt/aws/
21+
pip3 install --break-system-packages https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
22+
ln -s /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup
23+
24+
echo "Configuring CloudFormation helper scripts"
25+
mkdir -p /etc/cfn/
26+
mv /opt/instance/cfn-hup/cfn-hup.conf /etc/cfn/cfn-hup.conf
27+
sed -i "s;__AWS_STACK_ID__;\"$STACK_ID\";g" /etc/cfn/cfn-hup.conf
28+
sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/cfn-hup.conf
29+
30+
mkdir -p /etc/cfn/hooks.d/system
31+
mv /opt/instance/cfn-hup/cfn-auto-reloader.conf /etc/cfn/hooks.d/cfn-auto-reloader.conf
32+
sed -i "s;__AWS_STACK_NAME__;\"$STACK_NAME\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf
33+
sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf
34+
35+
echo "Starting CloudFormation helper scripts as a service"
36+
mv /opt/instance/cfn-hup/cfn-hup.service /etc/systemd/system/cfn-hup.service
37+
38+
systemctl daemon-reload
39+
systemctl enable --now cfn-hup
40+
systemctl start cfn-hup.service
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/bin/bash
2+
3+
make_fs () {
4+
# If file system = to ext4 use mkfs.ext4, if xfs use mkfs.xfs
5+
if [ -z "$1" ]; then
6+
echo "Error: No file system type provided."
7+
echo "Usage: make_fs <file system type [ xfs | ext4 ]> <target_volume_id>"
8+
exit 1
9+
fi
10+
11+
if [ -z "$2" ]; then
12+
echo "Error: No target volume ID provided."
13+
echo "Usage: make_fs <file system type [ xfs | ext4 ]> <target_volume_id>"
14+
exit 1
15+
fi
16+
17+
local file_system=$1
18+
local volume_id=$2
19+
if [ "$file_system" == "ext4" ]; then
20+
mkfs -t ext4 "$volume_id"
21+
return "$?"
22+
else
23+
mkfs.xfs -f "$volume_id"
24+
return "$?"
25+
fi
26+
}
27+
28+
# We need an nvme disk that is not mounted and not partitioned
29+
get_all_empty_nvme_disks () {
30+
local all_not_mounted_nvme_disks
31+
local all_mounted_nvme_partitions
32+
local unmounted_nvme_disks=()
33+
local sorted_unmounted_nvme_disks
34+
35+
all_not_mounted_nvme_disks=$(lsblk -lnb | awk '{if ($7=="") {print $1}}' | grep nvme)
36+
all_mounted_nvme_partitions=$(mount | awk '{print $1}' | grep /dev/nvme)
37+
for disk in ${all_not_mounted_nvme_disks[*]}; do
38+
if [[ ! "${all_mounted_nvme_partitions[*]}" =~ $disk ]]; then
39+
unmounted_nvme_disks+=("$disk")
40+
fi
41+
done
42+
# Sort the array
43+
sorted_unmounted_nvme_disks=($(printf '%s\n' "${unmounted_nvme_disks[*]}" | sort))
44+
echo "${sorted_unmounted_nvme_disks[*]}"
45+
}
46+
47+
get_next_empty_nvme_disk () {
48+
local sorted_unmounted_nvme_disks
49+
sorted_unmounted_nvme_disks=($(get_all_empty_nvme_disks))
50+
# Return the first unmounted nvme disk
51+
echo "/dev/${sorted_unmounted_nvme_disks[0]}"
52+
}
53+
54+
# Add input as command line parameters for name of the directory to mount
55+
if [ -n "$1" ]; then
56+
DIR_NAME=$1
57+
else
58+
echo "Error: No data file system mount path is provided."
59+
echo "Usage: instance/storage/setup.sh <file_system_mount_path> <file_system_type [ xfs | ext4 ]> <target_volume_size_in_bytes> "
60+
echo "Default file system type is ext4"
61+
echo "If you skip <target_volume_size_in_bytes>, script will try to use the first unformatted volume ID."
62+
echo "Usage example: instance/storage/setup.sh /data ext4 300000000000000"
63+
exit 1
64+
fi
65+
66+
# Case input for $2 between ext4 and xfs, use ext4 as default
67+
case $2 in
68+
ext4)
69+
echo "File system set to ext4"
70+
FILE_SYSTEM="ext4"
71+
FS_CONFIG="defaults"
72+
;;
73+
xfs)
74+
echo "File system set to xfs"
75+
FILE_SYSTEM="xfs"
76+
FS_CONFIG="noatime,nodiratime,nodiscard" # See more: https://cdrdv2-public.intel.com/686417/rocksdb-benchmark-tuning-guide-on-xeon.pdf
77+
;;
78+
*)
79+
echo "File system set to ext4"
80+
FILE_SYSTEM="ext4"
81+
FS_CONFIG="defaults"
82+
;;
83+
esac
84+
85+
if [ -n "$3" ]; then
86+
VOLUME_SIZE=$3
87+
else
88+
echo "The size of volume for $DIR_NAME is not specified. Will try to guess volume ID."
89+
fi
90+
91+
echo "Checking if $DIR_NAME is mounted, and dont do anything if it is"
92+
if [ $(df --output=target | grep -c "$DIR_NAME") -lt 1 ]; then
93+
94+
if [ -n "$VOLUME_SIZE" ]; then
95+
VOLUME_ID=/dev/$(lsblk -lnb | awk -v VOLUME_SIZE_BYTES="$VOLUME_SIZE" '{if ($4== VOLUME_SIZE_BYTES) {print $1}}')
96+
echo "Data volume size defined, use respective volume id: $VOLUME_ID"
97+
else
98+
VOLUME_ID=$(get_next_empty_nvme_disk)
99+
echo "Data volume size undefined, trying volume id: $VOLUME_ID"
100+
fi
101+
102+
make_fs $FILE_SYSTEM "$VOLUME_ID"
103+
104+
sleep 10
105+
VOLUME_UUID=$(lsblk -fn -o UUID "$VOLUME_ID")
106+
VOLUME_FSTAB_CONF="UUID=$VOLUME_UUID $DIR_NAME $FILE_SYSTEM $FS_CONFIG 0 2"
107+
echo "VOLUME_ID=$VOLUME_ID"
108+
echo "VOLUME_UUID=$VOLUME_UUID"
109+
echo "VOLUME_FSTAB_CONF=$VOLUME_FSTAB_CONF"
110+
111+
# Check if data disc is already in fstab and replace the line if it is with the new disc UUID
112+
echo "Checking fstab for volume $DIR_NAME"
113+
if [ $(grep -c "$DIR_NAME" /etc/fstab) -gt 0 ]; then
114+
SED_REPLACEMENT_STRING="$(grep -n "$DIR_NAME" /etc/fstab | cut -d: -f1)s#.*#$VOLUME_FSTAB_CONF#"
115+
# if file exists, delete it
116+
if [ -f /etc/fstab.bak ]; then
117+
rm /etc/fstab.bak
118+
fi
119+
cp /etc/fstab /etc/fstab.bak
120+
sed -i "$SED_REPLACEMENT_STRING" /etc/fstab
121+
else
122+
echo "$VOLUME_FSTAB_CONF" | tee -a /etc/fstab
123+
fi
124+
125+
mount -a
126+
chown -R bcuser:bcuser "$DIR_NAME"
127+
else
128+
echo "$DIR_NAME volume is mounted, nothing changed"
129+
fi
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
3+
print_usage() {
4+
echo "Usage: instance/storage/update-cloudwatch-dashboard.sh <DASHBOARD_NAME> ["" | /data/data | /data/accounts]"
5+
echo "Example: instance/storage/update-cloudwatch-dashboard.sh solana-single-node-extendedrpc-i-0f961f2f2646b4bf4"
6+
}
7+
8+
get_device_for_mount_path() {
9+
if [ -n "$1" ]; then
10+
MOUNT_PATH=$1
11+
else
12+
echo "Error: No mount path provided."
13+
echo "Usage: get_device_for_mount_path [/data/accounts | /data/data]"
14+
exit 1
15+
fi
16+
DEVICE=$(mount | grep "${MOUNT_PATH}" | awk '{print $1}')
17+
echo "${DEVICE#/dev/}"
18+
}
19+
20+
get_device_for_replacement () {
21+
if [ -n "$1" ]; then
22+
MOUNT_PATH=$1
23+
else
24+
echo "Error: No mount path provided."
25+
echo "Usage: get_device_for_replacement [/data/accounts | /data/data]"
26+
exit 1
27+
fi
28+
case $MOUNT_PATH in
29+
"/data/accounts")
30+
REPLACING_DEVICE="nvme2n1"
31+
;;
32+
"/data/data")
33+
REPLACING_DEVICE="nvme1n1"
34+
;;
35+
*)
36+
echo "Error: Invalid mount path provided."
37+
echo "Usage: get_device_for_replacement [/data/accounts | /data/data]"
38+
exit 1
39+
;;
40+
esac
41+
echo "${REPLACING_DEVICE}"
42+
}
43+
44+
if [ -n "$1" ]; then
45+
DASHBOARD_NAME=$1
46+
else
47+
echo "Error: No dashboard name provided."
48+
print_usage
49+
exit 1
50+
fi
51+
52+
case $2 in
53+
/data/accounts)
54+
MOUNT_PATHS=("/data/accounts")
55+
;;
56+
/data/data)
57+
MOUNT_PATHS=("/data/data")
58+
;;
59+
*)
60+
MOUNT_PATHS=("/data/accounts" "/data/data")
61+
;;
62+
esac
63+
64+
echo "MOUNT_PATHS=${MOUNT_PATHS[*]}"
65+
66+
# Download cloudwatch dashboard using aws cli
67+
if [ ! -f /tmp/dashboard.json ]; then
68+
aws cloudwatch get-dashboard --dashboard-name "$DASHBOARD_NAME" --output json | jq -r .DashboardBody > /tmp/dashboard.json
69+
fi
70+
71+
for MOUNT_PATH in ${MOUNT_PATHS[*]}; do
72+
DEVICE=$(get_device_for_mount_path "$MOUNT_PATH")
73+
FIND_DEVICE=$(get_device_for_replacement "$MOUNT_PATH")
74+
REPLACING_DEVICE="$DEVICE"
75+
echo "Found device $DEVICE for mount path $MOUNT_PATH"
76+
77+
SED_STRING="s/$FIND_DEVICE/$REPLACING_DEVICE/g"
78+
79+
echo "Replacing $FIND_DEVICE with $REPLACING_DEVICE in /tmp/dashboard.json using $SED_STRING"
80+
sed -i "$SED_STRING" /tmp/dashboard.json
81+
done
82+
83+
# Upload cloudwatch dashboard using aws cli
84+
aws cloudwatch put-dashboard --dashboard-name "$DASHBOARD_NAME" --dashboard-body file:///tmp/dashboard.json
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/bash
2+
3+
if [ -n "$1" ]; then
4+
export SOLANA_VERSION=$1
5+
else
6+
echo "Error: No Solana version is provided"
7+
echo "Usage: node/build-binaries.sh <solana_version>"
8+
exit 1
9+
fi
10+
11+
echo "Install rustc, cargo and rustfmt."
12+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rust-installer.sh
13+
chmod 755 ./rust-installer.sh
14+
./rust-installer.sh -y
15+
16+
export HOME="/root"
17+
source $HOME/.cargo/env
18+
rustup component add rustfmt
19+
20+
echo "Verifying we use the latest stable version of Rust"
21+
rustup update
22+
23+
echo "Getting the source for stable version v$SOLANA_VERSION"
24+
wget https://github.com/anza-xyz/agave/archive/refs/tags/v$SOLANA_VERSION.tar.gz
25+
tar -xzvf v$SOLANA_VERSION.tar.gz
26+
cd agave-$SOLANA_VERSION
27+
28+
echo "Configuring rust version..."
29+
source $PWD/ci/rust-version.sh all
30+
31+
echo "Installing libssl-dev, pkg-config, zlib1g-dev, protobuf etc."
32+
apt-get update
33+
apt-get -y install libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang cmake make libprotobuf-dev protobuf-compiler
34+
35+
echo "Building Solana..."
36+
./scripts/cargo-install-all.sh .
37+
38+
echo "Check agave-validator version"
39+
./bin/agave-validator --version
40+
41+
echo "Modifying path"
42+
if [ ! -d "/home/bcuser/bin" ]; then
43+
mkdir -p /home/bcuser/bin
44+
fi
45+
mv $PWD/bin/* /home/bcuser/bin
46+
echo export PATH=/home/bcuser/bin:$PATH >> /home/bcuser/.profile

0 commit comments

Comments
 (0)