diff options
author | Valentin Rothberg <rothberg@redhat.com> | 2021-11-23 11:08:31 +0100 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2021-11-30 14:22:52 +0100 |
commit | 5bdd571b1e46f26e23f030456efb009cbb765e4c (patch) | |
tree | adad2324e3c5f094785a411cb537d8f922a3fafc /pkg/api/handlers/compat/images.go | |
parent | 8de68b170716dd1293c5a044f3e9cfd962fdbfb1 (diff) | |
download | podman-5bdd571b1e46f26e23f030456efb009cbb765e4c.tar.gz podman-5bdd571b1e46f26e23f030456efb009cbb765e4c.tar.bz2 podman-5bdd571b1e46f26e23f030456efb009cbb765e4c.zip |
compat API: allow enforcing short-names resolution to Docker Hub
The Docker-compatible REST API has historically behaved just as the rest
of Podman and Buildah (and the atomic Docker in older RHEL/Fedora) where
`containers-registries.conf` is centrally controlling which registries
a short name may resolve to during pull or local image lookups. Please
refer to a blog for more details [1].
Docker, however, is only resolving short names to docker.io which has
been reported (see #12320) to break certain clients who rely on this
behavior. In order to support this scenario, `containers.conf(5)`
received a new option to control whether Podman's compat API resolves
to docker.io only or behaves as before.
Most endpoints allow for directly normalizing parameters that represent
an image. If set in containers.conf, Podman will then normalize the
references directly to docker.io. The build endpoint is an outlier
since images are also referenced in Dockerfiles. The Buildah API,
however, supports specifying a custom `types.SystemContext` in which
we can set a field that enforces short-name resolution to docker.io
in `c/image/pkg/shortnames`.
Notice that this a "hybrid" approach of doing the normalization directly
in the compat endpoints *and* in `pkg/shortnames` by passing a system
context. Doing such a hybrid approach is neccessary since the compat
and the libpod endpoints share the same `libimage.Runtime` which makes
a global enforcement via the `libimage.Runtime.systemContext`
impossible. Having two separate runtimes for the compat and the libpod
endpoints seems risky and not generally applicable to all endpoints.
[1] https://www.redhat.com/sysadmin/container-image-short-names
Fixes: #12320
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'pkg/api/handlers/compat/images.go')
-rw-r--r-- | pkg/api/handlers/compat/images.go | 77 |
1 files changed, 51 insertions, 26 deletions
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index 0b7ba8bee..af8b6b63d 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -12,7 +12,6 @@ import ( "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" "github.com/containers/image/v5/manifest" - "github.com/containers/image/v5/pkg/shortnames" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" @@ -56,6 +55,12 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { defer os.Remove(tmpfile.Name()) name := utils.GetName(r) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, name) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + imageEngine := abi.ImageEngine{Libpod: runtime} saveOptions := entities.ImageSaveOptions{ @@ -63,7 +68,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { Output: tmpfile.Name(), } - if err := imageEngine.Save(r.Context(), name, nil, saveOptions); err != nil { + if err := imageEngine.Save(r.Context(), possiblyNormalizedName, nil, saveOptions); err != nil { if errors.Cause(err) == storage.ErrImageUnknown { utils.ImageNotFound(w, name, errors.Wrapf(err, "failed to find image %s", name)) return @@ -87,9 +92,6 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { } func CommitContainer(w http.ResponseWriter, r *http.Request) { - var ( - destImage string - ) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) @@ -98,12 +100,12 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { Changes string `schema:"changes"` Comment string `schema:"comment"` Container string `schema:"container"` + Pause bool `schema:"pause"` + Repo string `schema:"repo"` + Tag string `schema:"tag"` // fromSrc string # fromSrc is currently unused - Pause bool `schema:"pause"` - Repo string `schema:"repo"` - Tag string `schema:"tag"` }{ - // This is where you can override the golang default value for one of fields + Tag: "latest", } if err := decoder.Decode(&query, r.URL.Query()); err != nil { @@ -116,7 +118,6 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { return } sc := runtime.SystemContext() - tag := "latest" options := libpod.ContainerCommitOptions{ Pause: true, } @@ -133,9 +134,6 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { return } - if len(query.Tag) > 0 { - tag = query.Tag - } options.Message = query.Comment options.Author = query.Author options.Pause = query.Pause @@ -146,9 +144,15 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { return } - // I know mitr hates this ... but doing for now + var destImage string if len(query.Repo) > 1 { - destImage = fmt.Sprintf("%s:%s", query.Repo, tag) + destImage = fmt.Sprintf("%s:%s", query.Repo, query.Tag) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, destImage) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + destImage = possiblyNormalizedName } commitImage, err := ctr.Commit(r.Context(), destImage, options) @@ -156,7 +160,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "CommitFailure")) return } - utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint + utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: commitImage.ID()}) // nolint } func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { @@ -195,12 +199,22 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { } } + reference := query.Repo + if query.Repo != "" { + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, reference) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + reference = possiblyNormalizedName + } + platformSpecs := strings.Split(query.Platform, "/") opts := entities.ImageImportOptions{ Source: source, Changes: query.Changes, Message: query.Message, - Reference: query.Repo, + Reference: reference, OS: platformSpecs[0], } if len(platformSpecs) > 1 { @@ -250,13 +264,9 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { return } - fromImage := mergeNameAndTagOrDigest(query.FromImage, query.Tag) - - // without this early check this function would return 200 but reported error via body stream soon after - // it's better to let caller know early via HTTP status code that request cannot be processed - _, err := shortnames.Resolve(runtime.SystemContext(), fromImage) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, mergeNameAndTagOrDigest(query.FromImage, query.Tag)) if err != nil { - utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrap(err, "failed to resolve image name")) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) return } @@ -291,7 +301,7 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { pullResChan := make(chan pullResult) go func() { - pulledImages, err := runtime.LibimageRuntime().Pull(r.Context(), fromImage, config.PullPolicyAlways, pullOptions) + pulledImages, err := runtime.LibimageRuntime().Pull(r.Context(), possiblyNormalizedName, config.PullPolicyAlways, pullOptions) pullResChan <- pullResult{images: pulledImages, err: err} }() @@ -371,7 +381,13 @@ func GetImage(w http.ResponseWriter, r *http.Request) { // 404 no such // 500 internal name := utils.GetName(r) - newImage, err := utils.GetImage(r, name) + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, name) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + + newImage, err := utils.GetImage(r, possiblyNormalizedName) if err != nil { // Here we need to fiddle with the error message because docker-py is looking for "No // such image" to determine on how to raise the correct exception. @@ -483,7 +499,16 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { return } - images := query.Names + images := make([]string, len(query.Names)) + for i, img := range query.Names { + possiblyNormalizedName, err := utils.NormalizeToDockerHub(r, img) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error normalizing image")) + return + } + images[i] = possiblyNormalizedName + } + tmpfile, err := ioutil.TempFile("", "api.tar") if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) |