diff options
author | Valentin Rothberg <rothberg@redhat.com> | 2020-02-04 12:48:13 +0100 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2020-02-10 12:36:45 +0100 |
commit | 76e2a0c5d3365205cb104280d41015d6ab25cd9a (patch) | |
tree | caaa958c7b9c09f283e7e4498fdd8c473403f32e /pkg | |
parent | ee811431d29962c3a801903ca3ca342a2000f65e (diff) | |
download | podman-76e2a0c5d3365205cb104280d41015d6ab25cd9a.tar.gz podman-76e2a0c5d3365205cb104280d41015d6ab25cd9a.tar.bz2 podman-76e2a0c5d3365205cb104280d41015d6ab25cd9a.zip |
v2 api: /libpod/images/pull
Implement the /libpod/images/pull endpoint and correct the swagger docs.
The reference parameter is mandatory and must either be a
c/image/docker/reference or a reference to the "docker://" transport as
the pull endpoint is meant to only support pulling images from a
registry.
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/api/handlers/libpod/images.go | 125 | ||||
-rw-r--r-- | pkg/api/handlers/types.go | 29 | ||||
-rw-r--r-- | pkg/api/server/register_images.go | 25 |
3 files changed, 157 insertions, 22 deletions
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 6c926c45b..fffcad8ed 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -1,15 +1,22 @@ package libpod import ( + "context" "fmt" "io/ioutil" "net/http" "os" "strconv" + "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/util" "github.com/gorilla/schema" "github.com/pkg/errors" ) @@ -186,6 +193,120 @@ func ImagesImport(w http.ResponseWriter, r *http.Request) { } func ImagesPull(w http.ResponseWriter, r *http.Request) { - //TODO ... - utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.New("/libpod/images/pull is not yet implemented")) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decoder").(*schema.Decoder) + query := struct { + Reference string `schema:"reference"` + Credentials string `schema:"credentials"` + OverrideOS string `schema:"overrideOS"` + OverrideArch string `schema:"overrideArch"` + TLSVerify bool `schema:"tlsVerify"` + AllTags bool `schema:"allTags"` + }{ + TLSVerify: true, + } + + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + return + } + + if len(query.Reference) == 0 { + utils.InternalServerError(w, errors.New("reference parameter cannot be empty")) + return + } + // Enforce the docker transport. This is just a precaution as some callers + // might accustomed to using the "transport:reference" notation. Using + // another than the "docker://" transport does not really make sense for a + // remote case. For loading tarballs, the load and import endpoints should + // be used. + imageRef, err := alltransports.ParseImageName(query.Reference) + if err == nil && imageRef.Transport().Name() != docker.Transport.Name() { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Errorf("reference %q must be a docker reference", query.Reference)) + return + } else if err != nil { + origErr := err + imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s:%s", docker.Transport.Name(), query.Reference)) + if err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(origErr, "reference %q must be a docker reference", query.Reference)) + return + } + } + + // all-tags doesn't work with a tagged reference, so let's check early + namedRef, err := reference.Parse(query.Reference) + if err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "error parsing reference %q", query.Reference)) + return + } + if _, isTagged := namedRef.(reference.Tagged); isTagged && query.AllTags { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Errorf("reference %q must not have a tag for all-tags", query.Reference)) + return + } + + var registryCreds *types.DockerAuthConfig + if len(query.Credentials) != 0 { + creds, err := util.ParseRegistryCreds(query.Credentials) + if err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "error parsing credentials %q", query.Credentials)) + return + } + registryCreds = creds + } + + // Setup the registry options + dockerRegistryOptions := image.DockerRegistryOptions{ + DockerRegistryCreds: registryCreds, + OSChoice: query.OverrideOS, + ArchitectureChoice: query.OverrideArch, + } + if query.TLSVerify { + dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) + } + + // Prepare the images we want to pull + imagesToPull := []string{} + res := []handlers.LibpodImagesPullReport{} + imageName := namedRef.String() + + if !query.AllTags { + imagesToPull = append(imagesToPull, imageName) + } else { + systemContext := image.GetSystemContext("", "", false) + tags, err := docker.GetRepositoryTags(context.Background(), systemContext, imageRef) + if err != nil { + utils.InternalServerError(w, errors.Wrap(err, "error getting repository tags")) + return + } + for _, tag := range tags { + imagesToPull = append(imagesToPull, fmt.Sprintf("%s:%s", imageName, tag)) + } + } + + // Finally pull the images + for _, img := range imagesToPull { + newImage, err := runtime.ImageRuntime().New( + context.Background(), + img, + "", + "", + os.Stderr, + &dockerRegistryOptions, + image.SigningOptions{}, + nil, + util.PullImageAlways) + if err != nil { + utils.InternalServerError(w, errors.Wrapf(err, "error pulling image %q", query.Reference)) + return + } + res = append(res, handlers.LibpodImagesPullReport{ID: newImage.ID()}) + } + + utils.WriteResponse(w, http.StatusOK, res) } diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index 6169adb18..c19397e60 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -33,6 +33,20 @@ type ContainerConfig struct { dockerContainer.Config } +type LibpodImagesLoadReport struct { + ID string `json:"id"` + RepoTags []string `json:"repoTags"` +} + +type LibpodImagesImportReport struct { + ID string `json:"id"` + RepoTags []string `json:"repoTags"` +} + +type LibpodImagesPullReport struct { + ID string `json:"id"` +} + type ImageSummary struct { docker.ImageSummary CreatedTime time.Time `json:"CreatedTime,omitempty"` @@ -49,21 +63,6 @@ type LibpodContainersPruneReport struct { PruneError string `json:"error"` } -type LibpodImagesLoadReport struct { - ID string `json:"id"` - RepoTags []string `json:"repoTags"` -} - -type LibpodImagesImportReport struct { - ID string `json:"id"` - RepoTags []string `json:"repoTags"` -} - -type LibpodImagesPullReport struct { - ID string `json:"id"` - RepoTags []string `json:"repoTags"` -} - type Info struct { docker.Info BuildahVersion string diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 4a46b6ee6..98858dac2 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -691,34 +691,49 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // 500: // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/import"), APIHandler(s.Context, libpod.ImagesImport)).Methods(http.MethodPost) - // swagger:operation GET /libpod/images/pull libpod libpodImagesPull + // swagger:operation POST /libpod/images/pull libpod libpodImagesPull // --- // tags: // - images - // summary: Import image - // description: Import a previosly exported image as a tarball. + // summary: Pull images + // description: Pull one or more images from a container registry. // parameters: // - in: query // name: reference - // description: Mandatory reference to the image (e.g., quay.io/image/name:tag)/ + // description: Mandatory reference to the image (e.g., quay.io/image/name:tag) // type: string // - in: query // name: credentials // description: username:password for the registry. // type: string // - in: query + // name: os + // description: Pull image for the specified operating system. + // type: string + // - in: query + // name: arch + // description: Pull image for the specified architecture. + // type: string + // - in: query // name: tls-verify // description: Require TLS verification. // type: boolean // default: true + // - in: query + // name: all-tags + // description: Pull all tagged images in the repository. + // type: bool // produces: // - application/json // responses: // 200: // $ref: "#/responses/DocsLibpodImagesPullResponse" + // $ref: "#/response/LibpodImagesPullResponse" + // 400: + // $ref: "#/responses/BadParamError" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/pull"), APIHandler(s.Context, libpod.ImagesPull)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/images/pull"), APIHandler(s.Context, libpod.ImagesPull)).Methods(http.MethodPut) // swagger:operation POST /libpod/images/prune libpod libpodPruneImages // --- // tags: |