diff options
author | benoitc <bchesneau@gmail.com> | 2014-02-15 10:24:56 +0100 |
---|---|---|
committer | benoitc <bchesneau@gmail.com> | 2014-02-15 10:24:56 +0100 |
commit | 6e59a7834da941fee2e33941097f24f09ac493a0 (patch) | |
tree | 17ecaf32c858803ace139dfae0f8a1dea3577bf4 | |
parent | 483dc97723009f5b1c67194bf6e0e1e05010829d (diff) | |
download | couchdb-1994-merge-rcouch-multi-repo.tar.gz |
remove node_package dependency1994-merge-rcouch-multi-repo
-rw-r--r-- | NOTICE | 42 | ||||
-rw-r--r-- | rebar.config | 4 | ||||
-rwxr-xr-x | rel/files/app_epath.sh | 173 | ||||
-rwxr-xr-x | rel/files/env.sh | 229 | ||||
-rwxr-xr-x | rel/files/erl | 45 | ||||
-rwxr-xr-x | rel/files/nodetool | 197 | ||||
-rwxr-xr-x | rel/files/runner | 319 | ||||
-rw-r--r-- | rel/reltool.config.script | 10 |
8 files changed, 999 insertions, 20 deletions
@@ -21,7 +21,7 @@ This product also includes the following third-party components: * jQuery (http://jquery.org/) Copyright 2012 jQuery Foundation and other contributors - + * jQuery UI (http://jqueryui.com) Copyright 2011, Paul Bakaus @@ -75,13 +75,13 @@ This product also includes the following third-party components: Copyright 2011, Jeremy Ashkenas * Sphinx (http://sphinx-doc.org/) - + Copyright 2011, the Sphinx team * Sizzle (http://sizzlejs.com/) - + Copyright 2010, The Dojo Foundation - + * Underscore.js 1.4.2 (http://underscorejs.org) Copyright 2012, Jeremy Ashkenas @@ -131,25 +131,25 @@ This product also includes the following third-party components: Copyright 2006-2013 (c) M. Alsup * couch_dbupdates - + Copyright 2012, Benoît Chesneau <benoitc@refuge.io> - + * mocha.js (https://github.com/visionmedia/mocha) - + Copyright (c) 2011-2013 TJ Holowaychuk <tj@vision-media.ca> * chaijs https://github.com/chaijs - + Copyright (c) 2011-2013 Jake Luer jake@alogicalparadox.com * sinon-chai Copyright © 2012–2013 Domenic Denicola <domenic@domenicdenicola.com> - + * spin.js Copyright (c) 2011 Felix Gnass [fgnass at neteye dot de] - + * font-awesome http://fortawesome.github.io/Font-Awesome/ Copyright (c) 2013 Dave Gandy @@ -188,4 +188,24 @@ This product also includes the following third-party components: * rebar https://github.com/rebar/rebar - Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) + Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) + + * rel/files/app_epath.sh https://github.com/basho/node_package + + Copyright 2014 Basho - Apache License 2 + + * rel/files/env.sh https://github.com/basho/node_package + + Copyright 2014 Basho - Apache License 2 + + * rel/files/erl https://github.com/basho/node_package + + Copyright 2014 Basho - Apache License 2 + + * rel/files/nodetool https://github.com/basho/node_package + + Copyright 2014 Basho - Apache License 2 + + * rel/files/runner https://github.com/basho/node_package + + Copyright 2014 Basho - Apache License 2 diff --git a/rebar.config b/rebar.config index a6959a382..a7a255c53 100644 --- a/rebar.config +++ b/rebar.config @@ -9,10 +9,6 @@ {require_otp_vsn, "R14B04|R15|R16"}. {deps, [ - %% packaging - {node_package, ".*", {git, "git://github.com/basho/node_package", - {tag, "1.3.8"}}}, - %% erlang oauth module {oauth, ".*", {git, "http://git-wip-us.apache.org/repos/asf/couchdb-oauth.git", diff --git a/rel/files/app_epath.sh b/rel/files/app_epath.sh new file mode 100755 index 000000000..a3de910b0 --- /dev/null +++ b/rel/files/app_epath.sh @@ -0,0 +1,173 @@ +#!/bin/sh + +## --------------------------------------------------------------------- +## +## app_epath.sh: Parse Erlang app.config with POSIX tools for automation +## +## Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved. +## +## This file is provided to you under the Apache License, +## Version 2.0 (the "License"); you may not use this file +## except in compliance with the License. You may obtain +## a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, +## software distributed under the License is distributed on an +## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +## KIND, either express or implied. See the License for the +## specific language governing permissions and limitations +## under the License. +## +## --------------------------------------------------------------------- + +## Example usage: +# +# #!/bin/sh +# +# # Load the functions +# . path/to/app_epath.sh +# +# # Build the path info +# epaths=`make_app_epaths path/to/app.config` +# +# # View all of the settings. Quotes are important. +# echo "$epaths" +# +# # Grep around in the paths for various items. +# echo "$epaths" | grep 'riak_core ring_creation_size' +# echo "$epaths" | grep "lager handlers lager_file_backend" | grep info +# +# # Use the epath function to get directly at settings +# epath 'riak_core ring_creation_size' "$epaths" +# epath 'riak_core platform_bin_dir' "$epaths" +# epath 'riak_kv storage_backend' "$epaths" +# +# # Use epath to view all of the riak_core settings +# epath riak_core +# +## End example + +## Here is a command you can put in your path for general cli use. +# +# #!/bin/sh +# +# # $1 quoted eterm path for search: 'riak_core ring_creation_size' +# # $2 path/to/app.config +# +# . path/to/app_epath.sh +# +# epaths=`make_app_epaths "$2"` +# epath "$1" "$epaths" +# +## End epath command + +# make_app_epaths takes a path to an app.config file as its argument and +# and returns (prints) a flattened text structure of configuration settings. + +make_app_epaths () { + # Remove all lines containing comments + # Remove all blank lines + # Remove the first [ + # Remove the last ]. + # Remove all blank lines again (just in case) + appconfig=`cat $1 \ + | sed '/^[ \t]*%/d' \ + | sed '/^[ \t]*$/d' \ + | sed -e '/\[/{s///;:a' -e '$!N;$!ba' -e '}' \ + | sed '$ s/\]\.//g' \ + | sed '/^[ \t]*$/d'` + + STACK= + INQUOTE=0 + # The awk puts each char, spaces included, on their own line for parsing. + echo "$appconfig" | awk '{gsub(//,"\n");print}' | while read i; do + case "x${i}x" in + "x\"x") + # Flip the INQUOTE state and echo the quote + if [ 1 -eq $INQUOTE ]; then + INQUOTE=0 + else + INQUOTE=1 + fi + STACK="${STACK}${i}" + ;; + "xx") + if [ 1 -eq $INQUOTE ]; then + # If in quotes, keep this space + STACK="${STACK} " + fi + ;; + "x,x") + if [ 1 -eq $INQUOTE ]; then + # If in quotes, keep this comma + STACK="${STACK}," + else + # Commas outside quotes means a new item is next + STACK="${STACK} " + fi + ;; + "x{x") + if [ 1 -eq $INQUOTE ]; then + # If in quotes, keep this bracket + STACK="${STACK}{" + else + # Add brace to the stack; will pop off from here on next } + STACK="${STACK} __EBRACE__ " + fi + ;; + "x}x") + if [ 1 -eq $INQUOTE ]; then + # If in quotes, keep this bracket + STACK="${STACK}}" + else + # We're only interested in printing leaves, not + # intermediates like 'riak_core http', which contain lists. + # See if the current stack ends with ] (end of list). + echo $STACK | grep -E "__EBRACKET__$" >/dev/null 2>&1 + if [ 1 -eq $? ]; then + # If not, print the stack without all of the mess. + echo "$STACK" | \ + sed 's/ *__EBRACE__//g;s/ *__EBRACKET__//g;s/^ *//' + fi + # Pop off everything from the last brace on. + STACK=`echo "$STACK" | sed 's/\(.*\) __EBRACE__.*/\1/g'` + fi + ;; + "x[x") + if [ 1 -eq $INQUOTE ]; then + # If in quotes, keep this bracket + STACK="${STACK}[" + else + # Add a placeholder to aid in determining whether or not to + # print. That is, we don't want to print 'riak_core http'. + STACK="${STACK} __EBRACKET__ " + fi + ;; + "x]x") + if [ 1 -eq $INQUOTE ]; then + # If in quotes, keep this bracket + STACK="${STACK}]" + fi + # Don't actually do anything with ], as the starting brackets + # are instead removed with }. + ;; + *) + # Anything else is just pushed. + STACK="${STACK}${i}" + ;; + esac + done +} + +epath () { + # arg1 - a pattern to search for + # arg2 - output of make_app_epaths, passed in quoted + # output - search of arg2 for arg1, trimming arg1 from the beginning + # Note: there may be multiple lines of output. + pat=$1 + shift + echo "$*" | grep "$pat " | sed "s/^${pat} *//" +} + diff --git a/rel/files/env.sh b/rel/files/env.sh new file mode 100755 index 000000000..5a44a9919 --- /dev/null +++ b/rel/files/env.sh @@ -0,0 +1,229 @@ +#!/bin/sh +# -*- tab-width:4;indent-tabs-mode:nil -*- +# ex: ts=4 sw=4 et + +# installed by node_package (github.com/basho/node_package) + +# /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. +if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then + POSIX_SHELL="true" + export POSIX_SHELL + # To support 'whoami' add /usr/ucb to path + PATH=/usr/ucb:$PATH + export PATH + exec /usr/bin/ksh $0 "$@" +fi +unset POSIX_SHELL # clear it so if we invoke other scripts, they run as ksh as well + +RUNNER_SCRIPT_DIR={{runner_script_dir}} +RUNNER_SCRIPT=${0##*/} + +RUNNER_BASE_DIR={{runner_base_dir}} +RUNNER_ETC_DIR={{runner_etc_dir}} +RUNNER_LOG_DIR={{runner_log_dir}} +RUNNER_LIB_DIR={{runner_lib_dir}} +RUNNER_PATCH_DIR={{runner_patch_dir}} +PIPE_DIR={{pipe_dir}} +RUNNER_USER={{runner_user}} +APP_VERSION={{app_version}} + +# Variables needed to support creation of .pid files +# PID directory and pid file name of this app +# ex: /var/run/riak & /var/run/riak/riak.pid +RUN_DIR="/var/run" # for now hard coded unless we find a platform that differs +PID_DIR=$RUN_DIR/$RUNNER_SCRIPT +PID_FILE=$PID_DIR/$RUNNER_SCRIPT.pid + +# Threshold where users will be warned of low ulimit file settings +# default it if it is not set +ULIMIT_WARN={{runner_ulimit_warn}} +if [ -z "$ULIMIT_WARN" ]; then + ULIMIT_WARN=4096 +fi + +# Registered process to wait for to consider start a success +WAIT_FOR_PROCESS={{runner_wait_process}} + +WHOAMI=`whoami` + +# Echo to stderr on errors +echoerr() { echo "$@" 1>&2; } + +# Extract the target node name from node.args +NAME_ARG=`egrep '^\-s?name' $RUNNER_ETC_DIR/vm.args` +if [ -z "$NAME_ARG" ]; then + echoerr "vm.args needs to have either -name or -sname parameter." + exit 1 +fi + +# Learn how to specify node name for connection from remote nodes +echo "$NAME_ARG" | grep '^-sname' > /dev/null 2>&1 +if [ "X$?" = "X0" ]; then + NAME_PARAM="-sname" + NAME_HOST="" +else + NAME_PARAM="-name" + echo "$NAME_ARG" | grep '@.*' > /dev/null 2>&1 + if [ "X$?" = "X0" ]; then + NAME_HOST=`echo "${NAME_ARG}" | sed -e 's/.*\(@.*\)$/\1/'` + else + NAME_HOST="" + fi +fi + +# Extract the target cookie +COOKIE_ARG=`grep '^\-setcookie' $RUNNER_ETC_DIR/vm.args` +if [ -z "$COOKIE_ARG" ]; then + echoerr "vm.args needs to have a -setcookie parameter." + exit 1 +fi + +# Parse out release and erts info +START_ERL=`cat $RUNNER_BASE_DIR/releases/start_erl.data` +ERTS_VSN=${START_ERL% *} +APP_VSN=${START_ERL#* } + +# Add ERTS bin dir to our path +ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin + +# Setup command to control the node +NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG" +NODETOOL_LITE="$ERTS_PATH/escript $ERTS_PATH/nodetool" + +# Ping node without stealing stdin +ping_node() { + $NODETOOL ping < /dev/null +} + +# Attempts to create a pid directory like /var/run/APPNAME and then +# changes the permissions on that directory so the $RUNNER_USER can +# read/write/delete .pid files during startup/shutdown +create_pid_dir() { + # Validate RUNNER_USER is set and they have permissions to write to /var/run + # Don't continue if we've already sudo'd to RUNNER_USER + if ([ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]); then + if [ -w $RUN_DIR ]; then + mkdir -p $PID_DIR + ES=$? + if [ "$ES" -ne 0 ]; then + return 1 + else + # Change permissions on $PID_DIR + chown $RUNNER_USER $PID_DIR + ES=$? + if [ "$ES" -ne 0 ]; then + return 1 + else + return 0 + fi + fi + else + # If we don't have permissions, fail + return 1 + fi + fi + + # If RUNNER_USER is not set this is probably a test setup (devrel) and does + # not need a .pid file, so do not return error + return 0 +} + +# Attempt to create a pid file for the process +# This function assumes the process is already up and running and can +# respond to a getpid call. It also assumes that two processes +# with the same name will not be run on the machine +# Do not print any error messages as failure to create a pid file because +# pid files are strictly optional +# This function should really only be called in a "start" function +# you have been warned +create_pid_file() { + # Validate a pid directory even exists + if [ -w $PID_DIR ]; then + # Grab the proper pid from getpid + get_pid + ES=$? + if [ "$ES" -ne 0 ]; then + return $ES + else + # Remove pid file if it already exists since we do not + # plan for multiple identical runners on a single machine + rm -f $PID_FILE + echo $PID > $PID_FILE + return 0 + fi + else + return 1 + fi +} + +# Function to su into correct user +check_user() { + # Validate that the user running the script is the owner of the + # RUN_DIR. + if ([ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]); then + type sudo > /dev/null 2>&1 + if [ "$?" -ne 0 ]; then + echoerr "sudo doesn't appear to be installed and your EUID isn't $RUNNER_USER" 1>&2 + exit 1 + fi + exec sudo -H -u $RUNNER_USER -i $RUNNER_SCRIPT_DIR/$RUNNER_SCRIPT $@ + fi +} + +# Function to validate the node is down +node_down_check() { + MUTE=`ping_node 2> /dev/null` + if [ "$?" -eq 0 ]; then + echoerr "Node is already running!" + exit 1 + fi +} + +# Function to validate the node is up +node_up_check() { + MUTE=`ping_node 2> /dev/null` + if [ "$?" -ne 0 ]; then + echoerr "Node is not running!" + exit 1 + fi +} + +# Function to check if the config file is valid +check_config() { + MUTE=`$NODETOOL_LITE chkconfig $RUNNER_ETC_DIR/app.config` + if [ "$?" -ne 0 ]; then + echoerr "Error reading $RUNNER_ETC_DIR/app.config" + exit 1 + fi + echo "config is OK" +} + +# Function to check if ulimit is properly set +check_ulimit() { + + # don't fail if this is unset + if [ ! -z "$ULIMIT_WARN" ]; then + ULIMIT_F=`ulimit -n` + if [ "$ULIMIT_F" -lt $ULIMIT_WARN ]; then + echo "!!!!" + echo "!!!! WARNING: ulimit -n is ${ULIMIT_F}; ${ULIMIT_WARN} is the recommended minimum." + echo "!!!!" + fi + fi +} + +# Set the PID global variable, return 1 on error +get_pid() { + PID=`$NODETOOL getpid < /dev/null` + if [ "$?" -ne 0 ]; then + echo "Node is not running!" + return 1 + fi + + # don't allow empty or init pid's + if [ -z $PID ] || [ "$PID" -le 1 ]; then + return 1 + fi + + return 0 +} diff --git a/rel/files/erl b/rel/files/erl new file mode 100755 index 000000000..ec41a7700 --- /dev/null +++ b/rel/files/erl @@ -0,0 +1,45 @@ +#!/bin/sh + +# /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. +if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then + POSIX_SHELL="true" + export POSIX_SHELL + exec /usr/bin/ksh $0 "$@" +fi +unset POSIX_SHELL # clear it so if we invoke other scripts, they run as ksh as well + + +## This script replaces the default "erl" in erts-VSN/bin. This is necessary +## as escript depends on erl and in turn, erl depends on having access to a +## bootscript (start.boot). Note that this script is ONLY invoked as a side-effect +## of running escript -- the embedded node bypasses erl and uses erlexec directly +## (as it should). +## +## Note that this script makes the assumption that there is a start_clean.boot +## file available in $ROOTDIR/release/VSN. + +## installed by node_package (github.com/basho/node_package) + +# Determine the abspath of where this script is executing from. +ERTS_BIN_DIR=$(cd ${0%/*} && pwd) + +# Now determine the root directory -- this script runs from erts-VSN/bin, +# so we simply need to strip off two dirs from the end of the ERTS_BIN_DIR +# path. +ROOTDIR=${ERTS_BIN_DIR%/*/*} + +# Parse out release and erts info +START_ERL=`cat $ROOTDIR/releases/start_erl.data` +ERTS_VSN=${START_ERL% *} +APP_VSN=${START_ERL#* } + +BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin +EMU=beam +PROGNAME=`echo $0 | sed 's/.*\///'` +CMD="$BINDIR/erlexec" +export EMU +export ROOTDIR +export BINDIR +export PROGNAME + +exec $CMD -boot $ROOTDIR/releases/$APP_VSN/start_clean ${1+"$@"} diff --git a/rel/files/nodetool b/rel/files/nodetool new file mode 100755 index 000000000..41b88e455 --- /dev/null +++ b/rel/files/nodetool @@ -0,0 +1,197 @@ +#!/usr/bin/env escript +%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ft=erlang ts=4 sw=4 et +%% ------------------------------------------------------------------- +%% +%% nodetool: Helper Script for interacting with live nodes +%% +%% ------------------------------------------------------------------- + +%% installed by node_package (github.com/basho/node_package) + +main(Args) -> + ok = start_epmd(), + %% Extract the args + {RestArgs, TargetNode} = process_args(Args, [], undefined), + + %% Extract the RPC timeout from process dictionary, if it's defined + RpcTimeout = case erlang:get(rpctimeout) of + undefined -> + 60000; + Value -> + Value + end, + + %% process_args() has side-effects (e.g. when processing "-name"), + %% so take care of app-starting business first. + [application:start(App) || App <- [crypto, public_key, ssl]], + + %% any commands that don't need a running node + case RestArgs of + ["chkconfig", File] -> + case file:consult(File) of + {ok, _} -> + io:format("ok\n"), + halt(0); + {error, {Line, Mod, Term}} -> + io:format(standard_error, + ["Error on line ", + file:format_error({Line, Mod, Term}), "\n"], []), + halt(1); + {error, R} -> + io:format(standard_error, + ["Error reading config file: ", + file:format_error(R), "\n"], []), + halt(1) + end; + _ -> + ok + end, + + %% See if the node is currently running -- if it's not, we'll bail + case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of + {true, pong} -> + ok; + {false, pong} -> + io:format(standard_error, "failed to connect to node ~p .\n", [TargetNode]), + halt(1); + {_, pang} -> + io:format(standard_error, "Node ~p not responding to pings.\n", [TargetNode]), + halt(1) + end, + + case RestArgs of + ["getpid"] -> + io:format("~p\n", [list_to_integer(rpc:call(TargetNode, os, getpid, []))]); + ["ping"] -> + %% If we got this far, the node already responsed to a ping, so just dump + %% a "pong" + io:format("pong\n"); + ["stop"] -> + io:format("~p\n", [rpc:call(TargetNode, init, stop, [], RpcTimeout)]); + ["restart"] -> + io:format("~p\n", [rpc:call(TargetNode, init, restart, [], RpcTimeout)]); + ["reboot"] -> + io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], RpcTimeout)]); + ["rpc", Module, Function | RpcArgs] -> + case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), + [RpcArgs], RpcTimeout) of + ok -> + ok; + {badrpc, Reason} -> + io:format(standard_error, "RPC to ~p failed: ~p\n", [TargetNode, Reason]), + halt(1); + _ -> + halt(1) + end; + ["rpc_infinity", Module, Function | RpcArgs] -> + case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), [RpcArgs], infinity) of + ok -> + ok; + {badrpc, Reason} -> + io:format(standard_error, "RPC to ~p failed: ~p\n", [TargetNode, Reason]), + halt(1); + _ -> + halt(1) + end; + ["rpcterms", Module, Function, ArgsAsString] -> + case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), + consult(ArgsAsString), RpcTimeout) of + {badrpc, Reason} -> + io:format(standard_error, "RPC to ~p failed: ~p\n", [TargetNode, Reason]), + halt(1); + Other -> + io:format("~p\n", [Other]) + end; + Other -> + io:format("Other: ~p\n", [Other]), + io:format("Usage: nodetool {ping|stop|restart|reboot|chkconfig}\n") + end, + net_kernel:stop(). + +process_args([], Acc, TargetNode) -> + {lists:reverse(Acc), TargetNode}; +process_args(["-setcookie", Cookie | Rest], Acc, TargetNode) -> + erlang:set_cookie(node(), list_to_atom(Cookie)), + process_args(Rest, Acc, TargetNode); +process_args(["-name", TargetName | Rest], Acc, _) -> + ThisNode = append_node_suffix(TargetName, "_maint_"), + {ok, _} = net_kernel:start([ThisNode, longnames]), + process_args(Rest, Acc, nodename(TargetName)); +process_args(["-sname", TargetName | Rest], Acc, _) -> + ThisNode = append_node_suffix(TargetName, "_maint_"), + {ok, _} = net_kernel:start([ThisNode, shortnames]), + process_args(Rest, Acc, nodename(TargetName)); +process_args(["-rpctimeout", TimeoutStr | Rest], Acc, TargetNode) -> + Timeout = case TimeoutStr of + "infinity" -> infinity; + _ -> list_to_integer(TimeoutStr) + end, + erlang:put(rpctimeout, Timeout), + process_args(Rest, Acc, TargetNode); +process_args([Arg | Rest], Acc, Opts) -> + process_args(Rest, [Arg | Acc], Opts). + + +start_epmd() -> + [] = os:cmd(epmd_path() ++ " -daemon"), + ok. + +epmd_path() -> + ErtsBinDir = filename:dirname(escript:script_name()), + Name = "epmd", + case os:find_executable(Name, ErtsBinDir) of + false -> + case os:find_executable(Name) of + false -> + io:format("Could not find epmd.~n"), + halt(1); + GlobalEpmd -> + GlobalEpmd + end; + Epmd -> + Epmd + end. + + +nodename(Name) -> + case string:tokens(Name, "@") of + [_Node, _Host] -> + list_to_atom(Name); + [Node] -> + [_, Host] = string:tokens(atom_to_list(node()), "@"), + list_to_atom(lists:concat([Node, "@", Host])) + end. + +append_node_suffix(Name, Suffix) -> + case string:tokens(Name, "@") of + [Node, Host] -> + list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host])); + [Node] -> + list_to_atom(lists:concat([Node, Suffix, os:getpid()])) + end. + + +%% +%% Given a string or binary, parse it into a list of terms, ala file:consult/0 +%% +consult(Str) when is_list(Str) -> + consult([], Str, []); +consult(Bin) when is_binary(Bin)-> + consult([], binary_to_list(Bin), []). + +consult(Cont, Str, Acc) -> + case erl_scan:tokens(Cont, Str, 0) of + {done, Result, Remaining} -> + case Result of + {ok, Tokens, _} -> + {ok, Term} = erl_parse:parse_term(Tokens), + consult([], Remaining, [Term | Acc]); + {eof, _Other} -> + lists:reverse(Acc); + {error, Info, _} -> + {error, Info} + end; + {more, Cont1} -> + consult(Cont1, eof, Acc) + end. diff --git a/rel/files/runner b/rel/files/runner new file mode 100755 index 000000000..ffad13f96 --- /dev/null +++ b/rel/files/runner @@ -0,0 +1,319 @@ +#!/bin/sh +# -*- tab-width:4;indent-tabs-mode:nil -*- +# ex: ts=4 sw=4 et + +# installed by node_package (github.com/basho/node_package) + +# Pull environment for this install +. "{{runner_base_dir}}/lib/env.sh" + + +# Keep track of where script was invoked +ORIGINAL_DIR=$(pwd) + +# Make sure CWD is set to runner run dir +cd $RUNNER_BASE_DIR + +# Identify the script name +SCRIPT=`basename $0` + +usage() { + echo "Usage: $SCRIPT {start | stop| restart | reboot | ping | console | attach | " + echo " attach-direct | ertspath | chkconfig | escript | version | " + echo " getpid | top [-interval N] [-sort reductions|memory|msg_q] [-lines N] }" +} + +# All commands must either call bootstrap or bootstrapd +# Call bootstrap for non-daemon commands like ping or chkconfig +# Call bootstrapd for daemon commands like start/stop/console +bootstrap() { + # Make sure the user running this script is the owner and/or su to that user + check_user $@ + ES=$? + if [ "$ES" -ne 0 ]; then + exit $ES + fi +} + +bootstrapd() { + # Create PID directory if it does not exist before dropping permissiongs + # to the runner user + create_pid_dir + ES=$? + if [ "$ES" -ne 0 ]; then + echoerr "Unable to access $PID_DIR, permission denied, run script as root" + exit 1 + fi + + # Now call bootstrap to drop to $RUNNER_USER + bootstrap $@ +} + +# Check the first argument for instructions +case "$1" in + start) + # Bootstrap daemon command (check perms & drop to $RUNNER_USER) + bootstrapd $@ + + # Make sure there is not already a node running + node_down_check + + # Sanity check the app.config file + check_config > /dev/null + ES=$? + if [ "$ES" -ne 0 ]; then + exit $ES + fi + + # Warn the user if ulimit is too low + check_ulimit + + # Make sure log directory exists + mkdir -p $RUNNER_LOG_DIR + + HEART_COMMAND="$RUNNER_SCRIPT_DIR/$RUNNER_SCRIPT start" + export HEART_COMMAND + mkdir -p $PIPE_DIR + $ERTS_PATH/run_erl -daemon $PIPE_DIR/ $RUNNER_LOG_DIR \ + "exec $RUNNER_SCRIPT_DIR/$RUNNER_SCRIPT console" 2>&1 + + if [ ! -z "$WAIT_FOR_PROCESS" ]; then + # Wait for the node to come up. We can't just ping it because + # distributed erlang comes up for a second before the node crashes + # (eg. in the case of an unwriteable disk). Once the node comes + # up we check for the $WAIT_FOR_PROCESS} process. If that's running + # then we assume things are good enough. This will at least let + # the user know when the node is crashing right after startup. + WAIT=${WAIT_FOR_ERLANG:-15} + while [ $WAIT -gt 0 ]; do + WAIT=`expr $WAIT - 1` + sleep 1 + + # squash stderr output to not frighten users if the node does not + # come up right away + MUTE=`ping_node 2> /dev/null` + if [ "$?" -ne 0 ]; then + continue + fi + PROCESS=`$NODETOOL rpcterms erlang whereis "'${WAIT_FOR_PROCESS}'."` + if [ "$PROCESS" != "undefined" ]; then + # Attempt to create a .pid file for the process + create_pid_file + exit 0 + fi + done + echo "${SCRIPT} failed to start within ${WAIT_FOR_ERLANG:-15} seconds," + echo "see the output of '${SCRIPT} console' for more information." + echo "If you want to wait longer, set the environment variable" + echo "WAIT_FOR_ERLANG to the number of seconds to wait." + exit 1 + fi + + # Attempt to create .pid file + create_pid_file + ;; + + stop) + # Bootstrap daemon command (check perms & drop to $RUNNER_USER) + bootstrapd $@ + + get_pid + ES=$? + if [ "$ES" -ne 0 ] || [ -z $PID ]; then + exit $ES + fi + + # Tell nodetool to stop + $NODETOOL stop + ES=$? + if [ "$ES" -ne 0 ]; then + exit $ES + fi + + # Now wait for the app to *really* stop + while `kill -s 0 $PID 2>/dev/null`; + do + sleep 1 + done + + # remove pid file + rm -f $PID_FILE + ;; + + restart) + # Bootstrap daemon command (check perms & drop to $RUNNER_USER) + bootstrapd $@ + + ## Restart the VM without exiting the process + $NODETOOL restart + ES=$? + if [ "$ES" -ne 0 ]; then + exit $ES + fi + ;; + + reboot) + # Bootstrap daemon command (check perms & drop to $RUNNER_USER) + bootstrapd $@ + + ## Restart the VM completely (uses heart to restart it) + $NODETOOL reboot + ES=$? + if [ "$ES" -ne 0 ]; then + exit $ES + fi + ;; + + ping) + # Bootstrap command (simply drop to $RUNNER_USER) + bootstrap $@ + + ## See if the VM is alive + ping_node + ES=$? + if [ "$ES" -ne 0 ]; then + exit $ES + fi + ;; + + attach-direct) + # Bootstrap daemon command (check perms & drop to $RUNNER_USER) + bootstrapd $@ + + # Allow attaching to a node without pinging it + if [ "$2" = "-f" ]; then + echo "Forcing connection..." + else + # Make sure a node is running + node_up_check + fi + + echo "Direct Shell: Use \"Ctrl-D\" to quit. \"Ctrl-C\" will terminate the $SCRIPT node." + shift + exec $ERTS_PATH/to_erl $PIPE_DIR + ;; + + attach) + # Bootstrap daemon command (check perms & drop to $RUNNER_USER) + bootstrapd $@ + + # Make sure a node is running + node_up_check + + echo "Remote Shell: Use \"Ctrl-C a\" to quit. q() or init:stop() will terminate the $SCRIPT node." + shift + NODE_NAME=${NAME_ARG#* } + exec $ERTS_PATH/erl -name c_$$_$NODE_NAME -hidden -remsh $NODE_NAME $COOKIE_ARG + ;; + + console) + # Bootstrap daemon command (check perms & drop to $RUNNER_USER) + bootstrapd $@ + + RES=`ping_node` + if [ "$?" -eq 0 ]; then + echo "Node is already running - use '$SCRIPT attach' instead" + exit 1 + fi + + # Sanity check the app.config file + check_config + ES=$? + if [ "$ES" -ne 0 ]; then + exit $ES + fi + + # Warn the user if ulimit -n is less than the defined threshold + check_ulimit + + # Make sure log directory exists + mkdir -p $RUNNER_LOG_DIR + + # Setup beam-required vars + ROOTDIR=$RUNNER_BASE_DIR + BINDIR=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin + EMU=beam + PROGNAME=`echo $0 | sed 's/.*\///'` + CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$RUNNER_SCRIPT \ + -config $RUNNER_ETC_DIR/app.config \ + -pa $RUNNER_PATCH_DIR \ + -args_file $RUNNER_ETC_DIR/vm.args -- ${1+"$@"}" + export EMU + export ROOTDIR + export BINDIR + export PROGNAME + + # Dump environment info for logging purposes + echo "Exec: $CMD" + echo "Root: $ROOTDIR" + + # Log the startup + logger -t "$SCRIPT[$$]" "Starting up" + + # Start the VM + exec $CMD + ;; + + top) + # Bootstrap command (simply drop to $RUNNER_USER) + bootstrap $@ + + # Make sure the local node IS running + node_up_check + + shift + MYPID=$$ + NODE_NAME=${NAME_ARG#* } + $ERTS_PATH/erl -noshell -noinput \ + -pa $RUNNER_PATCH_DIR \ + -hidden $NAME_PARAM np_etop$MYPID$NAME_HOST $COOKIE_ARG \ + -s etop -s erlang halt -output text \ + -node $NODE_NAME \ + $* -tracing off + ;; + + ertspath) + echo $ERTS_PATH + ;; + + chkconfig) + # Bootstrap command (simply drop to $RUNNER_USER) + bootstrap $@ + + check_config + ;; + + escript) + # Bootstrap command (simply drop to $RUNNER_USER) + bootstrap $@ + + shift + $ERTS_PATH/escript "$@" + ES=$? + if [ "$ES" -ne 0 ]; then + exit $ES + fi + ;; + + version) + echo $APP_VERSION + ;; + + getpid) + # Bootstrap command (simply drop to $RUNNER_USER) + bootstrap $@ + + # Get the PID from nodetool + get_pid + ES=$? + if [ "$ES" -ne 0 ] || [ -z $PID ]; then + exit $ES + fi + echo $PID + ;; + *) + usage + ;; +esac + +exit 0 diff --git a/rel/reltool.config.script b/rel/reltool.config.script index 60e3a370d..19b40a537 100644 --- a/rel/reltool.config.script +++ b/rel/reltool.config.script @@ -127,11 +127,11 @@ CouchJSName = proplists:get_value(couchjs_name, Cfg, "couchjs"), {copy, "../etc/couchdb/empty", "data/KEEP"}, %% Copy base files for starting and interacting w/ node - {copy, "../src/node_package/priv/base/erl", "{{erts_vsn}}/bin/erl"}, - {copy, "../src/node_package/priv/base/nodetool", "{{erts_vsn}}/bin/nodetool"}, - {template, "../src/node_package/priv/base/runner", "bin/couchdb"}, - {template, "../src/node_package/priv/base/env.sh", "lib/env.sh"}, - {template, "../src/node_package/priv/base/app_epath.sh", "lib/app_epath.sh"}, + {copy, "files/erl", "{{erts_vsn}}/bin/erl"}, + {copy, "files/nodetool", "{{erts_vsn}}/bin/nodetool"}, + {template, "files/runner", "bin/couchdb"}, + {template, "files/env.sh", "lib/env.sh"}, + {template, "files/app_epath.sh", "lib/app_epath.sh"}, %% config files {template, "../etc/couchdb/app.config", "etc/app.config"}, |