The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/1588
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) ===
From a3f49ffaecb82e280134e517494cb74251a1e7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Wed, 10 Feb 2016 11:24:41 -0500 Subject: [PATCH 1/6] Remove backward compatibility code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This has been around for a long time now and nobody should be using those old versions anymore, so lets clean things up before we get to 2.0. Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- client.go | 63 ++++++++++++++++++--------------------------------------- lxc/copy.go | 52 ++++++++--------------------------------------- lxc/image.go | 6 ++---- lxd/images.go | 5 +---- lxd/migrate.go | 15 ++------------ shared/image.go | 30 ++++++++++----------------- shared/util.go | 16 --------------- 7 files changed, 44 insertions(+), 143 deletions(-) diff --git a/client.go b/client.go index 6bb9550..136b96c 100644 --- a/client.go +++ b/client.go @@ -530,8 +530,7 @@ func (c *Client) CopyImage(image string, dest *Client, copy_aliases bool, aliase "server": c.BaseURL, "fingerprint": fingerprint} - // FIXME: InterfaceToBool is there for backward compatibility - if !shared.InterfaceToBool(info.Public) { + if !info.Public { var secret string resp, err := c.post("images/"+fingerprint+"/secret", nil, Async) @@ -540,19 +539,13 @@ func (c *Client) CopyImage(image string, dest *Client, copy_aliases bool, aliase } op, err := resp.MetadataAsOperation() - if err == nil && op.Metadata != nil { - secret, err = op.Metadata.GetString("secret") - if err != nil { - return err - } - } else { - // FIXME: This is a backward compatibility codepath - md := secretMd{} - if err := json.Unmarshal(resp.Metadata, &md); err != nil { - return err - } + if err != nil { + return err + } - secret = md.Secret + secret, err = op.Metadata.GetString("secret") + if err != nil { + return err } source["secret"] = secret @@ -1158,8 +1151,7 @@ func (c *Client) Init(name string, imgremote string, image string, profiles *[]s return nil, fmt.Errorf("The image architecture is incompatible with the target server") } - // FIXME: InterfaceToBool is there for backward compatibility - if !shared.InterfaceToBool(imageinfo.Public) { + if !imageinfo.Public { var secret string resp, err := tmpremote.post("images/"+fingerprint+"/secret", nil, Async) @@ -1168,19 +1160,13 @@ func (c *Client) Init(name string, imgremote string, image string, profiles *[]s } op, err := resp.MetadataAsOperation() - if err == nil && op.Metadata != nil { - secret, err = op.Metadata.GetString("secret") - if err != nil { - return nil, err - } - } else { - // FIXME: This is a backward compatibility codepath - md := secretMd{} - if err := json.Unmarshal(resp.Metadata, &md); err != nil { - return nil, err - } + if err != nil { + return nil, err + } - secret = md.Secret + secret, err = op.Metadata.GetString("secret") + if err != nil { + return nil, err } source["secret"] = secret @@ -1332,22 +1318,13 @@ func (c *Client) Exec(name string, cmd []string, env map[string]string, var fds shared.Jmap op, err := resp.MetadataAsOperation() - if err == nil && op.Metadata != nil { - fds, err = op.Metadata.GetMap("fds") - if err != nil { - return -1, err - } - } else { - // FIXME: This is a backward compatibility codepath - md := execMd{} - if err := json.Unmarshal(resp.Metadata, &md); err != nil { - return -1, err - } + if err != nil { + return -1, err + } - fds, err = shared.ParseMetadata(md.FDs) - if err != nil { - return -1, err - } + fds, err = op.Metadata.GetMap("fds") + if err != nil { + return -1, err } if controlHandler != nil { diff --git a/lxc/copy.go b/lxc/copy.go index a07480e..0f3d20d 100644 --- a/lxc/copy.go +++ b/lxc/copy.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "fmt" "strings" @@ -122,15 +121,12 @@ func copyContainer(config *lxd.Config, sourceResource string, destResource strin secrets := map[string]string{} op, err := sourceWSResponse.MetadataAsOperation() - if err == nil && op.Metadata != nil { - for k, v := range *op.Metadata { - secrets[k] = v.(string) - } - } else { - // FIXME: This is a backward compatibility codepath - if err := json.Unmarshal(sourceWSResponse.Metadata, &secrets); err != nil { - return err - } + if err != nil { + return err + } + + for k, v := range *op.Metadata { + secrets[k] = v.(string) } addresses, err := source.Addresses() @@ -146,55 +142,23 @@ func copyContainer(config *lxd.Config, sourceResource string, destResource strin * course, if all the errors are websocket errors, let's just * report that. */ - var realError error - for _, addr := range addresses { var migration *lxd.Response sourceWSUrl := "https://" + addr + sourceWSResponse.Operation migration, err = dest.MigrateFrom(destName, sourceWSUrl, secrets, status.Architecture, status.Config, status.Devices, status.Profiles, baseImage, ephemeral == 1) if err != nil { - if !strings.Contains(err.Error(), "websocket: bad handshake") { - realError = err - } - shared.Debugf("intermediate error: %s", err) continue } if err = dest.WaitForSuccess(migration.Operation); err != nil { - if !strings.Contains(err.Error(), "websocket: bad handshake") { - realError = err - } - shared.Debugf("intermediate error: %s", err) - // FIXME: This is a backward compatibility codepath - sourceWSUrl := "wss://" + addr + sourceWSResponse.Operation + "/websocket" - - migration, err = dest.MigrateFrom(destName, sourceWSUrl, secrets, status.Architecture, status.Config, status.Devices, status.Profiles, baseImage, ephemeral == 1) - if err != nil { - if !strings.Contains(err.Error(), "websocket: bad handshake") { - realError = err - } - shared.Debugf("intermediate error: %s", err) - continue - } - - if err = dest.WaitForSuccess(migration.Operation); err != nil { - if !strings.Contains(err.Error(), "websocket: bad handshake") { - realError = err - } - shared.Debugf("intermediate error: %s", err) - continue - } + return err } return nil } - if realError != nil { - return realError - } else { - return err - } + return err } } diff --git a/lxc/image.go b/lxc/image.go index 9ace054..2be6fe9 100644 --- a/lxc/image.go +++ b/lxc/image.go @@ -255,8 +255,7 @@ func (c *imageCmd) run(config *lxd.Config, args []string) error { fmt.Printf(i18n.G("Fingerprint: %s")+"\n", info.Fingerprint) public := i18n.G("no") - // FIXME: InterfaceToBool is there for backward compatibility - if shared.InterfaceToBool(info) { + if info.Public { public = i18n.G("yes") } @@ -504,8 +503,7 @@ func showImages(images []shared.ImageInfo, filters []string) error { public := i18n.G("no") description := findDescription(image.Properties) - // FIXME: InterfaceToBool is there for backward compatibility - if shared.InterfaceToBool(image.Public) { + if image.Public { public = i18n.G("yes") } diff --git a/lxd/images.go b/lxd/images.go index c69f8b9..a24de23 100644 --- a/lxd/images.go +++ b/lxd/images.go @@ -660,10 +660,7 @@ func imageBuildFromInfo(d *Daemon, info shared.ImageInfo) (metadata map[string]s info.Fingerprint, info.Filename, info.Size, - - // FIXME: InterfaceToBool is there for backward compatibility - shared.InterfaceToBool(info.Public), - + info.Public, info.Architecture, info.CreationDate, info.ExpiryDate, diff --git a/lxd/migrate.go b/lxd/migrate.go index 46f2efd..254ad4d 100644 --- a/lxd/migrate.go +++ b/lxd/migrate.go @@ -446,20 +446,9 @@ func (c *migrationSink) connectWithSecret(secret string) (*websocket.Conn, error query := url.Values{"secret": []string{secret}} // The URL is a https URL to the operation, mangle to be a wss URL to the secret - url := c.url - if strings.HasPrefix(url, "https://") { - url = fmt.Sprintf("wss://%s", strings.TrimPrefix(url, "https://")) - } - - // FIXME: This is a backward compatibility codepath - if !strings.HasSuffix(url, "/websocket") { - url = fmt.Sprintf("%s/websocket", url) - } - - // Append the secret suffix - url = fmt.Sprintf("%s?%s", url, query.Encode()) + wsUrl := fmt.Sprintf("wss://%s/websocket?%s", strings.TrimPrefix(c.url, "https://"), query.Encode()) - return lxd.WebsocketDial(c.dialer, url) + return lxd.WebsocketDial(c.dialer, wsUrl) } func (c *migrationSink) do() error { diff --git a/shared/image.go b/shared/image.go index 692ed3c..c3d3073 100644 --- a/shared/image.go +++ b/shared/image.go @@ -15,14 +15,11 @@ type ImageInfo struct { Fingerprint string `json:"fingerprint"` Filename string `json:"filename"` Properties map[string]string `json:"properties"` - - // FIXME: This is an interface{] instead of a bool for backward compatibility - Public interface{} `json:"public"` - - Size int64 `json:"size"` - CreationDate int64 `json:"created_at"` - ExpiryDate int64 `json:"expires_at"` - UploadDate int64 `json:"uploaded_at"` + Public bool `json:"public"` + Size int64 `json:"size"` + CreationDate int64 `json:"created_at"` + ExpiryDate int64 `json:"expires_at"` + UploadDate int64 `json:"uploaded_at"` } /* @@ -37,21 +34,16 @@ type BriefImageInfo struct { func (i *ImageInfo) BriefInfo() BriefImageInfo { retstate := BriefImageInfo{ Properties: i.Properties, - - // FIXME: InterfaceToBool is there for backward compatibility - Public: InterfaceToBool(i.Public)} + Public: i.Public} return retstate } type ImageBaseInfo struct { - Id int - Fingerprint string - Filename string - Size int64 - - // FIXME: This is an interface{] instead of a bool for backward compatibility - Public interface{} - + Id int + Fingerprint string + Filename string + Size int64 + Public bool Architecture int CreationDate int64 ExpiryDate int64 diff --git a/shared/util.go b/shared/util.go index 3615789..c3c6a29 100644 --- a/shared/util.go +++ b/shared/util.go @@ -501,22 +501,6 @@ func ValidHostname(name string) bool { return true } -// FIXME: InterfaceToBool is there for backward compatibility -func InterfaceToBool(value interface{}) bool { - switch t := value.(type) { - case bool: - return t - case float32: - return t == 1 - case float64: - return t == 1 - case int: - return t == 1 - default: - return false - } -} - func TextEditor(inPath string, inContent []byte) ([]byte, error) { var f *os.File var err error From 6cec2158e513dade1af838be3ced08b0e8a1da6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Wed, 10 Feb 2016 14:08:33 -0500 Subject: [PATCH 2/6] Fail when unsetting a key that's not currentl set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #1477 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/config.go | 40 ++++++++++++++++++++++++--- po/lxd.pot | 86 +++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 84 insertions(+), 42 deletions(-) diff --git a/lxc/config.go b/lxc/config.go index 19709bb..1f95535 100644 --- a/lxc/config.go +++ b/lxc/config.go @@ -90,7 +90,7 @@ To set the server trust password: lxc config set core.trust_password blah`) } -func doSet(config *lxd.Config, args []string) error { +func doSet(config *lxd.Config, args []string, unset bool) error { if len(args) != 4 { return errArgs } @@ -108,11 +108,23 @@ func doSet(config *lxd.Config, args []string) error { if !terminal.IsTerminal(int(syscall.Stdin)) && value == "-" { buf, err := ioutil.ReadAll(os.Stdin) if err != nil { - return fmt.Errorf("Can't read from stdin: %s", err) + return fmt.Errorf(i18n.G("Can't read from stdin: %s"), err) } value = string(buf[:]) } + if unset { + st, err := d.ContainerStatus(container) + if err != nil { + return err + } + + _, ok := st.Config[key] + if !ok { + return fmt.Errorf(i18n.G("Can't unset key '%s', it's not currently set."), key) + } + } + return d.SetContainerConfig(container, key, value) } @@ -135,6 +147,16 @@ func (c *configCmd) run(config *lxd.Config, args []string) error { return err } + ss, err := c.ServerStatus() + if err != nil { + return err + } + + _, ok := ss.Config[args[1]] + if !ok { + return fmt.Errorf(i18n.G("Can't unset key '%s', it's not currently set."), args[1]) + } + _, err = c.SetServerConfig(args[1], "") return err } @@ -147,13 +169,23 @@ func (c *configCmd) run(config *lxd.Config, args []string) error { return err } + ss, err := c.ServerStatus() + if err != nil { + return err + } + + _, ok := ss.Config[args[1]] + if !ok { + return fmt.Errorf(i18n.G("Can't unset key '%s', it's not currently set."), args[1]) + } + _, err = c.SetServerConfig(args[2], "") return err } // Deal with container args = append(args, "") - return doSet(config, args) + return doSet(config, args, true) case "set": if len(args) < 3 { @@ -184,7 +216,7 @@ func (c *configCmd) run(config *lxd.Config, args []string) error { } // Deal with container - return doSet(config, args) + return doSet(config, args, false) case "trust": if len(args) < 2 { diff --git a/po/lxd.pot b/po/lxd.pot index 5baf553..f423d71 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-02-09 15:53-0700\n" + "POT-Creation-Date: 2016-02-10 14:08-0500\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" @@ -65,7 +65,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:501 +#: lxc/image.go:500 #, c-format msgid "%s (%d more)" msgstr "" @@ -78,11 +78,11 @@ msgstr "" msgid "(none)" msgstr "" -#: lxc/image.go:522 lxc/image.go:544 +#: lxc/image.go:520 lxc/image.go:542 msgid "ALIAS" msgstr "" -#: lxc/image.go:526 +#: lxc/image.go:524 msgid "ARCH" msgstr "" @@ -95,7 +95,7 @@ msgstr "" msgid "Admin password for %s: " msgstr "" -#: lxc/image.go:282 +#: lxc/image.go:281 msgid "Aliases:" msgstr "" @@ -103,7 +103,7 @@ msgstr "" msgid "An environment variable of the form HOME=/home/foo" msgstr "" -#: lxc/image.go:265 +#: lxc/image.go:264 #, c-format msgid "Architecture: %s" msgstr "" @@ -112,10 +112,20 @@ msgstr "" msgid "Available commands:" msgstr "" -#: lxc/config.go:232 +#: lxc/config.go:264 msgid "COMMON NAME" msgstr "" +#: lxc/config.go:111 +#, c-format +msgid "Can't read from stdin: %s" +msgstr "" + +#: lxc/config.go:124 lxc/config.go:157 lxc/config.go:179 +#, c-format +msgid "Can't unset key '%s', it's not currently set." +msgstr "" + #: lxc/profile.go:329 msgid "Cannot provide container name to list" msgstr "" @@ -144,7 +154,7 @@ msgstr "" msgid "Config key/value to apply to the new container" msgstr "" -#: lxc/config.go:458 lxc/config.go:523 lxc/image.go:599 lxc/profile.go:185 +#: lxc/config.go:490 lxc/config.go:555 lxc/image.go:597 lxc/profile.go:185 #, c-format msgid "Config parsing error: %s" msgstr "" @@ -171,7 +181,7 @@ msgstr "" msgid "Copy aliases from source" msgstr "" -#: lxc/copy.go:23 +#: lxc/copy.go:22 msgid "Copy containers within or in between lxd instances.\n" "\n" "lxc copy [remote:]<source container> [remote:]<destination container> [--ephemeral|e]" @@ -198,7 +208,7 @@ msgid "Create a read-only snapshot of a container.\n" "lxc snapshot u1 snap0" msgstr "" -#: lxc/image.go:270 +#: lxc/image.go:269 #, c-format msgid "Created: %s" msgstr "" @@ -212,7 +222,7 @@ msgstr "" msgid "Creating the container" msgstr "" -#: lxc/image.go:525 +#: lxc/image.go:523 msgid "DESCRIPTION" msgstr "" @@ -224,12 +234,12 @@ msgid "Delete containers or container snapshots.\n" "Destroy containers or snapshots with any attached data (configuration, snapshots, ...)." msgstr "" -#: lxc/config.go:571 +#: lxc/config.go:603 #, c-format msgid "Device %s added to %s" msgstr "" -#: lxc/config.go:599 +#: lxc/config.go:631 #, c-format msgid "Device %s removed from %s" msgstr "" @@ -238,7 +248,7 @@ msgstr "" msgid "EPHEMERAL" msgstr "" -#: lxc/config.go:234 +#: lxc/config.go:266 msgid "EXPIRY DATE" msgstr "" @@ -254,7 +264,7 @@ msgstr "" msgid "Environment:" msgstr "" -#: lxc/copy.go:30 lxc/copy.go:31 lxc/init.go:136 lxc/init.go:137 lxc/launch.go:40 lxc/launch.go:41 +#: lxc/copy.go:29 lxc/copy.go:30 lxc/init.go:136 lxc/init.go:137 lxc/launch.go:40 lxc/launch.go:41 msgid "Ephemeral container" msgstr "" @@ -268,16 +278,16 @@ msgid "Execute the specified command in a container.\n" "lxc exec [remote:]container [--mode=auto|interactive|non-interactive] [--env EDITOR=/usr/bin/vim]... <command>" msgstr "" -#: lxc/image.go:274 +#: lxc/image.go:273 #, c-format msgid "Expires: %s" msgstr "" -#: lxc/image.go:276 +#: lxc/image.go:275 msgid "Expires: never" msgstr "" -#: lxc/config.go:231 lxc/image.go:523 lxc/image.go:545 +#: lxc/config.go:263 lxc/image.go:521 lxc/image.go:543 msgid "FINGERPRINT" msgstr "" @@ -316,7 +326,7 @@ msgstr "" msgid "IPV6" msgstr "" -#: lxc/config.go:233 +#: lxc/config.go:265 msgid "ISSUE DATE" msgstr "" @@ -328,7 +338,7 @@ msgstr "" msgid "Image copied successfully!" msgstr "" -#: lxc/image.go:340 +#: lxc/image.go:339 #, c-format msgid "Image imported with fingerprint: %s" msgstr "" @@ -627,15 +637,15 @@ msgstr "" msgid "New alias to define at target" msgstr "" -#: lxc/config.go:245 +#: lxc/config.go:277 msgid "No certificate provided to add" msgstr "" -#: lxc/config.go:268 +#: lxc/config.go:300 msgid "No fingerprint specified." msgstr "" -#: lxc/image.go:332 +#: lxc/image.go:331 msgid "Only https:// is supported for remote image import." msgstr "" @@ -643,7 +653,7 @@ msgstr "" msgid "Options:" msgstr "" -#: lxc/image.go:426 +#: lxc/image.go:425 #, c-format msgid "Output is in %s" msgstr "" @@ -656,7 +666,7 @@ msgstr "" msgid "PID" msgstr "" -#: lxc/image.go:524 lxc/remote.go:273 +#: lxc/image.go:522 lxc/remote.go:273 msgid "PUBLIC" msgstr "" @@ -682,7 +692,7 @@ msgstr "" msgid "Press enter to open the editor again" msgstr "" -#: lxc/config.go:459 lxc/config.go:524 lxc/image.go:600 +#: lxc/config.go:491 lxc/config.go:556 lxc/image.go:598 msgid "Press enter to start the editor again" msgstr "" @@ -733,7 +743,7 @@ msgstr "" msgid "Profiles: %s" msgstr "" -#: lxc/image.go:278 +#: lxc/image.go:277 msgid "Properties:" msgstr "" @@ -741,7 +751,7 @@ msgstr "" msgid "Public image server" msgstr "" -#: lxc/image.go:266 +#: lxc/image.go:265 #, c-format msgid "Public: %s" msgstr "" @@ -761,7 +771,7 @@ msgstr "" msgid "Retrieving image: %s" msgstr "" -#: lxc/image.go:527 +#: lxc/image.go:525 msgid "SIZE" msgstr "" @@ -814,7 +824,7 @@ msgstr "" msgid "Show the container's last 100 log lines?" msgstr "" -#: lxc/image.go:263 +#: lxc/image.go:262 #, c-format msgid "Size: %.2fMB" msgstr "" @@ -845,7 +855,7 @@ msgstr "" msgid "Time to wait for the container before killing it." msgstr "" -#: lxc/image.go:267 +#: lxc/image.go:266 msgid "Timestamps:" msgstr "" @@ -862,7 +872,7 @@ msgstr "" msgid "Type: persistent" msgstr "" -#: lxc/image.go:528 +#: lxc/image.go:526 msgid "UPLOAD DATE" msgstr "" @@ -870,7 +880,7 @@ msgstr "" msgid "URL" msgstr "" -#: lxc/image.go:272 +#: lxc/image.go:271 #, c-format msgid "Uploaded: %s" msgstr "" @@ -912,7 +922,7 @@ msgstr "" msgid "bad result type from action" msgstr "" -#: lxc/copy.go:79 +#: lxc/copy.go:78 msgid "can't copy to the same container name" msgstr "" @@ -942,11 +952,11 @@ msgstr "" msgid "got bad version" msgstr "" -#: lxc/image.go:256 lxc/image.go:504 +#: lxc/image.go:256 lxc/image.go:503 msgid "no" msgstr "" -#: lxc/copy.go:101 +#: lxc/copy.go:100 msgid "not all the profiles from the source exist on the target" msgstr "" @@ -982,11 +992,11 @@ msgstr "" msgid "wrong number of subcommand arguments" msgstr "" -#: lxc/image.go:260 lxc/image.go:509 +#: lxc/image.go:259 lxc/image.go:507 msgid "yes" msgstr "" -#: lxc/copy.go:39 +#: lxc/copy.go:38 msgid "you must specify a source container name" msgstr "" From 7959797f3fd9698bc4552b8183c47465ff112f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Wed, 10 Feb 2016 14:40:32 -0500 Subject: [PATCH 3/6] Implement interactive mode in delete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #781 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/delete.go | 44 ++++++++++++++++++++++++++++++++++++-------- po/lxd.pot | 29 +++++++++++++++++++++++++---- test/main.sh | 2 +- test/suites/basic.sh | 4 ++-- test/suites/filemanip.sh | 2 +- 5 files changed, 65 insertions(+), 16 deletions(-) diff --git a/lxc/delete.go b/lxc/delete.go index d241aea..d09498d 100644 --- a/lxc/delete.go +++ b/lxc/delete.go @@ -1,14 +1,21 @@ package main import ( + "bufio" "fmt" + "os" + "strings" "github.com/lxc/lxd" "github.com/lxc/lxd/i18n" "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/gnuflag" ) -type deleteCmd struct{} +type deleteCmd struct { + force bool + interactive bool +} func (c *deleteCmd) showByDefault() bool { return true @@ -23,9 +30,24 @@ lxc delete [remote:]<container>[/<snapshot>] [remote:][<container>[/<snapshot>]. Destroy containers or snapshots with any attached data (configuration, snapshots, ...).`) } -func (c *deleteCmd) flags() {} +func (c *deleteCmd) flags() { + gnuflag.BoolVar(&c.force, "f", false, i18n.G("Force the removal of stopped containers.")) + gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force the removal of stopped containers.")) + gnuflag.BoolVar(&c.interactive, "i", false, i18n.G("Require user confirmation.")) + gnuflag.BoolVar(&c.interactive, "interactive", false, i18n.G("Require user confirmation.")) +} + +func (c *deleteCmd) doDelete(d *lxd.Client, name string) error { + if c.interactive { + reader := bufio.NewReader(os.Stdin) + fmt.Printf(i18n.G("Remove %s (yes/no): "), name) + input, _ := reader.ReadString('\n') + input = strings.TrimSuffix(input, "\n") + if !shared.StringInSlice(strings.ToLower(input), []string{i18n.G("yes")}) { + return fmt.Errorf(i18n.G("User aborted delete operation.")) + } + } -func doDelete(d *lxd.Client, name string) error { resp, err := d.Delete(name) if err != nil { return err @@ -47,14 +69,20 @@ func (c *deleteCmd) run(config *lxd.Config, args []string) error { return err } - ct, err := d.ContainerStatus(name) + if shared.IsSnapshot(name) { + return c.doDelete(d, name) + } + ct, err := d.ContainerStatus(name) if err != nil { - // Could be a snapshot - return doDelete(d, name) + return err } if ct.Status.StatusCode != 0 && ct.Status.StatusCode != shared.Stopped { + if !c.force { + return fmt.Errorf(i18n.G("The container is currently running, stop it first or pass --force.")) + } + resp, err := d.Action(name, shared.Stop, -1, true) if err != nil { return err @@ -73,10 +101,10 @@ func (c *deleteCmd) run(config *lxd.Config, args []string) error { return nil } } - if err := doDelete(d, name); err != nil { + + if err := c.doDelete(d, name); err != nil { return err } } return nil - } diff --git a/po/lxd.pot b/po/lxd.pot index f423d71..2afd38d 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-02-10 14:08-0500\n" + "POT-Creation-Date: 2016-02-10 14:55-0500\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" @@ -226,7 +226,7 @@ msgstr "" msgid "DESCRIPTION" msgstr "" -#: lxc/delete.go:18 +#: lxc/delete.go:25 msgid "Delete containers or container snapshots.\n" "\n" "lxc delete [remote:]<container>[/<snapshot>] [remote:][<container>[/<snapshot>]...]\n" @@ -310,6 +310,10 @@ msgstr "" msgid "Force the container to shutdown." msgstr "" +#: lxc/delete.go:34 lxc/delete.go:35 +msgid "Force the removal of stopped containers." +msgstr "" + #: lxc/main.go:56 msgid "Force using the local unix socket." msgstr "" @@ -766,6 +770,15 @@ msgstr "" msgid "Remote admin password" msgstr "" +#: lxc/delete.go:43 +#, c-format +msgid "Remove %s (yes/no): " +msgstr "" + +#: lxc/delete.go:36 lxc/delete.go:37 +msgid "Require user confirmation." +msgstr "" + #: lxc/init.go:244 #, c-format msgid "Retrieving image: %s" @@ -843,10 +856,14 @@ msgstr "" msgid "Status: %s" msgstr "" -#: lxc/delete.go:69 +#: lxc/delete.go:97 msgid "Stopping container failed!" msgstr "" +#: lxc/delete.go:83 +msgid "The container is currently running, stop it first or pass --force." +msgstr "" + #: lxc/publish.go:57 msgid "There is no \"image name\". Did you want an alias?" msgstr "" @@ -894,6 +911,10 @@ msgstr "" msgid "Usage: lxc [subcommand] [options]" msgstr "" +#: lxc/delete.go:47 +msgid "User aborted delete operation." +msgstr "" + #: lxc/restore.go:35 msgid "Whether or not to restore the container's running state from snapshot (if available)" msgstr "" @@ -992,7 +1013,7 @@ msgstr "" msgid "wrong number of subcommand arguments" msgstr "" -#: lxc/image.go:259 lxc/image.go:507 +#: lxc/delete.go:46 lxc/image.go:259 lxc/image.go:507 msgid "yes" msgstr "" diff --git a/test/main.sh b/test/main.sh index 55e05d6..e13bd2b 100755 --- a/test/main.sh +++ b/test/main.sh @@ -183,7 +183,7 @@ kill_lxd() { # Delete all containers echo "==> Deleting all containers" for container in $(lxc list --force-local | tail -n+3 | grep "^| " | cut -d' ' -f2); do - lxc delete "${container}" --force-local || true + lxc delete "${container}" --force-local -f || true done # Delete all images diff --git a/test/suites/basic.sh b/test/suites/basic.sh index 6b55f13..0a1e31a 100644 --- a/test/suites/basic.sh +++ b/test/suites/basic.sh @@ -211,10 +211,10 @@ test_basic_usage() { lxc launch testimage deleterunning my_curl -X DELETE "https://${LXD_ADDR}/1.0/containers/deleterunning" | grep "container is running" - lxc delete deleterunning + lxc delete deleterunning -f # cleanup - lxc delete foo + lxc delete foo -f # check that an apparmor profile is created for this container, that it is # unloaded on stop, and that it is deleted when the container is deleted diff --git a/test/suites/filemanip.sh b/test/suites/filemanip.sh index a8c2cb8..dd2317c 100644 --- a/test/suites/filemanip.sh +++ b/test/suites/filemanip.sh @@ -10,5 +10,5 @@ test_filemanip() { [ ! -f /tmp/main.sh ] [ -f "${LXD_DIR}/containers/filemanip/rootfs/tmp/main.sh" ] - lxc delete filemanip + lxc delete filemanip -f } From 21d3b9b3dfe4c98ecff990fd2fe7e29d36404bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Wed, 10 Feb 2016 15:15:10 -0500 Subject: [PATCH 4/6] specs: Re-sync database spec with reality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- specs/database.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/specs/database.md b/specs/database.md index 6f5162d..2632fd5 100644 --- a/specs/database.md +++ b/specs/database.md @@ -109,8 +109,8 @@ id | INTEGER | SERIAL | NOT NULL | SERIAL name | VARCHAR(255) | - | NOT NULL | Container name architecture | INTEGER | - | NOT NULL | Container architecture type | INTEGER | 0 | NOT NULL | Container type (0 = container, 1 = container snapshot) -power\_state | INTEGER | 0 | NOT NULL | Container power state (0 = off, 1 = on) ephemeral | INTEGER | 0 | NOT NULL | Whether the container is ephemeral (0 = persistent, 1 = ephemeral) +creation\_date | DATETIME | - | | Image creation date (user supplied, 0 = unknown) Index: UNIQUE ON id AND name @@ -190,31 +190,31 @@ last\_use\_date | DATETIME | - | | Last time Index: UNIQUE ON id AND fingerprint -## images\_properties +## images\_aliases Column | Type | Default | Constraint | Description :----- | :--- | :------ | :--------- | :---------- id | INTEGER | SERIAL | NOT NULL | SERIAL +name | VARCHAR(255) | - | NOT NULL | Alias name image\_id | INTEGER | - | NOT NULL | images.id FK -type | INTEGER | 0 | NOT NULL | Property type (0 = string, 1 = text) -key | VARCHAR(255) | - | NOT NULL | Property name -value | TEXT | - | | Property value (NULL for unset) +description | VARCHAR(255) | - | | Description of the alias -Index: UNIQUE ON id +Index: UNIQUE ON id AND name Foreign keys: image\_id REFERENCES images(id) -## images\_aliases +## images\_properties Column | Type | Default | Constraint | Description :----- | :--- | :------ | :--------- | :---------- id | INTEGER | SERIAL | NOT NULL | SERIAL -name | VARCHAR(255) | - | NOT NULL | Alias name image\_id | INTEGER | - | NOT NULL | images.id FK -description | VARCHAR(255) | - | | Description of the alias +type | INTEGER | 0 | NOT NULL | Property type (0 = string, 1 = text) +key | VARCHAR(255) | - | NOT NULL | Property name +value | TEXT | - | | Property value (NULL for unset) -Index: UNIQUE ON id AND name +Index: UNIQUE ON id Foreign keys: image\_id REFERENCES images(id) From e63445b6f9acbd14d4aab29278724b1c0401e388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Wed, 10 Feb 2016 16:00:59 -0500 Subject: [PATCH 5/6] Record a creation time for containers and snapshots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/container.go | 2 ++ lxd/container_lxc.go | 7 +++++++ lxd/container_snapshot.go | 27 ++++++++++++--------------- lxd/db.go | 3 ++- lxd/db_containers.go | 12 ++++++++---- lxd/db_images.go | 4 ++-- lxd/db_update.go | 14 ++++++++++++++ shared/container.go | 7 +++++++ specs/rest-api.md | 3 +++ test/suites/basic.sh | 2 +- 10 files changed, 58 insertions(+), 23 deletions(-) diff --git a/lxd/container.go b/lxd/container.go index 87dd19d..272b7f2 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -296,6 +296,7 @@ type containerArgs struct { Architecture int BaseImage string Config map[string]string + CreationDate *time.Time Ctype containerType Devices shared.Devices Ephemeral bool @@ -350,6 +351,7 @@ type container interface { Id() int Name() string Architecture() int + CreationDate() *time.Time ExpandedConfig() map[string]string ExpandedDevices() shared.Devices LocalConfig() map[string]string diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index 073b9b7..3a22f74 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -86,6 +86,7 @@ func containerLXCCreate(d *Daemon, args containerArgs) (container, error) { ephemeral: args.Ephemeral, architecture: args.Architecture, cType: args.Ctype, + creationDate: args.CreationDate, profiles: args.Profiles, localConfig: args.Config, localDevices: args.Devices} @@ -181,6 +182,7 @@ func containerLXCLoad(d *Daemon, args containerArgs) (container, error) { ephemeral: args.Ephemeral, architecture: args.Architecture, cType: args.Ctype, + creationDate: args.CreationDate, profiles: args.Profiles, localConfig: args.Config, localDevices: args.Devices} @@ -206,6 +208,7 @@ type containerLXC struct { // Properties architecture int cType containerType + creationDate *time.Time ephemeral bool id int name string @@ -1362,6 +1365,7 @@ func (c *containerLXC) RenderState() (*shared.ContainerState, error) { return &shared.ContainerState{ Architecture: c.architecture, Config: c.localConfig, + CreationDate: c.creationDate.Unix(), Devices: c.localDevices, Ephemeral: c.ephemeral, ExpandedConfig: c.expandedConfig, @@ -3825,6 +3829,9 @@ func (c *containerLXC) Architecture() int { return c.architecture } +func (c *containerLXC) CreationDate() *time.Time { + return c.creationDate +} func (c *containerLXC) ExpandedConfig() map[string]string { return c.expandedConfig } diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go index dfff877..063364e 100644 --- a/lxd/container_snapshot.go +++ b/lxd/container_snapshot.go @@ -10,8 +10,6 @@ import ( "github.com/gorilla/mux" "github.com/lxc/lxd/shared" - - log "gopkg.in/inconshreveable/log15.v2" ) func containerSnapshotsGet(d *Daemon, r *http.Request) Response { @@ -22,13 +20,12 @@ func containerSnapshotsGet(d *Daemon, r *http.Request) Response { } cname := mux.Vars(r)["name"] - // Makes sure the requested container exists. - _, err = containerLoadByName(d, cname) + c, err := containerLoadByName(d, cname) if err != nil { return SmartError(err) } - results, err := dbContainerGetSnapshots(d.db, cname) + snaps, err := c.Snapshots() if err != nil { return SmartError(err) } @@ -36,19 +33,16 @@ func containerSnapshotsGet(d *Daemon, r *http.Request) Response { resultString := []string{} resultMap := []shared.Jmap{} - for _, name := range results { - sc, err := containerLoadByName(d, name) - if err != nil { - shared.Log.Error("Failed to load snapshot", log.Ctx{"snapshot": name}) - continue - } - - snapName := strings.SplitN(name, shared.SnapshotDelimiter, 2)[1] + for _, snap := range snaps { + snapName := strings.SplitN(snap.Name(), shared.SnapshotDelimiter, 2)[1] if recursion == 0 { url := fmt.Sprintf("/%s/containers/%s/snapshots/%s", shared.APIVersion, cname, snapName) resultString = append(resultString, url) } else { - body := shared.Jmap{"name": snapName, "stateful": shared.PathExists(sc.StatePath())} + body := shared.Jmap{ + "name": snapName, + "creation_date": snap.CreationDate().Unix(), + "stateful": shared.PathExists(snap.StatePath())} resultMap = append(resultMap, body) } } @@ -189,7 +183,10 @@ func snapshotHandler(d *Daemon, r *http.Request) Response { } func snapshotGet(sc container, name string) Response { - body := shared.Jmap{"name": name, "stateful": shared.PathExists(sc.StatePath())} + body := shared.Jmap{ + "name": name, + "creation_date": sc.CreationDate().Unix(), + "stateful": shared.PathExists(sc.StatePath())} return SyncResponse(true, body) } diff --git a/lxd/db.go b/lxd/db.go index 9b5606c..646d076 100644 --- a/lxd/db.go +++ b/lxd/db.go @@ -34,7 +34,7 @@ type Profile struct { // Profiles will contain a list of all Profiles. type Profiles []Profile -const DB_CURRENT_VERSION int = 21 +const DB_CURRENT_VERSION int = 22 // CURRENT_SCHEMA contains the current SQLite SQL Schema. const CURRENT_SCHEMA string = ` @@ -58,6 +58,7 @@ CREATE TABLE IF NOT EXISTS containers ( architecture INTEGER NOT NULL, type INTEGER NOT NULL, ephemeral INTEGER NOT NULL DEFAULT 0, + creation_date DATETIME, UNIQUE (name) ); CREATE TABLE IF NOT EXISTS containers_config ( diff --git a/lxd/db_containers.go b/lxd/db_containers.go index a1092e8..c8a1e53 100644 --- a/lxd/db_containers.go +++ b/lxd/db_containers.go @@ -3,6 +3,7 @@ package main import ( "database/sql" "fmt" + "time" "github.com/lxc/lxd/shared" @@ -65,9 +66,9 @@ func dbContainerGet(db *sql.DB, name string) (containerArgs, error) { args.Name = name ephemInt := -1 - q := "SELECT id, architecture, type, ephemeral FROM containers WHERE name=?" + q := "SELECT id, architecture, type, ephemeral, creation_date FROM containers WHERE name=?" arg1 := []interface{}{name} - arg2 := []interface{}{&args.Id, &args.Architecture, &args.Ctype, &ephemInt} + arg2 := []interface{}{&args.Id, &args.Architecture, &args.Ctype, &ephemInt, &args.CreationDate} err := dbQueryRowScan(db, q, arg1, arg2) if err != nil { return args, err @@ -123,14 +124,17 @@ func dbContainerCreate(db *sql.DB, args containerArgs) (int, error) { ephemInt = 1 } - str := fmt.Sprintf("INSERT INTO containers (name, architecture, type, ephemeral) VALUES (?, ?, ?, ?)") + now := time.Now().UTC() + args.CreationDate = &now + + str := fmt.Sprintf("INSERT INTO containers (name, architecture, type, ephemeral, creation_date) VALUES (?, ?, ?, ?, ?)") stmt, err := tx.Prepare(str) if err != nil { tx.Rollback() return 0, err } defer stmt.Close() - result, err := stmt.Exec(args.Name, args.Architecture, args.Ctype, ephemInt) + result, err := stmt.Exec(args.Name, args.Architecture, args.Ctype, ephemInt, args.CreationDate.Unix()) if err != nil { tx.Rollback() return 0, err diff --git a/lxd/db_images.go b/lxd/db_images.go index 3f42e2b..2cd41c7 100644 --- a/lxd/db_images.go +++ b/lxd/db_images.go @@ -150,9 +150,9 @@ func dbImageSetPublic(db *sql.DB, id int, public bool) error { return err } -// Insert an alias into the database. +// Insert an alias ento the database. func dbImageAliasAdd(db *sql.DB, name string, imageID int, desc string) error { - stmt := `INSERT into images_aliases (name, image_id, description) values (?, ?, ?)` + stmt := `INSERT INTO images_aliases (name, image_id, description) values (?, ?, ?)` _, err := dbExec(db, stmt, name, imageID, desc) return err } diff --git a/lxd/db_update.go b/lxd/db_update.go index 6a7a8fe..0e39f84 100644 --- a/lxd/db_update.go +++ b/lxd/db_update.go @@ -15,6 +15,14 @@ import ( log "gopkg.in/inconshreveable/log15.v2" ) +func dbUpdateFromV21(db *sql.DB) error { + stmt := ` +ALTER TABLE containers ADD COLUMN creation_date DATETIME NOT NULL DEFAULT 0; +INSERT INTO schema (version, updated_at) VALUES (?, strftime("%s"));` + _, err := db.Exec(stmt, 22) + return err +} + func dbUpdateFromV20(d *Daemon) error { cNames, err := dbContainersList(d.db, cTypeRegular) if err != nil { @@ -900,6 +908,12 @@ func dbUpdate(d *Daemon, prevVersion int) error { return err } } + if prevVersion < 22 { + err = dbUpdateFromV21(db) + if err != nil { + return err + } + } return nil } diff --git a/shared/container.go b/shared/container.go index cb75ec3..d4bb6da 100644 --- a/shared/container.go +++ b/shared/container.go @@ -24,9 +24,16 @@ type ContainerExecControl struct { Args map[string]string `json:"args"` } +type SnapshotState struct { + CreationDate int64 `json:"creation_date"` + Name string `json:"name"` + Stateful bool `json:"stateful"` +} + type ContainerState struct { Architecture int `json:"architecture"` Config map[string]string `json:"config"` + CreationDate int64 `json:"creation_date"` Devices Devices `json:"devices"` Ephemeral bool `json:"ephemeral"` ExpandedConfig map[string]string `json:"expanded_config"` diff --git a/specs/rest-api.md b/specs/rest-api.md index 017ece0..0ca83db 100644 --- a/specs/rest-api.md +++ b/specs/rest-api.md @@ -385,6 +385,8 @@ Output: 'profiles': ["default"], 'architecture': 2, 'config': {"limits.cpu": "3"}, + 'ephemeral': false, + 'creation_date': 1455136027, 'expanded_config': {"limits.cpu": "3"} # the result of expanding profiles and adding the container's local config 'devices': { 'rootfs': { @@ -572,6 +574,7 @@ Input: Return: { + 'creation_date': 1455139453, 'name': "my-snapshot", 'stateful': true } diff --git a/test/suites/basic.sh b/test/suites/basic.sh index 0a1e31a..c24d53a 100644 --- a/test/suites/basic.sh +++ b/test/suites/basic.sh @@ -44,7 +44,7 @@ test_basic_usage() { # Test container creation lxc init testimage foo - lxc list | grep foo | grep STOPPED + lxc list | grep foo | grep STOPPED || bash lxc list fo | grep foo | grep STOPPED # Test container rename From 87408f9b9c49fda54df4a494e8d5fdb7f1d60e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Wed, 10 Feb 2016 16:01:16 -0500 Subject: [PATCH 6/6] Rework snapshot rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Don't print the awkward list in "lxc list" anymore. - Update "lxc info" to show: - Snapshot name - Snapshot creation date (if known) - Snapshot type (stateful or stateless) Closes #1560 Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- client.go | 16 +++------------- lxc/info.go | 22 +++++++++++++++++++++- lxc/list.go | 15 ++------------- po/lxd.pot | 61 +++++++++++++++++++++++++++++++++++++------------------------ 4 files changed, 63 insertions(+), 51 deletions(-) diff --git a/client.go b/client.go index 136b96c..45eb1a2 100644 --- a/client.go +++ b/client.go @@ -1624,30 +1624,20 @@ func (c *Client) Snapshot(container string, snapshotName string, stateful bool) return c.post(fmt.Sprintf("containers/%s/snapshots", container), body, Async) } -func (c *Client) ListSnapshots(container string) ([]string, error) { +func (c *Client) ListSnapshots(container string) ([]shared.SnapshotState, error) { qUrl := fmt.Sprintf("containers/%s/snapshots?recursion=1", container) resp, err := c.get(qUrl) if err != nil { return nil, err } - var result []shared.Jmap + var result []shared.SnapshotState if err := json.Unmarshal(resp.Metadata, &result); err != nil { return nil, err } - names := []string{} - - for _, snapjmap := range result { - name, err := snapjmap.GetString("name") - if err != nil { - continue - } - names = append(names, name) - } - - return names, nil + return result, nil } func (c *Client) GetServerConfigString() ([]string, error) { diff --git a/lxc/info.go b/lxc/info.go index 6116341..ba644e1 100644 --- a/lxc/info.go +++ b/lxc/info.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "strings" + "time" "gopkg.in/yaml.v2" @@ -76,7 +77,13 @@ func containerInfo(d *lxd.Client, name string, showLog bool) error { return err } + const layout = "2006/01/02 15:04 UTC" + fmt.Printf(i18n.G("Name: %s")+"\n", ct.Name) + if ct.CreationDate != 0 { + fmt.Printf(i18n.G("Created: %s")+"\n", time.Unix(ct.CreationDate, 0).UTC().Format(layout)) + } + fmt.Printf(i18n.G("Status: %s")+"\n", ct.Status.Status) if ct.Ephemeral { fmt.Printf(i18n.G("Type: ephemeral") + "\n") @@ -109,11 +116,24 @@ func containerInfo(d *lxd.Client, name string, showLog bool) error { if err != nil { return nil } + for _, snap := range snaps { if first_snapshot { fmt.Println(i18n.G("Snapshots:")) } - fmt.Printf(" %s\n", snap) + fmt.Printf(" %s", snap.Name) + + if snap.CreationDate != 0 { + fmt.Printf(" ("+i18n.G("taken at %s")+")", time.Unix(snap.CreationDate, 0).UTC().Format(layout)) + } + + if snap.Stateful { + fmt.Printf(" (" + i18n.G("stateful") + ")") + } else { + fmt.Printf(" (" + i18n.G("stateless") + ")") + } + fmt.Printf("\n") + first_snapshot = false } diff --git a/lxc/list.go b/lxc/list.go index 349aeef..ee25e73 100644 --- a/lxc/list.go +++ b/lxc/list.go @@ -149,7 +149,7 @@ func shouldShow(filters []string, state *shared.ContainerState) bool { return true } -func listContainers(cinfos []shared.ContainerInfo, filters []string, columns []Column, listsnaps bool) error { +func listContainers(cinfos []shared.ContainerInfo, filters []string, columns []Column) error { headers := []string{} for _, column := range columns { headers = append(headers, column.Name) @@ -175,17 +175,6 @@ func listContainers(cinfos []shared.ContainerInfo, filters []string, columns []C table.AppendBulk(data) table.Render() - if listsnaps && len(cinfos) == 1 { - csnaps := cinfos[0].Snaps - first_snapshot := true - for _, snap := range csnaps { - if first_snapshot { - fmt.Println(i18n.G("Snapshots:")) - } - fmt.Printf(" %s\n", snap) - first_snapshot = false - } - } return nil } @@ -250,7 +239,7 @@ func (c *listCmd) run(config *lxd.Config, args []string) error { } } - return listContainers(cts, filters, columns, len(cts) == 1) + return listContainers(cts, filters, columns) } func nameColumnData(cinfo shared.ContainerInfo) string { diff --git a/po/lxd.pot b/po/lxd.pot index 2afd38d..07753b4 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-02-10 14:55-0500\n" + "POT-Creation-Date: 2016-02-10 16:04-0500\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" @@ -74,7 +74,7 @@ msgstr "" msgid "'/' not allowed in snapshot name" msgstr "" -#: lxc/info.go:102 lxc/profile.go:221 +#: lxc/info.go:109 lxc/profile.go:221 msgid "(none)" msgstr "" @@ -208,7 +208,7 @@ msgid "Create a read-only snapshot of a container.\n" "lxc snapshot u1 snap0" msgstr "" -#: lxc/image.go:269 +#: lxc/image.go:269 lxc/info.go:84 #, c-format msgid "Created: %s" msgstr "" @@ -244,7 +244,7 @@ msgstr "" msgid "Device %s removed from %s" msgstr "" -#: lxc/list.go:239 +#: lxc/list.go:228 msgid "EPHEMERAL" msgstr "" @@ -322,11 +322,11 @@ msgstr "" msgid "Generating a client certificate. This may take a minute..." msgstr "" -#: lxc/list.go:237 +#: lxc/list.go:226 msgid "IPV4" msgstr "" -#: lxc/list.go:238 +#: lxc/list.go:227 msgid "IPV6" msgstr "" @@ -347,7 +347,7 @@ msgstr "" msgid "Image imported with fingerprint: %s" msgstr "" -#: lxc/info.go:88 +#: lxc/info.go:95 #, c-format msgid "Init: %d" msgstr "" @@ -380,7 +380,7 @@ msgstr "" msgid "Invalid target %s" msgstr "" -#: lxc/info.go:90 +#: lxc/info.go:97 msgid "Ips:" msgstr "" @@ -402,7 +402,7 @@ msgid "Launch a container from a particular image.\n" "lxc launch ubuntu u1" msgstr "" -#: lxc/info.go:24 +#: lxc/info.go:25 msgid "List information on containers.\n" "\n" "This will support remotes and images as well, but only containers for now.\n" @@ -433,7 +433,7 @@ msgid "Lists the available resources.\n" "* p - pid of container init process" msgstr "" -#: lxc/info.go:131 +#: lxc/info.go:151 msgid "Log:" msgstr "" @@ -624,15 +624,15 @@ msgid "Move containers within or in between lxd instances.\n" " Rename a local container.\n" msgstr "" -#: lxc/list.go:235 lxc/remote.go:271 +#: lxc/list.go:224 lxc/remote.go:271 msgid "NAME" msgstr "" -#: lxc/list.go:304 lxc/remote.go:257 +#: lxc/list.go:293 lxc/remote.go:257 msgid "NO" msgstr "" -#: lxc/info.go:79 +#: lxc/info.go:82 #, c-format msgid "Name: %s" msgstr "" @@ -666,7 +666,7 @@ msgstr "" msgid "Override the terminal mode (auto, interactive or non-interactive)" msgstr "" -#: lxc/list.go:241 +#: lxc/list.go:230 msgid "PID" msgstr "" @@ -718,7 +718,7 @@ msgid "Prints the version number of LXD.\n" "lxc version" msgstr "" -#: lxc/info.go:89 +#: lxc/info.go:96 #, c-format msgid "Processcount: %d" msgstr "" @@ -742,7 +742,7 @@ msgstr "" msgid "Profile to apply to the new container" msgstr "" -#: lxc/info.go:86 +#: lxc/info.go:93 #, c-format msgid "Profiles: %s" msgstr "" @@ -788,11 +788,11 @@ msgstr "" msgid "SIZE" msgstr "" -#: lxc/list.go:240 +#: lxc/list.go:229 msgid "SNAPSHOTS" msgstr "" -#: lxc/list.go:236 +#: lxc/list.go:225 msgid "STATE" msgstr "" @@ -833,7 +833,7 @@ msgstr "" msgid "Show all commands (not just interesting ones)" msgstr "" -#: lxc/info.go:33 +#: lxc/info.go:34 msgid "Show the container's last 100 log lines?" msgstr "" @@ -842,7 +842,7 @@ msgstr "" msgid "Size: %.2fMB" msgstr "" -#: lxc/info.go:114 lxc/list.go:183 +#: lxc/info.go:122 msgid "Snapshots:" msgstr "" @@ -851,7 +851,7 @@ msgstr "" msgid "Starting %s" msgstr "" -#: lxc/info.go:80 +#: lxc/info.go:87 #, c-format msgid "Status: %s" msgstr "" @@ -881,11 +881,11 @@ msgstr "" msgid "Try `lxc info --show-log %s` for more info" msgstr "" -#: lxc/info.go:82 +#: lxc/info.go:89 msgid "Type: ephemeral" msgstr "" -#: lxc/info.go:84 +#: lxc/info.go:91 msgid "Type: persistent" msgstr "" @@ -927,7 +927,7 @@ msgstr "" msgid "Whether to show the expanded configuration" msgstr "" -#: lxc/list.go:302 lxc/remote.go:259 +#: lxc/list.go:291 lxc/remote.go:259 msgid "YES" msgstr "" @@ -1005,6 +1005,19 @@ msgstr "" msgid "remote %s exists as <%s>" msgstr "" +#: lxc/info.go:131 +msgid "stateful" +msgstr "" + +#: lxc/info.go:133 +msgid "stateless" +msgstr "" + +#: lxc/info.go:127 +#, c-format +msgid "taken at %s" +msgstr "" + #: lxc/exec.go:158 msgid "unreachable return reached" msgstr ""
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel