#!/bin/sh all="" env="" port="" force= log() { logger -I $PPID -t container -p local1.notice -- "$*" } # Unpacks a given oci-archive.tar[.gz] in the current directory. Sanity # checks, at least one index.json in the top-level dir of the archive. # If there are more index files, this function does not handle them. unpack_archive() { image=$1 name=$2 # Supported transports for load and create case "$image" in oci:*) # Unpacked OCI image file=${image#oci:} ;; oci-archive:*) # Packed OCI image, .tar or .tar.gz format file=${image#oci-archive:} ;; *) # docker://*, docker-archive:*, or URL echo "$image" return 0 ;; esac if [ ! -e "$file" ]; then if [ -e "/var/lib/conatainers/oci/$file" ]; then file="/var/lib/conatainers/oci/$file" elif [ -e "/lib/oci/$file" ]; then file="/lib/oci/$file" else log "Error: cannot find OCI archive $file in search path." exit 1 fi fi if [ -d "$file" ]; then index=$(find "$file" -name index.json) if [ -z "$index" ]; then log "Error: cannot find index.json in OCI image $file" exit 1 fi else index=$(tar tf "$file" |grep index.json) if [ -z "$index" ]; then log "Error: invalid OCI archive, cannot find index.json in $file" exit 1 fi [ -n "$quiet" ] || log "Extracting OCI archive $file ..." tar xf "$file" || (log "Error: failed unpacking $file in $(pwd)"; exit 1) fi dir=$(dirname "$index") [ -n "$quiet" ] || log "Loading OCI image $dir ..." podman load -qi "$dir" >/dev/null # Rename image from podman default $dir:latest if [ -n "$name" ]; then podman tag "$dir" "$name" >/dev/null podman rmi "$dir" >/dev/null else name=$dir fi echo "$name" } running() { run=$(podman inspect "$1" 2>/dev/null |jq .[].State.Running) [ "$run" = "true" ] && return 0 return 1 } # shellcheck disable=SC2086 create() { name=$1 image=$2 shift 2 if [ -z "$name" ] || [ -z "$image" ]; then echo "Usage:" echo " container create NAME IMAGE" exit 1 fi # Unpack and load docker-archive/oci/oci-archive, returning image # name, or return docker:// URL for download. image=$(unpack_archive "$image") if [ -z "$logging" ]; then logging="--log-driver k8s-file --log-opt path=/run/containers/$name.fifo" fi args="$args --replace --quiet --cgroup-parent=containers $caps" args="$args --restart=$restart --systemd=false --tz=local $privileged" args="$args $ro $vol $mount $hostname $entrypoint $env $port $logging" pidfn=/run/container:${name}.pid [ -n "$quiet" ] || log "---------------------------------------" [ -n "$quiet" ] || log "Got name: $name image: $image" [ -n "$quiet" ] || log "Got networks: $network" if [ -n "$network" ]; then for net in $network; do args="$args --net=$net" done for srv in $dns; do args="$args --dns=$srv" done for domain in $search; do args="$args --dns-search=$domain" done else args="$args --network=none" fi # shellcheck disable=SC2048 log "Calling podman create --name $name --conmon-pidfile=$pidfn $args $image $*" if podman create --name "$name" --conmon-pidfile="$pidfn" $args "$image" $*; then [ -n "$quiet" ] || log "Successfully created container $name from $image" rm -f "/run/containers/env/${name}.env" [ -n "$manual" ] || start "$name" exit 0 fi log "Error: failed creating container $name, please check the configuration." exit 1 } delete() { name=$1 image=$2 if [ -z "$name" ]; then echo "Usage:" echo " container delete NAME" exit 1 fi podman rm -vif "$name" >/dev/null 2>&1 [ -n "$quiet" ] || log "Container $name has been removed." } waitfor() { timeout=$2 while [ ! -f "$1" ]; do _=$((timeout -= 1)) if [ $timeout -le 0 ]; then log "Timeout waiting for $1, aborting!" exit 1 fi sleep 1; done } start() { name=$1 if running "$name"; then [ -n "$quiet" ] || echo "$name: already running." return fi initctl -bq cond set "container:$name" # Real work is done by wrap() courtesy of finit sysv emulation } stop() { name=$1 if ! running "$name"; then [ -n "$quiet" ] || echo "$name: not running." return fi initctl -bq cond clr "container:$name" # Real work is done by wrap() courtesy of finit sysv emulation } wrap() { name=$1 cmd=$2 podman "$cmd" "$name" } # Removes network $1 from all containers netwrm() { net=$1 for c in $(podman ps $all --format "{{.Names}}"); do for n in $(podman inspect "$c" |jq -r '.[].NetworkSettings.Networks | keys[]'); do if [ "$n" = "$net" ]; then podman network disconnect $force "$n" "$c" >/dev/null fi done done } # Schedule restart of (any) container using network $1 to activate network changes netrestart() { net=$1 for c in $(podman ps $all --format "{{.Names}}"); do for n in $(podman inspect "$c" |jq -r '.[].NetworkSettings.Networks | keys[]'); do if [ "$n" = "$net" ]; then initctl -nbq touch "container@$c" fi done done } usage() { cat </dev/null) for n in $nets; do if [ "$network" = "$n" ]; then pid=$(echo "$json" | jq .[].State.Pid) echo "$pid" exit 0 fi done done fi ;; help) usage ;; load) url=$1 name=$2 # shellcheck disable=SC2086 if echo "$url" | grep -q "://"; then file=$(basename "$url") curl -k $creds -Lo "$file" "$url" else file="$url" fi # shellcheck disable=SC2086 name=$(unpack_archive "$file" $name) # Show resulting image(s) matching $name if [ -n "$name" ]; then podman images -n "$name" else exit 1 fi ;; ls | list) cmd=$1 [ -n "$cmd" ] && shift case $cmd in image*) podman images $all --format "{{.Repository}}:{{.Tag}}" ;; oci) find /lib/oci /var/lib/containers/oci -type f 2>/dev/null ;; *) podman ps $all --format "{{.Names}}" ;; esac ;; pull) podman pull "$@" ;; remove) podman rmi $all $force -i "$1" ;; run) img=$1 cmd=$2 [ -n "$port" ] || port="-P" if [ -n "$cmd" ]; then shift 2 [ -n "$detach" ] || echo "Starting $img ENTRYPOINT $cmd :: use Ctrl-p Ctrl-q to detach" podman run -it --rm $detach $port --entrypoint="$cmd" "$img" "$@" else [ -n "$detach" ] || echo "Starting $img :: use Ctrl-p Ctrl-q to detach" podman run -it --rm $detach $port "$img" fi ;; save) name=$1 file=$2 if echo "$file" | grep -q ".gz"; then file=${file%%.gz} gzip=true fi if ! echo "$file" | grep -q ".tar"; then file=${file}.tar gzip=true fi podman save -o "$file" "$name" if [ -s "$file" ] && [ -n "$gzip" ]; then gzip "$file" fi ;; shell) podman exec -it "$1" sh -l ;; show) cmd=$1 [ -n "$cmd" ] && shift case $cmd in image*) if [ -n "$simple" ]; then podman images $all --format "{{.Names}} {{.Size}}" \ | sed 's/\[\(.*\)\] /\1 /g' \ | awk '{ printf "%-60s %s %s\n", $1, $2, $3}' else podman images $all fi ;; volume*) printf "%-20s CONTAINER\n" "VOLUME" for v in $(podman volume ls --format "{{.Name}}"); do printf "%-20s" "$v" podman ps -a --filter volume="$v" --format '{{.Names}}' | sed 's/^/ /' done ;; *) if [ -n "$simple" ]; then podman ps $all --format "{{.ID}} {{.Names}} {{.Image}}" \ | awk '{ printf "%s %-30s %s\n", $1, $2, $3}' else podman ps $all fi ;; esac ;; start) if [ -n "$name" ]; then wrap "$name" start elif [ -n "$1" ]; then start "$1" else usage exit 1 fi ;; restart) if [ -n "$name" ]; then wrap "$name" restart elif [ -n "$1" ]; then cmd=$1 name=$2 if [ "$cmd" = "network" ] && [ -n "$name" ]; then netrestart "$name" else name=$1 stop "$name" timeout=20 while running "$name"; do _=$((timeoute -= 1)) if [ $timeout -le 0 ]; then log "Timeout waiting for container $1 to stop before restarting it." exit 1 fi sleep 1 done start "$name" fi else usage exit 1 fi ;; stop) if [ -n "$name" ]; then wrap "$name" stop elif [ -n "$1" ]; then stop "$1" else usage exit 1 fi ;; stat*) podman stats -i 2 ;; upgrade) img=$(podman inspect "$1" | jq -r .[].ImageName) if [ -z "$img" ]; then echo "No such container ($1), or invalid ImageName. Cannot upgrade." exit 1; fi if echo "$img" | grep -Eq '^localhost/'; then # Likely an OCI archive, or local directory, assume user has updated image. file=$(awk '{s=$NF} END{print s}' "/var/lib/containers/active/${1}.sh") echo "Upgrading container ${1} with local archive: $file ..." else podman stop "$1" podman pull "$img" || (echo "Failed fetching $img, check your network (settings)."; exit 1) fi "/var/lib/containers/active/${1}.sh" || (echo "Failed recreating container $1"; exit 1) ;; volume) cmd=$1 [ -n "$cmd" ] && shift case $cmd in prune) podman volume $force prune ;; *) false ;; esac ;; *) usage exit 1 ;; esac