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