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