Skip to content
This repository was archived by the owner on Aug 16, 2021. It is now read-only.

Commit 058a38f

Browse files
authored
Merge pull request #92 from postgres-ai/dmius-duration-param
Spot live time duration option added
2 parents 7ed2114 + 0fd9110 commit 058a38f

File tree

2 files changed

+173
-8
lines changed

2 files changed

+173
-8
lines changed

nancy_run.sh

Lines changed: 154 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ KEEP_ALIVE=0
1616
VERBOSE_OUTPUT_REDIRECT=" > /dev/null"
1717
EBS_SIZE_MULTIPLIER=15
1818
POSTGRES_VERSION_DEFAULT=10
19+
AWS_BLOCK_DURATION=0 # by default no time limit
1920

2021
#######################################
2122
# Print an error/warning/notice message to STDERR
@@ -498,6 +499,8 @@ while [ $# -gt 0 ]; do
498499
AWS_SSH_KEY_PATH="$2"; shift 2 ;;
499500
--aws-ebs-volume-size )
500501
AWS_EBS_VOLUME_SIZE="$2"; shift 2 ;;
502+
--aws-block-duration )
503+
AWS_BLOCK_DURATION=$2; shift 2 ;;
501504

502505
--s3cfg-path )
503506
S3_CFG_PATH="$2"; shift 2 ;;
@@ -546,6 +549,45 @@ if $DEBUG ; then
546549
echo "AWS_EBS_VOLUME_SIZE: $AWS_EBS_VOLUME_SIZE"
547550
fi
548551

552+
#######################################
553+
# Check path to file/directory.
554+
# Globals:
555+
# None
556+
# Arguments:
557+
# (text) name of the variable holding the
558+
# file path (starts with 'file://' or 's3://') or any string
559+
# Returns:
560+
# (integer) for input starting with 's3://' always returns 0
561+
# for 'file://': 0 if file exists locally, error if it doesn't
562+
# 1 if the input is empty,
563+
# -1 otherwise.
564+
#######################################
565+
function checkPath() {
566+
if [[ -z $1 ]]; then
567+
return 1
568+
fi
569+
eval path=\$$1
570+
571+
if [[ $path =~ "s3://" ]]; then
572+
dbg "$1 looks like a S3 file path. Warning: Its presence will not be checked!"
573+
return 0 # we do not actually check S3 paths at the moment
574+
elif [[ $path =~ "file://" ]]; then
575+
dbg "$1 looks like a local file path."
576+
path=${path/file:\/\//}
577+
if [[ -f $path ]]; then
578+
dbg "$path found."
579+
eval "$1=\"$path\"" # update original variable
580+
return 0 # file found
581+
else
582+
err "File '$path' is not found locally."
583+
exit 1
584+
fi
585+
else
586+
dbg "Value of $1 is not a file path. Use its value as a content."
587+
return -1 #
588+
fi
589+
}
590+
549591
### CLI parameters checks ###
550592
if [[ "$RUN_ON" == "aws" ]]; then
551593
if [ ! -z ${CONTAINER_ID+x} ]; then
@@ -562,6 +604,19 @@ if [[ "$RUN_ON" == "aws" ]]; then
562604
err "ERROR: AWS EC2 Instance type not given."
563605
exit 1
564606
fi
607+
if [[ -z ${AWS_BLOCK_DURATION+x} ]]; then
608+
err "NOTICE: Container live time duration is not given."
609+
else
610+
case $AWS_BLOCK_DURATION in
611+
0|60|120|240|300|360)
612+
dbg "Container live time duration is $AWS_BLOCK_DURATION. "
613+
;;
614+
*)
615+
err "Container live time duration (--aws-block-duration) has wrong value: $AWS_BLOCK_DURATION. Available values of AWS spot instance duration in minutes is 60, 120, 180, 240, 300, or 360)."
616+
exit 1
617+
;;
618+
esac
619+
fi
565620
elif [[ "$RUN_ON" == "localhost" ]]; then
566621
if [[ ! -z ${AWS_KEYPAIR_NAME+x} ]] || [[ ! -z ${AWS_SSH_KEY_PATH+x} ]] ; then
567622
err "ERROR: options '--aws-keypair-name' and '--aws-ssh-key-path' must be used with '--run on aws'."
@@ -575,6 +630,10 @@ elif [[ "$RUN_ON" == "localhost" ]]; then
575630
err "ERROR: option '--aws-ebs-volume-size' must be used with '--run on aws'."
576631
exit 1
577632
fi
633+
if [[ "$AWS_BLOCK_DURATION" != "0" ]]; then
634+
err "ERROR: option '--aws-block-duration' must be used with '--run on aws'."
635+
exit 1
636+
fi
578637
else
579638
err "ERROR: incorrect value for option --run-on"
580639
exit 1
@@ -787,7 +846,93 @@ else
787846
fi
788847
shopt -s expand_aliases
789848

