From cdfcae52a49118d43e2064dd228b789b8452664f Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Fri, 12 Oct 2012 23:23:10 +0200 Subject: Major refactoring. Splitting out the method groups into separate files. --- .app/lib/app-app | 403 ++++++++++++++++++++++++++++++ .app/lib/app-common | 100 ++++++++ .app/lib/app-conf | 20 +- .app/lib/app-operate | 130 ++++++++++ .app/lib/default-method | 37 --- .app/lib/pid-method | 37 +++ .gitignore | 2 +- README.md | 31 --- app | 646 ++++-------------------------------------------- docs/README.md | 78 ++++++ 10 files changed, 813 insertions(+), 671 deletions(-) create mode 100644 .app/lib/app-app create mode 100644 .app/lib/app-common create mode 100644 .app/lib/app-operate delete mode 100755 .app/lib/default-method create mode 100755 .app/lib/pid-method delete mode 100644 README.md create mode 100644 docs/README.md diff --git a/.app/lib/app-app b/.app/lib/app-app new file mode 100644 index 0000000..d59a231 --- /dev/null +++ b/.app/lib/app-app @@ -0,0 +1,403 @@ +#!/bin/bash + +if [ -n "$APPSH_REPO" ] +then + repo="$APPSH_REPO" +else + repo="http://repo1.maven.org" +fi + +# TODO: support file:// repositories +# TODO: look in the local repository first +# TODO: assert that we got a 200 OK +get() { + local exit + + curl -o $2 $1 -D curl.tmp + + exit=`grep "^HTTP/[0-9]\.[0-9] 200 .*" curl.tmp >/dev/null; echo $?` + head=`head -n 1 curl.tmp` + rm curl.tmp + if [ "$exit" != 0 ] + then + echo "Unable to download $1: $head" + exit 1 + fi +} + +resolved_version= +resolve_snapshot() { + local base_url + local metadata + + echo "Resolving version $version..." + metadata=$BASEDIR/.app/var/download/$groupId-$artifactId-$version-metadata.xml + base_url=$repo/$(echo $groupId | sed "s,\.,/,g")/$artifactId/$version + get $base_url/maven-metadata.xml $metadata + resolved_version=`xmlstarlet sel -t -m '//snapshotVersion[extension[text()="zip"]]' -v value $metadata` + + if [ -z "$resolved_version" ] + then + echo "Unable to resolve version." + exit 1 + fi + echo "Resolved version $version to $resolved_version" +} + +zip_file= +download_artifact() { + zip_file=$BASEDIR/.app/var/download/$groupId-$artifactId-$resolved_version.zip + if [ -r $zip_file ] + then + echo "Artifact already downloaded." + return 0 + fi + echo "Downloading artifact" + base_url=$repo/$(echo $groupId | sed "s,\.,/,g")/$artifactId/$version + get $base_url/$artifactId-$resolved_version.zip $zip_file + + # TODO: download checksum. bash is too shady to trust +} + +method_install_usage() { + if [ -n "$1" ] + then + echo "Error:" "$@" >&2 + fi + + echo "usage:" >&2 + echo "" + echo "Install Maven artifact from repo:" >&2 + echo " $0 install -m groupId:artifactId [-n name] -i instance [-v version]" >&2 + echo "Name defaults to artifactId." >&2 + echo "" + echo "Install zip file:" >&2 + echo " $0 install -f file -n name -i instance [-v version]" >&2 + echo "The version defaults to the current timestamp" >&2 + exit 1 +} + +method_install() { + local m + + while getopts "m:n:i:v:f:" opt + do + case $opt in + m) + m=$OPTARG + groupId=`echo $OPTARG | cut -s -f 1 -d :` + artifactId=`echo $OPTARG | cut -s -f 2 -d :` + if [ -z "$groupId" -o -z "$artifactId" ] + then + method_install_usage "Invalid -m value." + fi + ;; + n) + name=$OPTARG + ;; + i) + instance=$OPTARG + ;; + v) + version=$OPTARG + ;; + f) + file=$OPTARG + ;; + \?) + method_install_usage "Invalid option: -$OPTARG" + ;; + esac + done + + if [ -z "$file" -a -z "$m" ] + then + method_install_usage "Either -f or -m has to be specified." + fi + + if [ -n "$file" -a -n "$m" ] + then + method_install_usage "Only one of -f or -m can specified." + fi + + if [ -z "$instance" ] + then + method_install_usage "Missing required argument: -i instance." + fi + + if [ -z "version" ] + then + method_install_usage "Missing required argument: -v version." + fi + + if [ -n "$m" ] + then + if [ -z "$name" ] + then + name=$artifactId + fi + + resolve_snapshot + + download_artifact + else + zip_file=$file + + if [ -z "$version" ] + then + version=`TZ=UTC date +"%Y%m%d-%H%M%S"` + fi + + resolved_version=$version + fi + + if [ ! -d $name/$instance ] + then + echo "Creating instance '$instance' for $name" + mkdir -p $name/$instance + fi + + if [ -d $name/$instance/versions/$resolved_version ] + then + echo "Version $resolved_version is already installed" + exit 1 + fi + + mkdir -p $name/$instance/versions/$resolved_version + + echo "Unpacking..." + unzip -q -d $name/$instance/versions/$resolved_version $zip_file + + ( + cd $name/$instance/versions/$resolved_version + if [ -d scripts ] + then + find scripts | xargs chmod +x + fi + + if [ -x scripts/postinstall ] + then + echo "Running postinstall..." + set +e + env -i \ + PATH=$PATH \ + scripts/postinstall + set -e + ret=`echo $?` + if [ "$ret" != 0 ] + then + echo "Postinstall failed!" + exit 1 + fi + echo "Postinstall completed successfully" + fi + ) + + echo "Changing current symlink" + rm -f $BASEDIR/$name/$instance/current + ln -s versions/$resolved_version/root $BASEDIR/$name/$instance/current + + ( + cd $name/$instance/current + find bin -type f | xargs chmod +x + ) + + if [ -r $BASEDIR/.app/var/list ] + then + sed "/^$name:$instance/d" $BASEDIR/.app/var/list > $BASEDIR/.app/var/list.new + fi + echo "$name:$instance:$version" >> $BASEDIR/.app/var/list.new + mv $BASEDIR/.app/var/list.new $BASEDIR/.app/var/list +} + +method_set_current_usage() { + if [ -n "$1" ] + then + echo "Error:" "$@" >&2 + fi + + echo "usage: set-current -n name -i instance -v version" >&2 + exit 1 +} + +method_set_current() { + local name + local instance + local version + + while getopts "n:i:v:" opt + do + case $opt in + n) + name=$OPTARG + ;; + i) + instance=$OPTARG + ;; + v) + version=$OPTARG + ;; + \?) + method_set_current_usage "Invalid option: -$OPTARG" + ;; + esac + done + + if [ -z "$version" ] + then + echo "Missing required option -v version." >&2 + exit 1 + fi + + assert_is_instance method_set_current_usage "$name" "$instance" "no" + + if [ ! -d $BASEDIR/$name/$instance/versions/$version ] + then + echo "Invalid version: $version." + exit 1 + fi + + rm -f $BASEDIR/$name/$instance/current + ln -s versions/$version/root $BASEDIR/$name/$instance/current + + return 0 +} + +method_list_usage() { + if [ -n "$1" ] + then + echo "Error:" "$@" >&2 + fi + + echo "usage: list [-n name] [-P field]" >&2 + echo "" + echo "List all installed applications" >&2 + echo " $0 list" >&2 + echo "" + echo "List all applications in an parseable format:" >&2 + echo " $0 -P instance -P version -n foo" >&2 + exit 1 +} + +method_list() { + local mode="pretty" + local vars + local filter_name + + while getopts "P:n:" opt + do + case $opt in + P) + mode="parseable" + vars="$vars $OPTARG" + ;; + n) + filter_name=$OPTARG + ;; + \?) + method_list_usage "Invalid option: -$OPTARG" + ;; + esac + done + + if [ ! -r $BASEDIR/.app/var/list ] + then + return + fi + + if [ $mode = "pretty" ] + then + printf "%-20s %-20s %-20s\n" "Name" "Instance" "Version" + list_apps "$filter_name" name instance version | (IFS=:; while read name instance version + do + printf "%-20s %-20s %-20s\n" "$name" "$instance" "$version" + done) + else + list_apps "$filter_name" $vars + fi +} + +method_list_versions_usage() { + if [ -n "$1" ] + then + echo "Error:" "$@" >&2 + fi + + echo "usage: list-versions -n name -i instance [-P]" >&2 + echo " -P - parseable output" >&2 + exit 1 +} + +method_list_versions() { + local name + local instance + local version + local mode="pretty" + + while getopts "n:i:P" opt + do + case $opt in + n) + name=$OPTARG + ;; + i) + instance=$OPTARG + ;; + v) + version=$OPTARG + ;; + P) + mode="parseable" + ;; + \?) + method_list_versions_usage "Invalid option: -$OPTARG" + ;; + esac + done + + assert_is_instance method_list_versions_usage "$name" "$instance" "no" + + if [ $mode = "pretty" ] + then + echo "Available versions for $name/$instance:" + fi + + find_versions $name $instance + + return 0 +} + +method_app_usage() { + if [ -n "$1" ] + then + echo "Error:" $@ >&2 + fi + + echo "usage: $0 app " >&2 + echo "" >&2 + echo "Available methods:" >&2 + echo " install - Installs an application" >&2 + echo " list - List all installed applications" >&2 + echo " list-versions - List all available versions for a single application" >&2 + echo " set-current - Set the current version" >&2 +} + +method_app() { + local name="$1" + local instance="$2" + + if [ $# -gt 2 ] + then + method=$3 + shift + fi + + case "$method" in + install) method_install "$name" "$instance" "$@" ;; + list) method_list "$name" "$instance" "$@" ;; + list-versions) method_list_versions "$name" "$instance" "$@" ;; + set-current) method_set_current "$name" "$instance" "$@" ;; + *) method_app_usage ;; + esac + exit $? +} diff --git a/.app/lib/app-common b/.app/lib/app-common new file mode 100644 index 0000000..11e724b --- /dev/null +++ b/.app/lib/app-common @@ -0,0 +1,100 @@ +#!/bin/bash + +assert_is_instance() { + local usage=$1 + local name=$2 + local instance=$3 + local check_link=$4 + + if [ -z "$name" ] + then + $usage "Missing required option -n." + fi + + if [ -z "$instance" ] + then + $usage "Missing required option -i." + fi + + if [ ! -d $name/$instance ] + then + echo "No such application/instance: $name/$instance." >&2 + exit 1 + fi + + if [ "$check_link" != "no" ] + then + if [ ! -e $name/$instance/current ] + then + echo "Missing 'current' link." >&2 + exit 1 + fi + fi +} + +list_apps() { + filter_name=$1 + shift + vars="$@" + + sort $BASEDIR/.app/var/list | while read line + do + echo $line | (IFS=:; while read name instance version junk + do + if [ -n "$filter_name" -a "$filter_name" != "$name" ] + then + continue + fi + + local line="" + IFS=" "; for var in $vars + do + case $var in + name) x=$name;; + instance) x=$instance;; + version) x=$version;; + current_version) x=`find_current_version $name $instance`;; + *) x="";; + esac + + if [ -z "$line" ] + then + line="$line$x" + else + line="$line:$x" + fi + done + echo $line + done) + done +} + +find_current_version() { + name=$1 + instance=$2 + + if [ ! -L $BASEDIR/$name/$instance/current ] + then + return 0 + fi + + ( + cd $BASEDIR/$name/$instance + ls -l current | sed -n "s,.* current -> versions/\(.*\)/root,\1,p" + ) +} + +find_versions() { + name=$1 + instance=$2 + + if [ ! -d $BASEDIR/$name/$instance/versions ] + then + return 0 + fi + + ( + cd $BASEDIR/$name/$instance/versions + ls -1d * + ) +} diff --git a/.app/lib/app-conf b/.app/lib/app-conf index 3fcfe88..fa705e4 100644 --- a/.app/lib/app-conf +++ b/.app/lib/app-conf @@ -95,16 +95,18 @@ conf_delete() { mv $file.tmp $file } -conf_usage() { +method_conf_usage() { if [ -n "$1" ] then echo "Error:" $@ >&2 fi - echo "usage:" >&2 + echo "usage: $0 conf " >&2 echo "" - echo " $0 conf -n name -i instance" >&2 - echo " $0 conf -n name -i instance -s [group.key] [value]" >&2 + echo "Available methods:" >&2 + echo " get - list all configuration parameters" >&2 + echo " set [group.key] [value] - set a configuration parameter" >&2 + echo " delete [group.key] - deletes a configuration parameter" >&2 exit 1 } @@ -117,24 +119,24 @@ method_conf() { do case $opt in n) - name=$OPTARG + name="$OPTARG" ;; i) - instance=$OPTARG + instance="$OPTARG" ;; d) mode="delete" - key=$OPTARG + key="$OPTARG" ;; s) mode="set" - key=$OPTARG + key="$OPTARG" # Remove all options so far shift $((OPTIND-1)) value="$1" ;; \?) - conf_usage "Invalid option: -$OPTARG" + method_conf_usage "Invalid option: -$OPTARG" ;; esac done diff --git a/.app/lib/app-operate b/.app/lib/app-operate new file mode 100644 index 0000000..fc0d63f --- /dev/null +++ b/.app/lib/app-operate @@ -0,0 +1,130 @@ +#!/bin/bash + +start_usage() { + if [ -n "$1" ] + then + echo "Error:" "$@" >&2 + fi + + echo "usage: $0 start -n name -i instance" >&2 + exit 1 +} + +stop_usage() { + if [ -n "$1" ] + then + echo "Error:" "$@" >&2 + fi + + echo "usage: $0 stop -n name -i instance" >&2 + exit 1 +} + +# TODO: set ulimit +# TODO: set umask +# TODO: change group newgrp/sg +method_start() { + run_control start_usage "start" "$@" +} + +method_stop() { + run_control stop_usage "stop" "$@" +} + +run_control() { + local usage=$0; shift + local method=$1; shift + local name + local instance + + while getopts "n:i:" opt + do + case $opt in + n) + name=$OPTARG + ;; + i) + instance=$OPTARG + ;; + \?) + $usage "Invalid option: -$OPTARG" + ;; + esac + done + + assert_is_instance $usage "$name" "$instance" + + ( + cd $name/$instance/current + + bin=`get_conf $BASEDIR $name $instance app.method` + + if [ -z "$bin" ] + then + bin=$BASEDIR/.app/lib/pid-method + fi + + if [ ! -x "$bin" ] + then + echo "Invalid executable: $bin" >&2 + exit 1 + fi + + e="`get_conf_in_group $BASEDIR $name $instance env`" + + # Set a default PATH which can be overridden by the application's settings + set +e + env -i \ + PATH=/bin:/usr/bin \ + $e \ + APPSH_METHOD=$method \ + APPSH_BASEDIR=$BASEDIR \ + APPSH_NAME=$name \ + APPSH_INSTANCE=$instance \ + $bin + local ret=$? + set +x + set -e + + case $ret in + 0) + echo "Application ${method}ed" + ;; + *) + echo "Error starting $name/$instance" + ;; + esac + ) +} + +method_operate_usage() { + if [ -n "$1" ] + then + echo "Error:" $@ >&2 + fi + + echo "usage: $0 operate " >&2 + echo "" >&2 + echo "Available methods:" >&2 + echo " start" >&2 + echo " stop" >&2 + echo " restart" >&2 + echo " status" >&2 +} + +method_operate() { + if [ $# -gt 0 ] + then + method=$1 + shift + fi + + case "$method" in + start) method_start $first "$@" ;; + stop) method_stop $first "$@" ;; + status) method_status $first "$@" ;; + restart) method_restart $first "$@" ;; + *) method_operate_usage ;; + esac + exit $? +} diff --git a/.app/lib/default-method b/.app/lib/default-method deleted file mode 100755 index e718849..0000000 --- a/.app/lib/default-method +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -set -e - -PID_FILE=$APPSH_BASEDIR/.app/var/pid/$name-$instance.pid - -. $APPSH_BASEDIR/.app/lib/app-conf - -bin=`get_conf $APPSH_BASEDIR $APPSH_NAME $APPSH_INSTANCE app.bin` - -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" - -case "$APPSH_METHOD" in - start) - set -x - $bin & - ret=$? - pid=$! - echo "Application launched with PID=$pid" - echo $pid > $PID_FILE - ;; - *) - exit 1 - ;; -esac diff --git a/.app/lib/pid-method b/.app/lib/pid-method new file mode 100755 index 0000000..e718849 --- /dev/null +++ b/.app/lib/pid-method @@ -0,0 +1,37 @@ +#!/bin/bash + +set -e + +PID_FILE=$APPSH_BASEDIR/.app/var/pid/$name-$instance.pid + +. $APPSH_BASEDIR/.app/lib/app-conf + +bin=`get_conf $APPSH_BASEDIR $APPSH_NAME $APPSH_INSTANCE app.bin` + +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" + +case "$APPSH_METHOD" in + start) + set -x + $bin & + ret=$? + pid=$! + echo "Application launched with PID=$pid" + echo $pid > $PID_FILE + ;; + *) + exit 1 + ;; +esac diff --git a/.gitignore b/.gitignore index bafa041..e80433c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ apps.list downloads target .app/var -/*/*/* +/*/.gitignore diff --git a/README.md b/README.md deleted file mode 100644 index 4373e16..0000000 --- a/README.md +++ /dev/null @@ -1,31 +0,0 @@ -Environment ------------ - -The following environment variables are set by default: - -TODOs ------ - -* Support installation-wide settings. Useful for shared environment - settings etc (PATH). - -* Add support for hooks in .app/hooks. Example hooks: - * Diff config. Save a backup of the config On installtaion - -* Support changing current version. - -* Document app.sh - * Concept: config. group, key and value. - * Scriptable - -* init.d support - -Method Contract ---------------- - -### Environment variables you can depend on - -* `APPSH_NAME` -* `APPSH_INSTANCE` -* `APPSH_METHOD` - diff --git a/app b/app index aa88066..c02d417 100755 --- a/app +++ b/app @@ -2,613 +2,74 @@ set -e -BASEDIR=`dirname $0` -BASEDIR=`cd $BASEDIR; pwd` +if [ -z "$BASEDIR" ] +then + BASEDIR=`dirname $0` + BASEDIR=`cd $BASEDIR; pwd` +fi export BASEDIR mkdir -p $BASEDIR/.app/var/pid mkdir -p $BASEDIR/.app/var/download -if [ -n "$APPSH_REPO" ] -then - repo="$APPSH_REPO" -else - repo="http://repo1.maven.org" -fi - -# TODO: support file:// repositories -# TODO: look in the local repository first -# TODO: assert that we got a 200 OK -get() { - local exit - - curl -o $2 $1 -D curl.tmp - - exit=`grep "^HTTP/[0-9]\.[0-9] 200 .*" curl.tmp >/dev/null; echo $?` - head=`head -n 1 curl.tmp` - rm curl.tmp - if [ "$exit" != 0 ] - then - echo "Unable to download $1: $head" - exit 1 - fi -} - -resolved_version= -resolve_snapshot() { - local base_url - local metadata - - echo "Resolving version $version..." - metadata=$BASEDIR/.app/var/download/$groupId-$artifactId-$version-metadata.xml - base_url=$repo/$(echo $groupId | sed "s,\.,/,g")/$artifactId/$version - get $base_url/maven-metadata.xml $metadata - resolved_version=`xmlstarlet sel -t -m '//snapshotVersion[extension[text()="zip"]]' -v value $metadata` - - if [ -z "$resolved_version" ] - then - echo "Unable to resolve version." - exit 1 - fi - echo "Resolved version $version to $resolved_version" -} - -zip_file= -download_artifact() { - zip_file=$BASEDIR/.app/var/download/$groupId-$artifactId-$resolved_version.zip - if [ -r $zip_file ] - then - echo "Artifact already downloaded." - return 0 - fi - echo "Downloading artifact" - base_url=$repo/$(echo $groupId | sed "s,\.,/,g")/$artifactId/$version - get $base_url/$artifactId-$resolved_version.zip $zip_file - - # TODO: download checksum. bash is too shady to trust -} - -assert_is_instance() { - local usage=$1 - local name=$2 - local instance=$3 - local check_link=$4 - - if [ -z "$name" ] - then - $usage "Missing required option -n." - fi - - if [ -z "$instance" ] - then - $usage "Missing required option -i." - fi - - if [ ! -d $name/$instance ] - then - echo "No such application/instance: $name/$instance." >&2 - exit 1 - fi - - if [ "$check_link" != "no" ] - then - if [ ! -e $name/$instance/current ] - then - echo "Missing 'current' link." >&2 - exit 1 - fi - fi -} - -install_usage() { - if [ -n "$1" ] - then - echo "Error:" "$@" >&2 - fi - - echo "usage:" >&2 - echo "" - echo "Install Maven artifact from repo:" >&2 - echo " $0 install -m groupId:artifactId [-n name] -i instance [-v version]" >&2 - echo "Name defaults to artifactId." >&2 - echo "" - echo "Install zip file:" >&2 - echo " $0 install -f file -n name -i instance [-v version]" >&2 - echo "The version defaults to the current timestamp" >&2 - exit 1 -} - -method_install() { - local m - - while getopts "m:n:i:v:f:" opt - do - case $opt in - m) - m=$OPTARG - groupId=`echo $OPTARG | cut -s -f 1 -d :` - artifactId=`echo $OPTARG | cut -s -f 2 -d :` - if [ -z "$groupId" -o -z "$artifactId" ] - then - install_usage "Invalid -m value." - fi - ;; - n) - name=$OPTARG - ;; - i) - instance=$OPTARG - ;; - v) - version=$OPTARG - ;; - f) - file=$OPTARG - ;; - \?) - install_usage "Invalid option: -$OPTARG" - ;; - esac - done - - if [ -z "$file" -a -z "$m" ] - then - install_usage "Either -f or -m has to be specified." - fi - - if [ -n "$file" -a -n "$m" ] - then - install_usage "Only one of -f or -m can specified." - fi - - if [ -z "$instance" ] - then - install_usage "Missing required argument: -i instance." - fi - - if [ -z "version" ] - then - install_usage "Missing required argument: -v version." - fi - - if [ -n "$m" ] - then - if [ -z "$name" ] - then - name=$artifactId - fi - - resolve_snapshot - - download_artifact - else - zip_file=$file - - if [ -z "$version" ] - then - version=`TZ=UTC date +"%Y%m%d-%H%M%S"` - fi - - resolved_version=$version - fi - - if [ ! -d $name/$instance ] - then - echo "Creating instance '$instance' for $name" - mkdir -p $name/$instance - fi - - if [ -d $name/$instance/versions/$resolved_version ] - then - echo "Version $resolved_version is already installed" - exit 1 - fi - - mkdir -p $name/$instance/versions/$resolved_version - - echo "Unpacking..." - unzip -q -d $name/$instance/versions/$resolved_version $zip_file - - ( - cd $name/$instance/versions/$resolved_version - if [ -d scripts ] - then - find scripts | xargs chmod +x - fi - - if [ -x scripts/postinstall ] - then - echo "Running postinstall..." - set +e - env -i \ - PATH=$PATH \ - scripts/postinstall - set -e - ret=`echo $?` - if [ "$ret" != 0 ] - then - echo "Postinstall failed!" - exit 1 - fi - echo "Postinstall completed successfully" - fi - ) - - echo "Changing current symlink" - rm -f $BASEDIR/$name/$instance/current - ln -s versions/$resolved_version/root $BASEDIR/$name/$instance/current - - ( - cd $name/$instance/current - find bin -type f | xargs chmod +x - ) - - if [ -r $BASEDIR/.app/var/list ] - then - sed "/^$name:$instance/d" $BASEDIR/.app/var/list > $BASEDIR/.app/var/list.new - fi - echo "$name:$instance:$version" >> $BASEDIR/.app/var/list.new - mv $BASEDIR/.app/var/list.new $BASEDIR/.app/var/list -} - -start_usage() { - if [ -n "$1" ] - then - echo "Error:" "$@" >&2 - fi - - echo "usage: $0 start -n name -i instance" >&2 - exit 1 -} - -stop_usage() { - if [ -n "$1" ] - then - echo "Error:" "$@" >&2 - fi - - echo "usage: $0 stop -n name -i instance" >&2 - exit 1 -} - -# TODO: set ulimit -# TODO: set umask -# TODO: change group newgrp/sg -method_start() { - run_control start_usage "start" "$@" -} - -method_stop() { - run_control stop_usage "stop" "$@" -} - -run_control() { - local usage=$0; shift - local method=$1; shift - local name - local instance - - while getopts "n:i:" opt - do - case $opt in - n) - name=$OPTARG - ;; - i) - instance=$OPTARG - ;; - \?) - $usage "Invalid option: -$OPTARG" - ;; - esac - done - - assert_is_instance $usage "$name" "$instance" - - ( - cd $name/$instance/current - - bin=`get_conf $BASEDIR $name $instance app.method` - - if [ -z "$bin" ] - then - bin=$BASEDIR/.app/lib/default-method - fi - - if [ ! -x "$bin" ] - then - echo "Invalid executable: $bin" >&2 - exit 1 - fi - - e="`get_conf_in_group $BASEDIR $name $instance env`" - - # Set a default PATH which can be overridden by the application's settings - set +e - env -i \ - PATH=/bin:/usr/bin \ - $e \ - APPSH_METHOD=$method \ - APPSH_BASEDIR=$BASEDIR \ - APPSH_NAME=$name \ - APPSH_INSTANCE=$instance \ - $bin - local ret=$? - set +x - set -e - - case $ret in - 0) - echo "Application ${method}ed" - ;; - *) - echo "Error starting $name/$instance" - ;; - esac - ) -} - -list_usage() { - if [ -n "$1" ] - then - echo "Error:" "$@" >&2 - fi - - echo "usage: list [-n name] [-P field]" >&2 - echo "" - echo "List all installed applications" >&2 - echo " $0 list" >&2 - echo "" - echo "List all applications in an parseable format:" >&2 - echo " $0 -P instance -P version -n foo" >&2 - exit 1 -} - -find_current_version() { - name=$1 - instance=$2 - - if [ ! -L $BASEDIR/$name/$instance/current ] - then - return 0 - fi - - ( - cd $BASEDIR/$name/$instance - ls -l current | sed -n "s,.* current -> versions/\(.*\)/root,\1,p" - ) -} - -find_versions() { - name=$1 - instance=$2 - - if [ ! -d $BASEDIR/$name/$instance/versions ] - then - return 0 - fi - - ( - cd $BASEDIR/$name/$instance/versions - ls -1d * - ) -} - -list_apps() { - filter_name=$1 - shift - vars="$@" - - sort $BASEDIR/.app/var/list | while read line - do - echo $line | (IFS=:; while read name instance version junk - do - if [ -n "$filter_name" -a "$filter_name" != "$name" ] - then - continue - fi - - local line="" - IFS=" "; for var in $vars - do - case $var in - name) x=$name;; - instance) x=$instance;; - version) x=$version;; - current_version) x=`find_current_version $name $instance`;; - *) x="";; - esac - - if [ -z "$line" ] - then - line="$line$x" - else - line="$line:$x" - fi - done - echo $line - done) - done -} - -method_list() { - local mode="pretty" - local vars - local filter_name - - while getopts "P:n:" opt - do - case $opt in - P) - mode="parseable" - vars="$vars $OPTARG" - ;; - n) - filter_name=$OPTARG - ;; - \?) - list_usage "Invalid option: -$OPTARG" - ;; - esac - done - - if [ ! -r $BASEDIR/.app/var/list ] - then - return - fi - - if [ $mode = "pretty" ] - then - printf "%-20s %-20s %-20s\n" "Name" "Instance" "Version" - list_apps "$filter_name" name instance version | (IFS=:; while read name instance version - do - printf "%-20s %-20s %-20s\n" "$name" "$instance" "$version" - done) - else - list_apps "$filter_name" $vars - fi -} - -list_versions_usage() { - if [ -n "$1" ] - then - echo "Error:" "$@" >&2 - fi - - echo "usage: list-versions -n name -i instance [-P]" >&2 - echo " -P - parseable output" >&2 - exit 1 -} - -method_list_versions() { - local name - local instance - local version - local mode="pretty" - - while getopts "n:i:P" opt - do - case $opt in - n) - name=$OPTARG - ;; - i) - instance=$OPTARG - ;; - v) - version=$OPTARG - ;; - P) - mode="parseable" - ;; - \?) - list_versions_usage "Invalid option: -$OPTARG" - ;; - esac - done - - assert_is_instance list_versions_usage "$name" "$instance" "no" - - if [ $mode = "pretty" ] - then - echo "Available versions for $name/$instance:" - fi - - find_versions $name $instance - - return 0 -} - -set_current_usage() { - if [ -n "$1" ] - then - echo "Error:" "$@" >&2 - fi - - echo "usage: set-current -n name -i instance -v version" >&2 - exit 1 -} - -method_set_current() { - local name - local instance - local version - - while getopts "n:i:v:" opt - do - case $opt in - n) - name=$OPTARG - ;; - i) - instance=$OPTARG - ;; - v) - version=$OPTARG - ;; - \?) - set_current_usage "Invalid option: -$OPTARG" - ;; - esac - done - - if [ -z "$version" ] - then - echo "Missing required option -v version." >&2 - exit 1 - fi - - assert_is_instance set_current_usage "$name" "$instance" "no" - - if [ ! -d $BASEDIR/$name/$instance/versions/$version ] - then - echo "Invalid version: $version." - exit 1 - fi - - rm -f $BASEDIR/$name/$instance/current - ln -s versions/$version/root $BASEDIR/$name/$instance/current - - return 0 -} - method_usage() { - echo "usage: $0 " >&2 + echo "usage: $0 [-n name] [-i instance] " >&2 echo "" >&2 - echo "Available methods:" >&2 - echo " conf - Application configuration management" >&2 - echo " install - Installs an application" >&2 - echo " list - List all installed applications" >&2 - echo " list-versions - List all available versions for a single application" >&2 - echo " set-current - Set the current version" >&2 - echo " start - Starts an applications" >&2 - echo " stop - Stops an applications" >&2 + echo "Available method groups:" >&2 + echo " app" >&2 + echo " conf" >&2 + echo " operate" >&2 echo "" >&2 - echo "Run '$0 ' to get more help" >&2 + echo "Run $0 -h for more help" >&2 } +. $BASEDIR/.app/lib/app-app . $BASEDIR/.app/lib/app-conf +. $BASEDIR/.app/lib/app-operate main() { - local method="" - local first + local method + local name="$APPSH_NAME" + local instance="$APPSH_INSTANCE" - while getopts "n:i:" opt + while getopts "n:i:h" opt do case $opt in n) name=$OPTARG - first="$first -n $name" - shift - shift + shift 2 OPTIND=1 ;; i) instance=$OPTARG - first="$first -i $instance" - shift + shift 2 + OPTIND=1 + ;; + h) shift OPTIND=1 + h="$1" + + if [ -z "$h" ] + then + method_usage + else + case "$h" in + app) + method_app_usage + ;; + conf) + method_conf_usage + ;; + operate) + method_operate_usage + ;; + *) + echo "No such method group: $h" + ;; + esac + fi + exit 1 ;; \?) echo "Invalid option: $OPTARG" @@ -616,18 +77,17 @@ main() { esac done - method=$1 - shift + if [ $# -gt 0 ] + then + method=$1 + shift + fi case "$method" in - conf) method_conf $first "$@" ;; - install) method_install $first "$@" ;; - list) method_list $first "$@" ;; - list-versions) method_list_versions $first "$@" ;; - set-current) method_set_current $first "$@" ;; - start) method_start $first "$@" ;; - stop) method_stop $first "$@" ;; - *) method_usage "$@" ;; + app) method_app "$name" "$instance" "$@" ;; + conf) method_conf "$name" "$instance" "$@" ;; + operate) method_operate "$name" "$instance" "$@" ;; + *) method_usage ;; esac exit $? } diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..d744062 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,78 @@ +Environment +----------- + +The following environment variables are set by default: + +TODOs +----- + +* Support installation-wide settings. Useful for shared environment + settings etc (PATH). + +* Add support for hooks in .app/hooks. Example hooks: + * Diff config. Save a backup of the config On installtaion + +* Support changing current version. + +* Document app.sh + * Concept: config. group, key and value. + * Scriptable + +* init.d support + +Commands +-------- + +### `app` + +#### `install` + +#### `upgrade` + +Tries to upgrade all instances where the version doesn't match the resolved version. + +#### `list` + +#### `list-versions` + +#### `set-current` + +### `conf` + +#### `get` + + ./app -n $n -i $i conf get + +#### `set` + + ./app -n $n -i $i conf set group.key value + +#### `delete` + + ./app -n $n -i $i conf delete group.key + +### `operate` + +The operate sub-methods are provided by the application. + +#### Supported methods by `pid-method` + +#### `start` + +#### `stop` + +#### `status` + +### `foreach` + +Runs the given command for each of the selected instances. + +Method Contract +--------------- + +### Environment variables you can depend on + +* `APPSH_NAME` +* `APPSH_INSTANCE` +* `APPSH_METHOD` + -- cgit v1.2.3