aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/images.go1
-rw-r--r--pkg/api/handlers/libpod/images.go70
-rw-r--r--pkg/api/server/register_images.go34
-rw-r--r--pkg/bindings/images/images.go28
-rw-r--r--pkg/domain/entities/images.go17
-rw-r--r--pkg/domain/infra/abi/images.go14
-rw-r--r--pkg/domain/infra/tunnel/images.go23
7 files changed, 167 insertions, 20 deletions
diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go
index 6872dd780..8765e20ca 100644
--- a/pkg/api/handlers/compat/images.go
+++ b/pkg/api/handlers/compat/images.go
@@ -365,7 +365,6 @@ func LoadImages(w http.ResponseWriter, r *http.Request) {
return
}
id, err := runtime.LoadImage(r.Context(), "", f.Name(), writer, "")
- //id, err := runtime.Import(r.Context())
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to load image"))
return
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 8d3fc4e00..85f7903dc 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -234,6 +234,76 @@ func ExportImage(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, rdr)
}
+func ExportImages(w http.ResponseWriter, r *http.Request) {
+ var (
+ output string
+ )
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ Compress bool `schema:"compress"`
+ Format string `schema:"format"`
+ References []string `schema:"references"`
+ }{
+ Format: define.OCIArchive,
+ }
+
+ 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
+ }
+
+ // References are mandatory!
+ if len(query.References) == 0 {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.New("No references"))
+ return
+ }
+
+ // Format is mandatory! Currently, we only support multi-image docker
+ // archives.
+ switch query.Format {
+ case define.V2s2Archive:
+ tmpfile, err := ioutil.TempFile("", "api.tar")
+ if err != nil {
+ utils.Error(w, "unable to create tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ output = tmpfile.Name()
+ if err := tmpfile.Close(); err != nil {
+ utils.Error(w, "unable to close tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ return
+ }
+ default:
+ utils.Error(w, "unsupported format", http.StatusInternalServerError, errors.Errorf("unsupported format %q", query.Format))
+ return
+ }
+ defer os.RemoveAll(output)
+
+ // Use the ABI image engine to share as much code as possible.
+ opts := entities.ImageSaveOptions{
+ Compress: query.Compress,
+ Format: query.Format,
+ MultiImageArchive: true,
+ Output: output,
+ }
+
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+ if err := imageEngine.Save(r.Context(), query.References[0], query.References[1:], opts); err != nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err)
+ return
+ }
+
+ rdr, err := os.Open(output)
+ if err != nil {
+ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile"))
+ return
+ }
+ defer rdr.Close()
+ utils.WriteResponse(w, http.StatusOK, rdr)
+}
+
func ImagesLoad(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 64258a073..b1007fe09 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -1028,6 +1028,40 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/{name:.*}/get"), s.APIHandler(libpod.ExportImage)).Methods(http.MethodGet)
+ // swagger:operation GET /libpod/images/export libpod libpodExportImages
+ // ---
+ // tags:
+ // - images
+ // summary: Export multiple images
+ // description: Export multiple images into a single object. Only `docker-archive` is currently supported.
+ // parameters:
+ // - in: query
+ // name: format
+ // type: string
+ // description: format for exported image (only docker-archive is supported)
+ // - in: query
+ // name: references
+ // description: references to images to export
+ // type: array
+ // items:
+ // type: string
+ // - in: query
+ // name: compress
+ // type: boolean
+ // description: use compression on image
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: no error
+ // schema:
+ // type: string
+ // format: binary
+ // 404:
+ // $ref: '#/responses/NoSuchImage'
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/libpod/images/export"), s.APIHandler(libpod.ExportImages)).Methods(http.MethodGet)
// swagger:operation GET /libpod/images/{name:.*}/json libpod libpodInspectImage
// ---
// tags:
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index 9f6e78b79..a80c94025 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -128,6 +128,34 @@ func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadRe
return &report, response.Process(&report)
}
+func MultiExport(ctx context.Context, namesOrIds []string, w io.Writer, format *string, compress *bool) error {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return err
+ }
+ params := url.Values{}
+ if format != nil {
+ params.Set("format", *format)
+ }
+ if compress != nil {
+ params.Set("compress", strconv.FormatBool(*compress))
+ }
+ for _, ref := range namesOrIds {
+ params.Add("references", ref)
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/images/export", params, nil)
+ if err != nil {
+ return err
+ }
+
+ if response.StatusCode/100 == 2 || response.StatusCode/100 == 3 {
+ _, err = io.Copy(w, response.Body)
+ return err
+ }
+ return response.Process(nil)
+
+}
+
// Export saves an image from local storage as a tarball or image archive. The optional format
// parameter is used to change the format of the output.
func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, compress *bool) error {
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 3a12a4e22..2a8133680 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -271,11 +271,22 @@ type ImageImportReport struct {
Id string //nolint
}
+// ImageSaveOptions provide options for saving images.
type ImageSaveOptions struct {
+ // Compress layers when saving to a directory.
Compress bool
- Format string
- Output string
- Quiet bool
+ // Format of saving the image: oci-archive, oci-dir (directory with oci
+ // manifest type), docker-archive, docker-dir (directory with v2s2
+ // manifest type).
+ Format string
+ // MultiImageArchive denotes if the created archive shall include more
+ // than one image. Additional tags will be interpreted as references
+ // to images which are added to the archive.
+ MultiImageArchive bool
+ // Output - write image to the specified path.
+ Output string
+ // Quiet - suppress output when copying images
+ Quiet bool
}
// ImageTreeOptions provides options for ImageEngine.Tree()
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 6b94ca9c0..33060b34b 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -14,7 +14,6 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker"
- dockerarchive "github.com/containers/image/v5/docker/archive"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/signature"
@@ -230,15 +229,6 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
}
}
- // Special-case for docker-archive which allows multiple tags.
- if imageRef.Transport().Name() == dockerarchive.Transport.Name() {
- newImage, err := ir.Libpod.ImageRuntime().LoadFromArchiveReference(ctx, imageRef, options.SignaturePolicy, writer)
- if err != nil {
- return nil, err
- }
- return &entities.ImagePullReport{Images: []string{newImage[0].ID()}}, nil
- }
-
var registryCreds *types.DockerAuthConfig
if len(options.Username) > 0 && len(options.Password) > 0 {
registryCreds = &types.DockerAuthConfig{
@@ -481,6 +471,10 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti
}
func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error {
+ if options.MultiImageArchive {
+ nameOrIDs := append([]string{nameOrID}, tags...)
+ return ir.Libpod.ImageRuntime().SaveImages(ctx, nameOrIDs, options.Format, options.Output, options.Quiet)
+ }
newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID)
if err != nil {
return err
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index b255c5da4..185cc2f9a 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -251,12 +251,23 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string,
return err
}
- exErr := images.Export(ir.ClientCxt, nameOrID, f, &options.Format, &options.Compress)
- if err := f.Close(); err != nil {
- return err
- }
- if exErr != nil {
- return exErr
+ if options.MultiImageArchive {
+ exErr := images.MultiExport(ir.ClientCxt, append([]string{nameOrID}, tags...), f, &options.Format, &options.Compress)
+ if err := f.Close(); err != nil {
+ return err
+ }
+ if exErr != nil {
+ return exErr
+ }
+ } else {
+ // FIXME: tags are entirely ignored here but shouldn't.
+ exErr := images.Export(ir.ClientCxt, nameOrID, f, &options.Format, &options.Compress)
+ if err := f.Close(); err != nil {
+ return err
+ }
+ if exErr != nil {
+ return exErr
+ }
}
if options.Format != "oci-dir" && options.Format != "docker-dir" {