github.com/verrazzano/verrazzano@v1.7.1/tools/scripts/vz-registry-image-helper.sh (about)

     1  #!/bin/bash
     2  # Copyright (c) 2021, 2022, Oracle and/or its affiliates.
     3  # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     4  #
     5  # Script to allow users to load Verrazzano images into a private Docker registry.
     6  # - Using the Verrazzano BOM, pull/tag/push images into a target registry/repository
     7  # - Using a local cache of Verrazzano image tarballs, load into the local Docker registry and push to the remote registry/repo
     8  # - Clean up the local registry
     9  #
    10  set -o pipefail
    11  set -o errtrace
    12  
    13  SCRIPT_DIR=$(cd $(dirname "$0"); pwd -P)
    14  . ${SCRIPT_DIR}/bom_utils.sh
    15  
    16  # variables
    17  TO_REGISTRY=
    18  TO_REPO=
    19  USELOCAL=0
    20  IMAGES_DIR=./images
    21  INCREMENTAL_CLEAN=false
    22  CLEAN_ALL=false
    23  DRY_RUN=false
    24  INCLUDE_COMPONENTS=
    25  EXCLUDE_COMPONENTS=
    26  LIST_IMAGES_ONLY=
    27  
    28  function usage() {
    29    ec=${1:-0}
    30    echo """
    31  This script is to help pushing Verrazzano container images into a private repository from their default locations,
    32  or to generate a tarball of Verrazzano container images.
    33  
    34  usage:
    35  
    36    $0 -t <docker-registry> [-l <archive-path> -r <repository-path>]
    37    $0 -t <docker-registry> [-b <path> -r <repository-path>]
    38    $0 -f <tarfile-location> [ -l <images-location> ]
    39    $0 -c [-b <path> | -l <archive-path>]
    40  
    41  Options:
    42   -t <docker-registry>   Target docker registry to push to, e.g., iad.ocir.io
    43   -r <repository-path>   Repository name/prefix for each image, e.g \"path/to/my/image\"; if not specified the default will be used according to the BOM
    44   -b <path>              Bill of materials (BOM) of Verrazzano components; if not specified, defaults to ./verrazzano-bom.json
    45   -l <archive-dir>       Use the specified imagesDir to load local Docker image tarballs from instead of pulling from
    46   -i <component>         Include the specified component in the operation (can be repeated for multiple components)
    47   -f <tarfile_dir>           The name of the directory that will be used to save Docker image tarballs locally
    48   -e <component>         Exclude the specified component from the operation (can be repeated for multiple components)
    49   -c                     Clean all local images/tags
    50   -z                     Incrementally clean each local image after it has been successfully pushed
    51   -d                     Dry-run only, do not perform Docker operations
    52   -o                     List components
    53   -m <outputfile>        List images to output file, do not process them
    54  
    55  Examples:
    56  
    57    # Loads all images into registry 'myreg.io' using the default repository paths for each image in the BOM
    58    $0 -t myreg.io
    59  
    60    # Loads all Verrazzano images into registry 'myreg.io' in repository 'myrepo/user1'
    61    $0 -t myreg.io -r 'myrepo/user1'
    62  
    63    # Loads all Verrazzano images into registry 'myreg.io' in repository 'myrepo/user1' using the BOM /path/to/my-bom.json
    64    # and removes the locally downloaded image after a successful push
    65    $0 -c -t myreg.io -r 'myrepo/user1' -b /path/to/my-bom.json
    66  
    67    # Loads all Docker tarball images in the imagesDir /path/to/exploded/tarball into registry 'myreg.io' in repository 'myrepo'
    68    $0 -t myreg.io -l /path/to/exploded/tarball -r myrepo
    69  
    70    # Do a dry-run with the tarball location /path/to/exploded/tarball with registry 'myreg.io' in repository 'myrepo'
    71    $0 -d -t myreg.io -l /path/to/exploded/tarball -r myrepo
    72  
    73    # List out the Verrazzano components in the default BOM file
    74    $0 -d -t myreg.io -o
    75  
    76    # List out the Verrazzano components in the specified BOM file
    77    $0 -d -t myreg.io -o -b /path/to/my-bom.json
    78  
    79    # Processes all Verrazzano images *except* verrazzano-platform-operator and verrazzano-application-operator
    80    $0 -t myreg.io -r 'myrepo/user1' -b /path/to/my-bom.json -e verrazzano-platform-operator -e verrazzano-application-operator
    81  
    82    # Processes *only* the images for the Verrazzano components cert-manager and istio
    83    $0 -t myreg.io -r 'myrepo/user1' -b /path/to/my-bom.json -i cert-manager -i istio
    84  
    85    # Pull and save Verrazzano images locally as tarfiles to the specified location
    86    $0 -f /tmp/myvzimages -b /path/to/my-bom.json
    87  """
    88    exit ${ec}
    89  }
    90  
    91  function exit_trap() {
    92    local rc=$?
    93    local lc="$BASH_COMMAND"
    94  
    95    if [[ $rc -ne 0 ]]; then
    96      echo "Command [$lc] exited with code [$rc]"
    97    fi
    98  }
    99  
   100  trap exit_trap EXIT
   101  
   102  function run_docker() {
   103    local fatal=false
   104    if [ "${DRY_RUN}" != "true" ]; then
   105      tmpfile=$(mktemp -t vz-helper-docker-err.XXXXXX)
   106      docker $* 2>${tmpfile}
   107      local result=$?
   108      local denied=$(egrep -i "(Anonymous|permission_denied|403.*forbidden)" ${tmpfile})
   109      if [ -n "$denied" ]; then
   110         echo """
   111  
   112  Permission error from Docker:
   113  
   114  $(cat ${tmpfile})
   115  
   116  Please log into the target registry and try again.
   117  
   118        """
   119        fatal=true
   120      elif [ "${result}" != "0" ]; then
   121        echo "An error occurred running docker command:"
   122        cat ${tmpfile}
   123      fi
   124    fi
   125    rm ${tmpfile}
   126    if [ "${fatal}" == "true" ]; then
   127      # Fatal error occurred, exit immeditately
   128      exit 1
   129    fi
   130    return ${result}
   131  }
   132  
   133  # Wrapper for Docker pull
   134  function load() {
   135    local archive=$1
   136    echo ">> Loading archive: ${archive}"
   137    run_docker load -i "${archive}"
   138  }
   139  
   140  # Wrapper for Docker pull
   141  function pull() {
   142    local image=$1
   143    echo ">> Pulling image: ${image}"
   144    run_docker pull "${image}"
   145  }
   146  
   147  # Wrapper for Docker tag
   148  function tag() {
   149    local from_image=$1
   150    local to_image=$2
   151    echo ">> Tagging image: ${from_image} to ${to_image}"
   152    run_docker tag "${from_image}" "${to_image}"
   153  }
   154  
   155  # Wrapper for Docker push
   156  function push() {
   157    local image=$1
   158    echo ">> Pushing image: ${image}"
   159    run_docker push "${image}"
   160  }
   161  
   162  # Wrapper for Docker save
   163  function save() {
   164    local tarFile=$1
   165    local image=$2
   166    echo ">> Saving image: ${image} to ${tarFile}"
   167    run_docker save -o "${tarFile}" "${image}" 
   168  }
   169  
   170  # Wrapper for Docker rmi
   171  function remove() {
   172    local images=$*
   173    echo ">> Removing images: $*"
   174    run_docker rmi ${images}
   175  }
   176  
   177  # Perform requirements checks and validate arguments
   178  function check() {
   179    if [ "${INCREMENTAL_CLEAN}" == "true" ] && [ "${CLEAN_ALL}" == "true" ]; then
   180      echo "Incremental clean and clean-all both specified, these can not be used together"
   181      usage 1
   182    fi
   183  
   184    if [ "$USELOCAL" -ne 0 ]; then
   185      echo "Use local images specified, ignoring -b if set"
   186      if [ -z "${IMAGES-DIR}" ]; then
   187        echo "Use local images specified, but no location specified"
   188        usage 1
   189      fi
   190    fi
   191  
   192    if [ -z "${TO_REGISTRY}" ] && [ -z "${TARDIR}" ]; then
   193      echo "Target registry not specified!"
   194      usage 1
   195    fi
   196  
   197    echo "Checking if docker is installed ..."
   198    if ! docker --help >/dev/null; then
   199      echo "[ERROR] docker is not installed, please install docker"
   200      usage 1
   201    fi
   202  
   203    echo "Checking if jq is installed ..."
   204    if ! jq --help >/dev/null; then
   205      echo "[ERROR] jq is not install ... please install jq"
   206      usage 1
   207    fi
   208  
   209  }
   210  
   211  # Process an image
   212  # - Do pull (if necessary) and tag, and then push to the new registry
   213  # - attempts up to 10 times before failing
   214  # - Cleans up the locally downloaded/loaded image when done
   215  function process_image() {
   216    local from_image=$1
   217    local to_image=$2
   218  
   219    if [ "${CLEAN_ALL}" == "true" ]; then
   220      remove ${to_image} ${from_image}
   221      return 0
   222    fi
   223  
   224    echo "Processing image ${from_image} to ${to_image}"
   225    local success=false
   226    for i in {1..10}; do
   227      # Only pull the image if we are not looking at local images
   228      if [ "$USELOCAL" -eq 0 ]; then
   229        pull "${from_image}"
   230        if [[ $? -ne 0 ]]; then
   231          sleep 30
   232          continue
   233        fi
   234      fi
   235  
   236      tag ${from_image} ${to_image}
   237      if [[ $? -ne 0 ]]; then
   238        sleep 30
   239        continue
   240      fi
   241  
   242      # push
   243      push ${to_image}
   244      if [[ $? -ne 0 ]]; then
   245        sleep 30
   246        continue
   247      fi
   248  
   249      success=true
   250      break
   251    done
   252  
   253    if [ "${INCREMENTAL_CLEAN}" == "true" ]; then
   254      remove ${to_image} ${from_image}
   255    fi
   256  
   257    if [[ "${success}" == "false" ]]; then
   258      echo "[ERROR] Failed to manage image [${from_image}]"
   259      exit 1
   260    fi
   261  }
   262  
   263  # Get the target repo if overridden, otherwise return the provided default
   264  function get_target_repo() {
   265    local default_repo=$1
   266    local target_repo=${TO_REPO}
   267    if [ -z "${target_repo}" ]; then
   268      target_repo=${default_repo}
   269    fi
   270    echo "${target_repo}"
   271  }
   272  
   273  # Main driver for processing images from a locally downloaded set of tarballs
   274  function process_local_archives() {
   275    # Loop through tar files
   276    echo "Using local image downloads"
   277    for file in ${IMAGES_DIR}/*.tar; do
   278      if [ ! -e ${file} ]; then
   279        echo "Image tar file ${file} does not exist!"
   280        exit 1
   281      fi
   282      # load tar file into the local Docker registry
   283      load $file
   284  
   285      # Build up the image name and target image names, and do a tag/push
   286      local from_image=$(tar xOvf $file manifest.json | jq -r '.[0].RepoTags[0]')
   287      local from_image_name=$(basename $from_image)
   288      local from_repository=$(dirname $from_image | cut -d \/ -f 2-)
   289  
   290      local target_repo=$(get_target_repo ${from_repository})
   291      local to_image=${TO_REGISTRY}/${target_repo}/${from_repository}/${from_image_name}
   292      if [ ! -z "$LIST_IMAGES_ONLY" ]; then
   293        echo ${to_image} >> $LIST_IMAGES_ONLY
   294      else
   295        process_image ${from_image} ${to_image}
   296      fi
   297    done
   298  }
   299  
   300  # Returns 0 if the specified component is in the excludes list, 1 otherwise
   301  function is_component_excluded() {
   302      local seeking=$1
   303      local excludes=(${EXCLUDE_COMPONENTS})
   304      local in=1
   305      for comp in "${excludes[@]}"; do
   306          if [[ "$comp" == "$seeking" ]]; then
   307              in=0
   308              break
   309          fi
   310      done
   311      return $in
   312  }
   313  
   314  # Main driver for pulling/tagging/pushing images based on the Verrazzano bill of materials (BOM)
   315  function process_images_from_bom() {
   316    # Loop through registry components
   317    echo "Using image registry ${BOM_FILE}"
   318  
   319    local components=(${INCLUDE_COMPONENTS})
   320    if [ "${#components[@]}" == "0" ]; then
   321      components=($(list_components))
   322    fi
   323  
   324    echo "Components: ${components[*]}"
   325  
   326    for component in "${components[@]}"; do
   327      if is_component_excluded ${component} ; then
   328        echo "Component ${component} excluded"
   329        continue
   330      fi
   331      local subcomponents=($(list_subcomponent_names ${component}))
   332      for subcomp in "${subcomponents[@]}"; do
   333        echo "Processing images for Verrazzano subcomponent ${component}/${subcomp}"
   334        # Load the repository and base image names for the component
   335        #local from_repository=$(get_subcomponent_repo $component $subcomp)
   336        local image_names=$(list_subcomponent_images $component $subcomp)
   337  
   338        # for each image in the subcomponent list:
   339        # - resolve the BOM registry location for the image
   340        # - resolve the BOM repository for the image
   341        # - build the from/to locations for the image
   342        # - call process_image to pull/tag/push the image
   343        for base_image in ${image_names}; do
   344          local from_registry=$(resolve_image_registry_from_bom $component $subcomp $base_image)
   345          local from_image_prefix=${from_registry}
   346          local from_repository=$(resolve_image_repo_from_bom $component $subcomp $base_image)
   347          if [ -n "${from_repository}" ] && [ "${from_repository}" != "null" ]; then
   348            from_image_prefix=${from_image_prefix}/${from_repository}
   349          fi
   350  
   351          local to_image_prefix=${TO_REGISTRY}
   352          if [ -n "${TO_REPO}" ]; then
   353            to_image_prefix=${to_image_prefix}/${TO_REPO}
   354          fi
   355          to_image_prefix=${to_image_prefix}/${from_repository}
   356  
   357          # Build up the image name and target image name, and do a pull/tag/push
   358          local from_image=${from_image_prefix}/${base_image}
   359          local to_image=${to_image_prefix}/${base_image}
   360          if [ ! -z "$LIST_IMAGES_ONLY" ]; then
   361            echo ${to_image} >> $LIST_IMAGES_ONLY
   362          else
   363            process_image ${from_image} ${to_image}
   364          fi
   365        done
   366      done
   367    done
   368  }
   369  
   370  # Main driver for pulling/saving images based on the Verrazzano bill of materials (BOM)
   371  function pull_and_save_images() {
   372    imagesDir=$1
   373  
   374    echo "Creating Verrazzano images tar file ${tarfile}, using ${imagesDir} to store images locally"
   375  
   376    if [ ! -e ${imagesDir} ]; then
   377      echo "Creating image imagesDir ${imagesDir}"
   378      mkdir -p ${imagesDir}
   379    fi
   380  
   381    # Loop through registry components
   382    echo "Using image registry ${BOM_FILE}"
   383    local components=($(list_components))
   384    for component in "${components[@]}"; do
   385      local subcomponent_names=$(list_subcomponent_names ${component})
   386      for subcomponent in ${subcomponent_names}; do
   387        local image_names=$(list_subcomponent_images ${component} ${subcomponent})
   388        for base_image in ${image_names}; do
   389          local from_registry=$(resolve_image_registry_from_bom ${component} ${subcomponent} $base_image)
   390          local from_image_prefix=${from_registry}
   391          local from_repository=$(resolve_image_repo_from_bom ${component} ${subcomponent} $base_image)
   392          if [ -n "${from_repository}" ] && [ "${from_repository}" != "null" ]; then
   393            from_image_prefix=${from_image_prefix}/${from_repository}
   394          fi
   395  
   396          local from_image=${from_image_prefix}/${base_image}
   397          echo "Processing:  ${from_image}"
   398          local tarname=$(echo "${from_image}.tar" | sed -e 's;/;_;g' -e 's/:/-/g')
   399          local tarLocation=${imagesDir}/${tarname}
   400  
   401          if [ -e ${tarLocation} ]; then
   402            # Some images may be replicated in the BOM in different subcomponents, skip the pull/save if we've already
   403            # done it for this image
   404            echo "${tarLocation} already exists, skipping..."
   405            continue
   406          fi
   407  
   408          # Pull and save the image
   409          pull $from_image
   410          save ${tarLocation} ${from_image}
   411  
   412          if [ "${INCREMENTAL_CLEAN}" == "true" ]; then
   413            remove ${from_image}
   414          fi
   415        done
   416      done
   417    done
   418  }
   419  
   420  output_bom_components() {
   421    echo """
   422  Verrazzano components in BOM file ${BOM_FILE}:
   423  
   424  $(list_components)
   425    """
   426  }
   427  
   428  # Main fn
   429  function main() {
   430    if [ -n "${TARDIR}" ]; then
   431      pull_and_save_images ${TARDIR}
   432    elif [ "$USELOCAL" != "0" ]; then
   433      process_local_archives
   434    else
   435      process_images_from_bom
   436    fi
   437    if [ "${CLEAN_ALL}" == "true" ]; then
   438      echo "[SUCCESS] All local images cleaned"
   439    else
   440      if [ ! -z "$LIST_IMAGES_ONLY" ]; then
   441        echo "[SUCCESS] All images listed"
   442      else
   443        echo "[SUCCESS] All images processed"
   444      fi
   445    fi
   446  }
   447  
   448  while getopts 'hzcdom:b:t:f:r:l:i:e:' opt; do
   449    case $opt in
   450    b)
   451      BOM_FILE=$OPTARG
   452      ;;
   453    d)
   454      DRY_RUN=true
   455      ;;
   456    e)
   457      echo "Exclude component: ${OPTARG}"
   458      EXCLUDE_COMPONENTS="${EXCLUDE_COMPONENTS} ${OPTARG}"
   459      ;;
   460    f)
   461      TARDIR=$OPTARG
   462      ;;
   463    i)
   464      echo "Include component: ${OPTARG}"
   465      INCLUDE_COMPONENTS="${INCLUDE_COMPONENTS} ${OPTARG}"
   466      ;;
   467    r)
   468      TO_REPO=$OPTARG
   469      ;;
   470    t)
   471      TO_REGISTRY=$OPTARG
   472      ;;
   473    z)
   474      INCREMENTAL_CLEAN=true
   475      ;;
   476    c)
   477      CLEAN_ALL=true
   478      ;;
   479    o)
   480      output_bom_components
   481      exit 0
   482      ;;
   483    m)
   484      LIST_IMAGES_ONLY="${OPTARG}"
   485      ;;
   486    l)
   487      USELOCAL=1
   488      IMAGES_DIR="${OPTARG}"
   489      ;;
   490    h | ?)
   491      usage
   492      ;;
   493    esac
   494  done
   495  
   496  # Check the system requirements and arguments
   497  check
   498  # Exec main
   499  main