github.com/containers/podman/v5@v5.1.0-rc1/hack/podman-registry (about) 1 #! /bin/bash 2 # 3 # podman-registry - start/stop/monitor a local instance of registry:2 4 # 5 ME=$(basename $0) 6 7 ############################################################################### 8 # BEGIN defaults 9 10 PODMAN_REGISTRY_IMAGE=quay.io/libpod/registry:2.8 11 12 PODMAN_REGISTRY_USER= 13 PODMAN_REGISTRY_PASS= 14 PODMAN_REGISTRY_PORT= 15 16 # Podman binary to run 17 PODMAN=${PODMAN:-$(dirname $0)/../bin/podman} 18 19 # END defaults 20 ############################################################################### 21 # BEGIN help messages 22 23 missing=" argument is missing; see $ME -h for details" 24 usage="Usage: $ME [options] [start|stop|ps|logs] 25 26 $ME manages a local instance of a container registry. 27 28 When called to start a registry, $ME will pull an image 29 into a local temporary directory, create an htpasswd, start the 30 registry, and dump a series of environment variables to stdout: 31 32 \$ $ME start 33 PODMAN_REGISTRY_IMAGE=\"docker.io/library/registry:2.8\" 34 PODMAN_REGISTRY_PORT=\"5050\" 35 PODMAN_REGISTRY_USER=\"userZ3RZ\" 36 PODMAN_REGISTRY_PASS=\"T8JVJzKrcl4p6uT\" 37 38 Expected usage, therefore, is something like this in a script 39 40 eval \$($ME start) 41 42 To stop the registry, you will need to know the port number: 43 44 $ME -P \$PODMAN_REGISTRY_PORT stop 45 46 Override the default image, port, user, password with: 47 48 -i IMAGE registry image to pull (default: $PODMAN_REGISTRY_IMAGE) 49 -u USER registry user (default: random) 50 -p PASS password for registry user (default: random) 51 -P PORT port to bind to (on 127.0.0.1) (default: random, 5000-5999) 52 53 Other options: 54 55 -h display usage message 56 " 57 58 die () { 59 echo "$ME: $*" >&2 60 exit 1 61 } 62 63 # END help messages 64 ############################################################################### 65 # BEGIN option processing 66 67 while getopts "i:u:p:P:hv" opt; do 68 case "$opt" in 69 i) PODMAN_REGISTRY_IMAGE=$OPTARG ;; 70 u) PODMAN_REGISTRY_USER=$OPTARG ;; 71 p) PODMAN_REGISTRY_PASS=$OPTARG ;; 72 P) PODMAN_REGISTRY_PORT=$OPTARG ;; 73 h) echo "$usage"; exit 0;; 74 v) verbose=1 ;; 75 \?) echo "Run '$ME -h' for help" >&2; exit 1;; 76 esac 77 done 78 shift $((OPTIND-1)) 79 80 # END option processing 81 ############################################################################### 82 # BEGIN helper functions 83 84 function random_string() { 85 local length=${1:-10} 86 87 head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length 88 } 89 90 function podman() { 91 if [ -z "${PODMAN_REGISTRY_PORT}" ]; then 92 die "podman port undefined; please invoke me with -P PORT" 93 fi 94 95 if [ -z "${PODMAN_REGISTRY_WORKDIR}" ]; then 96 PODMAN_REGISTRY_WORKDIR=${TMPDIR:-/tmp}/podman-registry-${PODMAN_REGISTRY_PORT} 97 if [ ! -d ${PODMAN_REGISTRY_WORKDIR} ]; then 98 die "$ME: directory does not exist: ${PODMAN_REGISTRY_WORKDIR}" 99 fi 100 fi 101 102 # Reset $PODMAN, so ps/logs/stop use same args as the initial start 103 PODMAN="$(<${PODMAN_REGISTRY_WORKDIR}/PODMAN)" 104 105 ${PODMAN} --root ${PODMAN_REGISTRY_WORKDIR}/root \ 106 --runroot ${PODMAN_REGISTRY_WORKDIR}/runroot \ 107 "$@" 108 } 109 110 ############### 111 # must_pass # Run a command quietly; abort with error on failure 112 ############### 113 function must_pass() { 114 local log=${PODMAN_REGISTRY_WORKDIR}/log 115 116 "$@" &> $log 117 if [ $? -ne 0 ]; then 118 echo "$ME: Command failed: $*" >&2 119 cat $log >&2 120 121 # If we ever get here, it's a given that the registry is not running. 122 # Clean up after ourselves. 123 ${PODMAN} unshare rm -rf ${PODMAN_REGISTRY_WORKDIR} 124 exit 1 125 fi 126 } 127 128 ################### 129 # wait_for_port # Returns once port is available on localhost 130 ################### 131 function wait_for_port() { 132 local port=$1 # Numeric port 133 134 local host=127.0.0.1 135 local _timeout=5 136 137 # Wait 138 while [ $_timeout -gt 0 ]; do 139 { exec {unused_fd}<> /dev/tcp/$host/$port; } &>/dev/null && return 140 sleep 1 141 _timeout=$(( $_timeout - 1 )) 142 done 143 144 die "Timed out waiting for port $port" 145 } 146 147 # END helper functions 148 ############################################################################### 149 # BEGIN action processing 150 151 function do_start() { 152 # If called without a port, assign a random one in the 5xxx range 153 if [ -z "${PODMAN_REGISTRY_PORT}" ]; then 154 for port in $(shuf -i 5000-5999);do 155 if ! { exec {unused_fd}<> /dev/tcp/127.0.0.1/$port; } &>/dev/null; then 156 PODMAN_REGISTRY_PORT=$port 157 break 158 fi 159 done 160 fi 161 162 PODMAN_REGISTRY_WORKDIR=${TMPDIR:-/tmp}/podman-registry-${PODMAN_REGISTRY_PORT} 163 if [ -d ${PODMAN_REGISTRY_WORKDIR} ]; then 164 die "$ME: directory exists: ${PODMAN_REGISTRY_WORKDIR} (another registry might already be running on this port)" 165 fi 166 167 # Randomly-generated username and password, if none given on command line 168 if [ -z "${PODMAN_REGISTRY_USER}" ]; then 169 PODMAN_REGISTRY_USER="user$(random_string 4)" 170 fi 171 if [ -z "${PODMAN_REGISTRY_PASS}" ]; then 172 PODMAN_REGISTRY_PASS=$(random_string 15) 173 fi 174 175 # For the next few commands, die on any error 176 set -e 177 178 mkdir -p ${PODMAN_REGISTRY_WORKDIR} 179 180 # Preserve initial podman path & args, so all subsequent invocations 181 # of this script are consistent with the first one. 182 echo "$PODMAN" >${PODMAN_REGISTRY_WORKDIR}/PODMAN 183 184 local AUTHDIR=${PODMAN_REGISTRY_WORKDIR}/auth 185 mkdir -p $AUTHDIR 186 187 # Pull registry image, but into a separate container storage 188 mkdir -p ${PODMAN_REGISTRY_WORKDIR}/root 189 mkdir -p ${PODMAN_REGISTRY_WORKDIR}/runroot 190 191 set +e 192 193 # Give it three tries, to compensate for flakes 194 podman pull ${PODMAN_REGISTRY_IMAGE} &>/dev/null || 195 podman pull ${PODMAN_REGISTRY_IMAGE} &>/dev/null || 196 must_pass podman pull ${PODMAN_REGISTRY_IMAGE} 197 198 # Registry image needs a cert. Self-signed is good enough. 199 local CERT=$AUTHDIR/domain.crt 200 must_pass openssl req -newkey rsa:4096 -nodes -sha256 \ 201 -keyout ${AUTHDIR}/domain.key -x509 -days 2 \ 202 -out ${AUTHDIR}/domain.crt \ 203 -subj "/C=US/ST=Foo/L=Bar/O=Red Hat, Inc./CN=localhost" 204 205 # Store credentials where container will see them. We can't run 206 # this one via must_pass because we need its stdout. 207 htpasswd -Bbn ${PODMAN_REGISTRY_USER} ${PODMAN_REGISTRY_PASS} \ 208 > $AUTHDIR/htpasswd 209 if [ $? -ne 0 ]; then 210 rm -rf ${PODMAN_REGISTRY_WORKDIR} 211 die "Command failed: htpasswd" 212 fi 213 214 # In case someone needs to debug 215 echo "${PODMAN_REGISTRY_USER}:${PODMAN_REGISTRY_PASS}" \ 216 > $AUTHDIR/htpasswd-plaintext 217 218 # Run the registry container. 219 must_pass podman run --quiet -d \ 220 -p ${PODMAN_REGISTRY_PORT}:5000 \ 221 --name registry \ 222 -v $AUTHDIR:/auth:Z \ 223 -e "REGISTRY_AUTH=htpasswd" \ 224 -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ 225 -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \ 226 -e "REGISTRY_HTTP_TLS_CERTIFICATE=/auth/domain.crt" \ 227 -e "REGISTRY_HTTP_TLS_KEY=/auth/domain.key" \ 228 ${PODMAN_REGISTRY_IMAGE} 229 230 # Confirm that registry started and port is active 231 wait_for_port $PODMAN_REGISTRY_PORT 232 233 # Dump settings. Our caller will use these to access the registry. 234 for v in IMAGE PORT USER PASS; do 235 echo "PODMAN_REGISTRY_${v}=\"$(eval echo \$PODMAN_REGISTRY_${v})\"" 236 done 237 } 238 239 240 function do_stop() { 241 podman stop registry 242 podman rm -f registry 243 244 # Use straight podman, not our alias function, to avoid 'overlay: EBUSY' 245 cmd="rm -rf ${PODMAN_REGISTRY_WORKDIR}" 246 if [[ $(id -u) -eq 0 ]]; then 247 $cmd 248 else 249 ${PODMAN} unshare $cmd 250 fi 251 } 252 253 254 function do_ps() { 255 podman ps -a 256 } 257 258 259 function do_logs() { 260 podman logs registry 261 } 262 263 # END action processing 264 ############################################################################### 265 # BEGIN command-line processing 266 267 # First command-line arg must be an action 268 action=${1?ACTION$missing} 269 shift 270 271 case "$action" in 272 start) do_start ;; 273 stop) do_stop ;; 274 ps) do_ps ;; 275 logs) do_logs ;; 276 *) die "Unknown action '$action'; must be start / stop / ps / logs" ;; 277 esac 278 279 # END command-line processing 280 ############################################################################### 281 282 exit 0