diff --git a/README.md b/README.md index 44970b5..edcc821 100644 --- a/README.md +++ b/README.md @@ -943,7 +943,45 @@ Compose und die systemd-Units verwenden ausschliesslich CMK_IMAGE. Aus einem Tarball geladene Images (`localhost/…`) gelten dabei als stimmig, solange Name und Tag passen. -### 8.8 Nachsehen, was gilt +### 8.8 Registry-Pfad herausfinden + +„Login Succeeded" heisst noch nicht, dass der Pfad stimmt. Meldet der Pull +`unable to copy from source docker://…`, existiert das Repository unter diesem +Namen nicht oder die Subskription deckt es nicht ab: + +```bash +./scripts/cmk-manage.sh image repos --edition enterprise +``` + +Das fragt zuerst den Katalog (`/v2/_catalog`) ab – den sperren allerdings viele +Registries – und probiert danach die üblichen Pfade durch: + +``` +Uebliche Pfade fuer "raw" durchprobiert + STATUS TAGS PFAD + 200 = nutzbar, 404 = gibt es nicht, 401/403 = kein Zugriff + 401 - raw/check-mk-raw (kein Zugriff - oder gibt es nicht) + 200 2394 checkmk/check-mk-raw + ... +[ OK ] Nutzbar: docker.io/checkmk/check-mk-raw +``` + +Scheitert ein `image pull` gegen eine private Registry, läuft diese Prüfung +automatisch an und zeigt gleich, was dort wirklich liegt. + +> **401 heisst nicht zwingend „vorhanden".** Viele Registries – Docker Hub +> zum Beispiel – antworten auch auf nicht existierende Repositories mit 401, +> um deren Existenz nicht zu verraten. Verlässlich ist nur **200 = nutzbar** +> und **404 = gibt es nicht**. + +Führt kein Pfad zum Ziel, bleibt der Tarball-Weg. Er ist für Enterprise +ohnehin der dokumentierte und funktioniert unabhängig von der Registry: + +```bash +./scripts/cmk-manage.sh image download --edition enterprise --version 2.4.0p9 +``` + +### 8.9 Nachsehen, was gilt ```bash ./scripts/cmk-manage.sh image status # konfiguriertes + lokale Images @@ -1999,6 +2037,7 @@ Checkmk-Werksnotizen (Werks/Release Notes) lesen. | Nach Reboot laufen die Container nicht (Podman) | systemd-Unit fehlt: `./install.sh --with-systemd` bzw. `--quadlet`, danach `systemctl is-enabled checkmk-stack` | | `omd restore`: *version is not installed* | passendes Image holen: `cmk-manage.sh image pull --edition --version `, dann `up` | | Import bricht mit Editions-Warnung ab | Enterprise-Backup benötigt ein Enterprise-Image, siehe [Kapitel 8](#8-checkmk-image-beschaffen-raw-enterprise--co) | +| `Login Succeeded`, aber Pull scheitert mit `unable to copy from source` | Der Repository-Pfad oder das Tag stimmt nicht. `cmk-manage.sh image repos --edition enterprise` zeigt, was die Registry anbietet; sonst über den Tarball gehen | | `image not known` / Podman sucht bei `registry.access.redhat.com` | kurzer Image-Name. `CMK_IMAGE` vollqualifiziert setzen: `cmk-manage.sh image use docker.io/…` bzw. `registry.checkmk.com/enterprise/…` | | `checkmk/check-mk-enterprise` nicht auf Docker Hub gefunden | richtig so – Enterprise liegt nur auf `registry.checkmk.com` oder als Tarball vor; ab 2.5 heißt die Edition `pro` und liegt frei auf Docker Hub | | Tarball mit `podman import` eingespielt, Image „unbekannt“ | `import` erzeugt ein Image ohne Metadaten. Fehl-Image mit `podman rmi ` entfernen und `cmk-manage.sh image load --file …` verwenden (nutzt `load`) | diff --git a/README_en.md b/README_en.md index 5d5e06e..357d47b 100644 --- a/README_en.md +++ b/README_en.md @@ -947,7 +947,45 @@ the edition or version is changed: the image contains …".) Images loaded from a tarball (`localhost/…`) count as consistent as long as name and tag match. -### 8.8 Checking what is in effect +### 8.8 Finding the registry path + +"Login Succeeded" does not yet mean the path is right. If the pull reports +`unable to copy from source docker://…`, the repository does not exist under +that name, or the subscription does not cover it: + +```bash +./scripts/cmk-manage.sh image repos --edition enterprise +``` + +This first queries the catalog (`/v2/_catalog`) – which many registries block – +and then probes the usual paths: + +``` +Uebliche Pfade fuer "raw" durchprobiert + STATUS TAGS PFAD + 200 = nutzbar, 404 = gibt es nicht, 401/403 = kein Zugriff + 401 - raw/check-mk-raw (kein Zugriff - oder gibt es nicht) + 200 2394 checkmk/check-mk-raw + ... +[ OK ] Nutzbar: docker.io/checkmk/check-mk-raw +``` + +If an `image pull` against a private registry fails, this probe runs +automatically and shows what is actually there. + +> **401 does not necessarily mean "it exists".** Many registries – Docker Hub +> among them – answer 401 for non-existent repositories too, so as not to +> reveal whether they exist. Only **200 = usable** and **404 = does not exist** +> are reliable. + +If no path works, the tarball route remains. For Enterprise it is the +documented one anyway, and it works independently of the registry: + +```bash +./scripts/cmk-manage.sh image download --edition enterprise --version 2.4.0p9 +``` + +### 8.9 Checking what is in effect ```bash ./scripts/cmk-manage.sh image status # configured image + local images @@ -2009,6 +2047,7 @@ release notes beforehand. | Containers do not run after a reboot (Podman) | systemd unit missing: `./install.sh --with-systemd` or `--quadlet`, then `systemctl is-enabled checkmk-stack` | | `omd restore`: *version is not installed* | fetch the matching image: `cmk-manage.sh image pull --edition --version `, then `up` | | Import aborts with an edition warning | an Enterprise backup needs an Enterprise image, see [chapter 8](#8-obtaining-the-checkmk-image-raw-enterprise--co) | +| `Login Succeeded` but the pull fails with `unable to copy from source` | The repository path or the tag is wrong. `cmk-manage.sh image repos --edition enterprise` shows what the registry offers; otherwise use the tarball | | `image not known` / Podman searches `registry.access.redhat.com` | short image name. Set `CMK_IMAGE` fully qualified: `cmk-manage.sh image use docker.io/…` or `registry.checkmk.com/enterprise/…` | | `checkmk/check-mk-enterprise` not found on Docker Hub | correct – Enterprise is only on `registry.checkmk.com` or available as a tarball; from 2.5 the edition is called `pro` and is public on Docker Hub | | Tarball installed with `podman import`, image "unknown" | `import` produces an image without metadata. Remove the broken image with `podman rmi ` and use `cmk-manage.sh image load --file …` (which uses `load`) | diff --git a/scripts/cmk-manage.sh b/scripts/cmk-manage.sh index 9f3c5da..ef54e7a 100755 --- a/scripts/cmk-manage.sh +++ b/scripts/cmk-manage.sh @@ -116,6 +116,8 @@ KOMMANDOS - Checkmk-Image beschaffen --limit N hoechstens N Eintraege (Vorgabe 25), --all fuer alle --user U --password P statt der Werte aus der .env --bare nur die nackte Liste, ohne Rahmen + image repos [Opt.] herausfinden, welche Repositories die Registry + wirklich anbietet (Katalog + uebliche Pfade) image test [Opt.] Zugangsdaten pruefen, ohne etwas herunterzuladen --edition E Edition, deren Repository geprueft wird --version V Version fuer die Pruefung des Downloadportals @@ -607,6 +609,13 @@ EOF log "Hole Image: $ref" if ! $RUNTIME pull "$ref"; then err "Das Image '$ref' konnte nicht geholt werden." + if [ "$reg" != "docker.io" ] && [ -n "$user" ]; then + warn "Die Anmeldung hat geklappt, der Pfad oder das Tag aber nicht. + Ich sehe nach, was diese Registry tatsaechlich anbietet ..." + image_cmd_repos --edition "$edition" --registry "$reg" \ + --user "$user" --password "$pass" || true + return 1 + fi if [ "$edition" = enterprise ]; then cat >&2 <<'EOF' @@ -1134,8 +1143,9 @@ image_cmd_test() { # --------------------------------------------------------------------- # Token fuer eine Registry besorgen ("" = anonym versuchen) -registry_get_token() { # api repo user pass -> Token auf stdout - local api="$1" repo="$2" user="$3" pass="$4" +registry_get_token() { # api repo user pass [scope] -> Token auf stdout + local api="$1" repo="$2" user="$3" pass="$4" scope="${5:-}" + [ -n "$scope" ] || scope="repository:$repo:pull" local hdrf; hdrf="$(mktemp)" curl -sS -o /dev/null -D "$hdrf" --max-time 30 "https://$api/v2/" >/dev/null 2>&1 || true local wa realm service @@ -1149,7 +1159,7 @@ registry_get_token() { # api repo user pass -> Token auf stdout service="$(printf '%s' "$wa" | sed -n 's/.*service="\([^"]*\)".*/\1/p')" [ -n "$realm" ] || return 0 local realm_host; realm_host="$(printf '%s' "$realm" | sed -E 's|^[a-z]+://([^/]+).*|\1|')" - local url="$realm?service=$service&scope=repository:$repo:pull" + local url="$realm?service=$service&scope=$scope" local body netrc="" if [ -n "$user" ]; then netrc="$(netrc_make "$user" "$pass" "$api" "$realm_host")" @@ -1198,6 +1208,123 @@ download_versions() { # user pass -> Versionsverzeichnisse | sed -E 's|href="([^"/]+)/?"|\1|' | sort -u } +# --------------------------------------------------------------------- +# Welches Repository heisst in der Registry wie? +# +# Der Pfad ist nicht zu erraten - "Login Succeeded" heisst noch lange +# nicht, dass enterprise/check-mk-enterprise existiert. Deshalb erst den +# Katalog fragen und, falls der gesperrt ist, die ueblichen Pfade +# durchprobieren. HTTP 404 = gibt es nicht, 401/403 = keine Berechtigung, +# 200 = nutzbar. +# --------------------------------------------------------------------- +registry_catalog() { # api user pass -> Repository-Namen + local api="$1" user="$2" pass="$3" + local token; token="$(registry_get_token "$api" "" "$user" "$pass" "registry:catalog:*")" + local -a a=(-sS --max-time 45) + if [ -n "$token" ]; then a+=(-H "Authorization: Bearer $token") + elif [ -n "$user" ]; then + local netrc; netrc="$(netrc_make "$user" "$pass" "$api")"; a+=(--netrc-file "$netrc") + fi + curl "${a[@]}" "https://$api/v2/_catalog?n=1000" 2>/dev/null \ + | tr ',' '\n' | grep -oE '"[a-z0-9][a-z0-9._/-]*"' | tr -d '"' \ + | grep -v '^repositories$' | sed '/^$/d' | sort -u +} + +registry_repo_candidates() { # edition -> moegliche Repository-Pfade + local edition="$1" a + for a in $(edition_aliases "$edition"); do + printf '%s/check-mk-%s\n' "$a" "$a" + printf 'check-mk-%s\n' "$a" + printf 'checkmk/check-mk-%s\n' "$a" + printf 'enterprise/check-mk-%s\n' "$a" + done +} + +# Status eines Repositories ermitteln: 200 / 401 / 403 / 404 / 000 +registry_repo_status() { # api repo user pass -> "code anzahl_tags" + local api="$1" repo="$2" user="$3" pass="$4" + local token; token="$(registry_get_token "$api" "$repo" "$user" "$pass")" + local -a a=(-sS -o /tmp/.cmkrepo.$$ -w '%{http_code}' --max-time 30) + [ -n "$token" ] && a+=(-H "Authorization: Bearer $token") + local code + code="$(curl "${a[@]}" "https://$api/v2/$repo/tags/list" 2>/dev/null)" || code=000 + local n=0 + [ "${code:-000}" = 200 ] && n="$(tr ',' '\n' <"/tmp/.cmkrepo.$$" 2>/dev/null | grep -c '"' || echo 0)" + rm -f "/tmp/.cmkrepo.$$" + printf '%s %s\n' "${code:-000}" "$n" +} + +image_cmd_repos() { + local edition="${CMK_EDITION:-enterprise}" reg="${CMK_REGISTRY:-registry.checkmk.com}" + local user="${CMK_REGISTRY_USER:-}" pass="${CMK_REGISTRY_PASSWORD:-}" + while [ $# -gt 0 ]; do + case "$1" in + --edition) edition="$2"; shift 2 ;; + --registry) reg="$2"; shift 2 ;; + --user) user="$2"; shift 2 ;; + --password) pass="$2"; shift 2 ;; + *) die "Unbekannte Option fuer 'image repos': $1" ;; + esac + done + command -v curl >/dev/null 2>&1 || die "Fuer 'image repos' wird curl benoetigt." + local api; api="$(registry_api_host "$reg")" + + printf '\n%sRegistry %s%s\n' "$C_BLD" "$reg" "$C_RST" + [ -n "$user" ] || warn "Keine Zugangsdaten hinterlegt - ohne Anmeldung ist hier wenig zu sehen." + + printf '\n%sKatalog (/v2/_catalog)%s\n' "$C_BLD" "$C_RST" + local -a cat=() + mapfile -t cat < <(registry_catalog "$api" "$user" "$pass" | grep -iE 'check|mk' | head -50) + if [ ${#cat[@]} -gt 0 ]; then + printf ' %s\n' "${cat[@]}" + else + printf ' (nicht abrufbar - viele Registries sperren den Katalog)\n' + fi + + printf '\n%sUebliche Pfade fuer "%s" durchprobiert%s\n' "$C_BLD" "$edition" "$C_RST" + printf ' %-6s %-8s %s\n' "STATUS" "TAGS" "PFAD" + printf ' %s200 = nutzbar, 404 = gibt es nicht, 401/403 = kein Zugriff%s\n' "$C_BLU" "$C_RST" + local cand code n line ok="" + while IFS= read -r cand; do + [ -n "$cand" ] || continue + line="$(registry_repo_status "$api" "$cand" "$user" "$pass")" + code="${line%% *}"; n="${line##* }" + case "$code" in + 200) printf ' %s%-6s%s %-8s %s\n' "$C_GRN" "$code" "$C_RST" "$n" "$cand" + [ -z "$ok" ] && ok="$cand" ;; + 401|403) printf ' %s%-6s%s %-8s %s (kein Zugriff - oder gibt es nicht;\n%38sviele Registries verbergen das)\n' \ + "$C_YEL" "$code" "$C_RST" "-" "$cand" "" ;; + 404) printf ' %-6s %-8s %s (gibt es nicht)\n' "$code" "-" "$cand" ;; + *) printf ' %-6s %-8s %s\n' "${code:-000}" "-" "$cand" ;; + esac + done < <(registry_repo_candidates "$edition" | awk '!seen[$0]++') + + echo + if [ -n "$ok" ]; then + ok "Nutzbar: $reg/$ok" + cat < +EOF + else + err "Unter '$reg' wurde kein nutzbares Repository fuer '$edition' gefunden." + cat < + * Der Pfad heisst anders. Steht er im Kundenportal, laesst er sich + direkt setzen: + $SELF config set CMK_REGISTRY + $SELF image use /: +EOF + fi + echo +} + image_cmd_versions() { local edition="${CMK_EDITION:-raw}" source="all" filter="" limit=25 local ruser="${CMK_REGISTRY_USER:-}" rpass="${CMK_REGISTRY_PASSWORD:-}" @@ -1324,8 +1451,9 @@ cmd_image() { use|set) image_cmd_use "$@" ;; test|check) image_cmd_test "$@" ;; versions|tags) image_cmd_versions "$@" ;; + repos|repo) image_cmd_repos "$@" ;; *) die "Unbekanntes Unterkommando 'image $sub'. - Moeglich: status | list | versions | test | pull | download | load | use" ;; + Moeglich: status | list | versions | repos | test | pull | download | load | use" ;; esac }