The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/2216

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Add help documentation for the target of `image export`.

If the target is a file, append the appropriate file extension to the
user-provided filename.  Properly parse the Content-Disposition header
to retrieve the image's filename instead of assuming `filename` is the
only parameter in the header.

Fix a bug where specifying a filename (as opposed to a directory) would
create the file with incorrect permissions (664 instead of 600).

Fixes #2205

Signed-off-by: Sean Christopherson <sean.j.christopher...@intel.com>
From 8d8f813ca0dd80a24f9635092b2b870ced8cc2ff Mon Sep 17 00:00:00 2001
From: Sean Christopherson <sean.j.christopher...@intel.com>
Date: Tue, 19 Jul 2016 10:14:59 -0700
Subject: [PATCH] Document image export target behavior and fix bugs

Add help documentation for the target of `image export`.

If the target is a file, append the appropriate file extension to the
user-provided filename.  Properly parse the Content-Disposition header
to retrieve the image's filename instead of assuming `filename` is the
only parameter in the header.

Fix a bug where specifying a filename (as opposed to a directory) would
create the file with incorrect permissions (664 instead of 600).

Fixes #2205

Signed-off-by: Sean Christopherson <sean.j.christopher...@intel.com>
---
 client.go             |  60 +++++++++++---------------
 lxc/image.go          |  11 ++++-
 po/lxd.pot            | 115 +++++++++++++++++++++++++++-----------------------
 test/suites/basic.sh  |   7 ++-
 test/suites/remote.sh |   6 +--
 5 files changed, 105 insertions(+), 94 deletions(-)

