[lxc-devel] [lxd/master] Document image export target behavior and fix bugs
sean-jc on Github
lxc-bot at linuxcontainers.org
Tue Jul 19 18:18:46 UTC 2016
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 833 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160719/a5ec02c2/attachment.bin>
-------------- next part --------------
From 8d8f813ca0dd80a24f9635092b2b870ced8cc2ff Mon Sep 17 00:00:00 2001
From: Sean Christopherson <sean.j.christopherson at 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.christopherson at 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 at 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 at ADDRESS>\n"
"Language-Team: LANGUAGE <LL at 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
More information about the lxc-devel
mailing list