From e1daac32c5b7ca0d902c16135d361aa5303f5124 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Sun, 27 Jan 2013 15:00:27 +0100 Subject: o Creating an initial version of install-file. --- STYLE-GUIDE.md | 17 ++++ app | 28 +++--- bin/app-init | 4 +- bin/pid-method | 113 ------------------------ build.log | 0 lib/common | 75 +++++++++++----- libexec/app-cat-conf | 7 ++ libexec/app-install-file | 170 +++++++----------------------------- libexec/app-method-pid | 113 ++++++++++++++++++++++++ libexec/app-operate | 68 ++++++--------- libexec/app-run-hook | 49 +++++++++++ libexec/app-set-version | 50 +++++++++++ test/app-init.bats | 11 ++- test/data/app-a/hooks/post-install | 5 ++ test/data/app-a/scripts/postinstall | 21 ----- test/utils.bash | 1 + 16 files changed, 378 insertions(+), 354 deletions(-) create mode 100644 STYLE-GUIDE.md delete mode 100755 bin/pid-method create mode 100644 build.log create mode 100755 libexec/app-method-pid create mode 100755 libexec/app-run-hook create mode 100755 libexec/app-set-version create mode 100755 test/data/app-a/hooks/post-install delete mode 100644 test/data/app-a/scripts/postinstall diff --git a/STYLE-GUIDE.md b/STYLE-GUIDE.md new file mode 100644 index 0000000..21f1523 --- /dev/null +++ b/STYLE-GUIDE.md @@ -0,0 +1,17 @@ +Style Guide +----------- + +Usage +===== + +* Always echo to `stderr`. +* Exit with code 1. +* Enclose required arguments in angle brackets: `-v ` +* Enclose optional arguments in square brackets: `[-h hook]` + + usage() { + echo "usage: $0 -v [-h hook]" + exit 1 + } + + diff --git a/app b/app index 880026a..a70b026 100755 --- a/app +++ b/app @@ -1,19 +1,5 @@ #!/bin/bash -e -PRG="$0" -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi -done - -APPSH_HOME=`dirname "$PRG"` -APPSH_HOME=`cd "$APPSH_HOME" && pwd` - usage() { if [ -n "$1" ] then @@ -30,6 +16,20 @@ usage() { echo "Run $0 -h for more help" >&2 } +PRG="$0" +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi +done + +APPSH_HOME=`dirname "$PRG"` +APPSH_HOME=`cd "$APPSH_HOME" && pwd` + . $APPSH_HOME/lib/common while getopts "h" opt diff --git a/bin/app-init b/bin/app-init index cca82e1..d61f989 100755 --- a/bin/app-init +++ b/bin/app-init @@ -48,7 +48,7 @@ fi # TODO: install a trap handler and rm -rf "$dir" -resolver=`grep_path "/app-resolver-$resolver_name$" "$PATH:$APPSH_HOME/libexec" | head -n 1` +resolver=`grep_path "/app-resolver-$resolver_name$" "$PATH" | head -n 1` if [ -z "$resolver" ] then @@ -75,3 +75,5 @@ fi echo "Resolved version to $version" "$resolver" download-version -v "$version" -f .app/latest.zip + +app-install-file -v "$version" -f .app/latest.zip diff --git a/bin/pid-method b/bin/pid-method deleted file mode 100755 index 29f6b4f..0000000 --- a/bin/pid-method +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash -e - -set -u - -. $APPSH_HOME/.app/lib/app-conf - -pid_file=$APPSH_APPS/.app/var/pid/$APPSH_NAME-$APPSH_INSTANCE.pid -bin=`get_conf $APPSH_APPS $APPSH_NAME $APPSH_INSTANCE app.bin` - -cd $APPSH_APPS/$APPSH_NAME/$APPSH_INSTANCE/current - -if [ -z "$bin" ] -then - echo "Missing required configuration: app.bin." >&2 - exit 1 -fi - -if [ ! -r "$bin" ] -then - echo "No such file: $bin" >&2 - exit 1 -fi - -chmod +x "$bin" - -PID= -if [ -r $pid_file ] -then - PID="`cat $pid_file`" -fi - -do_status() { - if [ -z "$PID" ] - then - echo stopped - else - if [ `ps -p "$PID" 2>/dev/null | wc -l` -gt 1 ] - then - echo running - else - echo crashed - fi - fi -} - -method_start() { - case `do_status` in - running) - echo "The application is already running as $PID." - exit 1 - ;; - esac - - $bin <&- 1<&- 2<&- & - - PID=$! - echo "Application launched as $PID" - echo $PID > $pid_file - - return 0 -} - -method_stop() { - case `do_status` in - stopped) - echo "The application not running." - exit 1 - ;; - crashed) - echo "The application crashed. Was running as $PID" - # TODO: should this remove the PID file? - # That makes it possible to run "stop" to stop "status" from showing "crashed" - exit 1 - ;; - esac - - signal="-9" - echo -n "Sending kill $signal to $PID, waiting for shutdown" - kill $signal $PID - - while [ "`do_status`" == "running" ] - do - sleep 1 - echo -n "." - done - - echo " OK" - rm -f $pid_file - return 0 -} - -method_status() { - case `do_status` in - running) - echo "$APPSH_NAME/$APPSH_INSTANCE is running as $PID" - ;; - stopped) - echo "$APPSH_NAME/$APPSH_INSTANCE is not running" - ;; - crashed) - echo "$APPSH_NAME/$APPSH_INSTANCE crashed. Was running as $PID" - ;; - esac -} - -case "$APPSH_METHOD" in - start) method_start ;; - stop) method_stop ;; - status) method_status ;; - *) exit 1 ;; -esac - -exit $? diff --git a/build.log b/build.log new file mode 100644 index 0000000..e69de29 diff --git a/lib/common b/lib/common index 22c8cd0..8c33209 100644 --- a/lib/common +++ b/lib/common @@ -2,13 +2,17 @@ assert_is_app() { local check_link=yes + local version= - while getopts "C" opt + while getopts "Cv:" opt do case $opt in C) check_link=no ;; + v) + version=$OPTARG + ;; esac done @@ -22,12 +26,24 @@ assert_is_app() { then if [ ! -e current ] then - echo "Missing 'current' link." >&2 - exit 1 + fatal "Missing 'current' link." >&2 + fi + fi + + if [[ $version != "" ]] + then + if [[ ! -d versions/$version ]] + then + fatal "No such version: $version" fi fi } +fatal() { + echo "$usage_app: fatal: $@" 2>&1 + exit 1 +} + list_apps() { local filter_name=$1; shift local filter_instnace=$1; shift @@ -121,27 +137,33 @@ grep_path() { # TODO: set ulimit # TODO: set umask # TODO: change group newgrp/sg +# usage: run_app [-v version] [bin to execute] run_app() { - local name=$1; shift - local instance=$1; shift - local bin=$1; shift - local method=$1; shift - - assert_is_instance operate_usage "$name" "$instance" + version= + while getopts "v:" opt + do + case $opt in + v) + version=$OPTARG + shift 2 + OPTIND=1 + ;; + esac + done - local x="" - if [ ! -z "$APPSH_APPS" ] - then - x="$APPSH_APPS/" - fi + local bin=$1; shift + local e=`app-cat-conf -f .app/config -n "env\..*" | cut -f 2- -d .` ( - cd $x$name/$instance - APPSH_INSTANCE_HOME=`pwd` - cd current + if [[ $version == "" ]] + then + assert_is_app + cd current + else + assert_is_app -v "$version" + cd "versions/$version" + fi - local e=`$APPSH_HOME/bin/app-cat-conf -f $apps/$name/$instance/current/etc/app.conf -g env | cut -f 2- -d .` - #e="`get_conf_in_group $apps $name $instance env`" # This magically get the expansion of $u correct. IFS=" " @@ -152,12 +174,7 @@ run_app() { PATH=/bin:/usr/bin \ $e \ PWD="$PWD" \ - APPSH_METHOD=$method \ - APPSH_APPS=$apps \ APPSH_HOME=$APPSH_HOME \ - APPSH_NAME=$name \ - APPSH_INSTANCE=$instance \ - APPSH_INSTANCE_HOME=$APPSH_INSTANCE_HOME \ $bin "$@" local ret=$? set +x @@ -166,3 +183,13 @@ run_app() { exit $ret ) } + +##################################################################### +# Common init + +# Add the app-* apps to PATH. They're added last to allow the user to +# overload their implementations. +PATH=$PATH:$APPSH_HOME/bin:$APPSH_HOME/libexec + +# Save for later +usage_app=$0 diff --git a/libexec/app-cat-conf b/libexec/app-cat-conf index 5da435e..5b1c614 100755 --- a/libexec/app-cat-conf +++ b/libexec/app-cat-conf @@ -38,6 +38,13 @@ fi APPSH_DEFAULT_CONFIG=${APPSH_DEFAULT_CONFIG-$APPSH_HOME/lib/default-config} +# TODO: find config files in the paths above $file's paths and perhaps /etc/appsh/config + +if [[ ! -r $file ]] +then + fatal "No such file: $file" +fi + # The awk script makes sure each key only appears once cat "$file" "$APPSH_DEFAULT_CONFIG" | \ sed -n -e "$filter" | \ diff --git a/libexec/app-install-file b/libexec/app-install-file index a0d929a..1e3edb8 100755 --- a/libexec/app-install-file +++ b/libexec/app-install-file @@ -8,127 +8,53 @@ APPSH_HOME=$(cd $(dirname "$0")/.. && pwd) . $APPSH_HOME/lib/common # HEADER END -calculate_md5() { - local file="$1"; shift - - md5sum "$file" | cut -c 1-32 -} - -# TODO: support file:// repositories -# TODO: look in the local repository first -get() { - local url=$1 - local file=$2 - local exit - - curl -o $file $url -D curl.tmp - - exit=`grep "^HTTP/[0-9]\.[0-9] 200 .*" curl.tmp >/dev/null; echo $?` - head=`head -n 1 curl.tmp` - rm -f curl.tmp - if [ "$exit" != 0 ] - then - echo "Unable to download $url: $head" >&2 - exit 1 - fi -} - -resolve_snapshot() { - local groupId=$1; shift - local groupIdSlash=$1; shift - local artifactId=$1; shift - local version=$1; shift - - local metadata=$apps/.app/var/download/$groupId-$artifactId-$version-metadata.xml - local base_url=$repo/$groupIdSlash/$artifactId/$version - get $base_url/maven-metadata.xml $metadata - local resolved_version=`xmlstarlet sel -t -m '//snapshotVersion[extension[text()="zip"]]' -v value $metadata` - echo $resolved_version -} - -download_artifact() { - local file="$1"; shift - local url="$1"; shift - - echo "Downloading $url.md5" - get $url.md5 $file.md5 - local expected_md5="`cat $file.md5`" - - if [ -r $file ] - then - if [ "$expected_md5" == "`calculate_md5 $file`" ] - then - echo "Artifact already downloaded." - else - rm -f "$file" - fi - return 0 - fi - echo "Downloading artifact: $url" - get $url $file - - local actual_md5="`calculate_md5 $file`" - if [ "$expected_md5" == "$actual_md5" ] - then - echo "Artifact downloaded." - else - echo "Invalid checksum. Expected $expected_md5, got $actual_md5" >&2 - exit 1 - fi +usage() { + echo "usage: $0 -v -f " + exit 1 } -if [ $# -lt 2 ] +version= +file= + +while getopts "v:f:" opt +do + case $opt in + v) + version=$OPTARG + shift 2 + OPTIND=1 + ;; + f) + file=$OPTARG + shift 2 + OPTIND=1 + ;; + esac +done + +if [[ -z $version || -z $file || $# != 0 ]] then - method_install_usage + usage fi -case "$resolver" in - maven) - ;; - file) - if [ ! -r "$url" ] - then - echo "Could not read file: $url" >&2 - exit 1 - fi - - # TODO: should the zip file be copied into download/ so that - # there's always a local copy? - zip_file=$url - - if [ -z "$version" ] - then - version=`TZ=UTC date +"%Y%m%d-%H%M%S"` - fi - - resolved_version=$version - ;; - *) - method_install_usage "Invalid resolver type: $resolver" - ;; -esac - -if [ -d versions/$resolved_version ] +if [ -d versions/$version ] then - echo "Version $resolved_version is already installed" + echo "Version $version is already installed" exit 1 fi -mkdir -p versions/$resolved_version +mkdir -p versions/$version echo "Unpacking..." -unzip -q -d versions/$resolved_version $zip_file +unzip -q -d versions/$version $file -if [ ! -d versions/$resolved_version/root ] +if [ ! -d versions/$version/root ] then echo "Invalid zip file, did not contain a ./root directory." >&2 exit 1 fi -echo "Changing current symlink" -rm -f current -ln -s versions/$resolved_version/root current - +# TODO: This should go away if [ -d current/bin ] then ( @@ -137,38 +63,8 @@ then ) fi -( - cd versions/$resolved_version - if [ -d scripts ] - then - find scripts | xargs chmod +x - fi +app-run-hook -v "$version" -h pre-install - if [ -x scripts/postinstall ] - then - echo "Running postinstall..." - cd root - set +e - env -i \ - PATH=/bin:/usr/bin \ - APPSH_APPS=$apps \ - APPSH_HOME=$APPSH_HOME \ - APPSH_VERSION=$resolved_version \ - ../scripts/postinstall - set -e - ret=`echo $?` - if [ "$ret" != 0 ] - then - echo "Postinstall failed!" - exit 1 - fi - echo "Postinstall completed successfully" - fi -) +app-set-version -v "$version" -# if [ -r $apps/.app/var/list ] -# then -# sed "/^$name:$instance/d" $apps/.app/var/list > $apps/.app/var/list.new -# fi -# echo "$name:$instance:$version:$url" >> $apps/.app/var/list.new -# mv $apps/.app/var/list.new $apps/.app/var/list +app-run-hook -v "$version" -h post-install diff --git a/libexec/app-method-pid b/libexec/app-method-pid new file mode 100755 index 0000000..29f6b4f --- /dev/null +++ b/libexec/app-method-pid @@ -0,0 +1,113 @@ +#!/bin/bash -e + +set -u + +. $APPSH_HOME/.app/lib/app-conf + +pid_file=$APPSH_APPS/.app/var/pid/$APPSH_NAME-$APPSH_INSTANCE.pid +bin=`get_conf $APPSH_APPS $APPSH_NAME $APPSH_INSTANCE app.bin` + +cd $APPSH_APPS/$APPSH_NAME/$APPSH_INSTANCE/current + +if [ -z "$bin" ] +then + echo "Missing required configuration: app.bin." >&2 + exit 1 +fi + +if [ ! -r "$bin" ] +then + echo "No such file: $bin" >&2 + exit 1 +fi + +chmod +x "$bin" + +PID= +if [ -r $pid_file ] +then + PID="`cat $pid_file`" +fi + +do_status() { + if [ -z "$PID" ] + then + echo stopped + else + if [ `ps -p "$PID" 2>/dev/null | wc -l` -gt 1 ] + then + echo running + else + echo crashed + fi + fi +} + +method_start() { + case `do_status` in + running) + echo "The application is already running as $PID." + exit 1 + ;; + esac + + $bin <&- 1<&- 2<&- & + + PID=$! + echo "Application launched as $PID" + echo $PID > $pid_file + + return 0 +} + +method_stop() { + case `do_status` in + stopped) + echo "The application not running." + exit 1 + ;; + crashed) + echo "The application crashed. Was running as $PID" + # TODO: should this remove the PID file? + # That makes it possible to run "stop" to stop "status" from showing "crashed" + exit 1 + ;; + esac + + signal="-9" + echo -n "Sending kill $signal to $PID, waiting for shutdown" + kill $signal $PID + + while [ "`do_status`" == "running" ] + do + sleep 1 + echo -n "." + done + + echo " OK" + rm -f $pid_file + return 0 +} + +method_status() { + case `do_status` in + running) + echo "$APPSH_NAME/$APPSH_INSTANCE is running as $PID" + ;; + stopped) + echo "$APPSH_NAME/$APPSH_INSTANCE is not running" + ;; + crashed) + echo "$APPSH_NAME/$APPSH_INSTANCE crashed. Was running as $PID" + ;; + esac +} + +case "$APPSH_METHOD" in + start) method_start ;; + stop) method_stop ;; + status) method_status ;; + *) exit 1 ;; +esac + +exit $? diff --git a/libexec/app-operate b/libexec/app-operate index 8e4ecac..007948c 100755 --- a/libexec/app-operate +++ b/libexec/app-operate @@ -8,43 +8,31 @@ APPSH_HOME=$(cd $(dirname "$0")/.. && pwd) . $APPSH_HOME/lib/common # HEADER END -method_operate() { - local name="$1"; shift - local instance="$1"; shift - local method="$1" - - if [ $# -gt 0 ] - then - shift - fi - - bin=`$APPSH_HOME/bin/app-cat-conf -f $apps/$name/$instance/current/etc/app.conf -g app -k method | cut -f 2 -d =` - - if [ -z "$bin" ] - then - bin=$APPSH_HOME/.app/lib/pid-method - fi - - if [ ! -x "$name/$instance/current/$bin" ] - then - echo "Invalid executable: $bin" >&2 - exit 1 - fi - - case "$method" in - start) run_app "$name" "$instance" "$bin" "start" "$@" ;; - stop) run_app "$name" "$instance" "$bin" "stop" "$@" ;; - status) run_app "$name" "$instance" "$bin" "status" "$@" ;; - restart) run_app "$name" "$instance" "$bin" "restart" "$@" ;; - run) run_app "$name" "$instance" "$bin" "run" "$@" ;; - *) - if [ -z "$method" ] - then - method_operate_usage - else - method_operate_usage "Unknown method $method" - fi - ;; - esac - exit $? -} +assert_is_app + +method="$1" + +bin=`app-conf get app.method` +bin=${bin-$APPSH_HOME/.app/libexec/app-method-pid} + +if [ ! -x "current/$bin" ] +then + echo "Invalid executable: $bin" >&2 + exit 1 +fi + +case "$method" in + start) run_app "$name" "$instance" "$bin" "start" "$@" ;; + stop) run_app "$name" "$instance" "$bin" "stop" "$@" ;; + status) run_app "$name" "$instance" "$bin" "status" "$@" ;; + restart) run_app "$name" "$instance" "$bin" "restart" "$@" ;; + run) run_app "$name" "$instance" "$bin" "run" "$@" ;; + *) + if [ -z "$method" ] + then + method_operate_usage + else + method_operate_usage "Unknown method $method" + fi + ;; +esac diff --git a/libexec/app-run-hook b/libexec/app-run-hook new file mode 100755 index 0000000..ec96c58 --- /dev/null +++ b/libexec/app-run-hook @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e +set -u + +APPSH_HOME=$(cd $(dirname "$0")/.. && pwd) + +. $APPSH_HOME/lib/common +# HEADER END + +usage() { + echo "usage: $usage_app -v [version] -h [hook]" 2>&1 +} + +version= +hook= + +while getopts "v:h:" opt +do + case $opt in + v) + version=$OPTARG + shift 2 + OPTIND=1 + ;; + h) + hook=$OPTARG + shift 2 + OPTIND=1 + ;; + esac +done + +if [[ -z $version || -z $hook || $# != 0 ]] +then + usage +fi + +bin=versions/$version/hooks/$hook + +if [[ ! -r $bin ]] +then + exit 0 +fi + +# TODO: remove after +#chmod +x $bin +echo "Running hook: $hook" +run_app -v $version hooks/$hook diff --git a/libexec/app-set-version b/libexec/app-set-version new file mode 100755 index 0000000..75d44a8 --- /dev/null +++ b/libexec/app-set-version @@ -0,0 +1,50 @@ +#!/bin/bash + +set -e +set -u + +APPSH_HOME=$(cd $(dirname "$0")/.. && pwd) + +. $APPSH_HOME/lib/common +# HEADER END + +version= + +while getopts "v:" opt +do + case $opt in + v) + version=$OPTARG + shift 2 + OPTIND=1 + ;; + esac +done + +if [[ -z $version || $# != 0 ]] +then + usage +fi + +if [[ ! -d versions/$version ]] +then + fatal "Invalid version: not a directory: versions/$version" +fi + +if [[ -e current && ! -s current ]] +then + fatal "'current' is not a symlink" +fi + +if [[ -e current ]] +then + prev=`ls -l current` + prev=${prev/#* -> versions} + prev=${prev:1} + prev=${prev/%?root} + echo "Changing current symlink from $prev to $version" + ln -f -s versions/$version/root current +else + echo "Creating current symlink for version $version" + ln -s versions/$version/root current +fi diff --git a/test/app-init.bats b/test/app-init.bats index b62c42e..4e7c281 100755 --- a/test/app-init.bats +++ b/test/app-init.bats @@ -31,12 +31,15 @@ load utils fi app init -d my-app maven -r "file://$BATS_TMPDIR/repo" org.example:app-a:1.0-SNAPSHOT; echo_lines - eq '$status' 0 - eq '${lines[0]}' "Resolving version 1.0-SNAPSHOT..." + eq '$status' 0 + eq '${lines[0]}' "Resolving version 1.0-SNAPSHOT..." match '${lines[1]}' "Resolved version to 1.0-.*" match '${lines[2]}' "Downloading org.example:app-a:1.0-.*" - - eq '${#lines[*]}' 3 + eq '${lines[3]}' "Unpacking..." + match '${lines[4]}' "Creating current symlink for version 1.0-.*" + eq '${lines[5]}' "Running hook: post-install" + eq '${lines[6]}' "Post install" + eq '${#lines[*]}' 7 is_directory "my-app/.app" } diff --git a/test/data/app-a/hooks/post-install b/test/data/app-a/hooks/post-install new file mode 100755 index 0000000..99ad974 --- /dev/null +++ b/test/data/app-a/hooks/post-install @@ -0,0 +1,5 @@ +#!/bin/bash -e + +set -u + +echo "Post install" diff --git a/test/data/app-a/scripts/postinstall b/test/data/app-a/scripts/postinstall deleted file mode 100644 index dc176ff..0000000 --- a/test/data/app-a/scripts/postinstall +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -e - -echo "Hello World!" - -if [ -d etc/$APPSH_INSTANCE ] -then - find etc/$APPSH_INSTANCE -maxdepth 1 -type f | while read file - do - cp $file . - done -fi - -LOGS=$APPSH_APPS/$APPSH_NAME/$APPSH_INSTANCE/logs - -if [ ! -d $LOGS ] -then - echo "Creating logs directory" - mkdir $LOGS -fi - -ln -s $LOGS logs diff --git a/test/utils.bash b/test/utils.bash index fa2602e..3a9d425 100644 --- a/test/utils.bash +++ b/test/utils.bash @@ -7,6 +7,7 @@ exit_usage=1 exit_usage_wrong=0 setup() { + find test/data -name \*.zip | xargs rm -f PATH=/bin:/usr/bin PATH=$PATH:$APPSH_HOME APPSH_HOME=$(cd $BATS_TEST_DIRNAME/..; echo `pwd`) -- cgit v1.2.3