790-
trap cleanup_and_exit EXIT
849+
## Docker tools
850+
function waitEC2Ready() {
851+
cmd=$1
852+
machine=$2
853+
checkPrice=$3
854+
while true; do
855+
sleep 5; STOP=1
856+
ps ax | grep "$cmd" | grep "$machine" >/dev/null && STOP=0
857+
((STOP==1)) && return 0
858+
if [ $checkPrice -eq 1 ]; then
859+
status=$( \
860+
aws ec2 describe-spot-instance-requests \
861+
--filters="Name=launch.instance-type,Values=$AWS_EC2_TYPE" \
862+
| jq '.SpotInstanceRequests | sort_by(.CreateTime) | .[] | .Status.Code' \
863+
| tail -n 1
864+
)
865+
if [[ "$status" == "\"price-too-low\"" ]]; then
866+
echo "price-too-low"; # this value is result of function (not message for user), will check later
867+
return 0
868+
fi
869+
fi
870+
done
871+
}
872+
873+
# Params:
874+
# 1) machine name
875+
# 2) AWS EC2 instance type
876+
# 3) price
877+
# 4) duration (minutes)
878+
# 5) key pair name
879+
# 6) key path
880+
# 7) zone
881+
function createDockerMachine() {
882+
msg "Attempt to create a docker machine..."
883+
docker-machine create --driver=amazonec2 \
884+
--amazonec2-request-spot-instance \
885+
--amazonec2-keypair-name="$5" \
886+
--amazonec2-ssh-keypath="$6" \
887+
--amazonec2-instance-type=$2 \
888+
--amazonec2-spot-price=$3 \
889+
--amazonec2-block-duration-minutes=$4 \
890+
--amazonec2-zone=$7 \
891+
$1 2> >(grep -v "failed waiting for successful resource state" >&2) &
892+
}
893+
894+
function destroyDockerMachine() {
895+
# If spot request wasn't fulfilled, there is no associated instance,
896+
# so "docker-machine rm" will show an error, which is safe to ignore.
897+
# We better filter it out to avoid any confusions.
898+
# What is used here is called "process substitution",
899+
# see https://www.gnu.org/software/bash/manual/bash.html#Process-Substitution
900+
# The same trick is used in createDockerMachine to filter out errors
901+
# when we have "price-too-low" attempts, such errors come in few minutes
902+
# after an attempt and are generally unexpected by user.
903+
cmdout=$(docker-machine rm --force $1 2> >(grep -v "unknown instance" >&2) )
904+
msg "Termination requested for machine, current status: $cmdout"
905+
}
906+
907+
function cleanupAndExit {
908+
if [ "$KEEP_ALIVE" -gt "0" ]; then
909+
msg "Debug timeout is $KEEP_ALIVE seconds – started."
910+
msg " To connect to the docker machine use:"
911+
msg " docker \`docker-machine config $DOCKER_MACHINE\` exec -it pg_nancy_${CURRENT_TS} bash"
912+
sleep $KEEP_ALIVE
913+
fi
914+
msg "Remove temp files..." # if exists
915+
if [[ ! -z "${dockerConfig+x}" ]]; then
916+
docker $dockerConfig exec -i ${containerHash} bash -c "sudo rm -rf $MACHINE_HOME"
917+
fi
918+
rm -rf "$TMP_PATH"
919+
if [[ "$RUN_ON" == "localhost" ]]; then
920+
msg "Remove docker container"
921+
docker container rm -f $containerHash
922+
elif [[ "$RUN_ON" == "aws" ]]; then
923+
destroyDockerMachine $DOCKER_MACHINE
924+
if [ ! -z ${VOLUME_ID+x} ]; then
925+
msg "Wait and delete volume $VOLUME_ID"
926+
sleep 60 # wait for the machine to be removed
927+
delvolout=$(aws ec2 delete-volume --volume-id $VOLUME_ID)
928+
msg "Volume $VOLUME_ID deleted"
929+
fi
930+
else
931+
err "ASSERT: must not reach this point"
932+
exit 1
933+
fi
934+
}
935+
trap cleanupAndExit EXIT
791936

