func (e *environ) StartInstance(machineId string, info *state.Info, tools *state.Tools) (environs.Instance, error) { defer delay() log.Printf("environs/dummy: dummy startinstance, machine %s", machineId) if err := e.checkBroken("StartInstance"); err != nil { return nil, err } e.state.mu.Lock() defer e.state.mu.Unlock() if _, ok := e.Config().CACert(); !ok { return nil, fmt.Errorf("no CA certificate in environment configuration") } if info.EntityName != state.MachineEntityName(machineId) { return nil, fmt.Errorf("entity name must match started machine") } if tools != nil && (strings.HasPrefix(tools.Series, "unknown") || strings.HasPrefix(tools.Arch, "unknown")) { return nil, fmt.Errorf("cannot find image for %s-%s", tools.Series, tools.Arch) } i := &instance{ state: e.state, id: state.InstanceId(fmt.Sprintf("%s-%d", e.state.name, e.state.maxId)), ports: make(map[state.Port]bool), machineId: machineId, } e.state.insts[i.id] = i e.state.maxId++ e.state.ops <- OpStartInstance{ Env: e.state.name, MachineId: machineId, Instance: i, Info: info, Secret: e.ecfg().secret(), } return i, nil }
// InvalidStateInfo holds information about no state - it will always // give an error when connected to. The machine id gives the machine id // of the machine to be started func InvalidStateInfo(machineId string) *state.Info { return &state.Info{ Addrs: []string{"0.1.2.3:1234"}, EntityName: state.MachineEntityName(machineId), Password: "******", CACert: []byte(testing.CACert), } }
// Run runs a machine agent. func (a *MachineAgent) Run(_ *cmd.Context) error { if err := a.Conf.read(state.MachineEntityName(a.MachineId)); err != nil { return err } defer log.Printf("cmd/jujud: machine agent exiting") defer a.tomb.Done() return RunLoop(a.Conf.Conf, a) }
// primeAgent adds a new Machine to run the given jobs, and sets up the // machine agent's directory. It returns the new machine and the // agent's configuration. func (s *MachineSuite) primeAgent(c *C, jobs ...state.MachineJob) (*state.Machine, *agent.Conf) { m, err := s.State.InjectMachine("ardbeg-0", jobs...) c.Assert(err, IsNil) err = m.SetMongoPassword("machine-password") c.Assert(err, IsNil) conf, _ := s.agentSuite.primeAgent(c, state.MachineEntityName(m.Id()), "machine-password") return m, conf }
func verifyConfig(cfg *MachineConfig) (err error) { defer trivial.ErrorContextf(&err, "invalid machine configuration") if !state.IsMachineId(cfg.MachineId) { return fmt.Errorf("invalid machine id") } if cfg.ProviderType == "" { return fmt.Errorf("missing provider type") } if cfg.DataDir == "" { return fmt.Errorf("missing var directory") } if cfg.Tools == nil { return fmt.Errorf("missing tools") } if cfg.Tools.URL == "" { return fmt.Errorf("missing tools URL") } if cfg.StateInfo == nil { return fmt.Errorf("missing state info") } if len(cfg.StateInfo.CACert) == 0 { return fmt.Errorf("missing CA certificate") } if cfg.StateServer { if cfg.InstanceIdAccessor == "" { return fmt.Errorf("missing instance id accessor") } if cfg.Config == nil { return fmt.Errorf("missing environment configuration") } if cfg.StateInfo.EntityName != "" { return fmt.Errorf("entity name must be blank when starting a state server") } if len(cfg.StateServerCert) == 0 { return fmt.Errorf("missing state server certificate") } if len(cfg.StateServerKey) == 0 { return fmt.Errorf("missing state server private key") } } else { if len(cfg.StateInfo.Addrs) == 0 { return fmt.Errorf("missing state hosts") } if cfg.StateInfo.EntityName != state.MachineEntityName(cfg.MachineId) { return fmt.Errorf("entity name must match started machine") } } for _, r := range cfg.StateInfo.Password { if r == '\'' || r == '\\' || r < 32 { return fmt.Errorf("password has disallowed characters") } } return nil }
// startInstance is the internal version of StartInstance, used by Bootstrap // as well as via StartInstance itself. func (e *environ) startInstance(scfg *startInstanceParams) (environs.Instance, error) { if scfg.tools == nil { var err error flags := environs.HighestVersion | environs.CompatVersion scfg.tools, err = environs.FindTools(e, version.Current, flags) if err != nil { return nil, err } } log.Printf("environs/openstack: starting machine %s in %q running tools version %q from %q", scfg.machineId, e.name, scfg.tools.Binary, scfg.tools.URL) // TODO(wallyworld) - implement spec lookup if strings.Contains(scfg.tools.Series, "unknown") || strings.Contains(scfg.tools.Series, "unknown") { return nil, fmt.Errorf("cannot find image for unknown series or architecture") } userData, err := e.userData(scfg) if err != nil { return nil, fmt.Errorf("cannot make user data: %v", err) } log.Debugf("environs/openstack: openstack user data: %q", userData) groups, err := e.setUpGroups(scfg.machineId) if err != nil { return nil, fmt.Errorf("cannot set up groups: %v", err) } var groupNames = make([]nova.SecurityGroupName, len(groups)) for i, g := range groups { groupNames[i] = nova.SecurityGroupName{g.Name} } var server *nova.Entity for a := shortAttempt.Start(); a.Next(); { server, err = e.nova().RunServer(nova.RunServerOpts{ Name: state.MachineEntityName(scfg.machineId), // TODO(wallyworld) - do not use hard coded image FlavorId: defaultFlavorId, ImageId: defaultImageId, UserData: userData, SecurityGroupNames: groupNames, }) if err == nil || !gooseerrors.IsNotFound(err) { break } } if err != nil { return nil, fmt.Errorf("cannot run instance: %v", err) } inst := &instance{e, server} log.Printf("environs/openstack: started instance %q", inst.Id()) return inst, nil }
func (s *MachineSuite) TestMachineEntityName(c *C) { c.Assert(state.MachineEntityName("10"), Equals, "machine-10") }
func (a *MachineAgent) EntityName() string { return state.MachineEntityName(a.MachineId) }
func New(cfg *MachineConfig) (*cloudinit.Config, error) { if err := verifyConfig(cfg); err != nil { return nil, err } c := cloudinit.New() c.AddSSHAuthorizedKeys(cfg.AuthorizedKeys) c.AddPackage("git") addScripts(c, fmt.Sprintf("mkdir -p %s", cfg.DataDir), "mkdir -p /var/log/juju") // Make a directory for the tools to live in, then fetch the // tools and unarchive them into it. addScripts(c, "bin="+shquote(cfg.jujuTools()), "mkdir -p $bin", fmt.Sprintf("wget --no-verbose -O - %s | tar xz -C $bin", shquote(cfg.Tools.URL)), fmt.Sprintf("echo -n %s > $bin/downloaded-url.txt", shquote(cfg.Tools.URL)), ) debugFlag := "" // TODO: disable debug mode by default when the system is stable. if true || log.Debug { debugFlag = " --debug" } if cfg.StateServer { certKey := string(cfg.StateServerCert) + string(cfg.StateServerKey) addFile(c, cfg.dataFile("server.pem"), certKey, 0600) // TODO The public bucket must come from the environment configuration. b := cfg.Tools.Binary url := fmt.Sprintf("http://juju-dist.s3.amazonaws.com/tools/mongo-2.2.0-%s-%s.tgz", b.Series, b.Arch) addScripts(c, "mkdir -p /opt", fmt.Sprintf("wget --no-verbose -O - %s | tar xz -C /opt", shquote(url)), ) if err := addMongoToBoot(c, cfg); err != nil { return nil, err } // We temporarily give bootstrap-state a directory // of its own so that it can get the state info via the // same mechanism as other jujud commands. acfg, err := addAgentInfo(c, cfg, "bootstrap") if err != nil { return nil, err } addScripts(c, cfg.jujuTools()+"/jujud bootstrap-state"+ " --data-dir "+shquote(cfg.DataDir)+ " --instance-id "+cfg.InstanceIdAccessor+ " --env-config "+shquote(base64yaml(cfg.Config))+ debugFlag, "rm -rf "+shquote(acfg.Dir()), ) } if _, err := addAgentToBoot(c, cfg, "machine", state.MachineEntityName(cfg.MachineId), fmt.Sprintf("--machine-id %s "+debugFlag, cfg.MachineId)); err != nil { return nil, err } // general options c.SetAptUpgrade(true) c.SetAptUpdate(true) c.SetOutput(cloudinit.OutAll, "| tee -a /var/log/cloud-init-output.log", "") return c, nil }