dev 7.8 KB

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