func (suite *StateSuite) TestAddStateInstance(c *gc.C) { storage := suite.newStorage(c) for _, str := range []string{"a", "b", "c"} { id := instance.Id(str) err := common.AddStateInstance(storage, instance.Id(id)) c.Assert(err, jc.ErrorIsNil) } storedState, err := common.LoadState(storage) c.Assert(err, jc.ErrorIsNil) c.Check(storedState, gc.DeepEquals, &common.BootstrapState{ StateInstances: []instance.Id{ instance.Id("a"), instance.Id("b"), instance.Id("c"), }, }) }
func (env *joyentEnviron) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { if args.InstanceConfig.HasNetworks() { return nil, errors.New("starting instances with networks is not supported yet") } series := args.Tools.OneSeries() arches := args.Tools.Arches() spec, err := env.FindInstanceSpec(&instances.InstanceConstraint{ Region: env.Ecfg().Region(), Series: series, Arches: arches, Constraints: args.Constraints, }, args.ImageMetadata) if err != nil { return nil, err } tools, err := args.Tools.Match(tools.Filter{Arch: spec.Image.Arch}) if err != nil { return nil, errors.Errorf("chosen architecture %v not present in %v", spec.Image.Arch, arches) } args.InstanceConfig.Tools = tools[0] if err := instancecfg.FinishInstanceConfig(args.InstanceConfig, env.Config()); err != nil { return nil, err } // This is a hack that ensures that instances can communicate over // the internal network. Joyent sometimes gives instances // different 10.x.x.x/21 networks and adding this route allows // them to talk despite this. See: // https://bugs.launchpad.net/juju-core/+bug/1401130 cloudcfg, err := cloudinit.New(args.InstanceConfig.Series) if err != nil { return nil, errors.Annotate(err, "cannot create cloudinit template") } ifupScript := ` #!/bin/bash # These guards help to ensure that this hack only runs if Joyent's # internal network still works as it does at time of writing. [ "$IFACE" == "eth1" ] || [ "$IFACE" == "--all" ] || exit 0 /sbin/ip -4 --oneline addr show dev eth1 | fgrep --quiet " inet 10." || exit 0 /sbin/ip route add 10.0.0.0/8 dev eth1 `[1:] cloudcfg.AddBootTextFile("/etc/network/if-up.d/joyent", ifupScript, 0755) userData, err := providerinit.ComposeUserData(args.InstanceConfig, cloudcfg, JoyentRenderer{}) if err != nil { return nil, errors.Annotate(err, "cannot make user data") } logger.Debugf("joyent user data: %d bytes", len(userData)) var machine *cloudapi.Machine machine, err = env.compute.cloudapi.CreateMachine(cloudapi.CreateMachineOpts{ //Name: env.machineFullName(machineConf.MachineId), Package: spec.InstanceType.Name, Image: spec.Image.Id, Metadata: map[string]string{"metadata.cloud-init:user-data": string(userData)}, Tags: map[string]string{"tag.group": "juju", "tag.env": env.Config().Name()}, }) if err != nil { return nil, errors.Annotate(err, "cannot create instances") } machineId := machine.Id logger.Infof("provisioning instance %q", machineId) machine, err = env.compute.cloudapi.GetMachine(machineId) if err != nil { return nil, errors.Annotate(err, "cannot start instances") } // wait for machine to start for !strings.EqualFold(machine.State, "running") { time.Sleep(1 * time.Second) machine, err = env.compute.cloudapi.GetMachine(machineId) if err != nil { return nil, errors.Annotate(err, "cannot start instances") } } logger.Infof("started instance %q", machineId) inst := &joyentInstance{ machine: machine, env: env, } if multiwatcher.AnyJobNeedsState(args.InstanceConfig.Jobs...) { if err := common.AddStateInstance(env.Storage(), inst.Id()); err != nil { logger.Errorf("could not record instance in provider-state: %v", err) } } disk64 := uint64(machine.Disk) hc := instance.HardwareCharacteristics{ Arch: &spec.Image.Arch, Mem: &spec.InstanceType.Mem, CpuCores: &spec.InstanceType.CpuCores, CpuPower: spec.InstanceType.CpuPower, RootDisk: &disk64, } return &environs.StartInstanceResult{ Instance: inst, Hardware: &hc, }, nil }