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  }