The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6019
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 8a4044b0ed5ead1718a745ed82df87d25ac8cbba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Fri, 26 Jul 2019 19:21:44 -0400 Subject: [PATCH 1/8] lxc/utils: Add getConfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/utils.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lxc/utils.go b/lxc/utils.go index 5ef1c93e88..c113c64716 100644 --- a/lxc/utils.go +++ b/lxc/utils.go @@ -2,11 +2,17 @@ package main import ( "fmt" + "io/ioutil" + "os" "sort" + "strings" + + "github.com/pkg/errors" "github.com/lxc/lxd/client" "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/i18n" + "github.com/lxc/lxd/shared/termios" ) // Lists @@ -217,3 +223,40 @@ func GetExistingAliases(aliases []string, allAliases []api.ImageAliasesEntry) [] } return existing } + +func getConfig(args ...string) (map[string]string, error) { + if len(args) == 2 && !strings.Contains(args[0], "=") { + if args[1] == "-" && !termios.IsTerminal(getStdinFd()) { + buf, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return nil, errors.Wrap(err, i18n.G("Can't read from stdin: %s")) + } + + args[1] = string(buf[:]) + } + + return map[string]string{args[0]: args[1]}, nil + } + + values := map[string]string{} + + for _, arg := range args { + fields := strings.SplitN(arg, "=", 2) + if len(fields) != 2 { + return nil, fmt.Errorf("Invalid key=value configuration: %s", arg) + } + + if fields[1] == "-" && !termios.IsTerminal(getStdinFd()) { + buf, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return nil, fmt.Errorf(i18n.G("Can't read from stdin: %s"), err) + } + + fields[1] = string(buf[:]) + } + + values[fields[0]] = fields[1] + } + + return values, nil +} From e655097bd562c773d7b8fa208d45f3dc891c71ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Fri, 26 Jul 2019 19:22:01 -0400 Subject: [PATCH 2/8] lxc/config: Rework config set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/config.go | 66 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/lxc/config.go b/lxc/config.go index 50b13dac8b..fb97012239 100644 --- a/lxc/config.go +++ b/lxc/config.go @@ -449,18 +449,21 @@ type cmdConfigSet struct { func (c *cmdConfigSet) Command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = i18n.G("set [<remote>:][<container>] <key> <value>") + cmd.Use = i18n.G("set [<remote>:][<container>] <key>=<value>...") cmd.Short = i18n.G("Set container or server configuration keys") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( - `Set container or server configuration keys`)) + `Set container or server configuration keys + +For backward compatibility, a single configuration key may still be set with: + lxc config set [<remote>:][<container>] <key> <value>`)) cmd.Example = cli.FormatSection("", i18n.G( - `lxc config set [<remote>:]<container> limits.cpu 2 + `lxc config set [<remote>:]<container> limits.cpu=2 Will set a CPU limit of "2" for the container. -lxc config set core.https_address [::]:8443 +lxc config set core.https_address=[::]:8443 Will have LXD listen on IPv4 and IPv6 port 8443. -lxc config set core.trust_password blah +lxc config set core.trust_password=blah Will set the server's trust password to blah.`)) cmd.Flags().StringVar(&c.config.flagTarget, "target", "", i18n.G("Cluster member name")+"``") @@ -471,14 +474,14 @@ lxc config set core.trust_password blah func (c *cmdConfigSet) Run(cmd *cobra.Command, args []string) error { // Sanity checks - exit, err := c.global.CheckArgs(cmd, args, 2, 3) + exit, err := c.global.CheckArgs(cmd, args, 1, -1) if exit { return err } // Parse remote remote := "" - if len(args) > 2 { + if len(args) != 2 && !strings.Contains(args[0], "=") { remote = args[0] } @@ -489,22 +492,16 @@ func (c *cmdConfigSet) Run(cmd *cobra.Command, args []string) error { resource := resources[0] - // Set the config key + // Set the config keys if resource.name != "" { // Sanity checks if c.config.flagTarget != "" { return fmt.Errorf(i18n.G("--target cannot be used with containers")) } - key := args[len(args)-2] - value := args[len(args)-1] - - if !termios.IsTerminal(getStdinFd()) && value == "-" { - buf, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return fmt.Errorf(i18n.G("Can't read from stdin: %s"), err) - } - value = string(buf[:]) + keys, err := getConfig(args[1:]...) + if err != nil { + return err } container, etag, err := resource.server.GetContainer(resource.name) @@ -512,15 +509,17 @@ func (c *cmdConfigSet) Run(cmd *cobra.Command, args []string) error { return err } - if cmd.Name() == "unset" { - _, ok := container.Config[key] - if !ok { - return fmt.Errorf(i18n.G("Can't unset key '%s', it's not currently set"), key) - } + for k, v := range keys { + if cmd.Name() == "unset" { + _, ok := container.Config[k] + if !ok { + return fmt.Errorf(i18n.G("Can't unset key '%s', it's not currently set"), k) + } - delete(container.Config, key) - } else { - container.Config[key] = value + delete(container.Config, k) + } else { + container.Config[k] = v + } } op, err := resource.server.UpdateContainer(resource.name, container.Writable(), etag) @@ -546,7 +545,22 @@ func (c *cmdConfigSet) Run(cmd *cobra.Command, args []string) error { return err } - server.Config[args[len(args)-2]] = args[len(args)-1] + var keys map[string]string + if remote == "" { + keys, err = getConfig(args[0:]...) + if err != nil { + return err + } + } else { + keys, err = getConfig(args[1:]...) + if err != nil { + return err + } + } + + for k, v := range keys { + server.Config[k] = v + } return resource.server.UpdateServer(server.Writable(), etag) } From 80c8e8a1143582b732f6b3a4697bf21d520be700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Jul 2019 16:15:06 -0400 Subject: [PATCH 3/8] lxc/network: Rework network set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/network.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lxc/network.go b/lxc/network.go index fc130590e7..45d59ce449 100644 --- a/lxc/network.go +++ b/lxc/network.go @@ -1049,10 +1049,13 @@ type cmdNetworkSet struct { func (c *cmdNetworkSet) Command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = i18n.G("set [<remote>:]<network> <key> <value>") + cmd.Use = i18n.G("set [<remote>:]<network> <key>=<value>...") cmd.Short = i18n.G("Set network configuration keys") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( - `Set network configuration keys`)) + `Set network configuration keys + +For backward compatibility, a single configuration key may still be set with: + lxc network set [<remote>:]<network> <key> <value>`)) cmd.Flags().StringVar(&c.network.flagTarget, "target", "", i18n.G("Cluster member name")+"``") cmd.RunE = c.Run @@ -1062,7 +1065,7 @@ func (c *cmdNetworkSet) Command() *cobra.Command { func (c *cmdNetworkSet) Run(cmd *cobra.Command, args []string) error { // Sanity checks - exit, err := c.global.CheckArgs(cmd, args, 3, 3) + exit, err := c.global.CheckArgs(cmd, args, 2, -1) if exit { return err } @@ -1080,11 +1083,12 @@ func (c *cmdNetworkSet) Run(cmd *cobra.Command, args []string) error { return fmt.Errorf(i18n.G("Missing network name")) } - // Set the config key + // Handle targeting if c.network.flagTarget != "" { client = client.UseTarget(c.network.flagTarget) } + // Get the network network, etag, err := client.GetNetwork(resource.name) if err != nil { return err @@ -1094,18 +1098,15 @@ func (c *cmdNetworkSet) Run(cmd *cobra.Command, args []string) error { return fmt.Errorf(i18n.G("Only managed networks can be modified")) } - key := args[1] - value := args[2] - - if !termios.IsTerminal(getStdinFd()) && value == "-" { - buf, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return fmt.Errorf(i18n.G("Can't read from stdin: %s"), err) - } - value = string(buf[:]) + // Set the keys + keys, err := getConfig(args[1:]...) + if err != nil { + return err } - network.Config[key] = value + for k, v := range keys { + network.Config[k] = v + } return client.UpdateNetwork(resource.name, network.Writable(), etag) } From 813c98890a8b555843c8ecfb61d75a45ade8f0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Jul 2019 16:21:05 -0400 Subject: [PATCH 4/8] lxc/profile: Rework profile set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/profile.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lxc/profile.go b/lxc/profile.go index 26526bcb9b..e8c1acf632 100644 --- a/lxc/profile.go +++ b/lxc/profile.go @@ -798,10 +798,13 @@ type cmdProfileSet struct { func (c *cmdProfileSet) Command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = i18n.G("set [<remote>:]<profile> <key> <value>") + cmd.Use = i18n.G("set [<remote>:]<profile> <key><value>...") cmd.Short = i18n.G("Set profile configuration keys") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( - `Set profile configuration keys`)) + `Set profile configuration keys + +For backward compatibility, a single configuration key may still be set with: + lxc profile set [<remote>:]<profile> <key> <value>`)) cmd.RunE = c.Run @@ -810,7 +813,7 @@ func (c *cmdProfileSet) Command() *cobra.Command { func (c *cmdProfileSet) Run(cmd *cobra.Command, args []string) error { // Sanity checks - exit, err := c.global.CheckArgs(cmd, args, 3, 3) + exit, err := c.global.CheckArgs(cmd, args, 2, -1) if exit { return err } @@ -827,24 +830,21 @@ func (c *cmdProfileSet) Run(cmd *cobra.Command, args []string) error { return fmt.Errorf(i18n.G("Missing profile name")) } - // Set the configuration key - key := args[1] - value := args[2] - - if !termios.IsTerminal(getStdinFd()) && value == "-" { - buf, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return fmt.Errorf(i18n.G("Can't read from stdin: %s"), err) - } - value = string(buf[:]) + // Get the profile + profile, etag, err := resource.server.GetProfile(resource.name) + if err != nil { + return err } - profile, etag, err := resource.server.GetProfile(resource.name) + // Set the configuration key + keys, err := getConfig(args[1:]...) if err != nil { return err } - profile.Config[key] = value + for k, v := range keys { + profile.Config[k] = v + } return resource.server.UpdateProfile(resource.name, profile.Writable(), etag) } From 24e4f32f09f8484332a64351570bd64a5697a683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Jul 2019 16:24:45 -0400 Subject: [PATCH 5/8] lxc/project: Rework project set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/project.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lxc/project.go b/lxc/project.go index 645778be5e..bd62dca6d9 100644 --- a/lxc/project.go +++ b/lxc/project.go @@ -521,10 +521,13 @@ type cmdProjectSet struct { func (c *cmdProjectSet) Command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = i18n.G("set [<remote>:]<project> <key> <value>") + cmd.Use = i18n.G("set [<remote>:]<project> <key>=<value>...") cmd.Short = i18n.G("Set project configuration keys") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( - `Set project configuration keys`)) + `Set project configuration keys + +For backward compatibility, a single configuration key may still be set with: + lxc project set [<remote>:]<project> <key> <value>`)) cmd.RunE = c.Run @@ -533,7 +536,7 @@ func (c *cmdProjectSet) Command() *cobra.Command { func (c *cmdProjectSet) Run(cmd *cobra.Command, args []string) error { // Sanity checks - exit, err := c.global.CheckArgs(cmd, args, 3, 3) + exit, err := c.global.CheckArgs(cmd, args, 2, -1) if exit { return err } @@ -550,24 +553,21 @@ func (c *cmdProjectSet) Run(cmd *cobra.Command, args []string) error { return fmt.Errorf(i18n.G("Missing project name")) } - // Set the configuration key - key := args[1] - value := args[2] - - if !termios.IsTerminal(getStdinFd()) && value == "-" { - buf, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return fmt.Errorf(i18n.G("Can't read from stdin: %s"), err) - } - value = string(buf[:]) + // Get the project + project, etag, err := resource.server.GetProject(resource.name) + if err != nil { + return err } - project, etag, err := resource.server.GetProject(resource.name) + // Set the configuration key + keys, err := getConfig(args[1:]...) if err != nil { return err } - project.Config[key] = value + for k, v := range keys { + project.Config[k] = v + } return resource.server.UpdateProject(resource.name, project.Writable(), etag) } From 2f1ee6a3f7458a26a7850249878abd06544199e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Jul 2019 16:28:46 -0400 Subject: [PATCH 6/8] lxc/config: Rework config device set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/config_device.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/lxc/config_device.go b/lxc/config_device.go index 0cb5b0e5b4..bbff5aa95f 100644 --- a/lxc/config_device.go +++ b/lxc/config_device.go @@ -495,10 +495,13 @@ type cmdConfigDeviceSet struct { func (c *cmdConfigDeviceSet) Command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = i18n.G("set [<remote>:]<container|profile> <device> <key> <value>") + cmd.Use = i18n.G("set [<remote>:]<container|profile> <device> <key>=<value>...") cmd.Short = i18n.G("Set container device configuration keys") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( - `Set container device configuration keys`)) + `Set container device configuration keys + +For backward compatibility, a single configuration key may still be set with: + lxc config device set [<remote>:]<container|profile> <device> <key> <value>`)) cmd.RunE = c.Run @@ -507,7 +510,7 @@ func (c *cmdConfigDeviceSet) Command() *cobra.Command { func (c *cmdConfigDeviceSet) Run(cmd *cobra.Command, args []string) error { // Sanity checks - exit, err := c.global.CheckArgs(cmd, args, 4, 4) + exit, err := c.global.CheckArgs(cmd, args, 3, -1) if exit { return err } @@ -526,8 +529,11 @@ func (c *cmdConfigDeviceSet) Run(cmd *cobra.Command, args []string) error { // Set the device config key devname := args[1] - key := args[2] - value := args[3] + + keys, err := getConfig(args[2:]...) + if err != nil { + return err + } if c.profile != nil { profile, etag, err := resource.server.GetProfile(resource.name) @@ -540,7 +546,9 @@ func (c *cmdConfigDeviceSet) Run(cmd *cobra.Command, args []string) error { return fmt.Errorf(i18n.G("The device doesn't exist")) } - dev[key] = value + for k, v := range keys { + dev[k] = v + } profile.Devices[devname] = dev err = resource.server.UpdateProfile(resource.name, profile.Writable(), etag) @@ -557,7 +565,9 @@ func (c *cmdConfigDeviceSet) Run(cmd *cobra.Command, args []string) error { return fmt.Errorf(i18n.G("The device doesn't exist")) } - dev[key] = value + for k, v := range keys { + dev[k] = v + } container.Devices[devname] = dev op, err := resource.server.UpdateContainer(resource.name, container.Writable(), etag) From 4fd05be5f986338cd50f3f7fe6d6f99915469bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Jul 2019 16:34:33 -0400 Subject: [PATCH 7/8] lxc/storage: Rework storage set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/storage.go | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/lxc/storage.go b/lxc/storage.go index ef0e8cacdd..63f5eccbb9 100644 --- a/lxc/storage.go +++ b/lxc/storage.go @@ -586,7 +586,10 @@ func (c *cmdStorageSet) Command() *cobra.Command { cmd.Use = i18n.G("set [<remote>:]<pool> <key> <value>") cmd.Short = i18n.G("Set storage pool configuration keys") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( - `Set storage pool configuration keys`)) + `Set storage pool configuration keys + +For backward compatibility, a single configuration key may still be set with: + lxc storage set [<remote>:]<pool> <key> <value>`)) cmd.Flags().StringVar(&c.storage.flagTarget, "target", "", i18n.G("Cluster member name")+"``") cmd.RunE = c.Run @@ -596,16 +599,13 @@ func (c *cmdStorageSet) Command() *cobra.Command { func (c *cmdStorageSet) Run(cmd *cobra.Command, args []string) error { // Sanity checks - exit, err := c.global.CheckArgs(cmd, args, 3, 3) + exit, err := c.global.CheckArgs(cmd, args, 2, -1) if exit { return err } // Parse remote - remote := "" - if len(args) > 0 { - remote = args[0] - } + remote := args[0] resources, err := c.global.ParseServers(remote) if err != nil { @@ -613,7 +613,6 @@ func (c *cmdStorageSet) Run(cmd *cobra.Command, args []string) error { } resource := resources[0] - if resource.name == "" { return fmt.Errorf(i18n.G("Missing pool name")) } @@ -624,18 +623,16 @@ func (c *cmdStorageSet) Run(cmd *cobra.Command, args []string) error { return err } - // Read the value - value := args[2] - if !termios.IsTerminal(getStdinFd()) && value == "-" { - buf, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return fmt.Errorf(i18n.G("Can't read from stdin: %s"), err) - } - value = string(buf[:]) + // Parse key/values + keys, err := getConfig(args[1:]...) + if err != nil { + return err } // Update the pool - pool.Config[args[1]] = value + for k, v := range keys { + pool.Config[k] = v + } err = resource.server.UpdateStoragePool(resource.name, pool.Writable(), etag) if err != nil { From 684a697bdede9192e39d501bf9d94241471c52e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Jul 2019 16:41:01 -0400 Subject: [PATCH 8/8] lxc/storage: Rework storage volume set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxc/storage_volume.go | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lxc/storage_volume.go b/lxc/storage_volume.go index b167c6d203..e2d55d6ce9 100644 --- a/lxc/storage_volume.go +++ b/lxc/storage_volume.go @@ -1276,10 +1276,13 @@ type cmdStorageVolumeSet struct { func (c *cmdStorageVolumeSet) Command() *cobra.Command { cmd := &cobra.Command{} - cmd.Use = i18n.G("set [<remote>:]<pool> <volume> <key> <value>") + cmd.Use = i18n.G("set [<remote>:]<pool> <volume> <key>=<value>...") cmd.Short = i18n.G("Set storage volume configuration keys") cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G( - `Set storage volume configuration keys`)) + `Set storage volume configuration keys + +For backward compatibility, a single configuration key may still be set with: + lxc storage volume set [<remote>:]<pool> <volume> <key> <value>`)) cmd.Flags().StringVar(&c.storage.flagTarget, "target", "", i18n.G("Cluster member name")+"``") cmd.RunE = c.Run @@ -1289,7 +1292,7 @@ func (c *cmdStorageVolumeSet) Command() *cobra.Command { func (c *cmdStorageVolumeSet) Run(cmd *cobra.Command, args []string) error { // Sanity checks - exit, err := c.global.CheckArgs(cmd, args, 4, 4) + exit, err := c.global.CheckArgs(cmd, args, 3, -1) if exit { return err } @@ -1301,7 +1304,6 @@ func (c *cmdStorageVolumeSet) Run(cmd *cobra.Command, args []string) error { } resource := resources[0] - if resource.name == "" { return fmt.Errorf(i18n.G("Missing pool name")) } @@ -1322,20 +1324,17 @@ func (c *cmdStorageVolumeSet) Run(cmd *cobra.Command, args []string) error { return err } - // Get the value - key := args[2] - value := args[3] - - if !termios.IsTerminal(getStdinFd()) && value == "-" { - buf, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return fmt.Errorf(i18n.G("Can't read from stdin: %s"), err) - } - value = string(buf[:]) + // Get the values + keys, err := getConfig(args[2:]...) + if err != nil { + return err } // Update the volume - vol.Config[key] = value + for k, v := range keys { + vol.Config[k] = v + } + err = client.UpdateStoragePoolVolume(resource.name, vol.Type, vol.Name, vol.Writable(), etag) if err != nil { return err
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel