From 88d1f29257869b0de9369881236477163804125a Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Tue, 28 Apr 2015 22:36:44 -0500 Subject: use mustache instead of erlydtl for overlays --- priv/templates/bin | 63 +++++ priv/templates/bin.dtl | 63 ----- priv/templates/bin_windows | 85 ++++++ priv/templates/bin_windows.dtl | 85 ------ priv/templates/erl_ini | 4 + priv/templates/erl_ini.dtl | 4 - priv/templates/erl_script | 13 + priv/templates/erl_script.dtl | 13 - priv/templates/extended_bin | 428 +++++++++++++++++++++++++++++ priv/templates/extended_bin.dtl | 428 ----------------------------- priv/templates/extended_bin_windows | 207 ++++++++++++++ priv/templates/extended_bin_windows.dtl | 207 -------------- priv/templates/install_upgrade_escript | 134 +++++++++ priv/templates/install_upgrade_escript.dtl | 134 --------- priv/templates/nodetool | 137 +++++++++ priv/templates/nodetool.dtl | 137 --------- priv/templates/sys_config | 9 + priv/templates/sys_config.dtl | 9 - priv/templates/vm_args | 19 ++ priv/templates/vm_args.dtl | 19 -- rebar.config | 16 +- rebar.lock | 18 +- src/rlx_prv_archive.erl | 3 +- src/rlx_prv_assembler.erl | 26 +- src/rlx_prv_overlay.erl | 208 ++++++-------- src/rlx_util.erl | 62 ++++- test/rlx_release_SUITE.erl | 64 +---- test/rlx_test_utils.erl | 20 +- 28 files changed, 1270 insertions(+), 1345 deletions(-) create mode 100755 priv/templates/bin delete mode 100755 priv/templates/bin.dtl create mode 100644 priv/templates/bin_windows delete mode 100644 priv/templates/bin_windows.dtl create mode 100644 priv/templates/erl_ini delete mode 100644 priv/templates/erl_ini.dtl create mode 100644 priv/templates/erl_script delete mode 100644 priv/templates/erl_script.dtl create mode 100755 priv/templates/extended_bin delete mode 100755 priv/templates/extended_bin.dtl create mode 100644 priv/templates/extended_bin_windows delete mode 100644 priv/templates/extended_bin_windows.dtl create mode 100644 priv/templates/install_upgrade_escript delete mode 100644 priv/templates/install_upgrade_escript.dtl create mode 100644 priv/templates/nodetool delete mode 100644 priv/templates/nodetool.dtl create mode 100644 priv/templates/sys_config delete mode 100644 priv/templates/sys_config.dtl create mode 100644 priv/templates/vm_args delete mode 100644 priv/templates/vm_args.dtl diff --git a/priv/templates/bin b/priv/templates/bin new file mode 100755 index 0000000..dd11707 --- /dev/null +++ b/priv/templates/bin @@ -0,0 +1,63 @@ +#!/bin/sh + +set -e + +SCRIPT_DIR="$(dirname "$0")" +RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REL_NAME="{{ rel_name }}" +REL_VSN="{{ rel_vsn }}" +ERTS_VSN="{{ erts_vsn }}" +REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN" +ERL_OPTS="{{ erl_opts }}" + +find_erts_dir() { + local erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN" + if [ -d "$erts_dir" ]; then + ERTS_DIR="$erts_dir"; + ROOTDIR="$RELEASE_ROOT_DIR" + else + local erl="$(which erl)" + code="io:format(\"~s\", [code:root_dir()]), halt()." + local erl_root="$("$erl" -noshell -eval "$code")" + ERTS_DIR="$erl_root/erts-$ERTS_VSN" + ROOTDIR="$erl_root" + fi +} + +find_sys_config() { + local possible_sys="$REL_DIR/sys.config" + if [ -f "$possible_sys" ]; then + SYS_CONFIG="$possible_sys" + fi +} + +find_vm_args() { + local possible_vm_args="$REL_DIR/vm.args" + if [ -f "$possible_vm_args" ]; then + VM_ARGS="$possible_vm_args" + fi +} + +find_erts_dir +find_sys_config +find_vm_args +export ROOTDIR="$RELEASE_ROOT_DIR" +export BINDIR="$ERTS_DIR/bin" +export EMU="beam" +export PROGNAME="erl" +export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH" +ERTS_LIB_DIR="$ERTS_DIR/../lib" +[ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start +cd "$ROOTDIR" + +# Save extra arguments +ARGS="$@" + +# Build arguments for erlexec +set -- "$ERL_OPTS" +[ "$SYS_CONFIG" ] && set -- "$@" -config "$SYS_CONFIG" +[ "$VM_ARGS" ] && set -- "$@" -args_file "$VM_ARGS" +set -- "$@" -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" -boot "$REL_DIR/$BOOTFILE" "$ARGS" + +# Boot the release +$BINDIR/erlexec $@ diff --git a/priv/templates/bin.dtl b/priv/templates/bin.dtl deleted file mode 100755 index dd11707..0000000 --- a/priv/templates/bin.dtl +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh - -set -e - -SCRIPT_DIR="$(dirname "$0")" -RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -REL_NAME="{{ rel_name }}" -REL_VSN="{{ rel_vsn }}" -ERTS_VSN="{{ erts_vsn }}" -REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN" -ERL_OPTS="{{ erl_opts }}" - -find_erts_dir() { - local erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN" - if [ -d "$erts_dir" ]; then - ERTS_DIR="$erts_dir"; - ROOTDIR="$RELEASE_ROOT_DIR" - else - local erl="$(which erl)" - code="io:format(\"~s\", [code:root_dir()]), halt()." - local erl_root="$("$erl" -noshell -eval "$code")" - ERTS_DIR="$erl_root/erts-$ERTS_VSN" - ROOTDIR="$erl_root" - fi -} - -find_sys_config() { - local possible_sys="$REL_DIR/sys.config" - if [ -f "$possible_sys" ]; then - SYS_CONFIG="$possible_sys" - fi -} - -find_vm_args() { - local possible_vm_args="$REL_DIR/vm.args" - if [ -f "$possible_vm_args" ]; then - VM_ARGS="$possible_vm_args" - fi -} - -find_erts_dir -find_sys_config -find_vm_args -export ROOTDIR="$RELEASE_ROOT_DIR" -export BINDIR="$ERTS_DIR/bin" -export EMU="beam" -export PROGNAME="erl" -export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH" -ERTS_LIB_DIR="$ERTS_DIR/../lib" -[ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start -cd "$ROOTDIR" - -# Save extra arguments -ARGS="$@" - -# Build arguments for erlexec -set -- "$ERL_OPTS" -[ "$SYS_CONFIG" ] && set -- "$@" -config "$SYS_CONFIG" -[ "$VM_ARGS" ] && set -- "$@" -args_file "$VM_ARGS" -set -- "$@" -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" -boot "$REL_DIR/$BOOTFILE" "$ARGS" - -# Boot the release -$BINDIR/erlexec $@ diff --git a/priv/templates/bin_windows b/priv/templates/bin_windows new file mode 100644 index 0000000..3466292 --- /dev/null +++ b/priv/templates/bin_windows @@ -0,0 +1,85 @@ +:: This is a simple start batch file that runs the release in an Erlang shell + +:: Set variables that describe the release +@set rel_name={{ rel_name }} +@set rel_vsn={{ rel_vsn }} +@set erts_vsn={{ erts_vsn }} +@set erl_opts={{ erl_opts }} + +:: Set the root release directory based on the location of this batch file +@set script_dir=%~dp0 +@for %%A in ("%script_dir%\..") do ( + set "release_root_dir=%%~fA" +) +@set rel_dir=%release_root_dir%\releases\%rel_vsn% + +@call :find_erts_dir +@call :find_sys_config +@call :set_boot_script_var + +@set rootdir=%release_root_dir% +@set bindir=%erts_dir%\bin +@set progname=erl +@set erl=%bindir%\erl + +cd %rootdir% + +:: Write the erl.ini file +@set erl_ini=%erts_dir%\bin\erl.ini +@set converted_bindir=%bindir:\=\\% +@set converted_rootdir=%rootdir:\=\\% +@echo [erlang] > "%erl_ini%" +@echo Bindir=%converted_bindir% >> "%erl_ini%" +@echo Progname=%progname% >> "%erl_ini%" +@echo Rootdir=%converted_rootdir% >> "%erl_ini%" + +:: Start the release in an `erl` shell +@"%erl%" %erl_opts% %sys_config% -boot "%boot_script%" %* + +@goto :eof + +:: Find the ERTS dir +:find_erts_dir +@set erts_dir=%release_root_dir%\erts-%erts_vsn% +@if exist %erts_dir% ( + goto :set_erts_dir_from_default +) else ( + goto :set_erts_dir_from_erl +) +@goto :eof + +:: Set the ERTS dir from the passed in erts_vsn +:set_erts_dir_from_default +@set erts_dir=%erts_dir% +@set root_dir=%release_root_dir% +@goto :eof + +:: Set the ERTS dir from erl +:set_erts_dir_from_erl +@for /f "delims=" %%i in ('where erl') do ( + set erl=%%i +) +@set dir_cmd="%erl%" -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop +@for /f %%i in ('%%dir_cmd%%') do ( + set erl_root=%%i +) +@set erts_dir=%erl_root%\erts-%erts_vsn% +@set rootdir=%erl_root% +@goto :eof + +:: Find the sys.config file +:find_sys_config +@set possible_sys=%rel_dir%\sys.config +@if exist "%possible_sys%" ( + set sys_config=-config "%possible_sys%" +) +@goto :eof + +:: set boot_script variable +:set_boot_script_var +@if exist "%rel_dir%\%rel_name%.boot" ( + set boot_script=%rel_dir%\%rel_name% +) else ( + set boot_script=%rel_dir%\start +) +@goto :eof diff --git a/priv/templates/bin_windows.dtl b/priv/templates/bin_windows.dtl deleted file mode 100644 index 3466292..0000000 --- a/priv/templates/bin_windows.dtl +++ /dev/null @@ -1,85 +0,0 @@ -:: This is a simple start batch file that runs the release in an Erlang shell - -:: Set variables that describe the release -@set rel_name={{ rel_name }} -@set rel_vsn={{ rel_vsn }} -@set erts_vsn={{ erts_vsn }} -@set erl_opts={{ erl_opts }} - -:: Set the root release directory based on the location of this batch file -@set script_dir=%~dp0 -@for %%A in ("%script_dir%\..") do ( - set "release_root_dir=%%~fA" -) -@set rel_dir=%release_root_dir%\releases\%rel_vsn% - -@call :find_erts_dir -@call :find_sys_config -@call :set_boot_script_var - -@set rootdir=%release_root_dir% -@set bindir=%erts_dir%\bin -@set progname=erl -@set erl=%bindir%\erl - -cd %rootdir% - -:: Write the erl.ini file -@set erl_ini=%erts_dir%\bin\erl.ini -@set converted_bindir=%bindir:\=\\% -@set converted_rootdir=%rootdir:\=\\% -@echo [erlang] > "%erl_ini%" -@echo Bindir=%converted_bindir% >> "%erl_ini%" -@echo Progname=%progname% >> "%erl_ini%" -@echo Rootdir=%converted_rootdir% >> "%erl_ini%" - -:: Start the release in an `erl` shell -@"%erl%" %erl_opts% %sys_config% -boot "%boot_script%" %* - -@goto :eof - -:: Find the ERTS dir -:find_erts_dir -@set erts_dir=%release_root_dir%\erts-%erts_vsn% -@if exist %erts_dir% ( - goto :set_erts_dir_from_default -) else ( - goto :set_erts_dir_from_erl -) -@goto :eof - -:: Set the ERTS dir from the passed in erts_vsn -:set_erts_dir_from_default -@set erts_dir=%erts_dir% -@set root_dir=%release_root_dir% -@goto :eof - -:: Set the ERTS dir from erl -:set_erts_dir_from_erl -@for /f "delims=" %%i in ('where erl') do ( - set erl=%%i -) -@set dir_cmd="%erl%" -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop -@for /f %%i in ('%%dir_cmd%%') do ( - set erl_root=%%i -) -@set erts_dir=%erl_root%\erts-%erts_vsn% -@set rootdir=%erl_root% -@goto :eof - -:: Find the sys.config file -:find_sys_config -@set possible_sys=%rel_dir%\sys.config -@if exist "%possible_sys%" ( - set sys_config=-config "%possible_sys%" -) -@goto :eof - -:: set boot_script variable -:set_boot_script_var -@if exist "%rel_dir%\%rel_name%.boot" ( - set boot_script=%rel_dir%\%rel_name% -) else ( - set boot_script=%rel_dir%\start -) -@goto :eof diff --git a/priv/templates/erl_ini b/priv/templates/erl_ini new file mode 100644 index 0000000..b2ce5bc --- /dev/null +++ b/priv/templates/erl_ini @@ -0,0 +1,4 @@ +[erlang] +Bindir={{ bin_dir }} +Progname=erl +Rootdir={{ output_dir }} diff --git a/priv/templates/erl_ini.dtl b/priv/templates/erl_ini.dtl deleted file mode 100644 index b2ce5bc..0000000 --- a/priv/templates/erl_ini.dtl +++ /dev/null @@ -1,4 +0,0 @@ -[erlang] -Bindir={{ bin_dir }} -Progname=erl -Rootdir={{ output_dir }} diff --git a/priv/templates/erl_script b/priv/templates/erl_script new file mode 100644 index 0000000..72e93dd --- /dev/null +++ b/priv/templates/erl_script @@ -0,0 +1,13 @@ +#!/bin/sh +set -e + +SCRIPT_DIR=`dirname $0` +ROOTDIR=`cd $SCRIPT_DIR/../../ && pwd` +BINDIR=$ROOTDIR/erts-{{ erts_vsn }}/bin +EMU=beam +PROGNAME=`echo $0 | sed 's/.*\\///'` +export EMU +export ROOTDIR +export BINDIR +export PROGNAME +exec "$BINDIR/erlexec" ${1+"$@"} diff --git a/priv/templates/erl_script.dtl b/priv/templates/erl_script.dtl deleted file mode 100644 index 72e93dd..0000000 --- a/priv/templates/erl_script.dtl +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -e - -SCRIPT_DIR=`dirname $0` -ROOTDIR=`cd $SCRIPT_DIR/../../ && pwd` -BINDIR=$ROOTDIR/erts-{{ erts_vsn }}/bin -EMU=beam -PROGNAME=`echo $0 | sed 's/.*\\///'` -export EMU -export ROOTDIR -export BINDIR -export PROGNAME -exec "$BINDIR/erlexec" ${1+"$@"} diff --git a/priv/templates/extended_bin b/priv/templates/extended_bin new file mode 100755 index 0000000..452669c --- /dev/null +++ b/priv/templates/extended_bin @@ -0,0 +1,428 @@ +#!/bin/sh + +set -e + +SCRIPT_DIR="$(dirname "$0")" +RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +REL_NAME="{{ rel_name }}" +REL_VSN="{{ rel_vsn }}" +ERTS_VSN="{{ erts_vsn }}" +CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}" +REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN" +ERL_OPTS="{{ erl_opts }}" +RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}" + +find_erts_dir() { + local erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN" + if [ -d "$erts_dir" ]; then + ERTS_DIR="$erts_dir"; + ROOTDIR="$RELEASE_ROOT_DIR" + else + local erl="$(which erl)" + code="io:format(\"~s\", [code:root_dir()]), halt()." + local erl_root="$("$erl" -noshell -eval "$code")" + ERTS_DIR="$erl_root/erts-$ERTS_VSN" + ROOTDIR="$erl_root" + fi +} + +# Get node pid +relx_get_pid() { + if output="$(relx_nodetool rpcterms os getpid)" + then + echo "$output" | sed -e 's/"//g' + return 0 + else + echo "$output" + return 1 + fi +} + +relx_get_longname() { + id="longname$(relx_gen_id)-${NAME}" + "$BINDIR/erl" -boot start_clean -eval 'io:format("~s~n", [node()]), halt()' -noshell -name $id | sed -e 's/.*@//g' +} + +# Connect to a remote node +relx_rem_sh() { + # Generate a unique id used to allow multiple remsh to the same node + # transparently + id="remsh$(relx_gen_id)-${NAME}" + + # Get the node's ticktime so that we use the same thing. + TICKTIME="$(relx_nodetool rpcterms net_kernel get_net_ticktime)" + + # Setup remote shell command to control node + exec "$BINDIR/erl" "$NAME_TYPE" "$id" -remsh "$NAME" -boot start_clean \ + -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ + -setcookie "$COOKIE" -kernel net_ticktime $TICKTIME +} + +# Generate a random id +relx_gen_id() { + od -X /dev/urandom | head -n1 | awk '{print $2}' +} + +# Control a node +relx_nodetool() { + command="$1"; shift + + "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \ + -setcookie "$COOKIE" "$command" $@ +} + +# Run an escript in the node's environment +relx_escript() { + shift; scriptpath="$1"; shift + export RELEASE_ROOT_DIR + + "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" $@ +} + +# Output a start command for the last argument of run_erl +relx_start_command() { + printf "exec \"%s\" \"%s\"" "$RELEASE_ROOT_DIR/bin/$REL_NAME" \ + "$START_OPTION" +} + +# Use $CWD/vm.args if exists, otherwise releases/VSN/vm.args +if [ -z "$VMARGS_PATH" ]; then + if [ -f "$RELEASE_ROOT_DIR/vm.args" ]; then + VMARGS_PATH="$RELEASE_ROOT_DIR/vm.args" + USE_DIR="$RELEASE_ROOT_DIR" + else + USE_DIR="$REL_DIR" + VMARGS_PATH="$REL_DIR/vm.args" + fi +fi + +if [ $RELX_REPLACE_OS_VARS ]; then + awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < $VMARGS_PATH > $VMARGS_PATH.2.config + VMARGS_PATH=$VMARGS_PATH.2.config +fi + +# Make sure log directory exists +mkdir -p "$RUNNER_LOG_DIR" + +if [ -z "$RELX_CONFIG_PATH" ]; then + if [ -f "$USE_DIR/sys.config" ]; then + RELX_CONFIG_PATH="$USE_DIR/sys.config" + else + RELX_CONFIG_PATH="$REL_DIR/sys.config" + fi +fi + +if [ $RELX_REPLACE_OS_VARS ]; then + awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < $RELX_CONFIG_PATH > $RELX_CONFIG_PATH.2.config + RELX_CONFIG_PATH=$RELX_CONFIG_PATH.2.config +fi + +# Extract the target node name from node.args +NAME_ARG=$(egrep '^-s?name' "$VMARGS_PATH") +if [ -z "$NAME_ARG" ]; then + echo "vm.args needs to have either -name or -sname parameter." + exit 1 +fi + +# Extract the name type and name from the NAME_ARG for REMSH +NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')" +NAME="$(echo "$NAME_ARG" | awk '{print $2}')" + +PIPE_DIR="${PIPE_DIR:-/tmp/erl_pipes/$NAME/}" + +# Extract the target cookie +COOKIE_ARG="$(grep '^-setcookie' "$VMARGS_PATH")" +if [ -z "$COOKIE_ARG" ]; then + echo "vm.args needs to have a -setcookie parameter." + exit 1 +fi + +# Extract cookie name from COOKIE_ARG +COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')" + +find_erts_dir +export ROOTDIR="$RELEASE_ROOT_DIR" +export BINDIR="$ERTS_DIR/bin" +export EMU="beam" +export PROGNAME="erl" +export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH" +ERTS_LIB_DIR="$ERTS_DIR/../lib" + +cd "$ROOTDIR" + +# User can specify an sname without @hostname +# This will fail when creating remote shell +# So here we check for @ and add @hostname if missing +case $NAME in + *@*) + # Nothing to do + ;; + *) + # Add @hostname + case $NAME_TYPE in + -sname) + NAME=$NAME@`hostname -s` + ;; + -name) + NAME=$NAME@$(relx_get_longname) + ;; + esac + ;; +esac + +# Check the first argument for instructions +case "$1" in + start|start_boot) + + # Make sure there is not already a node running + #RES=`$NODETOOL ping` + #if [ "$RES" = "pong" ]; then + # echo "Node is already running!" + # exit 1 + #fi + # Save this for later. + CMD=$1 + case "$1" in + start) + shift + START_OPTION="console" + HEART_OPTION="start" + ;; + start_boot) + shift + START_OPTION="console_boot" + HEART_OPTION="start_boot" + ;; + esac + RUN_PARAM="$@" + + # Set arguments for the heart command + set -- "$SCRIPT_DIR/$REL_NAME" "$HEART_OPTION" + [ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM" + + # Export the HEART_COMMAND + HEART_COMMAND="$RELEASE_ROOT_DIR/bin/$REL_NAME $CMD" + export HEART_COMMAND + + mkdir -p "$PIPE_DIR" + + "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \ + "$(relx_start_command)" + ;; + + stop) + # Wait for the node to completely stop... + PID="$(relx_get_pid)" + if ! relx_nodetool "stop"; then + exit 1 + fi + while $(kill -0 "$PID" 2>/dev/null); + do + sleep 1 + done + ;; + + restart) + ## Restart the VM without exiting the process + if ! relx_nodetool "restart"; then + exit 1 + fi + ;; + + reboot) + ## Restart the VM completely (uses heart to restart it) + if ! relx_nodetool "reboot"; then + exit 1 + fi + ;; + + pid) + ## Get the VM's pid + if ! relx_get_pid; then + exit 1 + fi + ;; + + ping) + ## See if the VM is alive + if ! relx_nodetool "ping"; then + exit 1 + fi + ;; + + escript) + ## Run an escript under the node's environment + if ! relx_escript $@; then + exit 1 + fi + ;; + + attach) + # Make sure a node IS running + if ! relx_nodetool "ping" > /dev/null; then + echo "Node is not running!" + exit 1 + fi + + shift + exec "$BINDIR/to_erl" "$PIPE_DIR" + ;; + + remote_console) + # Make sure a node IS running + if ! relx_nodetool "ping" > /dev/null; then + echo "Node is not running!" + exit 1 + fi + + shift + relx_rem_sh + ;; + + upgrade|downgrade|install) + if [ -z "$2" ]; then + echo "Missing package argument" + echo "Usage: $REL_NAME $1 {package base name}" + echo "NOTE {package base name} MUST NOT include the .tar.gz suffix" + exit 1 + fi + + # Make sure a node IS running + if ! relx_nodetool "ping" > /dev/null; then + echo "Node is not running!" + exit 1 + fi + + exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \ + "install" "$REL_NAME" "$NAME" "$COOKIE" "$2" + ;; + + unpack) + if [ -z "$2" ]; then + echo "Missing package argument" + echo "Usage: $REL_NAME $1 {package base name}" + echo "NOTE {package base name} MUST NOT include the .tar.gz suffix" + exit 1 + fi + + # Make sure a node IS running + if ! relx_nodetool "ping" > /dev/null; then + echo "Node is not running!" + exit 1 + fi + + exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \ + "unpack" "$REL_NAME" "$NAME" "$COOKIE" "$2" + ;; + + console|console_clean|console_boot) + # .boot file typically just $REL_NAME (ie, the app name) + # however, for debugging, sometimes start_clean.boot is useful. + # For e.g. 'setup', one may even want to name another boot script. + case "$1" in + console) + if [ -f "$REL_DIR/$REL_NAME.boot" ]; then + BOOTFILE="$REL_DIR/$REL_NAME" + else + BOOTFILE="$REL_DIR/start" + fi + ;; + console_clean) + BOOTFILE="$ROOTDIR/bin/start_clean" + ;; + console_boot) + shift + BOOTFILE="$1" + shift + ;; + esac + # Setup beam-required vars + EMU="beam" + PROGNAME="${0#*/}" + + export EMU + export PROGNAME + + # Store passed arguments since they will be erased by `set` + ARGS="$@" + + # Build an array of arguments to pass to exec later on + # Build it here because this command will be used for logging. + set -- "$BINDIR/erlexec" -boot "$BOOTFILE" \ + -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ + -env ERL_LIBS "$REL_DIR/lib" -config "$RELX_CONFIG_PATH" \ + -args_file "$VMARGS_PATH" + + # Dump environment info for logging purposes + echo "Exec: $@" -- ${1+$ARGS} + echo "Root: $ROOTDIR" + + # Log the startup + echo "$RELEASE_ROOT_DIR" + logger -t "$REL_NAME[$$]" "Starting up" + + # Start the VM + exec "$@" -- ${1+$ARGS} + ;; + + foreground) + # start up the release in the foreground for use by runit + # or other supervision services + + [ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start + FOREGROUNDOPTIONS="-noshell -noinput +Bd" + + # Setup beam-required vars + EMU=beam + PROGNAME="${0#*/}" + + export EMU + export PROGNAME + + # Store passed arguments since they will be erased by `set` + ARGS="$@" + + # Build an array of arguments to pass to exec later on + # Build it here because this command will be used for logging. + set -- "$BINDIR/erlexec" $FOREGROUNDOPTIONS \ + -boot "$REL_DIR/$BOOTFILE" -mode "$CODE_LOADING_MODE" -config "$RELX_CONFIG_PATH" \ + -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ + -args_file "$VMARGS_PATH" + + # Dump environment info for logging purposes + echo "Exec: $@" -- ${1+$ARGS} + echo "Root: $ROOTDIR" + + # Start the VM + exec "$@" -- ${1+$ARGS} + ;; + rpc) + # Make sure a node IS running + if ! relx_nodetool "ping" > /dev/null; then + echo "Node is not running!" + exit 1 + fi + + shift + + relx_nodetool rpc $@ + ;; + rpcterms) + # Make sure a node IS running + if ! relx_nodetool "ping" > /dev/null; then + echo "Node is not running!" + exit 1 + fi + + shift + + relx_nodetool rpcterms $@ + ;; + + *) + echo "Usage: $REL_NAME {start|start_boot |foreground|stop|restart|reboot|pid|ping|console|console_clean|console_boot |attach|remote_console|upgrade|escript|rpc|rpcterms}" + exit 1 + ;; +esac + +exit 0 diff --git a/priv/templates/extended_bin.dtl b/priv/templates/extended_bin.dtl deleted file mode 100755 index 452669c..0000000 --- a/priv/templates/extended_bin.dtl +++ /dev/null @@ -1,428 +0,0 @@ -#!/bin/sh - -set -e - -SCRIPT_DIR="$(dirname "$0")" -RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -REL_NAME="{{ rel_name }}" -REL_VSN="{{ rel_vsn }}" -ERTS_VSN="{{ erts_vsn }}" -CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}" -REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN" -ERL_OPTS="{{ erl_opts }}" -RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}" - -find_erts_dir() { - local erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN" - if [ -d "$erts_dir" ]; then - ERTS_DIR="$erts_dir"; - ROOTDIR="$RELEASE_ROOT_DIR" - else - local erl="$(which erl)" - code="io:format(\"~s\", [code:root_dir()]), halt()." - local erl_root="$("$erl" -noshell -eval "$code")" - ERTS_DIR="$erl_root/erts-$ERTS_VSN" - ROOTDIR="$erl_root" - fi -} - -# Get node pid -relx_get_pid() { - if output="$(relx_nodetool rpcterms os getpid)" - then - echo "$output" | sed -e 's/"//g' - return 0 - else - echo "$output" - return 1 - fi -} - -relx_get_longname() { - id="longname$(relx_gen_id)-${NAME}" - "$BINDIR/erl" -boot start_clean -eval 'io:format("~s~n", [node()]), halt()' -noshell -name $id | sed -e 's/.*@//g' -} - -# Connect to a remote node -relx_rem_sh() { - # Generate a unique id used to allow multiple remsh to the same node - # transparently - id="remsh$(relx_gen_id)-${NAME}" - - # Get the node's ticktime so that we use the same thing. - TICKTIME="$(relx_nodetool rpcterms net_kernel get_net_ticktime)" - - # Setup remote shell command to control node - exec "$BINDIR/erl" "$NAME_TYPE" "$id" -remsh "$NAME" -boot start_clean \ - -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ - -setcookie "$COOKIE" -kernel net_ticktime $TICKTIME -} - -# Generate a random id -relx_gen_id() { - od -X /dev/urandom | head -n1 | awk '{print $2}' -} - -# Control a node -relx_nodetool() { - command="$1"; shift - - "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \ - -setcookie "$COOKIE" "$command" $@ -} - -# Run an escript in the node's environment -relx_escript() { - shift; scriptpath="$1"; shift - export RELEASE_ROOT_DIR - - "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" $@ -} - -# Output a start command for the last argument of run_erl -relx_start_command() { - printf "exec \"%s\" \"%s\"" "$RELEASE_ROOT_DIR/bin/$REL_NAME" \ - "$START_OPTION" -} - -# Use $CWD/vm.args if exists, otherwise releases/VSN/vm.args -if [ -z "$VMARGS_PATH" ]; then - if [ -f "$RELEASE_ROOT_DIR/vm.args" ]; then - VMARGS_PATH="$RELEASE_ROOT_DIR/vm.args" - USE_DIR="$RELEASE_ROOT_DIR" - else - USE_DIR="$REL_DIR" - VMARGS_PATH="$REL_DIR/vm.args" - fi -fi - -if [ $RELX_REPLACE_OS_VARS ]; then - awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < $VMARGS_PATH > $VMARGS_PATH.2.config - VMARGS_PATH=$VMARGS_PATH.2.config -fi - -# Make sure log directory exists -mkdir -p "$RUNNER_LOG_DIR" - -if [ -z "$RELX_CONFIG_PATH" ]; then - if [ -f "$USE_DIR/sys.config" ]; then - RELX_CONFIG_PATH="$USE_DIR/sys.config" - else - RELX_CONFIG_PATH="$REL_DIR/sys.config" - fi -fi - -if [ $RELX_REPLACE_OS_VARS ]; then - awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < $RELX_CONFIG_PATH > $RELX_CONFIG_PATH.2.config - RELX_CONFIG_PATH=$RELX_CONFIG_PATH.2.config -fi - -# Extract the target node name from node.args -NAME_ARG=$(egrep '^-s?name' "$VMARGS_PATH") -if [ -z "$NAME_ARG" ]; then - echo "vm.args needs to have either -name or -sname parameter." - exit 1 -fi - -# Extract the name type and name from the NAME_ARG for REMSH -NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')" -NAME="$(echo "$NAME_ARG" | awk '{print $2}')" - -PIPE_DIR="${PIPE_DIR:-/tmp/erl_pipes/$NAME/}" - -# Extract the target cookie -COOKIE_ARG="$(grep '^-setcookie' "$VMARGS_PATH")" -if [ -z "$COOKIE_ARG" ]; then - echo "vm.args needs to have a -setcookie parameter." - exit 1 -fi - -# Extract cookie name from COOKIE_ARG -COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')" - -find_erts_dir -export ROOTDIR="$RELEASE_ROOT_DIR" -export BINDIR="$ERTS_DIR/bin" -export EMU="beam" -export PROGNAME="erl" -export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH" -ERTS_LIB_DIR="$ERTS_DIR/../lib" - -cd "$ROOTDIR" - -# User can specify an sname without @hostname -# This will fail when creating remote shell -# So here we check for @ and add @hostname if missing -case $NAME in - *@*) - # Nothing to do - ;; - *) - # Add @hostname - case $NAME_TYPE in - -sname) - NAME=$NAME@`hostname -s` - ;; - -name) - NAME=$NAME@$(relx_get_longname) - ;; - esac - ;; -esac - -# Check the first argument for instructions -case "$1" in - start|start_boot) - - # Make sure there is not already a node running - #RES=`$NODETOOL ping` - #if [ "$RES" = "pong" ]; then - # echo "Node is already running!" - # exit 1 - #fi - # Save this for later. - CMD=$1 - case "$1" in - start) - shift - START_OPTION="console" - HEART_OPTION="start" - ;; - start_boot) - shift - START_OPTION="console_boot" - HEART_OPTION="start_boot" - ;; - esac - RUN_PARAM="$@" - - # Set arguments for the heart command - set -- "$SCRIPT_DIR/$REL_NAME" "$HEART_OPTION" - [ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM" - - # Export the HEART_COMMAND - HEART_COMMAND="$RELEASE_ROOT_DIR/bin/$REL_NAME $CMD" - export HEART_COMMAND - - mkdir -p "$PIPE_DIR" - - "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \ - "$(relx_start_command)" - ;; - - stop) - # Wait for the node to completely stop... - PID="$(relx_get_pid)" - if ! relx_nodetool "stop"; then - exit 1 - fi - while $(kill -0 "$PID" 2>/dev/null); - do - sleep 1 - done - ;; - - restart) - ## Restart the VM without exiting the process - if ! relx_nodetool "restart"; then - exit 1 - fi - ;; - - reboot) - ## Restart the VM completely (uses heart to restart it) - if ! relx_nodetool "reboot"; then - exit 1 - fi - ;; - - pid) - ## Get the VM's pid - if ! relx_get_pid; then - exit 1 - fi - ;; - - ping) - ## See if the VM is alive - if ! relx_nodetool "ping"; then - exit 1 - fi - ;; - - escript) - ## Run an escript under the node's environment - if ! relx_escript $@; then - exit 1 - fi - ;; - - attach) - # Make sure a node IS running - if ! relx_nodetool "ping" > /dev/null; then - echo "Node is not running!" - exit 1 - fi - - shift - exec "$BINDIR/to_erl" "$PIPE_DIR" - ;; - - remote_console) - # Make sure a node IS running - if ! relx_nodetool "ping" > /dev/null; then - echo "Node is not running!" - exit 1 - fi - - shift - relx_rem_sh - ;; - - upgrade|downgrade|install) - if [ -z "$2" ]; then - echo "Missing package argument" - echo "Usage: $REL_NAME $1 {package base name}" - echo "NOTE {package base name} MUST NOT include the .tar.gz suffix" - exit 1 - fi - - # Make sure a node IS running - if ! relx_nodetool "ping" > /dev/null; then - echo "Node is not running!" - exit 1 - fi - - exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \ - "install" "$REL_NAME" "$NAME" "$COOKIE" "$2" - ;; - - unpack) - if [ -z "$2" ]; then - echo "Missing package argument" - echo "Usage: $REL_NAME $1 {package base name}" - echo "NOTE {package base name} MUST NOT include the .tar.gz suffix" - exit 1 - fi - - # Make sure a node IS running - if ! relx_nodetool "ping" > /dev/null; then - echo "Node is not running!" - exit 1 - fi - - exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \ - "unpack" "$REL_NAME" "$NAME" "$COOKIE" "$2" - ;; - - console|console_clean|console_boot) - # .boot file typically just $REL_NAME (ie, the app name) - # however, for debugging, sometimes start_clean.boot is useful. - # For e.g. 'setup', one may even want to name another boot script. - case "$1" in - console) - if [ -f "$REL_DIR/$REL_NAME.boot" ]; then - BOOTFILE="$REL_DIR/$REL_NAME" - else - BOOTFILE="$REL_DIR/start" - fi - ;; - console_clean) - BOOTFILE="$ROOTDIR/bin/start_clean" - ;; - console_boot) - shift - BOOTFILE="$1" - shift - ;; - esac - # Setup beam-required vars - EMU="beam" - PROGNAME="${0#*/}" - - export EMU - export PROGNAME - - # Store passed arguments since they will be erased by `set` - ARGS="$@" - - # Build an array of arguments to pass to exec later on - # Build it here because this command will be used for logging. - set -- "$BINDIR/erlexec" -boot "$BOOTFILE" \ - -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ - -env ERL_LIBS "$REL_DIR/lib" -config "$RELX_CONFIG_PATH" \ - -args_file "$VMARGS_PATH" - - # Dump environment info for logging purposes - echo "Exec: $@" -- ${1+$ARGS} - echo "Root: $ROOTDIR" - - # Log the startup - echo "$RELEASE_ROOT_DIR" - logger -t "$REL_NAME[$$]" "Starting up" - - # Start the VM - exec "$@" -- ${1+$ARGS} - ;; - - foreground) - # start up the release in the foreground for use by runit - # or other supervision services - - [ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start - FOREGROUNDOPTIONS="-noshell -noinput +Bd" - - # Setup beam-required vars - EMU=beam - PROGNAME="${0#*/}" - - export EMU - export PROGNAME - - # Store passed arguments since they will be erased by `set` - ARGS="$@" - - # Build an array of arguments to pass to exec later on - # Build it here because this command will be used for logging. - set -- "$BINDIR/erlexec" $FOREGROUNDOPTIONS \ - -boot "$REL_DIR/$BOOTFILE" -mode "$CODE_LOADING_MODE" -config "$RELX_CONFIG_PATH" \ - -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ - -args_file "$VMARGS_PATH" - - # Dump environment info for logging purposes - echo "Exec: $@" -- ${1+$ARGS} - echo "Root: $ROOTDIR" - - # Start the VM - exec "$@" -- ${1+$ARGS} - ;; - rpc) - # Make sure a node IS running - if ! relx_nodetool "ping" > /dev/null; then - echo "Node is not running!" - exit 1 - fi - - shift - - relx_nodetool rpc $@ - ;; - rpcterms) - # Make sure a node IS running - if ! relx_nodetool "ping" > /dev/null; then - echo "Node is not running!" - exit 1 - fi - - shift - - relx_nodetool rpcterms $@ - ;; - - *) - echo "Usage: $REL_NAME {start|start_boot |foreground|stop|restart|reboot|pid|ping|console|console_clean|console_boot |attach|remote_console|upgrade|escript|rpc|rpcterms}" - exit 1 - ;; -esac - -exit 0 diff --git a/priv/templates/extended_bin_windows b/priv/templates/extended_bin_windows new file mode 100644 index 0000000..cb9f747 --- /dev/null +++ b/priv/templates/extended_bin_windows @@ -0,0 +1,207 @@ +:: This batch file handles managing an Erlang node as a Windows service. +:: +:: Commands provided: +:: +:: * install - install the release as a Windows service +:: * start - start the service and Erlang node +:: * stop - stop the service and Erlang node +:: * restart - run the stop command and start command +:: * uninstall - uninstall the service and kill a running node +:: * ping - check if the node is running +:: * console - start the Erlang release in a `werl` Windows shell +:: * attach - connect to a running node and open an interactive console +:: * list - display a listing of installed Erlang services +:: * usage - display available commands + +:: Set variables that describe the release +@set rel_name={{ rel_name }} +@set rel_vsn={{ rel_vsn }} +@set erts_vsn={{ erts_vsn }} +@set erl_opts={{ erl_opts }} + +:: Discover the release root directory from the directory +:: of this script +@set script_dir=%~dp0 +@for %%A in ("%script_dir%\..") do @( + set release_root_dir=%%~fA +) +@set rel_dir=%release_root_dir%\releases\%rel_vsn% + +@call :find_erts_dir +@call :find_sys_config +@call :set_boot_script_var + +@set service_name=%rel_name%_%rel_vsn% +@set bindir=%erts_dir%\bin +@set vm_args=%rel_dir%\vm.args +@set progname=erl.exe +@set clean_boot_script=%release_root_dir%\bin\start_clean +@set erlsrv="%bindir%\erlsrv.exe" +@set epmd="%bindir%\epmd.exe" +@set escript="%bindir%\escript.exe" +@set werl="%bindir%\werl.exe" +@set nodetool="%release_root_dir%\bin\nodetool" + +:: Extract node type and name from vm.args +@for /f "usebackq tokens=1-2" %%I in (`findstr /b "\-name \-sname" "%vm_args%"`) do @( + set node_type=%%I + set node_name=%%J +) + +:: Extract cookie from vm.args +@for /f "usebackq tokens=1-2" %%I in (`findstr /b \-setcookie "%vm_args%"`) do @( + set cookie=%%J +) + +:: Write the erl.ini file to set up paths relative to this script +@call :write_ini + +:: If a start.boot file is not present, copy one from the named .boot file +@if not exist "%rel_dir%\start.boot" ( + copy "%rel_dir%\%rel_name%.boot" "%rel_dir%\start.boot" >nul +) + +@if "%1"=="install" @goto install +@if "%1"=="uninstall" @goto uninstall +@if "%1"=="start" @goto start +@if "%1"=="stop" @goto stop +@if "%1"=="restart" @call :stop && @goto start +@if "%1"=="upgrade" @goto relup +@if "%1"=="downgrade" @goto relup +@if "%1"=="console" @goto console +@if "%1"=="ping" @goto ping +@if "%1"=="list" @goto list +@if "%1"=="attach" @goto attach +@if "%1"=="" @goto usage +@echo Unknown command: "%1" + +@goto :eof + +:: Find the ERTS dir +:find_erts_dir +@set possible_erts_dir=%release_root_dir%\erts-%erts_vsn% +@if exist "%possible_erts_dir%" ( + call :set_erts_dir_from_default +) else ( + call :set_erts_dir_from_erl +) +@goto :eof + +:: Set the ERTS dir from the passed in erts_vsn +:set_erts_dir_from_default +@set erts_dir=%possible_erts_dir% +@set rootdir=%release_root_dir% +@goto :eof + +:: Set the ERTS dir from erl +:set_erts_dir_from_erl +@for /f "delims=" %%i in ('where erl') do @( + set erl=%%i +) +@set dir_cmd="%erl%" -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop +@for /f %%i in ('%%dir_cmd%%') do @( + set erl_root=%%i +) +@set erts_dir=%erl_root%\erts-%erts_vsn% +@set rootdir=%erl_root% +@goto :eof + +:: Find the sys.config file +:find_sys_config +@set possible_sys=%rel_dir%\sys.config +@if exist %possible_sys% ( + set sys_config=-config %possible_sys% +) +@goto :eof + +:: set boot_script variable +:set_boot_script_var +@if exist "%rel_dir%\%rel_name%.boot" ( + set boot_script=%rel_dir%\%rel_name% +) else ( + set boot_script=%rel_dir%\start +) +@goto :eof + +:: Write the erl.ini file +:write_ini +@set erl_ini=%erts_dir%\bin\erl.ini +@set converted_bindir=%bindir:\=\\% +@set converted_rootdir=%rootdir:\=\\% +@echo [erlang] > "%erl_ini%" +@echo Bindir=%converted_bindir% >> "%erl_ini%" +@echo Progname=%progname% >> "%erl_ini%" +@echo Rootdir=%converted_rootdir% >> "%erl_ini%" +@goto :eof + +:: Display usage information +:usage +@echo usage: %~n0 ^(install^|uninstall^|start^|stop^|restart^|upgrade^|downgrade^|console^|ping^|list^|attach^) +@goto :eof + +:: Install the release as a Windows service +:: or install the specified version passed as argument +:install +@if "" == "%2" ( + :: Install the service + set args=%erl_opts% -setcookie %cookie% ++ -rootdir \"%rootdir%\" + set start_erl=%erts_dir%\bin\start_erl.exe + set description=Erlang node %node_name% in %rootdir% + %erlsrv% add %service_name% %node_type% "%node_name%" -c "%description%" ^ + -w "%rootdir%" -m "%start_erl%" -args "%args%" ^ + -stopaction "init:stop()." +) else ( + :: relup and reldown + goto relup +) +@goto :eof + +:: Uninstall the Windows service +:uninstall +@%erlsrv% remove %service_name% +@%epmd% -kill +@goto :eof + +:: Start the Windows service +:start +@%erlsrv% start %service_name% +@goto :eof + +:: Stop the Windows service +:stop +@%erlsrv% stop %service_name% +@goto :eof + +:: Relup and reldown +:relup +@if "" == "%2" ( + echo Missing package argument + echo Usage: %rel_name% %1 {package base name} + echo NOTE {package base name} MUST NOT include the .tar.gz suffix + set ERRORLEVEL=1 + exit /b %ERRORLEVEL% +) +@%escript% "%rootdir%/bin/install_upgrade.escript" "%rel_name%" "%node_name%" "%cookie%" "%2" +@goto :eof + +:: Start a console +:console +@start "%rel_name% console" %werl% -boot "%boot_script%" "%sys_config%" ^ + -args_file "%vm_args%" +@goto :eof + +:: Ping the running node +:ping +@%escript% %nodetool% ping %node_type% "%node_name%" -setcookie "%cookie%" +@goto :eof + +:: List installed Erlang services +:list +@%erlsrv% list %service_name% +@goto :eof + +:: Attach to a running node +:attach +@start "%node_name% attach" %werl% -boot "%clean_boot_script%" ^ + -remsh %node_name% %node_type% console -setcookie %cookie% +@goto :eof diff --git a/priv/templates/extended_bin_windows.dtl b/priv/templates/extended_bin_windows.dtl deleted file mode 100644 index cb9f747..0000000 --- a/priv/templates/extended_bin_windows.dtl +++ /dev/null @@ -1,207 +0,0 @@ -:: This batch file handles managing an Erlang node as a Windows service. -:: -:: Commands provided: -:: -:: * install - install the release as a Windows service -:: * start - start the service and Erlang node -:: * stop - stop the service and Erlang node -:: * restart - run the stop command and start command -:: * uninstall - uninstall the service and kill a running node -:: * ping - check if the node is running -:: * console - start the Erlang release in a `werl` Windows shell -:: * attach - connect to a running node and open an interactive console -:: * list - display a listing of installed Erlang services -:: * usage - display available commands - -:: Set variables that describe the release -@set rel_name={{ rel_name }} -@set rel_vsn={{ rel_vsn }} -@set erts_vsn={{ erts_vsn }} -@set erl_opts={{ erl_opts }} - -:: Discover the release root directory from the directory -:: of this script -@set script_dir=%~dp0 -@for %%A in ("%script_dir%\..") do @( - set release_root_dir=%%~fA -) -@set rel_dir=%release_root_dir%\releases\%rel_vsn% - -@call :find_erts_dir -@call :find_sys_config -@call :set_boot_script_var - -@set service_name=%rel_name%_%rel_vsn% -@set bindir=%erts_dir%\bin -@set vm_args=%rel_dir%\vm.args -@set progname=erl.exe -@set clean_boot_script=%release_root_dir%\bin\start_clean -@set erlsrv="%bindir%\erlsrv.exe" -@set epmd="%bindir%\epmd.exe" -@set escript="%bindir%\escript.exe" -@set werl="%bindir%\werl.exe" -@set nodetool="%release_root_dir%\bin\nodetool" - -:: Extract node type and name from vm.args -@for /f "usebackq tokens=1-2" %%I in (`findstr /b "\-name \-sname" "%vm_args%"`) do @( - set node_type=%%I - set node_name=%%J -) - -:: Extract cookie from vm.args -@for /f "usebackq tokens=1-2" %%I in (`findstr /b \-setcookie "%vm_args%"`) do @( - set cookie=%%J -) - -:: Write the erl.ini file to set up paths relative to this script -@call :write_ini - -:: If a start.boot file is not present, copy one from the named .boot file -@if not exist "%rel_dir%\start.boot" ( - copy "%rel_dir%\%rel_name%.boot" "%rel_dir%\start.boot" >nul -) - -@if "%1"=="install" @goto install -@if "%1"=="uninstall" @goto uninstall -@if "%1"=="start" @goto start -@if "%1"=="stop" @goto stop -@if "%1"=="restart" @call :stop && @goto start -@if "%1"=="upgrade" @goto relup -@if "%1"=="downgrade" @goto relup -@if "%1"=="console" @goto console -@if "%1"=="ping" @goto ping -@if "%1"=="list" @goto list -@if "%1"=="attach" @goto attach -@if "%1"=="" @goto usage -@echo Unknown command: "%1" - -@goto :eof - -:: Find the ERTS dir -:find_erts_dir -@set possible_erts_dir=%release_root_dir%\erts-%erts_vsn% -@if exist "%possible_erts_dir%" ( - call :set_erts_dir_from_default -) else ( - call :set_erts_dir_from_erl -) -@goto :eof - -:: Set the ERTS dir from the passed in erts_vsn -:set_erts_dir_from_default -@set erts_dir=%possible_erts_dir% -@set rootdir=%release_root_dir% -@goto :eof - -:: Set the ERTS dir from erl -:set_erts_dir_from_erl -@for /f "delims=" %%i in ('where erl') do @( - set erl=%%i -) -@set dir_cmd="%erl%" -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop -@for /f %%i in ('%%dir_cmd%%') do @( - set erl_root=%%i -) -@set erts_dir=%erl_root%\erts-%erts_vsn% -@set rootdir=%erl_root% -@goto :eof - -:: Find the sys.config file -:find_sys_config -@set possible_sys=%rel_dir%\sys.config -@if exist %possible_sys% ( - set sys_config=-config %possible_sys% -) -@goto :eof - -:: set boot_script variable -:set_boot_script_var -@if exist "%rel_dir%\%rel_name%.boot" ( - set boot_script=%rel_dir%\%rel_name% -) else ( - set boot_script=%rel_dir%\start -) -@goto :eof - -:: Write the erl.ini file -:write_ini -@set erl_ini=%erts_dir%\bin\erl.ini -@set converted_bindir=%bindir:\=\\% -@set converted_rootdir=%rootdir:\=\\% -@echo [erlang] > "%erl_ini%" -@echo Bindir=%converted_bindir% >> "%erl_ini%" -@echo Progname=%progname% >> "%erl_ini%" -@echo Rootdir=%converted_rootdir% >> "%erl_ini%" -@goto :eof - -:: Display usage information -:usage -@echo usage: %~n0 ^(install^|uninstall^|start^|stop^|restart^|upgrade^|downgrade^|console^|ping^|list^|attach^) -@goto :eof - -:: Install the release as a Windows service -:: or install the specified version passed as argument -:install -@if "" == "%2" ( - :: Install the service - set args=%erl_opts% -setcookie %cookie% ++ -rootdir \"%rootdir%\" - set start_erl=%erts_dir%\bin\start_erl.exe - set description=Erlang node %node_name% in %rootdir% - %erlsrv% add %service_name% %node_type% "%node_name%" -c "%description%" ^ - -w "%rootdir%" -m "%start_erl%" -args "%args%" ^ - -stopaction "init:stop()." -) else ( - :: relup and reldown - goto relup -) -@goto :eof - -:: Uninstall the Windows service -:uninstall -@%erlsrv% remove %service_name% -@%epmd% -kill -@goto :eof - -:: Start the Windows service -:start -@%erlsrv% start %service_name% -@goto :eof - -:: Stop the Windows service -:stop -@%erlsrv% stop %service_name% -@goto :eof - -:: Relup and reldown -:relup -@if "" == "%2" ( - echo Missing package argument - echo Usage: %rel_name% %1 {package base name} - echo NOTE {package base name} MUST NOT include the .tar.gz suffix - set ERRORLEVEL=1 - exit /b %ERRORLEVEL% -) -@%escript% "%rootdir%/bin/install_upgrade.escript" "%rel_name%" "%node_name%" "%cookie%" "%2" -@goto :eof - -:: Start a console -:console -@start "%rel_name% console" %werl% -boot "%boot_script%" "%sys_config%" ^ - -args_file "%vm_args%" -@goto :eof - -:: Ping the running node -:ping -@%escript% %nodetool% ping %node_type% "%node_name%" -setcookie "%cookie%" -@goto :eof - -:: List installed Erlang services -:list -@%erlsrv% list %service_name% -@goto :eof - -:: Attach to a running node -:attach -@start "%node_name% attach" %werl% -boot "%clean_boot_script%" ^ - -remsh %node_name% %node_type% console -setcookie %cookie% -@goto :eof diff --git a/priv/templates/install_upgrade_escript b/priv/templates/install_upgrade_escript new file mode 100644 index 0000000..3fb9d04 --- /dev/null +++ b/priv/templates/install_upgrade_escript @@ -0,0 +1,134 @@ +#!/usr/bin/env escript +%%! -noshell -noinput +%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ft=erlang ts=4 sw=4 et + +-define(TIMEOUT, 300000). +-define(INFO(Fmt,Args), io:format(Fmt,Args)). + +%% Unpack or upgrade to a new tar.gz release +main(["unpack", RelName, NodeName, Cookie, VersionArg]) -> + TargetNode = start_distribution(NodeName, Cookie), + WhichReleases = which_releases(TargetNode), + Version = parse_version(VersionArg), + case proplists:get_value(Version, WhichReleases) of + undefined -> + %% not installed, so unpack tarball: + ?INFO("Release ~s not found, attempting to unpack releases/~s/~s.tar.gz~n",[Version,Version,RelName]), + ReleasePackage = Version ++ "/" ++ RelName, + case rpc:call(TargetNode, release_handler, unpack_release, + [ReleasePackage], ?TIMEOUT) of + {ok, Vsn} -> + ?INFO("Unpacked successfully: ~p~n", [Vsn]); + {error, UnpackReason} -> + print_existing_versions(TargetNode), + ?INFO("Unpack failed: ~p~n",[UnpackReason]), + erlang:halt(2) + end; + old -> + %% no need to unpack, has been installed previously + ?INFO("Release ~s is marked old, switching to it.~n",[Version]); + unpacked -> + ?INFO("Release ~s is already unpacked, now installing.~n",[Version]); + current -> + ?INFO("Release ~s is already installed and current. Making permanent.~n",[Version]); + permanent -> + ?INFO("Release ~s is already installed, and set permanent.~n",[Version]) + end; +main(["install", RelName, NodeName, Cookie, VersionArg]) -> + TargetNode = start_distribution(NodeName, Cookie), + WhichReleases = which_releases(TargetNode), + Version = parse_version(VersionArg), + case proplists:get_value(Version, WhichReleases) of + undefined -> + %% not installed, so unpack tarball: + ?INFO("Release ~s not found, attempting to unpack releases/~s/~s.tar.gz~n",[Version,Version,RelName]), + ReleasePackage = Version ++ "/" ++ RelName, + case rpc:call(TargetNode, release_handler, unpack_release, + [ReleasePackage], ?TIMEOUT) of + {ok, Vsn} -> + ?INFO("Unpacked successfully: ~p~n", [Vsn]), + install_and_permafy(TargetNode, RelName, Vsn); + {error, UnpackReason} -> + print_existing_versions(TargetNode), + ?INFO("Unpack failed: ~p~n",[UnpackReason]), + erlang:halt(2) + end; + old -> + %% no need to unpack, has been installed previously + ?INFO("Release ~s is marked old, switching to it.~n",[Version]), + install_and_permafy(TargetNode, RelName, Version); + unpacked -> + ?INFO("Release ~s is already unpacked, now installing.~n",[Version]), + install_and_permafy(TargetNode, RelName, Version); + current -> %% installed and in-use, just needs to be permanent + ?INFO("Release ~s is already installed and current. Making permanent.~n",[Version]), + permafy(TargetNode, RelName, Version); + permanent -> + ?INFO("Release ~s is already installed, and set permanent.~n",[Version]) + end; +main(_) -> + erlang:halt(1). + +parse_version(V) when is_list(V) -> + hd(string:tokens(V,"/")). + +install_and_permafy(TargetNode, RelName, Vsn) -> + case rpc:call(TargetNode, release_handler, check_install_release, [Vsn], ?TIMEOUT) of + {ok, _OtherVsn, _Desc} -> + ok; + {error, Reason} -> + ?INFO("ERROR: release_handler:check_install_release failed: ~p~n",[Reason]), + erlang:halt(3) + end, + case rpc:call(TargetNode, release_handler, install_release, [Vsn], ?TIMEOUT) of + {ok, _, _} -> + ?INFO("Installed Release: ~s~n", [Vsn]), + permafy(TargetNode, RelName, Vsn), + ok; + {error, {no_such_release, Vsn}} -> + VerList = + iolist_to_binary( + [io_lib:format("* ~s\t~s~n",[V,S]) || {V,S} <- which_releases(TargetNode)]), + ?INFO("Installed versions:~n~s", [VerList]), + ?INFO("ERROR: Unable to revert to '~s' - not installed.~n", [Vsn]), + erlang:halt(2) + end. + +permafy(TargetNode, RelName, Vsn) -> + ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], ?TIMEOUT), + file:copy(filename:join(["bin", RelName++"-"++Vsn]), + filename:join(["bin", RelName])), + ?INFO("Made release permanent: ~p~n", [Vsn]), + ok. + +which_releases(TargetNode) -> + R = rpc:call(TargetNode, release_handler, which_releases, [], ?TIMEOUT), + [ {V, S} || {_,V,_, S} <- R ]. + +print_existing_versions(TargetNode) -> + VerList = iolist_to_binary([ + io_lib:format("* ~s\t~s~n",[V,S]) + || {V,S} <- which_releases(TargetNode) ]), + ?INFO("Installed versions:~n~s", [VerList]). + +start_distribution(NodeName, Cookie) -> + MyNode = make_script_node(NodeName), + {ok, _Pid} = net_kernel:start([MyNode, longnames]), + erlang:set_cookie(node(), list_to_atom(Cookie)), + TargetNode = list_to_atom(NodeName), + case {net_kernel:connect_node(TargetNode), + net_adm:ping(TargetNode)} of + {true, pong} -> + ok; + {_, pang} -> + io:format("Node ~p not responding to pings.\n", [TargetNode]), + erlang:halt(1) + end, + {ok, Cwd} = file:get_cwd(), + ok = rpc:call(TargetNode, file, set_cwd, [Cwd], ?TIMEOUT), + TargetNode. + +make_script_node(Node) -> + [Name, Host] = string:tokens(Node, "@"), + list_to_atom(lists:concat([Name, "_upgrader_", os:getpid(), "@", Host])). diff --git a/priv/templates/install_upgrade_escript.dtl b/priv/templates/install_upgrade_escript.dtl deleted file mode 100644 index 3fb9d04..0000000 --- a/priv/templates/install_upgrade_escript.dtl +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env escript -%%! -noshell -noinput -%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- -%% ex: ft=erlang ts=4 sw=4 et - --define(TIMEOUT, 300000). --define(INFO(Fmt,Args), io:format(Fmt,Args)). - -%% Unpack or upgrade to a new tar.gz release -main(["unpack", RelName, NodeName, Cookie, VersionArg]) -> - TargetNode = start_distribution(NodeName, Cookie), - WhichReleases = which_releases(TargetNode), - Version = parse_version(VersionArg), - case proplists:get_value(Version, WhichReleases) of - undefined -> - %% not installed, so unpack tarball: - ?INFO("Release ~s not found, attempting to unpack releases/~s/~s.tar.gz~n",[Version,Version,RelName]), - ReleasePackage = Version ++ "/" ++ RelName, - case rpc:call(TargetNode, release_handler, unpack_release, - [ReleasePackage], ?TIMEOUT) of - {ok, Vsn} -> - ?INFO("Unpacked successfully: ~p~n", [Vsn]); - {error, UnpackReason} -> - print_existing_versions(TargetNode), - ?INFO("Unpack failed: ~p~n",[UnpackReason]), - erlang:halt(2) - end; - old -> - %% no need to unpack, has been installed previously - ?INFO("Release ~s is marked old, switching to it.~n",[Version]); - unpacked -> - ?INFO("Release ~s is already unpacked, now installing.~n",[Version]); - current -> - ?INFO("Release ~s is already installed and current. Making permanent.~n",[Version]); - permanent -> - ?INFO("Release ~s is already installed, and set permanent.~n",[Version]) - end; -main(["install", RelName, NodeName, Cookie, VersionArg]) -> - TargetNode = start_distribution(NodeName, Cookie), - WhichReleases = which_releases(TargetNode), - Version = parse_version(VersionArg), - case proplists:get_value(Version, WhichReleases) of - undefined -> - %% not installed, so unpack tarball: - ?INFO("Release ~s not found, attempting to unpack releases/~s/~s.tar.gz~n",[Version,Version,RelName]), - ReleasePackage = Version ++ "/" ++ RelName, - case rpc:call(TargetNode, release_handler, unpack_release, - [ReleasePackage], ?TIMEOUT) of - {ok, Vsn} -> - ?INFO("Unpacked successfully: ~p~n", [Vsn]), - install_and_permafy(TargetNode, RelName, Vsn); - {error, UnpackReason} -> - print_existing_versions(TargetNode), - ?INFO("Unpack failed: ~p~n",[UnpackReason]), - erlang:halt(2) - end; - old -> - %% no need to unpack, has been installed previously - ?INFO("Release ~s is marked old, switching to it.~n",[Version]), - install_and_permafy(TargetNode, RelName, Version); - unpacked -> - ?INFO("Release ~s is already unpacked, now installing.~n",[Version]), - install_and_permafy(TargetNode, RelName, Version); - current -> %% installed and in-use, just needs to be permanent - ?INFO("Release ~s is already installed and current. Making permanent.~n",[Version]), - permafy(TargetNode, RelName, Version); - permanent -> - ?INFO("Release ~s is already installed, and set permanent.~n",[Version]) - end; -main(_) -> - erlang:halt(1). - -parse_version(V) when is_list(V) -> - hd(string:tokens(V,"/")). - -install_and_permafy(TargetNode, RelName, Vsn) -> - case rpc:call(TargetNode, release_handler, check_install_release, [Vsn], ?TIMEOUT) of - {ok, _OtherVsn, _Desc} -> - ok; - {error, Reason} -> - ?INFO("ERROR: release_handler:check_install_release failed: ~p~n",[Reason]), - erlang:halt(3) - end, - case rpc:call(TargetNode, release_handler, install_release, [Vsn], ?TIMEOUT) of - {ok, _, _} -> - ?INFO("Installed Release: ~s~n", [Vsn]), - permafy(TargetNode, RelName, Vsn), - ok; - {error, {no_such_release, Vsn}} -> - VerList = - iolist_to_binary( - [io_lib:format("* ~s\t~s~n",[V,S]) || {V,S} <- which_releases(TargetNode)]), - ?INFO("Installed versions:~n~s", [VerList]), - ?INFO("ERROR: Unable to revert to '~s' - not installed.~n", [Vsn]), - erlang:halt(2) - end. - -permafy(TargetNode, RelName, Vsn) -> - ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], ?TIMEOUT), - file:copy(filename:join(["bin", RelName++"-"++Vsn]), - filename:join(["bin", RelName])), - ?INFO("Made release permanent: ~p~n", [Vsn]), - ok. - -which_releases(TargetNode) -> - R = rpc:call(TargetNode, release_handler, which_releases, [], ?TIMEOUT), - [ {V, S} || {_,V,_, S} <- R ]. - -print_existing_versions(TargetNode) -> - VerList = iolist_to_binary([ - io_lib:format("* ~s\t~s~n",[V,S]) - || {V,S} <- which_releases(TargetNode) ]), - ?INFO("Installed versions:~n~s", [VerList]). - -start_distribution(NodeName, Cookie) -> - MyNode = make_script_node(NodeName), - {ok, _Pid} = net_kernel:start([MyNode, longnames]), - erlang:set_cookie(node(), list_to_atom(Cookie)), - TargetNode = list_to_atom(NodeName), - case {net_kernel:connect_node(TargetNode), - net_adm:ping(TargetNode)} of - {true, pong} -> - ok; - {_, pang} -> - io:format("Node ~p not responding to pings.\n", [TargetNode]), - erlang:halt(1) - end, - {ok, Cwd} = file:get_cwd(), - ok = rpc:call(TargetNode, file, set_cwd, [Cwd], ?TIMEOUT), - TargetNode. - -make_script_node(Node) -> - [Name, Host] = string:tokens(Node, "@"), - list_to_atom(lists:concat([Name, "_upgrader_", os:getpid(), "@", Host])). diff --git a/priv/templates/nodetool b/priv/templates/nodetool new file mode 100644 index 0000000..dee14b4 --- /dev/null +++ b/priv/templates/nodetool @@ -0,0 +1,137 @@ +%% -*- 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 +%% +%% ------------------------------------------------------------------- + +main(Args) -> + ok = start_epmd(), + %% Extract the args + {RestArgs, TargetNode} = process_args(Args, [], undefined), + + %% 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; + {_, pang} -> + io:format("Node ~p not responding to pings.\n", [TargetNode]), + halt(1) + end, + + case RestArgs of + ["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, [], 60000)]); + ["restart"] -> + io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]); + ["reboot"] -> + io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]); + ["rpc", Module, Function | RpcArgs] -> + case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), + [RpcArgs], 60000) of + ok -> + ok; + {badrpc, Reason} -> + io:format("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(lists:flatten(ArgsAsString)), 60000) of + {badrpc, Reason} -> + io:format("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}\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([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/priv/templates/nodetool.dtl b/priv/templates/nodetool.dtl deleted file mode 100644 index dee14b4..0000000 --- a/priv/templates/nodetool.dtl +++ /dev/null @@ -1,137 +0,0 @@ -%% -*- 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 -%% -%% ------------------------------------------------------------------- - -main(Args) -> - ok = start_epmd(), - %% Extract the args - {RestArgs, TargetNode} = process_args(Args, [], undefined), - - %% 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; - {_, pang} -> - io:format("Node ~p not responding to pings.\n", [TargetNode]), - halt(1) - end, - - case RestArgs of - ["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, [], 60000)]); - ["restart"] -> - io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]); - ["reboot"] -> - io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]); - ["rpc", Module, Function | RpcArgs] -> - case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), - [RpcArgs], 60000) of - ok -> - ok; - {badrpc, Reason} -> - io:format("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(lists:flatten(ArgsAsString)), 60000) of - {badrpc, Reason} -> - io:format("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}\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([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/priv/templates/sys_config b/priv/templates/sys_config new file mode 100644 index 0000000..eda2e75 --- /dev/null +++ b/priv/templates/sys_config @@ -0,0 +1,9 @@ +%% Thanks to Ulf Wiger at Ericcson for these comments: +%% +%% This file is identified via the erl command line option -config File. +%% Note that File should have no extension, e.g. +%% erl -config .../sys (if this file is called sys.config) +%% +%% In this file, you can redefine application environment variables. +%% This way, you don't have to modify the .app files of e.g. OTP applications. +[]. diff --git a/priv/templates/sys_config.dtl b/priv/templates/sys_config.dtl deleted file mode 100644 index eda2e75..0000000 --- a/priv/templates/sys_config.dtl +++ /dev/null @@ -1,9 +0,0 @@ -%% Thanks to Ulf Wiger at Ericcson for these comments: -%% -%% This file is identified via the erl command line option -config File. -%% Note that File should have no extension, e.g. -%% erl -config .../sys (if this file is called sys.config) -%% -%% In this file, you can redefine application environment variables. -%% This way, you don't have to modify the .app files of e.g. OTP applications. -[]. diff --git a/priv/templates/vm_args b/priv/templates/vm_args new file mode 100644 index 0000000..558ce39 --- /dev/null +++ b/priv/templates/vm_args @@ -0,0 +1,19 @@ +## Name of the node +-name {{ rel_name }}@127.0.0.1 + +## Cookie for distributed erlang +-setcookie {{ rel_name }} + +## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive +## (Disabled by default..use with caution!) +##-heart + +## Enable kernel poll and a few async threads +##+K true +##+A 5 + +## Increase number of concurrent ports/sockets +##-env ERL_MAX_PORTS 4096 + +## Tweak GC to run more often +##-env ERL_FULLSWEEP_AFTER 10 diff --git a/priv/templates/vm_args.dtl b/priv/templates/vm_args.dtl deleted file mode 100644 index 558ce39..0000000 --- a/priv/templates/vm_args.dtl +++ /dev/null @@ -1,19 +0,0 @@ -## Name of the node --name {{ rel_name }}@127.0.0.1 - -## Cookie for distributed erlang --setcookie {{ rel_name }} - -## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive -## (Disabled by default..use with caution!) -##-heart - -## Enable kernel poll and a few async threads -##+K true -##+A 5 - -## Increase number of concurrent ports/sockets -##-env ERL_MAX_PORTS 4096 - -## Tweak GC to run more often -##-env ERL_FULLSWEEP_AFTER 10 diff --git a/rebar.config b/rebar.config index 74ae226..facbdc8 100644 --- a/rebar.config +++ b/rebar.config @@ -6,16 +6,17 @@ {providers, ".*", {git, "https://github.com/tsloughter/providers.git", {tag, "v1.3.0"}}}, - {erlydtl, ".*", - {git, "https://github.com/erlydtl/erlydtl.git", + {mustache, ".*", + {git, "https://github.com/soranoba/mustache.git", {branch, "master"}}}, {getopt, "", {git, "https://github.com/jcomellas/getopt.git", {branch, "master"}}}]}. +{escript_incl_extra, [{"priv/templates/*", "."}]}. {escript_emu_args, "%%! +sbtu +A0 -noinput\n"}. {escript_incl_apps, - [getopt, erlware_commons, merl, erlydtl, providers, relx]}. + [getopt, erlware_commons, providers, relx]}. %% Compiler Options ============================================================ {erl_opts, @@ -28,13 +29,6 @@ {eunit_opts, [{report, {eunit_surefire, [{dir, "."}]}}]}. -%% Erlydtl ===================================================================== -{erlydtl_opts, [{doc_root, "priv/templates"}, - force_recompile, - {compiler_options, [report, return, debug_info]}]}. - -{provider_hooks, [{pre, [{compile, {erlydtl, compile}}]}]}. - %% Profiles ==================================================================== {profiles, [{dev, [{deps, [{neotoma, ".*", @@ -43,6 +37,8 @@ ] }]}. +{overrides, [{override, mustache, [{deps, []}, {plugins, []}]}]}. + {ct_opts, [{cover_spec, "cover.spec"}, {cover_enabled, true}, {cover_print_enabled, true}]}. diff --git a/rebar.lock b/rebar.lock index 8bfc7c1..0f47b93 100644 --- a/rebar.lock +++ b/rebar.lock @@ -2,27 +2,19 @@ {git,"https://github.com/erlware/rebar_vsn_plugin.git", {ref,"fd40c960c7912193631d948fe962e1162a8d1334"}}, 1}, - {<<"merl">>, - {git,"git://github.com/erlydtl/merl.git", - {ref,"750b09d44425f435ff579a4d28bf5844bb5b4ef1"}}, - 1}, - {<<"eunit_formatters">>, - {git,"git://github.com/seancribbs/eunit_formatters", - {ref,"2c73eb6e46b0863f19507857b386a48a53aaf141"}}, - 1}, {<<"providers">>, {git,"https://github.com/tsloughter/providers.git", {ref,"d565693cbbca3457df34d95c53c47e1faa8cde6c"}}, 0}, + {<<"mustache">>, + {git,"https://github.com/soranoba/mustache.git", + {ref,"5a15c03e0bcc307dd4165208802b1641dbf88b50"}}, + 0}, {<<"getopt">>, {git,"https://github.com/jcomellas/getopt.git", {ref,"626698975e63866156159661d100785d65eab6f9"}}, 0}, - {<<"erlydtl">>, - {git,"https://github.com/erlydtl/erlydtl.git", - {ref,"de00ccf522be8d3f9b0dcb7cd680f83b4fb7267a"}}, - 0}, {<<"erlware_commons">>, {git,"https://github.com/erlware/erlware_commons.git", - {ref,"05b956da26788f30b3cb793fa6ace02b75f481d0"}}, + {ref,"ef0d252b11c863f9c228af2fe93a4e42fba2f7f3"}}, 0}]. diff --git a/src/rlx_prv_archive.erl b/src/rlx_prv_archive.erl index 3ed34f8..59fbdc2 100644 --- a/src/rlx_prv_archive.erl +++ b/src/rlx_prv_archive.erl @@ -144,8 +144,7 @@ overlay_files(_, undefined, _) -> overlay_files(OverlayVars, Overlay, OutputDir) -> [begin To = to(O), - ToTemplateName = rlx_prv_overlay:make_template_name("rlx_template_to_template", To), - File = rlx_prv_overlay:render_string(OverlayVars, To, ToTemplateName), + File = rlx_prv_overlay:render_string(OverlayVars, To), {ec_cnv:to_list(File), ec_cnv:to_list(filename:join(OutputDir, File))} end || O <- Overlay, filter(O)]. diff --git a/src/rlx_prv_assembler.erl b/src/rlx_prv_assembler.erl index 60cef76..5fa1817 100644 --- a/src/rlx_prv_assembler.erl +++ b/src/rlx_prv_assembler.erl @@ -545,20 +545,20 @@ ensure_not_exist(RelConfPath) -> end. erl_script(ErtsVsn) -> - render(erl_script_dtl, [{erts_vsn, ErtsVsn}]). + render(erl_script, [{erts_vsn, ErtsVsn}]). bin_file_contents(OsFamily, RelName, RelVsn, ErtsVsn, ErlOpts) -> Template = case OsFamily of - unix -> bin_dtl; - win32 -> bin_windows_dtl + unix -> bin; + win32 -> bin_windows end, render(Template, [{rel_name, RelName}, {rel_vsn, RelVsn}, {erts_vsn, ErtsVsn}, {erl_opts, ErlOpts}]). extended_bin_file_contents(OsFamily, RelName, RelVsn, ErtsVsn, ErlOpts) -> Template = case OsFamily of - unix -> extended_bin_dtl; - win32 -> extended_bin_windows_dtl + unix -> extended_bin; + win32 -> extended_bin_windows end, render(Template, [{rel_name, RelName}, {rel_vsn, RelVsn}, {erts_vsn, ErtsVsn}, {erl_opts, ErlOpts}]). @@ -566,23 +566,25 @@ extended_bin_file_contents(OsFamily, RelName, RelVsn, ErtsVsn, ErlOpts) -> erl_ini(OutputDir, ErtsVsn) -> ErtsDirName = string:concat("erts-", ErtsVsn), BinDir = filename:join([OutputDir, ErtsDirName, bin]), - render(erl_ini_dtl, [{bin_dir, BinDir}, {output_dir, OutputDir}]). + render(erl_ini, [{bin_dir, BinDir}, {output_dir, OutputDir}]). install_upgrade_escript_contents() -> - render(install_upgrade_escript_dtl). + render(install_upgrade_escript). nodetool_contents() -> - render(nodetool_dtl). + render(nodetool). sys_config_file() -> - render(sys_config_dtl). + render(sys_config). vm_args_file(RelName) -> - render(vm_args_dtl, [{rel_name, RelName}]). + render(vm_args, [{rel_name, RelName}]). render(Template) -> render(Template, []). render(Template, Data) -> - {ok, Rendered} = Template:render(Data), - Rendered. + Files = rlx_util:template_files(), + Tpl = rlx_util:load_file(Files, escript, atom_to_list(Template)), + {ok, Content} = rlx_util:render(Tpl, Data), + Content. diff --git a/src/rlx_prv_overlay.erl b/src/rlx_prv_overlay.erl index d9c409c..6c27b5c 100644 --- a/src/rlx_prv_overlay.erl +++ b/src/rlx_prv_overlay.erl @@ -29,13 +29,10 @@ format_error/1]). -export([generate_overlay_vars/2, - make_template_name/2, - render_string/3]). + render_string/2]). -define(DIRECTORY_RE, ".*(\/|\\\\)$"). --define(ERLYDTL_COMPILE_OPTS, [report_warnings, return_errors, {auto_escape, false}, {out_dir, false}]). - -include("relx.hrl"). -define(PROVIDER, overlay). @@ -64,7 +61,8 @@ do(State) -> {error, Reason} -> {error, Reason}; OverlayVars -> - do_overlay(State, OverlayVars) + Files = rlx_util:template_files(), + do_overlay(State, Files, OverlayVars) end; false -> ?RLX_ERROR({unresolved_release, RelName, RelVsn}) @@ -79,6 +77,9 @@ format_error({ec_file_error, AppDir, TargetDir, E}) -> format_error({unable_to_read_varsfile, FileName, Reason}) -> io_lib:format("Unable to read vars file (~s) for overlay due to: ~p", [FileName, Reason]); +format_error({read_template, FileName, Reason}) -> + io_lib:format("Unable to read template file (~s) for overlay due to: ~s", + [FileName, file:format_error(Reason)]); format_error({overlay_failed, Errors}) -> [[format_error(rlx_util:error_reason(Error)), "\n"] || Error <- Errors]; format_error({dir_render_failed, Dir, Error}) -> @@ -183,8 +184,16 @@ merge_overlay_vars(State, FileNames) -> lists:foldl(fun(FileName, Acc) -> RelativePath = filename:join(RelativeRoot, erlang:iolist_to_binary(FileName)), case file:consult(RelativePath) of + %% {ok, [Terms]} -> + %% lists:ukeymerge(1, lists:ukeysort(1, Terms), Acc); + %% % the location of the included overlay files will be relative + %% %% to the current one being read + %% %% OverlayRelativeRoot = filename:dirname(FileName), + %% %% NewTerms = check_overlay_inclusion(State, OverlayRelativeRoot, Terms), + + %% %% lists:ukeymerge(1, lists:ukeysort(1, NewTerms), Acc); {ok, Terms} -> - % the location of the included overlay files will be relative + %% the location of the included overlay files will be relative %% to the current one being read OverlayRelativeRoot = filename:dirname(FileName), NewTerms = check_overlay_inclusion(State, OverlayRelativeRoot, Terms), @@ -237,26 +246,7 @@ generate_release_vars(Release) -> {release_erts_version, rlx_release:erts(Release)}, {release_name, rlx_release:name(Release)}, {rel_vsn, rlx_release:vsn(Release)}, - {release_version, rlx_release:vsn(Release)}, - {release_applications, lists:map(fun(App) -> - rlx_app_info:name(App) - end, rlx_release:application_details(Release))}, - {release, [generate_app_vars(App)|| App <- rlx_release:application_details(Release)]}, - {release_goals, [if - erlang:is_list(Constraint) -> - Constraint; - true -> - rlx_depsolver:format_constraint(Constraint) - end || Constraint <- rlx_release:goals(Release)]}]. - --spec generate_app_vars(rlx_app_info:t()) -> AppInfo::tuple(). -generate_app_vars(App) -> - {rlx_app_info:name(App), - [{version, rlx_app_info:original_vsn(App)}, - {dir, rlx_app_info:dir(App)}, - {active_dependencies, rlx_app_info:active_deps(App)}, - {library_dependencies, rlx_app_info:library_deps(App)}, - {link, rlx_app_info:link(App)}]}. + {release_version, rlx_release:vsn(Release)}]. -spec generate_state_vars(rlx_state:t()) -> proplists:proplist(). generate_state_vars(State) -> @@ -288,16 +278,16 @@ generate_state_vars(State) -> erlang:atom_to_list(Name1) ++ "-" ++ Vsn1 end}]. --spec do_overlay(rlx_state:t(), proplists:proplist()) -> +-spec do_overlay(rlx_state:t(), list(), proplists:proplist()) -> {ok, rlx_state:t()} | relx:error(). -do_overlay(State, OverlayVars) -> +do_overlay(State, Files, OverlayVars) -> case rlx_state:get(State, overlay, undefined) of undefined -> {ok, State}; Overlays -> handle_errors(State, lists:map(fun(Overlay) -> - do_individual_overlay(State, OverlayVars, + do_individual_overlay(State, Files, OverlayVars, Overlay) end, Overlays)) end. @@ -313,61 +303,49 @@ handle_errors(State, Result) -> {ok, State} end. --spec do_individual_overlay(rlx_state:t(), proplists:proplist(), +-spec do_individual_overlay(rlx_state:t(), list(), proplists:proplist(), OverlayDirective::term()) -> {ok, rlx_state:t()} | relx:error(). -do_individual_overlay(State, OverlayVars, {mkdir, Dir}) -> - ModuleName = make_template_name("rlx_mkdir_template", Dir), - case erlydtl:compile(erlang:iolist_to_binary(Dir), ModuleName, ?ERLYDTL_COMPILE_OPTS) of - {ok, ModuleName} -> - case render(ModuleName, OverlayVars) of - {ok, IoList} -> - Absolute = absolutize(State, - filename:join(rlx_state:output_dir(State), - erlang:iolist_to_binary(IoList))), - case rlx_util:mkdir_p(Absolute) of - {error, Error} -> - ?RLX_ERROR({unable_to_make_dir, Absolute, Error}); - ok -> - ok - end; +do_individual_overlay(State, _Files, OverlayVars, {mkdir, Dir}) -> + case rlx_util:render(erlang:iolist_to_binary(Dir), OverlayVars) of + {ok, IoList} -> + Absolute = absolutize(State, + filename:join(rlx_state:output_dir(State), + erlang:iolist_to_binary(IoList))), + case rlx_util:mkdir_p(Absolute) of {error, Error} -> - ?RLX_ERROR({dir_render_failed, Dir, Error}) + ?RLX_ERROR({unable_to_make_dir, Absolute, Error}); + ok -> + ok end; - {error, Reason, _Warnings} -> - ?RLX_ERROR({unable_to_compile_template, Dir, Reason}) + {error, Error} -> + ?RLX_ERROR({dir_render_failed, Dir, Error}) end; -do_individual_overlay(State, OverlayVars, {copy, From, To}) -> - FromTemplateName = make_template_name("rlx_copy_from_template", From), - ToTemplateName = make_template_name("rlx_copy_to_template", To), - file_render_do(OverlayVars, From, FromTemplateName, +do_individual_overlay(State, _Files, OverlayVars, {copy, From, To}) -> + file_render_do(OverlayVars, From, fun(FromFile) -> - file_render_do(OverlayVars, To, ToTemplateName, + file_render_do(OverlayVars, To, fun(ToFile) -> copy_to(State, FromFile, ToFile) end) end); -do_individual_overlay(State, OverlayVars, {link, From, To}) -> +do_individual_overlay(State, Files, OverlayVars, {link, From, To}) -> case rlx_state:dev_mode(State) of false -> - do_individual_overlay(State, OverlayVars, {copy, From, To}); + do_individual_overlay(State, Files, OverlayVars, {copy, From, To}); true -> - FromTemplateName = make_template_name("rlx_copy_from_template", From), - ToTemplateName = make_template_name("rlx_copy_to_template", To), - file_render_do(OverlayVars, From, FromTemplateName, + file_render_do(OverlayVars, From, fun(FromFile) -> - file_render_do(OverlayVars, To, ToTemplateName, + file_render_do(OverlayVars, To, fun(ToFile) -> link_to(State, FromFile, ToFile) end) end) end; -do_individual_overlay(State, OverlayVars, {template, From, To}) -> - FromTemplateName = make_template_name("rlx_template_from_template", From), - ToTemplateName = make_template_name("rlx_template_to_template", To), - file_render_do(OverlayVars, From, FromTemplateName, +do_individual_overlay(State, _Files, OverlayVars, {template, From, To}) -> + file_render_do(OverlayVars, From, fun(FromFile) -> - file_render_do(OverlayVars, To, ToTemplateName, + file_render_do(OverlayVars, To, fun(ToFile) -> RelativeRoot = get_relative_root(State), FromFile0 = absolutize(State, @@ -465,85 +443,55 @@ is_directory(ToFile0, ToFile1) -> -spec render_template(proplists:proplist(), iolist()) -> ok | relx:error(). render_template(OverlayVars, Data) -> - TemplateName = make_template_name("rlx_template_renderer", Data), - case erlydtl:compile(Data, TemplateName, ?ERLYDTL_COMPILE_OPTS) of - Good when Good =:= ok; Good =:= {ok, TemplateName} -> - case render(TemplateName, OverlayVars) of - {ok, IoData} -> - {ok, IoData}; - {error, Reason} -> - ?RLX_ERROR({unable_to_render_template, Data, Reason}) - end; - {error, Reason, _Warnings} -> - ?RLX_ERROR({unable_to_compile_template, Data, Reason}) + case rlx_util:render(Data, OverlayVars) of + {ok, IoData} -> + {ok, IoData}; + {error, Reason} -> + ?RLX_ERROR({unable_to_render_template, Data, Reason}) end. write_template(OverlayVars, FromFile, ToFile) -> - case render_template(OverlayVars, FromFile) of - {ok, IoData} -> - case filelib:ensure_dir(ToFile) of - ok -> - case file:write_file(ToFile, IoData) of + case file:read_file(FromFile) of + {ok, File} -> + case render_template(OverlayVars, File) of + {ok, IoData} -> + case filelib:ensure_dir(ToFile) of ok -> - {ok, FileInfo} = file:read_file_info(FromFile), - ok = file:write_file_info(ToFile, FileInfo), - ok; + case file:write_file(ToFile, IoData) of + ok -> + {ok, FileInfo} = file:read_file_info(FromFile), + ok = file:write_file_info(ToFile, FileInfo), + ok; + {error, Reason} -> + ?RLX_ERROR({unable_to_write, ToFile, Reason}) + end; {error, Reason} -> - ?RLX_ERROR({unable_to_write, ToFile, Reason}) + ?RLX_ERROR({unable_to_enclosing_dir, ToFile, Reason}) end; - {error, Reason} -> - ?RLX_ERROR({unable_to_enclosing_dir, ToFile, Reason}) + Error -> + Error end; - Error -> - Error + {error, Error} -> + ?RLX_ERROR({read_template, Error}) end. -render_string(OverlayVars, Data, TemplateName) -> - case erlydtl:compile(erlang:iolist_to_binary(Data), TemplateName, ?ERLYDTL_COMPILE_OPTS) of - {ok, TemplateName} -> - case render(TemplateName, OverlayVars) of - {ok, IoList} -> - erlang:iolist_to_binary(IoList); - {error, Error} -> - ?RLX_ERROR({render_failed, Data, Error}) - end; - {error, Reason, _Warnings} -> - ?RLX_ERROR({unable_to_compile_template, Data, Reason}) +render_string(OverlayVars, Data) -> + case rlx_util:render(Data, OverlayVars) of + {ok, IoList} -> + erlang:iolist_to_binary(IoList); + {error, Error} -> + ?RLX_ERROR({render_failed, Data, Error}) end. --spec file_render_do(proplists:proplist(), iolist(), module(), +-spec file_render_do(proplists:proplist(), iolist(), fun((term()) -> {ok, rlx_state:t()} | relx:error())) -> {ok, rlx_state:t()} | relx:error(). -file_render_do(OverlayVars, Data, TemplateName, NextAction) -> - case erlydtl:compile(erlang:iolist_to_binary(Data), TemplateName, ?ERLYDTL_COMPILE_OPTS) of - {ok, TemplateName} -> - case render(TemplateName, OverlayVars) of - {ok, IoList} -> - NextAction(IoList); - {error, Error} -> - ?RLX_ERROR({render_failed, Data, Error}) - end; - {error, Reason, _Warnings} -> - ?RLX_ERROR({unable_to_compile_template, Data, Reason}) - end. - --spec make_template_name(string(), term()) -> module(). -make_template_name(Base, Value) -> - %% Seed so we get different values each time - random:seed(os:timestamp()), - Hash = erlang:phash2(Value), - Ran = random:uniform(10000000), - erlang:list_to_atom(Base ++ "_" ++ - erlang:integer_to_list(Hash) ++ - "_" ++ erlang:integer_to_list(Ran)). - --spec render(module(), proplists:proplist()) -> {ok, iolist()} | {error, Reason::term()}. -render(ModuleName, OverlayVars) -> - try - ModuleName:render(OverlayVars) - catch - _:Reason -> - {error, Reason} +file_render_do(OverlayVars, File, NextAction) -> + case rlx_util:render(File, OverlayVars) of + {ok, IoList} -> + NextAction(IoList); + {error, Error} -> + ?RLX_ERROR({render_failed, File, Error}) end. absolutize(State, FileName) -> diff --git a/src/rlx_util.erl b/src/rlx_util.erl index 9c4dcc2..4639ffa 100644 --- a/src/rlx_util.erl +++ b/src/rlx_util.erl @@ -32,7 +32,12 @@ error_reason/1, indent/1, optional_to_string/1, - wildcard_paths/1]). + wildcard_paths/1, + render/1, + render/2, + load_file/3, + template_files/0, + escript_foldl/3]). -define(ONE_LEVEL_INDENT, " "). %%============================================================================ @@ -147,6 +152,61 @@ wildcard(Path) when is_list(Path) -> Paths -> Paths end. +render(Template) -> + render(Template, []). + +render(Template, Data) -> + {ok, mustache:render(ec_cnv:to_binary(Template), Data)}. + +load_file(Files, escript, Name) -> + {Name, Bin} = lists:keyfind(Name, 1, Files), + Bin; +load_file(_Files, file, Name) -> + {ok, Bin} = file:read_file(Name), + Bin. + +template_files() -> + find_priv_templates() ++ escript_files(). + +find_priv_templates() -> + Files = filelib:wildcard(filename:join([code:priv_dir(relx), "templates", "*"])), + lists:map(fun(File) -> + {ok, Bin} = file:read_file(File), + {filename:basename(File), Bin} + end, Files). + +%% Scan the current escript for available files +escript_files() -> + try + {ok, Files} = escript_foldl( + fun(Name, _, GetBin, Acc) -> + [{filename:basename(Name), GetBin()} | Acc] + end, [], filename:absname(escript:script_name())), + Files + catch + _:_ -> + [] + end. + +escript_foldl(Fun, Acc, File) -> + case escript:extract(File, [compile_source]) of + {ok, [_Shebang, _Comment, _EmuArgs, Body]} -> + case Body of + {source, BeamCode} -> + GetInfo = fun() -> file:read_file_info(File) end, + GetBin = fun() -> BeamCode end, + {ok, Fun(".", GetInfo, GetBin, Acc)}; + {beam, BeamCode} -> + GetInfo = fun() -> file:read_file_info(File) end, + GetBin = fun() -> BeamCode end, + {ok, Fun(".", GetInfo, GetBin, Acc)}; + {archive, ArchiveBin} -> + zip:foldl(Fun, Acc, {File, ArchiveBin}) + end; + {error, _} = Error -> + Error + end. + %%%=================================================================== %%% Test Functions %%%=================================================================== diff --git a/test/rlx_release_SUITE.erl b/test/rlx_release_SUITE.erl index 766eb40..c4c782b 100644 --- a/test/rlx_release_SUITE.erl +++ b/test/rlx_release_SUITE.erl @@ -497,18 +497,17 @@ overlay_release(Config) -> VarsFile1 = filename:join([LibDir1, "vars1.config"]), rlx_test_utils:write_config(VarsFile1, [{yahoo, "yahoo"}, - {yahoo2, [{foo, "bar"}]}, - {yahoo3, [{bar, "{{yahoo}}/{{yahoo2.foo}}"}]}, - {foo_dir, "foodir"}]), + {yahoo2, [{foo, "bar"}]}, + {foo_dir, "foodir"}]), VarsFile2 = filename:join([LibDir1, "vars2.config"]), rlx_test_utils:write_config(VarsFile2, [{google, "yahoo"}, - {yahoo2, [{foo, "foo"}]}, - OverlayVars3]), + {yahoo2, "foo"}, + OverlayVars3]), VarsFile3 = filename:join([LibDir1, "vars3.config"]), rlx_test_utils:write_config(VarsFile3, [{google, "yahoo"}, - {yahoo4, [{foo, "{{yahoo}}/{{yahoo2.foo}}4"}]}]), + {yahoo4, "{{yahoo}}/{{yahoo2}}4"}]), ok = rlx_util:mkdir_p(TestDirFull), ok = file:write_file(TestFileFull, rlx_test_utils:test_template_contents()), @@ -557,59 +556,6 @@ overlay_release(Config) -> proplists:get_value(release_version, TemplateData)), ?assertEqual(foo, proplists:get_value(release_name, TemplateData)), - ?assertEqual([kernel,stdlib,lib_dep_1,non_goal_2,non_goal_1, - goal_app_1,goal_app_2], - proplists:get_value(release_applications, TemplateData)), - ?assert(proplists:is_defined(std_version, TemplateData)), - ?assert(proplists:is_defined(kernel_version, TemplateData)), - ?assertEqual("0.0.1", - proplists:get_value(non_goal_1_version, TemplateData)), - ?assertEqual("0.0.1", - proplists:get_value(non_goal_2_version, TemplateData)), - ?assertEqual("0.0.1", - proplists:get_value(goal_app_1_version, TemplateData)), - ?assertEqual("0.0.1", - proplists:get_value(goal_app_2_version, TemplateData)), - ?assertEqual("0.0.1", - proplists:get_value(lib_dep_1, TemplateData)), - ?assert(proplists:is_defined(lib_dep_1_dir, TemplateData)), - ?assertEqual([stdlib,kernel], - proplists:get_value(lib_dep_1_active, TemplateData)), - ?assertEqual([], - proplists:get_value(lib_dep_1_library, TemplateData)), - ?assertEqual("false", - proplists:get_value(lib_dep_1_link, TemplateData)), - ?assertEqual("(3:debug)", - proplists:get_value(log, TemplateData)), - ?assertEqual(filename:join(OutputDir, "foo"), - proplists:get_value(output_dir, TemplateData)), - ?assertEqual(filename:join(OutputDir, "foo"), - proplists:get_value(target_dir, TemplateData)), - ?assertEqual([], - proplists:get_value(overridden, TemplateData)), - ?assertEqual([""], - proplists:get_value(goals, TemplateData)), - ?assert(proplists:is_defined(lib_dirs, TemplateData)), - ?assert(proplists:is_defined(config_file, TemplateData)), - ?assertEqual([""], - proplists:get_value(goals, TemplateData)), - ?assertEqual([], - proplists:get_value(sys_config, TemplateData)), - ?assert(proplists:is_defined(root_dir, TemplateData)), - ?assertEqual(foo, - proplists:get_value(default_release_name, TemplateData)), - ?assertEqual("0.0.1", - proplists:get_value(default_release_version, TemplateData)), - ?assertEqual("foo-0.0.1", - proplists:get_value(default_release, TemplateData)), - ?assertEqual("yahoo", - proplists:get_value(yahoo, TemplateData)), - ?assertEqual("foo", - proplists:get_value(yahoo2_foo, TemplateData)), - ?assertEqual("foodir", - proplists:get_value(foo_dir, TemplateData)), - ?assertEqual("yahoo/foo", - proplists:get_value(yahoo3, TemplateData)), ?assertEqual("yahoo/foo4", proplists:get_value(yahoo4, TemplateData)), ?assertEqual("yahoo", diff --git a/test/rlx_test_utils.erl b/test/rlx_test_utils.erl index 644c49f..4e0fc0d 100644 --- a/test/rlx_test_utils.erl +++ b/test/rlx_test_utils.erl @@ -65,34 +65,16 @@ test_template_contents() -> "{release_name, {{release_name}}}.\n" "{rel_vsn, \"{{release_version}}\"}.\n" "{release_version, \"{{release_version}}\"}.\n" - "{release_applications, [{{ release_applications|join:\", \" }}]}.\n" - "{std_version, \"{{release.stdlib.version}}\"}.\n" - "{kernel_version, \"{{release.kernel.version}}\"}.\n" - "{non_goal_1_version, \"{{release.non_goal_1.version}}\"}.\n" - "{non_goal_2_version, \"{{release.non_goal_2.version}}\"}.\n" - "{goal_app_1_version, \"{{release.goal_app_1.version}}\"}.\n" - "{goal_app_2_version, \"{{release.goal_app_2.version}}\"}.\n" - "{lib_dep_1, \"{{release.lib_dep_1.version}}\"}.\n" - "{lib_dep_1_dir, \"{{release.lib_dep_1.dir}}\"}.\n" - "{lib_dep_1_active, [{{ release.lib_dep_1.active_dependencies|join:\", \" }}]}.\n" - "{lib_dep_1_library, [{{ release.lib_dep_1.library_dependencies|join:\", \" }}]}.\n" - "{lib_dep_1_link, \"{{release.lib_dep_1.link}}\"}.\n" "{log, \"{{log}}\"}.\n" "{output_dir, \"{{output_dir}}\"}.\n" "{target_dir, \"{{target_dir}}\"}.\n" - "{overridden, [{{ overridden|join:\", \" }}]}.\n" - "{goals, [\"{{ goals|join:\", \" }}\"]}.\n" - "{lib_dirs, [\"{{ lib_dirs|join:\", \" }}\"]}.\n" "{config_file, \"{{ config_file }}\"}.\n" - "{providers, [{{ providers|join:\", \" }}]}.\n" "{sys_config, \"{{sys_config}}\"}.\n" "{root_dir, \"{{root_dir}}\"}.\n" "{default_release_name, {{default_release_name}}}.\n" "{default_release_version, \"{{default_release_version}}\"}.\n" "{default_release, \"{{default_release}}\"}.\n" + "{yahoo4, \"{{yahoo4}}\"}.\n" "{yahoo, \"{{yahoo}}\"}.\n" - "{yahoo2_foo, \"{{yahoo2.foo}}\"}.\n" "{foo_dir, \"{{foo_dir}}\"}.\n" - "{yahoo3, \"{{yahoo3.bar}}\"}.\n" - "{yahoo4, \"{{yahoo4.foo}}\"}.\n" "{google, \"{{google}}\"}.\n". -- cgit v1.2.3