[lxc-devel] [lxd/master] Rework all the `set` commands to allow key=value

stgraber on Github lxc-bot at linuxcontainers.org
Sat Jul 27 20:58:09 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190727/10000af6/attachment-0001.bin>
-------------- next part --------------
From 8a4044b0ed5ead1718a745ed82df87d25ac8cbba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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


More information about the lxc-devel mailing list