The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/1950

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) ===
Closes #1939

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
From 0aec2538c40b8e5373524b79b82de75a07a5fa6c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Tue, 26 Apr 2016 14:43:52 -0400
Subject: [PATCH] Properly validate the server configuration keys
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #1939

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/api_1.0.go         | 113 ++++--------------
 lxd/certificates.go    |   2 +-
 lxd/containers_post.go |   5 +-
 lxd/daemon.go          | 247 +++++++-------------------------------
 lxd/daemon_config.go   | 319 +++++++++++++++++++++++++++++++++++++++++++++++++
 lxd/db_images.go       |   2 +-
 lxd/db_update.go       |   5 +-
 lxd/images.go          |  32 +----
 lxd/main.go            |   6 +-
 lxd/storage_lvm.go     | 143 ++++++----------------
 lxd/storage_zfs.go     |  33 +----
 11 files changed, 443 insertions(+), 464 deletions(-)
 create mode 100644 lxd/daemon_config.go

diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go
index db3a8fb..6b20078 100644
--- a/lxd/api_1.0.go
+++ b/lxd/api_1.0.go
@@ -5,6 +5,7 @@ import (
        "fmt"
        "net/http"
        "os"
+       "reflect"
        "syscall"
 
        "gopkg.in/lxc/go-lxc.v2"
@@ -125,23 +126,7 @@ func api10Get(d *Daemon, r *http.Request) Response {
 
                body["environment"] = env
                body["public"] = false
-
-               serverConfig, err := d.ConfigValuesGet()
-               if err != nil {
-                       return InternalError(err)
-               }
-
-               config := shared.Jmap{}
-
-               for key, value := range serverConfig {
-                       if key == "core.trust_password" {
-                               config[key] = true
-                       } else {
-                               config[key] = value
-                       }
-               }
-
-               body["config"] = config
+               body["config"] = daemonConfigRender()
        } else {
                body["auth"] = "untrusted"
                body["public"] = false
@@ -166,6 +151,14 @@ func api10Put(d *Daemon, r *http.Request) Response {
                return BadRequest(err)
        }
 
+       // Deal with special keys
+       for k, v := range req.Config {
+               config := daemonConfig[k]
+               if config.hiddenValue && v == true {
+                       req.Config[k] = oldConfig[k]
+               }
+       }
+
        // Diff the configs
        changedConfig := map[string]interface{}{}
        for key, value := range oldConfig {
@@ -180,84 +173,26 @@ func api10Put(d *Daemon, r *http.Request) Response {
                }
        }
 
-       for key, value := range changedConfig {
-               if value == nil {
-                       value = ""
+       for key, valueRaw := range changedConfig {
+               if valueRaw == nil {
+                       valueRaw = ""
                }
 
-               if !d.ConfigKeyIsValid(key) {
-                       return BadRequest(fmt.Errorf("Bad server config key: 
'%s'", key))
+               s := reflect.ValueOf(valueRaw)
+               if !s.IsValid() || s.Kind() != reflect.String {
+                       return BadRequest(fmt.Errorf("Invalid value type for 
'%s'", key))
                }
 
-               if key == "core.trust_password" {
-                       if value == true {
-                               continue
-                       }
-
-                       err := d.PasswordSet(value.(string))
-                       if err != nil {
-                               return InternalError(err)
-                       }
-               } else if key == "storage.lvm_vg_name" {
-                       err := storageLVMSetVolumeGroupNameConfig(d, 
value.(string))
-                       if err != nil {
-                               return InternalError(err)
-                       }
-                       if err = d.SetupStorageDriver(); err != nil {
-                               return InternalError(err)
-                       }
-               } else if key == "storage.lvm_thinpool_name" {
-                       err := storageLVMSetThinPoolNameConfig(d, 
value.(string))
-                       if err != nil {
-                               return InternalError(err)
-                       }
-               } else if key == "storage.zfs_pool_name" {
-                       err := storageZFSSetPoolNameConfig(d, value.(string))
-                       if err != nil {
-                               return InternalError(err)
-                       }
-                       if err = d.SetupStorageDriver(); err != nil {
-                               return InternalError(err)
-                       }
-               } else if key == "core.https_address" {
-                       old_address, err := 
d.ConfigValueGet("core.https_address")
-                       if err != nil {
-                               return InternalError(err)
-                       }
-
-                       err = d.UpdateHTTPsPort(old_address, value.(string))
-                       if err != nil {
-                               return InternalError(err)
-                       }
+               value := valueRaw.(string)
 
-                       err = d.ConfigValueSet(key, value.(string))
-                       if err != nil {
-                               return InternalError(err)
-                       }
-               } else if key == "core.proxy_https" || key == "core.proxy_http" 
|| key == "core.proxy_ignore_hosts" {
-                       err = d.ConfigValueSet(key, value.(string))
-                       if err != nil {
-                               return InternalError(err)
-                       }
-
-                       // Update the cached proxy function
-                       d.updateProxy()
-
-                       // Clear the simplestreams cache as it's tied to the 
old proxy config
-                       imageStreamCacheLock.Lock()
-                       for k, _ := range imageStreamCache {
-                               delete(imageStreamCache, k)
-                       }
-                       imageStreamCacheLock.Unlock()
+               confKey, ok := daemonConfig[key]
+               if !ok {
+                       return BadRequest(fmt.Errorf("Bad server config key: 
'%s'", key))
+               }
 
-               } else {
-                       err := d.ConfigValueSet(key, value.(string))
-                       if err != nil {
-                               return InternalError(err)
-                       }
-                       if key == "images.remote_cache_expiry" {
-                               d.pruneChan <- true
-                       }
+               err := confKey.Set(d, value)
+               if err != nil {
+                       return BadRequest(err)
                }
        }
 
diff --git a/lxd/certificates.go b/lxd/certificates.go
index e432f4a..df3b70b 100644
--- a/lxd/certificates.go
+++ b/lxd/certificates.go
@@ -140,7 +140,7 @@ func certificatesPost(d *Daemon, r *http.Request) Response {
                }
        }
 
-       if !d.isTrustedClient(r) && !d.PasswordCheck(req.Password) {
+       if !d.isTrustedClient(r) && d.PasswordCheck(req.Password) != nil {
                return Forbidden
        }
 
diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index 8668dd1..25e37ca 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -122,8 +122,9 @@ func createFromImage(d *Daemon, req *containerPostReq) 
Response {
 
        run := func(op *operation) error {
                if req.Source.Server != "" {
-                       updateCached, _ := 
d.ConfigValueGet("images.auto_update_cached")
-                       hash, err = d.ImageDownload(op, req.Source.Server, 
req.Source.Protocol, req.Source.Certificate, req.Source.Secret, hash, true, 
updateCached != "false")
+                       hash, err = d.ImageDownload(
+                               op, req.Source.Server, req.Source.Protocol, 
req.Source.Certificate, req.Source.Secret,
+                               hash, true, 
daemonConfig["images.auto_update_cached"].GetBool())
                        if err != nil {
                                return err
                        }
diff --git a/lxd/daemon.go b/lxd/daemon.go
index 5a1883e..4e3f635 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -2,7 +2,6 @@ package main
 
 import (
        "bytes"
-       "crypto/rand"
        "crypto/tls"
        "crypto/x509"
        "database/sql"
@@ -55,11 +54,6 @@ var cgSwapAccounting = false
 // UserNS
 var runningInUserns = false
 
-const (
-       pwSaltBytes = 32
-       pwHashBytes = 64
-)
-
 type Socket struct {
        Socket      net.Listener
        CloseOnExit bool
@@ -89,8 +83,6 @@ type Daemon struct {
 
        devlxd *net.UnixListener
 
-       configValues map[string]string
-
        MockMode  bool
        SetupMode bool
 
@@ -113,14 +105,6 @@ type Command struct {
        delete        func(d *Daemon, r *http.Request) Response
 }
 
-func (d *Daemon) updateProxy() {
-       d.proxy = shared.ProxyFromConfig(
-               d.configValues["core.proxy_https"],
-               d.configValues["core.proxy_http"],
-               d.configValues["core.proxy_ignore_hosts"],
-       )
-}
-
 func (d *Daemon) httpGetSync(url string, certificate string) (*lxd.Response, 
error) {
        var err error
 
@@ -360,32 +344,27 @@ func (d *Daemon) createCmd(version string, c Command) {
        })
 }
 
-func (d *Daemon) SetupStorageDriver() error {
-       lvmVgName, err := d.ConfigValueGet("storage.lvm_vg_name")
-       if err != nil {
-               return fmt.Errorf("Couldn't read config: %s", err)
-       }
+func (d *Daemon) SetupStorageDriver(driver string) error {
+       var err error
 
-       zfsPoolName, err := d.ConfigValueGet("storage.zfs_pool_name")
-       if err != nil {
-               return fmt.Errorf("Couldn't read config: %s", err)
-       }
+       lvmVgName := daemonConfig["storage.lvm_vg_name"].Get()
+       zfsPoolName := daemonConfig["storage.zfs_pool_name"].Get()
 
-       if lvmVgName != "" {
+       if driver == "lvm" || lvmVgName != "" {
                d.Storage, err = newStorage(d, storageTypeLvm)
                if err != nil {
                        shared.Logf("Could not initialize storage type LVM: %s 
- falling back to dir", err)
                } else {
                        return nil
                }
-       } else if zfsPoolName != "" {
+       } else if driver == "zfs" || zfsPoolName != "" {
                d.Storage, err = newStorage(d, storageTypeZfs)
                if err != nil {
                        shared.Logf("Could not initialize storage type ZFS: %s 
- falling back to dir", err)
                } else {
                        return nil
                }
-       } else if d.BackingFs == "btrfs" {
+       } else if driver == "btrfs" || d.BackingFs == "btrfs" {
                d.Storage, err = newStorage(d, storageTypeBtrfs)
                if err != nil {
                        shared.Logf("Could not initialize storage type btrfs: 
%s - falling back to dir", err)
@@ -445,9 +424,9 @@ func setupSharedMounts() error {
 func (d *Daemon) ListenAddresses() ([]string, error) {
        addresses := make([]string, 0)
 
-       value, err := d.ConfigValueGet("core.https_address")
-       if err != nil || value == "" {
-               return addresses, err
+       value := daemonConfig["core.https_address"].Get()
+       if value == "" {
+               return addresses, nil
        }
 
        localHost, localPort, err := net.SplitHostPort(value)
@@ -502,7 +481,9 @@ func (d *Daemon) ListenAddresses() ([]string, error) {
        return addresses, nil
 }
 
-func (d *Daemon) UpdateHTTPsPort(oldAddress string, newAddress string) error {
+func (d *Daemon) UpdateHTTPsPort(newAddress string) error {
+       oldAddress := daemonConfig["core.https_address"].Get()
+
        if oldAddress == newAddress {
                return nil
        }
@@ -754,22 +735,26 @@ func (d *Daemon) Init() error {
                return err
        }
 
+       /* Load all config values from the database */
+       err = daemonConfigInit(d.db)
+       if err != nil {
+               return err
+       }
+
        /* Setup the storage driver */
        if !d.MockMode {
-               err = d.SetupStorageDriver()
+               err = d.SetupStorageDriver("")
                if err != nil {
                        return fmt.Errorf("Failed to setup storage: %s", err)
                }
        }
 
-       /* Load all config values from the database */
-       _, err = d.ConfigValuesGet()
-       if err != nil {
-               return err
-       }
-
        /* set the initial proxy function based on config values in the DB */
-       d.updateProxy()
+       d.proxy = shared.ProxyFromConfig(
+               daemonConfig["core.proxy_https"].Get(),
+               daemonConfig["core.proxy_http"].Get(),
+               daemonConfig["core.proxy_ignore_hosts"].Get(),
+       )
 
        /* Setup /dev/lxd */
        d.devlxd, err = createAndBindDevLxd()
@@ -902,11 +887,7 @@ func (d *Daemon) Init() error {
                d.UnixSocket = &Socket{Socket: unixl, CloseOnExit: true}
        }
 
-       listenAddr, err := d.ConfigValueGet("core.https_address")
-       if err != nil {
-               return err
-       }
-
+       listenAddr := daemonConfig["core.https_address"].Get()
        if listenAddr != "" {
                _, _, err := net.SplitHostPort(listenAddr)
                if err != nil {
@@ -980,18 +961,9 @@ func (d *Daemon) Ready() error {
                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)
+                       interval := 
daemonConfig["images.auto_update_interval"].GetInt64()
+                       if interval > 0 {
+                               timer := time.NewTimer(time.Duration(interval) 
* time.Hour)
                                timeChan := timer.C
 
                                select {
@@ -1101,162 +1073,31 @@ func (d *Daemon) Stop() error {
        return err
 }
 
-// ConfigKeyIsValid returns if the given key is a known config value.
-func (d *Daemon) ConfigKeyIsValid(key string) bool {
-       switch key {
-       case "core.https_address":
-               return true
-       case "core.https_allowed_origin":
-               return true
-       case "core.https_allowed_methods":
-               return true
-       case "core.https_allowed_headers":
-               return true
-       case "core.proxy_https":
-               return true
-       case "core.proxy_http":
-               return true
-       case "core.proxy_ignore_hosts":
-               return true
-       case "core.trust_password":
-               return true
-       case "storage.lvm_vg_name":
-               return true
-       case "storage.lvm_thinpool_name":
-               return true
-       case "storage.lvm_fstype":
-               return true
-       case "storage.lvm_volume_size":
-               return true
-       case "storage.zfs_remove_snapshots":
-               return true
-       case "storage.zfs_pool_name":
-               return true
-       case "images.remote_cache_expiry":
-               return true
-       case "images.compression_algorithm":
-               return true
-       case "images.auto_update_interval":
-               return true
-       case "images.auto_update_cached":
-               return true
-       }
-
-       return false
-}
-
-// ConfigValueGet returns a config value from the memory,
-// calls ConfigValuesGet if required.
-// It returns a empty result if the config key isn't given.
-func (d *Daemon) ConfigValueGet(key string) (string, error) {
-       if d.configValues == nil {
-               if _, err := d.ConfigValuesGet(); err != nil {
-                       return "", err
-               }
-       }
-
-       if val, ok := d.configValues[key]; ok {
-               return val, nil
-       }
-
-       return "", nil
-}
-
-// ConfigValuesGet fetches all config values and stores them in memory.
-func (d *Daemon) ConfigValuesGet() (map[string]string, error) {
-       if d.configValues == nil {
-               var err error
-               d.configValues, err = dbConfigValuesGet(d.db)
-               if err != nil {
-                       return d.configValues, err
-               }
-       }
-
-       return d.configValues, nil
-}
-
-// ConfigValueSet sets a new or updates a config value,
-// it updates the value in the DB and in memory.
-func (d *Daemon) ConfigValueSet(key string, value string) error {
-       if err := dbConfigValueSet(d.db, key, value); err != nil {
-               return err
-       }
-
-       if d.configValues == nil {
-               if _, err := d.ConfigValuesGet(); err != nil {
-                       return err
-               }
-       }
+func (d *Daemon) PasswordCheck(password string) error {
+       value := daemonConfig["core.trust_password"].Get()
 
+       // No password set
        if value == "" {
-               delete(d.configValues, key)
-       } else {
-               d.configValues[key] = value
-       }
-
-       return nil
-}
-
-// PasswordSet sets the password to the new value.
-func (d *Daemon) PasswordSet(password string) error {
-       shared.Log.Info("Setting new https password")
-       var value = password
-       if password != "" {
-               buf := make([]byte, pwSaltBytes)
-               _, err := io.ReadFull(rand.Reader, buf)
-               if err != nil {
-                       return err
-               }
-
-               hash, err := scrypt.Key([]byte(password), buf, 1<<14, 8, 1, 
pwHashBytes)
-               if err != nil {
-                       return err
-               }
-
-               buf = append(buf, hash...)
-               value = hex.EncodeToString(buf)
+               return fmt.Errorf("No password is set")
        }
 
-       err := d.ConfigValueSet("core.trust_password", value)
+       // Compare the password
+       buff, err := hex.DecodeString(value)
        if err != nil {
                return err
        }
 
-       return nil
-}
-
-// PasswordCheck checks if the given password is the same
-// as we have in the DB.
-func (d *Daemon) PasswordCheck(password string) bool {
-       value, err := d.ConfigValueGet("core.trust_password")
+       salt := buff[0:32]
+       hash, err := scrypt.Key([]byte(password), salt, 1<<14, 8, 1, 64)
        if err != nil {
-               shared.Log.Error("verifyAdminPwd", log.Ctx{"err": err})
-               return false
-       }
-
-       // No password set
-       if value == "" {
-               return false
+               return err
        }
 
-       buff, err := hex.DecodeString(value)
-       if err != nil {
-               shared.Log.Error("hex decode failed", log.Ctx{"err": err})
-               return false
+       if !bytes.Equal(hash, buff[32:]) {
+               return fmt.Errorf("Bad password provided")
        }
 
-       salt := buff[0:pwSaltBytes]
-       hash, err := scrypt.Key([]byte(password), salt, 1<<14, 8, 1, 
pwHashBytes)
-       if err != nil {
-               shared.Log.Error("Failed to create hash to check", 
log.Ctx{"err": err})
-               return false
-       }
-       if !bytes.Equal(hash, buff[pwSaltBytes:]) {
-               shared.Log.Error("Bad password received", log.Ctx{"err": err})
-               return false
-       }
-       shared.Log.Debug("Verified the admin password")
-       return true
+       return nil
 }
 
 type lxdHttpServer struct {
@@ -1265,18 +1106,18 @@ type lxdHttpServer struct {
 }
 
 func (s *lxdHttpServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-       allowedOrigin, _ := s.d.ConfigValueGet("core.https_allowed_origin")
+       allowedOrigin := daemonConfig["core.https_allowed_origin"].Get()
        origin := req.Header.Get("Origin")
        if allowedOrigin != "" && origin != "" {
                rw.Header().Set("Access-Control-Allow-Origin", allowedOrigin)
        }
 
-       allowedMethods, _ := s.d.ConfigValueGet("core.https_allowed_methods")
+       allowedMethods := daemonConfig["core.https_allowed_methods"].Get()
        if allowedMethods != "" && origin != "" {
                rw.Header().Set("Access-Control-Allow-Methods", allowedMethods)
        }
 
-       allowedHeaders, _ := s.d.ConfigValueGet("core.https_allowed_headers")
+       allowedHeaders := daemonConfig["core.https_allowed_headers"].Get()
        if allowedHeaders != "" && origin != "" {
                rw.Header().Set("Access-Control-Allow-Headers", allowedHeaders)
        }
diff --git a/lxd/daemon_config.go b/lxd/daemon_config.go
new file mode 100644
index 0000000..5e83233
--- /dev/null
+++ b/lxd/daemon_config.go
@@ -0,0 +1,319 @@
+package main
+
+import (
+       "crypto/rand"
+       "database/sql"
+       "encoding/hex"
+       "fmt"
+       "io"
+       "strconv"
+       "strings"
+       "sync"
+
+       "golang.org/x/crypto/scrypt"
+       log "gopkg.in/inconshreveable/log15.v2"
+
+       "github.com/lxc/lxd/shared"
+)
+
+var daemonConfigLock sync.Mutex
+var daemonConfig map[string]*daemonConfigKey
+
+type daemonConfigKey struct {
+       valueType    string
+       defaultValue string
+       validValues  []string
+       currentValue string
+       hiddenValue  bool
+
+       validator func(d *Daemon, key string, value string) error
+       setter    func(d *Daemon, key string, value string) (string, error)
+       trigger   func(d *Daemon, key string, value string)
+}
+
+func (k *daemonConfigKey) name() string {
+       name := ""
+
+       // Look for a matching entry in daemonConfig
+       daemonConfigLock.Lock()
+       for key, value := range daemonConfig {
+               if value == k {
+                       name = key
+                       break
+               }
+       }
+       daemonConfigLock.Unlock()
+
+       return name
+}
+
+func (k *daemonConfigKey) Validate(d *Daemon, value string) error {
+       // No need to validate when unsetting
+       if value == "" {
+               return nil
+       }
+
+       // Validate booleans
+       if k.valueType == "bool" && 
!shared.StringInSlice(strings.ToLower(value), []string{"true", "false", "1", 
"0", "yes", "no"}) {
+               return fmt.Errorf("Invalid value for a boolean: %s", value)
+       }
+
+       // Validate integers
+       if k.valueType == "int" {
+               _, err := strconv.ParseInt(value, 10, 64)
+               if err != nil {
+                       return err
+               }
+       }
+
+       // Check against valid values
+       if k.validValues != nil && !shared.StringInSlice(value, k.validValues) {
+               return fmt.Errorf("Invalid value, only the following values are 
allowed: %s", k.validValues)
+       }
+
+       // Run external validation function
+       if k.validator != nil {
+               err := k.validator(d, k.name(), value)
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+func (k *daemonConfigKey) Set(d *Daemon, value string) error {
+       var name string
+
+       // Check if we are actually changing things
+       oldValue := k.currentValue
+       if oldValue == value {
+               return nil
+       }
+
+       // Validate the new value
+       err := k.Validate(d, value)
+       if err != nil {
+               return err
+       }
+
+       // Run external setting function
+       if k.setter != nil {
+               value, err = k.setter(d, k.name(), value)
+               if err != nil {
+                       return err
+               }
+       }
+
+       // Get the configuration key and make sure daemonConfig is sane
+       name = k.name()
+       if name == "" {
+               return fmt.Errorf("Corrupted configuration cache")
+       }
+
+       // Actually apply the change
+       daemonConfigLock.Lock()
+       k.currentValue = value
+       daemonConfigLock.Unlock()
+
+       err = dbConfigValueSet(d.db, name, value)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+func (k *daemonConfigKey) Get() string {
+       value := k.currentValue
+
+       // Get the default value if not set
+       if value == "" {
+               value = k.defaultValue
+       }
+
+       return value
+}
+
+func (k *daemonConfigKey) GetBool() bool {
+       value := k.currentValue
+
+       // Get the default value if not set
+       if value == "" {
+               value = k.defaultValue
+       }
+
+       // Convert to boolean
+       if shared.StringInSlice(strings.ToLower(value), []string{"true", "1", 
"yes"}) {
+               return true
+       }
+
+       return false
+}
+
+func (k *daemonConfigKey) GetInt64() int64 {
+       value := k.currentValue
+
+       // Get the default value if not set
+       if value == "" {
+               value = k.defaultValue
+       }
+
+       // Convert to int64
+       ret, _ := strconv.ParseInt(value, 10, 64)
+       return ret
+}
+
+func daemonConfigInit(db *sql.DB) error {
+       // Set all the keys
+       daemonConfig = map[string]*daemonConfigKey{
+               "core.https_address":         &daemonConfigKey{valueType: 
"string", setter: daemonConfigSetAddress},
+               "core.https_allowed_headers": &daemonConfigKey{valueType: 
"string"},
+               "core.https_allowed_methods": &daemonConfigKey{valueType: 
"string"},
+               "core.https_allowed_origin":  &daemonConfigKey{valueType: 
"string"},
+               "core.proxy_http":            &daemonConfigKey{valueType: 
"string", setter: daemonConfigSetProxy},
+               "core.proxy_https":           &daemonConfigKey{valueType: 
"string", setter: daemonConfigSetProxy},
+               "core.proxy_ignore_hosts":    &daemonConfigKey{valueType: 
"string", setter: daemonConfigSetProxy},
+               "core.trust_password":        &daemonConfigKey{valueType: 
"string", hiddenValue: true, setter: daemonConfigSetPassword},
+
+               "images.auto_update_cached":    &daemonConfigKey{valueType: 
"bool", defaultValue: "true"},
+               "images.auto_update_interval":  &daemonConfigKey{valueType: 
"int", defaultValue: "6"},
+               "images.compression_algorithm": &daemonConfigKey{valueType: 
"string", defaultValue: "gzip"},
+               "images.remote_cache_expiry":   &daemonConfigKey{valueType: 
"int", defaultValue: "10", trigger: daemonConfigTriggerExpiry},
+
+               "storage.lvm_fstype":           &daemonConfigKey{valueType: 
"string", defaultValue: "ext4", validValues: []string{"ext4", "xfs"}},
+               "storage.lvm_thinpool_name":    &daemonConfigKey{valueType: 
"string", defaultValue: "LXDPool", validator: storageLVMValidateThinPoolName},
+               "storage.lvm_vg_name":          &daemonConfigKey{valueType: 
"string", validator: storageLVMValidateVolumeGroupName, setter: 
daemonConfigSetStorage},
+               "storage.lvm_volume_size":      &daemonConfigKey{valueType: 
"string", defaultValue: "10GiB"},
+               "storage.zfs_pool_name":        &daemonConfigKey{valueType: 
"string", validator: storageZFSValidatePoolName, setter: 
daemonConfigSetStorage},
+               "storage.zfs_remove_snapshots": &daemonConfigKey{valueType: 
"bool"},
+       }
+
+       // Load the values from the DB
+       dbValues, err := dbConfigValuesGet(db)
+       if err != nil {
+               return err
+       }
+
+       daemonConfigLock.Lock()
+       for k, v := range dbValues {
+               _, ok := daemonConfig[k]
+               if !ok {
+                       shared.Log.Error("Found invalid configuration key in 
database", log.Ctx{"key": k})
+               }
+
+               daemonConfig[k].currentValue = v
+       }
+       daemonConfigLock.Unlock()
+
+       return nil
+}
+
+func daemonConfigRender() map[string]interface{} {
+       config := map[string]interface{}{}
+
+       // Turn the config into a JSON-compatible map
+       for k, v := range daemonConfig {
+               value := v.Get()
+               if value != v.defaultValue {
+                       if v.hiddenValue {
+                               config[k] = true
+                       } else {
+                               config[k] = value
+                       }
+               }
+       }
+
+       return config
+}
+
+func daemonConfigSetPassword(d *Daemon, key string, value string) (string, 
error) {
+       // Nothing to do on unset
+       if value == "" {
+               return value, nil
+       }
+
+       // Hash the password
+       buf := make([]byte, 32)
+       _, err := io.ReadFull(rand.Reader, buf)
+       if err != nil {
+               return "", err
+       }
+
+       hash, err := scrypt.Key([]byte(value), buf, 1<<14, 8, 1, 64)
+       if err != nil {
+               return "", err
+       }
+
+       buf = append(buf, hash...)
+       value = hex.EncodeToString(buf)
+
+       return value, nil
+}
+
+func daemonConfigSetStorage(d *Daemon, key string, value string) (string, 
error) {
+       driver := ""
+
+       // Guess the driver name from the key
+       switch key {
+       case "storage.lvm_vg_name":
+               driver = "lvm"
+       case "storage.zfs_pool_name":
+               driver = "zfs"
+       }
+
+       // Should never actually hit this
+       if driver == "" {
+               return "", fmt.Errorf("Invalid storage key: %s", key)
+       }
+
+       // Update the current storage driver
+       err := d.SetupStorageDriver(driver)
+       if err != nil {
+               return "", err
+       }
+
+       return value, nil
+}
+
+func daemonConfigSetAddress(d *Daemon, key string, value string) (string, 
error) {
+       // Update the current https address
+       err := d.UpdateHTTPsPort(value)
+       if err != nil {
+               return "", err
+       }
+
+       return value, nil
+}
+
+func daemonConfigSetProxy(d *Daemon, key string, value string) (string, error) 
{
+       // Get the current config
+       config := map[string]string{}
+       config["core.proxy_https"] = daemonConfig["core.proxy_https"].Get()
+       config["core.proxy_http"] = daemonConfig["core.proxy_http"].Get()
+       config["core.proxy_ignore_hosts"] = 
daemonConfig["core.proxy_ignore_hosts"].Get()
+
+       // Apply the change
+       config[key] = value
+
+       // Update the cached proxy function
+       d.proxy = shared.ProxyFromConfig(
+               config["core.proxy_https"],
+               config["core.proxy_http"],
+               config["core.proxy_ignore_hosts"],
+       )
+
+       // Clear the simplestreams cache as it's tied to the old proxy config
+       imageStreamCacheLock.Lock()
+       for k, _ := range imageStreamCache {
+               delete(imageStreamCache, k)
+       }
+       imageStreamCacheLock.Unlock()
+
+       return value, nil
+}
+
+func daemonConfigTriggerExpiry(d *Daemon, key string, value string) {
+       // Trigger an image pruning run
+       d.pruneChan <- true
+}
diff --git a/lxd/db_images.go b/lxd/db_images.go
index efaefd4..76ce7bb 100644
--- a/lxd/db_images.go
+++ b/lxd/db_images.go
@@ -38,7 +38,7 @@ func dbImagesGet(db *sql.DB, public bool) ([]string, error) {
        return results, nil
 }
 
-func dbImagesGetExpired(db *sql.DB, expiry int) ([]string, error) {
+func dbImagesGetExpired(db *sql.DB, expiry int64) ([]string, error) {
        q := `SELECT fingerprint FROM images WHERE cached=1 AND 
creation_date<=strftime('%s', date('now', '-` + fmt.Sprintf("%d", expiry) + ` 
day'))`
 
        var fp string
diff --git a/lxd/db_update.go b/lxd/db_update.go
index d5c6956..0534b50 100644
--- a/lxd/db_update.go
+++ b/lxd/db_update.go
@@ -236,10 +236,7 @@ func dbUpdateFromV15(d *Daemon) error {
                return err
        }
 
-       vgName, err := d.ConfigValueGet("storage.lvm_vg_name")
-       if err != nil {
-               return fmt.Errorf("Error checking server config: %v", err)
-       }
+       vgName := daemonConfig["storage.lvm_vg_name"].Get()
 
        for _, cName := range cNames {
                var lvLinkPath string
diff --git a/lxd/images.go b/lxd/images.go
index 28c8a90..1a78796 100644
--- a/lxd/images.go
+++ b/lxd/images.go
@@ -230,17 +230,8 @@ func imgPostContInfo(d *Daemon, r *http.Request, req 
imagePostReq,
        }
        tarfile.Close()
 
-       compress, err := d.ConfigValueGet("images.compression_algorithm")
-       if err != nil {
-               return info, err
-       }
-
-       // Default to gzip for this
-       if compress == "" {
-               compress = "gzip"
-       }
-
        var compressedPath string
+       compress := daemonConfig["images.compression_algorithm"].Get()
        if compress != "none" {
                compressedPath, err = compressFile(tarfile.Name(), compress)
                if err != nil {
@@ -889,33 +880,22 @@ func autoUpdateImages(d *Daemon) {
 
 func pruneExpiredImages(d *Daemon) {
        shared.Debugf("Pruning expired images")
-       expiry, err := d.ConfigValueGet("images.remote_cache_expiry")
-       if err != nil {
-               shared.Log.Error("Unable to read the images.remote_cache_expiry 
key")
-               return
-       }
-
-       if expiry == "" {
-               expiry = "10"
-       }
-
-       expiryInt, err := strconv.Atoi(expiry)
-       if err != nil {
-               shared.Log.Error("Invalid value for 
images.remote_cache_expiry", log.Ctx{"err": err})
-               return
-       }
 
-       images, err := dbImagesGetExpired(d.db, expiryInt)
+       // Get the list of expires images
+       expiry := daemonConfig["images.remote_cache_expiry"].GetInt64()
+       images, err := dbImagesGetExpired(d.db, expiry)
        if err != nil {
                shared.Log.Error("Unable to retrieve the list of expired 
images", log.Ctx{"err": err})
                return
        }
 
+       // Delete them
        for _, fp := range images {
                if err := doDeleteImage(d, fp); err != nil {
                        shared.Log.Error("Error deleting image", log.Ctx{"err": 
err, "fp": fp})
                }
        }
+
        shared.Debugf("Done pruning expired images")
 }
 
diff --git a/lxd/main.go b/lxd/main.go
index 2065163..14cafbb 100644
--- a/lxd/main.go
+++ b/lxd/main.go
@@ -474,11 +474,7 @@ func cmdActivateIfNeeded() error {
        }
 
        // Look for network socket
-       value, err := d.ConfigValueGet("core.https_address")
-       if err != nil {
-               return err
-       }
-
+       value := daemonConfig["core.https_address"].Get()
        if value != "" {
                shared.Debugf("Daemon has core.https_address set, 
activating...")
                _, err := lxd.NewClient(&lxd.DefaultConfig, "local")
diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index 9f571aa..5fb501d 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -17,9 +17,6 @@ import (
        log "gopkg.in/inconshreveable/log15.v2"
 )
 
-var storageLvmDefaultThinLVSize = "10GiB"
-var storageLvmDefaultThinPoolName = "LXDPool"
-
 func storageLVMCheckVolumeGroup(vgName string) error {
        output, err := exec.Command("vgdisplay", "-s", vgName).CombinedOutput()
        if err != nil {
@@ -53,18 +50,8 @@ func storageLVMThinpoolExists(vgName string, poolName 
string) (bool, error) {
 
 func storageLVMGetThinPoolUsers(d *Daemon) ([]string, error) {
        results := []string{}
-       vgname, err := d.ConfigValueGet("storage.lvm_vg_name")
-       if err != nil {
-               return results, fmt.Errorf("Error getting lvm_vg_name config")
-       }
-       if vgname == "" {
-               return results, nil
-       }
-       poolname, err := d.ConfigValueGet("storage.lvm_thinpool_name")
-       if err != nil {
-               return results, fmt.Errorf("Error getting lvm_thinpool_name 
config")
-       }
-       if poolname == "" {
+
+       if daemonConfig["storage.lvm_vg_name"].Get() == "" {
                return results, nil
        }
 
@@ -72,6 +59,7 @@ func storageLVMGetThinPoolUsers(d *Daemon) ([]string, error) {
        if err != nil {
                return results, err
        }
+
        for _, cName := range cNames {
                var lvLinkPath string
                if strings.Contains(cName, shared.SnapshotDelimiter) {
@@ -100,63 +88,52 @@ func storageLVMGetThinPoolUsers(d *Daemon) ([]string, 
error) {
        return results, nil
 }
 
-func storageLVMSetThinPoolNameConfig(d *Daemon, poolname string) error {
+func storageLVMValidateThinPoolName(d *Daemon, key string, value string) error 
{
        users, err := storageLVMGetThinPoolUsers(d)
        if err != nil {
                return fmt.Errorf("Error checking if a pool is already in use: 
%v", err)
        }
+
        if len(users) > 0 {
                return fmt.Errorf("Can not change LVM config. Images or 
containers are still using LVs: %v", users)
        }
 
-       vgname, err := d.ConfigValueGet("storage.lvm_vg_name")
-       if err != nil {
-               return fmt.Errorf("Error getting lvm_vg_name config: %v", err)
-       }
-
-       if poolname != "" {
+       vgname := daemonConfig["storage.lvm_vg_name"].Get()
+       if value != "" {
                if vgname == "" {
                        return fmt.Errorf("Can not set lvm_thinpool_name 
without lvm_vg_name set.")
                }
 
-               poolExists, err := storageLVMThinpoolExists(vgname, poolname)
+               poolExists, err := storageLVMThinpoolExists(vgname, value)
                if err != nil {
-                       return fmt.Errorf("Error checking for thin pool '%s' in 
'%s': %v", poolname, vgname, err)
+                       return fmt.Errorf("Error checking for thin pool '%s' in 
'%s': %v", value, vgname, err)
                }
+
                if !poolExists {
-                       return fmt.Errorf("Pool '%s' does not exist in Volume 
Group '%s'", poolname, vgname)
+                       return fmt.Errorf("Pool '%s' does not exist in Volume 
Group '%s'", value, vgname)
                }
        }
 
-       err = d.ConfigValueSet("storage.lvm_thinpool_name", poolname)
-       if err != nil {
-               return err
-       }
-
        return nil
 }
 
-func storageLVMSetVolumeGroupNameConfig(d *Daemon, vgname string) error {
+func storageLVMValidateVolumeGroupName(d *Daemon, key string, value string) 
error {
        users, err := storageLVMGetThinPoolUsers(d)
        if err != nil {
                return fmt.Errorf("Error checking if a pool is already in use: 
%v", err)
        }
+
        if len(users) > 0 {
                return fmt.Errorf("Can not change LVM config. Images or 
containers are still using LVs: %v", users)
        }
 
-       if vgname != "" {
-               err = storageLVMCheckVolumeGroup(vgname)
+       if value != "" {
+               err = storageLVMCheckVolumeGroup(value)
                if err != nil {
                        return err
                }
        }
 
-       err = d.ConfigValueSet("storage.lvm_vg_name", vgname)
-       if err != nil {
-               return err
-       }
-
        return nil
 }
 
@@ -210,10 +187,7 @@ func (s *storageLvm) Init(config map[string]interface{}) 
(storage, error) {
        }
 
        if config["vgName"] == nil {
-               vgName, err := s.d.ConfigValueGet("storage.lvm_vg_name")
-               if err != nil {
-                       return s, fmt.Errorf("Error checking server config: 
%v", err)
-               }
+               vgName := daemonConfig["storage.lvm_vg_name"].Get()
                if vgName == "" {
                        return s, fmt.Errorf("LVM isn't enabled")
                }
@@ -345,17 +319,8 @@ func (s *storageLvm) ContainerCreateFromImage(
                return err
        }
 
-       var fstype string
-       fstype, err = s.d.ConfigValueGet("storage.lvm_fstype")
-       if err != nil {
-               return fmt.Errorf("Error checking server config, err=%v", err)
-       }
-
-       if fstype == "" {
-               fstype = "ext4"
-       }
-
        // Generate a new xfs's UUID
+       fstype := daemonConfig["storage.lvm_fstype"].Get()
        if fstype == "xfs" {
                err := xfsGenerateNewUUID(lvpath)
                if err != nil {
@@ -458,16 +423,9 @@ func (s *storageLvm) ContainerCopy(container container, 
sourceContainer containe
 func (s *storageLvm) ContainerStart(container container) error {
        lvName := containerNameToLVName(container.Name())
        lvpath := fmt.Sprintf("/dev/%s/%s", s.vgName, lvName)
-       fstype, err := s.d.ConfigValueGet("storage.lvm_fstype")
-       if err != nil {
-               return fmt.Errorf("Error checking server config, err=%v", err)
-       }
+       fstype := daemonConfig["storage.lvm_fstype"].Get()
 
-       if fstype == "" {
-               fstype = "ext4"
-       }
-
-       err = tryMount(lvpath, container.Path(), fstype, 0, "discard")
+       err := tryMount(lvpath, container.Path(), fstype, 0, "discard")
        if err != nil {
                return fmt.Errorf(
                        "Error mounting snapshot LV path='%s': %v",
@@ -702,17 +660,8 @@ func (s *storageLvm) ContainerSnapshotStart(container 
container) error {
                }
        }
 
-       var fstype string
-       fstype, err = s.d.ConfigValueGet("storage.lvm_fstype")
-       if err != nil {
-               return fmt.Errorf("Error checking server config, err=%v", err)
-       }
-
-       if fstype == "" {
-               fstype = "ext4"
-       }
-
        // Generate a new xfs's UUID
+       fstype := daemonConfig["storage.lvm_fstype"].Get()
        if fstype == "xfs" {
                err := xfsGenerateNewUUID(lvpath)
                if err != nil {
@@ -775,21 +724,11 @@ func (s *storageLvm) ImageCreate(fingerprint string) 
error {
                }
        }()
 
-       var fstype string
-       fstype, err = s.d.ConfigValueGet("storage.lvm_fstype")
-       if err != nil {
-               return fmt.Errorf("Error checking server config, err=%v", err)
-       }
-
-       if fstype == "" {
-               fstype = "ext4"
-       }
-
+       fstype := daemonConfig["storage.lvm_fstype"].Get()
        err = tryMount(lvpath, tempLVMountPoint, fstype, 0, "discard")
        if err != nil {
                shared.Logf("Error mounting image LV for untarring: %v", err)
                return fmt.Errorf("Error mounting image LV: %v", err)
-
        }
 
        untarErr := untarImage(finalName, tempLVMountPoint)
@@ -833,24 +772,26 @@ func (s *storageLvm) ImageDelete(fingerprint string) 
error {
 }
 
 func (s *storageLvm) createDefaultThinPool() (string, error) {
+       thinPoolName := daemonConfig["storage.lvm_thinpool_name"].Get()
+
        // Create a tiny 1G thinpool
        output, err := tryExec(
                "lvcreate",
                "--poolmetadatasize", "1G",
                "-L", "1G",
                "--thinpool",
-               fmt.Sprintf("%s/%s", s.vgName, storageLvmDefaultThinPoolName))
+               fmt.Sprintf("%s/%s", s.vgName, thinPoolName))
 
        if err != nil {
                s.log.Error(
                        "Could not create thin pool",
                        log.Ctx{
-                               "name":   storageLvmDefaultThinPoolName,
+                               "name":   thinPoolName,
                                "err":    err,
                                "output": string(output)})
 
                return "", fmt.Errorf(
-                       "Could not create LVM thin pool named %s", 
storageLvmDefaultThinPoolName)
+                       "Could not create LVM thin pool named %s", thinPoolName)
        }
 
        // Grow it to the maximum VG size (two step process required by old LVM)
@@ -858,49 +799,41 @@ func (s *storageLvm) createDefaultThinPool() (string, 
error) {
                "lvextend",
                "--alloc", "anywhere",
                "-l", "100%FREE",
-               fmt.Sprintf("%s/%s", s.vgName, storageLvmDefaultThinPoolName))
+               fmt.Sprintf("%s/%s", s.vgName, thinPoolName))
 
        if err != nil {
                s.log.Error(
                        "Could not grow thin pool",
                        log.Ctx{
-                               "name":   storageLvmDefaultThinPoolName,
+                               "name":   thinPoolName,
                                "err":    err,
                                "output": string(output)})
 
                return "", fmt.Errorf(
-                       "Could not grow LVM thin pool named %s", 
storageLvmDefaultThinPoolName)
+                       "Could not grow LVM thin pool named %s", thinPoolName)
        }
 
-       return storageLvmDefaultThinPoolName, nil
+       return thinPoolName, nil
 }
 
 func (s *storageLvm) createThinLV(lvname string) (string, error) {
-       poolname, err := s.d.ConfigValueGet("storage.lvm_thinpool_name")
-       if err != nil {
-               return "", fmt.Errorf("Error checking server config, err=%v", 
err)
-       }
+       var err error
 
+       poolname := daemonConfig["storage.lvm_thinpool_name"].Get()
        if poolname == "" {
                poolname, err = s.createDefaultThinPool()
                if err != nil {
                        return "", fmt.Errorf("Error creating LVM thin pool: 
%v", err)
                }
-               err = storageLVMSetThinPoolNameConfig(s.d, poolname)
+
+               err = storageLVMValidateThinPoolName(s.d, "", poolname)
                if err != nil {
                        s.log.Error("Setting thin pool name", log.Ctx{"err": 
err})
                        return "", fmt.Errorf("Error setting LVM thin pool 
config: %v", err)
                }
        }
 
-       lvSize, err := s.d.ConfigValueGet("storage.lvm_volume_size")
-       if err != nil {
-               return "", fmt.Errorf("Error checking server config, err=%v", 
err)
-       }
-
-       if lvSize == "" {
-               lvSize = storageLvmDefaultThinLVSize
-       }
+       lvSize := daemonConfig["storage.lvm_volume_size"].Get()
 
        output, err := tryExec(
                "lvcreate",
@@ -908,7 +841,6 @@ func (s *storageLvm) createThinLV(lvname string) (string, 
error) {
                "-n", lvname,
                "--virtualsize", lvSize,
                fmt.Sprintf("%s/%s", s.vgName, poolname))
-
        if err != nil {
                s.log.Error("Could not create LV", log.Ctx{"lvname": lvname, 
"output": string(output)})
                return "", fmt.Errorf("Could not create thin LV named %s", 
lvname)
@@ -916,8 +848,7 @@ func (s *storageLvm) createThinLV(lvname string) (string, 
error) {
 
        lvpath := fmt.Sprintf("/dev/%s/%s", s.vgName, lvname)
 
-       fstype, err := s.d.ConfigValueGet("storage.lvm_fstype")
-
+       fstype := daemonConfig["storage.lvm_fstype"].Get()
        switch fstype {
        case "xfs":
                output, err = tryExec(
@@ -932,7 +863,7 @@ func (s *storageLvm) createThinLV(lvname string) (string, 
error) {
        }
 
        if err != nil {
-               s.log.Error("mkfs.ext4", log.Ctx{"output": string(output)})
+               s.log.Error("Filesystem creation failed", log.Ctx{"output": 
string(output)})
                return "", fmt.Errorf("Error making filesystem on image LV: 
%v", err)
        }
 
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index 162ba18..7b35aa9 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -34,11 +34,7 @@ func (s *storageZfs) Init(config map[string]interface{}) 
(storage, error) {
        }
 
        if config["zfsPool"] == nil {
-               zfsPool, err := s.d.ConfigValueGet("storage.zfs_pool_name")
-               if err != nil {
-                       return s, fmt.Errorf("Error checking server config: 
%v", err)
-               }
-
+               zfsPool := daemonConfig["storage.zfs_pool_name"].Get()
                if zfsPool == "" {
                        return s, fmt.Errorf("ZFS isn't enabled")
                }
@@ -173,12 +169,7 @@ func (s *storageZfs) ContainerCanRestore(container 
container, sourceContainer co
        }
 
        if snaps[len(snaps)-1].Name() != sourceContainer.Name() {
-               v, err := s.d.ConfigValueGet("storage.zfs_remove_snapshots")
-               if err != nil {
-                       return err
-               }
-
-               if v != "true" {
+               if !daemonConfig["storage.zfs_remove_snapshots"].GetBool() {
                        return fmt.Errorf("ZFS can only restore from the latest 
snapshot. Delete newer snapshots or copy the snapshot into a new container 
instead.")
                }
 
@@ -1111,7 +1102,7 @@ func (s *storageZfs) zfsGetPoolUsers() ([]string, error) {
 }
 
 // Global functions
-func storageZFSSetPoolNameConfig(d *Daemon, poolname string) error {
+func storageZFSValidatePoolName(d *Daemon, key string, value string) error {
        s := storageZfs{}
 
        // Confirm the backend is working
@@ -1121,20 +1112,15 @@ func storageZFSSetPoolNameConfig(d *Daemon, poolname 
string) error {
        }
 
        // Confirm the new pool exists and is compatible
-       if poolname != "" {
-               err = s.zfsCheckPool(poolname)
+       if value != "" {
+               err = s.zfsCheckPool(value)
                if err != nil {
                        return fmt.Errorf("Invalid ZFS pool: %v", err)
                }
        }
 
-       // Check if we're switching pools
-       oldPoolname, err := d.ConfigValueGet("storage.zfs_pool_name")
-       if err != nil {
-               return err
-       }
-
        // Confirm the old pool isn't in use anymore
+       oldPoolname := daemonConfig["storage.zfs_pool_name"].Get()
        if oldPoolname != "" {
                s.zfsPool = oldPoolname
 
@@ -1147,13 +1133,6 @@ func storageZFSSetPoolNameConfig(d *Daemon, poolname 
string) error {
                        return fmt.Errorf("Can not change ZFS config. Images or 
containers are still using the ZFS pool: %v", users)
                }
        }
-       s.zfsPool = poolname
-
-       // All good, set the new pool name
-       err = d.ConfigValueSet("storage.zfs_pool_name", poolname)
-       if err != nil {
-               return err
-       }
 
        return nil
 }
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to