github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cloudconfig/instancecfg/instancecfg.go (about) 1 // Copyright 2012, 2013, 2015, 2016 Canonical Ltd. 2 // Copyright 2015 Cloudbase Solutions SRL 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package instancecfg 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "net" 11 "path" 12 "reflect" 13 "strconv" 14 "time" 15 16 "github.com/juju/errors" 17 "github.com/juju/loggo" 18 "github.com/juju/utils/proxy" 19 "github.com/juju/utils/shell" 20 "github.com/juju/version" 21 "gopkg.in/juju/names.v2" 22 "gopkg.in/yaml.v2" 23 24 "github.com/juju/juju/agent" 25 agenttools "github.com/juju/juju/agent/tools" 26 "github.com/juju/juju/api" 27 "github.com/juju/juju/apiserver/params" 28 "github.com/juju/juju/cloud" 29 "github.com/juju/juju/constraints" 30 "github.com/juju/juju/controller" 31 "github.com/juju/juju/environs/config" 32 "github.com/juju/juju/environs/imagemetadata" 33 "github.com/juju/juju/environs/tags" 34 "github.com/juju/juju/instance" 35 "github.com/juju/juju/juju/paths" 36 "github.com/juju/juju/mongo" 37 "github.com/juju/juju/service" 38 "github.com/juju/juju/service/common" 39 "github.com/juju/juju/state/multiwatcher" 40 coretools "github.com/juju/juju/tools" 41 ) 42 43 var logger = loggo.GetLogger("juju.cloudconfig.instancecfg") 44 45 // InstanceConfig represents initialization information for a new juju instance. 46 type InstanceConfig struct { 47 // Tags is a set of tags to set on the instance, if supported. This 48 // should be populated using the InstanceTags method in this package. 49 Tags map[string]string 50 51 // Bootstrap contains bootstrap-specific configuration. If this is set, 52 // Controller must also be set. 53 Bootstrap *BootstrapConfig 54 55 // Controller contains controller-specific configuration. If this is 56 // set, then the instance will be configured as a controller machine. 57 Controller *ControllerConfig 58 59 // APIInfo holds the means for the new instance to communicate with the 60 // juju state API. Unless the new instance is running a controller (Controller is 61 // set), there must be at least one controller address supplied. 62 // The entity name must match that of the instance being started, 63 // or be empty when starting a controller. 64 APIInfo *api.Info 65 66 // ControllerTag identifies the controller. 67 ControllerTag names.ControllerTag 68 69 // MachineNonce is set at provisioning/bootstrap time and used to 70 // ensure the agent is running on the correct instance. 71 MachineNonce string 72 73 // tools is the list of juju tools used to install the Juju agent 74 // on the new instance. Each of the entries in the list must have 75 // identical versions and hashes, but may have different URLs. 76 tools coretools.List 77 78 // DataDir holds the directory that juju state will be put in the new 79 // instance. 80 DataDir string 81 82 // LogDir holds the directory that juju logs will be written to. 83 LogDir string 84 85 // MetricsSpoolDir represents the spool directory path, where all 86 // metrics are stored. 87 MetricsSpoolDir string 88 89 // Jobs holds what machine jobs to run. 90 Jobs []multiwatcher.MachineJob 91 92 // CloudInitOutputLog specifies the path to the output log for cloud-init. 93 // The directory containing the log file must already exist. 94 CloudInitOutputLog string 95 96 // MachineId identifies the new machine. 97 MachineId string 98 99 // MachineContainerType specifies the type of container that the instance 100 // is. If the instance is not a container, then the type is "". 101 MachineContainerType instance.ContainerType 102 103 // MachineContainerHostname specifies the hostname to be used with the 104 // cloud config for the instance. If this is not set, hostname uses the default. 105 MachineContainerHostname string 106 107 // AuthorizedKeys specifies the keys that are allowed to 108 // connect to the instance (see cloudinit.SSHAddAuthorizedKeys) 109 // If no keys are supplied, there can be no ssh access to the node. 110 // On a bootstrap instance, that is fatal. On other 111 // instances it will mean that the ssh, scp and debug-hooks 112 // commands cannot work. 113 AuthorizedKeys string 114 115 // AgentEnvironment defines additional configuration variables to set in 116 // the instance agent config. 117 AgentEnvironment map[string]string 118 119 // DisableSSLHostnameVerification can be set to true to tell cloud-init 120 // that it shouldn't verify SSL certificates 121 DisableSSLHostnameVerification bool 122 123 // Series represents the instance series. 124 Series string 125 126 // MachineAgentServiceName is the init service name for the Juju machine agent. 127 MachineAgentServiceName string 128 129 // ProxySettings define normal http, https and ftp proxies. 130 ProxySettings proxy.Settings 131 132 // AptProxySettings define the http, https and ftp proxy settings to use 133 // for apt, which may or may not be the same as the normal ProxySettings. 134 AptProxySettings proxy.Settings 135 136 // AptMirror defines an APT mirror location, which, if specified, will 137 // override the default APT sources. 138 AptMirror string 139 140 // The type of Simple Stream to download and deploy on this instance. 141 ImageStream string 142 143 // EnableOSRefreshUpdate specifies whether Juju will refresh its 144 // respective OS's updates list. 145 EnableOSRefreshUpdate bool 146 147 // EnableOSUpgrade defines Juju's behavior when provisioning 148 // instances. If enabled, the OS will perform any upgrades 149 // available as part of its provisioning. 150 EnableOSUpgrade bool 151 } 152 153 // ControllerConfig represents controller-specific initialization information 154 // for a new juju instance. This is only relevant for controller machines. 155 type ControllerConfig struct { 156 // MongoInfo holds the means for the new instance to communicate with the 157 // juju state database. Unless the new instance is running a controller 158 // (Controller is set), there must be at least one controller address supplied. 159 // The entity name must match that of the instance being started, 160 // or be empty when starting a controller. 161 MongoInfo *mongo.MongoInfo 162 163 // Config contains controller config attributes. 164 Config controller.Config 165 166 // The public key used to sign Juju simplestreams image metadata. 167 PublicImageSigningKey string 168 } 169 170 // BootstrapConfig represents bootstrap-specific initialization information 171 // for a new juju instance. This is only relevant for the bootstrap machine. 172 type BootstrapConfig struct { 173 StateInitializationParams 174 175 // GUI is the Juju GUI archive to be installed in the new instance. 176 GUI *coretools.GUIArchive 177 178 // Timeout is the amount of time to wait for bootstrap to complete. 179 Timeout time.Duration 180 181 // StateServingInfo holds the information for serving the state. 182 // This is only specified for bootstrap; controllers started 183 // subsequently will acquire their serving info from another 184 // server. 185 StateServingInfo params.StateServingInfo 186 } 187 188 // StateInitializationParams contains parameters for initializing the 189 // state database. 190 // 191 // This structure will be passed to the bootstrap agent. To do so, the 192 // Marshal and Unmarshal methods must be used. 193 type StateInitializationParams struct { 194 // ControllerModelConfig holds the initial controller model configuration. 195 ControllerModelConfig *config.Config 196 197 // ControllerCloudName is the name of the cloud that Juju will be 198 // bootstrapped in. 199 ControllerCloudName string 200 201 // ControllerCloud contains the properties of the cloud that Juju will 202 // be bootstrapped in. 203 ControllerCloud cloud.Cloud 204 205 // ControllerCloudRegion is the name of the cloud region that Juju will be 206 // bootstrapped in. 207 ControllerCloudRegion string 208 209 // ControllerCloudCredentialName is the name of the cloud credential that 210 // Juju will be bootstrapped with. 211 ControllerCloudCredentialName string 212 213 // ControllerCloudCredential contains the cloud credential that Juju will 214 // be bootstrapped with. 215 ControllerCloudCredential *cloud.Credential 216 217 // ControllerConfig is the set of config attributes relevant 218 // to a controller. 219 ControllerConfig controller.Config 220 221 // ControllerInheritedConfig is a set of config attributes to be shared by all 222 // models managed by this controller. 223 ControllerInheritedConfig map[string]interface{} 224 225 // RegionInheritedConfig holds region specific configuration attributes to 226 // be shared across all models in the same controller on a particular 227 // cloud. 228 RegionInheritedConfig cloud.RegionConfig 229 230 // HostedModelConfig is a set of config attributes to be overlaid 231 // on the controller model config (Config, above) to construct the 232 // initial hosted model config. 233 HostedModelConfig map[string]interface{} 234 235 // BootstrapMachineInstanceId is the instance ID of the bootstrap 236 // machine instance being initialized. 237 BootstrapMachineInstanceId instance.Id 238 239 // BootstrapMachineConstraints holds the constraints for the bootstrap 240 // machine. 241 BootstrapMachineConstraints constraints.Value 242 243 // BootstrapMachineHardwareCharacteristics contains the harrdware 244 // characteristics of the bootstrap machine instance being initialized. 245 BootstrapMachineHardwareCharacteristics *instance.HardwareCharacteristics 246 247 // ModelConstraints holds the initial model constraints. 248 ModelConstraints constraints.Value 249 250 // CustomImageMetadata is optional custom simplestreams image metadata 251 // to store in environment storage at bootstrap time. This is ignored 252 // in non-bootstrap instances. 253 CustomImageMetadata []*imagemetadata.ImageMetadata 254 } 255 256 type stateInitializationParamsInternal struct { 257 ControllerConfig map[string]interface{} `yaml:"controller-config"` 258 ControllerModelConfig map[string]interface{} `yaml:"controller-model-config"` 259 ControllerInheritedConfig map[string]interface{} `yaml:"controller-config-defaults,omitempty"` 260 RegionInheritedConfig cloud.RegionConfig `yaml:"region-inherited-config,omitempty"` 261 HostedModelConfig map[string]interface{} `yaml:"hosted-model-config,omitempty"` 262 BootstrapMachineInstanceId instance.Id `yaml:"bootstrap-machine-instance-id"` 263 BootstrapMachineConstraints constraints.Value `yaml:"bootstrap-machine-constraints"` 264 BootstrapMachineHardwareCharacteristics *instance.HardwareCharacteristics `yaml:"bootstrap-machine-hardware,omitempty"` 265 ModelConstraints constraints.Value `yaml:"model-constraints"` 266 CustomImageMetadataJSON string `yaml:"custom-image-metadata,omitempty"` 267 ControllerCloudName string `yaml:"controller-cloud-name"` 268 ControllerCloud string `yaml:"controller-cloud"` 269 ControllerCloudRegion string `yaml:"controller-cloud-region"` 270 ControllerCloudCredentialName string `yaml:"controller-cloud-credential-name,omitempty"` 271 ControllerCloudCredential *cloud.Credential `yaml:"controller-cloud-credential,omitempty"` 272 } 273 274 // Marshal marshals StateInitializationParams to an opaque byte array. 275 func (p *StateInitializationParams) Marshal() ([]byte, error) { 276 customImageMetadataJSON, err := json.Marshal(p.CustomImageMetadata) 277 if err != nil { 278 return nil, errors.Annotate(err, "marshalling custom image metadata") 279 } 280 controllerCloud, err := cloud.MarshalCloud(p.ControllerCloud) 281 if err != nil { 282 return nil, errors.Annotate(err, "marshalling cloud definition") 283 } 284 internal := stateInitializationParamsInternal{ 285 p.ControllerConfig, 286 p.ControllerModelConfig.AllAttrs(), 287 p.ControllerInheritedConfig, 288 p.RegionInheritedConfig, 289 p.HostedModelConfig, 290 p.BootstrapMachineInstanceId, 291 p.BootstrapMachineConstraints, 292 p.BootstrapMachineHardwareCharacteristics, 293 p.ModelConstraints, 294 string(customImageMetadataJSON), 295 p.ControllerCloudName, 296 string(controllerCloud), 297 p.ControllerCloudRegion, 298 p.ControllerCloudCredentialName, 299 p.ControllerCloudCredential, 300 } 301 return yaml.Marshal(&internal) 302 } 303 304 // Unmarshal unmarshals StateInitializationParams from a byte array that 305 // was generated with StateInitializationParams.Marshal. 306 func (p *StateInitializationParams) Unmarshal(data []byte) error { 307 var internal stateInitializationParamsInternal 308 if err := yaml.Unmarshal(data, &internal); err != nil { 309 return errors.Annotate(err, "unmarshalling state initialization params") 310 } 311 var imageMetadata []*imagemetadata.ImageMetadata 312 if err := json.Unmarshal([]byte(internal.CustomImageMetadataJSON), &imageMetadata); err != nil { 313 return errors.Trace(err) 314 } 315 cfg, err := config.New(config.NoDefaults, internal.ControllerModelConfig) 316 if err != nil { 317 return errors.Trace(err) 318 } 319 controllerCloud, err := cloud.UnmarshalCloud([]byte(internal.ControllerCloud)) 320 if err != nil { 321 return errors.Trace(err) 322 } 323 *p = StateInitializationParams{ 324 ControllerConfig: internal.ControllerConfig, 325 ControllerModelConfig: cfg, 326 ControllerInheritedConfig: internal.ControllerInheritedConfig, 327 RegionInheritedConfig: internal.RegionInheritedConfig, 328 HostedModelConfig: internal.HostedModelConfig, 329 BootstrapMachineInstanceId: internal.BootstrapMachineInstanceId, 330 BootstrapMachineConstraints: internal.BootstrapMachineConstraints, 331 BootstrapMachineHardwareCharacteristics: internal.BootstrapMachineHardwareCharacteristics, 332 ModelConstraints: internal.ModelConstraints, 333 CustomImageMetadata: imageMetadata, 334 ControllerCloudName: internal.ControllerCloudName, 335 ControllerCloud: controllerCloud, 336 ControllerCloudRegion: internal.ControllerCloudRegion, 337 ControllerCloudCredentialName: internal.ControllerCloudCredentialName, 338 ControllerCloudCredential: internal.ControllerCloudCredential, 339 } 340 return nil 341 } 342 343 func (cfg *InstanceConfig) agentInfo() service.AgentInfo { 344 return service.NewMachineAgentInfo( 345 cfg.MachineId, 346 cfg.DataDir, 347 cfg.LogDir, 348 ) 349 } 350 351 func (cfg *InstanceConfig) ToolsDir(renderer shell.Renderer) string { 352 return cfg.agentInfo().ToolsDir(renderer) 353 } 354 355 func (cfg *InstanceConfig) InitService(renderer shell.Renderer) (service.Service, error) { 356 conf := service.AgentConf(cfg.agentInfo(), renderer) 357 358 name := cfg.MachineAgentServiceName 359 svc, err := newService(name, conf, cfg.Series) 360 return svc, errors.Trace(err) 361 } 362 363 var newService = func(name string, conf common.Conf, series string) (service.Service, error) { 364 return service.NewService(name, conf, series) 365 } 366 367 func (cfg *InstanceConfig) AgentConfig( 368 tag names.Tag, 369 toolsVersion version.Number, 370 ) (agent.ConfigSetter, error) { 371 // TODO for HAState: the stateHostAddrs and apiHostAddrs here assume that 372 // if the instance is a controller then to use localhost. This may be 373 // sufficient, but needs thought in the new world order. 374 var password, cacert string 375 if cfg.Controller == nil { 376 password = cfg.APIInfo.Password 377 cacert = cfg.APIInfo.CACert 378 } else { 379 password = cfg.Controller.MongoInfo.Password 380 cacert = cfg.Controller.MongoInfo.CACert 381 } 382 configParams := agent.AgentConfigParams{ 383 Paths: agent.Paths{ 384 DataDir: cfg.DataDir, 385 LogDir: cfg.LogDir, 386 MetricsSpoolDir: cfg.MetricsSpoolDir, 387 }, 388 Jobs: cfg.Jobs, 389 Tag: tag, 390 UpgradedToVersion: toolsVersion, 391 Password: password, 392 Nonce: cfg.MachineNonce, 393 StateAddresses: cfg.stateHostAddrs(), 394 APIAddresses: cfg.APIHostAddrs(), 395 CACert: cacert, 396 Values: cfg.AgentEnvironment, 397 Controller: cfg.ControllerTag, 398 Model: cfg.APIInfo.ModelTag, 399 } 400 if cfg.Bootstrap == nil { 401 return agent.NewAgentConfig(configParams) 402 } 403 return agent.NewStateMachineConfig(configParams, cfg.Bootstrap.StateServingInfo) 404 } 405 406 // JujuTools returns the directory where Juju tools are stored. 407 func (cfg *InstanceConfig) JujuTools() string { 408 return agenttools.SharedToolsDir(cfg.DataDir, cfg.AgentVersion()) 409 } 410 411 // GUITools returns the directory where the Juju GUI release is stored. 412 func (cfg *InstanceConfig) GUITools() string { 413 return agenttools.SharedGUIDir(cfg.DataDir) 414 } 415 416 func (cfg *InstanceConfig) stateHostAddrs() []string { 417 var hosts []string 418 if cfg.Bootstrap != nil { 419 hosts = append(hosts, net.JoinHostPort( 420 "localhost", strconv.Itoa(cfg.Bootstrap.StateServingInfo.StatePort)), 421 ) 422 } 423 if cfg.Controller != nil { 424 hosts = append(hosts, cfg.Controller.MongoInfo.Addrs...) 425 } 426 return hosts 427 } 428 429 func (cfg *InstanceConfig) APIHostAddrs() []string { 430 var hosts []string 431 if cfg.Bootstrap != nil { 432 hosts = append(hosts, net.JoinHostPort( 433 "localhost", strconv.Itoa(cfg.Bootstrap.StateServingInfo.APIPort)), 434 ) 435 } 436 if cfg.APIInfo != nil { 437 hosts = append(hosts, cfg.APIInfo.Addrs...) 438 } 439 return hosts 440 } 441 442 // AgentVersion returns the version of the Juju agent that will be configured 443 // on the instance. The zero value will be returned if there are no tools set. 444 func (cfg *InstanceConfig) AgentVersion() version.Binary { 445 if len(cfg.tools) == 0 { 446 return version.Binary{} 447 } 448 return cfg.tools[0].Version 449 } 450 451 // ToolsList returns the list of tools in the order in which they will 452 // be tried. 453 func (cfg *InstanceConfig) ToolsList() coretools.List { 454 if cfg.tools == nil { 455 return nil 456 } 457 return copyToolsList(cfg.tools) 458 } 459 460 // SetTools sets the tools that should be tried when provisioning this 461 // instance. There must be at least one. Other than the URL, each item 462 // must be the same. 463 // 464 // TODO(axw) 2016-04-19 lp:1572116 465 // SetTools should verify that the tools have URLs, since they will 466 // be needed for downloading on the instance. We can't do that until 467 // all usage-sites are updated to pass through non-empty URLs. 468 func (cfg *InstanceConfig) SetTools(toolsList coretools.List) error { 469 if len(toolsList) == 0 { 470 return errors.New("need at least 1 tools") 471 } 472 var tools *coretools.Tools 473 for _, listed := range toolsList { 474 if listed == nil { 475 return errors.New("nil entry in tools list") 476 } 477 info := *listed 478 info.URL = "" 479 if tools == nil { 480 tools = &info 481 continue 482 } 483 if !reflect.DeepEqual(info, *tools) { 484 return errors.Errorf("tools info mismatch (%v, %v)", *tools, info) 485 } 486 } 487 cfg.tools = copyToolsList(toolsList) 488 return nil 489 } 490 491 func copyToolsList(in coretools.List) coretools.List { 492 out := make(coretools.List, len(in)) 493 for i, tools := range in { 494 copied := *tools 495 out[i] = &copied 496 } 497 return out 498 } 499 500 type requiresError string 501 502 func (e requiresError) Error() string { 503 return "invalid machine configuration: missing " + string(e) 504 } 505 506 // VerifyConfig verifies that the InstanceConfig is valid. 507 func (cfg *InstanceConfig) VerifyConfig() (err error) { 508 defer errors.DeferredAnnotatef(&err, "invalid machine configuration") 509 if !names.IsValidMachine(cfg.MachineId) { 510 return errors.New("invalid machine id") 511 } 512 if cfg.DataDir == "" { 513 return errors.New("missing var directory") 514 } 515 if cfg.LogDir == "" { 516 return errors.New("missing log directory") 517 } 518 if cfg.MetricsSpoolDir == "" { 519 return errors.New("missing metrics spool directory") 520 } 521 if len(cfg.Jobs) == 0 { 522 return errors.New("missing machine jobs") 523 } 524 if cfg.CloudInitOutputLog == "" { 525 return errors.New("missing cloud-init output log path") 526 } 527 if cfg.tools == nil { 528 // SetTools() has never been called successfully. 529 return errors.New("missing tools") 530 } 531 // We don't need to check cfg.toolsURLs since SetTools() does. 532 if cfg.APIInfo == nil { 533 return errors.New("missing API info") 534 } 535 if cfg.APIInfo.ModelTag.Id() == "" { 536 return errors.New("missing model tag") 537 } 538 if len(cfg.APIInfo.CACert) == 0 { 539 return errors.New("missing API CA certificate") 540 } 541 if cfg.MachineAgentServiceName == "" { 542 return errors.New("missing machine agent service name") 543 } 544 if cfg.MachineNonce == "" { 545 return errors.New("missing machine nonce") 546 } 547 if cfg.Controller != nil { 548 if err := cfg.verifyControllerConfig(); err != nil { 549 return errors.Trace(err) 550 } 551 } 552 if cfg.Bootstrap != nil { 553 if err := cfg.verifyBootstrapConfig(); err != nil { 554 return errors.Trace(err) 555 } 556 } else { 557 if cfg.APIInfo.Tag != names.NewMachineTag(cfg.MachineId) { 558 return errors.New("API entity tag must match started machine") 559 } 560 if len(cfg.APIInfo.Addrs) == 0 { 561 return errors.New("missing API hosts") 562 } 563 } 564 return nil 565 } 566 567 func (cfg *InstanceConfig) verifyBootstrapConfig() (err error) { 568 defer errors.DeferredAnnotatef(&err, "invalid bootstrap configuration") 569 if cfg.Controller == nil { 570 return errors.New("bootstrap config supplied without controller config") 571 } 572 if err := cfg.Bootstrap.VerifyConfig(); err != nil { 573 return errors.Trace(err) 574 } 575 if cfg.APIInfo.Tag != nil || cfg.Controller.MongoInfo.Tag != nil { 576 return errors.New("entity tag must be nil when bootstrapping") 577 } 578 return nil 579 } 580 581 func (cfg *InstanceConfig) verifyControllerConfig() (err error) { 582 defer errors.DeferredAnnotatef(&err, "invalid controller configuration") 583 if err := cfg.Controller.VerifyConfig(); err != nil { 584 return errors.Trace(err) 585 } 586 if cfg.Bootstrap == nil { 587 if len(cfg.Controller.MongoInfo.Addrs) == 0 { 588 return errors.New("missing state hosts") 589 } 590 if cfg.Controller.MongoInfo.Tag != names.NewMachineTag(cfg.MachineId) { 591 return errors.New("entity tag must match started machine") 592 } 593 } 594 return nil 595 } 596 597 // VerifyConfig verifies that the BootstrapConfig is valid. 598 func (cfg *BootstrapConfig) VerifyConfig() (err error) { 599 if cfg.ControllerModelConfig == nil { 600 return errors.New("missing model configuration") 601 } 602 if len(cfg.StateServingInfo.Cert) == 0 { 603 return errors.New("missing controller certificate") 604 } 605 if len(cfg.StateServingInfo.PrivateKey) == 0 { 606 return errors.New("missing controller private key") 607 } 608 if len(cfg.StateServingInfo.CAPrivateKey) == 0 { 609 return errors.New("missing ca cert private key") 610 } 611 if cfg.StateServingInfo.StatePort == 0 { 612 return errors.New("missing state port") 613 } 614 if cfg.StateServingInfo.APIPort == 0 { 615 return errors.New("missing API port") 616 } 617 if cfg.BootstrapMachineInstanceId == "" { 618 return errors.New("missing bootstrap machine instance ID") 619 } 620 if len(cfg.HostedModelConfig) == 0 { 621 return errors.New("missing hosted model config") 622 } 623 return nil 624 } 625 626 // VerifyConfig verifies that the ControllerConfig is valid. 627 func (cfg *ControllerConfig) VerifyConfig() error { 628 if cfg.MongoInfo == nil { 629 return errors.New("missing state info") 630 } 631 if len(cfg.MongoInfo.CACert) == 0 { 632 return errors.New("missing CA certificate") 633 } 634 return nil 635 } 636 637 // DefaultBridgePrefix is the prefix for all network bridge device 638 // name used for LXC and KVM containers. 639 const DefaultBridgePrefix = "br-" 640 641 // DefaultBridgeName is the network bridge device name used for LXC and KVM 642 // containers 643 const DefaultBridgeName = DefaultBridgePrefix + "eth0" 644 645 // NewInstanceConfig sets up a basic machine configuration, for a 646 // non-bootstrap node. You'll still need to supply more information, 647 // but this takes care of the fixed entries and the ones that are 648 // always needed. 649 func NewInstanceConfig( 650 controllerTag names.ControllerTag, 651 machineID, 652 machineNonce, 653 imageStream, 654 series string, 655 apiInfo *api.Info, 656 ) (*InstanceConfig, error) { 657 dataDir, err := paths.DataDir(series) 658 if err != nil { 659 return nil, err 660 } 661 logDir, err := paths.LogDir(series) 662 if err != nil { 663 return nil, err 664 } 665 metricsSpoolDir, err := paths.MetricsSpoolDir(series) 666 if err != nil { 667 return nil, err 668 } 669 cloudInitOutputLog := path.Join(logDir, "cloud-init-output.log") 670 icfg := &InstanceConfig{ 671 // Fixed entries. 672 DataDir: dataDir, 673 LogDir: path.Join(logDir, "juju"), 674 MetricsSpoolDir: metricsSpoolDir, 675 Jobs: []multiwatcher.MachineJob{multiwatcher.JobHostUnits}, 676 CloudInitOutputLog: cloudInitOutputLog, 677 MachineAgentServiceName: "jujud-" + names.NewMachineTag(machineID).String(), 678 Series: series, 679 Tags: map[string]string{}, 680 681 // Parameter entries. 682 ControllerTag: controllerTag, 683 MachineId: machineID, 684 MachineNonce: machineNonce, 685 APIInfo: apiInfo, 686 ImageStream: imageStream, 687 } 688 return icfg, nil 689 } 690 691 // NewBootstrapInstanceConfig sets up a basic machine configuration for a 692 // bootstrap node. You'll still need to supply more information, but this 693 // takes care of the fixed entries and the ones that are always needed. 694 func NewBootstrapInstanceConfig( 695 config controller.Config, 696 cons, modelCons constraints.Value, 697 series, publicImageSigningKey string, 698 ) (*InstanceConfig, error) { 699 // For a bootstrap instance, the caller must provide the state.Info 700 // and the api.Info. The machine id must *always* be "0". 701 icfg, err := NewInstanceConfig(names.NewControllerTag(config.ControllerUUID()), "0", agent.BootstrapNonce, "", series, nil) 702 if err != nil { 703 return nil, err 704 } 705 icfg.Controller = &ControllerConfig{ 706 PublicImageSigningKey: publicImageSigningKey, 707 } 708 icfg.Controller.Config = make(map[string]interface{}) 709 for k, v := range config { 710 icfg.Controller.Config[k] = v 711 } 712 icfg.Bootstrap = &BootstrapConfig{ 713 StateInitializationParams: StateInitializationParams{ 714 BootstrapMachineConstraints: cons, 715 ModelConstraints: modelCons, 716 }, 717 } 718 icfg.Jobs = []multiwatcher.MachineJob{ 719 multiwatcher.JobManageModel, 720 multiwatcher.JobHostUnits, 721 } 722 return icfg, nil 723 } 724 725 // PopulateInstanceConfig is called both from the FinishInstanceConfig below, 726 // which does have access to the environment config, and from the container 727 // provisioners, which don't have access to the environment config. Everything 728 // that is needed to provision a container needs to be returned to the 729 // provisioner in the ContainerConfig structure. Those values are then used to 730 // call this function. 731 func PopulateInstanceConfig(icfg *InstanceConfig, 732 providerType, authorizedKeys string, 733 sslHostnameVerification bool, 734 proxySettings, aptProxySettings proxy.Settings, 735 aptMirror string, 736 enableOSRefreshUpdates bool, 737 enableOSUpgrade bool, 738 ) error { 739 icfg.AuthorizedKeys = authorizedKeys 740 if icfg.AgentEnvironment == nil { 741 icfg.AgentEnvironment = make(map[string]string) 742 } 743 icfg.AgentEnvironment[agent.ProviderType] = providerType 744 icfg.AgentEnvironment[agent.ContainerType] = string(icfg.MachineContainerType) 745 icfg.DisableSSLHostnameVerification = !sslHostnameVerification 746 icfg.ProxySettings = proxySettings 747 icfg.AptProxySettings = aptProxySettings 748 icfg.AptMirror = aptMirror 749 icfg.EnableOSRefreshUpdate = enableOSRefreshUpdates 750 icfg.EnableOSUpgrade = enableOSUpgrade 751 return nil 752 } 753 754 // FinishInstanceConfig sets fields on a InstanceConfig that can be determined by 755 // inspecting a plain config.Config and the machine constraints at the last 756 // moment before creating the user-data. It assumes that the supplied Config comes 757 // from an environment that has passed through all the validation checks in the 758 // Bootstrap func, and that has set an agent-version (via finding the tools to, 759 // use for bootstrap, or otherwise). 760 // TODO(fwereade) This function is not meant to be "good" in any serious way: 761 // it is better that this functionality be collected in one place here than 762 // that it be spread out across 3 or 4 providers, but this is its only 763 // redeeming feature. 764 func FinishInstanceConfig(icfg *InstanceConfig, cfg *config.Config) (err error) { 765 defer errors.DeferredAnnotatef(&err, "cannot complete machine configuration") 766 if err := PopulateInstanceConfig( 767 icfg, 768 cfg.Type(), 769 cfg.AuthorizedKeys(), 770 cfg.SSLHostnameVerification(), 771 cfg.ProxySettings(), 772 cfg.AptProxySettings(), 773 cfg.AptMirror(), 774 cfg.EnableOSRefreshUpdate(), 775 cfg.EnableOSUpgrade(), 776 ); err != nil { 777 return errors.Trace(err) 778 } 779 if icfg.Controller != nil { 780 // Add NUMACTL preference. Needed to work for both bootstrap and high availability 781 // Only makes sense for controller 782 logger.Debugf("Setting numa ctl preference to %v", icfg.Controller.Config.NUMACtlPreference()) 783 // Unfortunately, AgentEnvironment can only take strings as values 784 icfg.AgentEnvironment[agent.NUMACtlPreference] = fmt.Sprintf("%v", icfg.Controller.Config.NUMACtlPreference()) 785 } 786 return nil 787 } 788 789 // InstanceTags returns the minimum set of tags that should be set on a 790 // machine instance, if the provider supports them. 791 func InstanceTags(modelUUID, controllerUUID string, tagger tags.ResourceTagger, jobs []multiwatcher.MachineJob) map[string]string { 792 instanceTags := tags.ResourceTags( 793 names.NewModelTag(modelUUID), 794 names.NewControllerTag(controllerUUID), 795 tagger, 796 ) 797 if multiwatcher.AnyJobNeedsState(jobs...) { 798 instanceTags[tags.JujuIsController] = "true" 799 } 800 return instanceTags 801 }