792937
if [[ "$RUN_ON" == "localhost" ]]; then
793938
if [[ -z ${CONTAINER_ID+x} ]]; then
@@ -824,9 +969,9 @@ elif [[ "$RUN_ON" == "aws" ]]; then
824969
region='a' #default zone
825970
fi
826971

827-
create_ec2_docker_machine $DOCKER_MACHINE $AWS_EC2_TYPE $EC2_PRICE \
828-
60 $AWS_KEYPAIR_NAME $AWS_SSH_KEY_PATH $zone;
829-
status=$(wait_ec2_docker_machine_ready "$DOCKER_MACHINE" true)
972+
createDockerMachine $DOCKER_MACHINE $AWS_EC2_TYPE $EC2_PRICE \
973+
$AWS_BLOCK_DURATION $AWS_KEYPAIR_NAME $AWS_SSH_KEY_PATH $zone;
974+
status=$(waitEC2Ready "docker-machine create" "$DOCKER_MACHINE" 1)
830975
if [[ "$status" == "price-too-low" ]]; then
831976
msg "Price $price is too low for $AWS_EC2_TYPE instance. Getting the up-to-date value from the error message..."
832977

@@ -852,11 +997,12 @@ elif [[ "$RUN_ON" == "aws" ]]; then
852997
DOCKER_MACHINE="nancy-$CURRENT_TS"
853998
DOCKER_MACHINE="${DOCKER_MACHINE//_/-}"
854999
#try start docker machine name with new price
855-
create_ec2_docker_machine $DOCKER_MACHINE $AWS_EC2_TYPE $EC2_PRICE \
856-
60 $AWS_KEYPAIR_NAME $AWS_SSH_KEY_PATH $zone
857-
wait_ec2_docker_machine_ready "$DOCKER_MACHINE" false;
1000+
msg "Attempt to create a new docker machine: $DOCKER_MACHINE with price: $EC2_PRICE."
1001+
createDockerMachine $DOCKER_MACHINE $AWS_EC2_TYPE $EC2_PRICE \
1002+
$AWS_BLOCK_DURATION $AWS_KEYPAIR_NAME $AWS_SSH_KEY_PATH $zone;
1003+
waitEC2Ready "docker-machine create" "$DOCKER_MACHINE" 0;
8581004
else
859-
err "$(date "+%Y-%m-%d %H:%M:%S") ERROR: Cannot determine actual price for the instance $AWS_EC2_TYPE."
1005+
err "ERROR: Cannot determine actual price for the instance $AWS_EC2_TYPE."
8601006
exit 1;
8611007
fi
8621008
fi
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
3+
output=$(
4+
${BASH_SOURCE%/*}/../nancy run \
5+
--run-on aws \
6+
--aws-keypair-name awskey \
7+
--aws-ssh-key-path /path/.ssh/awskey.pem \
8+
--aws-ec2-type i3.large \
9+
--aws-block-duration 30 \
10+
2>&1
11+
)
12+
13+
if [[ $output =~ " Container live time duration (--aws-block-duration) has wrong value: 30. Available values of AWS spot instance duration in minutes is 60, 120, 180, 240, 300, or 360)." ]]; then
14+
echo -e "\e[36mOK\e[39m"
15+
else
16+
>&2 echo -e "\e[31mFAILED\e[39m"
17+
>&2 echo -e "Output: $output"
18+
exit 1
19+
fi

0 commit comments

Comments
 (0)