run.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. #!/usr/bin/env bash
  2. ## This script runs CT (and necessary dependencies) in docker container(s)
  3. set -euo pipefail
  4. # ensure dir
  5. cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")/../.."
  6. help() {
  7. echo
  8. echo "-h|--help: To display this usage info"
  9. echo "--app lib_dir/app_name: For which app to run start docker-compose, and run common tests"
  10. echo "--console: Start EMQX in console mode but do not run test cases"
  11. echo "--attach: Attach to the Erlang docker container without running any test case"
  12. echo "--stop: Stop running containers for the given app"
  13. echo "--only-up: Only start the testbed but do not run CT"
  14. echo "--keep-up: Keep the testbed running after CT"
  15. echo "--ci: Set this flag in GitHub action to enforce no tests are skipped"
  16. echo "--: If any, all args after '--' are passed to rebar3 ct"
  17. echo " otherwise it runs the entire app's CT"
  18. }
  19. set +e
  20. if docker compose version; then
  21. DC='docker compose'
  22. elif command -v docker-compose; then
  23. DC='docker-compose'
  24. else
  25. echo 'Neither "docker compose" or "docker-compose" are available, stop.'
  26. exit 1
  27. fi
  28. set -e
  29. WHICH_APP='novalue'
  30. CONSOLE='no'
  31. KEEP_UP='no'
  32. ONLY_UP='no'
  33. ATTACH='no'
  34. STOP='no'
  35. IS_CI='no'
  36. SQLSERVER_ODBC_REQUEST='no'
  37. SNOWFLAKE_ODBC_REQUEST='no'
  38. UP='up'
  39. while [ "$#" -gt 0 ]; do
  40. case $1 in
  41. -h|--help)
  42. help
  43. exit 0
  44. ;;
  45. --app)
  46. WHICH_APP="${2%/}"
  47. shift 2
  48. ;;
  49. --only-up)
  50. ONLY_UP='yes'
  51. shift 1
  52. ;;
  53. --keep-up)
  54. KEEP_UP='yes'
  55. shift 1
  56. ;;
  57. --attach)
  58. ATTACH='yes'
  59. shift 1
  60. ;;
  61. --stop)
  62. STOP='yes'
  63. shift 1
  64. ;;
  65. --console)
  66. CONSOLE='yes'
  67. shift 1
  68. ;;
  69. --ci)
  70. IS_CI='yes'
  71. UP='up --quiet-pull'
  72. shift 1
  73. ;;
  74. --)
  75. shift 1
  76. REBAR3CT="$*"
  77. shift $#
  78. ;;
  79. *)
  80. echo "unknown option $1"
  81. exit 1
  82. ;;
  83. esac
  84. done
  85. if [ "${WHICH_APP}" = 'novalue' ]; then
  86. echo "must provide --app arg"
  87. help
  88. exit 1
  89. fi
  90. if [ ! -d "${WHICH_APP}" ]; then
  91. echo "must provide an existing path for --app arg"
  92. help
  93. exit 1
  94. fi
  95. ERLANG_CONTAINER='erlang'
  96. DOCKER_CT_ENVS_FILE="${WHICH_APP}/docker-ct"
  97. if [ -f "${WHICH_APP}/BSL.txt" ]; then
  98. if [ -n "${PROFILE:-}" ] && [ "${PROFILE}" != 'emqx-enterprise' ]; then
  99. echo "bad_profile: PROFILE=${PROFILE} will not work for app ${WHICH_APP}"
  100. exit 1
  101. fi
  102. fi
  103. if [ -z "${PROFILE+x}" ]; then
  104. case "${WHICH_APP}" in
  105. apps/emqx)
  106. export PROFILE='emqx-enterprise'
  107. ;;
  108. apps/emqx_bridge)
  109. export PROFILE='emqx-enterprise'
  110. ;;
  111. # emqx_connector test suite is using kafka bridge which is only available in emqx-enterprise
  112. apps/emqx_connector)
  113. export PROFILE='emqx-enterprise'
  114. ;;
  115. apps/emqx_dashboard)
  116. export PROFILE='emqx-enterprise'
  117. ;;
  118. apps/emqx_rule_engine)
  119. export PROFILE='emqx-enterprise'
  120. ;;
  121. # emqx_auth_http has scram:http support which is only available in emqx-enterprise
  122. apps/emqx_auth_http)
  123. export PROFILE='emqx-enterprise'
  124. ;;
  125. apps/emqx_fdb*)
  126. export PROFILE='emqx-platform'
  127. ;;
  128. apps/emqx_ds_fdb_backend)
  129. export PROFILE='emqx-platform'
  130. ;;
  131. apps/emqx_event_history)
  132. export PROFILE='emqx-platform'
  133. ;;
  134. apps/*)
  135. if [[ -f "${WHICH_APP}/BSL.txt" ]]; then
  136. export PROFILE='emqx-enterprise'
  137. else
  138. export PROFILE='emqx'
  139. fi
  140. ;;
  141. *)
  142. export PROFILE="${PROFILE:-emqx}"
  143. ;;
  144. esac
  145. fi
  146. if [ -f "$DOCKER_CT_ENVS_FILE" ]; then
  147. # shellcheck disable=SC2002
  148. CT_DEPS="$(cat "$DOCKER_CT_ENVS_FILE" | xargs)"
  149. fi
  150. CT_DEPS="${ERLANG_CONTAINER} ${CT_DEPS:-}"
  151. FILES=( )
  152. for dep in ${CT_DEPS}; do
  153. case "${dep}" in
  154. erlang)
  155. FILES+=( '.ci/docker-compose-file/docker-compose.yaml' )
  156. ;;
  157. toxiproxy)
  158. FILES+=( '.ci/docker-compose-file/docker-compose-toxiproxy.yaml' )
  159. ;;
  160. influxdb)
  161. FILES+=( '.ci/docker-compose-file/docker-compose-influxdb-tcp.yaml'
  162. '.ci/docker-compose-file/docker-compose-influxdb-tls.yaml' )
  163. ;;
  164. mongo)
  165. FILES+=( '.ci/docker-compose-file/docker-compose-mongo-single-tcp.yaml'
  166. '.ci/docker-compose-file/docker-compose-mongo-single-tls.yaml' )
  167. ;;
  168. mongo_rs_sharded)
  169. FILES+=( '.ci/docker-compose-file/docker-compose-mongo-replicaset-tcp.yaml'
  170. '.ci/docker-compose-file/docker-compose-mongo-sharded-tcp.yaml' )
  171. ;;
  172. redis)
  173. FILES+=( '.ci/docker-compose-file/docker-compose-redis-single-tcp.yaml'
  174. '.ci/docker-compose-file/docker-compose-redis-single-tls.yaml'
  175. '.ci/docker-compose-file/docker-compose-redis-sentinel-tcp.yaml'
  176. '.ci/docker-compose-file/docker-compose-redis-sentinel-tls.yaml' )
  177. ;;
  178. redis_cluster)
  179. FILES+=( '.ci/docker-compose-file/docker-compose-redis-cluster-tcp.yaml'
  180. '.ci/docker-compose-file/docker-compose-redis-cluster-tls.yaml' )
  181. ;;
  182. mysql)
  183. FILES+=( '.ci/docker-compose-file/docker-compose-mysql-tcp.yaml'
  184. '.ci/docker-compose-file/docker-compose-mysql-tls.yaml' )
  185. ;;
  186. pgsql)
  187. FILES+=( '.ci/docker-compose-file/docker-compose-pgsql-tcp.yaml'
  188. '.ci/docker-compose-file/docker-compose-pgsql-tls.yaml' )
  189. ;;
  190. kafka)
  191. FILES+=( '.ci/docker-compose-file/docker-compose-kafka.yaml' )
  192. ;;
  193. tdengine)
  194. FILES+=( '.ci/docker-compose-file/docker-compose-tdengine-restful.yaml' )
  195. ;;
  196. clickhouse)
  197. FILES+=( '.ci/docker-compose-file/docker-compose-clickhouse.yaml' )
  198. ;;
  199. dynamo)
  200. FILES+=( '.ci/docker-compose-file/docker-compose-dynamo.yaml' )
  201. ;;
  202. rocketmq)
  203. FILES+=( '.ci/docker-compose-file/docker-compose-rocketmq.yaml'
  204. '.ci/docker-compose-file/docker-compose-rocketmq-ssl.yaml' )
  205. ;;
  206. cassandra)
  207. FILES+=( '.ci/docker-compose-file/docker-compose-cassandra.yaml' )
  208. ;;
  209. sqlserver)
  210. SQLSERVER_ODBC_REQUEST='yes'
  211. FILES+=( '.ci/docker-compose-file/docker-compose-sqlserver.yaml' )
  212. ;;
  213. opents)
  214. FILES+=( '.ci/docker-compose-file/docker-compose-opents.yaml' )
  215. ;;
  216. pulsar)
  217. FILES+=( '.ci/docker-compose-file/docker-compose-pulsar.yaml' )
  218. ;;
  219. oracle)
  220. FILES+=( '.ci/docker-compose-file/docker-compose-oracle.yaml' )
  221. ;;
  222. iotdb)
  223. FILES+=( '.ci/docker-compose-file/docker-compose-iotdb.yaml' )
  224. ;;
  225. rabbitmq)
  226. FILES+=( '.ci/docker-compose-file/docker-compose-rabbitmq.yaml' )
  227. ;;
  228. minio)
  229. FILES+=( '.ci/docker-compose-file/docker-compose-minio-tcp.yaml'
  230. '.ci/docker-compose-file/docker-compose-minio-tls.yaml' )
  231. ;;
  232. gcp_emulator)
  233. FILES+=( '.ci/docker-compose-file/docker-compose-gcp-emulator.yaml' )
  234. ;;
  235. hstreamdb)
  236. FILES+=( '.ci/docker-compose-file/docker-compose-hstreamdb.yaml' )
  237. ;;
  238. kinesis)
  239. FILES+=( '.ci/docker-compose-file/docker-compose-kinesis.yaml' )
  240. ;;
  241. greptimedb)
  242. FILES+=( '.ci/docker-compose-file/docker-compose-greptimedb.yaml' )
  243. ;;
  244. ldap)
  245. FILES+=( '.ci/docker-compose-file/docker-compose-ldap.yaml' )
  246. ;;
  247. otel)
  248. FILES+=( '.ci/docker-compose-file/docker-compose-otel.yaml' )
  249. ;;
  250. elasticsearch)
  251. FILES+=( '.ci/docker-compose-file/docker-compose-elastic-search-tls.yaml' )
  252. ;;
  253. azurite)
  254. FILES+=( '.ci/docker-compose-file/docker-compose-azurite.yaml' )
  255. ;;
  256. couchbase)
  257. FILES+=( '.ci/docker-compose-file/docker-compose-couchbase.yaml' )
  258. ;;
  259. kdc)
  260. FILES+=( '.ci/docker-compose-file/docker-compose-kdc.yaml' )
  261. ;;
  262. datalayers)
  263. FILES+=( '.ci/docker-compose-file/docker-compose-datalayers-tcp.yaml'
  264. '.ci/docker-compose-file/docker-compose-datalayers-tls.yaml' )
  265. ;;
  266. snowflake)
  267. if [[ -z "${SNOWFLAKE_ACCOUNT_ID:-}" ]]; then
  268. echo "Snowflake environment requested, but SNOWFLAKE_ACCOUNT_ID is undefined"
  269. echo "Will NOT install Snowflake's ODBC drivers"
  270. else
  271. SNOWFLAKE_ODBC_REQUEST='yes'
  272. fi
  273. ;;
  274. schema-registry)
  275. FILES+=( '.ci/docker-compose-file/docker-compose-confluent-schema-registry.yaml' )
  276. ;;
  277. *)
  278. echo "unknown_ct_dependency $dep"
  279. exit 1
  280. ;;
  281. esac
  282. done
  283. if [ "$SQLSERVER_ODBC_REQUEST" = 'yes' ]; then
  284. INSTALL_SQLSERVER_ODBC="./scripts/install-msodbc-driver.sh"
  285. else
  286. INSTALL_SQLSERVER_ODBC="echo 'msodbc driver not requested'"
  287. fi
  288. if [ "$SNOWFLAKE_ODBC_REQUEST" = 'yes' ]; then
  289. INSTALL_SNOWFLAKE_ODBC="./scripts/install-snowflake-driver.sh"
  290. else
  291. INSTALL_SNOWFLAKE_ODBC="echo 'snowflake driver not requested'"
  292. fi
  293. for file in "${FILES[@]}"; do
  294. DC="$DC -f $file"
  295. done
  296. DOCKER_USER="$(id -u)"
  297. export DOCKER_USER
  298. TTY=''
  299. if [[ -t 1 ]]; then
  300. TTY='-t'
  301. fi
  302. # ensure directory with secrets is created by current user before running compose
  303. mkdir -p /tmp/emqx-ci/emqx-shared-secret
  304. if [ "$STOP" = 'no' ]; then
  305. # some left-over log file has to be deleted before a new docker-compose up
  306. rm -f '.ci/docker-compose-file/redis/*.log'
  307. set +e
  308. # shellcheck disable=2086 # no quotes for UP
  309. $DC $UP -d --build --remove-orphans
  310. RESULT=$?
  311. if [ $RESULT -ne 0 ]; then
  312. mkdir -p _build/test/logs
  313. LOG='_build/test/logs/docker-compose.log'
  314. echo "Dumping docker-compose log to $LOG"
  315. $DC logs --no-color --timestamps > "$LOG"
  316. exit 1
  317. fi
  318. set -e
  319. fi
  320. if [ "$DOCKER_USER" != "root" ]; then
  321. # the user must exist inside the container for `whoami` to work
  322. docker exec -i $TTY -u root:root \
  323. -e "SFACCOUNT=${SFACCOUNT:-myorg-myacc}" \
  324. "$ERLANG_CONTAINER" bash -c \
  325. "useradd --uid $DOCKER_USER -M -d / emqx || true && \
  326. mkdir -p /.cache /.hex /.mix && \
  327. chown $DOCKER_USER /.cache /.hex /.mix && \
  328. openssl rand -base64 -hex 16 > /.erlang.cookie && \
  329. chown $DOCKER_USER /.erlang.cookie && \
  330. chmod 0400 /.erlang.cookie && \
  331. chown -R $DOCKER_USER /var/lib/secret && \
  332. $INSTALL_SQLSERVER_ODBC && \
  333. $INSTALL_SNOWFLAKE_ODBC" || true
  334. fi
  335. if [ "$ONLY_UP" = 'yes' ]; then
  336. exit 0
  337. fi
  338. set +e
  339. if [ "$STOP" = 'yes' ]; then
  340. $DC down --remove-orphans
  341. elif [ "$ATTACH" = 'yes' ]; then
  342. docker exec -it "$ERLANG_CONTAINER" bash
  343. elif [ "$CONSOLE" = 'yes' ]; then
  344. docker exec -e PROFILE="$PROFILE" -i $TTY "$ERLANG_CONTAINER" bash -c "make run"
  345. else
  346. if [ -z "${REBAR3CT:-}" ]; then
  347. docker exec -e IS_CI="$IS_CI" \
  348. -e PROFILE="$PROFILE" \
  349. -e SUITEGROUP="${SUITEGROUP:-}" \
  350. -e ENABLE_COVER_COMPILE="${ENABLE_COVER_COMPILE:-}" \
  351. -e CT_COVER_EXPORT_PREFIX="${CT_COVER_EXPORT_PREFIX:-}" \
  352. -i $TTY "$ERLANG_CONTAINER" \
  353. bash -c "BUILD_WITHOUT_QUIC=1 make ${WHICH_APP}-ct"
  354. else
  355. # this is an ad-hoc run
  356. docker exec -e IS_CI="$IS_CI" \
  357. -e PROFILE="$PROFILE" \
  358. -i $TTY "$ERLANG_CONTAINER" \
  359. bash -c "./rebar3 ct $REBAR3CT"
  360. fi
  361. RESULT=$?
  362. if [ "$RESULT" -ne 0 ]; then
  363. LOG='_build/test/logs/docker-compose.log'
  364. echo "Dumping docker-compose log to $LOG"
  365. $DC logs --no-color --timestamps > "$LOG"
  366. fi
  367. if [ "$KEEP_UP" != 'yes' ]; then
  368. $DC down
  369. fi
  370. exit "$RESULT"
  371. fi