// startBootstrapNode starts the juju bootstrap node for this environment. func (env *maasEnviron) startBootstrapNode(cons constraints.Value) (instance.Instance, error) { // The bootstrap instance gets machine id "0". This is not related to // instance ids or MAAS system ids. Juju assigns the machine ID. const machineID = "0" // Create an empty bootstrap state file so we can get its URL. // If will be updated with the instance id and hardware characteristics // after the bootstrap instance is started. reader := strings.NewReader("") err := env.Storage().Put(environs.StateFile, reader, int64(0)) if err != nil { return nil, fmt.Errorf("cannot create bootstrap state file: %v", err) } stateFileURL, err := env.Storage().URL(environs.StateFile) if err != nil { return nil, fmt.Errorf("cannot create bootstrap state file: %v", err) } logger.Debugf("bootstrapping environment %q", env.Name()) possibleTools, err := environs.FindBootstrapTools(env, cons) if err != nil { return nil, err } err = environs.CheckToolsSeries(possibleTools, env.Config().DefaultSeries()) if err != nil { return nil, err } machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL) inst, err := env.internalStartInstance(cons, possibleTools, machineConfig) if err != nil { return nil, fmt.Errorf("cannot start bootstrap instance: %v", err) } return inst, nil }
func (s *ToolsSuite) TestCheckToolsSeriesAcceptsOneSetOfTools(c *C) { names := []string{"precise", "raring"} for _, series := range names { list := fakeToolsList(series) err := environs.CheckToolsSeries(list, series) c.Check(err, IsNil) } }
// TODO(bug 1199847): This work can be shared between providers. func (e *environ) Bootstrap(cons constraints.Value) error { // The bootstrap instance gets machine id "0". This is not related // to instance ids. Juju assigns the machine ID. const machineID = "0" log.Infof("environs/openstack: bootstrapping environment %q", e.name) if err := environs.VerifyBootstrapInit(e, shortAttempt); err != nil { return err } possibleTools, err := environs.FindBootstrapTools(e, cons) if err != nil { return err } err = environs.CheckToolsSeries(possibleTools, e.Config().DefaultSeries()) if err != nil { return err } // The client's authentication may have been reset by FindBootstrapTools() if the agent-version // attribute was updated so we need to re-authenticate. This will be a no-op if already authenticated. // An authenticated client is needed for the URL() call below. err = e.client.Authenticate() if err != nil { return err } stateFileURL, err := environs.CreateStateFile(e.Storage()) if err != nil { return err } machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL) // TODO(wallyworld) - save bootstrap machine metadata inst, characteristics, err := e.internalStartInstance(cons, possibleTools, machineConfig) if err != nil { return fmt.Errorf("cannot start bootstrap instance: %v", err) } err = environs.SaveState(e.Storage(), &environs.BootstrapState{ StateInstances: []instance.Id{inst.Id()}, Characteristics: []instance.HardwareCharacteristics{*characteristics}, }) if err != nil { // ignore error on StopInstance because the previous error is // more important. e.StopInstances([]instance.Instance{inst}) return fmt.Errorf("cannot save state: %v", err) } // TODO make safe in the case of racing Bootstraps // If two Bootstraps are called concurrently, there's // no way to use Swift to make sure that only one succeeds. // Perhaps consider using SimpleDB for state storage // which would enable that possibility. return nil }
// TODO(bug 1199847): This work can be shared between providers. func (e *environ) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, stateInfo *state.Info, apiInfo *api.Info) (instance.Instance, *instance.HardwareCharacteristics, error) { possibleTools, err := environs.FindInstanceTools(e, series, cons) if err != nil { return nil, nil, err } err = environs.CheckToolsSeries(possibleTools, series) if err != nil { return nil, nil, err } machineConfig := environs.NewMachineConfig(machineId, machineNonce, stateInfo, apiInfo) return e.internalStartInstance(cons, possibleTools, machineConfig) }
// TODO(bug 1199847): Much of this work can be shared between providers. func (e *environ) Bootstrap(cons constraints.Value) error { // The bootstrap instance gets machine id "0". This is not related to // instance ids. Juju assigns the machine ID. const machineID = "0" log.Infof("environs/ec2: bootstrapping environment %q", e.name) // If the state file exists, it might actually have just been // removed by Destroy, and eventual consistency has not caught // up yet, so we retry to verify if that is happening. if err := environs.VerifyBootstrapInit(e, shortAttempt); err != nil { return err } possibleTools, err := environs.FindBootstrapTools(e, cons) if err != nil { return err } err = environs.CheckToolsSeries(possibleTools, e.Config().DefaultSeries()) if err != nil { return err } stateFileURL, err := e.Storage().URL(environs.StateFile) if err != nil { return fmt.Errorf("cannot create bootstrap state file: %v", err) } machineConfig := environs.NewBootstrapMachineConfig(machineID, stateFileURL) // TODO(wallyworld) - save bootstrap machine metadata inst, characteristics, err := e.internalStartInstance(cons, possibleTools, machineConfig) if err != nil { return fmt.Errorf("cannot start bootstrap instance: %v", err) } err = environs.SaveState(e.Storage(), &environs.BootstrapState{ StateInstances: []instance.Id{inst.Id()}, Characteristics: []instance.HardwareCharacteristics{*characteristics}, }) if err != nil { // ignore error on StopInstance because the previous error is // more important. e.StopInstances([]instance.Instance{inst}) return fmt.Errorf("cannot save state: %v", err) } // TODO make safe in the case of racing Bootstraps // If two Bootstraps are called concurrently, there's // no way to use S3 to make sure that only one succeeds. // Perhaps consider using SimpleDB for state storage // which would enable that possibility. return nil }
// StartInstance is specified in the Environ interface. // TODO(bug 1199847): This work can be shared between providers. func (env *azureEnviron) StartInstance(machineID, machineNonce string, series string, cons constraints.Value, stateInfo *state.Info, apiInfo *api.Info) (instance.Instance, *instance.HardwareCharacteristics, error) { possibleTools, err := environs.FindInstanceTools(env, series, cons) if err != nil { return nil, nil, err } err = environs.CheckToolsSeries(possibleTools, series) if err != nil { return nil, nil, err } machineConfig := environs.NewMachineConfig(machineID, machineNonce, stateInfo, apiInfo) // TODO(bug 1193998) - return instance hardware characteristics as well. inst, err := env.internalStartInstance(cons, possibleTools, machineConfig) return inst, nil, err }
func (e *environ) StartInstance(machineId, machineNonce string, series string, cons constraints.Value, info *state.Info, apiInfo *api.Info) (instance.Instance, *instance.HardwareCharacteristics, error) { defer delay() log.Infof("environs/dummy: dummy startinstance, machine %s", machineId) if err := e.checkBroken("StartInstance"); err != nil { return nil, nil, err } possibleTools, err := environs.FindInstanceTools(e, series, cons) if err != nil { return nil, nil, err } err = environs.CheckToolsSeries(possibleTools, series) if err != nil { return nil, nil, err } log.Infof("environs/dummy: would pick tools from %s", possibleTools) e.state.mu.Lock() defer e.state.mu.Unlock() if machineNonce == "" { return nil, nil, fmt.Errorf("cannot start instance: missing machine nonce") } if _, ok := e.Config().CACert(); !ok { return nil, nil, fmt.Errorf("no CA certificate in environment configuration") } if info.Tag != names.MachineTag(machineId) { return nil, nil, fmt.Errorf("entity tag must match started machine") } if apiInfo.Tag != names.MachineTag(machineId) { return nil, nil, fmt.Errorf("entity tag must match started machine") } i := &dummyInstance{ state: e.state, id: instance.Id(fmt.Sprintf("%s-%d", e.state.name, e.state.maxId)), ports: make(map[instance.Port]bool), machineId: machineId, series: series, } var hc *instance.HardwareCharacteristics // To match current system capability, only provide hardware characteristics for // environ machines, not containers. if state.ParentId(machineId) == "" { // We will just assume the instance hardware characteristics exactly matches // the supplied constraints (if specified). hc = &instance.HardwareCharacteristics{ Arch: cons.Arch, Mem: cons.Mem, CpuCores: cons.CpuCores, CpuPower: cons.CpuPower, } // Fill in some expected instance hardware characteristics if constraints not specified. if hc.Arch == nil { arch := "amd64" hc.Arch = &arch } if hc.Mem == nil { mem := uint64(1024) hc.Mem = &mem } if hc.CpuCores == nil { cores := uint64(1) hc.CpuCores = &cores } } e.state.insts[i.id] = i e.state.maxId++ e.state.ops <- OpStartInstance{ Env: e.state.name, MachineId: machineId, MachineNonce: machineNonce, Constraints: cons, Instance: i, Info: info, APIInfo: apiInfo, Secret: e.ecfg().secret(), } return i, hc, nil }
func (s *ToolsSuite) TestCheckToolsSeriesRejectsToolsForMixedSeries(c *C) { list := fakeToolsList("precise", "raring") err := environs.CheckToolsSeries(list, "precise") c.Assert(err, NotNil) c.Check(err, ErrorMatches, "expected single series, got .*") }
func (s *ToolsSuite) TestCheckToolsSeriesRejectsToolsForOtherSeries(c *C) { list := fakeToolsList("hoary") err := environs.CheckToolsSeries(list, "warty") c.Assert(err, NotNil) c.Check(err, ErrorMatches, "tools mismatch: expected series warty, got hoary") }
func (s *ToolsSuite) TestCheckToolsSeriesAcceptsMultipleForSameSeries(c *C) { series := "quantal" list := fakeToolsList(series, series, series) err := environs.CheckToolsSeries(list, series) c.Check(err, IsNil) }
func (s *ToolsSuite) TestCheckToolsSeriesRequiresTools(c *C) { err := environs.CheckToolsSeries(fakeToolsList(), "precise") c.Assert(err, NotNil) c.Check(err, ErrorMatches, "expected single series, got \\[\\]") }