github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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/go-macaroon-bakery/macaroon-bakery/v3/bakery" 13 "github.com/juju/collections/set" 14 "github.com/juju/errors" 15 "github.com/juju/names/v5" 16 "github.com/juju/romulus" 17 "github.com/juju/utils/v3" 18 "gopkg.in/juju/environschema.v1" 19 "gopkg.in/yaml.v2" 20 21 "github.com/juju/juju/pki" 22 ) 23 24 const ( 25 // MongoProfLow represents the most conservative mongo memory profile. 26 MongoProfLow = "low" 27 // MongoProfDefault represents the mongo memory profile shipped by default. 28 MongoProfDefault = "default" 29 ) 30 31 // docs:controller-config-keys 32 const ( 33 // APIPort is the port used for api connections. 34 APIPort = "api-port" 35 36 // ControllerAPIPort is an optional port that may be set for controllers 37 // that have a very heavy load. If this port is set, this port is used by 38 // the controllers to talk to each other - used for the local API connection 39 // as well as the pubsub forwarders, and the raft workers. If this value is 40 // set, the api-port isn't opened until the controllers have started 41 // properly. 42 ControllerAPIPort = "controller-api-port" 43 44 // ControllerName is the canonical name for the controller. 45 ControllerName = "controller-name" 46 47 // ApplicationResourceDownloadLimit limits the number of concurrent resource download 48 // requests from unit agents which will be served. The limit is per application. 49 // Use a value of 0 to disable the limit. 50 ApplicationResourceDownloadLimit = "application-resource-download-limit" 51 52 // ControllerResourceDownloadLimit limits the number of concurrent resource download 53 // requests from unit agents which will be served. The limit is for the combined total 54 // of all applications on the controller. 55 // Use a value of 0 to disable the limit. 56 ControllerResourceDownloadLimit = "controller-resource-download-limit" 57 58 // AgentRateLimitMax is the maximum size of the token bucket used to 59 // ratelimit the agent connections. 60 AgentRateLimitMax = "agent-ratelimit-max" 61 62 // AgentRateLimitRate is the time taken to add a new token to the bucket. 63 // This effectively says that we can have a new agent connect per duration specified. 64 AgentRateLimitRate = "agent-ratelimit-rate" 65 66 // APIPortOpenDelay is a duration that the controller will wait 67 // between when the controller has been deemed to be ready to open 68 // the api-port and when the api-port is actually opened. This value 69 // is only used when a controller-api-port value is set. 70 APIPortOpenDelay = "api-port-open-delay" 71 72 // AuditingEnabled determines whether the controller will record 73 // auditing information. 74 AuditingEnabled = "auditing-enabled" 75 76 // AuditLogCaptureArgs determines whether the audit log will 77 // contain the arguments passed to API methods. 78 AuditLogCaptureArgs = "audit-log-capture-args" 79 80 // AuditLogMaxSize is the maximum size for the current audit log 81 // file, eg "250M". 82 AuditLogMaxSize = "audit-log-max-size" 83 84 // AuditLogMaxBackups is the number of old audit log files to keep 85 // (compressed). 86 AuditLogMaxBackups = "audit-log-max-backups" 87 88 // AuditLogExcludeMethods is a list of Facade.Method names that 89 // aren't interesting for audit logging purposes. A conversation 90 // with only calls to these will be excluded from the 91 // log. (They'll still appear in conversations that have other 92 // interesting calls though.) 93 AuditLogExcludeMethods = "audit-log-exclude-methods" 94 95 // ReadOnlyMethodsWildcard is the special value that can be added 96 // to the exclude-methods list that represents all of the read 97 // only methods (see apiserver/observer/auditfilter.go). This 98 // value will be stored in the DB (rather than being expanded at 99 // write time) so any changes to the set of read-only methods in 100 // new versions of Juju will be honoured. 101 ReadOnlyMethodsWildcard = "ReadOnlyMethods" 102 103 // StatePort is the port used for mongo connections. 104 StatePort = "state-port" 105 106 // CACertKey is the key for the controller's CA certificate attribute. 107 CACertKey = "ca-cert" 108 109 // ControllerUUIDKey is the key for the controller UUID attribute. 110 ControllerUUIDKey = "controller-uuid" 111 112 // LoginTokenRefreshURL sets the URL of the login JWT well-known endpoint. 113 // Use this when authentication/authorisation is done using a JWT in the 114 // login request rather than a username/password or macaroon and a local 115 // permissions model. 116 LoginTokenRefreshURL = "login-token-refresh-url" 117 118 // IdentityURL sets the URL of the identity manager. 119 // Use this when users should be managed externally rather than 120 // created locally on the controller. 121 IdentityURL = "identity-url" 122 123 // IdentityPublicKey sets the public key of the identity manager. 124 // Use this when users should be managed externally rather than 125 // created locally on the controller. 126 IdentityPublicKey = "identity-public-key" 127 128 // SetNUMAControlPolicyKey (true/false) is deprecated. 129 // Use to configure whether mongo is started with NUMA 130 // controller policy turned on. 131 SetNUMAControlPolicyKey = "set-numa-control-policy" 132 133 // AutocertDNSNameKey sets the DNS name of the controller. If a 134 // client connects to this name, an official certificate will be 135 // automatically requested. Connecting to any other host name 136 // will use the usual self-generated certificate. 137 AutocertDNSNameKey = "autocert-dns-name" 138 139 // AutocertURLKey sets the URL used to obtain official TLS 140 // certificates when a client connects to the API. By default, 141 // certficates are obtains from LetsEncrypt. A good value for 142 // testing is 143 // "https://acme-staging.api.letsencrypt.org/directory". 144 AutocertURLKey = "autocert-url" 145 146 // AllowModelAccessKey sets whether the controller will allow users to 147 // connect to models they have been authorized for, even when 148 // they don't have any access rights to the controller itself. 149 AllowModelAccessKey = "allow-model-access" 150 151 // MongoMemoryProfile sets the memory profile for MongoDB. Valid values are: 152 // - "low": use the least possible memory 153 // - "default": use the default memory profile 154 MongoMemoryProfile = "mongo-memory-profile" 155 156 // JujuDBSnapChannel selects the channel to use when installing Mongo 157 // snaps for focal or later. The value is ignored for older releases. 158 JujuDBSnapChannel = "juju-db-snap-channel" 159 160 // MaxDebugLogDuration is used to provide a backstop to the execution of a 161 // debug-log command. If someone starts a debug-log session in a remote 162 // screen for example, it is very easy to disconnect from the screen while 163 // leaving the debug-log process running. This causes unnecessary load on 164 // the API server. The max debug-log duration has a default of 24 hours, 165 // which should be more than enough time for a debugging session. 166 MaxDebugLogDuration = "max-debug-log-duration" 167 168 // AgentLogfileMaxSize is the maximum file size in MB of each 169 // agent/controller log file. 170 AgentLogfileMaxSize = "agent-logfile-max-size" 171 172 // AgentLogfileMaxBackups is the number of old agent/controller log files 173 // to keep (compressed). 174 AgentLogfileMaxBackups = "agent-logfile-max-backups" 175 176 // ModelLogfileMaxSize is the maximum size of the log file written out by the 177 // controller on behalf of workers running for a model. 178 ModelLogfileMaxSize = "model-logfile-max-size" 179 180 // ModelLogfileMaxBackups is the number of old model 181 // log files to keep (compressed). 182 ModelLogfileMaxBackups = "model-logfile-max-backups" 183 184 // ModelLogsSize is the size of the capped collections used to hold the 185 // logs for the models, eg "20M". Size is per model. 186 ModelLogsSize = "model-logs-size" 187 188 // MaxTxnLogSize is the maximum size the of capped txn log collection, eg "10M" 189 MaxTxnLogSize = "max-txn-log-size" 190 191 // MaxPruneTxnBatchSize (deprecated) is the maximum number of transactions 192 // we will evaluate in one go when pruning. Default is 1M transactions. 193 // A value <= 0 indicates to do all transactions at once. 194 MaxPruneTxnBatchSize = "max-prune-txn-batch-size" 195 196 // MaxPruneTxnPasses (deprecated) is the maximum number of batches that 197 // we will process. So total number of transactions that can be processed 198 // is MaxPruneTxnBatchSize * MaxPruneTxnPasses. A value <= 0 implies 199 // 'do a single pass'. If both MaxPruneTxnBatchSize and MaxPruneTxnPasses 200 // are 0, then the default value of 1M BatchSize and 100 passes 201 // will be used instead. 202 MaxPruneTxnPasses = "max-prune-txn-passes" 203 204 // PruneTxnQueryCount is the number of transactions to read in a single query. 205 // Minimum of 10, a value of 0 will indicate to use the default value (1000) 206 PruneTxnQueryCount = "prune-txn-query-count" 207 208 // PruneTxnSleepTime is the amount of time to sleep between processing each 209 // batch query. This is used to reduce load on the system, allowing other 210 // queries to time to operate. On large controllers, processing 1000 txs 211 // seems to take about 100ms, so a sleep time of 10ms represents a 10% 212 // slowdown, but allows other systems to operate concurrently. 213 // A negative number will indicate to use the default, a value of 0 214 // indicates to not sleep at all. 215 PruneTxnSleepTime = "prune-txn-sleep-time" 216 217 // MaxCharmStateSize is the maximum allowed size of charm-specific 218 // per-unit state data that charms can store to the controller in 219 // bytes. A value of 0 disables the quota checks although in 220 // principle, mongo imposes a hard (but configurable) limit of 16M. 221 MaxCharmStateSize = "max-charm-state-size" 222 223 // MaxAgentStateSize is the maximum allowed size of internal state 224 // data that agents can store to the controller in bytes. A value of 0 225 // disables the quota checks although in principle, mongo imposes a 226 // hard (but configurable) limit of 16M. 227 MaxAgentStateSize = "max-agent-state-size" 228 229 // MigrationMinionWaitMax is the maximum time that the migration-master 230 // worker will wait for agents to report for a migration phase when 231 // executing a model migration. 232 MigrationMinionWaitMax = "migration-agent-wait-time" 233 234 // JujuHASpace is the network space within which the MongoDB replica-set 235 // should communicate. 236 JujuHASpace = "juju-ha-space" 237 238 // JujuManagementSpace is the network space that agents should use to 239 // communicate with controllers. 240 JujuManagementSpace = "juju-mgmt-space" 241 242 // CAASOperatorImagePath sets the URL of the docker image 243 // used for the application operator. 244 // Deprecated: use CAASImageRepo 245 CAASOperatorImagePath = "caas-operator-image-path" 246 247 // CAASImageRepo sets the docker repo to use 248 // for the jujud operator and mongo images. 249 CAASImageRepo = "caas-image-repo" 250 251 // Features allows a list of runtime changeable features to be updated. 252 Features = "features" 253 254 // MeteringURL is the URL to use for metrics. 255 MeteringURL = "metering-url" 256 257 // PublicDNSAddress is the public DNS address (and port) of the controller. 258 PublicDNSAddress = "public-dns-address" 259 260 // QueryTracingEnabled returns whether query tracing is enabled. If so, any 261 // queries which take longer than QueryTracingThreshold will be logged. 262 QueryTracingEnabled = "query-tracing-enabled" 263 264 // QueryTracingThreshold returns the "threshold" for query tracing. Any 265 // queries which take longer than this value will be logged (if query tracing 266 // is enabled). The lower the threshold, the more queries will be output. A 267 // value of 0 means all queries will be output. 268 QueryTracingThreshold = "query-tracing-threshold" 269 270 // JujudControllerSnapSource returns the source for the controller snap. 271 // Can be set to "legacy", "snapstore", "local" or "local-dangerous". 272 // Cannot be changed. 273 JujudControllerSnapSource = "jujud-controller-snap-source" 274 ) 275 276 // Attribute Defaults 277 const ( 278 // DefaultApplicationResourceDownloadLimit allows unlimited 279 // resource download requests initiated by a unit agent per application. 280 DefaultApplicationResourceDownloadLimit = 0 281 282 // DefaultControllerResourceDownloadLimit allows unlimited concurrent resource 283 // download requests initiated by unit agents for any application on the controller. 284 DefaultControllerResourceDownloadLimit = 0 285 286 // DefaultAgentRateLimitMax allows the first 10 agents to connect without 287 // any issue. After that the rate limiting kicks in. 288 DefaultAgentRateLimitMax = 10 289 290 // DefaultAgentRateLimitRate will allow four agents to connect every 291 // second. A token is added to the ratelimit token bucket every 250ms. 292 DefaultAgentRateLimitRate = 250 * time.Millisecond 293 294 // DefaultAuditingEnabled contains the default value for the 295 // AuditingEnabled config value. 296 DefaultAuditingEnabled = true 297 298 // DefaultAuditLogCaptureArgs is the default for the 299 // AuditLogCaptureArgs setting (which is not to capture them). 300 DefaultAuditLogCaptureArgs = false 301 302 // DefaultAuditLogMaxSizeMB is the default size in MB at which we 303 // roll the audit log file. 304 DefaultAuditLogMaxSizeMB = 300 305 306 // DefaultAuditLogMaxBackups is the default number of files to 307 // keep. 308 DefaultAuditLogMaxBackups = 10 309 310 // DefaultNUMAControlPolicy should not be used by default. 311 // Only use numactl if user specifically requests it 312 DefaultNUMAControlPolicy = false 313 314 // DefaultStatePort is the default port the controller is listening on. 315 DefaultStatePort int = 37017 316 317 // DefaultAPIPort is the default port the API server is listening on. 318 DefaultAPIPort int = 17070 319 320 // DefaultAPIPortOpenDelay is the default value for api-port-open-delay. 321 DefaultAPIPortOpenDelay = 2 * time.Second 322 323 // DefaultMongoMemoryProfile is the default profile used by mongo. 324 DefaultMongoMemoryProfile = MongoProfDefault 325 326 // DefaultJujuDBSnapChannel is the default snap channel for installing 327 // mongo in focal or later. 328 DefaultJujuDBSnapChannel = "4.4/stable" 329 330 // DefaultMaxDebugLogDuration is the default duration that debug-log 331 // commands can run before being terminated by the API server. 332 DefaultMaxDebugLogDuration = 24 * time.Hour 333 334 // DefaultMaxTxnLogCollectionMB is the maximum size the txn log collection. 335 DefaultMaxTxnLogCollectionMB = 10 // 10 MB 336 337 // DefaultMaxPruneTxnBatchSize is the normal number of transaction 338 // we will prune in a given pass (1M) (deprecated) 339 DefaultMaxPruneTxnBatchSize = 1 * 1000 * 1000 340 341 // DefaultMaxPruneTxnPasses is the default number of 342 // batches we will process. (deprecated) 343 DefaultMaxPruneTxnPasses = 100 344 345 // DefaultAgentLogfileMaxSize is the maximum file size in MB of each 346 // agent/controller log file. 347 DefaultAgentLogfileMaxSize = 100 348 349 // DefaultAgentLogfileMaxBackups is the number of old agent/controller log 350 // files to keep (compressed). 351 DefaultAgentLogfileMaxBackups = 2 352 353 // DefaultModelLogfileMaxSize is the maximum file size in MB of 354 // the log file written out by the controller on behalf of workers 355 // running for a model. 356 DefaultModelLogfileMaxSize = 10 357 358 // DefaultModelLogfileMaxBackups is the number of old model 359 // log files to keep (compressed). 360 DefaultModelLogfileMaxBackups = 2 361 362 // DefaultModelLogsSizeMB is the size in MB of the capped logs collection 363 // for each model. 364 DefaultModelLogsSizeMB = 20 365 366 // DefaultPruneTxnQueryCount is the number of transactions 367 // to read in a single query. 368 DefaultPruneTxnQueryCount = 1000 369 370 // DefaultPruneTxnSleepTime is the amount of time to sleep between 371 // processing each batch query. This is used to reduce load on the system, 372 // allowing other queries to time to operate. On large controllers, 373 // processing 1000 txs seems to take about 100ms, so a sleep time of 10ms 374 // represents a 10% slowdown, but allows other systems to 375 // operate concurrently. 376 DefaultPruneTxnSleepTime = 10 * time.Millisecond 377 378 // DefaultMaxCharmStateSize is the maximum size (in bytes) of charm 379 // state data that each unit can store to the controller. 380 DefaultMaxCharmStateSize = 2 * 1024 * 1024 381 382 // DefaultMaxAgentStateSize is the maximum size (in bytes) of internal 383 // state data that agents can store to the controller. 384 DefaultMaxAgentStateSize = 512 * 1024 385 386 // DefaultMigrationMinionWaitMax is the default value for how long a 387 // migration minion will wait for the migration to complete. 388 DefaultMigrationMinionWaitMax = 15 * time.Minute 389 390 // DefaultQueryTracingEnabled is the default value for if query tracing 391 // is enabled. 392 DefaultQueryTracingEnabled = false 393 394 // DefaultQueryTracingThreshold is the default value for the threshold 395 // for query tracing. If a query takes longer than this to complete 396 // it will be logged if query tracing is enabled. 397 DefaultQueryTracingThreshold = time.Second 398 399 // JujudControllerSnapSource is the default value for the jujud controller 400 // snap source, which is the snapstore. 401 // TODO(jujud-controller-snap): change this to "snapstore" once it is implemented. 402 DefaultJujudControllerSnapSource = "legacy" 403 ) 404 405 var ( 406 // ControllerOnlyConfigAttributes lists all the controller config keys, so we 407 // can distinguish these from model config keys when bootstrapping. 408 ControllerOnlyConfigAttributes = []string{ 409 AllowModelAccessKey, 410 AgentRateLimitMax, 411 AgentRateLimitRate, 412 APIPort, 413 APIPortOpenDelay, 414 AutocertDNSNameKey, 415 AutocertURLKey, 416 CACertKey, 417 ControllerAPIPort, 418 ControllerName, 419 ControllerUUIDKey, 420 LoginTokenRefreshURL, 421 IdentityPublicKey, 422 IdentityURL, 423 SetNUMAControlPolicyKey, 424 StatePort, 425 MongoMemoryProfile, 426 JujuDBSnapChannel, 427 MaxDebugLogDuration, 428 MaxTxnLogSize, 429 MaxPruneTxnBatchSize, 430 MaxPruneTxnPasses, 431 AgentLogfileMaxBackups, 432 AgentLogfileMaxSize, 433 ModelLogfileMaxBackups, 434 ModelLogfileMaxSize, 435 ModelLogsSize, 436 PruneTxnQueryCount, 437 PruneTxnSleepTime, 438 PublicDNSAddress, 439 JujuHASpace, 440 JujuManagementSpace, 441 AuditingEnabled, 442 AuditLogCaptureArgs, 443 AuditLogMaxSize, 444 AuditLogMaxBackups, 445 AuditLogExcludeMethods, 446 CAASOperatorImagePath, 447 CAASImageRepo, 448 Features, 449 MeteringURL, 450 MaxCharmStateSize, 451 MaxAgentStateSize, 452 MigrationMinionWaitMax, 453 ApplicationResourceDownloadLimit, 454 ControllerResourceDownloadLimit, 455 QueryTracingEnabled, 456 QueryTracingThreshold, 457 JujudControllerSnapSource, 458 } 459 460 // For backwards compatibility, we must include "anything", "juju-apiserver" 461 // and "juju-mongodb" as hostnames as that is what clients specify 462 // as the hostname for verification (this certificate is used both 463 // for serving MongoDB and API server connections). We also 464 // explicitly include localhost. 465 DefaultDNSNames = []string{ 466 "localhost", 467 "juju-apiserver", 468 "juju-mongodb", 469 "anything", 470 } 471 472 // AllowedUpdateConfigAttributes contains all of the controller 473 // config attributes that are allowed to be updated after the 474 // controller has been created. 475 AllowedUpdateConfigAttributes = set.NewStrings( 476 AgentLogfileMaxBackups, 477 AgentLogfileMaxSize, 478 AgentRateLimitMax, 479 AgentRateLimitRate, 480 APIPortOpenDelay, 481 ApplicationResourceDownloadLimit, 482 AuditingEnabled, 483 AuditLogCaptureArgs, 484 AuditLogExcludeMethods, 485 AuditLogMaxBackups, 486 AuditLogMaxSize, 487 CAASImageRepo, 488 // TODO Juju 3.0: ControllerAPIPort should be required and treated 489 // more like api-port. 490 ControllerAPIPort, 491 ControllerName, 492 ControllerResourceDownloadLimit, 493 Features, 494 JujuHASpace, 495 JujuManagementSpace, 496 MaxAgentStateSize, 497 MaxCharmStateSize, 498 MaxDebugLogDuration, 499 MaxPruneTxnBatchSize, 500 MaxPruneTxnPasses, 501 MigrationMinionWaitMax, 502 ModelLogfileMaxBackups, 503 ModelLogfileMaxSize, 504 ModelLogsSize, 505 MongoMemoryProfile, 506 PruneTxnQueryCount, 507 PruneTxnSleepTime, 508 PublicDNSAddress, 509 QueryTracingEnabled, 510 QueryTracingThreshold, 511 ) 512 513 // DefaultAuditLogExcludeMethods is the default list of methods to 514 // exclude from the audit log. 515 DefaultAuditLogExcludeMethods = []string{ 516 // This special value means we exclude any methods in the set 517 // listed in apiserver/observer/auditfilter.go 518 ReadOnlyMethodsWildcard, 519 } 520 521 methodNameRE = regexp.MustCompile(`[[:alpha:]][[:alnum:]]*\.[[:alpha:]][[:alnum:]]*`) 522 ) 523 524 // ControllerOnlyAttribute returns true if the specified attribute name 525 // is a controller config key (as opposed to, say, a model config key). 526 func ControllerOnlyAttribute(attr string) bool { 527 for _, a := range ControllerOnlyConfigAttributes { 528 if attr == a { 529 return true 530 } 531 } 532 return false 533 } 534 535 // Config is a string-keyed map of controller configuration attributes. 536 type Config map[string]interface{} 537 538 // Validate validates the controller configuration. 539 func (c Config) Validate() error { 540 return Validate(c) 541 } 542 543 // NewConfig creates a new Config from the supplied attributes. 544 // Default values will be used where defaults are available. 545 // 546 // The controller UUID and CA certificate must be passed in. 547 // The UUID is typically generated by the immediate caller, 548 // and the CA certificate generated by environs/bootstrap.NewConfig. 549 func NewConfig(controllerUUID, caCert string, attrs map[string]interface{}) (Config, error) { 550 // TODO(wallyworld) - use core/config when it supports duration types 551 for k, v := range attrs { 552 field, ok := ConfigSchema[k] 553 if !ok || field.Type != environschema.Tlist { 554 continue 555 } 556 str, ok := v.(string) 557 if !ok { 558 continue 559 } 560 var coerced interface{} 561 err := yaml.Unmarshal([]byte(str), &coerced) 562 if err != nil { 563 return Config{}, errors.NewNotValid(err, fmt.Sprintf("value %q for attribute %q not valid", str, k)) 564 } 565 attrs[k] = coerced 566 } 567 coerced, err := configChecker.Coerce(attrs, nil) 568 if err != nil { 569 return Config{}, errors.Trace(err) 570 } 571 attrs = coerced.(map[string]interface{}) 572 attrs[ControllerUUIDKey] = controllerUUID 573 attrs[CACertKey] = caCert 574 config := Config(attrs) 575 return config, config.Validate() 576 } 577 578 // mustInt returns the named attribute as an integer, panicking if 579 // it is not found or is zero. Zero values should have been 580 // diagnosed at Validate time. 581 func (c Config) mustInt(name string) int { 582 // Values obtained over the api are encoded as float64. 583 if value, ok := c[name].(float64); ok { 584 return int(value) 585 } 586 value, _ := c[name].(int) 587 if value == 0 { 588 panic(errors.Errorf("empty value for %q found in configuration", name)) 589 } 590 return value 591 } 592 593 func (c Config) intOrDefault(name string, defaultVal int) int { 594 if _, ok := c[name]; ok { 595 return c.mustInt(name) 596 } 597 return defaultVal 598 } 599 600 func (c Config) boolOrDefault(name string, defaultVal bool) bool { 601 if value, ok := c[name]; ok { 602 // Value has already been validated. 603 return value.(bool) 604 } 605 return defaultVal 606 } 607 608 func (c Config) sizeMBOrDefault(name string, defaultVal int) int { 609 size := c.asString(name) 610 if size != "" { 611 // Value has already been validated. 612 value, _ := utils.ParseSize(size) 613 return int(value) 614 } 615 return defaultVal 616 } 617 618 // asString is a private helper method to keep the ugly string casting 619 // in once place. It returns the given named attribute as a string, 620 // returning "" if it isn't found. 621 func (c Config) asString(name string) string { 622 value, _ := c[name].(string) 623 return value 624 } 625 626 // mustString returns the named attribute as an string, panicking if 627 // it is not found or is empty. 628 func (c Config) mustString(name string) string { 629 value, _ := c[name].(string) 630 if value == "" { 631 panic(errors.Errorf("empty value for %q found in configuration (type %T, val %v)", name, c[name], c[name])) 632 } 633 return value 634 } 635 636 func (c Config) durationOrDefault(name string, defaultVal time.Duration) time.Duration { 637 switch v := c[name].(type) { 638 case string: 639 if v != "" { 640 // Value has already been validated. 641 value, _ := time.ParseDuration(v) 642 return value 643 } 644 case time.Duration: 645 return v 646 default: 647 // nil type shows up here 648 } 649 return defaultVal 650 } 651 652 // StatePort returns the mongo server port for the environment. 653 func (c Config) StatePort() int { 654 return c.mustInt(StatePort) 655 } 656 657 // APIPort returns the API server port for the environment. 658 func (c Config) APIPort() int { 659 return c.mustInt(APIPort) 660 } 661 662 // APIPortOpenDelay returns the duration to wait before opening 663 // the APIPort once the controller has started up. Only used when 664 // the ControllerAPIPort is non-zero. 665 func (c Config) APIPortOpenDelay() time.Duration { 666 return c.durationOrDefault(APIPortOpenDelay, DefaultAPIPortOpenDelay) 667 } 668 669 // ControllerAPIPort returns the optional API port to be used for 670 // the controllers to talk to each other. A zero value means that 671 // it is not set. 672 func (c Config) ControllerAPIPort() int { 673 if value, ok := c[ControllerAPIPort].(float64); ok { 674 return int(value) 675 } 676 // If the value isn't an int, this conversion will fail and value 677 // will be 0, which is what we want here. 678 value, _ := c[ControllerAPIPort].(int) 679 return value 680 } 681 682 // ApplicationResourceDownloadLimit limits the number of concurrent resource download 683 // requests from unit agents which will be served. The limit is per application. 684 func (c Config) ApplicationResourceDownloadLimit() int { 685 switch v := c[ApplicationResourceDownloadLimit].(type) { 686 case float64: 687 return int(v) 688 case int: 689 return v 690 default: 691 // nil type shows up here 692 } 693 return DefaultApplicationResourceDownloadLimit 694 } 695 696 // ControllerResourceDownloadLimit limits the number of concurrent resource download 697 // requests from unit agents which will be served. The limit is for the combined total 698 // of all applications on the controller. 699 func (c Config) ControllerResourceDownloadLimit() int { 700 switch v := c[ControllerResourceDownloadLimit].(type) { 701 case float64: 702 return int(v) 703 case int: 704 return v 705 default: 706 // nil type shows up here 707 } 708 return DefaultControllerResourceDownloadLimit 709 } 710 711 // AgentRateLimitMax is the initial size of the token bucket that is used to 712 // rate limit agent connections. 713 func (c Config) AgentRateLimitMax() int { 714 switch v := c[AgentRateLimitMax].(type) { 715 case float64: 716 return int(v) 717 case int: 718 return v 719 default: 720 // nil type shows up here 721 } 722 return DefaultAgentRateLimitMax 723 } 724 725 // AgentRateLimitRate is the time taken to add a token into the token bucket 726 // that is used to rate limit agent connections. 727 func (c Config) AgentRateLimitRate() time.Duration { 728 return c.durationOrDefault(AgentRateLimitRate, DefaultAgentRateLimitRate) 729 } 730 731 // AuditingEnabled returns whether or not auditing has been enabled 732 // for the environment. The default is false. 733 func (c Config) AuditingEnabled() bool { 734 if v, ok := c[AuditingEnabled]; ok { 735 return v.(bool) 736 } 737 return DefaultAuditingEnabled 738 } 739 740 // AuditLogCaptureArgs returns whether audit logging should capture 741 // the arguments to API methods. The default is false. 742 func (c Config) AuditLogCaptureArgs() bool { 743 if v, ok := c[AuditLogCaptureArgs]; ok { 744 return v.(bool) 745 } 746 return DefaultAuditLogCaptureArgs 747 } 748 749 // AuditLogMaxSizeMB returns the maximum size for an audit log file in 750 // MB. 751 func (c Config) AuditLogMaxSizeMB() int { 752 return c.sizeMBOrDefault(AuditLogMaxSize, DefaultAuditLogMaxSizeMB) 753 } 754 755 // AuditLogMaxBackups returns the maximum number of backup audit log 756 // files to keep. 757 func (c Config) AuditLogMaxBackups() int { 758 return c.intOrDefault(AuditLogMaxBackups, DefaultAuditLogMaxBackups) 759 } 760 761 // AuditLogExcludeMethods returns the set of method names that are 762 // considered uninteresting for audit logging. Conversations 763 // containing only these will be excluded from the audit log. 764 func (c Config) AuditLogExcludeMethods() set.Strings { 765 if value, ok := c[AuditLogExcludeMethods]; ok { 766 value := value.([]interface{}) 767 items := set.NewStrings() 768 for _, item := range value { 769 items.Add(item.(string)) 770 } 771 return items 772 } 773 return set.NewStrings(DefaultAuditLogExcludeMethods...) 774 } 775 776 // Features returns the controller config set features flags. 777 func (c Config) Features() set.Strings { 778 features := set.NewStrings() 779 if value, ok := c[Features]; ok { 780 value := value.([]interface{}) 781 for _, item := range value { 782 features.Add(item.(string)) 783 } 784 } 785 return features 786 } 787 788 // ControllerName returns the name for the controller 789 func (c Config) ControllerName() string { 790 return c.asString(ControllerName) 791 } 792 793 // ControllerUUID returns the uuid for the controller. 794 func (c Config) ControllerUUID() string { 795 return c.mustString(ControllerUUIDKey) 796 } 797 798 // CACert returns the certificate of the CA that signed the controller 799 // certificate, in PEM format, and whether the setting is available. 800 // 801 // TODO(axw) once the controller config is completely constructed, 802 // there will always be a CA certificate. Get rid of the bool result. 803 func (c Config) CACert() (string, bool) { 804 if s, ok := c[CACertKey]; ok { 805 return s.(string), true 806 } 807 return "", false 808 } 809 810 // IdentityURL returns the URL of the identity manager. 811 func (c Config) IdentityURL() string { 812 return c.asString(IdentityURL) 813 } 814 815 // AutocertURL returns the URL used to obtain official TLS certificates 816 // when a client connects to the API. See AutocertURLKey 817 // for more details. 818 func (c Config) AutocertURL() string { 819 return c.asString(AutocertURLKey) 820 } 821 822 // AutocertDNSName returns the DNS name of the controller. 823 // See AutocertDNSNameKey for more details. 824 func (c Config) AutocertDNSName() string { 825 return c.asString(AutocertDNSNameKey) 826 } 827 828 // IdentityPublicKey returns the public key of the identity manager. 829 func (c Config) IdentityPublicKey() *bakery.PublicKey { 830 key := c.asString(IdentityPublicKey) 831 if key == "" { 832 return nil 833 } 834 var pubKey bakery.PublicKey 835 err := pubKey.UnmarshalText([]byte(key)) 836 if err != nil { 837 // We check if the key string can be unmarshalled into a PublicKey in the 838 // Validate function, so we really do not expect this to fail. 839 panic(err) 840 } 841 return &pubKey 842 } 843 844 // LoginTokenRefreshURL returns the URL of the login jwt well known endpoint. 845 func (c Config) LoginTokenRefreshURL() string { 846 return c.asString(LoginTokenRefreshURL) 847 } 848 849 // MongoMemoryProfile returns the selected profile or low. 850 func (c Config) MongoMemoryProfile() string { 851 if profile, ok := c[MongoMemoryProfile]; ok { 852 return profile.(string) 853 } 854 return DefaultMongoMemoryProfile 855 } 856 857 // JujuDBSnapChannel returns the channel for installing mongo snaps. 858 func (c Config) JujuDBSnapChannel() string { 859 return c.asString(JujuDBSnapChannel) 860 } 861 862 // JujudControllerSnapSource returns the source of the jujud-controller snap. 863 func (c Config) JujudControllerSnapSource() string { 864 if src, ok := c[JujudControllerSnapSource]; ok { 865 return src.(string) 866 } 867 return DefaultJujudControllerSnapSource 868 } 869 870 // NUMACtlPreference returns if numactl is preferred. 871 func (c Config) NUMACtlPreference() bool { 872 if numa, ok := c[SetNUMAControlPolicyKey]; ok { 873 return numa.(bool) 874 } 875 return DefaultNUMAControlPolicy 876 } 877 878 // AllowModelAccess reports whether users are allowed to access models 879 // they have been granted permission for even when they can't access 880 // the controller. 881 func (c Config) AllowModelAccess() bool { 882 value, _ := c[AllowModelAccessKey].(bool) 883 return value 884 } 885 886 // AgentLogfileMaxSizeMB is the maximum file size in MB of each 887 // agent/controller log file. 888 func (c Config) AgentLogfileMaxSizeMB() int { 889 return c.sizeMBOrDefault(AgentLogfileMaxSize, DefaultAgentLogfileMaxSize) 890 } 891 892 // AgentLogfileMaxBackups is the number of old agent/controller log files to 893 // keep (compressed). 894 func (c Config) AgentLogfileMaxBackups() int { 895 return c.intOrDefault(AgentLogfileMaxBackups, DefaultAgentLogfileMaxBackups) 896 } 897 898 // ModelLogfileMaxBackups is the number of old model log files to keep (compressed). 899 func (c Config) ModelLogfileMaxBackups() int { 900 return c.intOrDefault(ModelLogfileMaxBackups, DefaultModelLogfileMaxBackups) 901 } 902 903 // ModelLogfileMaxSizeMB is the maximum size of the log file written out by the 904 // controller on behalf of workers running for a model. 905 func (c Config) ModelLogfileMaxSizeMB() int { 906 return c.sizeMBOrDefault(ModelLogfileMaxSize, DefaultModelLogfileMaxSize) 907 } 908 909 // ModelLogsSizeMB is the size of the capped collection used to store the model 910 // logs. Total size on disk will be ModelLogsSizeMB * number of models. 911 func (c Config) ModelLogsSizeMB() int { 912 return c.sizeMBOrDefault(ModelLogsSize, DefaultModelLogsSizeMB) 913 } 914 915 // MaxDebugLogDuration is the maximum time a debug-log session is allowed 916 // to run before it is terminated by the server. 917 func (c Config) MaxDebugLogDuration() time.Duration { 918 return c.durationOrDefault(MaxDebugLogDuration, DefaultMaxDebugLogDuration) 919 } 920 921 // MaxTxnLogSizeMB is the maximum size in MiB of the txn log collection. 922 func (c Config) MaxTxnLogSizeMB() int { 923 return c.sizeMBOrDefault(MaxTxnLogSize, DefaultMaxTxnLogCollectionMB) 924 } 925 926 // MaxPruneTxnBatchSize is the maximum size of the txn log collection. 927 func (c Config) MaxPruneTxnBatchSize() int { 928 return c.intOrDefault(MaxPruneTxnBatchSize, DefaultMaxPruneTxnBatchSize) 929 } 930 931 // MaxPruneTxnPasses is the maximum number of batches of the txn log collection we will process at a time. 932 func (c Config) MaxPruneTxnPasses() int { 933 return c.intOrDefault(MaxPruneTxnPasses, DefaultMaxPruneTxnPasses) 934 } 935 936 // PruneTxnQueryCount is the size of small batches for pruning 937 func (c Config) PruneTxnQueryCount() int { 938 return c.intOrDefault(PruneTxnQueryCount, DefaultPruneTxnQueryCount) 939 } 940 941 // PruneTxnSleepTime is the amount of time to sleep between batches. 942 func (c Config) PruneTxnSleepTime() time.Duration { 943 return c.durationOrDefault(PruneTxnSleepTime, DefaultPruneTxnSleepTime) 944 } 945 946 // PublicDNSAddress returns the DNS name of the controller. 947 func (c Config) PublicDNSAddress() string { 948 return c.asString(PublicDNSAddress) 949 } 950 951 // JujuHASpace is the network space within which the MongoDB replica-set 952 // should communicate. 953 func (c Config) JujuHASpace() string { 954 return c.asString(JujuHASpace) 955 } 956 957 // JujuManagementSpace is the network space that agents should use to 958 // communicate with controllers. 959 func (c Config) JujuManagementSpace() string { 960 return c.asString(JujuManagementSpace) 961 } 962 963 // CAASOperatorImagePath sets the URL of the docker image 964 // used for the application operator. 965 // Deprecated: use CAASImageRepo 966 func (c Config) CAASOperatorImagePath() string { 967 return c.asString(CAASOperatorImagePath) 968 } 969 970 // CAASImageRepo sets the URL of the docker repo 971 // used for the jujud operator and mongo images. 972 func (c Config) CAASImageRepo() string { 973 return c.asString(CAASImageRepo) 974 } 975 976 // MeteringURL returns the URL to use for metering api calls. 977 func (c Config) MeteringURL() string { 978 url := c.asString(MeteringURL) 979 if url == "" { 980 return romulus.DefaultAPIRoot 981 } 982 return url 983 } 984 985 // MaxCharmStateSize returns the max size (in bytes) of charm-specific state 986 // that each unit can store to the controller. A value of zero indicates no 987 // limit. 988 func (c Config) MaxCharmStateSize() int { 989 return c.intOrDefault(MaxCharmStateSize, DefaultMaxCharmStateSize) 990 } 991 992 // MaxAgentStateSize returns the max size (in bytes) of state data that agents 993 // can store to the controller. A value of zero indicates no limit. 994 func (c Config) MaxAgentStateSize() int { 995 return c.intOrDefault(MaxAgentStateSize, DefaultMaxAgentStateSize) 996 } 997 998 // MigrationMinionWaitMax returns a duration for the maximum time that the 999 // migration-master worker should wait for migration-minion reports during 1000 // phases of a model migration. 1001 func (c Config) MigrationMinionWaitMax() time.Duration { 1002 return c.durationOrDefault(MigrationMinionWaitMax, DefaultMigrationMinionWaitMax) 1003 } 1004 1005 // QueryTracingEnabled returns whether query tracing is enabled. 1006 func (c Config) QueryTracingEnabled() bool { 1007 return c.boolOrDefault(QueryTracingEnabled, DefaultQueryTracingEnabled) 1008 } 1009 1010 // QueryTracingThreshold returns the threshold for query tracing. The 1011 // lower the threshold, the more queries will be output. A value of 0 1012 // means all queries will be output. 1013 func (c Config) QueryTracingThreshold() time.Duration { 1014 return c.durationOrDefault(QueryTracingThreshold, DefaultQueryTracingThreshold) 1015 } 1016 1017 // Validate ensures that config is a valid configuration. 1018 func Validate(c Config) error { 1019 if v, ok := c[IdentityPublicKey].(string); ok { 1020 var key bakery.PublicKey 1021 if err := key.UnmarshalText([]byte(v)); err != nil { 1022 return errors.Annotate(err, "invalid identity public key") 1023 } 1024 } 1025 1026 if v, ok := c[IdentityURL].(string); ok { 1027 u, err := url.Parse(v) 1028 if err != nil { 1029 return errors.Annotate(err, "invalid identity URL") 1030 } 1031 // If we've got an identity public key, we allow an HTTP 1032 // scheme for the identity server because we won't need 1033 // to rely on insecure transport to obtain the public 1034 // key. 1035 if _, ok := c[IdentityPublicKey]; !ok && u.Scheme != "https" { 1036 return errors.Errorf("URL needs to be https when %s not provided", IdentityPublicKey) 1037 } 1038 } 1039 1040 if v, ok := c[LoginTokenRefreshURL].(string); ok { 1041 u, err := url.Parse(v) 1042 if err != nil { 1043 return errors.Annotate(err, "invalid login token refresh URL") 1044 } 1045 if u.Scheme == "" || u.Host == "" { 1046 return errors.NotValidf("logic token refresh URL %q", v) 1047 } 1048 } 1049 1050 caCert, caCertOK := c.CACert() 1051 if !caCertOK { 1052 return errors.Errorf("missing CA certificate") 1053 } 1054 if ok, err := pki.IsPemCA([]byte(caCert)); err != nil { 1055 return errors.Annotate(err, "bad CA certificate in configuration") 1056 } else if !ok { 1057 return errors.New("ca certificate in configuration is not a CA") 1058 } 1059 1060 if uuid, ok := c[ControllerUUIDKey].(string); ok && !utils.IsValidUUIDString(uuid) { 1061 return errors.Errorf("controller-uuid: expected UUID, got string(%q)", uuid) 1062 } 1063 1064 if v, ok := c[ApplicationResourceDownloadLimit].(int); ok { 1065 if v < 0 { 1066 return errors.Errorf("negative %s (%d) not valid, use 0 to disable the limit", ApplicationResourceDownloadLimit, v) 1067 } 1068 } 1069 if v, ok := c[ControllerResourceDownloadLimit].(int); ok { 1070 if v < 0 { 1071 return errors.Errorf("negative %s (%d) not valid, use 0 to disable the limit", ControllerResourceDownloadLimit, v) 1072 } 1073 } 1074 if v, ok := c[AgentRateLimitMax].(int); ok { 1075 if v < 0 { 1076 return errors.NotValidf("negative %s (%d)", AgentRateLimitMax, v) 1077 } 1078 } 1079 if v, ok := c[AgentRateLimitRate].(time.Duration); ok { 1080 if v == 0 { 1081 return errors.Errorf("%s cannot be zero", AgentRateLimitRate) 1082 } 1083 if v < 0 { 1084 return errors.Errorf("%s cannot be negative", AgentRateLimitRate) 1085 } 1086 if v > time.Minute { 1087 return errors.Errorf("%s must be between 0..1m", AgentRateLimitRate) 1088 } 1089 } 1090 1091 if mgoMemProfile, ok := c[MongoMemoryProfile].(string); ok { 1092 if mgoMemProfile != MongoProfLow && mgoMemProfile != MongoProfDefault { 1093 return errors.Errorf("mongo-memory-profile: expected one of %q or %q got string(%q)", MongoProfLow, MongoProfDefault, mgoMemProfile) 1094 } 1095 } 1096 1097 if v, ok := c[MaxDebugLogDuration].(time.Duration); ok { 1098 if v == 0 { 1099 return errors.Errorf("%s cannot be zero", MaxDebugLogDuration) 1100 } 1101 } 1102 1103 if v, ok := c[ModelLogsSize].(string); ok { 1104 mb, err := utils.ParseSize(v) 1105 if err != nil { 1106 return errors.Annotate(err, "invalid model logs size in configuration") 1107 } 1108 if mb < 1 { 1109 return errors.NotValidf("model logs size less than 1 MB") 1110 } 1111 } 1112 1113 if v, ok := c[AgentLogfileMaxBackups].(int); ok { 1114 if v < 0 { 1115 return errors.NotValidf("negative %s", AgentLogfileMaxBackups) 1116 } 1117 } 1118 if v, ok := c[AgentLogfileMaxSize].(string); ok { 1119 mb, err := utils.ParseSize(v) 1120 if err != nil { 1121 return errors.Annotatef(err, "invalid %s in configuration", AgentLogfileMaxSize) 1122 } 1123 if mb < 1 { 1124 return errors.NotValidf("%s less than 1 MB", AgentLogfileMaxSize) 1125 } 1126 } 1127 1128 if v, ok := c[ModelLogfileMaxBackups].(int); ok { 1129 if v < 0 { 1130 return errors.NotValidf("negative %s", ModelLogfileMaxBackups) 1131 } 1132 } 1133 if v, ok := c[ModelLogfileMaxSize].(string); ok { 1134 mb, err := utils.ParseSize(v) 1135 if err != nil { 1136 return errors.Annotatef(err, "invalid %s in configuration", ModelLogfileMaxSize) 1137 } 1138 if mb < 1 { 1139 return errors.NotValidf("%s less than 1 MB", ModelLogfileMaxSize) 1140 } 1141 } 1142 1143 if v, ok := c[MaxTxnLogSize].(string); ok { 1144 if _, err := utils.ParseSize(v); err != nil { 1145 return errors.Annotate(err, "invalid max txn log size in configuration") 1146 } 1147 } 1148 1149 if v, ok := c[PruneTxnSleepTime].(string); ok { 1150 if _, err := time.ParseDuration(v); err != nil { 1151 return errors.Annotatef(err, `%s must be a valid duration (eg "10ms")`, PruneTxnSleepTime) 1152 } 1153 } 1154 1155 if err := c.validateSpaceConfig(JujuHASpace, "juju HA"); err != nil { 1156 return errors.Trace(err) 1157 } 1158 1159 if err := c.validateSpaceConfig(JujuManagementSpace, "juju mgmt"); err != nil { 1160 return errors.Trace(err) 1161 } 1162 1163 var auditLogMaxSize int 1164 if v, ok := c[AuditLogMaxSize].(string); ok { 1165 if size, err := utils.ParseSize(v); err != nil { 1166 return errors.Annotate(err, "invalid audit log max size in configuration") 1167 } else { 1168 auditLogMaxSize = int(size) 1169 } 1170 } 1171 1172 if v, ok := c[AuditingEnabled].(bool); ok { 1173 if v && auditLogMaxSize == 0 { 1174 return errors.Errorf("invalid audit log max size: can't be 0 if auditing is enabled") 1175 } 1176 } 1177 1178 if v, ok := c[AuditLogMaxBackups].(int); ok { 1179 if v < 0 { 1180 return errors.Errorf("invalid audit log max backups: should be a number of files (or 0 to keep all), got %d", v) 1181 } 1182 } 1183 1184 if v, ok := c[AuditLogExcludeMethods].([]interface{}); ok { 1185 for i, name := range v { 1186 name := name.(string) 1187 if name != ReadOnlyMethodsWildcard && !methodNameRE.MatchString(name) { 1188 return errors.Errorf( 1189 `invalid audit log exclude methods: should be a list of "Facade.Method" names (or "ReadOnlyMethods"), got %q at position %d`, 1190 name, 1191 i+1, 1192 ) 1193 } 1194 } 1195 } 1196 1197 if v, ok := c[ControllerAPIPort].(int); ok { 1198 // TODO: change the validation so 0 is invalid and --reset is used. 1199 // However that doesn't exist yet. 1200 if v < 0 { 1201 return errors.NotValidf("non-positive integer for controller-api-port") 1202 } 1203 if v == c.APIPort() { 1204 return errors.NotValidf("controller-api-port matching api-port") 1205 } 1206 if v == c.StatePort() { 1207 return errors.NotValidf("controller-api-port matching state-port") 1208 } 1209 } 1210 if v, ok := c[APIPortOpenDelay].(string); ok { 1211 _, err := time.ParseDuration(v) 1212 if err != nil { 1213 return errors.Errorf("%s value %q must be a valid duration", APIPortOpenDelay, v) 1214 } 1215 } 1216 1217 // Each unit stores the charm and uniter state in a single document. 1218 // Given that mongo by default enforces a 16M limit for documents we 1219 // should also verify that the combined limits don't exceed 16M. 1220 var maxUnitStateSize int 1221 if v, ok := c[MaxCharmStateSize].(int); ok { 1222 if v < 0 { 1223 return errors.Errorf("invalid max charm state size: should be a number of bytes (or 0 to disable limit), got %d", v) 1224 } 1225 maxUnitStateSize += v 1226 } else { 1227 maxUnitStateSize += DefaultMaxCharmStateSize 1228 } 1229 1230 if v, ok := c[MaxAgentStateSize].(int); ok { 1231 if v < 0 { 1232 return errors.Errorf("invalid max agent state size: should be a number of bytes (or 0 to disable limit), got %d", v) 1233 } 1234 maxUnitStateSize += v 1235 } else { 1236 maxUnitStateSize += DefaultMaxAgentStateSize 1237 } 1238 1239 if mongoMax := 16 * 1024 * 1024; maxUnitStateSize > mongoMax { 1240 return errors.Errorf("invalid max charm/agent state sizes: combined value should not exceed mongo's 16M per-document limit, got %d", maxUnitStateSize) 1241 } 1242 1243 if v, ok := c[MigrationMinionWaitMax].(string); ok { 1244 _, err := time.ParseDuration(v) 1245 if err != nil { 1246 return errors.Errorf("%s value %q must be a valid duration", MigrationMinionWaitMax, v) 1247 } 1248 } 1249 1250 if d, ok := c[QueryTracingThreshold].(time.Duration); ok { 1251 if d < 0 { 1252 return errors.Errorf("%s value %q must be a positive duration", QueryTracingThreshold, d) 1253 } 1254 } 1255 1256 if v, ok := c[JujudControllerSnapSource].(string); ok { 1257 switch v { 1258 case "legacy": // TODO(jujud-controller-snap): remove once jujud-controller snap is fully implemented. 1259 case "snapstore", "local", "local-dangerous": 1260 default: 1261 return errors.Errorf("%s value %q must be one of legacy, snapstore, local or local-dangerous.", JujudControllerSnapSource, v) 1262 } 1263 } 1264 1265 return nil 1266 } 1267 1268 func (c Config) validateSpaceConfig(key, topic string) error { 1269 val := c[key] 1270 if val == nil { 1271 return nil 1272 } 1273 if v, ok := val.(string); ok { 1274 if !names.IsValidSpace(v) { 1275 return errors.NotValidf("%s space name %q", topic, val) 1276 } 1277 } else { 1278 return errors.NotValidf("type for %s space name %v", topic, val) 1279 } 1280 1281 return nil 1282 } 1283 1284 // AsSpaceConstraints checks to see whether config has spaces names populated 1285 // for management and/or HA (Mongo). 1286 // Non-empty values are merged with any input spaces and returned as a new 1287 // slice reference. 1288 // A slice pointer is used for congruence with the Spaces member in 1289 // constraints.Value. 1290 func (c Config) AsSpaceConstraints(spaces *[]string) *[]string { 1291 newSpaces := set.NewStrings() 1292 if spaces != nil { 1293 for _, s := range *spaces { 1294 newSpaces.Add(s) 1295 } 1296 } 1297 1298 for _, c := range []string{c.JujuManagementSpace(), c.JujuHASpace()} { 1299 // NOTE (hml) 2019-10-30 1300 // This can cause issues in deployment and/or enabling HA if 1301 // c == AlphaSpaceName as the provisioner expects any space 1302 // listed to have subnets. Which is only AWS today. 1303 if c != "" { 1304 newSpaces.Add(c) 1305 } 1306 } 1307 1308 // Preserve a nil pointer if there is no change. This conveys information 1309 // in constraints.Value (not set vs. deliberately set as empty). 1310 if spaces == nil && len(newSpaces) == 0 { 1311 return nil 1312 } 1313 ns := newSpaces.SortedValues() 1314 return &ns 1315 }