github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/provider/vsphere/config.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // +build !gccgo 5 6 package vsphere 7 8 import ( 9 "fmt" 10 "net/url" 11 12 "github.com/juju/errors" 13 "github.com/juju/schema" 14 15 "github.com/juju/juju/environs/config" 16 ) 17 18 // The vmware-specific config keys. 19 const ( 20 cfgDatacenter = "datacenter" 21 cfgHost = "host" 22 cfgUser = "user" 23 cfgPassword = "password" 24 cfgExternalNetwork = "external-network" 25 ) 26 27 // boilerplateConfig will be shown in help output, so please keep it up to 28 // date when you change environment configuration below. 29 var boilerplateConfig = ` 30 vmware: 31 type: vsphere 32 33 # IP address or DNS name of vsphere API host. 34 host: 35 36 # Vsphere API user credentials. 37 user: 38 password: 39 40 # Name of vsphere datacenter. 41 datacenter: 42 43 # Name of the network, that all created vms will use ot obtain public ip address. 44 # This network should have ip pool configured or DHCP server connected to it. 45 # This parameter is optional. 46 extenal-network: 47 `[1:] 48 49 // configFields is the spec for each vmware config value's type. 50 var configFields = schema.Fields{ 51 cfgHost: schema.String(), 52 cfgUser: schema.String(), 53 cfgPassword: schema.String(), 54 cfgDatacenter: schema.String(), 55 cfgExternalNetwork: schema.String(), 56 } 57 58 var requiredFields = []string{ 59 cfgHost, 60 cfgUser, 61 cfgPassword, 62 cfgDatacenter, 63 } 64 65 var configDefaults = schema.Defaults{ 66 cfgExternalNetwork: "", 67 } 68 69 var configSecretFields = []string{ 70 cfgPassword, 71 } 72 73 var configImmutableFields = []string{ 74 cfgHost, 75 cfgDatacenter, 76 } 77 78 type environConfig struct { 79 *config.Config 80 attrs map[string]interface{} 81 } 82 83 // newConfig builds a new environConfig from the provided Config and 84 // returns it. 85 func newConfig(cfg *config.Config) *environConfig { 86 return &environConfig{ 87 Config: cfg, 88 attrs: cfg.UnknownAttrs(), 89 } 90 } 91 92 // newValidConfig builds a new environConfig from the provided Config 93 // and returns it. The resulting config values are validated. 94 func newValidConfig(cfg *config.Config, defaults map[string]interface{}) (*environConfig, error) { 95 // Ensure that the provided config is valid. 96 if err := config.Validate(cfg, nil); err != nil { 97 return nil, errors.Trace(err) 98 } 99 100 // Apply the defaults and coerce/validate the custom config attrs. 101 validated, err := cfg.ValidateUnknownAttrs(configFields, defaults) 102 if err != nil { 103 return nil, errors.Trace(err) 104 } 105 validCfg, err := cfg.Apply(validated) 106 if err != nil { 107 return nil, errors.Trace(err) 108 } 109 110 // Build the config. 111 ecfg := newConfig(validCfg) 112 113 // Do final validation. 114 if err := ecfg.validate(); err != nil { 115 return nil, errors.Trace(err) 116 } 117 118 return ecfg, nil 119 } 120 121 func (c *environConfig) datacenter() string { 122 return c.attrs[cfgDatacenter].(string) 123 } 124 125 func (c *environConfig) host() string { 126 return c.attrs[cfgHost].(string) 127 } 128 129 func (c *environConfig) user() string { 130 return c.attrs[cfgUser].(string) 131 } 132 133 func (c *environConfig) password() string { 134 return c.attrs[cfgPassword].(string) 135 } 136 137 func (c *environConfig) externalNetwork() string { 138 return c.attrs[cfgExternalNetwork].(string) 139 } 140 141 func (c *environConfig) url() (*url.URL, error) { 142 return url.Parse(fmt.Sprintf("https://%s:%s@%s/sdk", c.user(), c.password(), c.host())) 143 } 144 145 // secret gathers the "secret" config values and returns them. 146 func (c *environConfig) secret() map[string]string { 147 secretAttrs := make(map[string]string, len(configSecretFields)) 148 for _, key := range configSecretFields { 149 secretAttrs[key] = c.attrs[key].(string) 150 } 151 return secretAttrs 152 } 153 154 // validate checks vmware-specific config values. 155 func (c environConfig) validate() error { 156 // All fields must be populated, even with just the default. 157 for _, field := range requiredFields { 158 if c.attrs[field].(string) == "" { 159 return errors.Errorf("%s: must not be empty", field) 160 } 161 } 162 if _, err := c.url(); err != nil { 163 return errors.Trace(err) 164 } 165 166 return nil 167 } 168 169 // update applies changes from the provided config to the env config. 170 // Changes to any immutable attributes result in an error. 171 func (c *environConfig) update(cfg *config.Config) error { 172 // Validate the updates. newValidConfig does not modify the "known" 173 // config attributes so it is safe to call Validate here first. 174 if err := config.Validate(cfg, c.Config); err != nil { 175 return errors.Trace(err) 176 } 177 178 updates, err := newValidConfig(cfg, configDefaults) 179 if err != nil { 180 return errors.Trace(err) 181 } 182 183 // Check that no immutable fields have changed. 184 attrs := updates.UnknownAttrs() 185 for _, field := range configImmutableFields { 186 if attrs[field] != c.attrs[field] { 187 return errors.Errorf("%s: cannot change from %v to %v", field, c.attrs[field], attrs[field]) 188 } 189 } 190 191 // Apply the updates. 192 c.Config = cfg 193 c.attrs = cfg.UnknownAttrs() 194 return nil 195 }