diff options
author | Daniel J Walsh <dwalsh@redhat.com> | 2021-01-15 03:49:42 -0500 |
---|---|---|
committer | Daniel J Walsh <dwalsh@redhat.com> | 2021-01-15 13:24:34 -0500 |
commit | cf51c7ed9f955390a0e417f208046e0b8fbadb26 (patch) | |
tree | e20cf39ccab111aa9a86745fc9951f0a5f45ecf8 /pkg/api | |
parent | 5a166b297339d547d89fbf14248c3aff129eb960 (diff) | |
download | podman-cf51c7ed9f955390a0e417f208046e0b8fbadb26.tar.gz podman-cf51c7ed9f955390a0e417f208046e0b8fbadb26.tar.bz2 podman-cf51c7ed9f955390a0e417f208046e0b8fbadb26.zip |
Allow podman push to push manifest lists
When doing a podman images, manifests lists look just like images, so
it is logical that users would assume that they can just podman push them
to a registry. The problem is we throw out weird errors when this happens
and users need to somehow figure out this is a manifest list rather then
an image, and frankly the user will not understand the difference.
This PR will make podman push just do the right thing, by failing over and
attempting to push the manifest if it fails to push the image.
Fix up handling of manifest push
Protocol should bring back a digest string, which can either be
printed or stored in a file.
We should not reimplement the manifest push setup code in the tunnel
code but take advantage of the api path, to make sure remote and local
work the same way.
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
Diffstat (limited to 'pkg/api')
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 49 | ||||
-rw-r--r-- | pkg/api/handlers/libpod/manifests.go | 61 |
2 files changed, 52 insertions, 58 deletions
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 5b15527b7..97cd5a65e 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -25,7 +25,6 @@ import ( utils2 "github.com/containers/podman/v2/utils" "github.com/gorilla/schema" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // Commit @@ -410,6 +409,8 @@ func PushImage(w http.ResponseWriter, r *http.Request) { query := struct { Destination string `schema:"destination"` TLSVerify bool `schema:"tlsVerify"` + Format string `schema:"format"` + All bool `schema:"all"` }{ // This is where you can override the golang default value for one of fields } @@ -434,45 +435,31 @@ func PushImage(w http.ResponseWriter, r *http.Request) { return } - newImage, err := runtime.ImageRuntime().NewFromLocal(source) - if err != nil { - utils.ImageNotFound(w, source, errors.Wrapf(err, "failed to find image %s", source)) - return - } - - authConf, authfile, key, err := auth.GetCredentials(r) + authconf, authfile, key, err := auth.GetCredentials(r) if err != nil { utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String())) return } defer auth.RemoveAuthfile(authfile) - logrus.Errorf("AuthConf: %v", authConf) + var username, password string + if authconf != nil { + username = authconf.Username + password = authconf.Password - dockerRegistryOptions := &image.DockerRegistryOptions{ - DockerRegistryCreds: authConf, } - if sys := runtime.SystemContext(); sys != nil { - dockerRegistryOptions.DockerCertPath = sys.DockerCertPath - dockerRegistryOptions.RegistriesConfPath = sys.SystemRegistriesConfPath + options := entities.ImagePushOptions{ + Authfile: authfile, + Username: username, + Password: password, + Format: query.Format, + All: query.All, } if _, found := r.URL.Query()["tlsVerify"]; found { - dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) - } - - err = newImage.PushImageToHeuristicDestination( - context.Background(), - destination, - "", // manifest type - authfile, - "", // digest file - "", // signature policy - os.Stderr, - false, // force compression - image.SigningOptions{}, - dockerRegistryOptions, - nil, // additional tags - ) - if err != nil { + options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) + } + + imageEngine := abi.ImageEngine{Libpod: runtime} + if err := imageEngine.Push(context.Background(), source, destination, options); err != nil { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", destination)) return } diff --git a/pkg/api/handlers/libpod/manifests.go b/pkg/api/handlers/libpod/manifests.go index 2031dd42f..dce861f6f 100644 --- a/pkg/api/handlers/libpod/manifests.go +++ b/pkg/api/handlers/libpod/manifests.go @@ -1,17 +1,18 @@ package libpod import ( + "context" "encoding/json" "net/http" - "github.com/containers/buildah/manifests" - copy2 "github.com/containers/image/v5/copy" "github.com/containers/image/v5/manifest" - "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers/utils" + "github.com/containers/podman/v2/pkg/auth" + "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/infra/abi" "github.com/gorilla/schema" "github.com/opencontainers/go-digest" @@ -123,15 +124,13 @@ func ManifestRemove(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: newID}) } func ManifestPush(w http.ResponseWriter, r *http.Request) { - // FIXME: parameters are missing (tlsVerify, format). - // Also, we should use the ABI function to avoid duplicate code. - // Also, support for XRegistryAuth headers are missing. - runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { All bool `schema:"all"` Destination string `schema:"destination"` + Format string `schema:"format"` + TLSVerify bool `schema:"tlsVerify"` }{ // Add defaults here once needed. } @@ -140,35 +139,43 @@ func ManifestPush(w http.ResponseWriter, r *http.Request) { errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - name := utils.GetName(r) - newImage, err := runtime.ImageRuntime().NewFromLocal(name) - if err != nil { - utils.ImageNotFound(w, name, err) + if _, err := utils.ParseDockerReference(query.Destination); err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } - dest, err := alltransports.ParseImageName(query.Destination) + + source := utils.GetName(r) + authConf, authfile, key, err := auth.GetCredentials(r) if err != nil { - utils.Error(w, "invalid destination parameter", http.StatusBadRequest, errors.Errorf("invalid destination parameter %q", query.Destination)) + utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String())) return } - rtc, err := runtime.GetConfig() - if err != nil { - utils.InternalServerError(w, err) - return + defer auth.RemoveAuthfile(authfile) + var username, password string + if authConf != nil { + username = authConf.Username + password = authConf.Password + } - sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false) - opts := manifests.PushOptions{ - Store: runtime.GetStore(), - ImageListSelection: copy2.CopySpecificImages, - SystemContext: sc, + + options := entities.ImagePushOptions{ + Authfile: authfile, + Username: username, + Password: password, + Format: query.Format, + All: query.All, + } + if sys := runtime.SystemContext(); sys != nil { + options.CertDir = sys.DockerCertPath } - if query.All { - opts.ImageListSelection = copy2.CopyAllImages + if _, found := r.URL.Query()["tlsVerify"]; found { + options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) } - newD, err := newImage.PushManifest(dest, opts) + imageEngine := abi.ImageEngine{Libpod: runtime} + digest, err := imageEngine.ManifestPush(context.Background(), source, query.Destination, options) if err != nil { - utils.InternalServerError(w, err) + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "error pushing image %q", query.Destination)) return } - utils.WriteResponse(w, http.StatusOK, newD.String()) + utils.WriteResponse(w, http.StatusOK, digest) } |