Skip to content

Instantly share code, notes, and snippets.

@maczniak
Created June 26, 2023 01:52
Show Gist options
  • Save maczniak/0b0310677791c32f9de748a33b654b16 to your computer and use it in GitHub Desktop.
Save maczniak/0b0310677791c32f9de748a33b654b16 to your computer and use it in GitHub Desktop.
## shebang https://ko.wikipedia.org/wiki/%EC%85%94%EB%B1%85 참고
#!/bin/sh
src_dir="contrib"
near_postfix="near"
network="mainnet"
near_source="nearcore" # nearcore or datalake
migrate_from=""
use_snapshots=1
download_workers=256
## 쉘 내장 명령어 (즉, 따로 실행파일이 없음)
## https://www.man7.org/linux/man-pages/man1/bash.1.html#SHELL_BUILTIN_COMMANDS 참고
## SIGINT나 SIGTERM 시그널을 받으면 문구를 출력하고 프로그램을 종료하게 만듬
## SIGTERM은 Ctrl-C를 누르거나 kill하면 발생하는 시그널
## 원래 두 시그널 모두 단순히 프로그램을 종료. man signal 참고
trap "echo Exited!; exit 2;" INT TERM
## 원하는 디렉토리가 존재하는지 검사
## [ ... ]는 test의 축약형. 중요! https://www.man7.org/linux/man-pages/man1/test.1.html 참고
if [ ! -d "./${src_dir}" ]; then
echo "Run ./install.sh from original git repository only!"
exit 1
fi
## common.sh 파일 실행 (여러 스크립트가 공유하는 값이나 함수를 이렇게 정의함)
## 그냥 스크립트를 실행하면 스크립트가 끝날 때 스크립트에서 정의한 값이나 환경변수나 함수가 사라짐
## 마침표(혹은 source)를 앞에 붙이면 스크립트가 정의한 값이나 환경변수나 함수가 유지됨
. ./${src_dir}/bin/common.sh
## install 함수를 정의함. 제일 아래에서 실행
install() {
if [ -f "${VERSION_FILE}" ]; then
echo "There is already an Aurora Standalone RPC installation running or an unfinished installation exists"
if confirmed "To continue with installation, you have to first uninstall. Would you like to uninstall?"; then
./uninstall.sh
else
exit 1
fi
fi
## &&는 앞 명령어가 문제없이 실행되었을 때만 뒤 명령어를 실행
## &&은 logical and로 앞과 뒤가 모두 참일 때만 참
## 그래서 앞이 거짓이면 뒤를 볼 (여기서는 실행해볼) 필요도 없이 거짓
## 프로그래밍에서 shortcut이라고 부름
## 반대로 logical or인 ||은 앞 명령어가 성공하면 뒤 명령어를 실행안함
## 앞과 뒤 하나만 참이어도 참이기 때문에 앞이 참이면 뒤를 볼 필요가 없기 때문에
## tee는 파이프로 받은 내용을 출력하면서 동시에 파일에 기록
## https://man7.org/linux/man-pages/man1/tee.1.html 참고
echo "Installing" && version | tee "${VERSION_FILE}"
## 쉘 내장 명령어
## 파이프 중 한 명령어라도 실패하면 전체 스크립트 종료하게 만듬
## 중간에 명령어가 실패했는데 계속 스크립트를 실행해서 예상할 수 없는 피해가 발생하지 않게 방지
## 자바스크립트의 "use strict"; 같은 역할
set -e
## mkdir -p 옵션은 경로중 디렉토리가 없으면 함께 만듬
## 즉, mkdir -p a/b/c a와 a/b 디렉토리가 없으면 함께 생성
mkdir -p "${INSTALL_DIR}/data/relayer" \
"${INSTALL_DIR}/data/refiner" \
"${INSTALL_DIR}/config/relayer" \
"${INSTALL_DIR}/config/refiner" \
"${INSTALL_DIR}/config/nginx" 2> /dev/null
if [ ! -f "${INSTALL_DIR}/config/relayer/relayer.yaml" ]; then
cp "./${src_dir}/config/relayer/${network}.yaml" "${INSTALL_DIR}/config/relayer/relayer.yaml"
fi
if [ ! -f "${INSTALL_DIR}/config/relayer/relayer.json" ]; then
echo "Generating relayer key..."
## $(...)는 명령어를 실행한 결과로 대치됨
## 과거에는 `...`같이 backtick을 사용했는데 중첩된(nested) 명령어를 처리할 수 없는 단점
## $(... $(...)) vs `... `...`` (`... ` + ... + ``으로 해석됨)
docker run --rm --name near_keygen -v "$(pwd)/${INSTALL_DIR}"/config/relayer:/config:rw --entrypoint /bin/sh nearaurora/srpc2-relayer -c "/usr/local/bin/nearkey relayer%.${near_postfix} > /config/relayer.json"
## relayer.json 파일에서 account_id란 단어가 있는 줄을 찾고 4번째 필드 추출. " 문자로 필드 구분
## relayerName=$(grep account_id "${INSTALL_DIR}/config/relayer/relayer.json" | cut -d\" -f4) 과 동일
relayerName=$(cat "${INSTALL_DIR}/config/relayer/relayer.json" | grep account_id | cut -d\" -f4)
## relayer.yaml 파일에서 %%SIGNER%% 부분을 ${relayerName} (즉, 위 명령어 결과값)으로 변경
## relayer.yaml2 파일에 적고 아래에서 다시 relayer.yaml 파일로 이름을 바꾸는 건 중요!
## sed ... relayer.yaml > relayer.yaml 처럼 적으면 sed가 relayer.yaml 파일을 읽기도 전에
## (> relayer.yaml 때문에) 쉘이 relayer.yaml 파일을 빈파일로 만들어버림
sed "s/%%SIGNER%%/${relayerName}/" "${INSTALL_DIR}/config/relayer/relayer.yaml" > "${INSTALL_DIR}/config/relayer/relayer.yaml2" && \
mv "${INSTALL_DIR}/config/relayer/relayer.yaml2" "${INSTALL_DIR}/config/relayer/relayer.yaml"
fi
if [ ! -f "${INSTALL_DIR}/config/relayer/filter.yaml" ]; then
cp "./${src_dir}/config/relayer/filter.yaml" "${INSTALL_DIR}/config/relayer/filter.yaml"
fi
if [ ! -f "${INSTALL_DIR}/config/refiner/refiner.json" ]; then
cp "./${src_dir}/config/refiner/${network}_${near_source}.json" "${INSTALL_DIR}/config/refiner/refiner.json"
fi
if [ ! -f "${INSTALL_DIR}/config/nginx/endpoint.conf" ]; then
cp "./${src_dir}/config/nginx/${network}.conf" "${INSTALL_DIR}/config/nginx/endpoint.conf"
fi
## $use_snapshots과 $migrate_from는 스크립트 옵션으로 지정하는 값 (파일 하단 참고)
## "x$migrate_from" != "x"는 $migrate_from이 비어있지 않으면 참
if [ $use_snapshots -eq 1 ] || [ "x$migrate_from" != "x" ]; then
latest=""
if [ ! -f "${INSTALL_DIR}/.latest" ]; then
echo Initial
## curl 옵션은 https://curl.se/docs/manpage.html 참고
## 군더더기 (curl 프로그램이 추가한 메시지) 없이 URL 내용만 얻기위해 사용
latest=$(curl -sSf https://snapshots.deploy.aurora.dev/snapshots/${network}-relayer2-latest)
echo "${latest}" > "${INSTALL_DIR}/.latest"
fi
latest=$(cat "${INSTALL_DIR}/.latest")
if [ ! -f "${INSTALL_DIR}/data/relayer/.version" ]; then
echo "Downloading database snapshot ${latest}..."
finish=0
## tar 파일을 다운받고 풀어서 .version 파일이 생길 때까지 반복해서 다운로드. 다운로드가 자주 실패해서 넣은 듯
while [ ${finish} -eq 0 ]; do
echo "Fetching, this can take some time..."
## tar -x ... >> lastfile 2> /dev/null 은 tar 파일의 결과(즉, tar 파일에 묶인 파일 목록)를 lastfile 뒤에 추가하고,
## 오류는 (2>) 출력 안함 (/dev/null). 다운받다 중간에서 끊기면 tar가 깨진 파일이라고 오류를 출력해서 넣은 듯
curl -#Sf https://snapshots.deploy.aurora.dev/158c1b69348fda67682197791/${network}-relayer2-"${latest}"/data.tar | tar -xv -C "${INSTALL_DIR}/data/relayer/" >> "${INSTALL_DIR}/data/relayer/.lastfile" 2> /dev/null
if [ -f "${INSTALL_DIR}/data/relayer/.version" ]; then
finish=1
fi
done
fi
fi
if [ ${near_source} = "nearcore" ]; then
if [ "x$migrate_from" != "x" ]; then
## symbolic (-s) link (ln)라고 뒤 이름이 앞 파일(혹은 디렉토리)을 지칭
## 윈도우의 바로가기에 해당
## 여러 버전에 해당하는 디렉토리가 있을 때 현재 사용하는 버전을 지칭하는 등의 목적으로 실볼링크를 사용
## 예를 들어, node-v18.16.1와 node-v20.3.1 디렉토리가 있을 때, node가 둘 중에 한 디렉토리를 가리키고, PATH에 node/bin를 추가
ln -s "$migrate_from/near" "${INSTALL_DIR}/near"
ln -s "$migrate_from/engine" "${INSTALL_DIR}/engine"
else
mkdir -p "${INSTALL_DIR}/near" "${INSTALL_DIR}/engine" 2> /dev/null
if [ ! -f "${INSTALL_DIR}/near/config.json" ]; then
echo "Downloading default configuration..."
curl -sSf -o "${INSTALL_DIR}/near/config.json" https://files.deploy.aurora.dev/"${network}"-new-rpc/config.json
fi
if [ ! -f "${INSTALL_DIR}/near/genesis.json" ]; then
echo "Downloading genesis file..."
curl -sSf -o "${INSTALL_DIR}/near/genesis.json.gz" https://files.deploy.aurora.dev/"${network}"-new-rpc/genesis.json.gz
echo "Extracting genesis file..."
gzip -d "${INSTALL_DIR}/near/genesis.json.gz"
fi
if [ ! -f "${INSTALL_DIR}/near/node_key.json" ]; then
echo "Generating node_key..."
docker run --rm --name near_keygen -v "$(pwd)/${INSTALL_DIR}"/near:/near:rw --entrypoint /bin/sh nearaurora/srpc2-relayer -c "/usr/local/bin/nearkey node%.${near_postfix} > /near/node_key.json"
fi
if [ ! -f "${INSTALL_DIR}/near/validator_key.json" ]; then
echo "Generating validator_key..."
docker run --rm --name near_keygen -v "$(pwd)/${INSTALL_DIR}"/near:/near:rw --entrypoint /bin/sh nearaurora/srpc2-relayer -c "/usr/local/bin/nearkey node%.${near_postfix} > /near/validator_key.json"
fi
if [ $use_snapshots -eq 1 ] && [ ! -f "${INSTALL_DIR}/near/data/CURRENT" ]; then
echo "Downloading near chain snapshot..."
latest=$(docker run --rm --entrypoint /bin/sh nearaurora/srpc2-relayer -c "/usr/local/bin/s5cmd --no-sign-request --numworkers $download_workers cat s3://near-protocol-public/backups/${network}/rpc/latest")
finish=0
while [ ${finish} -eq 0 ]; do
echo "Fetching, this can take some time..."
docker run --rm --name near_downloader -v "$(pwd)/${INSTALL_DIR}"/near:/near:rw --entrypoint /bin/sh nearaurora/srpc2-relayer -c "/usr/local/bin/s5cmd --stat --no-sign-request cp s3://near-protocol-public/backups/${network}/rpc/"${latest}"/* /near/data/"
if [ -f "${INSTALL_DIR}/near/data/CURRENT" ]; then
finish=1
fi
done
fi
fi
cp "./${src_dir}/config/docker/${network}_${near_source}.yaml" "${INSTALL_DIR}/docker-compose.yaml"
elif [ ${near_source} = "datalake" ]; then
## $AWS_SHARED_CREDENTIALS_FILE는 환경변수. 환경변수 이름은 보통 모두 대문자로
## 환경변수는 XXX=YYY export XXX, export XXX=YYY (다음부터 실행하는 모든 명령어에 적용), XXX=YYY 명령어 (이 명령어에만 적용) 식으로 정의
## AWS 로그인 토큰값을 저장한 파일로 보임. 아래 sed가 이 파일명을 docker-compose.yaml에 기록
if [ "x$AWS_SHARED_CREDENTIALS_FILE" = "x" ]; then
echo "Installation failed, environment variable AWS_SHARED_CREDENTIALS_FILE is needed for datalake config." \
"Please set environment variable AWS_SHARED_CREDENTIALS_FILE for [$USER] user or run installer as 'AWS_SHARED_CREDENTIALS_FILE={path to AWS credentials file} ./install.sh'"
echo "For more details, also see https://docs.aws.amazon.com/sdkref/latest/guide/file-location.html"
exit 1
fi
if [ ! -f "$AWS_SHARED_CREDENTIALS_FILE" ]; then
echo "Installation failed, datalake config requires AWS account." \
"Create [$AWS_SHARED_CREDENTIALS_FILE] file and run install script again!"
exit 1
fi
sed "s|%%AWS%%|${AWS_SHARED_CREDENTIALS_FILE}|" "./${src_dir}/config/docker/${network}_${near_source}.yaml" > "${INSTALL_DIR}/docker-compose.yaml"
else
echo "Installation failed, invalid near data source. It should either be 'datalake' or 'nearcore' !"
exit 1
fi
if [ $use_snapshots -eq 0 ] \
## [ ... ] 즉, test에서 -a는 logical and. 반대로 -o는 logical or
|| [ ${near_source} = "nearcore" -a -f "${INSTALL_DIR}/near/data/CURRENT" -a -f "${INSTALL_DIR}/data/relayer/.version" ] \
|| [ ${near_source} = "datalake" -a -f "${INSTALL_DIR}/data/relayer/.version" ]; then
echo "Setup complete [${network}, ${near_source}]"
fi
set +e
if [ "x$migrate_from" != "x" ]; then
"$migrate_from"/stop.sh
fi
cp "./${src_dir}/bin/start.sh" "${INSTALL_DIR}/start.sh"
cp "./${src_dir}/bin/stop.sh" "${INSTALL_DIR}/stop.sh"
cp "./${src_dir}/bin/common.sh" "${INSTALL_DIR}/common.sh"
echo "Starting..."
./"${INSTALL_DIR}"/start.sh
}
version() {
if [ ! -f "${VERSION_FILE}" ]; then
echo "Aurora Standalone RPC"
branch=$(git rev-parse --abbrev-ref HEAD) && echo "branch: $branch" \
&& commit=$(git rev-parse HEAD) && echo "commit: $commit" \
&& tag=$(git describe --exact-match "$commit") 2>/dev/null && echo "tag: $tag"
else
cat "${VERSION_FILE}"
fi
}
usage() {
printf '\nUsage: %s [options]' "$(basename "$0")"
printf '\nOptions\n'
printf ' %s\t%s\n' "-n {mainnet|testnet}" "network to use, default is mainnet."
printf ' %s\t%s\n' "-r {nearcore|datalake}" "near source for indexing, default is nearcore."
printf ' %s\t\t%s\n\t\t\t%s\n' "-m {path}" "use the existing nearcore data at 'path' instead of downloading snapshots from scratch." \
"This option is valid only if nearcore config is used, and '-s' option is ignored if this option is given."
printf ' %s\t%s\n\t\t\t%s\n' "-w {number [1-256]}" "number of workers used for downloading near snapshots, default is 256." \
"NOTE: On some OS and HW configurations, default number of workers may cause high CPU consumption during download."
printf ' %s\t\t\t%s\n\t\t\t%s\n\t\t\t%s\n' "-s" "if specified then snapshots are ignored during installation, default downloads and uses snapshots." \
"NOTE: Ignoring snapshots may cause refiner not to index near chain. This can only be a valid option" \
"if near source is selected as datalake otherwise refiner will not be sync with near core from scratch."
printf ' %s\t\t\t%s\n' "-v" "prints version"
printf ' %s\t\t\t%s\n\t\t\t%s\n' "-h" "prints usage"
printf 'Example\n%s\n\n' "./install.sh -n mainnet -r datalake -s"
}
## 프로그래밍 언어별 옵션을 처리하는 라이브러리가 존재. 쉘에서도 옵션을 쉽게 처리할 수 있도록 getopt 혹은 getopts 프로그램이 있음
## getopts ... opt는 스크립트 아규먼트 중 옵션을 찾아서 opt 변수에 넣음. 다 찾으면 while 반복문 종료
## n:, r:, m:, w:는 뒤에 아규먼트가 있고 (예, -n mainnet), 아규먼트 내용을 OPTARG 변수에 넣음
## svh는 뒤에 아규먼트가 없는 단일 옵션 (예, -h)
while getopts ":n:r:m:w:svh" opt; do
case "${opt}" in
n)
network="${OPTARG}"
if [ "$network" != "mainnet" ] && [ "$network" != "testnet" ]; then
echo "Invalid Value: -${opt} cannot be '${OPTARG}'"
usage
exit 1
fi
;;
r)
near_source="${OPTARG}"
if [ "$near_source" != "nearcore" ] && [ "$near_source" != "datalake" ]; then
echo "Invalid Value: -${opt} cannot be '${OPTARG}'"
usage
exit 1
fi
;;
w)
download_workers="${OPTARG}"
if [ "$download_workers" -lt 1 ] || [ "$download_workers" -gt 256 ]; then
echo "Invalid Value: -${opt} cannot be '${OPTARG}'"
usage
exit 1
fi
;;
m)
migrate_from=$(realpath "${OPTARG}")
if [ ! -d "$migrate_from/near" ] || [ ! -d "$migrate_from/engine" ]; then
echo "Invalid Value: path(s) '${OPTARG}/near' or(and) '${OPTARG}/engine' not exist"
usage
exit 1
fi
;;
s)
use_snapshots=0
;;
v)
version
exit 0
;;
h)
usage
exit 0
;;
\?)
echo "Invalid Option: -${OPTARG}" 1>&2
usage
exit 1
;;
:)
echo "Invalid Option: -${OPTARG} requires an argument" 1>&2
usage
exit 1
;;
esac
done
## shift는 쉘 내장 명령어. 아규먼트를 하나씩 당기는 역할 (예, 스크립트 1번 2번 3번 → 스크립트 2번 3번)
## OPTIND는 getopts가 처리하고 남은 (즉, 옵션이 아닌) 아규먼트의 번호
## $((...))은 expr의 축약형. https://man7.org/linux/man-pages/man1/expr.1.html 참고
## 참거짓 검사 [ ... ] test vs 산술과 문자열 연산 $((...)) expr
## 산술 연산은 expr 외에 awk, bc, dc 혹은 Python 같은 스크립트 언어를 사용하기도 함
## 옵션을 제외한 아규먼트를 1부터 순서대로 지칭할 수 있게 만드는데 이 스크립트에서는 없어도 됨
shift $((OPTIND-1))
install
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment