Nav apraksta

iris_helper.sh 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. #!/usr/bin/env bash
  2. ###############################################################################
  3. # Bash script to manage Docker Compose tasks for a Flask app (iriswebapp).
  4. #
  5. # Usage:
  6. # ./start_app.sh [options]
  7. #
  8. # If run with no options, the script will prompt for all inputs interactively.
  9. #
  10. # Options:
  11. # -e, --env-file <path> Path to the .env file to use
  12. # -m, --mode <dev|production> Mode to run (development or production)
  13. # -r, --reset-db Reset database (dev mode only)
  14. # -b, --build Rebuild containers (dev mode only)
  15. # -s, --services <list> Comma-separated list of services to start (dev)
  16. # e.g. "app,worker,rabbitmq,nginx,db"
  17. # -l, --logs Print Docker logs after starting (dev mode only)
  18. # -v, --version <version> Set version tags for containers (prod mode only)
  19. # e.g. "2.4.20" or "2.5.0-beta"
  20. # --init If .env doesn't exist, copy .env.model -> .env,
  21. # generate secrets, set versions to latest, pull
  22. # images, start in daemon mode, print admin pass.
  23. # -h, --help Show this help message.
  24. #
  25. ###############################################################################
  26. set -e
  27. # ---------------------------
  28. # DEFAULTS & CONSTANTS
  29. # ---------------------------
  30. DEFAULT_ENV_FILE=".env"
  31. DEV_DOCKER_FILE="docker-compose.dev.yml"
  32. PROD_DOCKER_FILE="docker-compose.yml" # Adjust if your production file is different
  33. VALID_VERSIONS=("v2.4.20" "v2.4.19" "v2.4.17" "v2.5.0-beta") # The first item is treated as the stable "latest"
  34. # If you have other containers in dev, adjust accordingly:
  35. DEV_CONTAINERS=("app" "worker" "rabbitmq" "nginx" "db")
  36. # ---------------------------
  37. # HELPER FUNCTIONS
  38. # ---------------------------
  39. print_help() {
  40. sed -n '2,27p' "$0" # prints lines 2-27 from this file (the usage block)
  41. exit 0
  42. }
  43. ask() {
  44. # Prompt for user input (yes/no). Default is 'no' if pressing Enter with no input.
  45. local prompt default reply
  46. if [ "${2:-}" = "Y" ]; then
  47. prompt="Y/n"
  48. default="Y"
  49. else
  50. prompt="y/N"
  51. default="N"
  52. fi
  53. read -r -p "$1 [$prompt] " reply
  54. if [ -z "$reply" ]; then
  55. reply=$default
  56. fi
  57. case "$reply" in
  58. [yY][eE][sS]|[yY]) return 0 ;;
  59. *) return 1 ;;
  60. esac
  61. }
  62. select_env_file() {
  63. # If ./.env is found, offer to use it. Otherwise, prompt for path.
  64. # In either case, let the user override by specifying a different path.
  65. local default="./.env"
  66. if [[ -n "$ENV_FILE" ]] && [[ -f "$ENV_FILE" ]]; then
  67. # If user already provided an ENV_FILE and it's valid, skip
  68. echo "Using .env file (from CLI arg): $ENV_FILE"
  69. return
  70. fi
  71. if [[ -f "$default" ]]; then
  72. echo "Found .env in the current directory: $default"
  73. if ask "Do you want to use $default?" "Y"; then
  74. ENV_FILE="$default"
  75. else
  76. read -r -p "Please enter the path to your .env file: " user_env_path
  77. if [[ ! -f "$user_env_path" ]]; then
  78. echo "Error: specified .env file does not exist."
  79. exit 1
  80. fi
  81. ENV_FILE="$user_env_path"
  82. fi
  83. else
  84. # If no default .env found, ask user
  85. read -r -p "No .env found. Please provide path to your .env file: " user_env_path
  86. if [[ ! -f "$user_env_path" ]]; then
  87. echo "Error: specified .env file does not exist."
  88. exit 1
  89. fi
  90. ENV_FILE="$user_env_path"
  91. fi
  92. echo "Using .env file at: $ENV_FILE"
  93. }
  94. # Cross-platform sed approach for macOS (BSD) and Linux:
  95. # Use '-i.bak' and remove the backup file afterward.
  96. set_version_in_env() {
  97. local version="$1"
  98. local envfile="$2"
  99. echo "Setting NGINX_IMAGE_TAG, DB_IMAGE_TAG, and APP_IMAGE_TAG to '$version' in $envfile"
  100. sed -i.bak "s|^NGINX_IMAGE_TAG=.*|NGINX_IMAGE_TAG=$version|" "$envfile" && rm -f "$envfile.bak"
  101. sed -i.bak "s|^DB_IMAGE_TAG=.*|DB_IMAGE_TAG=$version|" "$envfile" && rm -f "$envfile.bak"
  102. sed -i.bak "s|^APP_IMAGE_TAG=.*|APP_IMAGE_TAG=$version|" "$envfile" && rm -f "$envfile.bak"
  103. }
  104. # If we want to check or warn if specific services are already running in dev/prod
  105. manage_running_containers_for_services() {
  106. local services_list=("$@")
  107. for svc in "${services_list[@]}"; do
  108. local container_name="iriswebapp_${svc}"
  109. local container_id
  110. container_id=$(docker ps --filter "name=^${container_name}$" --format "{{.ID}}")
  111. if [[ -n "$container_id" ]]; then
  112. echo "Container '$container_name' is already running."
  113. echo "You may wish to stop/remove/restart from the command line, or proceed anyway."
  114. fi
  115. done
  116. }
  117. ###############################################################################
  118. # manage_all_running_iriswebapp_containers:
  119. # If any `iriswebapp_` containers are running, let the user apply a single
  120. # action to all of them: Stop, Remove, Restart, or Do nothing.
  121. ###############################################################################
  122. manage_all_running_iriswebapp_containers() {
  123. local running_containers
  124. running_containers=$(docker ps --filter "name=^iriswebapp_" --format "{{.Names}}")
  125. if [[ -z "$running_containers" ]]; then
  126. echo "No 'iriswebapp_' containers are currently running."
  127. return
  128. fi
  129. echo "The following 'iriswebapp_' containers are currently running:"
  130. echo "$running_containers"
  131. echo "Choose an action to apply to ALL of these containers:"
  132. echo "1) Stop all"
  133. echo "2) Stop & Remove containers"
  134. echo "3) Remove all containers and volumes (WILL DELETE ALL DATA)"
  135. echo "4) Restart all"
  136. echo "5) Do nothing"
  137. local choice
  138. read -r -p "Enter choice [1-5]: " choice
  139. if [[ -z "$choice" ]]; then
  140. choice="5" # default to "Do nothing" if empty
  141. fi
  142. local containers_arr=( $running_containers )
  143. case "$choice" in
  144. 1)
  145. echo "Stopping all containers..."
  146. docker stop "${containers_arr[@]}"
  147. ;;
  148. 2)
  149. echo "Stopping and removing all containers..."
  150. docker stop "${containers_arr[@]}"
  151. docker rm "${containers_arr[@]}"
  152. ;;
  153. 3)
  154. # This is a destructive action, so ask for confirmation
  155. if ! ask "Are you sure you want to remove all containers and volumes?" "N"; then
  156. echo "Aborting."
  157. return
  158. fi
  159. echo "Removing all containers and volumes..."
  160. docker compose down -v
  161. ;;
  162. 4)
  163. echo "Restarting all containers..."
  164. docker restart "${containers_arr[@]}"
  165. ;;
  166. 5)
  167. echo "Skipping..."
  168. ;;
  169. *)
  170. echo "Invalid choice: '$choice'. Skipping..."
  171. ;;
  172. esac
  173. }
  174. ###############################################################################
  175. # init_env():
  176. # If .env doesn't exist, copy .env.model -> .env, set versions to latest,
  177. # generate secrets, pull images, start in daemon, print admin password,
  178. # and optionally show logs.
  179. ###############################################################################
  180. init_env() {
  181. # If user specified a custom env-file location, respect it;
  182. # otherwise default to ./env. The user wants to do an init only if .env is missing
  183. # so let's define envfile=ENV_FILE or use the default if not set:
  184. local envfile="${ENV_FILE:-$DEFAULT_ENV_FILE}"
  185. if [[ -f "$envfile" ]]; then
  186. echo "'.env' already exists at '$envfile'. Skipping --init."
  187. return
  188. fi
  189. # Ensure there's a .env.model to copy from
  190. if [[ ! -f ".env.model" ]]; then
  191. echo "Error: .env.model not found in the current directory. Cannot proceed with --init."
  192. exit 1
  193. fi
  194. echo "Initializing a new .env from .env.model..."
  195. cp .env.model "$envfile"
  196. # 1) Set versions to the stable "latest"
  197. local latest_version="${VALID_VERSIONS[0]}"
  198. echo "Setting version tags to $latest_version..."
  199. set_version_in_env "$latest_version" "$envfile"
  200. # 2) Generate random secrets
  201. # - 32 hex chars => openssl rand -hex 16 => 16 bytes => 32 hex chars
  202. # - 16 hex chars => openssl rand -hex 8 => 8 bytes => 16 hex chars
  203. echo "Generating random secrets..."
  204. local pg_pass="$(openssl rand -hex 16)" # 32 hex
  205. local pg_admin_pass="$(openssl rand -hex 16)" # 32 hex
  206. local iris_secret_key="$(openssl rand -hex 16)" # 32 hex
  207. local iris_sec_salt="$(openssl rand -hex 16)" # 32 hex
  208. local iris_adm_pass="$(openssl rand -hex 8)" # 16 hex
  209. # 3) Insert them into the .env
  210. sed -i.bak "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$pg_pass|" "$envfile" && rm -f "$envfile.bak"
  211. sed -i.bak "s|^POSTGRES_ADMIN_PASSWORD=.*|POSTGRES_ADMIN_PASSWORD=$pg_admin_pass|" "$envfile" && rm -f "$envfile.bak"
  212. sed -i.bak "s|^IRIS_SECRET_KEY=.*|IRIS_SECRET_KEY=$iris_secret_key|" "$envfile" && rm -f "$envfile.bak"
  213. sed -i.bak "s|^IRIS_SECURITY_PASSWORD_SALT=.*|IRIS_SECURITY_PASSWORD_SALT=$iris_sec_salt|" "$envfile" && rm -f "$envfile.bak"
  214. sed -i.bak "s|^IRIS_ADM_PASSWORD=.*|IRIS_ADM_PASSWORD=$iris_adm_pass|" "$envfile" && rm -f "$envfile.bak"
  215. echo "Secrets generated and inserted into $envfile"
  216. # 4) Pull images (production by default)
  217. echo "Pulling Docker images (production stack)..."
  218. docker compose --env-file "$envfile" -f "$PROD_DOCKER_FILE" pull
  219. # 5) Start in daemon mode
  220. echo "Starting containers in daemon mode..."
  221. docker compose --env-file "$envfile" -f "$PROD_DOCKER_FILE" up -d
  222. # 6) Print the admin password
  223. echo "IRIS_ADM_PASSWORD has been set to: $iris_adm_pass"
  224. # 7) Ask if we want to tail logs
  225. if ask "Do you want to tail logs now?" "N"; then
  226. docker compose --env-file "$envfile" -f "$PROD_DOCKER_FILE" logs -f
  227. fi
  228. echo "Initialization complete."
  229. }
  230. # ---------------------------
  231. # PARSE ARGUMENTS
  232. # ---------------------------
  233. ENV_FILE=""
  234. MODE=""
  235. RESET_DB=false
  236. REBUILD=false
  237. SERVICES=""
  238. PRINT_LOGS=false
  239. VERSION=""
  240. INIT_MODE=false
  241. while [[ $# -gt 0 ]]; do
  242. case $1 in
  243. -e|--env-file)
  244. ENV_FILE="$2"
  245. shift; shift
  246. ;;
  247. -m|--mode)
  248. MODE="$2"
  249. shift; shift
  250. ;;
  251. -r|--reset-db)
  252. RESET_DB=true
  253. shift
  254. ;;
  255. -b|--build)
  256. REBUILD=true
  257. shift
  258. ;;
  259. -s|--services)
  260. SERVICES="$2"
  261. shift; shift
  262. ;;
  263. -l|--logs)
  264. PRINT_LOGS=true
  265. shift
  266. ;;
  267. -v|--version)
  268. VERSION="$2"
  269. shift; shift
  270. ;;
  271. --init)
  272. INIT_MODE=true
  273. shift
  274. ;;
  275. -h|--help)
  276. print_help
  277. ;;
  278. *)
  279. echo "Unknown option: $1"
  280. print_help
  281. ;;
  282. esac
  283. done
  284. # ---------------------------
  285. # If --init is set, run init_env and skip the rest
  286. # ---------------------------
  287. if $INIT_MODE; then
  288. manage_all_running_iriswebapp_containers
  289. init_env
  290. exit 0
  291. fi
  292. # ---------------------------
  293. # STEP 1: Check existing iriswebapp_ containers
  294. # ---------------------------
  295. manage_all_running_iriswebapp_containers
  296. # ---------------------------
  297. # STEP 2: INTERACTIVE MODE SELECTION
  298. # ---------------------------
  299. if [[ -z "$MODE" ]]; then
  300. echo "Select mode: "
  301. echo "1) Development"
  302. echo "2) Production"
  303. read -r -p "Enter choice [1 or 2]: " mode_choice
  304. if [[ -z "$mode_choice" ]]; then
  305. mode_choice="1"
  306. fi
  307. case "$mode_choice" in
  308. 1) MODE="development" ;;
  309. 2) MODE="production" ;;
  310. *) echo "Invalid choice. Exiting." ; exit 1 ;;
  311. esac
  312. fi
  313. # ---------------------------
  314. # STEP 3: ENV FILE SELECTION
  315. # ---------------------------
  316. select_env_file
  317. # ---------------------------
  318. # DEVELOPMENT MODE LOGIC
  319. # ---------------------------
  320. if [[ "$MODE" == "development" ]]; then
  321. if ! $RESET_DB; then
  322. if ask "Do you want to reset the database (docker compose down -v)?" "N"; then
  323. RESET_DB=true
  324. fi
  325. fi
  326. if $RESET_DB; then
  327. echo "Bringing down containers and removing volumes..."
  328. docker compose --file "$DEV_DOCKER_FILE" --env-file "$ENV_FILE" down -v
  329. fi
  330. if ! $REBUILD; then
  331. if ask "Do you want to rebuild containers?" "N"; then
  332. REBUILD=true
  333. fi
  334. fi
  335. if $REBUILD; then
  336. echo "Which services do you want to rebuild? Available: ${DEV_CONTAINERS[*]}"
  337. echo "Enter 'all' for all containers, or space-separated list: e.g. 'app worker'."
  338. read -r -p "Services to rebuild: " rebuild_services
  339. if [[ -z "$rebuild_services" ]]; then
  340. rebuild_services="all"
  341. fi
  342. if [[ "$rebuild_services" == "all" ]]; then
  343. rebuild_services="${DEV_CONTAINERS[*]}"
  344. fi
  345. echo "Building containers: $rebuild_services"
  346. docker compose --file "$DEV_DOCKER_FILE" --env-file "$ENV_FILE" build $rebuild_services
  347. fi
  348. if [[ -z "$SERVICES" ]]; then
  349. echo "Which services do you want to start? Options: ${DEV_CONTAINERS[*]}"
  350. echo "Enter 'all' for all, or space-separated list: e.g. 'app worker'."
  351. read -r -p "Services to start: " start_services
  352. if [[ -z "$start_services" ]]; then
  353. start_services="all"
  354. fi
  355. if [[ "$start_services" == "all" ]]; then
  356. start_services="${DEV_CONTAINERS[*]}"
  357. fi
  358. SERVICES="$start_services"
  359. fi
  360. IFS=' ' read -r -a services_array <<< "$SERVICES"
  361. manage_running_containers_for_services "${services_array[@]}"
  362. echo "Starting services: $SERVICES"
  363. docker compose --file "$DEV_DOCKER_FILE" --env-file "$ENV_FILE" up -d $SERVICES
  364. if ! $PRINT_LOGS; then
  365. if ask "Do you want to tail logs?" "N"; then
  366. PRINT_LOGS=true
  367. fi
  368. fi
  369. if $PRINT_LOGS; then
  370. echo "Tailing logs. Press Ctrl+C to stop."
  371. docker compose --file "$DEV_DOCKER_FILE" --env-file "$ENV_FILE" logs -f
  372. fi
  373. # ---------------------------
  374. # PRODUCTION MODE LOGIC
  375. # ---------------------------
  376. elif [[ "$MODE" == "production" ]]; then
  377. if [[ -z "$VERSION" ]]; then
  378. echo "Which version do you want to run?"
  379. for i in "${!VALID_VERSIONS[@]}"; do
  380. echo "$((i+1))) ${VALID_VERSIONS[$i]}"
  381. done
  382. echo "$(( ${#VALID_VERSIONS[@]} + 1 )) ) Custom version"
  383. read -r -p "Enter choice (1-${#VALID_VERSIONS[@]}, or ${#VALID_VERSIONS[@]}+1 for custom): " version_choice
  384. if [[ -z "$version_choice" ]]; then
  385. version_choice=1
  386. fi
  387. if (( version_choice >= 1 && version_choice <= ${#VALID_VERSIONS[@]} )); then
  388. VERSION="${VALID_VERSIONS[$((version_choice-1))]}"
  389. else
  390. read -r -p "Enter your custom version tag: " custom_version
  391. VERSION="$custom_version"
  392. fi
  393. fi
  394. set_version_in_env "$VERSION" "$ENV_FILE"
  395. ALL_PROD_SERVICES=("app" "worker" "rabbitmq" "nginx" "db")
  396. manage_running_containers_for_services "${ALL_PROD_SERVICES[@]}"
  397. echo "Starting production with version: $VERSION"
  398. docker compose --env-file "$ENV_FILE" -f "$PROD_DOCKER_FILE" up -d
  399. echo "Production containers are up."
  400. else
  401. echo "Invalid mode selected: $MODE"
  402. exit 1
  403. fi
  404. exit 0