[lxc-devel] [lxd/master] Bits needed by lxd-bridge init scripts

stgraber on Github lxc-bot at linuxcontainers.org
Wed Mar 23 01:49:57 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 780 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160323/2778016f/attachment.bin>
-------------- next part --------------
From f1b3135b3985912e14d077a95883a4dd5cc0de24 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Mar 2016 19:47:54 -0400
Subject: [PATCH 1/7] Rename IsMock to MockMode
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

And don't bother setting it to false, that's the default.

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/daemon.go    | 17 ++++++++---------
 lxd/db_test.go   |  8 ++++----
 lxd/db_update.go |  4 ++--
 lxd/main.go      |  1 -
 lxd/main_test.go |  2 +-
 lxd/storage.go   |  4 ++--
 6 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/lxd/daemon.go b/lxd/daemon.go
index 5a69b8e..e7933f7 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -90,7 +90,7 @@ type Daemon struct {
 
 	configValues map[string]string
 
-	IsMock bool
+	MockMode bool
 
 	imagesDownloading     map[string]chan bool
 	imagesDownloadingLock sync.RWMutex
@@ -528,7 +528,6 @@ func (d *Daemon) UpdateHTTPsPort(oldAddress string, newAddress string) error {
 func startDaemon(group string) (*Daemon, error) {
 	d := &Daemon{
 		group:                 group,
-		IsMock:                false,
 		imagesDownloading:     map[string]chan bool{},
 		imagesDownloadingLock: sync.RWMutex{},
 	}
@@ -575,11 +574,11 @@ func (d *Daemon) Init() error {
 		}
 	}
 
-	if !d.IsMock {
-		shared.Log.Info("LXD is starting",
+	if d.MockMode {
+		shared.Log.Info("Mock LXD is starting",
 			log.Ctx{"path": shared.VarPath("")})
 	} else {
-		shared.Log.Info("Mock LXD is starting",
+		shared.Log.Info("LXD is starting",
 			log.Ctx{"path": shared.VarPath("")})
 	}
 
@@ -744,7 +743,7 @@ func (d *Daemon) Init() error {
 	}
 
 	/* Setup the storage driver */
-	if !d.IsMock {
+	if !d.MockMode {
 		err = d.SetupStorageDriver()
 		if err != nil {
 			return fmt.Errorf("Failed to setup storage: %s", err)
@@ -824,7 +823,7 @@ func (d *Daemon) Init() error {
 		return err
 	}
 
-	if !d.IsMock {
+	if !d.MockMode {
 		/* Start the scheduler */
 		go deviceEventListener(d)
 
@@ -992,7 +991,7 @@ func (d *Daemon) Init() error {
 	})
 
 	// Restore containers
-	if !d.IsMock {
+	if !d.MockMode {
 		/* Restart containers */
 		go containersRestart(d)
 
@@ -1072,7 +1071,7 @@ func (d *Daemon) Stop() error {
 	shared.Log.Debug("Stopping /dev/lxd handler")
 	d.devlxd.Close()
 
-	if d.IsMock || forceStop {
+	if d.MockMode || forceStop {
 		return nil
 	}
 
diff --git a/lxd/db_test.go b/lxd/db_test.go
index 28055bd..6441717 100644
--- a/lxd/db_test.go
+++ b/lxd/db_test.go
@@ -37,7 +37,7 @@ func createTestDb(t *testing.T) (db *sql.DB) {
 	}
 
 	var err error
-	d := &Daemon{IsMock: true}
+	d := &Daemon{MockMode: true}
 	err = initializeDbObject(d, ":memory:")
 	db = d.db
 
@@ -195,7 +195,7 @@ func Test_initializing_db_is_indempotent(t *testing.T) {
 	var err error
 
 	// This calls "createDb" once already.
-	d := &Daemon{IsMock: true}
+	d := &Daemon{MockMode: true}
 	err = initializeDbObject(d, ":memory:")
 	db = d.db
 
@@ -230,7 +230,7 @@ func Test_running_dbUpdateFromV6_adds_on_delete_cascade(t *testing.T) {
 	var err error
 	var count int
 
-	d := &Daemon{IsMock: true}
+	d := &Daemon{MockMode: true}
 	err = initializeDbObject(d, ":memory:")
 	defer d.db.Close()
 
@@ -372,7 +372,7 @@ INSERT INTO containers_config (container_id, key, value) VALUES (1, 'thekey', 't
 
 	// The "foreign key" on containers_config now points to nothing.
 	// Let's run the schema upgrades.
-	d := &Daemon{IsMock: true}
+	d := &Daemon{MockMode: true}
 	d.db = db
 	err = dbUpdate(d, 1)
 
diff --git a/lxd/db_update.go b/lxd/db_update.go
index 31c3612..7637a26 100644
--- a/lxd/db_update.go
+++ b/lxd/db_update.go
@@ -334,7 +334,7 @@ INSERT INTO schema (version, updated_at) VALUES (?, strftime("%s"));`
 }
 
 func dbUpdateFromV11(d *Daemon) error {
-	if d.IsMock {
+	if d.MockMode {
 		// No need to move snapshots no mock runs,
 		// dbUpdateFromV12 will then set the db version to 13
 		return nil
@@ -412,7 +412,7 @@ INSERT INTO schema (version, updated_at) VALUES (?, strftime("%s"));`
 }
 
 func dbUpdateFromV10(d *Daemon) error {
-	if d.IsMock {
+	if d.MockMode {
 		// No need to move lxc to containers in mock runs,
 		// dbUpdateFromV12 will then set the db version to 13
 		return nil
diff --git a/lxd/main.go b/lxd/main.go
index b3c33e0..fcbc724 100644
--- a/lxd/main.go
+++ b/lxd/main.go
@@ -422,7 +422,6 @@ func cleanShutdown() error {
 func activateIfNeeded() error {
 	// Don't start a full daemon, we just need DB access
 	d := &Daemon{
-		IsMock:                false,
 		imagesDownloading:     map[string]chan bool{},
 		imagesDownloadingLock: sync.RWMutex{},
 	}
diff --git a/lxd/main_test.go b/lxd/main_test.go
index 4c619c5..7a61429 100644
--- a/lxd/main_test.go
+++ b/lxd/main_test.go
@@ -12,7 +12,7 @@ import (
 
 func mockStartDaemon() (*Daemon, error) {
 	d := &Daemon{
-		IsMock:                true,
+		MockMode:              true,
 		imagesDownloading:     map[string]chan bool{},
 		imagesDownloadingLock: sync.RWMutex{},
 	}
diff --git a/lxd/storage.go b/lxd/storage.go
index 5b3863a..915fa05 100644
--- a/lxd/storage.go
+++ b/lxd/storage.go
@@ -196,7 +196,7 @@ func newStorage(d *Daemon, sType storageType) (storage, error) {
 }
 
 func newStorageWithConfig(d *Daemon, sType storageType, config map[string]interface{}) (storage, error) {
-	if d.IsMock {
+	if d.MockMode {
 		return d.Storage, nil
 	}
 
@@ -236,7 +236,7 @@ func storageForFilename(d *Daemon, filename string) (storage, error) {
 	config := make(map[string]interface{})
 	storageType := storageTypeDir
 
-	if d.IsMock {
+	if d.MockMode {
 		return newStorageWithConfig(d, storageTypeMock, config)
 	}
 

From 9a39490dc1a02c559eab5060ee756cea5283427c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Mar 2016 19:57:22 -0400
Subject: [PATCH 2/7] Cleanup daemon initialization
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>
---
 lxd/daemon.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lxd/daemon.go b/lxd/daemon.go
index e7933f7..8099d75 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -528,8 +528,6 @@ func (d *Daemon) UpdateHTTPsPort(oldAddress string, newAddress string) error {
 func startDaemon(group string) (*Daemon, error) {
 	d := &Daemon{
 		group:                 group,
-		imagesDownloading:     map[string]chan bool{},
-		imagesDownloadingLock: sync.RWMutex{},
 	}
 
 	if err := d.Init(); err != nil {
@@ -551,6 +549,9 @@ func haveMacAdmin() bool {
 }
 
 func (d *Daemon) Init() error {
+	/* Initialize some variables */
+	d.imagesDownloading = map[string]chan bool{}
+
 	d.shutdownChan = make(chan bool)
 
 	/* Set the executable path */

From c30ad17ad30003fc3b39607ae79969fae4bb31ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Mar 2016 20:00:44 -0400
Subject: [PATCH 3/7] Remove the startDaemon function
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Most code paths don't use it anyway

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/daemon.go      | 13 -------------
 lxd/devlxd_test.go |  3 ++-
 lxd/main.go        |  4 ++--
 3 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/lxd/daemon.go b/lxd/daemon.go
index 8099d75..de7f016 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -524,19 +524,6 @@ func (d *Daemon) UpdateHTTPsPort(oldAddress string, newAddress string) error {
 	return nil
 }
 
-// StartDaemon starts the shared daemon with the provided configuration.
-func startDaemon(group string) (*Daemon, error) {
-	d := &Daemon{
-		group:                 group,
-	}
-
-	if err := d.Init(); err != nil {
-		return nil, err
-	}
-
-	return d, nil
-}
-
 func haveMacAdmin() bool {
 	c, err := capability.NewPid(0)
 	if err != nil {
diff --git a/lxd/devlxd_test.go b/lxd/devlxd_test.go
index 92f3b34..62d200b 100644
--- a/lxd/devlxd_test.go
+++ b/lxd/devlxd_test.go
@@ -120,7 +120,8 @@ func TestHttpRequest(t *testing.T) {
 	}
 	defer os.RemoveAll(testDir)
 
-	d, err := startDaemon("")
+	d := &Daemon{}
+	err := d.Init()
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/lxd/main.go b/lxd/main.go
index fcbc724..8e7939c 100644
--- a/lxd/main.go
+++ b/lxd/main.go
@@ -325,8 +325,8 @@ func daemon() error {
 		}()
 	}
 
-	d, err := startDaemon(*argGroup)
-
+	d := &Daemon{group: *argGroup}
+	err := d.Init()
 	if err != nil {
 		if d != nil && d.db != nil {
 			d.db.Close()

From f91b1571cc5aba55562a52a9c8c27a331f1fa9b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Mar 2016 20:24:47 -0400
Subject: [PATCH 4/7] Add a setup mode allowing to delay part of startup
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

In this mode, containers are restarted and images aren't processed until
after we exit setup mode.

This is needed to configure the default profile through the API before
containers start using it.

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/api_internal.go |  18 +++++++
 lxd/daemon.go       | 141 ++++++++++++++++++++++++++++------------------------
 lxd/main.go         |  32 +++++++++++-
 3 files changed, 126 insertions(+), 65 deletions(-)

diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index 46d372e..f0b9f35 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"fmt"
 	"net/http"
 	"strconv"
 
@@ -8,11 +9,27 @@ import (
 )
 
 var apiInternal = []Command{
+	internalReadyCmd,
 	internalShutdownCmd,
 	internalContainerOnStartCmd,
 	internalContainerOnStopCmd,
 }
 
+func internalReady(d *Daemon, r *http.Request) Response {
+	if !d.SetupMode {
+		return InternalError(fmt.Errorf("The server isn't currently in setup mode"))
+	}
+
+	err := d.Ready()
+	if err != nil {
+		return InternalError(err)
+	}
+
+	d.SetupMode = false
+
+	return EmptySyncResponse
+}
+
 func internalShutdown(d *Daemon, r *http.Request) Response {
 	d.shutdownChan <- true
 
@@ -63,5 +80,6 @@ func internalContainerOnStop(d *Daemon, r *http.Request) Response {
 }
 
 var internalShutdownCmd = Command{name: "shutdown", put: internalShutdown}
+var internalReadyCmd = Command{name: "ready", put: internalReady}
 var internalContainerOnStartCmd = Command{name: "containers/{id}/onstart", get: internalContainerOnStart}
 var internalContainerOnStopCmd = Command{name: "containers/{id}/onstop", get: internalContainerOnStop}
diff --git a/lxd/daemon.go b/lxd/daemon.go
index de7f016..abd9c14 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -90,7 +90,8 @@ type Daemon struct {
 
 	configValues map[string]string
 
-	MockMode bool
+	MockMode  bool
+	SetupMode bool
 
 	imagesDownloading     map[string]chan bool
 	imagesDownloadingLock sync.RWMutex
@@ -562,11 +563,15 @@ func (d *Daemon) Init() error {
 		}
 	}
 
+	/* Print welcome message */
 	if d.MockMode {
-		shared.Log.Info("Mock LXD is starting",
+		shared.Log.Info("LXD is starting in mock mode",
+			log.Ctx{"path": shared.VarPath("")})
+	} else if d.SetupMode {
+		shared.Log.Info("LXD is starting in setup mode",
 			log.Ctx{"path": shared.VarPath("")})
 	} else {
-		shared.Log.Info("LXD is starting",
+		shared.Log.Info("LXD is starting in normal mode",
 			log.Ctx{"path": shared.VarPath("")})
 	}
 
@@ -738,25 +743,6 @@ func (d *Daemon) Init() error {
 		}
 	}
 
-	/* Prune images */
-	d.pruneChan = make(chan bool)
-	go func() {
-		pruneExpiredImages(d)
-		for {
-			timer := time.NewTimer(24 * time.Hour)
-			timeChan := timer.C
-			select {
-			case <-timeChan:
-				/* run once per day */
-				pruneExpiredImages(d)
-			case <-d.pruneChan:
-				/* run when image.remote_cache_expiry is changed */
-				pruneExpiredImages(d)
-				timer.Stop()
-			}
-		}
-	}()
-
 	/* Load all config values from the database */
 	_, err = d.ConfigValuesGet()
 	if err != nil {
@@ -766,41 +752,6 @@ func (d *Daemon) Init() error {
 	/* set the initial proxy function based on config values in the DB */
 	d.updateProxy()
 
-	/* Auto-update images */
-	d.resetAutoUpdateChan = make(chan bool)
-	go func() {
-		autoUpdateImages(d)
-
-		for {
-			interval, _ := d.ConfigValueGet("images.auto_update_interval")
-			if interval == "" {
-				interval = "6"
-			}
-
-			intervalInt, err := strconv.Atoi(interval)
-			if err != nil {
-				intervalInt = 0
-			}
-
-			if intervalInt > 0 {
-				timer := time.NewTimer(time.Duration(intervalInt) * time.Hour)
-				timeChan := timer.C
-
-				select {
-				case <-timeChan:
-					autoUpdateImages(d)
-				case <-d.resetAutoUpdateChan:
-					timer.Stop()
-				}
-			} else {
-				select {
-				case <-d.resetAutoUpdateChan:
-					continue
-				}
-			}
-		}
-	}()
-
 	/* Setup /dev/lxd */
 	d.devlxd, err = createAndBindDevLxd()
 	if err != nil {
@@ -978,18 +929,80 @@ func (d *Daemon) Init() error {
 		return nil
 	})
 
-	// Restore containers
-	if !d.MockMode {
-		/* Restart containers */
-		go containersRestart(d)
-
-		/* Re-balance in case things changed while LXD was down */
-		deviceTaskBalance(d)
+	if !d.MockMode && !d.SetupMode {
+		err := d.Ready()
+		if err != nil {
+			return err
+		}
 	}
 
 	return nil
 }
 
+func (d *Daemon) Ready() error {
+	/* Prune images */
+	d.pruneChan = make(chan bool)
+	go func() {
+		pruneExpiredImages(d)
+		for {
+			timer := time.NewTimer(24 * time.Hour)
+			timeChan := timer.C
+			select {
+			case <-timeChan:
+				/* run once per day */
+				pruneExpiredImages(d)
+			case <-d.pruneChan:
+				/* run when image.remote_cache_expiry is changed */
+				pruneExpiredImages(d)
+				timer.Stop()
+			}
+		}
+	}()
+
+	/* Auto-update images */
+	d.resetAutoUpdateChan = make(chan bool)
+	go func() {
+		autoUpdateImages(d)
+
+		for {
+			interval, _ := d.ConfigValueGet("images.auto_update_interval")
+			if interval == "" {
+				interval = "6"
+			}
+
+			intervalInt, err := strconv.Atoi(interval)
+			if err != nil {
+				intervalInt = 0
+			}
+
+			if intervalInt > 0 {
+				timer := time.NewTimer(time.Duration(intervalInt) * time.Hour)
+				timeChan := timer.C
+
+				select {
+				case <-timeChan:
+					autoUpdateImages(d)
+				case <-d.resetAutoUpdateChan:
+					timer.Stop()
+				}
+			} else {
+				select {
+				case <-d.resetAutoUpdateChan:
+					continue
+				}
+			}
+		}
+	}()
+
+	/* Restore containers */
+	go containersRestart(d)
+
+	/* Re-balance in case things changed while LXD was down */
+	deviceTaskBalance(d)
+
+	return nil
+}
+
 // CheckTrustState returns True if the client is trusted else false.
 func (d *Daemon) CheckTrustState(cert x509.Certificate) bool {
 	for k, v := range d.clientCerts {
diff --git a/lxd/main.go b/lxd/main.go
index 8e7939c..a2145b8 100644
--- a/lxd/main.go
+++ b/lxd/main.go
@@ -76,6 +76,8 @@ func run() error {
 		fmt.Printf("         [--storage-create-device=DEVICE] [--storage-create-loop=SIZE] [--storage-pool=POOL]\n")
 		fmt.Printf("         [--trust-password=]\n")
 		fmt.Printf("        Setup storage and networking\n")
+		fmt.Printf("    ready\n")
+		fmt.Printf("        Tells LXD that any setup-mode configuration has been done and that it can start containers.\n")
 		fmt.Printf("    shutdown [--timeout=60]\n")
 		fmt.Printf("        Perform a clean shutdown of LXD and all running containers\n")
 		fmt.Printf("    waitready [--timeout=15]\n")
@@ -208,6 +210,8 @@ func run() error {
 			return callHook(os.Args[1:])
 		case "init":
 			return setupLXD()
+		case "ready":
+			return cmdReady()
 		case "shutdown":
 			return cleanShutdown()
 		case "waitready":
@@ -325,7 +329,9 @@ func daemon() error {
 		}()
 	}
 
-	d := &Daemon{group: *argGroup}
+	d := &Daemon{
+		group:     *argGroup,
+		SetupMode: shared.PathExists(shared.VarPath(".setup_mode"))}
 	err := d.Init()
 	if err != nil {
 		if d != nil && d.db != nil {
@@ -380,6 +386,30 @@ func daemon() error {
 	return ret
 }
 
+func cmdReady() error {
+	c, err := lxd.NewClient(&lxd.DefaultConfig, "local")
+	if err != nil {
+		return err
+	}
+
+	req, err := http.NewRequest("PUT", c.BaseURL+"/internal/ready", nil)
+	if err != nil {
+		return err
+	}
+
+	raw, err := c.Http.Do(req)
+	if err != nil {
+		return err
+	}
+
+	_, err = lxd.HoistResponse(raw, lxd.Sync)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func cleanShutdown() error {
 	var timeout int
 

From 2dc8ccdf93f01fcb25b77c834ca4a9496be9b544 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Mar 2016 20:29:44 -0400
Subject: [PATCH 5/7] Cleanup function names in main.go
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>
---
 lxd/main.go | 41 ++++++++++++++++++++++-------------------
 1 file changed, 22 insertions(+), 19 deletions(-)

diff --git a/lxd/main.go b/lxd/main.go
index a2145b8..d7ab929 100644
--- a/lxd/main.go
+++ b/lxd/main.go
@@ -196,26 +196,29 @@ func run() error {
 		// "forkputfile", "forkgetfile", "forkmount" and "forkumount" are handled specially in nsexec.go
 		// "forkgetnet" is partially handled in nsexec.go (setns)
 		switch os.Args[1] {
+		// Main commands
 		case "activateifneeded":
-			return activateIfNeeded()
+			return cmdActivateIfNeeded()
 		case "daemon":
-			return daemon()
-		case "forkgetnet":
-			return printnet()
-		case "forkmigrate":
-			return MigrateContainer(os.Args[1:])
-		case "forkstart":
-			return startContainer(os.Args[1:])
+			return cmdDaemon()
 		case "callhook":
-			return callHook(os.Args[1:])
+			return cmdCallHook(os.Args[1:])
 		case "init":
-			return setupLXD()
+			return cmdInit()
 		case "ready":
 			return cmdReady()
 		case "shutdown":
-			return cleanShutdown()
+			return cmdShutdown()
 		case "waitready":
-			return waitReady()
+			return cmdWaitReady()
+
+		// Internal commands
+		case "forkgetnet":
+			return printnet()
+		case "forkmigrate":
+			return MigrateContainer(os.Args[1:])
+		case "forkstart":
+			return startContainer(os.Args[1:])
 		}
 	}
 
@@ -225,10 +228,10 @@ func run() error {
 		return fmt.Errorf("Unknown arguments")
 	}
 
-	return daemon()
+	return cmdDaemon()
 }
 
-func callHook(args []string) error {
+func cmdCallHook(args []string) error {
 	if len(args) < 4 {
 		return fmt.Errorf("Invalid arguments")
 	}
@@ -297,7 +300,7 @@ func callHook(args []string) error {
 	return nil
 }
 
-func daemon() error {
+func cmdDaemon() error {
 	if *argCPUProfile != "" {
 		f, err := os.Create(*argCPUProfile)
 		if err != nil {
@@ -410,7 +413,7 @@ func cmdReady() error {
 	return nil
 }
 
-func cleanShutdown() error {
+func cmdShutdown() error {
 	var timeout int
 
 	if *argTimeout == -1 {
@@ -449,7 +452,7 @@ func cleanShutdown() error {
 	return nil
 }
 
-func activateIfNeeded() error {
+func cmdActivateIfNeeded() error {
 	// Don't start a full daemon, we just need DB access
 	d := &Daemon{
 		imagesDownloading:     map[string]chan bool{},
@@ -505,7 +508,7 @@ func activateIfNeeded() error {
 	return nil
 }
 
-func waitReady() error {
+func cmdWaitReady() error {
 	var timeout int
 
 	if *argTimeout == -1 {
@@ -538,7 +541,7 @@ func waitReady() error {
 	return nil
 }
 
-func setupLXD() error {
+func cmdInit() error {
 	var storageBackend string // dir or zfs
 	var storageMode string    // existing, loop or device
 	var storageLoopSize int   // Size in GB

From c8eb881fccf1bbe388dbb377115afc1133845f52 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Mar 2016 21:21:59 -0400
Subject: [PATCH 6/7] Improve waitready
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Make it wait for the daemon to actually be ready.

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/api_internal.go |  8 +++++++-
 lxd/daemon.go       |  4 ++++
 lxd/main.go         | 20 +++++++++++++++++++-
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index f0b9f35..ad32446 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -30,6 +30,12 @@ func internalReady(d *Daemon, r *http.Request) Response {
 	return EmptySyncResponse
 }
 
+func internalWaitReady(d *Daemon, r *http.Request) Response {
+	<-d.readyChan
+
+	return EmptySyncResponse
+}
+
 func internalShutdown(d *Daemon, r *http.Request) Response {
 	d.shutdownChan <- true
 
@@ -80,6 +86,6 @@ func internalContainerOnStop(d *Daemon, r *http.Request) Response {
 }
 
 var internalShutdownCmd = Command{name: "shutdown", put: internalShutdown}
-var internalReadyCmd = Command{name: "ready", put: internalReady}
+var internalReadyCmd = Command{name: "ready", put: internalReady, get: internalWaitReady}
 var internalContainerOnStartCmd = Command{name: "containers/{id}/onstart", get: internalContainerOnStart}
 var internalContainerOnStopCmd = Command{name: "containers/{id}/onstop", get: internalContainerOnStop}
diff --git a/lxd/daemon.go b/lxd/daemon.go
index abd9c14..becbdb6 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -76,6 +76,7 @@ type Daemon struct {
 	lxcpath             string
 	mux                 *mux.Router
 	tomb                tomb.Tomb
+	readyChan           chan bool
 	pruneChan           chan bool
 	shutdownChan        chan bool
 	resetAutoUpdateChan chan bool
@@ -540,6 +541,7 @@ func (d *Daemon) Init() error {
 	/* Initialize some variables */
 	d.imagesDownloading = map[string]chan bool{}
 
+	d.readyChan = make(chan bool)
 	d.shutdownChan = make(chan bool)
 
 	/* Set the executable path */
@@ -1000,6 +1002,8 @@ func (d *Daemon) Ready() error {
 	/* Re-balance in case things changed while LXD was down */
 	deviceTaskBalance(d)
 
+	close(d.readyChan)
+
 	return nil
 }
 
diff --git a/lxd/main.go b/lxd/main.go
index d7ab929..0a4bbd4 100644
--- a/lxd/main.go
+++ b/lxd/main.go
@@ -520,7 +520,25 @@ func cmdWaitReady() error {
 	finger := make(chan error, 1)
 	go func() {
 		for {
-			_, err := lxd.NewClient(&lxd.DefaultConfig, "local")
+			c, err := lxd.NewClient(&lxd.DefaultConfig, "local")
+			if err != nil {
+				time.Sleep(500 * time.Millisecond)
+				continue
+			}
+
+			req, err := http.NewRequest("GET", c.BaseURL+"/internal/ready", nil)
+			if err != nil {
+				time.Sleep(500 * time.Millisecond)
+				continue
+			}
+
+			raw, err := c.Http.Do(req)
+			if err != nil {
+				time.Sleep(500 * time.Millisecond)
+				continue
+			}
+
+			_, err = lxd.HoistResponse(raw, lxd.Sync)
 			if err != nil {
 				time.Sleep(500 * time.Millisecond)
 				continue

From 20f44913ae1d1c7d8a7c6b74c829c3aa47651957 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 22 Mar 2016 21:47:56 -0400
Subject: [PATCH 7/7] Implement device set/unset
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  | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lxc/profile.go |  12 ++++--
 po/lxd.pot     |  48 ++++++++++++-----------
 3 files changed, 154 insertions(+), 25 deletions(-)

diff --git a/lxc/config.go b/lxc/config.go
index 1bece90..54e8452 100644
--- a/lxc/config.go
+++ b/lxc/config.go
@@ -59,6 +59,8 @@ func (c *configCmd) usage() string {
 		`Manage configuration.
 
 lxc config device add <[remote:]container> <name> <type> [key=value]...     Add a device to a container.
+lxc config device set <[remote:]container> <name> <key> <value>             Set a device property.
+lxc config device unset <[remote:]container> <name> <key>                   Unset a device property.
 lxc config device list [remote:]<container>                                 List devices for container.
 lxc config device show [remote:]<container>                                 Show full device details for container.
 lxc config device remove [remote:]<container> <name>                        Remove device from container.
@@ -411,6 +413,10 @@ func (c *configCmd) run(config *lxd.Config, args []string) error {
 			return c.deviceAdd(config, "container", args)
 		case "remove":
 			return c.deviceRm(config, "container", args)
+		case "set":
+			return c.deviceSet(config, "container", args)
+		case "unset":
+			return c.deviceUnset(config, "container", args)
 		case "show":
 			return c.deviceShow(config, "container", args)
 		default:
@@ -612,6 +618,119 @@ func (c *configCmd) deviceAdd(config *lxd.Config, which string, args []string) e
 	return err
 }
 
+func (c *configCmd) deviceSet(config *lxd.Config, which string, args []string) error {
+	if len(args) < 6 {
+		return errArgs
+	}
+
+	remote, name := config.ParseRemoteAndContainer(args[2])
+
+	client, err := lxd.NewClient(config, remote)
+	if err != nil {
+		return err
+	}
+
+	devname := args[3]
+	key := args[4]
+	value := args[5]
+
+	if which == "profile" {
+		st, err := client.ProfileConfig(name)
+		if err != nil {
+			return err
+		}
+
+		dev, ok := st.Devices[devname]
+		if !ok {
+			return fmt.Errorf(i18n.G("The device doesn't exist"))
+		}
+
+		dev[key] = value
+		st.Devices[devname] = dev
+
+		err = client.PutProfile(name, *st)
+		if err != nil {
+			return err
+		}
+	} else {
+		st, err := client.ContainerInfo(name)
+		if err != nil {
+			return err
+		}
+
+		dev, ok := st.Devices[devname]
+		if !ok {
+			return fmt.Errorf(i18n.G("The device doesn't exist"))
+		}
+
+		dev[key] = value
+		st.Devices[devname] = dev
+
+		err = client.UpdateContainerConfig(name, st.Brief())
+		if err != nil {
+			return err
+		}
+	}
+
+	return err
+}
+
+func (c *configCmd) deviceUnset(config *lxd.Config, which string, args []string) error {
+	if len(args) < 5 {
+		return errArgs
+	}
+
+	remote, name := config.ParseRemoteAndContainer(args[2])
+
+	client, err := lxd.NewClient(config, remote)
+	if err != nil {
+		return err
+	}
+
+	devname := args[3]
+	key := args[4]
+
+	if which == "profile" {
+		st, err := client.ProfileConfig(name)
+		if err != nil {
+			return err
+		}
+
+		dev, ok := st.Devices[devname]
+		if !ok {
+			return fmt.Errorf(i18n.G("The device doesn't exist"))
+		}
+
+		delete(dev, key)
+		st.Devices[devname] = dev
+
+		err = client.PutProfile(name, *st)
+		if err != nil {
+			return err
+		}
+	} else {
+		st, err := client.ContainerInfo(name)
+		if err != nil {
+			return err
+		}
+
+		dev, ok := st.Devices[devname]
+		if !ok {
+			return fmt.Errorf(i18n.G("The device doesn't exist"))
+		}
+
+		delete(dev, key)
+		st.Devices[devname] = dev
+
+		err = client.UpdateContainerConfig(name, st.Brief())
+		if err != nil {
+			return err
+		}
+	}
+
+	return err
+}
+
 func (c *configCmd) deviceRm(config *lxd.Config, which string, args []string) error {
 	if len(args) < 4 {
 		return errArgs
diff --git a/lxc/profile.go b/lxc/profile.go
index ebcf1aa..1b42ce2 100644
--- a/lxc/profile.go
+++ b/lxc/profile.go
@@ -69,9 +69,11 @@ lxc profile apply <container> <profiles>
              lxc profile apply bar,default # Apply default second now
 
 Devices:
-lxc profile device list <profile>              List devices in the given profile.
-lxc profile device show <profile>              Show full device details in the given profile.
-lxc profile device remove <profile> <name>     Remove a device from a profile.
+lxc profile device list <profile>                                   List devices in the given profile.
+lxc profile device show <profile>                                   Show full device details in the given profile.
+lxc profile device remove <profile> <name>                          Remove a device from a profile.
+lxc profile device set <[remote:]container> <name> <key> <value>    Set a device property.
+lxc profile device unset <[remote:]container> <name> <key>          Unset a device property.
 lxc profile device add <profile name> <device name> <device type> [key=value]...
     Add a profile device, such as a disk or a nic, to the containers
     using the specified profile.`)
@@ -276,6 +278,10 @@ func (c *profileCmd) doProfileDevice(config *lxd.Config, args []string) error {
 		return cfg.deviceList(config, "profile", args)
 	case "show":
 		return cfg.deviceShow(config, "profile", args)
+	case "set":
+		return cfg.deviceSet(config, "profile", args)
+	case "unset":
+		return cfg.deviceUnset(config, "profile", args)
 	default:
 		return errArgs
 	}
diff --git a/po/lxd.pot b/po/lxd.pot
index c2ec8cf..ca5c42a 100644
--- a/po/lxd.pot
+++ b/po/lxd.pot
@@ -7,7 +7,7 @@
 msgid   ""
 msgstr  "Project-Id-Version: lxd\n"
         "Report-Msgid-Bugs-To: lxc-devel at lists.linuxcontainers.org\n"
-        "POT-Creation-Date: 2016-03-16 15:52-0400\n"
+        "POT-Creation-Date: 2016-03-22 21:30-0400\n"
         "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
         "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
         "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -74,7 +74,7 @@ msgstr  ""
 msgid   "'/' not allowed in snapshot name"
 msgstr  ""
 
-#: lxc/profile.go:223
+#: lxc/profile.go:225
 msgid   "(none)"
 msgstr  ""
 
@@ -121,7 +121,7 @@ msgstr  ""
 msgid   "Available commands:"
 msgstr  ""
 
-#: lxc/config.go:269
+#: lxc/config.go:271
 msgid   "COMMON NAME"
 msgstr  ""
 
@@ -129,17 +129,17 @@ msgstr  ""
 msgid   "CREATED AT"
 msgstr  ""
 
-#: lxc/config.go:113
+#: lxc/config.go:115
 #, c-format
 msgid   "Can't read from stdin: %s"
 msgstr  ""
 
-#: lxc/config.go:126 lxc/config.go:159 lxc/config.go:181
+#: lxc/config.go:128 lxc/config.go:161 lxc/config.go:183
 #, c-format
 msgid   "Can't unset key '%s', it's not currently set."
 msgstr  ""
 
-#: lxc/profile.go:334
+#: lxc/profile.go:336
 msgid   "Cannot provide container name to list"
 msgstr  ""
 
@@ -167,7 +167,7 @@ msgstr  ""
 msgid   "Config key/value to apply to the new container"
 msgstr  ""
 
-#: lxc/config.go:493 lxc/config.go:558 lxc/image.go:669 lxc/profile.go:187
+#: lxc/config.go:495 lxc/config.go:560 lxc/image.go:669 lxc/profile.go:189
 #, c-format
 msgid   "Config parsing error: %s"
 msgstr  ""
@@ -251,12 +251,12 @@ msgid   "Delete containers or container snapshots.\n"
         "Destroy containers or snapshots with any attached data (configuration, snapshots, ...)."
 msgstr  ""
 
-#: lxc/config.go:610
+#: lxc/config.go:612
 #, c-format
 msgid   "Device %s added to %s"
 msgstr  ""
 
-#: lxc/config.go:640
+#: lxc/config.go:642
 #, c-format
 msgid   "Device %s removed from %s"
 msgstr  ""
@@ -265,7 +265,7 @@ msgstr  ""
 msgid   "EPHEMERAL"
 msgstr  ""
 
-#: lxc/config.go:271
+#: lxc/config.go:273
 msgid   "EXPIRY DATE"
 msgstr  ""
 
@@ -306,7 +306,7 @@ msgstr  ""
 msgid   "Expires: never"
 msgstr  ""
 
-#: lxc/config.go:268 lxc/image.go:591 lxc/image.go:616
+#: lxc/config.go:270 lxc/image.go:591 lxc/image.go:616
 msgid   "FINGERPRINT"
 msgstr  ""
 
@@ -349,7 +349,7 @@ msgstr  ""
 msgid   "IPV6"
 msgstr  ""
 
-#: lxc/config.go:270
+#: lxc/config.go:272
 msgid   "ISSUE DATE"
 msgstr  ""
 
@@ -502,9 +502,11 @@ msgid   "Manage configuration profiles.\n"
         "             lxc profile apply bar,default # Apply default second now\n"
         "\n"
         "Devices:\n"
-        "lxc profile device list <profile>              List devices in the given profile.\n"
-        "lxc profile device show <profile>              Show full device details in the given profile.\n"
-        "lxc profile device remove <profile> <name>     Remove a device from a profile.\n"
+        "lxc profile device list <profile>                                   List devices in the given profile.\n"
+        "lxc profile device show <profile>                                   Show full device details in the given profile.\n"
+        "lxc profile device remove <profile> <name>                          Remove a device from a profile.\n"
+        "lxc profile device set <[remote:]container> <name> <key> <value>    Set a device property.\n"
+        "lxc profile device unset <[remote:]container> <name> <key>          Unset a device property.\n"
         "lxc profile device add <profile name> <device name> <device type> [key=value]...\n"
         "    Add a profile device, such as a disk or a nic, to the containers\n"
         "    using the specified profile."
@@ -514,6 +516,8 @@ msgstr  ""
 msgid   "Manage configuration.\n"
         "\n"
         "lxc config device add <[remote:]container> <name> <type> [key=value]...     Add a device to a container.\n"
+        "lxc config device set <[remote:]container> <name> <key> <value>             Set a device property.\n"
+        "lxc config device unset <[remote:]container> <name> <key>                   Unset a device property.\n"
         "lxc config device list [remote:]<container>                                 List devices for container.\n"
         "lxc config device show [remote:]<container>                                 Show full device details for container.\n"
         "lxc config device remove [remote:]<container> <name>                        Remove device from container.\n"
@@ -682,11 +686,11 @@ msgstr  ""
 msgid   "New alias to define at target"
 msgstr  ""
 
-#: lxc/config.go:280
+#: lxc/config.go:282
 msgid   "No certificate provided to add"
 msgstr  ""
 
-#: lxc/config.go:303
+#: lxc/config.go:305
 msgid   "No fingerprint specified."
 msgstr  ""
 
@@ -754,11 +758,11 @@ msgid   "Presents details on how to use LXD.\n"
         "lxd help [--all]"
 msgstr  ""
 
-#: lxc/profile.go:188
+#: lxc/profile.go:190
 msgid   "Press enter to open the editor again"
 msgstr  ""
 
-#: lxc/config.go:494 lxc/config.go:559 lxc/image.go:670
+#: lxc/config.go:496 lxc/config.go:561 lxc/image.go:670
 msgid   "Press enter to start the editor again"
 msgstr  ""
 
@@ -785,17 +789,17 @@ msgstr  ""
 msgid   "Processes: %d"
 msgstr  ""
 
-#: lxc/profile.go:225
+#: lxc/profile.go:227
 #, c-format
 msgid   "Profile %s applied to %s"
 msgstr  ""
 
-#: lxc/profile.go:139
+#: lxc/profile.go:141
 #, c-format
 msgid   "Profile %s created"
 msgstr  ""
 
-#: lxc/profile.go:209
+#: lxc/profile.go:211
 #, c-format
 msgid   "Profile %s deleted"
 msgstr  ""


More information about the lxc-devel mailing list