podmanRun 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. #!/usr/bin/env bash
  2. # shellcheck disable=SC1090,SC2004
  3. #
  4. # This script/function is a wrapper for podman run/exec that will automatically handle container
  5. # creation, removal, and reuse
  6. #
  7. # MIT License
  8. # Copyright (c) 2020 Bryan Roessler
  9. # Permission is hereby granted, free of charge, to any person obtaining a copy
  10. # of this software and associated documentation files (the "Software"), to deal
  11. # in the Software without restriction, including without limitation the rights
  12. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. # copies of the Software, and to permit persons to whom the Software is
  14. # furnished to do so, subject to the following conditions:
  15. #
  16. # The above copyright notice and this permission notice shall be included in all
  17. # copies or substantial portions of the Software.
  18. #
  19. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  25. # SOFTWARE.
  26. podmanRun() {
  27. _printHelpAndExit() {
  28. if [[ -z $_debug ]]; then
  29. cat <<-'EOF'
  30. USAGE
  31. podmanRun [-m MODE] [-o OPTIONS] [COMMANDS [ARGS]...] [--help] [--debug]
  32. COMMANDS
  33. COMMANDS to run in the container
  34. OPTIONS
  35. --mode, -m MODE
  36. 1. recreate (remove container if it already exists and create a new one)
  37. 2. persistent (reuse existing container if it exists)
  38. --options, -o OPTIONS
  39. OPTIONS to pass to podman run/exec
  40. Can be passed multiple times to concatenate
  41. Will be split on whitespace
  42. Final option should be the name of the container image
  43. --debug, -d
  44. Print debugging
  45. --help, -h
  46. Print this help message and exit
  47. EXAMPLES
  48. Note: IDE examples are using Atom Build package placeholders.
  49. Run an ephemeral PHP webserver container using the current directory as webroot:
  50. podmanRun -o "-p=8000:80 --name=php_script -v=$PWD:/var/www/html:z php:7.3-apache"
  51. Run an ephemeral PHP webserver container using the current directory as webroot using IDE:
  52. podmanRun -o "-p=8000:80 --name=php_{FILE_ACTIVE_NAME_BASE} \
  53. -v={FILE_ACTIVE_PATH}:/var/www/html:z php:7.3-apache"
  54. Run an ephemeral bash script:
  55. podmanRun -o "--name=bash_script -v=$PWD:$PWD:z -w=$PWD debian:testing" ./script.sh
  56. Run an ephemeral bash script using IDE:
  57. podmanRun -o "--name=bash_{FILE_ACTIVE_NAME_BASE}" \
  58. -o "-v={FILE_ACTIVE_PATH}:{FILE_ACTIVE_PATH}:z"
  59. -o "-w={FILE_ACTIVE_PATH}" \
  60. -o "debian:testing" \
  61. {FILE_ACTIVE} arg1 arg2
  62. EOF
  63. fi
  64. # Exit using passed exit code
  65. [[ -z $1 ]] && exit 0 || exit "$1"
  66. }
  67. # Parse input
  68. _parseInput() {
  69. declare -g _mode _debug
  70. declare -ga _opts_arr _cmds_arr
  71. local INPUT
  72. # Use getopt to print help
  73. if INPUT=$(getopt -o +m:o:dh -l mode:,options:,debug,help -- "$@"); then
  74. eval set -- "$INPUT"
  75. while true; do
  76. case "$1" in
  77. --mode|-m)
  78. shift
  79. _mode="$1"
  80. ;;
  81. --options|-o)
  82. shift
  83. #
  84. _opts_arr+=("$1")
  85. ;;
  86. --help|-h)
  87. _printHelpAndExit 0
  88. ;;
  89. --debug|-d)
  90. _debug="1"
  91. echo "Debugging on!"
  92. ;;
  93. --)
  94. shift
  95. break
  96. ;;
  97. esac
  98. shift
  99. done
  100. else
  101. err "Incorrect options provided!"
  102. _printHelpAndExit 1
  103. fi
  104. # Load remaining arguments into the commands array
  105. shift $((OPTIND - 1))
  106. _cmds_arr=("$@")
  107. # Set default mode
  108. [[ -z $_mode ]] && _mode="recreate"
  109. # Allow mode numbers
  110. [[ "$_mode" == "1" ]] && _mode="recreate"
  111. [[ "$_mode" == "2" ]] && _mode="persistent"
  112. # Sanity check
  113. [[ ! "$_mode" =~ ^(recreate|persistent)$ ]] && err "Bad --mode" && _printHelpAndExit 1
  114. # Split options on whitespace
  115. # This assumes that podman options are properly formatted
  116. # https://unix.stackexchange.com/a/519917/382539
  117. readarray -td' ' _opts_arr < <(printf '%s' "${_opts_arr[*]}")
  118. debug "_opts_arr:" "${_opts_arr[@]}"
  119. debug "_cmds_arr:" "${_cmds_arr[@]}"
  120. }
  121. debug() { [[ -n $_debug ]] && echo "debug: " "$@"; }
  122. err() { echo "error: $*" >&2; }
  123. _setContainerName() {
  124. debug "${FUNCNAME[0]}"
  125. export _cname
  126. local index _name
  127. # Get user-specified container name by parsing the options array for --name
  128. for ((index=0; index <= ${#_opts_arr[@]}; index++)); do
  129. if [[ "${_opts_arr[index]}" == "--name" ]]; then
  130. _name="${_opts_arr[index+1]}"
  131. elif [[ "${_opts_arr[index]}" =~ ^--name ]]; then
  132. _name="${_opts_arr[index]}"
  133. _name="${_name#--name=}"
  134. fi
  135. done
  136. _sanitize() {
  137. local i="$*"; i="${i// /}" && i="${i//[^a-zA-Z0-9_]/}" && i="${i,,}"; echo "$i" ;
  138. }
  139. # If no --name is specified, then generate one using the opts and cmds arrays
  140. if [[ -z $_name ]]; then
  141. _cname="$(_sanitize "${_opts_arr[*]}${_cmds_arr[*]}")"
  142. else
  143. _cname="$(_sanitize "$_name")"
  144. [[ "$_name" != "$_cname" ]] && err "You must provide a valid --name" && exit 1
  145. fi
  146. debug "_cname: $_cname"
  147. }
  148. _containerExists() { debug "${FUNCNAME[0]} $1"; podman container exists "$1"; }
  149. _removeContainer() { debug "${FUNCNAME[0]} $1"; _containerExists "$1" && podman rm -vf "$1"; }
  150. _runContainer() {
  151. debug "${FUNCNAME[0]} $1"
  152. if _containerExists "$1"; then
  153. debug "podman exec $1 sh -c" "${_cmds_arr[@]}"
  154. podman exec "$1" "${_cmds_arr[@]}"
  155. else
  156. debug "podman run" "${_opts_arr[@]}" "${_cmds_arr[@]}"
  157. podman run "${_opts_arr[@]}" "${_cmds_arr[@]}"
  158. fi
  159. }
  160. __main() {
  161. # Get input
  162. _parseInput "$@"
  163. # Set _cname (container name)
  164. _setContainerName
  165. # Remove container if necessary
  166. [[ "$_mode" == "recreate" ]] && _containerExists "$_cname" && _removeContainer "$_cname"
  167. # Run or execute container
  168. _runContainer "$_cname"
  169. # Cleanup more reliably
  170. [[ "$_mode" == "recreate" ]] && _containerExists "$_cname" && _removeContainer "$_cname"
  171. }
  172. # Allow this function to be executed directly
  173. __main "$@"
  174. exit $?
  175. }
  176. # Allow script to be called directly
  177. if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
  178. podmanRun "$@"
  179. fi