Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pulling mod by manifest hash #998

Merged
merged 7 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

These files are used by Linuxserver build processes to handle mods in our images. Not for end-user consumption.

* **31.12.24:** - Support pulling mods using manifest hash.
* **22.12.24:** - Add modcache support.
* **26.06.24:** - Add RO and User handlers.
* **10.06.24:** - Move lsiown to its own file. Remove support for legacy v2 and hybrid mods.
Expand Down
47 changes: 33 additions & 14 deletions docker-mods.v3
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

# Version 3
# 2022-09-25 - Initial Release
MOD_SCRIPT_VER="3.20241223"
MOD_SCRIPT_VER="3.20241231"

# Define custom folder paths
SCRIPTS_DIR="/custom-cont-init.d"
SERVICES_DIR="/custom-services.d"

if [[ ${DOCKER_MODS_DEBUG_CURL,,} = "true" ]]; then
CURL_NOISE_LEVEL="-v"
CURL_NOISE_LEVEL="-vs"
else
CURL_NOISE_LEVEL="--silent"
fi
Expand Down Expand Up @@ -295,24 +295,43 @@ run_mods() {
fi
;;
esac
ENDPOINT="${DOCKER_MOD%%:*}"
USERNAME="${DOCKER_MOD%%/*}"
REPO="${ENDPOINT#*/}"
TAG="${DOCKER_MOD#*:}"
# Identify what kind of image name format we're working with
if [[ ${DOCKER_MOD} == *:*@* ]]; then
ENDPOINT="${DOCKER_MOD%%:*}"
USERNAME="${DOCKER_MOD%%/*}"
REPO="${ENDPOINT#*/}"
FULLTAG="${DOCKER_MOD#*:}"
TAG="${FULLTAG%@*}"
TAGSHA="${DOCKER_MOD#*@}"
elif [[ ${DOCKER_MOD} == *@* ]]; then
ENDPOINT="${DOCKER_MOD%%@*}"
USERNAME="${DOCKER_MOD%%/*}"
REPO="${ENDPOINT#*/}"
unset FULLTAG
unset TAG
TAGSHA="${DOCKER_MOD#*@}"
elif [[ ${DOCKER_MOD} == *:* ]]; then
ENDPOINT="${DOCKER_MOD%%:*}"
USERNAME="${DOCKER_MOD%%/*}"
REPO="${ENDPOINT#*/}"
unset FULLTAG
TAG="${DOCKER_MOD#*:}"
unset TAGSHA
fi
if [[ "${TAG}" == "${DOCKER_MOD}" ]]; then
TAG="latest"
fi
FILENAME="${USERNAME}.${REPO}.${TAG}"
FILENAME="${USERNAME}.${REPO}${TAG:+.${TAG}}${TAGSHA:+.${TAGSHA#*:}}"
MANIFEST_URL="https://${REGISTRY}/v2/${ENDPOINT}/manifests"
BLOB_URL="https://${REGISTRY}/v2/${ENDPOINT}/blobs/"
MOD_UA="Mozilla/5.0 (Linux $(uname -m)) linuxserver.io ${REGISTRY}/${ENDPOINT}:${TAG}"
write_mod_debug "Registry='${REGISTRY}', Repository='${USERNAME}', Image='${ENDPOINT}', Tag='${TAG}'"
MOD_UA="Mozilla/5.0 (Linux $(uname -m)) linuxserver.io ${REGISTRY}/${ENDPOINT}${TAG:+:${TAG}}${TAGSHA:+@${TAGSHA}}"
write_mod_debug "Registry='${REGISTRY}', Repository='${USERNAME}', Image='${ENDPOINT}', Tag='${TAG:-N/A}', TagSHA='${TAGSHA:-N/A}'"
case "${REGISTRY}" in
"lscr.io") AUTH_URL="https://ghcr.io/token?scope=repository%3A${USERNAME}%2F${REPO}%3Apull";;
"ghcr.io") AUTH_URL="https://ghcr.io/token?scope=repository%3A${USERNAME}%2F${REPO}%3Apull";;
"quay.io") AUTH_URL="https://quay.io/v2/auth?service=quay.io&scope=repository%3A${USERNAME}%2F${REPO}%3Apull";;
"registry-1.docker.io") AUTH_URL="https://auth.docker.io/token?service=registry.docker.io&scope=repository:${ENDPOINT}:pull";;
*) AUTH_URL=$(get_auth_url "${MANIFEST_URL}" "${TAG}")
*) AUTH_URL=$(get_auth_url "${MANIFEST_URL}" "${TAG:-${TAGSHA}}")
esac
# Kill off modification logic if any of the usernames are banned
for BANNED in $(curl -s https://raw.githubusercontent.com/linuxserver/docker-mods/master/blacklist.txt); do
Expand All @@ -339,7 +358,7 @@ run_mods() {
write_mod_info "Adding ${DOCKER_MOD} to container"
# If we're using lscr try and get the manifest from ghcr, if it fails re-request a token from Docker Hub
if [[ "${REGISTRY}" == "lscr.io" ]]; then
if [[ -n $(curl --user-agent "${MOD_UA}" -sLH "Authorization: Bearer ${TOKEN}" "${MANIFEST_URL}/${TAG}" | jq -r '.errors' >/dev/null 2>&1) ]]; then
if [[ -n $(curl --user-agent "${MOD_UA}" -sLH "Authorization: Bearer ${TOKEN}" "${MANIFEST_URL}/${TAG:-${TAGSHA}}" | jq -r '.errors' >/dev/null 2>&1) ]]; then
write_mod_debug "Couldn't fetch manifest from ghcr.io, trying docker.io"
AUTH_URL="https://auth.docker.io/token?service=registry.docker.io&scope=repository:${ENDPOINT}:pull"
TOKEN="$(
Expand All @@ -360,7 +379,7 @@ run_mods() {
MOD_OFFLINE="true"
else
# Determine first and only layer of image
SHALAYER=$(get_blob_sha "${TOKEN}" "${MANIFEST_URL}" "${TAG}" "${ARCH:--amd64}")
SHALAYER=$(get_blob_sha "${TOKEN}" "${MANIFEST_URL}" "${TAGSHA:-$TAG}" "${ARCH:--amd64}")
if [[ $? -eq 1 ]]; then
write_mod_error "No manifest available for arch ${ARCH:--amd64}, cannot fetch mod"
continue
Expand All @@ -385,12 +404,12 @@ run_mods() {
write_mod_info "Downloading ${DOCKER_MOD} from ${REGISTRY}"
if [[ -f "/modcache/${FILENAME}.lock" ]]; then
write_mod_info "${DOCKER_MOD} is already being downloaded by another container, waiting..."
for ((i = 5 ; i < 21 ; i=i*2 )); do
for ((i = 5 ; i < 41 ; i=i*2 )); do
sleep $i
if [[ ! -f "/modcache/${FILENAME}.lock" ]]; then
SKIP_MOD_DOWNLOAD=true
break
elif [[ $i == 20 ]]; then
elif [[ $i == 40 ]]; then
write_mod_error "${DOCKER_MOD} timed out waiting for lock, skipping\n\tIf no other containers are using this mod you may need to delete /modcache/${FILENAME}.lock"
SKIP_MOD=true
fi
Expand Down