github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/controller/config.go (about)

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