github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "fmt" 8 "net/url" 9 "regexp" 10 "time" 11 12 "github.com/juju/collections/set" 13 "github.com/juju/errors" 14 "github.com/juju/romulus" 15 "github.com/juju/schema" 16 "github.com/juju/utils" 17 utilscert "github.com/juju/utils/cert" 18 "gopkg.in/juju/charmrepo.v3/csclient" 19 "gopkg.in/juju/names.v2" 20 "gopkg.in/macaroon-bakery.v2-unstable/bakery" 21 22 "github.com/juju/juju/cert" 23 "github.com/juju/juju/core/resources" 24 ) 25 26 const ( 27 // MongoProfLow represents the most conservative mongo memory profile. 28 MongoProfLow = "low" 29 // MongoProfDefault represents the mongo memory profile shipped by default. 30 MongoProfDefault = "default" 31 ) 32 33 const ( 34 // APIPort is the port used for api connections. 35 APIPort = "api-port" 36 37 // ControllerAPIPort is an optional port that may be set for controllers 38 // that have a very heavy load. If this port is set, this port is used by 39 // the controllers to talk to each other - used for the local API connection 40 // as well as the pubsub forwarders, and the raft workers. If this value is 41 // set, the api-port isn't opened until the controllers have started 42 // properly. 43 ControllerAPIPort = "controller-api-port" 44 45 // APIPortOpenDelay is a duration that the controller will wait 46 // between when the controller has been deemed to be ready to open 47 // the api-port and when the api-port is actually opened. This value 48 // is only used when a controller-api-port value is set. 49 APIPortOpenDelay = "api-port-open-delay" 50 51 // AuditingEnabled determines whether the controller will record 52 // auditing information. 53 AuditingEnabled = "auditing-enabled" 54 55 // AuditLogCaptureArgs determines whether the audit log will 56 // contain the arguments passed to API methods. 57 AuditLogCaptureArgs = "audit-log-capture-args" 58 59 // AuditLogMaxSize is the maximum size for the current audit log 60 // file, eg "250M". 61 AuditLogMaxSize = "audit-log-max-size" 62 63 // AuditLogMaxBackups is the number of old audit log files to keep 64 // (compressed). 65 AuditLogMaxBackups = "audit-log-max-backups" 66 67 // AuditLogExcludeMethods is a list of Facade.Method names that 68 // aren't interesting for audit logging purposes. A conversation 69 // with only calls to these will be excluded from the 70 // log. (They'll still appear in conversations that have other 71 // interesting calls though.) 72 AuditLogExcludeMethods = "audit-log-exclude-methods" 73 74 // ReadOnlyMethodsWildcard is the special value that can be added 75 // to the exclude-methods list that represents all of the read 76 // only methods (see apiserver/observer/auditfilter.go). This 77 // value will be stored in the DB (rather than being expanded at 78 // write time) so any changes to the set of read-only methods in 79 // new versions of Juju will be honoured. 80 ReadOnlyMethodsWildcard = "ReadOnlyMethods" 81 82 // StatePort is the port used for mongo connections. 83 StatePort = "state-port" 84 85 // CACertKey is the key for the controller's CA certificate attribute. 86 CACertKey = "ca-cert" 87 88 // CharmStoreURL is the key for the url to use for charmstore API calls 89 CharmStoreURL = "charmstore-url" 90 91 // ControllerUUIDKey is the key for the controller UUID attribute. 92 ControllerUUIDKey = "controller-uuid" 93 94 // IdentityURL sets the url of the identity manager. 95 IdentityURL = "identity-url" 96 97 // IdentityPublicKey sets the public key of the identity manager. 98 IdentityPublicKey = "identity-public-key" 99 100 // SetNUMAControlPolicyKey stores the value for this setting 101 SetNUMAControlPolicyKey = "set-numa-control-policy" 102 103 // AutocertDNSNameKey sets the DNS name of the controller. If a 104 // client connects to this name, an official certificate will be 105 // automatically requested. Connecting to any other host name 106 // will use the usual self-generated certificate. 107 AutocertDNSNameKey = "autocert-dns-name" 108 109 // AutocertURLKey sets the URL used to obtain official TLS 110 // certificates when a client connects to the API. By default, 111 // certficates are obtains from LetsEncrypt. A good value for 112 // testing is 113 // "https://acme-staging.api.letsencrypt.org/directory". 114 AutocertURLKey = "autocert-url" 115 116 // AllowModelAccessKey sets whether the controller will allow users to 117 // connect to models they have been authorized for even when 118 // they don't have any access rights to the controller itself. 119 AllowModelAccessKey = "allow-model-access" 120 121 // MongoMemoryProfile sets whether mongo uses the least possible memory or the 122 // detault 123 MongoMemoryProfile = "mongo-memory-profile" 124 125 // MaxLogsAge is the maximum age for log entries, eg "72h" 126 MaxLogsAge = "max-logs-age" 127 128 // MaxLogsSize is the maximum size the log collection can grow to 129 // before it is pruned, eg "4M" 130 MaxLogsSize = "max-logs-size" 131 132 // MaxTxnLogSize is the maximum size the of capped txn log collection, eg "10M" 133 MaxTxnLogSize = "max-txn-log-size" 134 135 // MaxPruneTxnBatchSize (deprecated) is the maximum number of transactions 136 // we will evaluate in one go when pruning. Default is 1M transactions. 137 // A value <= 0 indicates to do all transactions at once. 138 MaxPruneTxnBatchSize = "max-prune-txn-batch-size" 139 140 // MaxPruneTxnPasses (deprecated) is the maximum number of batches that we will process. 141 // So total number of transactions that can be processed is MaxPruneTxnBatchSize * MaxPruneTxnPasses. 142 // A value <= 0 implies 'do a single pass'. If both MaxPruneTxnBatchSize and MaxPruneTxnPasses are 0, then the 143 // default value of 1M BatchSize and 100 passes will be used instead. 144 MaxPruneTxnPasses = "max-prune-txn-passes" 145 146 // PruneTxnQueryCount is the number of transactions to read in a single query. 147 // Minimum of 10, a value of 0 will indicate to use the default value (1000) 148 PruneTxnQueryCount = "prune-txn-query-count" 149 150 // PruneTxnSleepTime is the amount of time to sleep between processing each 151 // batch query. This is used to reduce load on the system, allowing other queries 152 // to time to operate. On large controllers, processing 1000 txs seems to take 153 // about 100ms, so a sleep time of 10ms represents a 10% slowdown, but allows 154 // other systems to operate concurrently. 155 // A negative number will indicate to use the default, a value of 0 indicates 156 // to not sleep at all. 157 PruneTxnSleepTime = "prune-txn-sleep-time" 158 159 // Attribute Defaults 160 161 // DefaultAuditingEnabled contains the default value for the 162 // AuditingEnabled config value. 163 DefaultAuditingEnabled = true 164 165 // DefaultAuditLogCaptureArgs is the default for the 166 // AuditLogCaptureArgs setting (which is not to capture them). 167 DefaultAuditLogCaptureArgs = false 168 169 // DefaultAuditLogMaxSizeMB is the default size in MB at which we 170 // roll the audit log file. 171 DefaultAuditLogMaxSizeMB = 300 172 173 // DefaultAuditLogMaxBackups is the default number of files to 174 // keep. 175 DefaultAuditLogMaxBackups = 10 176 177 // DefaultNUMAControlPolicy should not be used by default. 178 // Only use numactl if user specifically requests it 179 DefaultNUMAControlPolicy = false 180 181 // DefaultStatePort is the default port the controller is listening on. 182 DefaultStatePort int = 37017 183 184 // DefaultAPIPort is the default port the API server is listening on. 185 DefaultAPIPort int = 17070 186 187 // DefaultAPIPortOpenDelay is the default value for api-port-open-delay. 188 // It is a string representation of a time.Duration. 189 DefaultAPIPortOpenDelay = "2s" 190 191 // DefaultMongoMemoryProfile is the default profile used by mongo. 192 DefaultMongoMemoryProfile = MongoProfLow 193 194 // DefaultMaxLogsAgeDays is the maximum age in days of log entries. 195 DefaultMaxLogsAgeDays = 3 196 197 // DefaultMaxLogCollectionMB is the maximum size the log collection can 198 // grow to before being pruned. 199 DefaultMaxLogCollectionMB = 4 * 1024 // 4 GB 200 201 // DefaultMaxTxnLogCollectionMB is the maximum size the txn log collection. 202 DefaultMaxTxnLogCollectionMB = 10 // 10 MB 203 204 // DefaultMaxPruneTxnBatchSize is the normal number of transaction we will prune in a given pass (1M) (deprecated) 205 DefaultMaxPruneTxnBatchSize = 1 * 1000 * 1000 206 207 // DefaultMaxPruneTxnPasses is the default number of batches we will process (deprecated) 208 DefaultMaxPruneTxnPasses = 100 209 210 // DefaultPruneTxnQueryCount is the number of transactions to read in a single query. 211 DefaultPruneTxnQueryCount = 1000 212 213 // DefaultPruneTxnSleepTime is the amount of time to sleep between processing each 214 // batch query. This is used to reduce load on the system, allowing other queries 215 // to time to operate. On large controllers, processing 1000 txs seems to take 216 // about 100ms, so a sleep time of 10ms represents a 10% slowdown, but allows 217 // other systems to operate concurrently. 218 DefaultPruneTxnSleepTime = "10ms" 219 220 // JujuHASpace is the network space within which the MongoDB replica-set 221 // should communicate. 222 JujuHASpace = "juju-ha-space" 223 224 // JujuManagementSpace is the network space that agents should use to 225 // communicate with controllers. 226 JujuManagementSpace = "juju-mgmt-space" 227 228 // CAASOperatorImagePath sets the url of the docker image 229 // used for the application operator. 230 CAASOperatorImagePath = "caas-operator-image-path" 231 232 // Features allows a list of runtime changeable features to be updated. 233 Features = "features" 234 235 // MeteringURL is the key for the url to use for metrics 236 MeteringURL = "metering-url" 237 ) 238 239 var ( 240 // ControllerOnlyConfigAttributes are attributes which are only relevant 241 // for a controller, never a model. 242 ControllerOnlyConfigAttributes = []string{ 243 AllowModelAccessKey, 244 APIPort, 245 APIPortOpenDelay, 246 AutocertDNSNameKey, 247 AutocertURLKey, 248 CACertKey, 249 CharmStoreURL, 250 ControllerAPIPort, 251 ControllerUUIDKey, 252 IdentityPublicKey, 253 IdentityURL, 254 SetNUMAControlPolicyKey, 255 StatePort, 256 MongoMemoryProfile, 257 MaxLogsSize, 258 MaxLogsAge, 259 MaxTxnLogSize, 260 MaxPruneTxnBatchSize, 261 MaxPruneTxnPasses, 262 PruneTxnQueryCount, 263 PruneTxnSleepTime, 264 JujuHASpace, 265 JujuManagementSpace, 266 AuditingEnabled, 267 AuditLogCaptureArgs, 268 AuditLogMaxSize, 269 AuditLogMaxBackups, 270 AuditLogExcludeMethods, 271 CAASOperatorImagePath, 272 Features, 273 MeteringURL, 274 } 275 276 // AllowedUpdateConfigAttributes contains all of the controller 277 // config attributes that are allowed to be updated after the 278 // controller has been created. 279 AllowedUpdateConfigAttributes = set.NewStrings( 280 APIPortOpenDelay, 281 AuditingEnabled, 282 AuditLogCaptureArgs, 283 AuditLogExcludeMethods, 284 // TODO Juju 3.0: ControllerAPIPort should be required and treated 285 // more like api-port. 286 ControllerAPIPort, 287 MaxPruneTxnBatchSize, 288 MaxPruneTxnPasses, 289 MaxLogsSize, 290 MaxLogsAge, 291 PruneTxnQueryCount, 292 PruneTxnSleepTime, 293 JujuHASpace, 294 JujuManagementSpace, 295 CAASOperatorImagePath, 296 Features, 297 ) 298 299 // DefaultAuditLogExcludeMethods is the default list of methods to 300 // exclude from the audit log. 301 DefaultAuditLogExcludeMethods = []string{ 302 // This special value means we exclude any methods in the set 303 // listed in apiserver/observer/auditfilter.go 304 ReadOnlyMethodsWildcard, 305 } 306 307 methodNameRE = regexp.MustCompile(`[[:alpha:]][[:alnum:]]*\.[[:alpha:]][[:alnum:]]*`) 308 ) 309 310 // ControllerOnlyAttribute returns true if the specified attribute name 311 // is only relevant for a controller. 312 func ControllerOnlyAttribute(attr string) bool { 313 for _, a := range ControllerOnlyConfigAttributes { 314 if attr == a { 315 return true 316 } 317 } 318 return false 319 } 320 321 // Config is a string-keyed map of controller configuration attributes. 322 type Config map[string]interface{} 323 324 // Validate validates the controller configuration. 325 func (c Config) Validate() error { 326 return Validate(c) 327 } 328 329 // NewConfig creates a new Config from the supplied attributes. 330 // Default values will be used where defaults are available. 331 // 332 // The controller UUID and CA certificate must be passed in. 333 // The UUID is typically generated by the immediate caller, 334 // and the CA certificate generated by environs/bootstrap.NewConfig. 335 func NewConfig(controllerUUID, caCert string, attrs map[string]interface{}) (Config, error) { 336 coerced, err := configChecker.Coerce(attrs, nil) 337 if err != nil { 338 return Config{}, errors.Trace(err) 339 } 340 attrs = coerced.(map[string]interface{}) 341 attrs[ControllerUUIDKey] = controllerUUID 342 attrs[CACertKey] = caCert 343 config := Config(attrs) 344 return config, config.Validate() 345 } 346 347 // mustInt returns the named attribute as an integer, panicking if 348 // it is not found or is zero. Zero values should have been 349 // diagnosed at Validate time. 350 func (c Config) mustInt(name string) int { 351 // Values obtained over the api are encoded as float64. 352 if value, ok := c[name].(float64); ok { 353 return int(value) 354 } 355 value, _ := c[name].(int) 356 if value == 0 { 357 panic(errors.Errorf("empty value for %q found in configuration", name)) 358 } 359 return value 360 } 361 362 func (c Config) intOrDefault(name string, defaultVal int) int { 363 if _, ok := c[name]; ok { 364 return c.mustInt(name) 365 } 366 return defaultVal 367 } 368 369 // asString is a private helper method to keep the ugly string casting 370 // in once place. It returns the given named attribute as a string, 371 // returning "" if it isn't found. 372 func (c Config) asString(name string) string { 373 value, _ := c[name].(string) 374 return value 375 } 376 377 // mustString returns the named attribute as an string, panicking if 378 // it is not found or is empty. 379 func (c Config) mustString(name string) string { 380 value, _ := c[name].(string) 381 if value == "" { 382 panic(errors.Errorf("empty value for %q found in configuration (type %T, val %v)", name, c[name], c[name])) 383 } 384 return value 385 } 386 387 // StatePort returns the controller port for the environment. 388 func (c Config) StatePort() int { 389 return c.mustInt(StatePort) 390 } 391 392 // APIPort returns the API server port for the environment. 393 func (c Config) APIPort() int { 394 return c.mustInt(APIPort) 395 } 396 397 // APIPortOpenDelay returns the duration to wait before opening 398 // the APIPort once the controller has started up. Only used when 399 // the ControllerAPIPort is non-zero. 400 func (c Config) APIPortOpenDelay() time.Duration { 401 v := c.asString(APIPortOpenDelay) 402 // We know that v must be a parseable time.Duration for the config 403 // to be valid. 404 d, _ := time.ParseDuration(v) 405 return d 406 } 407 408 // ControllerAPIPort returns the optional API port to be used for 409 // the controllers to talk to each other. A zero value means that 410 // it is not set. 411 func (c Config) ControllerAPIPort() int { 412 if value, ok := c[ControllerAPIPort].(float64); ok { 413 return int(value) 414 } 415 // If the value isn't an int, this conversion will fail and value 416 // will be 0, which is what we want here. 417 value, _ := c[ControllerAPIPort].(int) 418 return value 419 } 420 421 // AuditingEnabled returns whether or not auditing has been enabled 422 // for the environment. The default is false. 423 func (c Config) AuditingEnabled() bool { 424 if v, ok := c[AuditingEnabled]; ok { 425 return v.(bool) 426 } 427 return DefaultAuditingEnabled 428 } 429 430 // AuditLogCaptureArgs returns whether audit logging should capture 431 // the arguments to API methods. The default is false. 432 func (c Config) AuditLogCaptureArgs() bool { 433 if v, ok := c[AuditLogCaptureArgs]; ok { 434 return v.(bool) 435 } 436 return DefaultAuditLogCaptureArgs 437 } 438 439 // AuditLogMaxSizeMB returns the maximum size for an audit log file in 440 // MB. 441 func (c Config) AuditLogMaxSizeMB() int { 442 // Value has already been validated. 443 value, _ := utils.ParseSize(c.asString(AuditLogMaxSize)) 444 return int(value) 445 } 446 447 // AuditLogMaxBackups returns the maximum number of backup audit log 448 // files to keep. 449 func (c Config) AuditLogMaxBackups() int { 450 return c.intOrDefault(AuditLogMaxBackups, DefaultAuditLogMaxBackups) 451 } 452 453 // AuditLogExcludeMethods returns the set of method names that are 454 // considered uninteresting for audit logging. Conversations 455 // containing only these will be excluded from the audit log. 456 func (c Config) AuditLogExcludeMethods() set.Strings { 457 if value, ok := c[AuditLogExcludeMethods]; ok { 458 value := value.([]interface{}) 459 items := set.NewStrings() 460 for _, item := range value { 461 items.Add(item.(string)) 462 } 463 return items 464 } 465 return set.NewStrings(DefaultAuditLogExcludeMethods...) 466 } 467 468 // Features returns the controller config set features flags. 469 func (c Config) Features() set.Strings { 470 features := set.NewStrings() 471 if value, ok := c[Features]; ok { 472 value := value.([]interface{}) 473 for _, item := range value { 474 features.Add(item.(string)) 475 } 476 } 477 return features 478 } 479 480 // CharmStoreURL returns the URL to use for charmstore api calls. 481 func (c Config) CharmStoreURL() string { 482 url := c.asString(CharmStoreURL) 483 if url == "" { 484 return csclient.ServerURL 485 } 486 return url 487 } 488 489 // ControllerUUID returns the uuid for the model's controller. 490 func (c Config) ControllerUUID() string { 491 return c.mustString(ControllerUUIDKey) 492 } 493 494 // CACert returns the certificate of the CA that signed the controller 495 // certificate, in PEM format, and whether the setting is available. 496 // 497 // TODO(axw) once the controller config is completely constructed, 498 // there will always be a CA certificate. Get rid of the bool result. 499 func (c Config) CACert() (string, bool) { 500 if s, ok := c[CACertKey]; ok { 501 return s.(string), true 502 } 503 return "", false 504 } 505 506 // IdentityURL returns the url of the identity manager. 507 func (c Config) IdentityURL() string { 508 return c.asString(IdentityURL) 509 } 510 511 // AutocertURL returns the URL used to obtain official TLS certificates 512 // when a client connects to the API. See AutocertURLKey 513 // for more details. 514 func (c Config) AutocertURL() string { 515 return c.asString(AutocertURLKey) 516 } 517 518 // AutocertDNSName returns the DNS name of the controller. 519 // See AutocertDNSNameKey for more details. 520 func (c Config) AutocertDNSName() string { 521 return c.asString(AutocertDNSNameKey) 522 } 523 524 // IdentityPublicKey returns the public key of the identity manager. 525 func (c Config) IdentityPublicKey() *bakery.PublicKey { 526 key := c.asString(IdentityPublicKey) 527 if key == "" { 528 return nil 529 } 530 var pubKey bakery.PublicKey 531 err := pubKey.UnmarshalText([]byte(key)) 532 if err != nil { 533 // We check if the key string can be unmarshalled into a PublicKey in the 534 // Validate function, so we really do not expect this to fail. 535 panic(err) 536 } 537 return &pubKey 538 } 539 540 // MongoMemoryProfile returns the selected profile or low. 541 func (c Config) MongoMemoryProfile() string { 542 if profile, ok := c[MongoMemoryProfile]; ok { 543 return profile.(string) 544 } 545 return MongoProfLow 546 } 547 548 // NUMACtlPreference returns if numactl is preferred. 549 func (c Config) NUMACtlPreference() bool { 550 if numa, ok := c[SetNUMAControlPolicyKey]; ok { 551 return numa.(bool) 552 } 553 return DefaultNUMAControlPolicy 554 } 555 556 // AllowModelAccess reports whether users are allowed to access models 557 // they have been granted permission for even when they can't access 558 // the controller. 559 func (c Config) AllowModelAccess() bool { 560 value, _ := c[AllowModelAccessKey].(bool) 561 return value 562 } 563 564 // MaxLogsAge is the maximum age of log entries before they are pruned. 565 func (c Config) MaxLogsAge() time.Duration { 566 // Value has already been validated. 567 val, _ := time.ParseDuration(c.mustString(MaxLogsAge)) 568 return val 569 } 570 571 // MaxLogSizeMB is the maximum size in MiB which the log collection 572 // can grow to before being pruned. 573 func (c Config) MaxLogSizeMB() int { 574 // Value has already been validated. 575 val, _ := utils.ParseSize(c.mustString(MaxLogsSize)) 576 return int(val) 577 } 578 579 // MaxTxnLogSizeMB is the maximum size in MiB of the txn log collection. 580 func (c Config) MaxTxnLogSizeMB() int { 581 // Value has already been validated. 582 val, _ := utils.ParseSize(c.mustString(MaxTxnLogSize)) 583 return int(val) 584 } 585 586 // MaxPruneTxnBatchSize is the maximum size of the txn log collection. 587 func (c Config) MaxPruneTxnBatchSize() int { 588 return c.intOrDefault(MaxPruneTxnBatchSize, DefaultMaxPruneTxnBatchSize) 589 } 590 591 // MaxPruneTxnPasses is the maximum number of batches of the txn log collection we will process at a time. 592 func (c Config) MaxPruneTxnPasses() int { 593 return c.intOrDefault(MaxPruneTxnPasses, DefaultMaxPruneTxnPasses) 594 } 595 596 // PruneTxnQueryCount is the size of small batches for pruning 597 func (c Config) PruneTxnQueryCount() int { 598 return c.intOrDefault(PruneTxnQueryCount, DefaultPruneTxnQueryCount) 599 } 600 601 // PruneTxnSleepTime is the amount of time to sleep between batches. 602 func (c Config) PruneTxnSleepTime() time.Duration { 603 asInterface, ok := c[PruneTxnSleepTime] 604 if !ok { 605 asInterface = DefaultPruneTxnSleepTime 606 } 607 asStr, ok := asInterface.(string) 608 if !ok { 609 asStr = DefaultPruneTxnSleepTime 610 } 611 val, _ := time.ParseDuration(asStr) 612 return val 613 } 614 615 // JujuHASpace is the network space within which the MongoDB replica-set 616 // should communicate. 617 func (c Config) JujuHASpace() string { 618 return c.asString(JujuHASpace) 619 } 620 621 // JujuManagementSpace is the network space that agents should use to 622 // communicate with controllers. 623 func (c Config) JujuManagementSpace() string { 624 return c.asString(JujuManagementSpace) 625 } 626 627 // CAASOperatorImagePath sets the url of the docker image 628 // used for the application operator. 629 func (c Config) CAASOperatorImagePath() string { 630 return c.asString(CAASOperatorImagePath) 631 } 632 633 // MeteringURL returns the URL to use for metering api calls. 634 func (c Config) MeteringURL() string { 635 url := c.asString(MeteringURL) 636 if url == "" { 637 return romulus.DefaultAPIRoot 638 } 639 return url 640 } 641 642 // Validate ensures that config is a valid configuration. 643 func Validate(c Config) error { 644 if v, ok := c[IdentityPublicKey].(string); ok { 645 var key bakery.PublicKey 646 if err := key.UnmarshalText([]byte(v)); err != nil { 647 return errors.Annotate(err, "invalid identity public key") 648 } 649 } 650 651 if v, ok := c[IdentityURL].(string); ok { 652 u, err := url.Parse(v) 653 if err != nil { 654 return errors.Annotate(err, "invalid identity URL") 655 } 656 // If we've got an identity public key, we allow an HTTP 657 // scheme for the identity server because we won't need 658 // to rely on insecure transport to obtain the public 659 // key. 660 if _, ok := c[IdentityPublicKey]; !ok && u.Scheme != "https" { 661 return errors.Errorf("URL needs to be https when %s not provided", IdentityPublicKey) 662 } 663 } 664 665 caCert, caCertOK := c.CACert() 666 if !caCertOK { 667 return errors.Errorf("missing CA certificate") 668 } 669 if _, err := utilscert.ParseCert(caCert); err != nil { 670 return errors.Annotate(err, "bad CA certificate in configuration") 671 } 672 673 if uuid, ok := c[ControllerUUIDKey].(string); ok && !utils.IsValidUUIDString(uuid) { 674 return errors.Errorf("controller-uuid: expected UUID, got string(%q)", uuid) 675 } 676 677 if mgoMemProfile, ok := c[MongoMemoryProfile].(string); ok { 678 if mgoMemProfile != MongoProfLow && mgoMemProfile != MongoProfDefault { 679 return errors.Errorf("mongo-memory-profile: expected one of %s or %s got string(%q)", MongoProfLow, MongoProfDefault, mgoMemProfile) 680 } 681 } 682 683 if v, ok := c[MaxLogsAge].(string); ok { 684 if _, err := time.ParseDuration(v); err != nil { 685 return errors.Annotate(err, "invalid logs prune interval in configuration") 686 } 687 } 688 689 if v, ok := c[MaxLogsSize].(string); ok { 690 if _, err := utils.ParseSize(v); err != nil { 691 return errors.Annotate(err, "invalid max logs size in configuration") 692 } 693 } 694 695 if v, ok := c[MaxTxnLogSize].(string); ok { 696 if _, err := utils.ParseSize(v); err != nil { 697 return errors.Annotate(err, "invalid max txn log size in configuration") 698 } 699 } 700 701 if v, ok := c[PruneTxnSleepTime].(string); ok { 702 if _, err := time.ParseDuration(v); err != nil { 703 return errors.Annotatef(err, `%s must be a valid duration (eg "10ms")`, PruneTxnSleepTime) 704 } 705 } 706 707 if err := c.validateSpaceConfig(JujuHASpace, "juju HA"); err != nil { 708 return errors.Trace(err) 709 } 710 711 if err := c.validateSpaceConfig(JujuManagementSpace, "juju mgmt"); err != nil { 712 return errors.Trace(err) 713 } 714 715 if v, ok := c[CAASOperatorImagePath].(string); ok { 716 if err := resources.ValidateDockerRegistryPath(v); err != nil { 717 return errors.Trace(err) 718 } 719 } 720 721 var auditLogMaxSize int 722 if v, ok := c[AuditLogMaxSize].(string); ok { 723 if size, err := utils.ParseSize(v); err != nil { 724 return errors.Annotate(err, "invalid audit log max size in configuration") 725 } else { 726 auditLogMaxSize = int(size) 727 } 728 } 729 730 if v, ok := c[AuditingEnabled].(bool); ok { 731 if v && auditLogMaxSize == 0 { 732 return errors.Errorf("invalid audit log max size: can't be 0 if auditing is enabled") 733 } 734 } 735 736 if v, ok := c[AuditLogMaxBackups].(int); ok { 737 if v < 0 { 738 return errors.Errorf("invalid audit log max backups: should be a number of files (or 0 to keep all), got %d", v) 739 } 740 } 741 742 if v, ok := c[AuditLogExcludeMethods].([]interface{}); ok { 743 for i, name := range v { 744 name := name.(string) 745 if name != ReadOnlyMethodsWildcard && !methodNameRE.MatchString(name) { 746 return errors.Errorf( 747 `invalid audit log exclude methods: should be a list of "Facade.Method" names (or "ReadOnlyMethods"), got %q at position %d`, 748 name, 749 i+1, 750 ) 751 } 752 } 753 } 754 755 if v, ok := c[ControllerAPIPort].(int); ok { 756 // TODO: change the validation so 0 is invalide and --reset is used. 757 // However that doesn't exist yet. 758 if v < 0 { 759 return errors.NotValidf("non-positive integer for controller-api-port") 760 } 761 if v == c.APIPort() { 762 return errors.NotValidf("controller-api-port matching api-port") 763 } 764 if v == c.StatePort() { 765 return errors.NotValidf("controller-api-port matching state-port") 766 } 767 } 768 if v, ok := c[APIPortOpenDelay].(string); ok { 769 _, err := time.ParseDuration(v) 770 if err != nil { 771 return errors.Errorf("%s value %q must be a valid duration", APIPortOpenDelay, v) 772 } 773 } 774 775 return nil 776 } 777 778 func (c Config) validateSpaceConfig(key, topic string) error { 779 val := c[key] 780 if val == nil { 781 return nil 782 } 783 if v, ok := val.(string); ok { 784 if !names.IsValidSpace(v) { 785 return errors.NotValidf("%s space name %q", topic, val) 786 } 787 } else { 788 return errors.NotValidf("type for %s space name %v", topic, val) 789 } 790 791 return nil 792 } 793 794 // AsSpaceConstraints checks to see whether config has spaces names populated 795 // for management and/or HA (Mongo). 796 // Non-empty values are merged with any input spaces and returned as a new 797 // slice reference. 798 // A slice pointer is used for congruence with the Spaces member in 799 // constraints.Value. 800 func (c Config) AsSpaceConstraints(spaces *[]string) *[]string { 801 newSpaces := set.NewStrings() 802 if spaces != nil { 803 for _, s := range *spaces { 804 newSpaces.Add(s) 805 } 806 } 807 808 for _, c := range []string{c.JujuManagementSpace(), c.JujuHASpace()} { 809 if c != "" { 810 newSpaces.Add(c) 811 } 812 } 813 814 // Preserve a nil pointer if there is no change. This conveys information 815 // in constraints.Value (not set vs. deliberately set as empty). 816 if spaces == nil && len(newSpaces) == 0 { 817 return nil 818 } 819 ns := newSpaces.SortedValues() 820 return &ns 821 } 822 823 // GenerateControllerCertAndKey makes sure that the config has a CACert and 824 // CAPrivateKey, generates and returns new certificate and key. 825 func GenerateControllerCertAndKey(caCert, caKey string, hostAddresses []string) (string, string, error) { 826 return cert.NewDefaultServer(caCert, caKey, hostAddresses) 827 } 828 829 var configChecker = schema.FieldMap(schema.Fields{ 830 AuditingEnabled: schema.Bool(), 831 AuditLogCaptureArgs: schema.Bool(), 832 AuditLogMaxSize: schema.String(), 833 AuditLogMaxBackups: schema.ForceInt(), 834 AuditLogExcludeMethods: schema.List(schema.String()), 835 APIPort: schema.ForceInt(), 836 APIPortOpenDelay: schema.String(), 837 ControllerAPIPort: schema.ForceInt(), 838 StatePort: schema.ForceInt(), 839 IdentityURL: schema.String(), 840 IdentityPublicKey: schema.String(), 841 SetNUMAControlPolicyKey: schema.Bool(), 842 AutocertURLKey: schema.String(), 843 AutocertDNSNameKey: schema.String(), 844 AllowModelAccessKey: schema.Bool(), 845 MongoMemoryProfile: schema.String(), 846 MaxLogsAge: schema.String(), 847 MaxLogsSize: schema.String(), 848 MaxTxnLogSize: schema.String(), 849 MaxPruneTxnBatchSize: schema.ForceInt(), 850 MaxPruneTxnPasses: schema.ForceInt(), 851 PruneTxnQueryCount: schema.ForceInt(), 852 PruneTxnSleepTime: schema.String(), 853 JujuHASpace: schema.String(), 854 JujuManagementSpace: schema.String(), 855 CAASOperatorImagePath: schema.String(), 856 Features: schema.List(schema.String()), 857 CharmStoreURL: schema.String(), 858 MeteringURL: schema.String(), 859 }, schema.Defaults{ 860 APIPort: DefaultAPIPort, 861 APIPortOpenDelay: DefaultAPIPortOpenDelay, 862 ControllerAPIPort: schema.Omit, 863 AuditingEnabled: DefaultAuditingEnabled, 864 AuditLogCaptureArgs: DefaultAuditLogCaptureArgs, 865 AuditLogMaxSize: fmt.Sprintf("%vM", DefaultAuditLogMaxSizeMB), 866 AuditLogMaxBackups: DefaultAuditLogMaxBackups, 867 AuditLogExcludeMethods: DefaultAuditLogExcludeMethods, 868 StatePort: DefaultStatePort, 869 IdentityURL: schema.Omit, 870 IdentityPublicKey: schema.Omit, 871 SetNUMAControlPolicyKey: DefaultNUMAControlPolicy, 872 AutocertURLKey: schema.Omit, 873 AutocertDNSNameKey: schema.Omit, 874 AllowModelAccessKey: schema.Omit, 875 MongoMemoryProfile: schema.Omit, 876 MaxLogsAge: fmt.Sprintf("%vh", DefaultMaxLogsAgeDays*24), 877 MaxLogsSize: fmt.Sprintf("%vM", DefaultMaxLogCollectionMB), 878 MaxTxnLogSize: fmt.Sprintf("%vM", DefaultMaxTxnLogCollectionMB), 879 MaxPruneTxnBatchSize: DefaultMaxPruneTxnBatchSize, 880 MaxPruneTxnPasses: DefaultMaxPruneTxnPasses, 881 PruneTxnQueryCount: DefaultPruneTxnQueryCount, 882 PruneTxnSleepTime: DefaultPruneTxnSleepTime, 883 JujuHASpace: schema.Omit, 884 JujuManagementSpace: schema.Omit, 885 CAASOperatorImagePath: schema.Omit, 886 Features: schema.Omit, 887 CharmStoreURL: csclient.ServerURL, 888 MeteringURL: romulus.DefaultAPIRoot, 889 })