dev 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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. logerr() {
  7. if [ "${TERM:-dumb}" = dumb ]; then
  8. echo -e "ERROR: $*" 1>&2
  9. else
  10. echo -e "$(tput setaf 1)ERROR: $*$(tput sgr0)" 1>&2
  11. fi
  12. }
  13. usage() {
  14. cat <<EOF
  15. Run EMQX without building a release (which takes longer time).
  16. Node state is stored in '_build/dev-run/$PROFILE'.
  17. The node is started in interactive mode without a boot file.
  18. USAGE: $0 [COMMAND] [OPTION[]
  19. COMMANDS:
  20. help: Print this usage info.
  21. run: Default command.
  22. remsh: Attach to running node's remote console.
  23. Target node name is to be specified with -n|--name,
  24. otherwise defaults to 'emqx@127.0.0.1'.
  25. ctl: Equivalent to 'emqx ctl'.
  26. ctl command arguments should be passed after '--'
  27. e.g. $0 ctl -- help
  28. OPTIONS:
  29. -p|--profile: emqx | emqx-enterprise, defaults to 'PROFILE' env.
  30. -c|--compile: Force recompile, otherwise starts with the already built libs
  31. in '_build/\$PROFILE/lib/'.
  32. -e|--ekka-epmd: Force to use ekka_epmd.
  33. -n|--name: Node name, defaults to \$EMQX_NODE_NAME env.
  34. ENVIRONMENT VARIABLES:
  35. PROFILE: Overridden by '-p|--profile' option, defaults to 'emqx'.
  36. EMQX_NODE_NAME: Overridden by '-n|--name' or '-r|--remsh' option.
  37. The node name of the EMQX node. Default to emqx@127.0.0.1'.
  38. EMQX_NODE_COOKIE: Erlang cookie, defaults to ~/.erlang.cookie
  39. EOF
  40. }
  41. if [ -n "${DEBUG:-}" ]; then
  42. set -x
  43. fi
  44. export HOCON_ENV_OVERRIDE_PREFIX='EMQX_'
  45. export EMQX_LOG__FILE__DEFAULT__ENABLE='false'
  46. export EMQX_LOG__CONSOLE__ENABLE='true'
  47. SYSTEM="$(./scripts/get-distro.sh)"
  48. EMQX_NODE_NAME="${EMQX_NODE_NAME:-emqx@127.0.0.1}"
  49. PROFILE="${PROFILE:-emqx}"
  50. FORCE_COMPILE=0
  51. # Do not start using ekka epmd by default, so your IDE can connect to it
  52. EKKA_EPMD=0
  53. COMMAND='run'
  54. case "${1:-novalue}" in
  55. novalue)
  56. ;;
  57. run)
  58. shift
  59. ;;
  60. remsh)
  61. COMMAND='remsh'
  62. shift
  63. ;;
  64. ctl)
  65. COMMAND='ctl'
  66. shift
  67. ;;
  68. help)
  69. usage
  70. exit 0
  71. ;;
  72. -*)
  73. ;;
  74. esac
  75. while [ "$#" -gt 0 ]; do
  76. case $1 in
  77. -n|--name)
  78. EMQX_NODE_NAME="$2"
  79. shift 1
  80. ;;
  81. -p|--profile)
  82. PROFILE="${2}"
  83. shift 1;
  84. ;;
  85. -c|--compile)
  86. FORCE_COMPILE=1
  87. ;;
  88. -e|--ekka-epmd)
  89. EKKA_EPMD=1
  90. ;;
  91. --)
  92. shift
  93. PASSTHROUGH_ARGS=("$@")
  94. break
  95. ;;
  96. *)
  97. logerr "Unknown argument $1"
  98. exit 1
  99. ;;
  100. esac
  101. shift 1;
  102. done
  103. case "${PROFILE}" in
  104. ce|emqx)
  105. PROFILE='emqx'
  106. ;;
  107. ee|emqx-enterprise)
  108. PROFILE='emqx-enterprise'
  109. ;;
  110. *)
  111. echo "Unknown profile $PROFILE"
  112. exit 1
  113. ;;
  114. esac
  115. export PROFILE
  116. case "${PROFILE}" in
  117. emqx)
  118. export SCHEMA_MOD='emqx_conf_schema'
  119. ;;
  120. emqx-enterprise)
  121. export SCHEMA_MOD='emqx_enterprise_schema'
  122. ;;
  123. esac
  124. BASE_DIR="_build/dev-run/$PROFILE"
  125. export EMQX_ETC_DIR="$BASE_DIR/etc"
  126. export EMQX_DATA_DIR="$BASE_DIR/data"
  127. export EMQX_LOG_DIR="$BASE_DIR/log"
  128. CONFIGS_DIR="$EMQX_DATA_DIR/configs"
  129. # Use your cookie so your IDE can connect to it.
  130. COOKIE="${EMQX_NODE__COOKIE:-${EMQX_NODE_COOKIE:-$(cat ~/.erlang.cookie || echo 'emqxsecretcookie')}}"
  131. mkdir -p "$EMQX_ETC_DIR" "$EMQX_DATA_DIR/patches" "$EMQX_LOG_DIR" "$CONFIGS_DIR"
  132. if [ $EKKA_EPMD -eq 1 ]; then
  133. EPMD_ARGS='-start_epmd false -epmd_module ekka_epmd'
  134. else
  135. EPMD_ARGS=''
  136. fi
  137. ## build compile the profile is it's not compiled yet
  138. prepare_erl_libs() {
  139. local profile="$1"
  140. local libs_dir="_build/${profile}/lib"
  141. local erl_libs="${ERL_LIBS:-}"
  142. local sep
  143. if [ "${SYSTEM}" = 'windows' ]; then
  144. sep=';'
  145. else
  146. sep=':'
  147. fi
  148. if [ $FORCE_COMPILE -eq 1 ] || [ ! -d "$libs_dir" ]; then
  149. make "compile-${PROFILE}"
  150. else
  151. echo "Running from code in $libs_dir"
  152. fi
  153. for app in "${libs_dir}"/*; do
  154. if [ -n "$erl_libs" ]; then
  155. erl_libs="${erl_libs}${sep}${app}"
  156. else
  157. erl_libs="${app}"
  158. fi
  159. done
  160. export ERL_LIBS="$erl_libs"
  161. }
  162. ## poorman's mustache templating
  163. mustache() {
  164. local name="$1"
  165. local value="$2"
  166. local file="$3"
  167. if [[ "$UNAME" == "Darwin" ]]; then
  168. sed -i '' "s|{{[[:space:]]*${name}[[:space:]]*}}|${value}|g" "$file"
  169. else
  170. sed -i "s|{{\s*${name}\s*}}|${value}|g" "$file"
  171. fi
  172. }
  173. ## render the merged boot conf file.
  174. ## the merge action is done before the profile is compiled
  175. render_hocon_conf() {
  176. input="apps/emqx_conf/etc/emqx.conf.all"
  177. output="$EMQX_ETC_DIR/emqx.conf"
  178. cp "$input" "$output"
  179. mustache emqx_default_erlang_cookie "$COOKIE" "$output"
  180. mustache platform_data_dir "${EMQX_DATA_DIR}" "$output"
  181. mustache platform_log_dir "${EMQX_LOG_DIR}" "$output"
  182. mustache platform_etc_dir "${EMQX_ETC_DIR}" "$output"
  183. }
  184. ## Make comma separated quoted strings
  185. make_erlang_args() {
  186. local in=("$@")
  187. local args=''
  188. for arg in "${in[@]}"; do
  189. if [ -z "$args" ]; then
  190. args="\"$arg\""
  191. else
  192. args="$args, \"$arg\""
  193. fi
  194. done
  195. echo "$args"
  196. }
  197. call_hocon() {
  198. local args erl_code
  199. args="$(make_erlang_args "$@")"
  200. erl_code="
  201. {ok, _} = application:ensure_all_started(hocon), \
  202. try
  203. mnesia_hook:module_info()
  204. catch _:_->
  205. io:format(standard_error, \"Force setting DB backend to 'mnesia', and 'role' to 'core'~n\", []),
  206. os:putenv(\"EMQX_NODE__DB_BACKEND\", \"mnesia\"),
  207. os:putenv(\"EMQX_NODE__DB_ROLE\", \"core\")
  208. end,
  209. ok = hocon_cli:main([$args]),
  210. init:stop().
  211. "
  212. erl -noshell -eval "$erl_code"
  213. }
  214. # Function to generate app.config and vm.args
  215. # sets two environment variables CONF_FILE and ARGS_FILE
  216. generate_app_conf() {
  217. ## timestamp for each generation
  218. local NOW_TIME
  219. NOW_TIME="$(date +'%Y.%m.%d.%H.%M.%S')"
  220. ## this command populates two files: app.<time>.config and vm.<time>.args
  221. ## NOTE: the generate command merges environment variables to the base config (emqx.conf),
  222. ## but does not include the cluster-override.conf and local-override.conf
  223. ## meaning, certain overrides will not be mapped to app.<time>.config file
  224. call_hocon -v -t "$NOW_TIME" -s "$SCHEMA_MOD" -c "$EMQX_ETC_DIR"/emqx.conf -d "$EMQX_DATA_DIR"/configs generate
  225. ## filenames are per-hocon convention
  226. CONF_FILE="$CONFIGS_DIR/app.$NOW_TIME.config"
  227. ARGS_FILE="$CONFIGS_DIR/vm.$NOW_TIME.args"
  228. }
  229. # apps/emqx/etc/vm.args.cloud
  230. append_args_file() {
  231. ## ensure a new line at the end
  232. echo '' >> "$ARGS_FILE"
  233. cat <<EOF >> "$ARGS_FILE"
  234. -name $EMQX_NODE_NAME
  235. -mnesia dir '"$EMQX_DATA_DIR/mnesia/$EMQX_NODE_NAME"'
  236. -stdlib restricted_shell emqx_restricted_shell
  237. +spp true
  238. +A 4
  239. +IOt 4
  240. +SDio 8
  241. -shutdown_time 30000
  242. -pa '"$EMQX_DATA_DIR/patches"'
  243. -mnesia dump_log_write_threshold 5000
  244. -mnesia dump_log_time_threshold 60000
  245. -os_mon start_disksup false
  246. EOF
  247. }
  248. # copy cert files and acl.conf to etc
  249. copy_other_conf_files() {
  250. cp -r apps/emqx/etc/certs "$EMQX_ETC_DIR"/
  251. cp apps/emqx_authz/etc/acl.conf "$EMQX_ETC_DIR"/
  252. }
  253. is_current_profile_app() {
  254. local app="$1"
  255. case "$app" in
  256. lib-ee*)
  257. if [ "$PROFILE" = 'emqx-enterprise' ]; then
  258. return 0
  259. else
  260. return 1
  261. fi
  262. ;;
  263. *emqx_telemetry*)
  264. if [ "$PROFILE" = 'emqx-enterprise' ]; then
  265. return 1
  266. else
  267. return 0
  268. fi
  269. ;;
  270. *)
  271. if [ "$PROFILE" = 'emqx' ]; then
  272. if [ -f "$app"/BSL.txt ]; then
  273. return 1
  274. else
  275. return 0
  276. fi
  277. else
  278. return 0
  279. fi
  280. ;;
  281. esac
  282. }
  283. ## apps to load
  284. apps_to_load() {
  285. local apps csl
  286. apps="$(./scripts/find-apps.sh | xargs)"
  287. csl=""
  288. for app in $apps; do
  289. if ! is_current_profile_app "$app"; then
  290. continue
  291. fi
  292. name="$(basename "$app")"
  293. if [ -z "$csl" ]; then
  294. csl="$name"
  295. else
  296. csl="$csl,$name"
  297. fi
  298. done
  299. echo "$csl"
  300. }
  301. boot() {
  302. ## Make erl command aware where to load all the beams
  303. ## this should be done before every erl command
  304. prepare_erl_libs "$PROFILE"
  305. ## make sure copy acl.conf and certs to etc before render_hocon_conf
  306. ## hocon will check rules inside acl.conf.
  307. copy_other_conf_files
  308. render_hocon_conf
  309. generate_app_conf
  310. append_args_file
  311. APPS="$(apps_to_load)"
  312. BOOT_SEQUENCE="
  313. Apps=[${APPS}],
  314. ok=lists:foreach(fun application:load/1, Apps),
  315. io:format(user, \"~nLoaded: ~p~n\", [Apps]),
  316. {ok, _} = application:ensure_all_started(emqx_machine).
  317. "
  318. # shellcheck disable=SC2086
  319. erl -name "$EMQX_NODE_NAME" \
  320. $EPMD_ARGS \
  321. -proto_dist ekka \
  322. -args_file "$ARGS_FILE" \
  323. -config "$CONF_FILE" \
  324. -s emqx_restricted_shell set_prompt_func \
  325. -eval "$BOOT_SEQUENCE"
  326. }
  327. # Generate a random id
  328. gen_tmp_node_name() {
  329. local rnd
  330. rnd="$(od -t u -N 4 /dev/urandom | head -n1 | awk '{print $2 % 1000}')"
  331. echo "remsh${rnd}-$EMQX_NODE_NAME}"
  332. }
  333. remsh() {
  334. local tmpnode
  335. tmpnode="$(gen_tmp_node_name)"
  336. # shellcheck disable=SC2086
  337. erl -name "$tmpnode" \
  338. -hidden \
  339. -setcookie "$COOKIE" \
  340. -remsh "$EMQX_NODE_NAME" \
  341. $EPMD_ARGS
  342. }
  343. ctl() {
  344. if [ -z "${PASSTHROUGH_ARGS:-}" ]; then
  345. logerr "Need at least one argument for ctl command"
  346. logerr "e.g. $0 ctl -- help"
  347. exit 1
  348. fi
  349. local tmpnode args rpc_code output result
  350. tmpnode="$(gen_tmp_node_name)"
  351. args="$(make_erlang_args "${PASSTHROUGH_ARGS[@]}")"
  352. rpc_code="
  353. case rpc:call('$EMQX_NODE_NAME', emqx_ctl, run_command, [[$args]]) of
  354. ok ->
  355. init:stop(0);
  356. Error ->
  357. io:format(\"~p~n\", [Error]),
  358. init:stop(1)
  359. end"
  360. set +e
  361. # shellcheck disable=SC2086
  362. output="$(erl -name "$tmpnode" -setcookie "$COOKIE" -hidden -noshell $EPMD_ARGS -eval "$rpc_code" 2>&1)"
  363. result=$?
  364. if [ $result -eq 0 ]; then
  365. echo -e "$output"
  366. else
  367. logerr "$output"
  368. fi
  369. exit $result
  370. }
  371. case "$COMMAND" in
  372. run)
  373. boot
  374. ;;
  375. remsh)
  376. remsh
  377. ;;
  378. ctl)
  379. ctl
  380. ;;
  381. esac