dev 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. PROJ_ROOT="$(git rev-parse --show-toplevel)"
  4. cd "$PROJ_ROOT"
  5. usage() {
  6. cat <<EOF
  7. Run EMQX without building a release (which takes longer time).
  8. Node state is stored in '_build/dev-run/$PROFILE'.
  9. The node is started in interactive mode without a boot file.
  10. USAGE: $0 [OPTION]
  11. OPTIONS:
  12. -h|--help: Print this help usage info.
  13. -p|--profile: emqx | emqx-enterprise, defaults to 'PROFILE' env.
  14. -c|--compile: Force recompile, otherwise starts with the already built libs
  15. in '_build/\$PROFILE/lib/'.
  16. -e|--ekka-epmd: Force to use ekka_epmd.
  17. -n|--name: Node name, defaults to \$EMQX_NODE_NAME env.
  18. -r|--remsh [NAME]: Attach to running node's remote console.
  19. ENVIRONMENT VARIABLES:
  20. PROFILE: Overriden by '-p|--profile' option, defaults to 'emqx'.
  21. EMQX_NODE_NAME: Overriden by '-n|--name' or '-r|--remsh' option.
  22. The node name of the EMQX node. Default to emqx@127.0.0.1'.
  23. EMQX_NODE_COOKIE: Erlang cookie, defaults to ~/.erlang.cookie
  24. EOF
  25. }
  26. if [ -n "${DEBUG:-}" ]; then
  27. set -x
  28. fi
  29. export HOCON_ENV_OVERRIDE_PREFIX='EMQX_'
  30. EMQX_NODE_NAME="${EMQX_NODE_NAME:-emqx@127.0.0.1}"
  31. PROFILE="${PROFILE:-emqx}"
  32. FORCE_COMPILE=0
  33. # Do not start using ekka epmd by default, so your IDE can connect to it
  34. EKKA_EPMD=0
  35. REMSH=0
  36. while [ "$#" -gt 0 ]; do
  37. case $1 in
  38. -h|--help)
  39. usage
  40. exit 0
  41. ;;
  42. -n|--name)
  43. EMQX_NODE_NAME="$2"
  44. shift 1
  45. ;;
  46. -r|--remsh)
  47. REMSH=1
  48. if [[ $2 == *@* ]]; then
  49. EMQX_NODE_NAME="$2"
  50. shift 1
  51. fi
  52. ;;
  53. -p|--profile)
  54. PROFILE="${2}"
  55. shift 1;
  56. ;;
  57. -c|--compile)
  58. FORCE_COMPILE=1
  59. ;;
  60. -e|--ekka-epmd)
  61. EKKA_EPMD=1
  62. ;;
  63. *)
  64. echo "Unknown argument $1" >&2
  65. exit 1
  66. ;;
  67. esac
  68. shift 1;
  69. done
  70. case "${PROFILE}" in
  71. ce|emqx)
  72. PROFILE='emqx'
  73. ;;
  74. ee|emqx-enterprise)
  75. PROFILE='emqx-enterprise'
  76. ;;
  77. *)
  78. echo "Unknown profile $PROFILE"
  79. exit 1
  80. ;;
  81. esac
  82. export PROFILE
  83. case "${PROFILE}" in
  84. emqx)
  85. SCHEMA_MOD='emqx_conf_schema'
  86. ;;
  87. emqx-enterprise)
  88. SCHEMA_MOD='emqx_ee_conf_schema'
  89. ;;
  90. esac
  91. BASE_DIR="_build/dev-run/$PROFILE"
  92. export EMQX_ETC_DIR="$BASE_DIR/etc"
  93. export EMQX_DATA_DIR="$BASE_DIR/data"
  94. export EMQX_LOG_DIR="$BASE_DIR/log"
  95. CONFIGS_DIR="$EMQX_DATA_DIR/configs"
  96. # Use your cookie so your IDE can connect to it.
  97. COOKIE="${EMQX_NODE__COOKIE:-${EMQX_NODE_COOKIE:-$(cat ~/.erlang.cookie || echo 'emqxsecretcookie')}}"
  98. mkdir -p "$EMQX_ETC_DIR" "$EMQX_DATA_DIR/patches" "$EMQX_LOG_DIR" "$CONFIGS_DIR"
  99. if [ $EKKA_EPMD -eq 1 ]; then
  100. EPMD_ARGS='-start_epmd false -epmd_module ekka_epmd'
  101. else
  102. EPMD_ARGS=''
  103. fi
  104. ## build compile the profile is it's not compiled yet
  105. prepare_erl_libs() {
  106. local profile="$1"
  107. local libs_dir="_build/${profile}/lib"
  108. local erl_libs=''
  109. if [ $FORCE_COMPILE -eq 1 ] || [ ! -d "$libs_dir" ]; then
  110. make "compile-${PROFILE}"
  111. else
  112. echo "Running from code in $libs_dir"
  113. fi
  114. for app in "${libs_dir}"/*; do
  115. erl_libs="${erl_libs}:${app}"
  116. done
  117. export ERL_LIBS="$erl_libs"
  118. }
  119. ## poorman's mustache templating
  120. mustache() {
  121. local name="$1"
  122. local value="$2"
  123. local file="$3"
  124. sed -i "s|{{\s*${name}\s*}}|${value}|g" "$file"
  125. }
  126. ## render the merged boot conf file.
  127. ## the merge action is done before the profile is compiled
  128. render_hocon_conf() {
  129. input="apps/emqx_conf/etc/emqx.conf.all"
  130. output="$EMQX_ETC_DIR/emqx.conf"
  131. cp "$input" "$output"
  132. mustache emqx_default_erlang_cookie "$COOKIE" "$output"
  133. mustache platform_data_dir "${EMQX_DATA_DIR}" "$output"
  134. mustache platform_log_dir "${EMQX_LOG_DIR}" "$output"
  135. mustache platform_etc_dir "${EMQX_ETC_DIR}" "$output"
  136. }
  137. call_hocon() {
  138. local in=("$@")
  139. local args=''
  140. for arg in "${in[@]}"; do
  141. if [ -z "$args" ]; then
  142. args="\"$arg\""
  143. else
  144. args="$args, \"$arg\""
  145. fi
  146. done
  147. erl -noshell -eval "{ok, _} = application:ensure_all_started(hocon), ok = hocon_cli:main([$args]), init:stop()."
  148. }
  149. # Function to generate app.config and vm.args
  150. # sets two environment variables CONF_FILE and ARGS_FILE
  151. generate_app_conf() {
  152. ## timestamp for each generation
  153. local NOW_TIME
  154. NOW_TIME="$(date +'%Y.%m.%d.%H.%M.%S')"
  155. ## this command populates two files: app.<time>.config and vm.<time>.args
  156. ## NOTE: the generate command merges environment variables to the base config (emqx.conf),
  157. ## but does not include the cluster-override.conf and local-override.conf
  158. ## meaning, certain overrides will not be mapped to app.<time>.config file
  159. call_hocon -v -t "$NOW_TIME" -s "$SCHEMA_MOD" -c "$EMQX_ETC_DIR"/emqx.conf -d "$EMQX_DATA_DIR"/configs generate
  160. ## filenames are per-hocon convention
  161. CONF_FILE="$CONFIGS_DIR/app.$NOW_TIME.config"
  162. ARGS_FILE="$CONFIGS_DIR/vm.$NOW_TIME.args"
  163. }
  164. # apps/emqx/etc/vm.args.cloud
  165. append_args_file() {
  166. ## ensure a new line at the end
  167. echo '' >> "$ARGS_FILE"
  168. cat <<EOF >> "$ARGS_FILE"
  169. -name $EMQX_NODE_NAME
  170. -mnesia dir '"$EMQX_DATA_DIR/mnesia/$EMQX_NODE_NAME"'
  171. -stdlib restricted_shell emqx_restricted_shell
  172. +spp true
  173. +A 4
  174. +IOt 4
  175. +SDio 8
  176. -shutdown_time 30000
  177. -pa '"$EMQX_DATA_DIR/patches"'
  178. -mnesia dump_log_write_threshold 5000
  179. -mnesia dump_log_time_threshold 60000
  180. -os_mon start_disksup false
  181. EOF
  182. }
  183. # copy cert files and acl.conf to etc
  184. copy_other_conf_files() {
  185. cp -r apps/emqx/etc/certs "$EMQX_ETC_DIR"/
  186. cp apps/emqx_authz/etc/acl.conf "$EMQX_ETC_DIR"/
  187. }
  188. is_current_profile_app() {
  189. local app="$1"
  190. case "$app" in
  191. lib-ee*)
  192. if [ "$PROFILE" = 'emqx-enterprise' ]; then
  193. return 0
  194. else
  195. return 1
  196. fi
  197. ;;
  198. *)
  199. if [ "$PROFILE" = 'emqx' ]; then
  200. if [ -f "$app"/BSL.txt ]; then
  201. return 1
  202. else
  203. return 0
  204. fi
  205. else
  206. return 0
  207. fi
  208. ;;
  209. esac
  210. }
  211. ## apps to load
  212. apps_to_load() {
  213. local apps csl
  214. apps="$(./scripts/find-apps.sh | xargs)"
  215. csl=""
  216. for app in $apps; do
  217. if ! is_current_profile_app "$app"; then
  218. continue
  219. fi
  220. name="$(basename "$app")"
  221. if [ -z "$csl" ]; then
  222. csl="$name"
  223. else
  224. csl="$csl,$name"
  225. fi
  226. done
  227. echo "$csl"
  228. }
  229. boot() {
  230. ## Make erl command aware where to load all the beams
  231. ## this should be done before every erl command
  232. prepare_erl_libs "$PROFILE"
  233. render_hocon_conf
  234. generate_app_conf
  235. append_args_file
  236. copy_other_conf_files
  237. APPS="$(apps_to_load)"
  238. BOOT_SEQUENCE="
  239. Apps=[${APPS}],
  240. ok=lists:foreach(fun application:load/1, Apps),
  241. io:format(user, \"~nLoaded ~p apps~n\", [length(Apps)]),
  242. application:ensure_all_started(emqx_machine).
  243. "
  244. # shellcheck disable=SC2086
  245. erl -name "$EMQX_NODE_NAME" \
  246. $EPMD_ARGS \
  247. -proto_dist ekka \
  248. -args_file "$ARGS_FILE" \
  249. -config "$CONF_FILE" \
  250. -s emqx_restricted_shell set_prompt_func \
  251. -eval "$BOOT_SEQUENCE"
  252. }
  253. # Generate a random id
  254. gen_node_id() {
  255. od -t u -N 4 /dev/urandom | head -n1 | awk '{print $2 % 1000}'
  256. }
  257. remsh() {
  258. id="remsh$(gen_node_id)-${EMQX_NODE_NAME}"
  259. # shellcheck disable=SC2086
  260. erl -name "$id" \
  261. -setcookie "$COOKIE" \
  262. -hidden \
  263. -remsh "$EMQX_NODE_NAME" \
  264. $EPMD_ARGS
  265. }
  266. if [ $REMSH -eq 0 ]; then
  267. boot
  268. else
  269. remsh
  270. fi