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

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===

From 5ee9d0a532641a4f87aeee4d8263413845bc9738 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <[email protected]>
Date: Thu, 14 Jun 2018 08:31:37 +0000
Subject: [PATCH 1/3] Extract cmdInit.ApplyConfig into a separete
 initApplyConfig function

No logic change, just moving around code.

Signed-off-by: Free Ekanayaka <[email protected]>
---
 lxd/init.go      | 383 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lxd/main_init.go | 372 +----------------------------------------------------
 2 files changed, 384 insertions(+), 371 deletions(-)
 create mode 100644 lxd/init.go

diff --git a/lxd/init.go b/lxd/init.go
new file mode 100644
index 000000000..873faa00d
--- /dev/null
+++ b/lxd/init.go
@@ -0,0 +1,383 @@
+package main
+
+import (
+       "fmt"
+
+       lxd "github.com/lxc/lxd/client"
+       "github.com/lxc/lxd/lxd/cluster"
+       "github.com/lxc/lxd/shared"
+       "github.com/lxc/lxd/shared/api"
+       "github.com/pkg/errors"
+)
+
+type initData struct {
+       api.ServerPut `yaml:",inline"`
+       Cluster       *initDataCluster       `json:"cluster" yaml:"cluster"`
+       Networks      []api.NetworksPost     `json:"networks" yaml:"networks"`
+       StoragePools  []api.StoragePoolsPost `json:"storage_pools" 
yaml:"storage_pools"`
+       Profiles      []api.ProfilesPost     `json:"profiles" yaml:"profiles"`
+}
+
+type initDataCluster struct {
+       api.ClusterPut  `yaml:",inline"`
+       ClusterPassword string `json:"cluster_password" yaml:"cluster_password"`
+}
+
+// Helper to initialize a LXD instance using the definitions from an initData
+// object.
+//
+// It's used both by the 'lxd init' command and by the PUT /1.0/cluster API.
+func initApplyConfig(d lxd.ContainerServer, config initData) error {
+       // Handle reverts
+       revert := true
+       reverts := []func(){}
+       defer func() {
+               if !revert {
+                       return
+               }
+
+               // Lets undo things in reverse order
+               for i := len(reverts) - 1; i >= 0; i-- {
+                       reverts[i]()
+               }
+       }()
+
+       // Apply server configuration
+       if config.Config != nil && len(config.Config) > 0 {
+               // Get current config
+               currentServer, etag, err := d.GetServer()
+               if err != nil {
+                       return errors.Wrap(err, "Failed to retrieve current 
server configuration")
+               }
+
+               // Setup reverter
+               reverts = append(reverts, func() {
+                       d.UpdateServer(currentServer.Writable(), "")
+               })
+
+               // Prepare the update
+               newServer := api.ServerPut{}
+               err = shared.DeepCopy(currentServer.Writable(), &newServer)
+               if err != nil {
+                       return errors.Wrap(err, "Failed to copy server 
configuration")
+               }
+
+               for k, v := range config.Config {
+                       newServer.Config[k] = fmt.Sprintf("%v", v)
+               }
+
+               // Apply it
+               err = d.UpdateServer(newServer, etag)
+               if err != nil {
+                       return errors.Wrap(err, "Failed to update server 
configuration")
+               }
+       }
+
+       // Apply network configuration
+       if config.Networks != nil && len(config.Networks) > 0 {
+               // Get the list of networks
+               networkNames, err := d.GetNetworkNames()
+               if err != nil {
+                       return errors.Wrap(err, "Failed to retrieve list of 
networks")
+               }
+
+               // Network creator
+               createNetwork := func(network api.NetworksPost) error {
+                       // Create the network if doesn't exist
+                       err := d.CreateNetwork(network)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to create 
network '%s'", network.Name)
+                       }
+
+                       // Setup reverter
+                       reverts = append(reverts, func() {
+                               d.DeleteNetwork(network.Name)
+                       })
+
+                       return nil
+               }
+
+               // Network updater
+               updateNetwork := func(network api.NetworksPost) error {
+                       // Get the current network
+                       currentNetwork, etag, err := d.GetNetwork(network.Name)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to retrieve 
current network '%s'", network.Name)
+                       }
+
+                       // Setup reverter
+                       reverts = append(reverts, func() {
+                               d.UpdateNetwork(currentNetwork.Name, 
currentNetwork.Writable(), "")
+                       })
+
+                       // Prepare the update
+                       newNetwork := api.NetworkPut{}
+                       err = shared.DeepCopy(currentNetwork.Writable(), 
&newNetwork)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to copy 
configuration of network '%s'", network.Name)
+                       }
+
+                       // Description override
+                       if network.Description != "" {
+                               newNetwork.Description = network.Description
+                       }
+
+                       // Config overrides
+                       for k, v := range network.Config {
+                               newNetwork.Config[k] = fmt.Sprintf("%v", v)
+                       }
+
+                       // Apply it
+                       err = d.UpdateNetwork(currentNetwork.Name, newNetwork, 
etag)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to update 
network '%s'", network.Name)
+                       }
+
+                       return nil
+               }
+
+               for _, network := range config.Networks {
+                       // New network
+                       if !shared.StringInSlice(network.Name, networkNames) {
+                               err := createNetwork(network)
+                               if err != nil {
+                                       return err
+                               }
+
+                               continue
+                       }
+
+                       // Existing network
+                       err := updateNetwork(network)
+                       if err != nil {
+                               return err
+                       }
+               }
+       }
+
+       // Apply storage configuration
+       if config.StoragePools != nil && len(config.StoragePools) > 0 {
+               // Get the list of storagePools
+               storagePoolNames, err := d.GetStoragePoolNames()
+               if err != nil {
+                       return errors.Wrap(err, "Failed to retrieve list of 
storage pools")
+               }
+
+               // StoragePool creator
+               createStoragePool := func(storagePool api.StoragePoolsPost) 
error {
+                       // Create the storagePool if doesn't exist
+                       err := d.CreateStoragePool(storagePool)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to create 
storage pool '%s'", storagePool.Name)
+                       }
+
+                       // Setup reverter
+                       reverts = append(reverts, func() {
+                               d.DeleteStoragePool(storagePool.Name)
+                       })
+
+                       return nil
+               }
+
+               // StoragePool updater
+               updateStoragePool := func(storagePool api.StoragePoolsPost) 
error {
+                       // Get the current storagePool
+                       currentStoragePool, etag, err := 
d.GetStoragePool(storagePool.Name)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to retrieve 
current storage pool '%s'", storagePool.Name)
+                       }
+
+                       // Sanity check
+                       if currentStoragePool.Driver != storagePool.Driver {
+                               return fmt.Errorf("Storage pool '%s' is of type 
'%s' instead of '%s'", currentStoragePool.Name, currentStoragePool.Driver, 
storagePool.Driver)
+                       }
+
+                       // Setup reverter
+                       reverts = append(reverts, func() {
+                               d.UpdateStoragePool(currentStoragePool.Name, 
currentStoragePool.Writable(), "")
+                       })
+
+                       // Prepare the update
+                       newStoragePool := api.StoragePoolPut{}
+                       err = shared.DeepCopy(currentStoragePool.Writable(), 
&newStoragePool)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to copy 
configuration of storage pool '%s'", storagePool.Name)
+                       }
+
+                       // Description override
+                       if storagePool.Description != "" {
+                               newStoragePool.Description = 
storagePool.Description
+                       }
+
+                       // Config overrides
+                       for k, v := range storagePool.Config {
+                               newStoragePool.Config[k] = fmt.Sprintf("%v", v)
+                       }
+
+                       // Apply it
+                       err = d.UpdateStoragePool(currentStoragePool.Name, 
newStoragePool, etag)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to update 
storage pool '%s'", storagePool.Name)
+                       }
+
+                       return nil
+               }
+
+               for _, storagePool := range config.StoragePools {
+                       // New storagePool
+                       if !shared.StringInSlice(storagePool.Name, 
storagePoolNames) {
+                               err := createStoragePool(storagePool)
+                               if err != nil {
+                                       return err
+                               }
+
+                               continue
+                       }
+
+                       // Existing storagePool
+                       err := updateStoragePool(storagePool)
+                       if err != nil {
+                               return err
+                       }
+               }
+       }
+
+       // Apply profile configuration
+       if config.Profiles != nil && len(config.Profiles) > 0 {
+               // Get the list of profiles
+               profileNames, err := d.GetProfileNames()
+               if err != nil {
+                       return errors.Wrap(err, "Failed to retrieve list of 
profiles")
+               }
+
+               // Profile creator
+               createProfile := func(profile api.ProfilesPost) error {
+                       // Create the profile if doesn't exist
+                       err := d.CreateProfile(profile)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to create 
profile '%s'", profile.Name)
+                       }
+
+                       // Setup reverter
+                       reverts = append(reverts, func() {
+                               d.DeleteProfile(profile.Name)
+                       })
+
+                       return nil
+               }
+
+               // Profile updater
+               updateProfile := func(profile api.ProfilesPost) error {
+                       // Get the current profile
+                       currentProfile, etag, err := d.GetProfile(profile.Name)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to retrieve 
current profile '%s'", profile.Name)
+                       }
+
+                       // Setup reverter
+                       reverts = append(reverts, func() {
+                               d.UpdateProfile(currentProfile.Name, 
currentProfile.Writable(), "")
+                       })
+
+                       // Prepare the update
+                       newProfile := api.ProfilePut{}
+                       err = shared.DeepCopy(currentProfile.Writable(), 
&newProfile)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to copy 
configuration of profile '%s'", profile.Name)
+                       }
+
+                       // Description override
+                       if profile.Description != "" {
+                               newProfile.Description = profile.Description
+                       }
+
+                       // Config overrides
+                       for k, v := range profile.Config {
+                               newProfile.Config[k] = fmt.Sprintf("%v", v)
+                       }
+
+                       // Device overrides
+                       for k, v := range profile.Devices {
+                               // New device
+                               _, ok := newProfile.Devices[k]
+                               if !ok {
+                                       newProfile.Devices[k] = v
+                                       continue
+                               }
+
+                               // Existing device
+                               for configKey, configValue := range v {
+                                       newProfile.Devices[k][configKey] = 
fmt.Sprintf("%v", configValue)
+                               }
+                       }
+
+                       // Apply it
+                       err = d.UpdateProfile(currentProfile.Name, newProfile, 
etag)
+                       if err != nil {
+                               return errors.Wrapf(err, "Failed to update 
profile '%s'", profile.Name)
+                       }
+
+                       return nil
+               }
+
+               for _, profile := range config.Profiles {
+                       // New profile
+                       if !shared.StringInSlice(profile.Name, profileNames) {
+                               err := createProfile(profile)
+                               if err != nil {
+                                       return err
+                               }
+
+                               continue
+                       }
+
+                       // Existing profile
+                       err := updateProfile(profile)
+                       if err != nil {
+                               return err
+                       }
+               }
+       }
+
+       // Apply clustering configuration
+       if config.Cluster != nil && config.Cluster.Enabled {
+               // Get the current cluster configuration
+               currentCluster, etag, err := d.GetCluster()
+               if err != nil {
+                       return errors.Wrap(err, "Failed to retrieve current 
cluster config")
+               }
+
+               // Check if already enabled
+               if !currentCluster.Enabled {
+                       // Setup trust relationship
+                       if config.Cluster.ClusterAddress != "" && 
config.Cluster.ClusterPassword != "" {
+                               // Get our certificate
+                               serverConfig, _, err := d.GetServer()
+                               if err != nil {
+                                       return errors.Wrap(err, "Failed to 
retrieve server configuration")
+                               }
+
+                               // Try to setup trust
+                               err = 
cluster.SetupTrust(serverConfig.Environment.Certificate, 
config.Cluster.ClusterAddress,
+                                       config.Cluster.ClusterCertificate, 
config.Cluster.ClusterPassword)
+                               if err != nil {
+                                       return errors.Wrap(err, "Failed to 
setup cluster trust")
+                               }
+                       }
+
+                       // Configure the cluster
+                       op, err := d.UpdateCluster(config.Cluster.ClusterPut, 
etag)
+                       if err != nil {
+                               return errors.Wrap(err, "Failed to configure 
cluster")
+                       }
+
+                       err = op.Wait()
+                       if err != nil {
+                               return errors.Wrap(err, "Failed to configure 
cluster")
+                       }
+               }
+       }
+
+       revert = false
+       return nil
+}
diff --git a/lxd/main_init.go b/lxd/main_init.go
index 6ed65867a..ee1994bf2 100644
--- a/lxd/main_init.go
+++ b/lxd/main_init.go
@@ -7,25 +7,10 @@ import (
        "github.com/spf13/cobra"
 
        "github.com/lxc/lxd/client"
-       "github.com/lxc/lxd/lxd/cluster"
        "github.com/lxc/lxd/lxd/util"
        "github.com/lxc/lxd/shared"
-       "github.com/lxc/lxd/shared/api"
 )
 
-type initData struct {
-       api.ServerPut `yaml:",inline"`
-       Cluster       *initDataCluster       `json:"cluster" yaml:"cluster"`
-       Networks      []api.NetworksPost     `json:"networks" yaml:"networks"`
-       StoragePools  []api.StoragePoolsPost `json:"storage_pools" 
yaml:"storage_pools"`
-       Profiles      []api.ProfilesPost     `json:"profiles" yaml:"profiles"`
-}
-
-type initDataCluster struct {
-       api.ClusterPut  `yaml:",inline"`
-       ClusterPassword string `json:"cluster_password" yaml:"cluster_password"`
-}
-
 type cmdInit struct {
        global *cmdGlobal
 
@@ -114,7 +99,7 @@ func (c *cmdInit) Run(cmd *cobra.Command, args []string) 
error {
                }
        }
 
-       return c.ApplyConfig(cmd, args, d, *config)
+       return initApplyConfig(d, *config)
 }
 
 func (c *cmdInit) availableStorageDrivers(poolType string) []string {
@@ -157,358 +142,3 @@ func (c *cmdInit) availableStorageDrivers(poolType 
string) []string {
 
        return drivers
 }
-
-func (c *cmdInit) ApplyConfig(cmd *cobra.Command, args []string, d 
lxd.ContainerServer, config initData) error {
-       // Handle reverts
-       revert := true
-       reverts := []func(){}
-       defer func() {
-               if !revert {
-                       return
-               }
-
-               // Lets undo things in reverse order
-               for i := len(reverts) - 1; i >= 0; i-- {
-                       reverts[i]()
-               }
-       }()
-
-       // Apply server configuration
-       if config.Config != nil && len(config.Config) > 0 {
-               // Get current config
-               currentServer, etag, err := d.GetServer()
-               if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve current 
server configuration")
-               }
-
-               // Setup reverter
-               reverts = append(reverts, func() {
-                       d.UpdateServer(currentServer.Writable(), "")
-               })
-
-               // Prepare the update
-               newServer := api.ServerPut{}
-               err = shared.DeepCopy(currentServer.Writable(), &newServer)
-               if err != nil {
-                       return errors.Wrap(err, "Failed to copy server 
configuration")
-               }
-
-               for k, v := range config.Config {
-                       newServer.Config[k] = fmt.Sprintf("%v", v)
-               }
-
-               // Apply it
-               err = d.UpdateServer(newServer, etag)
-               if err != nil {
-                       return errors.Wrap(err, "Failed to update server 
configuration")
-               }
-       }
-
-       // Apply network configuration
-       if config.Networks != nil && len(config.Networks) > 0 {
-               // Get the list of networks
-               networkNames, err := d.GetNetworkNames()
-               if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve list of 
networks")
-               }
-
-               // Network creator
-               createNetwork := func(network api.NetworksPost) error {
-                       // Create the network if doesn't exist
-                       err := d.CreateNetwork(network)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to create 
network '%s'", network.Name)
-                       }
-
-                       // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.DeleteNetwork(network.Name)
-                       })
-
-                       return nil
-               }
-
-               // Network updater
-               updateNetwork := func(network api.NetworksPost) error {
-                       // Get the current network
-                       currentNetwork, etag, err := d.GetNetwork(network.Name)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to retrieve 
current network '%s'", network.Name)
-                       }
-
-                       // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.UpdateNetwork(currentNetwork.Name, 
currentNetwork.Writable(), "")
-                       })
-
-                       // Prepare the update
-                       newNetwork := api.NetworkPut{}
-                       err = shared.DeepCopy(currentNetwork.Writable(), 
&newNetwork)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to copy 
configuration of network '%s'", network.Name)
-                       }
-
-                       // Description override
-                       if network.Description != "" {
-                               newNetwork.Description = network.Description
-                       }
-
-                       // Config overrides
-                       for k, v := range network.Config {
-                               newNetwork.Config[k] = fmt.Sprintf("%v", v)
-                       }
-
-                       // Apply it
-                       err = d.UpdateNetwork(currentNetwork.Name, newNetwork, 
etag)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to update 
network '%s'", network.Name)
-                       }
-
-                       return nil
-               }
-
-               for _, network := range config.Networks {
-                       // New network
-                       if !shared.StringInSlice(network.Name, networkNames) {
-                               err := createNetwork(network)
-                               if err != nil {
-                                       return err
-                               }
-
-                               continue
-                       }
-
-                       // Existing network
-                       err := updateNetwork(network)
-                       if err != nil {
-                               return err
-                       }
-               }
-       }
-
-       // Apply storage configuration
-       if config.StoragePools != nil && len(config.StoragePools) > 0 {
-               // Get the list of storagePools
-               storagePoolNames, err := d.GetStoragePoolNames()
-               if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve list of 
storage pools")
-               }
-
-               // StoragePool creator
-               createStoragePool := func(storagePool api.StoragePoolsPost) 
error {
-                       // Create the storagePool if doesn't exist
-                       err := d.CreateStoragePool(storagePool)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to create 
storage pool '%s'", storagePool.Name)
-                       }
-
-                       // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.DeleteStoragePool(storagePool.Name)
-                       })
-
-                       return nil
-               }
-
-               // StoragePool updater
-               updateStoragePool := func(storagePool api.StoragePoolsPost) 
error {
-                       // Get the current storagePool
-                       currentStoragePool, etag, err := 
d.GetStoragePool(storagePool.Name)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to retrieve 
current storage pool '%s'", storagePool.Name)
-                       }
-
-                       // Sanity check
-                       if currentStoragePool.Driver != storagePool.Driver {
-                               return fmt.Errorf("Storage pool '%s' is of type 
'%s' instead of '%s'", currentStoragePool.Name, currentStoragePool.Driver, 
storagePool.Driver)
-                       }
-
-                       // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.UpdateStoragePool(currentStoragePool.Name, 
currentStoragePool.Writable(), "")
-                       })
-
-                       // Prepare the update
-                       newStoragePool := api.StoragePoolPut{}
-                       err = shared.DeepCopy(currentStoragePool.Writable(), 
&newStoragePool)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to copy 
configuration of storage pool '%s'", storagePool.Name)
-                       }
-
-                       // Description override
-                       if storagePool.Description != "" {
-                               newStoragePool.Description = 
storagePool.Description
-                       }
-
-                       // Config overrides
-                       for k, v := range storagePool.Config {
-                               newStoragePool.Config[k] = fmt.Sprintf("%v", v)
-                       }
-
-                       // Apply it
-                       err = d.UpdateStoragePool(currentStoragePool.Name, 
newStoragePool, etag)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to update 
storage pool '%s'", storagePool.Name)
-                       }
-
-                       return nil
-               }
-
-               for _, storagePool := range config.StoragePools {
-                       // New storagePool
-                       if !shared.StringInSlice(storagePool.Name, 
storagePoolNames) {
-                               err := createStoragePool(storagePool)
-                               if err != nil {
-                                       return err
-                               }
-
-                               continue
-                       }
-
-                       // Existing storagePool
-                       err := updateStoragePool(storagePool)
-                       if err != nil {
-                               return err
-                       }
-               }
-       }
-
-       // Apply profile configuration
-       if config.Profiles != nil && len(config.Profiles) > 0 {
-               // Get the list of profiles
-               profileNames, err := d.GetProfileNames()
-               if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve list of 
profiles")
-               }
-
-               // Profile creator
-               createProfile := func(profile api.ProfilesPost) error {
-                       // Create the profile if doesn't exist
-                       err := d.CreateProfile(profile)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to create 
profile '%s'", profile.Name)
-                       }
-
-                       // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.DeleteProfile(profile.Name)
-                       })
-
-                       return nil
-               }
-
-               // Profile updater
-               updateProfile := func(profile api.ProfilesPost) error {
-                       // Get the current profile
-                       currentProfile, etag, err := d.GetProfile(profile.Name)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to retrieve 
current profile '%s'", profile.Name)
-                       }
-
-                       // Setup reverter
-                       reverts = append(reverts, func() {
-                               d.UpdateProfile(currentProfile.Name, 
currentProfile.Writable(), "")
-                       })
-
-                       // Prepare the update
-                       newProfile := api.ProfilePut{}
-                       err = shared.DeepCopy(currentProfile.Writable(), 
&newProfile)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to copy 
configuration of profile '%s'", profile.Name)
-                       }
-
-                       // Description override
-                       if profile.Description != "" {
-                               newProfile.Description = profile.Description
-                       }
-
-                       // Config overrides
-                       for k, v := range profile.Config {
-                               newProfile.Config[k] = fmt.Sprintf("%v", v)
-                       }
-
-                       // Device overrides
-                       for k, v := range profile.Devices {
-                               // New device
-                               _, ok := newProfile.Devices[k]
-                               if !ok {
-                                       newProfile.Devices[k] = v
-                                       continue
-                               }
-
-                               // Existing device
-                               for configKey, configValue := range v {
-                                       newProfile.Devices[k][configKey] = 
fmt.Sprintf("%v", configValue)
-                               }
-                       }
-
-                       // Apply it
-                       err = d.UpdateProfile(currentProfile.Name, newProfile, 
etag)
-                       if err != nil {
-                               return errors.Wrapf(err, "Failed to update 
profile '%s'", profile.Name)
-                       }
-
-                       return nil
-               }
-
-               for _, profile := range config.Profiles {
-                       // New profile
-                       if !shared.StringInSlice(profile.Name, profileNames) {
-                               err := createProfile(profile)
-                               if err != nil {
-                                       return err
-                               }
-
-                               continue
-                       }
-
-                       // Existing profile
-                       err := updateProfile(profile)
-                       if err != nil {
-                               return err
-                       }
-               }
-       }
-
-       // Apply clustering configuration
-       if config.Cluster != nil && config.Cluster.Enabled {
-               // Get the current cluster configuration
-               currentCluster, etag, err := d.GetCluster()
-               if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve current 
cluster config")
-               }
-
-               // Check if already enabled
-               if !currentCluster.Enabled {
-                       // Setup trust relationship
-                       if config.Cluster.ClusterAddress != "" && 
config.Cluster.ClusterPassword != "" {
-                               // Get our certificate
-                               serverConfig, _, err := d.GetServer()
-                               if err != nil {
-                                       return errors.Wrap(err, "Failed to 
retrieve server configuration")
-                               }
-
-                               // Try to setup trust
-                               err = 
cluster.SetupTrust(serverConfig.Environment.Certificate, 
config.Cluster.ClusterAddress,
-                                       config.Cluster.ClusterCertificate, 
config.Cluster.ClusterPassword)
-                               if err != nil {
-                                       return errors.Wrap(err, "Failed to 
setup cluster trust")
-                               }
-                       }
-
-                       // Configure the cluster
-                       op, err := d.UpdateCluster(config.Cluster.ClusterPut, 
etag)
-                       if err != nil {
-                               return errors.Wrap(err, "Failed to configure 
cluster")
-                       }
-
-                       err = op.Wait()
-                       if err != nil {
-                               return errors.Wrap(err, "Failed to configure 
cluster")
-                       }
-               }
-       }
-
-       revert = false
-       return nil
-}

From e469bee487d9704efc152374d3de38928f30b8fb Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <[email protected]>
Date: Thu, 14 Jun 2018 09:31:41 +0000
Subject: [PATCH 2/3] Split initApplyConfig into initDataNodeApply and
 initDataClusterApply

The logic initDataNodeApply will be shared with the PUT /1.0/cluster API.

Signed-off-by: Free Ekanayaka <[email protected]>
---
 lxd/init.go                  | 109 ++++++++++++++++++++++---------------------
 lxd/main_init.go             |  15 +++++-
 lxd/main_init_auto.go        |  10 ++--
 lxd/main_init_interactive.go |  78 +++++++++++++++----------------
 lxd/main_init_preseed.go     |   4 +-
 5 files changed, 115 insertions(+), 101 deletions(-)

diff --git a/lxd/init.go b/lxd/init.go
index 873faa00d..49d441649 100644
--- a/lxd/init.go
+++ b/lxd/init.go
@@ -10,9 +10,8 @@ import (
        "github.com/pkg/errors"
 )
 
-type initData struct {
+type initDataNode struct {
        api.ServerPut `yaml:",inline"`
-       Cluster       *initDataCluster       `json:"cluster" yaml:"cluster"`
        Networks      []api.NetworksPost     `json:"networks" yaml:"networks"`
        StoragePools  []api.StoragePoolsPost `json:"storage_pools" 
yaml:"storage_pools"`
        Profiles      []api.ProfilesPost     `json:"profiles" yaml:"profiles"`
@@ -23,31 +22,28 @@ type initDataCluster struct {
        ClusterPassword string `json:"cluster_password" yaml:"cluster_password"`
 }
 
-// Helper to initialize a LXD instance using the definitions from an initData
-// object.
+// Helper to initialize node-specific entities on a LXD instance using the
+// definitions from the given initDataNode object.
 //
 // It's used both by the 'lxd init' command and by the PUT /1.0/cluster API.
-func initApplyConfig(d lxd.ContainerServer, config initData) error {
+//
+// In case of error, the returned function can be used to revert the changes.
+func initDataNodeApply(d lxd.ContainerServer, config initDataNode) (func(), 
error) {
        // Handle reverts
-       revert := true
        reverts := []func(){}
-       defer func() {
-               if !revert {
-                       return
-               }
-
+       revert := func() {
                // Lets undo things in reverse order
                for i := len(reverts) - 1; i >= 0; i-- {
                        reverts[i]()
                }
-       }()
+       }
 
        // Apply server configuration
        if config.Config != nil && len(config.Config) > 0 {
                // Get current config
                currentServer, etag, err := d.GetServer()
                if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve current 
server configuration")
+                       return revert, errors.Wrap(err, "Failed to retrieve 
current server configuration")
                }
 
                // Setup reverter
@@ -59,7 +55,7 @@ func initApplyConfig(d lxd.ContainerServer, config initData) 
error {
                newServer := api.ServerPut{}
                err = shared.DeepCopy(currentServer.Writable(), &newServer)
                if err != nil {
-                       return errors.Wrap(err, "Failed to copy server 
configuration")
+                       return revert, errors.Wrap(err, "Failed to copy server 
configuration")
                }
 
                for k, v := range config.Config {
@@ -69,7 +65,7 @@ func initApplyConfig(d lxd.ContainerServer, config initData) 
error {
                // Apply it
                err = d.UpdateServer(newServer, etag)
                if err != nil {
-                       return errors.Wrap(err, "Failed to update server 
configuration")
+                       return revert, errors.Wrap(err, "Failed to update 
server configuration")
                }
        }
 
@@ -78,7 +74,7 @@ func initApplyConfig(d lxd.ContainerServer, config initData) 
error {
                // Get the list of networks
                networkNames, err := d.GetNetworkNames()
                if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve list of 
networks")
+                       return revert, errors.Wrap(err, "Failed to retrieve 
list of networks")
                }
 
                // Network creator
@@ -141,7 +137,7 @@ func initApplyConfig(d lxd.ContainerServer, config 
initData) error {
                        if !shared.StringInSlice(network.Name, networkNames) {
                                err := createNetwork(network)
                                if err != nil {
-                                       return err
+                                       return revert, err
                                }
 
                                continue
@@ -150,7 +146,7 @@ func initApplyConfig(d lxd.ContainerServer, config 
initData) error {
                        // Existing network
                        err := updateNetwork(network)
                        if err != nil {
-                               return err
+                               return revert, err
                        }
                }
        }
@@ -160,7 +156,7 @@ func initApplyConfig(d lxd.ContainerServer, config 
initData) error {
                // Get the list of storagePools
                storagePoolNames, err := d.GetStoragePoolNames()
                if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve list of 
storage pools")
+                       return revert, errors.Wrap(err, "Failed to retrieve 
list of storage pools")
                }
 
                // StoragePool creator
@@ -228,7 +224,7 @@ func initApplyConfig(d lxd.ContainerServer, config 
initData) error {
                        if !shared.StringInSlice(storagePool.Name, 
storagePoolNames) {
                                err := createStoragePool(storagePool)
                                if err != nil {
-                                       return err
+                                       return revert, err
                                }
 
                                continue
@@ -237,7 +233,7 @@ func initApplyConfig(d lxd.ContainerServer, config 
initData) error {
                        // Existing storagePool
                        err := updateStoragePool(storagePool)
                        if err != nil {
-                               return err
+                               return revert, err
                        }
                }
        }
@@ -247,7 +243,7 @@ func initApplyConfig(d lxd.ContainerServer, config 
initData) error {
                // Get the list of profiles
                profileNames, err := d.GetProfileNames()
                if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve list of 
profiles")
+                       return revert, errors.Wrap(err, "Failed to retrieve 
list of profiles")
                }
 
                // Profile creator
@@ -325,7 +321,7 @@ func initApplyConfig(d lxd.ContainerServer, config 
initData) error {
                        if !shared.StringInSlice(profile.Name, profileNames) {
                                err := createProfile(profile)
                                if err != nil {
-                                       return err
+                                       return revert, err
                                }
 
                                continue
@@ -334,50 +330,57 @@ func initApplyConfig(d lxd.ContainerServer, config 
initData) error {
                        // Existing profile
                        err := updateProfile(profile)
                        if err != nil {
-                               return err
+                               return revert, err
                        }
                }
        }
 
-       // Apply clustering configuration
-       if config.Cluster != nil && config.Cluster.Enabled {
-               // Get the current cluster configuration
-               currentCluster, etag, err := d.GetCluster()
-               if err != nil {
-                       return errors.Wrap(err, "Failed to retrieve current 
cluster config")
-               }
+       return nil, nil
+}
 
-               // Check if already enabled
-               if !currentCluster.Enabled {
-                       // Setup trust relationship
-                       if config.Cluster.ClusterAddress != "" && 
config.Cluster.ClusterPassword != "" {
-                               // Get our certificate
-                               serverConfig, _, err := d.GetServer()
-                               if err != nil {
-                                       return errors.Wrap(err, "Failed to 
retrieve server configuration")
-                               }
+// Helper to initialize LXD clustering.
+//
+// Used by the 'lxd init' command.
+func initDataClusterApply(d lxd.ContainerServer, config *initDataCluster) 
error {
+       if config == nil || !config.Enabled {
+               return nil
+       }
 
-                               // Try to setup trust
-                               err = 
cluster.SetupTrust(serverConfig.Environment.Certificate, 
config.Cluster.ClusterAddress,
-                                       config.Cluster.ClusterCertificate, 
config.Cluster.ClusterPassword)
-                               if err != nil {
-                                       return errors.Wrap(err, "Failed to 
setup cluster trust")
-                               }
-                       }
+       // Get the current cluster configuration
+       currentCluster, etag, err := d.GetCluster()
+       if err != nil {
+               return errors.Wrap(err, "Failed to retrieve current cluster 
config")
+       }
 
-                       // Configure the cluster
-                       op, err := d.UpdateCluster(config.Cluster.ClusterPut, 
etag)
+       // Check if already enabled
+       if !currentCluster.Enabled {
+               // Setup trust relationship
+               if config.ClusterAddress != "" && config.ClusterPassword != "" {
+                       // Get our certificate
+                       serverConfig, _, err := d.GetServer()
                        if err != nil {
-                               return errors.Wrap(err, "Failed to configure 
cluster")
+                               return errors.Wrap(err, "Failed to retrieve 
server configuration")
                        }
 
-                       err = op.Wait()
+                       // Try to setup trust
+                       err = 
cluster.SetupTrust(serverConfig.Environment.Certificate, config.ClusterAddress,
+                               config.ClusterCertificate, 
config.ClusterPassword)
                        if err != nil {
-                               return errors.Wrap(err, "Failed to configure 
cluster")
+                               return errors.Wrap(err, "Failed to setup 
cluster trust")
                        }
                }
+
+               // Configure the cluster
+               op, err := d.UpdateCluster(config.ClusterPut, etag)
+               if err != nil {
+                       return errors.Wrap(err, "Failed to configure cluster")
+               }
+
+               err = op.Wait()
+               if err != nil {
+                       return errors.Wrap(err, "Failed to configure cluster")
+               }
        }
 
-       revert = false
        return nil
 }
diff --git a/lxd/main_init.go b/lxd/main_init.go
index ee1994bf2..7b819eea5 100644
--- a/lxd/main_init.go
+++ b/lxd/main_init.go
@@ -11,6 +11,11 @@ import (
        "github.com/lxc/lxd/shared"
 )
 
+type cmdInitData struct {
+       Node    initDataNode     `yaml:",inline"`
+       Cluster *initDataCluster `json:"cluster" yaml:"cluster"`
+}
+
 type cmdInit struct {
        global *cmdGlobal
 
@@ -73,7 +78,7 @@ func (c *cmdInit) Run(cmd *cobra.Command, args []string) 
error {
        }
 
        // Prepare the input data
-       var config *initData
+       var config *cmdInitData
 
        // Preseed mode
        if c.flagPreseed {
@@ -99,7 +104,13 @@ func (c *cmdInit) Run(cmd *cobra.Command, args []string) 
error {
                }
        }
 
-       return initApplyConfig(d, *config)
+       revert, err := initDataNodeApply(d, config.Node)
+       if err != nil {
+               revert()
+               return err
+       }
+
+       return initDataClusterApply(d, config.Cluster)
 }
 
 func (c *cmdInit) availableStorageDrivers(poolType string) []string {
diff --git a/lxd/main_init_auto.go b/lxd/main_init_auto.go
index 656b8d02f..a819bca3e 100644
--- a/lxd/main_init_auto.go
+++ b/lxd/main_init_auto.go
@@ -11,7 +11,7 @@ import (
        "github.com/lxc/lxd/shared/api"
 )
 
-func (c *cmdInit) RunAuto(cmd *cobra.Command, args []string, d 
lxd.ContainerServer) (*initData, error) {
+func (c *cmdInit) RunAuto(cmd *cobra.Command, args []string, d 
lxd.ContainerServer) (*cmdInitData, error) {
        // Sanity checks
        if c.flagStorageBackend != "" && 
!shared.StringInSlice(c.flagStorageBackend, supportedStoragePoolDrivers) {
                return nil, fmt.Errorf("The requested backend '%s' isn't 
supported by lxd init", c.flagStorageBackend)
@@ -59,8 +59,8 @@ func (c *cmdInit) RunAuto(cmd *cobra.Command, args []string, 
d lxd.ContainerServ
                c.flagNetworkPort = 8443
        }
 
-       // Fill in the configuration
-       config := initData{}
+       // Fill in the node configuration
+       config := initDataNode{}
        config.Config = map[string]interface{}{}
 
        // Network listening
@@ -143,7 +143,7 @@ func (c *cmdInit) RunAuto(cmd *cobra.Command, args 
[]string, d lxd.ContainerServ
                idx := 0
                for {
                        if 
shared.PathExists(fmt.Sprintf("/sys/class/net/lxdbr%d", idx)) {
-                               idx += 1
+                               idx++
                                continue
                        }
 
@@ -180,5 +180,5 @@ func (c *cmdInit) RunAuto(cmd *cobra.Command, args 
[]string, d lxd.ContainerServ
                }
        }
 
-       return &config, nil
+       return &cmdInitData{Node: config}, nil
 }
diff --git a/lxd/main_init_interactive.go b/lxd/main_init_interactive.go
index 73ae50d59..fef37b1ec 100644
--- a/lxd/main_init_interactive.go
+++ b/lxd/main_init_interactive.go
@@ -23,13 +23,13 @@ import (
        "github.com/lxc/lxd/shared/idmap"
 )
 
-func (c *cmdInit) RunInteractive(cmd *cobra.Command, args []string, d 
lxd.ContainerServer) (*initData, error) {
+func (c *cmdInit) RunInteractive(cmd *cobra.Command, args []string, d 
lxd.ContainerServer) (*cmdInitData, error) {
        // Initialize config
-       config := initData{}
-       config.Config = map[string]interface{}{}
-       config.Networks = []api.NetworksPost{}
-       config.StoragePools = []api.StoragePoolsPost{}
-       config.Profiles = []api.ProfilesPost{
+       config := cmdInitData{}
+       config.Node.Config = map[string]interface{}{}
+       config.Node.Networks = []api.NetworksPost{}
+       config.Node.StoragePools = []api.StoragePoolsPost{}
+       config.Node.Profiles = []api.ProfilesPost{
                {
                        Name: "default",
                        ProfilePut: api.ProfilePut{
@@ -85,7 +85,7 @@ func (c *cmdInit) RunInteractive(cmd *cobra.Command, args 
[]string, d lxd.Contai
        return &config, nil
 }
 
-func (c *cmdInit) askClustering(config *initData, d lxd.ContainerServer) error 
{
+func (c *cmdInit) askClustering(config *cmdInitData, d lxd.ContainerServer) 
error {
        if cli.AskBool("Would you like to use LXD clustering? (yes/no) 
[default=no]: ", "no") {
                config.Cluster = &initDataCluster{}
                config.Cluster.Enabled = true
@@ -103,7 +103,7 @@ func (c *cmdInit) askClustering(config *initData, d 
lxd.ContainerServer) error {
                address := util.NetworkInterfaceAddress()
                serverAddress := util.CanonicalNetworkAddress(cli.AskString(
                        fmt.Sprintf("What IP address or DNS name should be used 
to reach this node? [default=%s]: ", address), address, nil))
-               config.Config["core.https_address"] = serverAddress
+               config.Node.Config["core.https_address"] = serverAddress
 
                if cli.AskBool("Are you joining an existing cluster? (yes/no) 
[default=no]: ", "no") {
                        // Existing cluster
@@ -177,7 +177,7 @@ func (c *cmdInit) askClustering(config *initData, d 
lxd.ContainerServer) error {
                                return errors.Wrap(err, "Failed to retrieve 
storage pools from the cluster")
                        }
 
-                       config.StoragePools = []api.StoragePoolsPost{}
+                       config.Node.StoragePools = []api.StoragePoolsPost{}
                        for _, pool := range targetPools {
                                // Skip pending pools
                                if pool.Status == "PENDING" {
@@ -208,7 +208,7 @@ func (c *cmdInit) askClustering(config *initData, d 
lxd.ContainerServer) error {
                                                fmt.Sprintf(`Choose the local 
disk or dataset for storage pool "%s" (empty for loop disk): `, pool.Name), "", 
validator)
                                }
 
-                               config.StoragePools = 
append(config.StoragePools, newPool)
+                               config.Node.StoragePools = 
append(config.Node.StoragePools, newPool)
                        }
 
                        // Prompt for network config
@@ -217,7 +217,7 @@ func (c *cmdInit) askClustering(config *initData, d 
lxd.ContainerServer) error {
                                return errors.Wrap(err, "Failed to retrieve 
networks from the cluster")
                        }
 
-                       config.Networks = []api.NetworksPost{}
+                       config.Node.Networks = []api.NetworksPost{}
                        for _, network := range targetNetworks {
                                // Skip not-managed or pending networks
                                if !network.Managed || network.Status == 
"PENDING" {
@@ -240,12 +240,12 @@ func (c *cmdInit) askClustering(config *initData, d 
lxd.ContainerServer) error {
                                                fmt.Sprintf(`Choose the local 
network interface to connect to network "%s" (empty for none): `, 
network.Name), "", validator)
                                }
 
-                               config.Networks = append(config.Networks, 
newNetwork)
+                               config.Node.Networks = 
append(config.Node.Networks, newNetwork)
                        }
                } else {
                        // Password authentication
                        if cli.AskBool("Setup password authentication on the 
cluster? (yes/no) [default=yes]: ", "yes") {
-                               config.Config["core.trust_password"] = 
cli.AskPassword("Trust password for new clients: ")
+                               config.Node.Config["core.trust_password"] = 
cli.AskPassword("Trust password for new clients: ")
                        }
                }
        }
@@ -253,7 +253,7 @@ func (c *cmdInit) askClustering(config *initData, d 
lxd.ContainerServer) error {
        return nil
 }
 
-func (c *cmdInit) askMAAS(config *initData, d lxd.ContainerServer) error {
+func (c *cmdInit) askMAAS(config *cmdInitData, d lxd.ContainerServer) error {
        if !cli.AskBool("Would you like to connect to a MAAS server? (yes/no) 
[default=no]: ", "no") {
                return nil
        }
@@ -265,16 +265,16 @@ func (c *cmdInit) askMAAS(config *initData, d 
lxd.ContainerServer) error {
 
        maasHostname := cli.AskString(fmt.Sprintf("What's the name of this host 
in MAAS? [default=%s]: ", serverName), serverName, nil)
        if maasHostname != serverName {
-               config.Config["maas.machine"] = maasHostname
+               config.Node.Config["maas.machine"] = maasHostname
        }
 
-       config.Config["maas.api.url"] = cli.AskString("URL of your MAAS server 
(e.g. http://1.2.3.4:5240/MAAS): ", "", nil)
-       config.Config["maas.api.key"] = cli.AskString("API key for your MAAS 
server: ", "", nil)
+       config.Node.Config["maas.api.url"] = cli.AskString("URL of your MAAS 
server (e.g. http://1.2.3.4:5240/MAAS): ", "", nil)
+       config.Node.Config["maas.api.key"] = cli.AskString("API key for your 
MAAS server: ", "", nil)
 
        return nil
 }
 
-func (c *cmdInit) askNetworking(config *initData, d lxd.ContainerServer) error 
{
+func (c *cmdInit) askNetworking(config *cmdInitData, d lxd.ContainerServer) 
error {
        if config.Cluster != nil || !cli.AskBool("Would you like to create a 
new local network bridge? (yes/no) [default=yes]: ", "yes") {
                // At this time, only the Ubuntu kernel supports the Fan, 
detect it
                fanKernel := false
@@ -295,7 +295,7 @@ func (c *cmdInit) askNetworking(config *initData, d 
lxd.ContainerServer) error {
                                }
 
                                // Add to the default profile
-                               config.Profiles[0].Devices["eth0"] = 
map[string]string{
+                               config.Node.Profiles[0].Devices["eth0"] = 
map[string]string{
                                        "type":    "nic",
                                        "nictype": "macvlan",
                                        "name":    "eth0",
@@ -303,22 +303,22 @@ func (c *cmdInit) askNetworking(config *initData, d 
lxd.ContainerServer) error {
                                }
 
                                if 
shared.PathExists(fmt.Sprintf("/sys/class/net/%s/bridge", name)) {
-                                       
config.Profiles[0].Devices["eth0"]["nictype"] = "bridged"
+                                       
config.Node.Profiles[0].Devices["eth0"]["nictype"] = "bridged"
                                }
 
-                               if config.Config["maas.api.url"] != nil && 
cli.AskBool("Is this interface connected to your MAAS server? (yes/no) 
[default=yes]: ", "yes") {
+                               if config.Node.Config["maas.api.url"] != nil && 
cli.AskBool("Is this interface connected to your MAAS server? (yes/no) 
[default=yes]: ", "yes") {
                                        maasSubnetV4 := cli.AskString("MAAS 
IPv4 subnet name for this interface (empty for no subnet): ", "",
                                                func(input string) error { 
return nil })
 
                                        if maasSubnetV4 != "" {
-                                               
config.Profiles[0].Devices["eth0"]["maas.subnet.ipv4"] = maasSubnetV4
+                                               
config.Node.Profiles[0].Devices["eth0"]["maas.subnet.ipv4"] = maasSubnetV4
                                        }
 
                                        maasSubnetV6 := cli.AskString("MAAS 
IPv6 subnet name for this interface (empty for no subnet): ", "",
                                                func(input string) error { 
return nil })
 
                                        if maasSubnetV6 != "" {
-                                               
config.Profiles[0].Devices["eth0"]["maas.subnet.ipv6"] = maasSubnetV6
+                                               
config.Node.Profiles[0].Devices["eth0"]["maas.subnet.ipv6"] = maasSubnetV6
                                        }
                                }
 
@@ -333,10 +333,10 @@ func (c *cmdInit) askNetworking(config *initData, d 
lxd.ContainerServer) error {
                        }
 
                        // Add the new network
-                       config.Networks = append(config.Networks, network)
+                       config.Node.Networks = append(config.Node.Networks, 
network)
 
                        // Add to the default profile
-                       config.Profiles[0].Devices["eth0"] = map[string]string{
+                       config.Node.Profiles[0].Devices["eth0"] = 
map[string]string{
                                "type":    "nic",
                                "nictype": "bridged",
                                "name":    "eth0",
@@ -361,7 +361,7 @@ func (c *cmdInit) askNetworking(config *initData, d 
lxd.ContainerServer) error {
                }
 
                // Add to the default profile
-               config.Profiles[0].Devices["eth0"] = map[string]string{
+               config.Node.Profiles[0].Devices["eth0"] = map[string]string{
                        "type":    "nic",
                        "nictype": "bridged",
                        "name":    "eth0",
@@ -397,14 +397,14 @@ func (c *cmdInit) askNetworking(config *initData, d 
lxd.ContainerServer) error {
                }
 
                // Add the new network
-               config.Networks = append(config.Networks, network)
+               config.Node.Networks = append(config.Node.Networks, network)
                break
        }
 
        return nil
 }
 
-func (c *cmdInit) askStorage(config *initData, d lxd.ContainerServer) error {
+func (c *cmdInit) askStorage(config *cmdInitData, d lxd.ContainerServer) error 
{
        if config.Cluster != nil {
                if cli.AskBool("Do you want to configure a new local storage 
pool? (yes/no) [default=yes]: ", "yes") {
                        err := c.askStoragePool(config, d, "local")
@@ -430,7 +430,7 @@ func (c *cmdInit) askStorage(config *initData, d 
lxd.ContainerServer) error {
        return c.askStoragePool(config, d, "all")
 }
 
-func (c *cmdInit) askStoragePool(config *initData, d lxd.ContainerServer, 
poolType string) error {
+func (c *cmdInit) askStoragePool(config *cmdInitData, d lxd.ContainerServer, 
poolType string) error {
        // Figure out the preferred storage driver
        availableBackends := c.availableStorageDrivers(poolType)
 
@@ -474,7 +474,7 @@ func (c *cmdInit) askStoragePool(config *initData, d 
lxd.ContainerServer, poolTy
                }
 
                // Add to the default profile
-               config.Profiles[0].Devices["root"] = map[string]string{
+               config.Node.Profiles[0].Devices["root"] = map[string]string{
                        "type": "disk",
                        "path": "/",
                        "pool": pool.Name,
@@ -490,7 +490,7 @@ func (c *cmdInit) askStoragePool(config *initData, d 
lxd.ContainerServer, poolTy
 
                // Optimization for dir
                if pool.Driver == "dir" {
-                       config.StoragePools = append(config.StoragePools, pool)
+                       config.Node.StoragePools = 
append(config.Node.StoragePools, pool)
                        break
                }
 
@@ -498,7 +498,7 @@ func (c *cmdInit) askStoragePool(config *initData, d 
lxd.ContainerServer, poolTy
                if pool.Driver == "btrfs" && backingFs == "btrfs" {
                        if cli.AskBool(fmt.Sprintf("Would you like to create a 
new btrfs subvolume under %s? (yes/no) [default=yes]: ", shared.VarPath("")), 
"yes") {
                                pool.Config["source"] = 
shared.VarPath("storage-pools", pool.Name)
-                               config.StoragePools = 
append(config.StoragePools, pool)
+                               config.Node.StoragePools = 
append(config.Node.StoragePools, pool)
                                break
                        }
                }
@@ -577,14 +577,14 @@ your Linux distribution and run "lxd init" again 
afterwards.
                        }
                }
 
-               config.StoragePools = append(config.StoragePools, pool)
+               config.Node.StoragePools = append(config.Node.StoragePools, 
pool)
                break
        }
 
        return nil
 }
 
-func (c *cmdInit) askDaemon(config *initData, d lxd.ContainerServer) error {
+func (c *cmdInit) askDaemon(config *cmdInitData, d lxd.ContainerServer) error {
        // Detect lack of uid/gid
        idmapset, err := idmap.DefaultIdmapSet("")
        if (err != nil || len(idmapset.Idmap) == 0 || idmapset.Usable() != nil) 
&& shared.RunningInUserNS() {
@@ -601,7 +601,7 @@ they otherwise would.
 `)
 
                if cli.AskBool("Would you like to have your containers share 
their parent's allocation? (yes/no) [default=yes]: ", "yes") {
-                       config.Profiles[0].Config["security.privileged"] = 
"true"
+                       config.Node.Profiles[0].Config["security.privileged"] = 
"true"
                }
        }
 
@@ -625,16 +625,16 @@ they otherwise would.
                }
 
                netPort := cli.AskInt("Port to bind LXD to [default=8443]: ", 
1, 65535, "8443")
-               config.Config["core.https_address"] = fmt.Sprintf("%s:%d", 
netAddr, netPort)
-               config.Config["core.trust_password"] = cli.AskPassword("Trust 
password for new clients: ")
-               if config.Config["core.trust_password"] == "" {
+               config.Node.Config["core.https_address"] = fmt.Sprintf("%s:%d", 
netAddr, netPort)
+               config.Node.Config["core.trust_password"] = 
cli.AskPassword("Trust password for new clients: ")
+               if config.Node.Config["core.trust_password"] == "" {
                        fmt.Printf("No password set, client certificates will 
have to be manually trusted.")
                }
        }
 
        // Ask if the user wants images to be automatically refreshed
        if !cli.AskBool("Would you like stale cached images to be updated 
automatically? (yes/no) [default=yes] ", "yes") {
-               config.Config["images.auto_update_interval"] = "0"
+               config.Node.Config["images.auto_update_interval"] = "0"
        }
 
        return nil
diff --git a/lxd/main_init_preseed.go b/lxd/main_init_preseed.go
index e21cc2034..0f75481d6 100644
--- a/lxd/main_init_preseed.go
+++ b/lxd/main_init_preseed.go
@@ -11,7 +11,7 @@ import (
        "github.com/lxc/lxd/client"
 )
 
-func (c *cmdInit) RunPreseed(cmd *cobra.Command, args []string, d 
lxd.ContainerServer) (*initData, error) {
+func (c *cmdInit) RunPreseed(cmd *cobra.Command, args []string, d 
lxd.ContainerServer) (*cmdInitData, error) {
        // Read the YAML
        bytes, err := ioutil.ReadAll(os.Stdin)
        if err != nil {
@@ -19,7 +19,7 @@ func (c *cmdInit) RunPreseed(cmd *cobra.Command, args 
[]string, d lxd.ContainerS
        }
 
        // Parse the YAML
-       config := initData{}
+       config := cmdInitData{}
        err = yaml.Unmarshal(bytes, &config)
        if err != nil {
                return nil, errors.Wrap(err, "Failed to parse the preseed")

From 8b3a766c7c51f4897c4261479d42e24288b5590b Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <[email protected]>
Date: Thu, 14 Jun 2018 10:00:01 +0000
Subject: [PATCH 3/3] Extend api.Cluster schema

Signed-off-by: Free Ekanayaka <[email protected]>
---
 shared/api/cluster.go | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/shared/api/cluster.go b/shared/api/cluster.go
index 3ae4db490..0d9d7d550 100644
--- a/shared/api/cluster.go
+++ b/shared/api/cluster.go
@@ -4,8 +4,16 @@ package api
 //
 // API extension: clustering
 type Cluster struct {
-       ServerName string `json:"server_name" yaml:"server_name"`
-       Enabled    bool   `json:"enabled" yaml:"enabled"`
+       ServerName   string                   `json:"server_name" 
yaml:"server_name"`
+       Enabled      bool                     `json:"enabled" yaml:"enabled"`
+       MemberConfig []ClusterMemberConfigKey `json:"member_config" 
yaml:"member_config"`
+}
+
+// ClusterMemberConfigKey represents a single config key that a new member of
+// the cluster is required to provide when joining.
+type ClusterMemberConfigKey struct {
+       Name        string `json:"name" yaml:"name"`
+       Description string `json:"description" yaml:"description"`
 }
 
 // ClusterPut represents the fields required to bootstrap or join a LXD
_______________________________________________
lxc-devel mailing list
[email protected]
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to