// FinishMachineConfig sets fields on a MachineConfig that can be determined by // inspecting a plain config.Config and the machine constraints at the last // moment before bootstrapping. It assumes that the supplied Config comes from // an environment that has passed through all the validation checks in the // Bootstrap func, and that has set an agent-version (via finding the tools to, // use for bootstrap, or otherwise). // TODO(fwereade) This function is not meant to be "good" in any serious way: // it is better that this functionality be collected in one place here than // that it be spread out across 3 or 4 providers, but this is its only // redeeming feature. func FinishMachineConfig(mcfg *cloudinit.MachineConfig, cfg *config.Config) (err error) { defer errors.Maskf(&err, "cannot complete machine configuration") if err := PopulateMachineConfig( mcfg, cfg.Type(), cfg.AuthorizedKeys(), cfg.SSLHostnameVerification(), cfg.ProxySettings(), cfg.AptProxySettings(), cfg.PreferIPv6(), cfg.EnableOSRefreshUpdate(), cfg.EnableOSUpgrade(), ); err != nil { return err } // The following settings are only appropriate at bootstrap time. At the // moment, the only state server is the bootstrap node, but this // will probably change. if !mcfg.Bootstrap { return nil } if mcfg.APIInfo != nil || mcfg.MongoInfo != nil { return fmt.Errorf("machine configuration already has api/state info") } caCert, hasCACert := cfg.CACert() if !hasCACert { return fmt.Errorf("environment configuration has no ca-cert") } password := cfg.AdminSecret() if password == "" { return fmt.Errorf("environment configuration has no admin-secret") } passwordHash := utils.UserPasswordHash(password, utils.CompatSalt) mcfg.APIInfo = &api.Info{Password: passwordHash, CACert: caCert} mcfg.MongoInfo = &mongo.MongoInfo{Password: passwordHash, Info: mongo.Info{CACert: caCert}} // These really are directly relevant to running a state server. cert, key, err := cfg.GenerateStateServerCertAndKey() if err != nil { return errors.Annotate(err, "cannot generate state server certificate") } srvInfo := params.StateServingInfo{ StatePort: cfg.StatePort(), APIPort: cfg.APIPort(), Cert: string(cert), PrivateKey: string(key), } mcfg.StateServingInfo = &srvInfo if mcfg.Config, err = BootstrapConfig(cfg); err != nil { return err } return nil }
// FinishInstanceConfig sets fields on a InstanceConfig that can be determined by // inspecting a plain config.Config and the machine constraints at the last // moment before creating the user-data. It assumes that the supplied Config comes // from an environment that has passed through all the validation checks in the // Bootstrap func, and that has set an agent-version (via finding the tools to, // use for bootstrap, or otherwise). // TODO(fwereade) This function is not meant to be "good" in any serious way: // it is better that this functionality be collected in one place here than // that it be spread out across 3 or 4 providers, but this is its only // redeeming feature. func FinishInstanceConfig(icfg *InstanceConfig, cfg *config.Config) (err error) { defer errors.DeferredAnnotatef(&err, "cannot complete machine configuration") if err := PopulateInstanceConfig( icfg, cfg.Type(), cfg.AuthorizedKeys(), cfg.SSLHostnameVerification(), cfg.ProxySettings(), cfg.AptProxySettings(), cfg.AptMirror(), cfg.EnableOSRefreshUpdate(), cfg.EnableOSUpgrade(), ); err != nil { return errors.Trace(err) } if icfg.Controller != nil { // Add NUMACTL preference. Needed to work for both bootstrap and high availability // Only makes sense for controller logger.Debugf("Setting numa ctl preference to %v", icfg.Controller.Config.NUMACtlPreference()) // Unfortunately, AgentEnvironment can only take strings as values icfg.AgentEnvironment[agent.NUMACtlPreference] = fmt.Sprintf("%v", icfg.Controller.Config.NUMACtlPreference()) } return nil }
// FinishInstanceConfig sets fields on a InstanceConfig that can be determined by // inspecting a plain config.Config and the machine constraints at the last // moment before bootstrapping. It assumes that the supplied Config comes from // an environment that has passed through all the validation checks in the // Bootstrap func, and that has set an agent-version (via finding the tools to, // use for bootstrap, or otherwise). // TODO(fwereade) This function is not meant to be "good" in any serious way: // it is better that this functionality be collected in one place here than // that it be spread out across 3 or 4 providers, but this is its only // redeeming feature. func FinishInstanceConfig(icfg *InstanceConfig, cfg *config.Config) (err error) { defer errors.DeferredAnnotatef(&err, "cannot complete machine configuration") if err := PopulateInstanceConfig( icfg, cfg.Type(), cfg.AuthorizedKeys(), cfg.SSLHostnameVerification(), cfg.ProxySettings(), cfg.AptProxySettings(), cfg.AptMirror(), cfg.PreferIPv6(), cfg.EnableOSRefreshUpdate(), cfg.EnableOSUpgrade(), ); err != nil { return errors.Trace(err) } if isStateInstanceConfig(icfg) { // Add NUMACTL preference. Needed to work for both bootstrap and high availability // Only makes sense for controller logger.Debugf("Setting numa ctl preference to %v", cfg.NumaCtlPreference()) // Unfortunately, AgentEnvironment can only take strings as values icfg.AgentEnvironment[agent.NumaCtlPreference] = fmt.Sprintf("%v", cfg.NumaCtlPreference()) } // The following settings are only appropriate at bootstrap time. At the // moment, the only controller is the bootstrap node, but this // will probably change. if !icfg.Bootstrap { return nil } if icfg.APIInfo != nil || icfg.MongoInfo != nil { return errors.New("machine configuration already has api/state info") } caCert, hasCACert := cfg.CACert() if !hasCACert { return errors.New("model configuration has no ca-cert") } password := cfg.AdminSecret() if password == "" { return errors.New("model configuration has no admin-secret") } icfg.APIInfo = &api.Info{ Password: password, CACert: caCert, ModelTag: names.NewModelTag(cfg.UUID()), } icfg.MongoInfo = &mongo.MongoInfo{Password: password, Info: mongo.Info{CACert: caCert}} // These really are directly relevant to running a controller. // Initially, generate a controller certificate with no host IP // addresses in the SAN field. Once the controller is up and the // NIC addresses become known, the certificate can be regenerated. cert, key, err := cfg.GenerateControllerCertAndKey(nil) if err != nil { return errors.Annotate(err, "cannot generate controller certificate") } caPrivateKey, hasCAPrivateKey := cfg.CAPrivateKey() if !hasCAPrivateKey { return errors.New("model configuration has no ca-private-key") } srvInfo := params.StateServingInfo{ StatePort: cfg.StatePort(), APIPort: cfg.APIPort(), Cert: string(cert), PrivateKey: string(key), CAPrivateKey: caPrivateKey, } icfg.StateServingInfo = &srvInfo if icfg.Config, err = bootstrapConfig(cfg); err != nil { return errors.Trace(err) } return nil }
func (c *addCommand) Run(ctx *cmd.Context) error { client, err := c.getClientAPI() if err != nil { return errors.Trace(err) } defer client.Close() var machineManager MachineManagerAPI if len(c.Disks) > 0 { machineManager, err = c.getMachineManagerAPI() if err != nil { return errors.Trace(err) } defer machineManager.Close() if machineManager.BestAPIVersion() < 1 { return errors.New("cannot add machines with disks: not supported by the API server") } } logger.Infof("load config") var config *config.Config if defaultStore, err := configstore.Default(); err != nil { return err } else if config, err = c.Config(defaultStore, client); err != nil { return err } if c.Placement != nil && c.Placement.Scope == "ssh" { logger.Infof("manual provisioning") args := manual.ProvisionMachineArgs{ Host: c.Placement.Directive, Client: client, Stdin: ctx.Stdin, Stdout: ctx.Stdout, Stderr: ctx.Stderr, UpdateBehavior: ¶ms.UpdateBehavior{ config.EnableOSRefreshUpdate(), config.EnableOSUpgrade(), }, } machineId, err := manualProvisioner(args) if err == nil { ctx.Infof("created machine %v", machineId) } return err } logger.Infof("environment provisioning") if c.Placement != nil && c.Placement.Scope == "env-uuid" { c.Placement.Scope = client.EnvironmentUUID() } if c.Placement != nil && c.Placement.Scope == instance.MachineScope { // It does not make sense to add-machine <id>. return fmt.Errorf("machine-id cannot be specified when adding machines") } jobs := []multiwatcher.MachineJob{multiwatcher.JobHostUnits} envVersion, err := envcmd.GetEnvironmentVersion(client) if err != nil { return err } // Servers before 1.21-alpha2 don't have the networker so don't // try to use JobManageNetworking with them. // // In case of MAAS and Joyent JobManageNetworking is not added // to ensure the non-intrusive start of a networker like above // for the manual provisioning. See this related joyent bug // http://pad.lv/1401423 if envVersion.Compare(version.MustParse("1.21-alpha2")) >= 0 && config.Type() != provider.MAAS && config.Type() != provider.Joyent { jobs = append(jobs, multiwatcher.JobManageNetworking) } machineParams := params.AddMachineParams{ Placement: c.Placement, Series: c.Series, Constraints: c.Constraints, Jobs: jobs, Disks: c.Disks, } machines := make([]params.AddMachineParams, c.NumMachines) for i := 0; i < c.NumMachines; i++ { machines[i] = machineParams } var results []params.AddMachinesResult // If storage is specified, we attempt to use a new API on the service facade. if len(c.Disks) > 0 { results, err = machineManager.AddMachines(machines) } else { results, err = client.AddMachines(machines) if params.IsCodeNotImplemented(err) { if c.Placement != nil { containerType, parseErr := instance.ParseContainerType(c.Placement.Scope) if parseErr != nil { // The user specified a non-container placement directive: // return original API not implemented error. return err } machineParams.ContainerType = containerType machineParams.ParentId = c.Placement.Directive machineParams.Placement = nil } logger.Infof( "AddMachinesWithPlacement not supported by the API server, " + "falling back to 1.18 compatibility mode", ) results, err = client.AddMachines1dot18([]params.AddMachineParams{machineParams}) } } if params.IsCodeOperationBlocked(err) { return block.ProcessBlockedError(err, block.BlockChange) } if err != nil { return errors.Trace(err) } errs := []error{} for _, machineInfo := range results { if machineInfo.Error != nil { errs = append(errs, machineInfo.Error) continue } machineId := machineInfo.Machine if names.IsContainerMachine(machineId) { ctx.Infof("created container %v", machineId) } else { ctx.Infof("created machine %v", machineId) } } if len(errs) == 1 { fmt.Fprintf(ctx.Stderr, "failed to create 1 machine\n") return errs[0] } if len(errs) > 1 { fmt.Fprintf(ctx.Stderr, "failed to create %d machines\n", len(errs)) returnErr := []string{} for _, e := range errs { returnErr = append(returnErr, e.Error()) } return errors.New(strings.Join(returnErr, ", ")) } return nil }
func (c *AddMachineCommand) Run(ctx *cmd.Context) error { client, err := getAddMachineAPI(c) if err != nil { return errors.Trace(err) } defer client.Close() if c.Placement != nil && c.Placement.Scope == "ssh" { var config *config.Config if defaultStore, err := configstore.Default(); err != nil { return err } else if config, err = c.Config(defaultStore); err != nil { return err } args := manual.ProvisionMachineArgs{ Host: c.Placement.Directive, Client: client, Stdin: ctx.Stdin, Stdout: ctx.Stdout, Stderr: ctx.Stderr, UpdateBehavior: ¶ms.UpdateBehavior{ config.EnableOSRefreshUpdate(), config.EnableOSUpgrade(), }, } machineId, err := manualProvisioner(args) if err == nil { ctx.Infof("created machine %v", machineId) } return err } if c.Placement != nil && c.Placement.Scope == "env-uuid" { c.Placement.Scope = client.EnvironmentUUID() } if c.Placement != nil && c.Placement.Scope == instance.MachineScope { // It does not make sense to add-machine <id>. return fmt.Errorf("machine-id cannot be specified when adding machines") } machineParams := params.AddMachineParams{ Placement: c.Placement, Series: c.Series, Constraints: c.Constraints, Jobs: []params.MachineJob{params.JobHostUnits}, } machines := make([]params.AddMachineParams, c.NumMachines) for i := 0; i < c.NumMachines; i++ { machines[i] = machineParams } results, err := client.AddMachines(machines) if err != nil { return errors.Trace(err) } errs := []error{} for _, machineInfo := range results { if machineInfo.Error != nil { errs = append(errs, machineInfo.Error) continue } machineId := machineInfo.Machine if names.IsContainerMachine(machineId) { ctx.Infof("created container %v", machineId) } else { ctx.Infof("created machine %v", machineId) } } if len(errs) == 1 { fmt.Fprintf(ctx.Stderr, "failed to create 1 machine\n") return errs[0] } if len(errs) > 1 { fmt.Fprintf(ctx.Stderr, "failed to create %d machines\n", len(errs)) returnErr := []string{} for _, e := range errs { returnErr = append(returnErr, fmt.Sprintf("%s", e)) } return errors.New(strings.Join(returnErr, ", ")) } return nil }