From b028f6aa75f0745466e854a897918e73984596c9 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 31 Jan 2024 14:34:24 +0100 Subject: [PATCH] add new libpod/images/$name/resolve endpoint Podman Desktop [1] is looking into improving the user experience which requires to know the source of an image. Consider the user triggers an image pull and Podman Desktop wants to figure out whether the image name refers to a Red Hat registry, for instance, to prompt installing the RH auth extension. Since the input values of images may be a short name [2], Podman Desktop has no means to figure out the (potential) source of the image. Hence, add a new `/resolve` endpoint to allow external callers to figure out the (potential) fully-qualified image name of a given value. With the new endpoint, Podman Desktop can ask Podman directly to resolve the image name and then make an informed decision whether to prompt the user to perform certain tasks or not. This for sure can also be used for any other registry (e.g., Quay, Docker Hub). [1] https://github.com/containers/podman-desktop/issues/5771 [2] https://www.redhat.com/sysadmin/container-image-short-names Signed-off-by: Valentin Rothberg --- pkg/api/handlers/libpod/images.go | 35 +++++++++++++++++++++++++++++++ pkg/api/handlers/types.go | 6 ++++++ pkg/api/server/register_images.go | 22 +++++++++++++++++++ test/apiv2/10-images.at | 18 ++++++++++++++++ test/apiv2/test-apiv2 | 3 ++- test/registries.conf | 4 ++++ 6 files changed, 87 insertions(+), 1 deletion(-) diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 31bd08267d..5fc3b21e1c 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -15,6 +15,8 @@ import ( "github.com/containers/common/libimage" "github.com/containers/common/pkg/ssh" "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/pkg/shortnames" + "github.com/containers/image/v5/types" "github.com/containers/podman/v4/libpod" "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/api/handlers" @@ -720,3 +722,36 @@ func ImageScp(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, &reports.ScpReport{Id: rep.Names[0]}) } + +// Resolve the passed (short) name to one more candidates it may resolve to. +// See https://www.redhat.com/sysadmin/container-image-short-names. +// +// One user of this endpoint is Podman Desktop which needs to figure out where +// an image may resolve to. +func ImageResolve(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + name := utils.GetName(r) + + mode := types.ShortNameModeDisabled + sys := runtime.SystemContext() + sys.ShortNameMode = &mode + + resolved, err := shortnames.Resolve(sys, name) + if err != nil { + utils.Error(w, http.StatusBadRequest, fmt.Errorf("resolving %q: %w", name, err)) + return + } + + if len(resolved.PullCandidates) == 0 { // Should never happen but let's be defensive. + utils.Error(w, http.StatusInternalServerError, fmt.Errorf("name %q did not resolve to any candidate", name)) + return + } + + names := make([]string, 0, len(resolved.PullCandidates)) + for _, candidate := range resolved.PullCandidates { + names = append(names, candidate.Value.String()) + } + + report := handlers.LibpodImagesResolveReport{Names: names} + utils.WriteResponse(w, http.StatusOK, report) +} diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index e32b15a28a..2ea2fa516c 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -34,6 +34,12 @@ type LibpodImagesRemoveReport struct { Errors []string } +// LibpodImagesResolveReport includes a list of fully-qualified image references. +type LibpodImagesResolveReport struct { + // Fully-qualified image references. + Names []string +} + type ContainersPruneReport struct { docker.ContainersPruneReport } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 6264b192df..d541f5a972 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -1690,5 +1690,27 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // 500: // $ref: '#/responses/internalError' r.Handle(VersionedPath("/libpod/images/scp/{name:.*}"), s.APIHandler(libpod.ImageScp)).Methods(http.MethodPost) + // swagger:operation GET /libpod/images/{name}/resolve libpod ImageResolveLibpod + // --- + // tags: + // - images + // summary: Resolve an image (short) name + // description: Resolve the passed image name to a list of fully-qualified images referring to container registries. + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the (short) name to resolve + // produces: + // - application/json + // responses: + // 204: + // description: resolved image names + // 400: + // $ref: "#/responses/badParamError" + // 500: + // $ref: '#/responses/internalError' + r.Handle(VersionedPath("/libpod/images/{name:.*}/resolve"), s.APIHandler(libpod.ImageResolve)).Methods(http.MethodGet) return nil } diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index dba0eb3210..2c1f734c9e 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -325,4 +325,22 @@ t DELETE images/$iid_test2?noprune=false 200 t GET libpod/images/$iid_test1/exists 404 t GET libpod/images/$iid_test2/exists 404 +# If the /resolve tests fail, make sure to use ../registries.conf for the +# podman-service. + +# With an alias, we only get one item back. +t GET libpod/images/podman-desktop-test123:this/resolve 200 \ + .Names[0]="florent.fr/will/like:this" + +# If no alias matches, we will get a candidate for each unqualified-search +# registry. +t GET libpod/images/no-alias-for-sure/resolve 200 \ + .Names[0]="docker.io/library/no-alias-for-sure:latest" \ + .Names[1]="quay.io/no-alias-for-sure:latest" \ + .Names[2]="registry.fedoraproject.org/no-alias-for-sure:latest" + +# Test invalid input. +t GET libpod/images/noCAPITALcharAllowed/resolve 400 \ + .cause="repository name must be lowercase" + # vim: filetype=sh diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2 index c1ec290df6..c4d04d6d4a 100755 --- a/test/apiv2/test-apiv2 +++ b/test/apiv2/test-apiv2 @@ -448,7 +448,8 @@ function start_service() { $PODMAN_BIN unshare true fi - $PODMAN_BIN \ + CONTAINERS_REGISTRIES_CONF=$TESTS_DIR/../registries.conf \ + $PODMAN_BIN \ --root $WORKDIR/server_root --syslog=true \ system service \ --time 0 \ diff --git a/test/registries.conf b/test/registries.conf index 8e46717603..6569be833f 100644 --- a/test/registries.conf +++ b/test/registries.conf @@ -21,3 +21,7 @@ location="quay.io/libpod" [[registry]] location="localhost:5000" insecure=true + +# Alias used in tests. +[aliases] + "podman-desktop-test123"="florent.fr/will/like"