#!/usr/bin/env bash # # This script/function is a wrapper for podman run/exec that will automatically handle container # creation, removal, and reuse # # MIT License # Copyright (c) 2020 Bryan Roessler # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. podmanRun() { _printHelpAndExit() { if [[ -z $_debug ]]; then cat <<-'EOF' USAGE podmanRun [-m MODE] [-o OPTIONS] [COMMANDS [ARGS]...] [--help] [--debug] COMMANDS COMMANDS to run in the container OPTIONS --mode, -m MODE 1. recreate (default) (remove container if it already exists and create a new one) 2. persistent (reuse existing container if it exists) --options, -o OPTIONS OPTIONS to pass to podman run/exec Can be passed multiple times to concatenate Will be split on whitespace Final option should be the name of the container image --debug, -d Print debugging --help, -h Print this help message and exit EXAMPLES Note: IDE examples are using Atom Build package placeholders. Run an ephemeral PHP webserver container using the current directory as webroot: podmanRun -o "-p=8000:80 --name=php_script -v=$PWD:/var/www/html:z php:7.3-apache" Run an ephemeral PHP webserver container using the current directory as webroot using IDE: podmanRun -o "-p=8000:80 --name=php_{FILE_ACTIVE_NAME_BASE} \ -v={FILE_ACTIVE_PATH}:/var/www/html:z php:7.3-apache" Run an ephemeral bash script: podmanRun -o "--name=bash_script -v=$PWD:$PWD:z -w=$PWD debian:testing" ./script.sh Run an ephemeral bash script using IDE: podmanRun -o "--name=bash_{FILE_ACTIVE_NAME_BASE}" \ -o "-v={FILE_ACTIVE_PATH}:{FILE_ACTIVE_PATH}:z" \ -o "-w={FILE_ACTIVE_PATH}" \ -o "debian:testing" \ {FILE_ACTIVE} arg1 arg2 EOF fi # Exit using passed exit code [[ -z $1 ]] && exit 0 || exit "$1" } # Parse input _parseInput() { declare -g _mode _debug declare -ga _opts_arr _cmds_arr local INPUT # Use getopt to print help if INPUT=$(getopt -o +m:o:dh -l mode:,options:,debug,help -- "$@"); then eval set -- "$INPUT" while true; do case "$1" in --mode|-m) shift _mode="$1" ;; --options|-o) shift # _opts_arr+=("$1") ;; --help|-h) _printHelpAndExit 0 ;; --debug|-d) _debug="1" echo "Debugging on!" ;; --) shift break ;; esac shift done else err "Incorrect options provided!" _printHelpAndExit 1 fi # Load remaining arguments into the commands array shift $((OPTIND - 1)) _cmds_arr=("$@") # Set default mode [[ -z $_mode ]] && _mode="recreate" # Allow mode numbers [[ "$_mode" == "1" ]] && _mode="recreate" [[ "$_mode" == "2" ]] && _mode="persistent" # Sanity check [[ ! "$_mode" =~ ^(recreate|persistent)$ ]] && err "Bad --mode" && _printHelpAndExit 1 # Split options on whitespace # This assumes that podman options are properly formatted # https://unix.stackexchange.com/a/519917/382539 readarray -td' ' _opts_arr < <(printf '%s' "${_opts_arr[*]}") debug "_opts_arr:" "${_opts_arr[@]}" debug "_cmds_arr:" "${_cmds_arr[@]}" } debug() { [[ -n $_debug ]] && echo "debug: " "$@"; } err() { echo "error: $*" >&2; } _setContainerName() { debug "${FUNCNAME[0]}" export _cname local index _name # Get user-specified container name by parsing the options array for --name for ((index=0; index <= ${#_opts_arr[@]}; index++)); do if [[ "${_opts_arr[index]}" == "--name" ]]; then _name="${_opts_arr[index+1]}" elif [[ "${_opts_arr[index]}" =~ ^--name ]]; then _name="${_opts_arr[index]}" _name="${_name#--name=}" fi done _sanitize() { local i="$*"; i="${i// /}" && i="${i//[^a-zA-Z0-9_]/}" && i="${i,,}"; echo "$i" ; } # If no --name is specified, then generate one using the opts and cmds arrays if [[ -z $_name ]]; then _cname="$(_sanitize "${_opts_arr[*]}${_cmds_arr[*]}")" else _cname="$(_sanitize "$_name")" [[ "$_name" != "$_cname" ]] && err "You must provide a valid --name" && exit 1 fi debug "_cname: $_cname" } _containerExists() { debug "${FUNCNAME[0]} $1"; podman container exists "$1"; } _removeContainer() { debug "${FUNCNAME[0]} $1"; _containerExists "$1" && podman rm -vf "$1"; } _runContainer() { debug "${FUNCNAME[0]} $1" if _containerExists "$1"; then debug "podman exec $1 sh -c" "${_cmds_arr[@]}" podman exec "$1" "${_cmds_arr[@]}" else debug "podman run" "${_opts_arr[@]}" "${_cmds_arr[@]}" podman run "${_opts_arr[@]}" "${_cmds_arr[@]}" fi } __main() { # Get input _parseInput "$@" # Set _cname (container name) _setContainerName # Remove container if necessary [[ "$_mode" == "recreate" ]] && _containerExists "$_cname" && _removeContainer "$_cname" # Run or execute container _runContainer "$_cname" # Cleanup more reliably (usually not necessary) #[[ "$_mode" == "recreate" ]] && _containerExists "$_cname" && _removeContainer "$_cname" } # Allow this function to be executed directly __main "$@" exit $? } # Allow script to be called directly if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then podmanRun "$@" fi