image repos: herausfinden, was die Registry wirklich anbietet

"Login Succeeded" und trotzdem "unable to copy from source": der
Repository-Pfad enterprise/check-mk-enterprise war geraten, nicht
belegt. Statt weiter zu raten fragt das Werkzeug jetzt nach.

  image repos [--edition E] [--registry R] [--user/--password]

fragt zuerst /v2/_catalog ab (sperren viele Registries) und probiert
danach die ueblichen Pfade durch, je Edition und deren Aliasnamen:
  <ed>/check-mk-<ed>, check-mk-<ed>, checkmk/check-mk-<ed>,
  enterprise/check-mk-<ed>
Gemeldet wird pro Pfad der HTTP-Status samt Tag-Anzahl. Scheitert ein
"image pull" gegen eine private Registry, laeuft die Pruefung von
selbst an und zeigt gleich, was dort liegt.

Wichtig bei der Deutung und deshalb auch so beschriftet: 401 heisst
nicht "vorhanden". Docker Hub antwortet auch auf nicht existierende
Repositories mit 401. Verlaesslich sind nur 200 und 404.

Nebenbei am Versionsarchiv geklaert, dass die Editionsumbenennung erst
mit 2.5 greift:
  2.4.0p9  -> check-mk-{raw,enterprise,cloud,managed}-docker-...
  2.5.0p13 -> check-mk-{community,pro,ultimate,ultimatemt}-docker-...
Fuer 2.4 ist "--edition enterprise" also richtig.

registry_get_token nimmt jetzt einen frei waehlbaren Scope, damit auch
registry:catalog:* angefordert werden kann.

Gegen Docker Hub verifiziert: findet checkmk/check-mk-raw (2394 Tags)
und checkmk/check-mk-community (534 Tags); gegen registry.checkmk.com
ohne Zugangsdaten sauberer Fehlbericht statt Haenger.

