diff --git a/hack/image-filter.sh b/hack/image-filter.sh index 31c951a..9756824 100755 --- a/hack/image-filter.sh +++ b/hack/image-filter.sh @@ -1,78 +1,201 @@ #!/usr/bin/env bash -if [[ "${#}" -ne 0 && "$1" != "-" ]]; then - tmp=$(cat "${1}") -else - tmp=$(cat) +SITE="https://github.com/DaoCloud/public-image-mirror" +URL_PREFIX="https://raw.githubusercontent.com/DaoCloud/public-image-mirror/main" +DOMAIN_SUFFIX="m.daocloud.io" +TMPDIR="${TMPDIR:-/tmp}/public-image-mirror" + +# show the usage if arguments are provided +function usage() { + echo "Usage: $0" + echo " cat | $0 | kubectl apply -f -" + echo " kubectl kustomize | $0 | kubectl apply -f -" + echo + echo "For more information, please visit ${SITE}" +} + +if [[ $# -ne 0 ]]; then + usage >&2 + exit 1 fi -domains=( - cr.l5d.io - docker.elastic.co - docker.io - gcr.io - ghcr.io - k8s.gcr.io - mcr.microsoft.com - nvcr.io - quay.io - registry.jujucharms.com - rocks.canonical.com -) - -SUFFIX=${SUFFIX:-"m.daocloud.io"} - -function replace_domain() { - local domain="${1}" - IFS='.' read -ra arr <<<"${domain}" - local len=${#arr[@]} - if [[ "${len}" -eq 2 ]]; then - echo "${arr[0]}.${SUFFIX}" - return 0 - fi - - if [[ "${arr[0]}" == "registry" ]]; then - echo "${arr[1]}.${SUFFIX}" - return 0 - fi - if [[ "${arr[0]}" == "docker" ]]; then - echo "${arr[1]}.${SUFFIX}" - return 0 - fi - if [[ "${arr[0]}" == "cr" ]]; then - echo "${arr[1]}.${SUFFIX}" - return 0 - fi - - if [[ "${arr[0]}" =~ cr$ ]]; then - echo "${arr[0]}.${SUFFIX}" - return 0 - fi - - unset 'arr[${#arr[@]}-1]' - - IFS='-' - echo "${arr[*]}.${SUFFIX}" - - unset IFS - return 0 +# output the log to stderr +function log() { + local message="${*}" + echo "# $0: ${message}" >&2 } -function replace_all() { - tmp="${1}" - for line in ${domains[*]}; do - key="${line}" - val="$(replace_domain ${line})" - if [[ "${key}" == "" || "${val}" == "" ]]; then - continue +# check the command exists +function command_exists() { + local cmd="$1" + type "${cmd}" >/dev/null 2>&1 +} + +mkdir -p "${TMPDIR}" + +# fetch the file or download the file if it does not exist +function fetch() { + local file="${1}" + + # check local directory first + if [[ -f "${file}" ]]; then + cat "${file}" + return 0 + fi + + # check the cache directory + if [[ -s "${TMPDIR}/${file}" ]]; then + cat "${TMPDIR}/${file}" + return 0 + fi + + # download the file + local url="${2}" + log "Fetching ${file} from ${url} cache to ${TMPDIR}" + if command_exists wget; then + log wget "${url}" -c -O "${TMPDIR}/${file}" + wget "${url}" -c -O "${TMPDIR}/${file}" >&2 + cat "${TMPDIR}/${file}" + elif command_exists curl; then + log curl "${url}" -L -o "${TMPDIR}/${file}" + curl "${url}" -L -o "${TMPDIR}/${file}" >&2 + cat "${TMPDIR}/${file}" + else + log "Please install wget or curl to download the file and retry" + log "Or you can manually download the file ${url} and put it to ${TMPDIR}/${file}" + exit 1 + fi +} + +# take the first column +function first_column() { + cat | grep -v '^#' | awk '{print $1}' +} + +DOMAIN_LIST="$(fetch "domain.txt" "${URL_PREFIX}/domain.txt" | first_column)" +MIRROR_LIST="$(fetch "mirror.txt" "${URL_PREFIX}/mirror.txt" | first_column)" +EXCLUDE_LIST="$(fetch "exclude.txt" "${URL_PREFIX}/exclude.txt" | first_column)" + +# check the tag are excluded +function is_exclude() { + local tag="${1}" + for exclude in ${EXCLUDE_LIST}; do + if [[ "${tag}" =~ ${exclude} ]]; then + return 0 fi - tmp="$(echo "${tmp}" | sed -e "s# ${key}# ${val}#g")" done - - # Remove sha256 hash - tmp="$(echo "${tmp}" | sed -e "s#@sha256:[0-9a-f]\{64\}##g")" - echo "${tmp}" + return 1 } +# check the image is mirrored +function is_mirror() { + local name="${1}" + for mirror in ${MIRROR_LIST}; do + if [[ "${name}" == "${mirror}" ]]; then + return 0 + fi + done + return 1 +} -replace_all "${tmp}" +# replace the prefix of the image +function replace_image() { + local name="${1}" + for domain in ${DOMAIN_LIST}; do + old_prefix="${domain%%=*}" + new_prefix="${domain#*=}" + if [[ "${name}" =~ ^"${old_prefix}" ]]; then + echo "${name}" | sed "s#${old_prefix}#${new_prefix}#" + return + fi + done + echo "${name}" +} + +# completion the docker.io prefix +function completion_for_docker_io() { + local name="${1}" + if [[ "${name}" =~ "." ]]; then + echo "${name}" + else + echo "docker.io/${name}" + fi +} + +# replace image prefix on the line +function replace_line() { + local line="${1}" + + # get the image full name + local raw_image="$(echo "${line#*: }" | awk '{print $1}' | sed 's/"//g' | sed "s/'//g")" + local image="${raw_image}" + local image_name="" + local image_tag="" + + # remove the sha256 suffix + if [[ "${image}" =~ "@" ]]; then + image="${image%%@*}" + fi + + # split the image name and tag + if [[ "${image}" =~ ":" ]]; then + image_tag="${image#*:}" + image_name="${image%%:*}" + else + image_name="${image}" + fi + + # check the image is replaced + if [[ "${image_name}" =~ "${DOMAIN_SUFFIX}" ]]; then + return 1 + fi + + # completion the docker.io prefix + image_name="$(completion_for_docker_io "${image_name}")" + + # check the image is mirrored + if ! is_mirror "${image_name}"; then + log "Image '${image}' is not synchronized yet, if you want please send a PR to add it on ${SITE}" + return 1 + fi + + # check the tag is empty + if [[ "${image_tag}" == "" ]]; then + log "Image '${image}' must have a tag" + return 1 + fi + + # check the tag is excluded + if is_exclude "${image_tag}"; then + log "Image '${image}' tag excludes out of synchronize" + return 1 + fi + + # replace the prefix of the image + local new_image="$(replace_image "${image_name}"):${image_tag}" + local new_line=$(echo "${line}" | sed "s|${raw_image}|${new_image}|") + + log "Replace '${raw_image}' with '${new_image}'" + + echo "${new_line}" +} + +function main() { + local new_line + cat | while read line; do + if [[ "${line}" =~ "image: " || "${line}" =~ "image': " || "${line}" =~ 'image": ' ]]; then + new_line="$(replace_line "${line}")" + if [[ $? -eq 0 ]]; then + echo "${new_line}" + else + echo "${line}" + fi + else + echo "${line}" + fi + done +} + +IFS=$'\n' + +main