// machineTags returns machine-specific tags to set on the instance. func (p *ProvisionerAPI) machineTags(m *state.Machine, jobs []multiwatcher.MachineJob) (map[string]string, error) { // Names of all units deployed to the machine. // // TODO(axw) 2015-06-02 #1461358 // We need a worker that periodically updates // instance tags with current deployment info. units, err := m.Units() if err != nil { return nil, errors.Trace(err) } unitNames := make([]string, 0, len(units)) for _, unit := range units { if !unit.IsPrincipal() { continue } unitNames = append(unitNames, unit.Name()) } sort.Strings(unitNames) cfg, err := p.st.EnvironConfig() if err != nil { return nil, errors.Trace(err) } machineTags := instancecfg.InstanceTags(cfg, jobs) if len(unitNames) > 0 { machineTags[tags.JujuUnitsDeployed] = strings.Join(unitNames, " ") } return machineTags, nil }
// StartInstanceWithParams is a test helper function that starts an instance // with the given parameters, and a plausible but invalid configuration, and // returns the result of Environ.StartInstance. The provided params's // InstanceConfig and Tools field values will be ignored. func StartInstanceWithParams( env environs.Environ, machineId string, params environs.StartInstanceParams, ) ( *environs.StartInstanceResult, error, ) { preferredSeries := config.PreferredSeries(env.Config()) agentVersion, ok := env.Config().AgentVersion() if !ok { return nil, errors.New("missing agent version in model config") } filter := coretools.Filter{ Number: agentVersion, Series: preferredSeries, } if params.Constraints.Arch != nil { filter.Arch = *params.Constraints.Arch } stream := tools.PreferredStream(&agentVersion, env.Config().Development(), env.Config().AgentStream()) possibleTools, err := tools.FindTools(env, -1, -1, stream, filter) if err != nil { return nil, errors.Trace(err) } if params.ImageMetadata == nil { if err := SetImageMetadata( env, possibleTools.AllSeries(), possibleTools.Arches(), ¶ms.ImageMetadata, ); err != nil { return nil, errors.Trace(err) } } machineNonce := "fake_nonce" stateInfo := FakeStateInfo(machineId) apiInfo := FakeAPIInfo(machineId) instanceConfig, err := instancecfg.NewInstanceConfig( machineId, machineNonce, imagemetadata.ReleasedStream, preferredSeries, "", true, stateInfo, apiInfo, ) if err != nil { return nil, errors.Trace(err) } instanceConfig.Tags = instancecfg.InstanceTags(env.Config(), nil) params.Tools = possibleTools params.InstanceConfig = instanceConfig return env.StartInstance(params) }
func addInstanceTags(env environs.Environ, machines []*state.Machine) error { cfg := env.Config() tagger, ok := env.(environs.InstanceTagger) if !ok { logger.Debugf("environment type %q does not support instance tagging", cfg.Type()) return nil } // Tag each top-level, provisioned machine. logger.Infof("adding tags to existing machine instances") for _, m := range machines { if names.IsContainerMachine(m.Id()) { continue } instId, err := m.InstanceId() if errors.IsNotProvisioned(err) { continue } else if err != nil { return errors.Annotatef(err, "getting instance ID for machine %v", m.Id()) } stateMachineJobs := m.Jobs() paramsMachineJobs := make([]multiwatcher.MachineJob, len(stateMachineJobs)) for i, job := range stateMachineJobs { paramsMachineJobs[i] = job.ToParams() } tags := instancecfg.InstanceTags(cfg, paramsMachineJobs) logger.Infof("tagging instance %v: %v", instId, tags) if err := tagger.TagInstance(instId, tags); err != nil { return errors.Annotatef(err, "tagging instance %v for machine %v", instId, m.Id()) } } return nil }
func testInstanceTags(c *gc.C, cfg *config.Config, jobs []multiwatcher.MachineJob, expectTags map[string]string) { tags := instancecfg.InstanceTags(testing.ModelTag.Id(), testing.ControllerTag.Id(), cfg, jobs) c.Assert(tags, jc.DeepEquals, expectTags) }
// BootstrapInstance creates a new instance with the series of its choice, // constrained to those of the available tools, and // returns the instance result, series, and a function that // must be called to finalize the bootstrap process by transferring // the tools and installing the initial Juju controller. // This method is called by Bootstrap above, which implements environs.Bootstrap, but // is also exported so that providers can manipulate the started instance. func BootstrapInstance(ctx environs.BootstrapContext, env environs.Environ, args environs.BootstrapParams, ) (_ *environs.StartInstanceResult, selectedSeries string, _ environs.BootstrapFinalizer, err error) { // TODO make safe in the case of racing Bootstraps // If two Bootstraps are called concurrently, there's // no way to make sure that only one succeeds. // First thing, ensure we have tools otherwise there's no point. if args.BootstrapSeries != "" { selectedSeries = args.BootstrapSeries } else { selectedSeries = config.PreferredSeries(env.Config()) } availableTools, err := args.AvailableTools.Match(coretools.Filter{ Series: selectedSeries, }) if err != nil { return nil, "", nil, err } // Filter image metadata to the selected series. var imageMetadata []*imagemetadata.ImageMetadata seriesVersion, err := series.SeriesVersion(selectedSeries) if err != nil { return nil, "", nil, errors.Trace(err) } for _, m := range args.ImageMetadata { if m.Version != seriesVersion { continue } imageMetadata = append(imageMetadata, m) } // Get the bootstrap SSH client. Do this early, so we know // not to bother with any of the below if we can't finish the job. client := ssh.DefaultClient if client == nil { // This should never happen: if we don't have OpenSSH, then // go.crypto/ssh should be used with an auto-generated key. return nil, "", nil, fmt.Errorf("no SSH client available") } publicKey, err := simplestreams.UserPublicSigningKey() if err != nil { return nil, "", nil, err } envCfg := env.Config() instanceConfig, err := instancecfg.NewBootstrapInstanceConfig( args.ControllerConfig, args.BootstrapConstraints, args.ModelConstraints, selectedSeries, publicKey, ) if err != nil { return nil, "", nil, err } instanceConfig.EnableOSRefreshUpdate = env.Config().EnableOSRefreshUpdate() instanceConfig.EnableOSUpgrade = env.Config().EnableOSUpgrade() instanceConfig.Tags = instancecfg.InstanceTags(envCfg.UUID(), args.ControllerConfig.ControllerUUID(), envCfg, instanceConfig.Jobs) maybeSetBridge := func(icfg *instancecfg.InstanceConfig) { // If we need to override the default bridge name, do it now. When // args.ContainerBridgeName is empty, the default names for LXC // (lxcbr0) and KVM (virbr0) will be used. if args.ContainerBridgeName != "" { logger.Debugf("using %q as network bridge for all container types", args.ContainerBridgeName) if icfg.AgentEnvironment == nil { icfg.AgentEnvironment = make(map[string]string) } icfg.AgentEnvironment[agent.LxcBridge] = args.ContainerBridgeName } } maybeSetBridge(instanceConfig) cloudRegion := args.CloudName if args.CloudRegion != "" { cloudRegion += "/" + args.CloudRegion } fmt.Fprintf(ctx.GetStderr(), "Launching controller instance(s) on %s...\n", cloudRegion) // Print instance status reports status changes during provisioning. // Note the carriage returns, meaning subsequent prints are to the same // line of stderr, not a new line. instanceStatus := func(settableStatus status.Status, info string, data map[string]interface{}) error { // The data arg is not expected to be used in this case, but // print it, rather than ignore it, if we get something. dataString := "" if len(data) > 0 { dataString = fmt.Sprintf(" %v", data) } fmt.Fprintf(ctx.GetStderr(), " - %s%s\r", info, dataString) return nil } // Likely used after the final instanceStatus call to white-out the // current stderr line before the next use, removing any residual status // reporting output. statusCleanup := func(info string) error { // The leading spaces account for the leading characters // emitted by instanceStatus above. fmt.Fprintf(ctx.GetStderr(), " %s\r", info) return nil } result, err := env.StartInstance(environs.StartInstanceParams{ ControllerUUID: args.ControllerConfig.ControllerUUID(), Constraints: args.BootstrapConstraints, Tools: availableTools, InstanceConfig: instanceConfig, Placement: args.Placement, ImageMetadata: imageMetadata, StatusCallback: instanceStatus, CleanupCallback: statusCleanup, }) if err != nil { return nil, "", nil, errors.Annotate(err, "cannot start bootstrap instance") } // We need some padding below to overwrite any previous messages. We'll use a width of 40. msg := fmt.Sprintf(" - %s", result.Instance.Id()) if len(msg) < 40 { padding := make([]string, 40-len(msg)) msg += strings.Join(padding, " ") } fmt.Fprintln(ctx.GetStderr(), msg) finalize := func(ctx environs.BootstrapContext, icfg *instancecfg.InstanceConfig, opts environs.BootstrapDialOpts) error { icfg.Bootstrap.BootstrapMachineInstanceId = result.Instance.Id() icfg.Bootstrap.BootstrapMachineHardwareCharacteristics = result.Hardware envConfig := env.Config() if result.Config != nil { updated, err := envConfig.Apply(result.Config.UnknownAttrs()) if err != nil { return errors.Trace(err) } envConfig = updated } if err := instancecfg.FinishInstanceConfig(icfg, envConfig); err != nil { return err } maybeSetBridge(icfg) return FinishBootstrap(ctx, client, env, result.Instance, icfg, opts) } return result, selectedSeries, finalize, nil }
// BootstrapInstance creates a new instance with the series and architecture // of its choice, constrained to those of the available tools, and // returns the instance result, series, and a function that // must be called to finalize the bootstrap process by transferring // the tools and installing the initial Juju state server. // This method is called by Bootstrap above, which implements environs.Bootstrap, but // is also exported so that providers can manipulate the started instance. func BootstrapInstance(ctx environs.BootstrapContext, env environs.Environ, args environs.BootstrapParams, ) (_ *environs.StartInstanceResult, series string, _ environs.BootstrapFinalizer, err error) { // TODO make safe in the case of racing Bootstraps // If two Bootstraps are called concurrently, there's // no way to make sure that only one succeeds. // First thing, ensure we have tools otherwise there's no point. series = config.PreferredSeries(env.Config()) availableTools, err := args.AvailableTools.Match(coretools.Filter{Series: series}) if err != nil { return nil, "", nil, err } // Get the bootstrap SSH client. Do this early, so we know // not to bother with any of the below if we can't finish the job. client := ssh.DefaultClient if client == nil { // This should never happen: if we don't have OpenSSH, then // go.crypto/ssh should be used with an auto-generated key. return nil, "", nil, fmt.Errorf("no SSH client available") } instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(args.Constraints, series) if err != nil { return nil, "", nil, err } instanceConfig.EnableOSRefreshUpdate = env.Config().EnableOSRefreshUpdate() instanceConfig.EnableOSUpgrade = env.Config().EnableOSUpgrade() instanceConfig.Tags = instancecfg.InstanceTags(env.Config(), instanceConfig.Jobs) maybeSetBridge := func(icfg *instancecfg.InstanceConfig) { // If we need to override the default bridge name, do it now. When // args.ContainerBridgeName is empty, the default names for LXC // (lxcbr0) and KVM (virbr0) will be used. if args.ContainerBridgeName != "" { logger.Debugf("using %q as network bridge for all container types", args.ContainerBridgeName) if icfg.AgentEnvironment == nil { icfg.AgentEnvironment = make(map[string]string) } icfg.AgentEnvironment[agent.LxcBridge] = args.ContainerBridgeName } } maybeSetBridge(instanceConfig) fmt.Fprintln(ctx.GetStderr(), "Launching instance") result, err := env.StartInstance(environs.StartInstanceParams{ Constraints: args.Constraints, Tools: availableTools, InstanceConfig: instanceConfig, Placement: args.Placement, }) if err != nil { return nil, "", nil, errors.Annotate(err, "cannot start bootstrap instance") } fmt.Fprintf(ctx.GetStderr(), " - %s\n", result.Instance.Id()) finalize := func(ctx environs.BootstrapContext, icfg *instancecfg.InstanceConfig) error { icfg.InstanceId = result.Instance.Id() icfg.HardwareCharacteristics = result.Hardware if err := instancecfg.FinishInstanceConfig(icfg, env.Config()); err != nil { return err } maybeSetBridge(icfg) return FinishBootstrap(ctx, client, result.Instance, icfg) } return result, series, finalize, nil }
// BootstrapInstance creates a new instance with the series and architecture // of its choice, constrained to those of the available tools, and // returns the instance result, series, and a function that // must be called to finalize the bootstrap process by transferring // the tools and installing the initial Juju controller. // This method is called by Bootstrap above, which implements environs.Bootstrap, but // is also exported so that providers can manipulate the started instance. func BootstrapInstance(ctx environs.BootstrapContext, env environs.Environ, args environs.BootstrapParams, ) (_ *environs.StartInstanceResult, selectedSeries string, _ environs.BootstrapFinalizer, err error) { // TODO make safe in the case of racing Bootstraps // If two Bootstraps are called concurrently, there's // no way to make sure that only one succeeds. // First thing, ensure we have tools otherwise there's no point. if args.BootstrapSeries != "" { selectedSeries = args.BootstrapSeries } else { selectedSeries = config.PreferredSeries(env.Config()) } availableTools, err := args.AvailableTools.Match(coretools.Filter{ Series: selectedSeries, }) if err != nil { return nil, "", nil, err } // Filter image metadata to the selected series. var imageMetadata []*imagemetadata.ImageMetadata seriesVersion, err := series.SeriesVersion(selectedSeries) if err != nil { return nil, "", nil, errors.Trace(err) } for _, m := range args.ImageMetadata { if m.Version != seriesVersion { continue } imageMetadata = append(imageMetadata, m) } // Get the bootstrap SSH client. Do this early, so we know // not to bother with any of the below if we can't finish the job. client := ssh.DefaultClient if client == nil { // This should never happen: if we don't have OpenSSH, then // go.crypto/ssh should be used with an auto-generated key. return nil, "", nil, fmt.Errorf("no SSH client available") } publicKey, err := simplestreams.UserPublicSigningKey() if err != nil { return nil, "", nil, err } instanceConfig, err := instancecfg.NewBootstrapInstanceConfig( args.BootstrapConstraints, args.ModelConstraints, selectedSeries, publicKey, ) if err != nil { return nil, "", nil, err } instanceConfig.EnableOSRefreshUpdate = env.Config().EnableOSRefreshUpdate() instanceConfig.EnableOSUpgrade = env.Config().EnableOSUpgrade() instanceConfig.Tags = instancecfg.InstanceTags(env.Config(), instanceConfig.Jobs) maybeSetBridge := func(icfg *instancecfg.InstanceConfig) { // If we need to override the default bridge name, do it now. When // args.ContainerBridgeName is empty, the default names for LXC // (lxcbr0) and KVM (virbr0) will be used. if args.ContainerBridgeName != "" { logger.Debugf("using %q as network bridge for all container types", args.ContainerBridgeName) if icfg.AgentEnvironment == nil { icfg.AgentEnvironment = make(map[string]string) } icfg.AgentEnvironment[agent.LxcBridge] = args.ContainerBridgeName } } maybeSetBridge(instanceConfig) fmt.Fprintln(ctx.GetStderr(), "Launching instance") instanceStatus := func(settableStatus status.Status, info string, data map[string]interface{}) error { fmt.Fprintf(ctx.GetStderr(), "%s \r", info) return nil } result, err := env.StartInstance(environs.StartInstanceParams{ Constraints: args.BootstrapConstraints, Tools: availableTools, InstanceConfig: instanceConfig, Placement: args.Placement, ImageMetadata: imageMetadata, StatusCallback: instanceStatus, }) if err != nil { return nil, "", nil, errors.Annotate(err, "cannot start bootstrap instance") } fmt.Fprintf(ctx.GetStderr(), " - %s\n", result.Instance.Id()) finalize := func(ctx environs.BootstrapContext, icfg *instancecfg.InstanceConfig) error { icfg.InstanceId = result.Instance.Id() icfg.HardwareCharacteristics = result.Hardware envConfig := env.Config() if result.Config != nil { updated, err := envConfig.Apply(result.Config.UnknownAttrs()) if err != nil { return errors.Trace(err) } envConfig = updated } if err := instancecfg.FinishInstanceConfig(icfg, envConfig); err != nil { return err } maybeSetBridge(icfg) return FinishBootstrap(ctx, client, env, result.Instance, icfg) } return result, selectedSeries, finalize, nil }
func fillinStartInstanceParams(env environs.Environ, machineId string, isController bool, params *environs.StartInstanceParams) error { if params.ControllerUUID == "" { return errors.New("missing controller UUID in start instance parameters") } preferredSeries := config.PreferredSeries(env.Config()) agentVersion, ok := env.Config().AgentVersion() if !ok { return errors.New("missing agent version in model config") } filter := coretools.Filter{ Number: agentVersion, Series: preferredSeries, } if params.Constraints.Arch != nil { filter.Arch = *params.Constraints.Arch } stream := tools.PreferredStream(&agentVersion, env.Config().Development(), env.Config().AgentStream()) possibleTools, err := tools.FindTools(env, -1, -1, stream, filter) if err != nil { return errors.Trace(err) } if params.ImageMetadata == nil { if err := SetImageMetadata( env, possibleTools.AllSeries(), possibleTools.Arches(), ¶ms.ImageMetadata, ); err != nil { return errors.Trace(err) } } machineNonce := "fake_nonce" apiInfo := FakeAPIInfo(machineId) instanceConfig, err := instancecfg.NewInstanceConfig( testing.ControllerTag, machineId, machineNonce, imagemetadata.ReleasedStream, preferredSeries, apiInfo, ) if err != nil { return errors.Trace(err) } if isController { instanceConfig.Controller = &instancecfg.ControllerConfig{ Config: testing.FakeControllerConfig(), MongoInfo: &mongo.MongoInfo{ Info: mongo.Info{ Addrs: []string{"127.0.0.1:1234"}, CACert: "CA CERT\n" + testing.CACert, }, Password: "******", Tag: names.NewMachineTag(machineId), }, } instanceConfig.Jobs = []multiwatcher.MachineJob{multiwatcher.JobHostUnits, multiwatcher.JobManageModel} } cfg := env.Config() instanceConfig.Tags = instancecfg.InstanceTags(env.Config().UUID(), params.ControllerUUID, cfg, nil) params.Tools = possibleTools params.InstanceConfig = instanceConfig return nil }