Dokumentiert als Abschnitt 8.8 in beiden Sprachfassungen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-20 17:19:54 +02:00
co-authored by Claude Opus 5
parent 7b26042fa4
commit f8a8c7a2a6
3 changed files with 212 additions and 6 deletions
+40 -1
View File
@@ -943,7 +943,45 @@ Compose und die systemd-Units verwenden ausschliesslich CMK_IMAGE.
Aus einem Tarball geladene Images (`localhost/…`) gelten dabei als stimmig, Aus einem Tarball geladene Images (`localhost/…`) gelten dabei als stimmig,
solange Name und Tag passen. 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 ```bash
./scripts/cmk-manage.sh image status # konfiguriertes + lokale Images ./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` | | 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 <ed> --version <v>`, dann `up` | | `omd restore`: *version is not installed* | passendes Image holen: `cmk-manage.sh image pull --edition <ed> --version <v>`, 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) | | 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/…` | | `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 | | `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 <id>` entfernen und `cmk-manage.sh image load --file …` verwenden (nutzt `load`) | | Tarball mit `podman import` eingespielt, Image „unbekannt“ | `import` erzeugt ein Image ohne Metadaten. Fehl-Image mit `podman rmi <id>` entfernen und `cmk-manage.sh image load --file …` verwenden (nutzt `load`) |
+40 -1
View File
@@ -947,7 +947,45 @@ the edition or version is changed:
the image contains …".) Images loaded from a tarball (`localhost/…`) count as the image contains …".) Images loaded from a tarball (`localhost/…`) count as
consistent as long as name and tag match. 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 ```bash
./scripts/cmk-manage.sh image status # configured image + local images ./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` | | 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 <ed> --version <v>`, then `up` | | `omd restore`: *version is not installed* | fetch the matching image: `cmk-manage.sh image pull --edition <ed> --version <v>`, 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) | | 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/…` | | `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 | | `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 <id>` and use `cmk-manage.sh image load --file …` (which uses `load`) | | Tarball installed with `podman import`, image "unknown" | `import` produces an image without metadata. Remove the broken image with `podman rmi <id>` and use `cmk-manage.sh image load --file …` (which uses `load`) |
+132 -4
View File
@@ -116,6 +116,8 @@ KOMMANDOS - Checkmk-Image beschaffen
--limit N hoechstens N Eintraege (Vorgabe 25), --all fuer alle --limit N hoechstens N Eintraege (Vorgabe 25), --all fuer alle
--user U --password P statt der Werte aus der .env --user U --password P statt der Werte aus der .env
--bare nur die nackte Liste, ohne Rahmen --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 image test [Opt.] Zugangsdaten pruefen, ohne etwas herunterzuladen
--edition E Edition, deren Repository geprueft wird --edition E Edition, deren Repository geprueft wird
--version V Version fuer die Pruefung des Downloadportals --version V Version fuer die Pruefung des Downloadportals
@@ -607,6 +609,13 @@ EOF
log "Hole Image: $ref" log "Hole Image: $ref"
if ! $RUNTIME pull "$ref"; then if ! $RUNTIME pull "$ref"; then
err "Das Image '$ref' konnte nicht geholt werden." 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 if [ "$edition" = enterprise ]; then
cat >&2 <<'EOF' cat >&2 <<'EOF'
@@ -1134,8 +1143,9 @@ image_cmd_test() {
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# Token fuer eine Registry besorgen ("" = anonym versuchen) # Token fuer eine Registry besorgen ("" = anonym versuchen)
registry_get_token() { # api repo user pass -> Token auf stdout registry_get_token() { # api repo user pass [scope] -> Token auf stdout
local api="$1" repo="$2" user="$3" pass="$4" local api="$1" repo="$2" user="$3" pass="$4" scope="${5:-}"
[ -n "$scope" ] || scope="repository:$repo:pull"
local hdrf; hdrf="$(mktemp)" local hdrf; hdrf="$(mktemp)"
curl -sS -o /dev/null -D "$hdrf" --max-time 30 "https://$api/v2/" >/dev/null 2>&1 || true curl -sS -o /dev/null -D "$hdrf" --max-time 30 "https://$api/v2/" >/dev/null 2>&1 || true
local wa realm service 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')" service="$(printf '%s' "$wa" | sed -n 's/.*service="\([^"]*\)".*/\1/p')"
[ -n "$realm" ] || return 0 [ -n "$realm" ] || return 0
local realm_host; realm_host="$(printf '%s' "$realm" | sed -E 's|^[a-z]+://([^/]+).*|\1|')" 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="" local body netrc=""
if [ -n "$user" ]; then if [ -n "$user" ]; then
netrc="$(netrc_make "$user" "$pass" "$api" "$realm_host")" netrc="$(netrc_make "$user" "$pass" "$api" "$realm_host")"
@@ -1198,6 +1208,123 @@ download_versions() { # user pass -> Versionsverzeichnisse
| sed -E 's|href="([^"/]+)/?"|\1|' | sort -u | 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
${C_BLD}Weiter${C_RST}
Tags ansehen : $SELF image versions --source registry --edition $edition
Image setzen : $SELF image use $reg/$ok:<version>
EOF
else
err "Unter '$reg' wurde kein nutzbares Repository fuer '$edition' gefunden."
cat <<EOF
Moegliche Gruende:
* Die Subskription schliesst den Registry-Zugang nicht ein.
Dann fuehrt der Tarball-Weg zum Ziel:
$SELF image download --edition $edition --version <version>
* Der Pfad heisst anders. Steht er im Kundenportal, laesst er sich
direkt setzen:
$SELF config set CMK_REGISTRY <registry>
$SELF image use <registry>/<pfad>:<version>
EOF
fi
echo
}
image_cmd_versions() { image_cmd_versions() {
local edition="${CMK_EDITION:-raw}" source="all" filter="" limit=25 local edition="${CMK_EDITION:-raw}" source="all" filter="" limit=25
local ruser="${CMK_REGISTRY_USER:-}" rpass="${CMK_REGISTRY_PASSWORD:-}" local ruser="${CMK_REGISTRY_USER:-}" rpass="${CMK_REGISTRY_PASSWORD:-}"
@@ -1324,8 +1451,9 @@ cmd_image() {
use|set) image_cmd_use "$@" ;; use|set) image_cmd_use "$@" ;;
test|check) image_cmd_test "$@" ;; test|check) image_cmd_test "$@" ;;
versions|tags) image_cmd_versions "$@" ;; versions|tags) image_cmd_versions "$@" ;;
repos|repo) image_cmd_repos "$@" ;;
*) die "Unbekanntes Unterkommando 'image $sub'. *) 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 esac
} }