github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/controller/config.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller 5 6 import ( 7 "net/url" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/schema" 12 "github.com/juju/utils" 13 "gopkg.in/macaroon-bakery.v1/bakery" 14 15 "github.com/juju/juju/cert" 16 ) 17 18 var logger = loggo.GetLogger("juju.controller") 19 20 const ( 21 // APIPort is the port used for api connections. 22 APIPort = "api-port" 23 24 // AuditingEnabled determines whether the controller will record 25 // auditing information. 26 AuditingEnabled = "auditing-enabled" 27 28 // StatePort is the port used for mongo connections. 29 StatePort = "state-port" 30 31 // CACertKey is the key for the controller's CA certificate attribute. 32 CACertKey = "ca-cert" 33 34 // ControllerUUIDKey is the key for the controller UUID attribute. 35 ControllerUUIDKey = "controller-uuid" 36 37 // IdentityURL sets the url of the identity manager. 38 IdentityURL = "identity-url" 39 40 // IdentityPublicKey sets the public key of the identity manager. 41 IdentityPublicKey = "identity-public-key" 42 43 // NUMAControlPolicyKey stores the value for this setting 44 SetNUMAControlPolicyKey = "set-numa-control-policy" 45 46 // AutocertDNSNameKey sets the DNS name of the controller. If a 47 // client connects to this name, an official certificate will be 48 // automatically requested. Connecting to any other host name 49 // will use the usual self-generated certificate. 50 AutocertDNSNameKey = "autocert-dns-name" 51 52 // AutocertURLKey sets the URL used to obtain official TLS 53 // certificates when a client connects to the API. By default, 54 // certficates are obtains from LetsEncrypt. A good value for 55 // testing is 56 // "https://acme-staging.api.letsencrypt.org/directory". 57 AutocertURLKey = "autocert-url" 58 59 // Attribute Defaults 60 61 // DefaultAuditingEnabled contains the default value for the 62 // AuditingEnabled config value. 63 DefaultAuditingEnabled = false 64 65 // DefaultNUMAControlPolicy should not be used by default. 66 // Only use numactl if user specifically requests it 67 DefaultNUMAControlPolicy = false 68 69 // DefaultStatePort is the default port the controller is listening on. 70 DefaultStatePort int = 37017 71 72 // DefaultAPIPort is the default port the API server is listening on. 73 DefaultAPIPort int = 17070 74 ) 75 76 // ControllerOnlyConfigAttributes are attributes which are only relevant 77 // for a controller, never a model. 78 var ControllerOnlyConfigAttributes = []string{ 79 APIPort, 80 StatePort, 81 CACertKey, 82 ControllerUUIDKey, 83 IdentityURL, 84 IdentityPublicKey, 85 SetNUMAControlPolicyKey, 86 AutocertDNSNameKey, 87 AutocertURLKey, 88 } 89 90 // ControllerOnlyAttribute returns true if the specified attribute name 91 // is only relevant for a controller. 92 func ControllerOnlyAttribute(attr string) bool { 93 for _, a := range ControllerOnlyConfigAttributes { 94 if attr == a { 95 return true 96 } 97 } 98 return false 99 } 100 101 type Config map[string]interface{} 102 103 // Validate validates the controller configuration. 104 func (c Config) Validate() error { 105 return Validate(c) 106 } 107 108 // NewConfig creates a new Config from the supplied attributes. 109 // Default values will be used where defaults are available. 110 // 111 // The controller UUID and CA certificate must be passed in. 112 // The UUID is typically generated by the immediate caller, 113 // and the CA certificate generated by environs/bootstrap.NewConfig. 114 func NewConfig(controllerUUID, caCert string, attrs map[string]interface{}) (Config, error) { 115 coerced, err := configChecker.Coerce(attrs, nil) 116 if err != nil { 117 return Config{}, errors.Trace(err) 118 } 119 attrs = coerced.(map[string]interface{}) 120 attrs[ControllerUUIDKey] = controllerUUID 121 attrs[CACertKey] = caCert 122 config := Config(attrs) 123 return config, config.Validate() 124 } 125 126 // mustInt returns the named attribute as an integer, panicking if 127 // it is not found or is zero. Zero values should have been 128 // diagnosed at Validate time. 129 func (c Config) mustInt(name string) int { 130 // Values obtained over the api are encoded as float64. 131 if value, ok := c[name].(float64); ok { 132 return int(value) 133 } 134 value, _ := c[name].(int) 135 if value == 0 { 136 panic(errors.Errorf("empty value for %q found in configuration", name)) 137 } 138 return value 139 } 140 141 // asString is a private helper method to keep the ugly string casting 142 // in once place. It returns the given named attribute as a string, 143 // returning "" if it isn't found. 144 func (c Config) asString(name string) string { 145 value, _ := c[name].(string) 146 return value 147 } 148 149 // mustString returns the named attribute as an string, panicking if 150 // it is not found or is empty. 151 func (c Config) mustString(name string) string { 152 value, _ := c[name].(string) 153 if value == "" { 154 panic(errors.Errorf("empty value for %q found in configuration (type %T, val %v)", name, c[name], c[name])) 155 } 156 return value 157 } 158 159 // StatePort returns the controller port for the environment. 160 func (c Config) StatePort() int { 161 return c.mustInt(StatePort) 162 } 163 164 // APIPort returns the API server port for the environment. 165 func (c Config) APIPort() int { 166 return c.mustInt(APIPort) 167 } 168 169 // AuditingEnabled returns whether or not auditing has been enabled 170 // for the environment. The default is false. 171 func (c Config) AuditingEnabled() bool { 172 if v, ok := c[AuditingEnabled]; ok { 173 return v.(bool) 174 } 175 return false 176 } 177 178 // ControllerUUID returns the uuid for the model's controller. 179 func (c Config) ControllerUUID() string { 180 return c.mustString(ControllerUUIDKey) 181 } 182 183 // CACert returns the certificate of the CA that signed the controller 184 // certificate, in PEM format, and whether the setting is available. 185 func (c Config) CACert() (string, bool) { 186 if s, ok := c[CACertKey]; ok { 187 return s.(string), true 188 } 189 return "", false 190 } 191 192 // IdentityURL returns the url of the identity manager. 193 func (c Config) IdentityURL() string { 194 return c.asString(IdentityURL) 195 } 196 197 // AutocertURL returns the URL used to obtain official TLS certificates 198 // when a client connects to the API. See AutocertURLKey 199 // for more details. 200 func (c Config) AutocertURL() string { 201 return c.asString(AutocertURLKey) 202 } 203 204 // AutocertDNSName returns the DNS name of the controller. 205 // See AutocertDNSNameKey for more details. 206 func (c Config) AutocertDNSName() string { 207 return c.asString(AutocertDNSNameKey) 208 } 209 210 // IdentityPublicKey returns the public key of the identity manager. 211 func (c Config) IdentityPublicKey() *bakery.PublicKey { 212 key := c.asString(IdentityPublicKey) 213 if key == "" { 214 return nil 215 } 216 var pubKey bakery.PublicKey 217 err := pubKey.UnmarshalText([]byte(key)) 218 if err != nil { 219 // We check if the key string can be unmarshalled into a PublicKey in the 220 // Validate function, so we really do not expect this to fail. 221 panic(err) 222 } 223 return &pubKey 224 } 225 226 // NUMACtlPreference returns if numactl is preferred. 227 func (c Config) NUMACtlPreference() bool { 228 if numa, ok := c[SetNUMAControlPolicyKey]; ok { 229 return numa.(bool) 230 } 231 return DefaultNUMAControlPolicy 232 } 233 234 // Validate ensures that config is a valid configuration. 235 func Validate(c Config) error { 236 if v, ok := c[IdentityURL].(string); ok { 237 u, err := url.Parse(v) 238 if err != nil { 239 return errors.Annotate(err, "invalid identity URL") 240 } 241 if u.Scheme != "https" { 242 return errors.Errorf("URL needs to be https") 243 } 244 245 } 246 247 if v, ok := c[IdentityPublicKey].(string); ok { 248 var key bakery.PublicKey 249 if err := key.UnmarshalText([]byte(v)); err != nil { 250 return errors.Annotate(err, "invalid identity public key") 251 } 252 } 253 254 caCert, caCertOK := c.CACert() 255 if !caCertOK { 256 return errors.Errorf("missing CA certificate") 257 } 258 if _, err := cert.ParseCert(caCert); err != nil { 259 return errors.Annotate(err, "bad CA certificate in configuration") 260 } 261 262 if uuid, ok := c[ControllerUUIDKey].(string); ok && !utils.IsValidUUIDString(uuid) { 263 return errors.Errorf("controller-uuid: expected UUID, got string(%q)", uuid) 264 } 265 266 return nil 267 } 268 269 // GenerateControllerCertAndKey makes sure that the config has a CACert and 270 // CAPrivateKey, generates and returns new certificate and key. 271 func GenerateControllerCertAndKey(caCert, caKey string, hostAddresses []string) (string, string, error) { 272 return cert.NewDefaultServer(caCert, caKey, hostAddresses) 273 } 274 275 var configChecker = schema.FieldMap(schema.Fields{ 276 AuditingEnabled: schema.Bool(), 277 APIPort: schema.ForceInt(), 278 StatePort: schema.ForceInt(), 279 IdentityURL: schema.String(), 280 IdentityPublicKey: schema.String(), 281 SetNUMAControlPolicyKey: schema.Bool(), 282 AutocertURLKey: schema.String(), 283 AutocertDNSNameKey: schema.String(), 284 }, schema.Defaults{ 285 APIPort: DefaultAPIPort, 286 AuditingEnabled: DefaultAuditingEnabled, 287 StatePort: DefaultStatePort, 288 IdentityURL: schema.Omit, 289 IdentityPublicKey: schema.Omit, 290 SetNUMAControlPolicyKey: DefaultNUMAControlPolicy, 291 AutocertURLKey: schema.Omit, 292 AutocertDNSNameKey: schema.Omit, 293 })