podmanRun 7.1 KB

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