diff --git a/client.go b/client.go
index 3e0d791..533773f 100644
--- a/client.go
+++ b/client.go
@@ -845,56 +845,44 @@ func (c *Client) ExportImage(image string, target string) 
(string, error) {
        if target == "-" {
                wr = os.Stdout
                destpath = "stdout"
-       } else if fi, err := os.Stat(target); err == nil {
-               // file exists, so check if folder
-               switch mode := fi.Mode(); {
-               case mode.IsDir():
-                       // save in directory, header content-disposition can 
not be null
-                       // and will have a filename
-                       cd := 
strings.Split(raw.Header["Content-Disposition"][0], "=")
-
-                       // write filename from header
-                       destpath = filepath.Join(target, cd[1])
-                       f, err := os.Create(destpath)
-                       defer f.Close()
-
-                       if err != nil {
-                               return "", err
-                       }
-
-                       wr = f
-
-               default:
-                       // overwrite file
-                       destpath = target
-                       f, err := os.OpenFile(destpath, 
os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
-                       defer f.Close()
+       } else {
+               _, cdParams, err := 
mime.ParseMediaType(raw.Header.Get("Content-Disposition"))
+               if err != nil {
+                       return "", err
+               }
+               filename, ok := cdParams["filename"]
+               if !ok {
+                       return "", fmt.Errorf("No filename in 
Content-Disposition header.")
+               }
 
-                       if err != nil {
-                               return "", err
+               if fi, err := os.Stat(target); err == nil && fi.Mode().IsDir() {
+                       // The target is a directory, use the filename verbatim 
from the
+                       // Content-Disposition header
+                       destpath = filepath.Join(target, filename)
+               } else {
+                       // The target is a file, parse the extension from the 
source filename
+                       // and append it to the target filename.
+                       ext := filepath.Ext(filename)
+                       if strings.HasSuffix(filename, fmt.Sprintf(".tar%s", 
ext)) {
+                               ext = fmt.Sprintf(".tar%s", ext)
                        }
-
-                       wr = f
+                       destpath = fmt.Sprintf("%s%s", target, ext)
                }
-       } else {
-               // write as simple file
-               destpath = target
-               f, err := os.Create(destpath)
-               defer f.Close()
 
-               wr = f
+               f, err := os.OpenFile(destpath, 
os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
                if err != nil {
                        return "", err
                }
+               defer f.Close()
+
+               wr = f
        }
 
        _, err = io.Copy(wr, raw.Body)
-
        if err != nil {
                return "", err
        }
 
-       // it streams to stdout or file, so no response returned
        return destpath, nil
 }
 
diff --git a/lxc/image.go b/lxc/image.go
index 98a1d6c..9536f78 100644
--- a/lxc/image.go
+++ b/lxc/image.go
@@ -122,9 +122,18 @@ lxc image copy [remote:]<image> <remote>: 
[--alias=ALIAS].. [--copy-aliases] [--
 lxc image delete [remote:]<image> [remote:][<image>...]
     Delete one or more images from the LXD image store.
 
-lxc image export [remote:]<image>
+lxc image export [remote:]<image> [target]
     Export an image from the LXD image store into a distributable tarball.
 
+    The output target is optional and defaults to the working directory.
+    The target may be an existing directory, file name, or "-" to specify
+       stdout.  The target MUST be a directory when exporting a split image.
+       If the target is a directory, the image's name (each part's name for
+       split images) as found in the database will be used for the exported
+       image.  If the target is a file (not a directory and not stdout), then
+       the appropriate extension will be appended to the provided file name
+       based on the algorithm used to compress the image. 
+
 lxc image info [remote:]<image>
     Print everything LXD knows about a given image.
 
diff --git a/po/lxd.pot b/po/lxd.pot
index 7ebaabd..99370df 100644
--- a/po/lxd.pot
+++ b/po/lxd.pot
@@ -7,7 +7,7 @@
 msgid   ""
 msgstr  "Project-Id-Version: lxd\n"
         "Report-Msgid-Bugs-To: lxc-devel@lists.linuxcontainers.org\n"
-        "POT-Creation-Date: 2016-07-06 19:44-0600\n"
+        "POT-Creation-Date: 2016-07-19 10:05-0700\n"
         "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
         "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
         "Language-Team: LANGUAGE <l...@li.org>\n"
@@ -77,7 +77,7 @@ msgid   "### This is a yaml representation of the profile.\n"
         "### Note that the name is shown but cannot be changed"
 msgstr  ""
 
-#: lxc/image.go:603
+#: lxc/image.go:612
 #, c-format
 msgid   "%s (%d more)"
 msgstr  ""
@@ -90,15 +90,15 @@ msgstr  ""
 msgid   "(none)"
 msgstr  ""
 
-#: lxc/image.go:624 lxc/image.go:666
+#: lxc/image.go:633 lxc/image.go:675
 msgid   "ALIAS"
 msgstr  ""
 
-#: lxc/image.go:628
+#: lxc/image.go:637
 msgid   "ARCH"
 msgstr  ""
 
-#: lxc/list.go:409
+#: lxc/list.go:419
 msgid   "ARCHITECTURE"
 msgstr  ""
 
@@ -111,7 +111,7 @@ msgstr  ""
 msgid   "Admin password for %s: "
 msgstr  ""
 
-#: lxc/image.go:356
+#: lxc/image.go:365
 msgid   "Aliases:"
 msgstr  ""
 
@@ -119,12 +119,12 @@ msgstr  ""
 msgid   "An environment variable of the form HOME=/home/foo"
 msgstr  ""
 
-#: lxc/image.go:339 lxc/info.go:90
+#: lxc/image.go:348 lxc/info.go:90
 #, c-format
 msgid   "Architecture: %s"
 msgstr  ""
 
-#: lxc/image.go:360
+#: lxc/image.go:369
 #, c-format
 msgid   "Auto update: %s"
 msgstr  ""
@@ -145,7 +145,7 @@ msgstr  ""
 msgid   "COMMON NAME"
 msgstr  ""
 
-#: lxc/list.go:410
+#: lxc/list.go:420
 msgid   "CREATED AT"
 msgstr  ""
 
@@ -187,7 +187,7 @@ msgstr  ""
 msgid   "Config key/value to apply to the new container"
 msgstr  ""
 
-#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:720 lxc/profile.go:217
+#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:729 lxc/profile.go:217
 #, c-format
 msgid   "Config parsing error: %s"
 msgstr  ""
@@ -210,7 +210,7 @@ msgstr  ""
 msgid   "Container published with fingerprint: %s"
 msgstr  ""
 
-#: lxc/image.go:157
+#: lxc/image.go:166
 msgid   "Copy aliases from source"
 msgstr  ""
 
@@ -220,7 +220,7 @@ msgid   "Copy containers within or in between lxd 
instances.\n"
         "lxc copy [remote:]<source container> [remote:]<destination container> 
[--ephemeral|e]"
 msgstr  ""
 
-#: lxc/image.go:271
+#: lxc/image.go:280
 #, c-format
 msgid   "Copying the image: %s"
 msgstr  ""
@@ -245,7 +245,7 @@ msgid   "Create a read-only snapshot of a container.\n"
         "lxc snapshot u1 snap0"
 msgstr  ""
 
-#: lxc/image.go:344 lxc/info.go:92
+#: lxc/image.go:353 lxc/info.go:92
 #, c-format
 msgid   "Created: %s"
 msgstr  ""
@@ -259,7 +259,7 @@ msgstr  ""
 msgid   "Creating the container"
 msgstr  ""
 
-#: lxc/image.go:627 lxc/image.go:668
+#: lxc/image.go:636 lxc/image.go:677
 msgid   "DESCRIPTION"
 msgstr  ""
 
@@ -281,7 +281,7 @@ msgstr  ""
 msgid   "Device %s removed from %s"
 msgstr  ""
 
-#: lxc/list.go:554
+#: lxc/list.go:564
 msgid   "EPHEMERAL"
 msgstr  ""
 
@@ -317,16 +317,16 @@ msgid   "Execute the specified command in a container.\n"
         "Mode defaults to non-interactive, interactive mode is selected if 
both stdin AND stdout are terminals (stderr is ignored)."
 msgstr  ""
 
-#: lxc/image.go:348
+#: lxc/image.go:357
 #, c-format
 msgid   "Expires: %s"
 msgstr  ""
 
-#: lxc/image.go:350
+#: lxc/image.go:359
 msgid   "Expires: never"
 msgstr  ""
 
-#: lxc/config.go:273 lxc/image.go:625 lxc/image.go:667
+#: lxc/config.go:273 lxc/image.go:634 lxc/image.go:676
 msgid   "FINGERPRINT"
 msgstr  ""
 
@@ -334,7 +334,7 @@ msgstr  ""
 msgid   "Fast mode (same as --columns=nsacPt"
 msgstr  ""
 
-#: lxc/image.go:337
+#: lxc/image.go:346
 #, c-format
 msgid   "Fingerprint: %s"
 msgstr  ""
@@ -357,7 +357,7 @@ msgstr  ""
 msgid   "Force using the local unix socket."
 msgstr  ""
 
-#: lxc/image.go:160 lxc/list.go:123
+#: lxc/image.go:169 lxc/list.go:123
 msgid   "Format"
 msgstr  ""
 
@@ -365,11 +365,11 @@ msgstr  ""
 msgid   "Generating a client certificate. This may take a minute..."
 msgstr  ""
 
-#: lxc/list.go:407
+#: lxc/list.go:417
 msgid   "IPV4"
 msgstr  ""
 
-#: lxc/list.go:408
+#: lxc/list.go:418
 msgid   "IPV6"
 msgstr  ""
 
@@ -389,11 +389,11 @@ msgstr  ""
 msgid   "Ignore the container state (only for start)."
 msgstr  ""
 
-#: lxc/image.go:276
+#: lxc/image.go:285
 msgid   "Image copied successfully!"
 msgstr  ""
 
-#: lxc/image.go:428
+#: lxc/image.go:437
 #, c-format
 msgid   "Image imported with fingerprint: %s"
 msgstr  ""
@@ -439,11 +439,11 @@ msgstr  ""
 msgid   "Ips:"
 msgstr  ""
 
-#: lxc/image.go:158
+#: lxc/image.go:167
 msgid   "Keep the image up to date after initial copy"
 msgstr  ""
 
-#: lxc/list.go:411
+#: lxc/list.go:421
 msgid   "LAST USED AT"
 msgstr  ""
 
@@ -530,7 +530,7 @@ msgstr  ""
 msgid   "Log:"
 msgstr  ""
 
-#: lxc/image.go:156
+#: lxc/image.go:165
 msgid   "Make image public"
 msgstr  ""
 
@@ -669,9 +669,18 @@ msgid   "Manipulate container images.\n"
         "lxc image delete [remote:]<image> [remote:][<image>...]\n"
         "    Delete one or more images from the LXD image store.\n"
         "\n"
-        "lxc image export [remote:]<image>\n"
+        "lxc image export [remote:]<image> [target]\n"
         "    Export an image from the LXD image store into a distributable 
tarball.\n"
         "\n"
+        "    The output target is optional and defaults to the working 
directory.\n"
+        "    The target may be an existing directory, file name, or \"-\" to 
specify\n"
+        "      stdout.  The target MUST be a directory when exporting a split 
image.\n"
+        "      If the target is a directory, the image's name (each part's 
name for\n"
+        "      split images) as found in the database will be used for the 
exported\n"
+        "      image.  If the target is a file (not a directory and not 
stdout), then\n"
+        "      the appropriate extension will be appended to the provided file 
name\n"
+        "      based on the algorithm used to compress the image. \n"
+        "\n"
         "lxc image info [remote:]<image>\n"
         "    Print everything LXD knows about a given image.\n"
         "\n"
@@ -742,7 +751,7 @@ msgstr  ""
 msgid   "Must supply container name for: "
 msgstr  ""
 
-#: lxc/list.go:412 lxc/remote.go:376
+#: lxc/list.go:422 lxc/remote.go:376
 msgid   "NAME"
 msgstr  ""
 
@@ -755,7 +764,7 @@ msgstr  ""
 msgid   "Name: %s"
 msgstr  ""
 
-#: lxc/image.go:159 lxc/publish.go:33
+#: lxc/image.go:168 lxc/publish.go:33
 msgid   "New alias to define at target"
 msgstr  ""
 
@@ -771,7 +780,7 @@ msgstr  ""
 msgid   "Only https URLs are supported for simplestreams"
 msgstr  ""
 
-#: lxc/image.go:420
+#: lxc/image.go:429
 msgid   "Only https:// is supported for remote image import."
 msgstr  ""
 
@@ -779,7 +788,7 @@ msgstr  ""
 msgid   "Options:"
 msgstr  ""
 
-#: lxc/image.go:524
+#: lxc/image.go:533
 #, c-format
 msgid   "Output is in %s"
 msgstr  ""
@@ -788,15 +797,15 @@ msgstr  ""
 msgid   "Override the terminal mode (auto, interactive or non-interactive)"
 msgstr  ""
 
-#: lxc/list.go:556
+#: lxc/list.go:566
 msgid   "PERSISTENT"
 msgstr  ""
 
-#: lxc/list.go:413
+#: lxc/list.go:423
 msgid   "PID"
 msgstr  ""
 
-#: lxc/list.go:414
+#: lxc/list.go:424
 msgid   "PROFILES"
 msgstr  ""
 
@@ -804,7 +813,7 @@ msgstr  ""
 msgid   "PROTOCOL"
 msgstr  ""
 
-#: lxc/image.go:626 lxc/remote.go:379
+#: lxc/image.go:635 lxc/remote.go:379
 msgid   "PUBLIC"
 msgstr  ""
 
@@ -843,7 +852,7 @@ msgstr  ""
 msgid   "Press enter to open the editor again"
 msgstr  ""
 
-#: lxc/config.go:532 lxc/config.go:597 lxc/image.go:721
+#: lxc/config.go:532 lxc/config.go:597 lxc/image.go:730
 msgid   "Press enter to start the editor again"
 msgstr  ""
 
@@ -904,7 +913,7 @@ msgstr  ""
 msgid   "Profiles: %s"
 msgstr  ""
 
-#: lxc/image.go:352
+#: lxc/image.go:361
 msgid   "Properties:"
 msgstr  ""
 
@@ -912,7 +921,7 @@ msgstr  ""
 msgid   "Public image server"
 msgstr  ""
 
-#: lxc/image.go:340
+#: lxc/image.go:349
 #, c-format
 msgid   "Public: %s"
 msgstr  ""
@@ -945,15 +954,15 @@ msgstr  ""
 msgid   "Retrieving image: %s"
 msgstr  ""
 
-#: lxc/image.go:629
+#: lxc/image.go:638
 msgid   "SIZE"
 msgstr  ""
 
-#: lxc/list.go:415
+#: lxc/list.go:425
 msgid   "SNAPSHOTS"
 msgstr  ""
 
-#: lxc/list.go:416
+#: lxc/list.go:426
 msgid   "STATE"
 msgstr  ""
 
@@ -1006,7 +1015,7 @@ msgstr  ""
 msgid   "Show the container's last 100 log lines?"
 msgstr  ""
 
-#: lxc/image.go:338
+#: lxc/image.go:347
 #, c-format
 msgid   "Size: %.2fMB"
 msgstr  ""
@@ -1015,7 +1024,7 @@ msgstr  ""
 msgid   "Snapshots:"
 msgstr  ""
 
-#: lxc/image.go:362
+#: lxc/image.go:371
 msgid   "Source:"
 msgstr  ""
 
@@ -1049,7 +1058,7 @@ msgstr  ""
 msgid   "Swap (peak)"
 msgstr  ""
 
-#: lxc/list.go:417
+#: lxc/list.go:427
 msgid   "TYPE"
 msgstr  ""
 
@@ -1082,7 +1091,7 @@ msgstr  ""
 msgid   "Time to wait for the container before killing it."
 msgstr  ""
 
-#: lxc/image.go:341
+#: lxc/image.go:350
 msgid   "Timestamps:"
 msgstr  ""
 
@@ -1090,7 +1099,7 @@ msgstr  ""
 msgid   "To start your first container, try: lxc launch ubuntu:16.04"
 msgstr  ""
 
-#: lxc/image.go:411
+#: lxc/image.go:420
 #, c-format
 msgid   "Transferring image: %d%%"
 msgstr  ""
@@ -1108,7 +1117,7 @@ msgstr  ""
 msgid   "Type: persistent"
 msgstr  ""
 
-#: lxc/image.go:630
+#: lxc/image.go:639
 msgid   "UPLOAD DATE"
 msgstr  ""
 
@@ -1120,7 +1129,7 @@ msgstr  ""
 msgid   "Unable to read remote TLS certificate"
 msgstr  ""
 
-#: lxc/image.go:346
+#: lxc/image.go:355
 #, c-format
 msgid   "Uploaded: %s"
 msgstr  ""
@@ -1182,11 +1191,11 @@ msgstr  ""
 msgid   "didn't get any affected image, container or snapshot from server"
 msgstr  ""
 
-#: lxc/image.go:332
+#: lxc/image.go:341
 msgid   "disabled"
 msgstr  ""
 
-#: lxc/image.go:334
+#: lxc/image.go:343
 msgid   "enabled"
 msgstr  ""
 
@@ -1204,7 +1213,7 @@ msgstr  ""
 msgid   "got bad version"
 msgstr  ""
 
-#: lxc/image.go:327 lxc/image.go:606
+#: lxc/image.go:336 lxc/image.go:615
 msgid   "no"
 msgstr  ""
 
@@ -1262,7 +1271,7 @@ msgstr  ""
 msgid   "wrong number of subcommand arguments"
 msgstr  ""
 
-#: lxc/delete.go:45 lxc/image.go:329 lxc/image.go:610
+#: lxc/delete.go:45 lxc/image.go:338 lxc/image.go:619
 msgid   "yes"
 msgstr  ""
 
diff --git a/test/suites/basic.sh b/test/suites/basic.sh
index 7e4f915..b45ef7b 100644
--- a/test/suites/basic.sh
+++ b/test/suites/basic.sh
@@ -48,11 +48,16 @@ test_basic_usage() {
   lxc image import "${LXD_DIR}/testimage.tar.xz" --alias testimage
   rm "${LXD_DIR}/testimage.tar.xz"
 
-  # Test filename for image export (should be "out")
+  # Test filename for image export
   lxc image export testimage "${LXD_DIR}/"
   [ "${sum}" = "$(sha256sum "${LXD_DIR}/testimage.tar.xz" | cut -d' ' -f1)" ]
   rm "${LXD_DIR}/testimage.tar.xz"
 
+  # Test custom filename for image export
+  lxc image export testimage "${LXD_DIR}/foo"
+  [ "${sum}" = "$(sha256sum "${LXD_DIR}/foo.tar.xz" | cut -d' ' -f1)" ]
+  rm "${LXD_DIR}/foo.tar.xz"
+
 
   # Test image export with a split image.
   deps/import-busybox --split --alias splitimage
diff --git a/test/suites/remote.sh b/test/suites/remote.sh
index 7f1ba36..cf0f1dd 100644
--- a/test/suites/remote.sh
+++ b/test/suites/remote.sh
@@ -75,10 +75,10 @@ test_remote_usage() {
   lxc_remote remote add lxd2 "${LXD2_ADDR}" --accept-certificate --password foo
 
   # we need a public image on localhost
-  lxc_remote image export localhost:testimage "${LXD_DIR}/foo.img"
+  img=$(lxc_remote image export localhost:testimage "${LXD_DIR}/foo" | grep -o 
"foo.*")
   lxc_remote image delete localhost:testimage
-  sum=$(sha256sum "${LXD_DIR}/foo.img" | cut -d' ' -f1)
-  lxc_remote image import "${LXD_DIR}/foo.img" localhost: --public
+  sum=$(sha256sum "${LXD_DIR}/${img}" | cut -d' ' -f1)
+  lxc_remote image import "${LXD_DIR}/${img}" localhost: --public
   lxc_remote image alias create localhost:testimage "${sum}"
 
   lxc_remote image delete "lxd2:${sum}" || true
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to