func (env *localEnviron) writeBootstrapAgentConfFile(cert, key []byte) error { info, apiInfo, err := env.StateInfo() if err != nil { logger.Errorf("failed to get state info to write bootstrap agent file: %v", err) return err } tag := names.MachineTag("0") info.Tag = tag apiInfo.Tag = tag conf := &agent.Conf{ DataDir: env.config.rootDir(), StateInfo: info, APIInfo: apiInfo, StateServerCert: cert, StateServerKey: key, StatePort: env.config.StatePort(), APIPort: env.config.APIPort(), MachineNonce: state.BootstrapNonce, } if err := conf.Write(); err != nil { logger.Errorf("failed to write bootstrap agent file: %v", err) return err } return nil }
// DeployerTag returns the tag of the agent responsible for deploying // the unit. If no such entity can be determined, false is returned. func (u *Unit) DeployerTag() (string, bool) { if u.doc.Principal != "" { return names.UnitTag(u.doc.Principal), true } else if u.doc.MachineId != "" { return names.MachineTag(u.doc.MachineId), true } return "", false }
func (cfg *MachineConfig) addLogging(c *cloudinit.Config) error { var configRenderer syslog.SyslogConfigRenderer if cfg.StateServer { configRenderer = syslog.NewAccumulateConfig( names.MachineTag(cfg.MachineId)) } else { configRenderer = syslog.NewForwardConfig( names.MachineTag(cfg.MachineId), cfg.stateHostAddrs()) } content, err := configRenderer.Render() if err != nil { return err } c.AddFile("/etc/rsyslog.d/25-juju.conf", string(content), 0600) c.AddRunCmd("restart rsyslog") return nil }
// FakeAPIInfo 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 FakeAPIInfo(machineId string) *api.Info { return &api.Info{ Addrs: []string{"0.1.2.3:1234"}, Tag: names.MachineTag(machineId), Password: "******", CACert: []byte(testing.CACert), } }
func (env *localEnviron) setupLocalMachineAgent(cons constraints.Value) error { dataDir := env.config.rootDir() toolList, err := environs.FindBootstrapTools(env, cons) if err != nil { return err } // ensure we have at least one valid tools if len(toolList) == 0 { return fmt.Errorf("No bootstrap tools found") } // unpack the first tools into the agent dir. agentTools := toolList[0] logger.Debugf("tools: %#v", agentTools) // brutally abuse our knowledge of storage to directly open the file toolsUrl, err := url.Parse(agentTools.URL) toolsLocation := filepath.Join(env.config.storageDir(), toolsUrl.Path) logger.Infof("tools location: %v", toolsLocation) toolsFile, err := os.Open(toolsLocation) defer toolsFile.Close() // Again, brutally abuse our knowledge here. // The tools that FindBootstrapTools has returned us are based on the // default series in the config. However we are running potentially on a // different series. When the machine agent is started, it will be // looking based on the current series, so we need to override the series // returned in the tools to be the current series. agentTools.Version.Series = version.CurrentSeries() err = tools.UnpackTools(dataDir, agentTools, toolsFile) machineId := "0" // Always machine 0 tag := names.MachineTag(machineId) toolsDir := tools.SharedToolsDir(dataDir, agentTools.Version) logDir := env.config.logDir() logConfig := "--debug" // TODO(thumper): specify loggo config machineEnvironment := map[string]string{ "USER": env.config.user, "HOME": os.Getenv("HOME"), osenv.JujuProviderType: env.config.Type(), osenv.JujuStorageDir: env.config.storageDir(), osenv.JujuStorageAddr: env.config.storageAddr(), osenv.JujuSharedStorageDir: env.config.sharedStorageDir(), osenv.JujuSharedStorageAddr: env.config.sharedStorageAddr(), } agent := upstart.MachineAgentUpstartService( env.machineAgentServiceName(), toolsDir, dataDir, logDir, tag, machineId, logConfig, machineEnvironment) agent.InitDir = upstartScriptLocation logger.Infof("installing service %s to %s", env.machineAgentServiceName(), agent.InitDir) if err := agent.Install(); err != nil { logger.Errorf("could not install machine agent service: %v", err) return err } return nil }
// makeMachineConfig produces a valid cloudinit machine config. func makeMachineConfig(c *gc.C) *cloudinit.MachineConfig { dir := c.MkDir() machineID := "0" return &cloudinit.MachineConfig{ MachineId: machineID, MachineNonce: "gxshasqlnng", DataDir: dir, Tools: &tools.Tools{URL: "file://" + dir}, StateInfo: &state.Info{ CACert: []byte(testing.CACert), Addrs: []string{"127.0.0.1:123"}, Tag: names.MachineTag(machineID), }, APIInfo: &api.Info{ CACert: []byte(testing.CACert), Addrs: []string{"127.0.0.1:123"}, Tag: names.MachineTag(machineID), }, } }
func (s *CommonProvisionerSuite) checkStartInstanceCustom(c *C, m *state.Machine, secret string, cons constraints.Value) (inst instance.Instance) { s.State.StartSync() for { select { case o := <-s.op: switch o := o.(type) { case dummy.OpStartInstance: inst = o.Instance s.waitInstanceId(c, m, inst.Id()) // Check the instance was started with the expected params. c.Assert(o.MachineId, Equals, m.Id()) nonceParts := strings.SplitN(o.MachineNonce, ":", 2) c.Assert(nonceParts, HasLen, 2) c.Assert(nonceParts[0], Equals, names.MachineTag("0")) c.Assert(nonceParts[1], checkers.Satisfies, utils.IsValidUUIDString) c.Assert(o.Secret, Equals, secret) c.Assert(o.Constraints, DeepEquals, cons) // Check we can connect to the state with // the machine's entity name and password. info := s.StateInfo(c) info.Tag = m.Tag() c.Assert(o.Info.Password, Not(HasLen), 0) info.Password = o.Info.Password c.Assert(o.Info, DeepEquals, info) // Check we can connect to the state with // the machine's entity name and password. st, err := state.Open(o.Info, state.DefaultDialOpts()) c.Assert(err, IsNil) // All provisioned machines in this test suite have their hardware characteristics // attributes set to the same values as the constraints due to the dummy environment being used. hc, err := m.HardwareCharacteristics() c.Assert(err, IsNil) c.Assert(*hc, DeepEquals, instance.HardwareCharacteristics{ Arch: cons.Arch, Mem: cons.Mem, CpuCores: cons.CpuCores, CpuPower: cons.CpuPower, }) st.Close() return default: c.Logf("ignoring unexpected operation %#v", o) } case <-time.After(2 * time.Second): c.Fatalf("provisioner did not start an instance") return } } return }
func (task *provisionerTask) startMachine(machine *state.Machine) error { stateInfo, apiInfo, err := task.auth.SetupAuthentication(machine) if err != nil { logger.Errorf("failed to setup authentication: %v", err) return err } cons, err := machine.Constraints() if err != nil { return err } // Generate a unique nonce for the new instance. uuid, err := utils.NewUUID() if err != nil { return err } // Generated nonce has the format: "machine-#:UUID". The first // part is a badge, specifying the tag of the machine the provisioner // is running on, while the second part is a random UUID. nonce := fmt.Sprintf("%s:%s", names.MachineTag(task.machineId), uuid.String()) inst, metadata, err := task.broker.StartInstance(machine.Id(), nonce, machine.Series(), cons, stateInfo, apiInfo) if err != nil { // Set the state to error, so the machine will be skipped next // time until the error is resolved, but don't return an // error; just keep going with the other machines. logger.Errorf("cannot start instance for machine %q: %v", machine, err) if err1 := machine.SetStatus(params.StatusError, err.Error()); err1 != nil { // Something is wrong with this machine, better report it back. logger.Errorf("cannot set error status for machine %q: %v", machine, err1) return err1 } return nil } if err := machine.SetProvisioned(inst.Id(), nonce, metadata); err != nil { logger.Errorf("cannot register instance for machine %v: %v", machine, err) // The machine is started, but we can't record the mapping in // state. It'll keep running while we fail out and restart, // but will then be detected by findUnknownInstances and // killed again. // // TODO(dimitern) Stop the instance right away here. // // Multiple instantiations of a given machine (with the same // machine ID) cannot coexist, because findUnknownInstances is // called before startMachines. However, if the first machine // had started to do work before being replaced, we may // encounter surprising problems. return err } logger.Infof("started machine %s as instance %s with hardware %q", machine, inst.Id(), metadata) return nil }
// primeAgent adds a new Machine to run the given jobs, and sets up the // machine agent's directory. It returns the new machine, the // agent's configuration and the tools currently running. func (s *MachineSuite) primeAgent(c *C, jobs ...state.MachineJob) (*state.Machine, *agent.Conf, *tools.Tools) { m, err := s.State.InjectMachine("series", constraints.Value{}, "ardbeg-0", instance.HardwareCharacteristics{}, jobs...) c.Assert(err, IsNil) err = m.SetMongoPassword("machine-password") c.Assert(err, IsNil) err = m.SetPassword("machine-password") c.Assert(err, IsNil) conf, tools := s.agentSuite.primeAgent(c, names.MachineTag(m.Id()), "machine-password") conf.MachineNonce = state.BootstrapNonce conf.APIInfo.Nonce = conf.MachineNonce err = conf.Write() c.Assert(err, IsNil) return m, conf, tools }
// Tag returns a name identifying the machine that is safe to use // as a file name. The returned name will be different from other // Tag values returned by any other entities from the same state. func (m *Machine) Tag() string { return names.MachineTag(m.Id()) }
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 *deployerSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) // The two known machines now contain the following units: // machine 0 (not authorized): mysql/1 (principal1) // machine 1 (authorized): mysql/0 (principal0), logging/0 (subordinate0) var err error s.machine0, err = s.State.AddMachine("series", state.JobManageState, state.JobHostUnits) c.Assert(err, gc.IsNil) s.machine1, err = s.State.AddMachine("series", state.JobHostUnits) c.Assert(err, gc.IsNil) s.service0, err = s.State.AddService("mysql", s.AddTestingCharm(c, "mysql")) c.Assert(err, gc.IsNil) s.service1, err = s.State.AddService("logging", s.AddTestingCharm(c, "logging")) c.Assert(err, gc.IsNil) eps, err := s.State.InferEndpoints([]string{"mysql", "logging"}) c.Assert(err, gc.IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, gc.IsNil) s.principal0, err = s.service0.AddUnit() c.Assert(err, gc.IsNil) err = s.principal0.AssignToMachine(s.machine1) c.Assert(err, gc.IsNil) s.principal1, err = s.service0.AddUnit() c.Assert(err, gc.IsNil) err = s.principal1.AssignToMachine(s.machine0) c.Assert(err, gc.IsNil) relUnit0, err := rel.Unit(s.principal0) c.Assert(err, gc.IsNil) err = relUnit0.EnterScope(nil) c.Assert(err, gc.IsNil) s.subordinate0, err = s.service1.Unit("logging/0") c.Assert(err, gc.IsNil) // Create a FakeAuthorizer so we can check permissions, // set up assuming machine 1 has logged in. s.authorizer = apiservertesting.FakeAuthorizer{ Tag: names.MachineTag(s.machine1.Id()), LoggedIn: true, Manager: false, MachineAgent: true, } // Create the resource registry separately to track invocations to // Register. s.resources = common.NewResources() // Create a deployer API for machine 1. deployer, err := deployer.NewDeployerAPI( s.State, s.resources, s.authorizer, ) c.Assert(err, gc.IsNil) s.deployer = deployer }
func (a *MachineAgent) Tag() string { return names.MachineTag(a.MachineId) }
func (e *environ) machineFullName(machineId string) string { return fmt.Sprintf("juju-%s-%s", e.Name(), names.MachineTag(machineId)) }
func (s *machineSuite) TestMachineTag(c *gc.C) { c.Assert(names.MachineTag("10"), gc.Equals, "machine-10") // Check a container id. c.Assert(names.MachineTag("10/lxc/1"), gc.Equals, "machine-10-lxc-1") }
func verifyConfig(cfg *MachineConfig) (err error) { defer utils.ErrorContextf(&err, "invalid machine configuration") if !names.IsMachine(cfg.MachineId) { return fmt.Errorf("invalid machine id") } 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.APIInfo == nil { return fmt.Errorf("missing API info") } if len(cfg.APIInfo.CACert) == 0 { return fmt.Errorf("missing API CA certificate") } if cfg.StateServer { if cfg.Config == nil { return fmt.Errorf("missing environment configuration") } if cfg.StateInfo.Tag != "" { return fmt.Errorf("entity tag must be blank when starting a state server") } if cfg.APIInfo.Tag != "" { return fmt.Errorf("entity tag 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") } if cfg.StatePort == 0 { return fmt.Errorf("missing state port") } if cfg.APIPort == 0 { return fmt.Errorf("missing API port") } } else { if len(cfg.StateInfo.Addrs) == 0 { return fmt.Errorf("missing state hosts") } if cfg.StateInfo.Tag != names.MachineTag(cfg.MachineId) { return fmt.Errorf("entity tag must match started machine") } if len(cfg.APIInfo.Addrs) == 0 { return fmt.Errorf("missing API hosts") } if cfg.APIInfo.Tag != names.MachineTag(cfg.MachineId) { return fmt.Errorf("entity tag must match started machine") } } if cfg.MachineNonce == "" { return fmt.Errorf("missing machine nonce") } return nil }
func (manager *containerManager) StartContainer( machineId, series, nonce string, network *NetworkConfig, tools *tools.Tools, environConfig *config.Config, stateInfo *state.Info, apiInfo *api.Info) (instance.Instance, error) { name := names.MachineTag(machineId) if manager.name != "" { name = fmt.Sprintf("%s-%s", manager.name, name) } // Note here that the lxcObjectFacotry only returns a valid container // object, and doesn't actually construct the underlying lxc container on // disk. container := lxcObjectFactory.New(name) // Create the cloud-init. directory := jujuContainerDirectory(name) logger.Tracef("create directory: %s", directory) if err := os.MkdirAll(directory, 0755); err != nil { logger.Errorf("failed to create container directory: %v", err) return nil, err } logger.Tracef("write cloud-init") userDataFilename, err := writeUserData(directory, machineId, nonce, tools, environConfig, stateInfo, apiInfo) if err != nil { logger.Errorf("failed to write user data: %v", err) return nil, err } logger.Tracef("write the lxc.conf file") configFile, err := writeLxcConfig(network, directory, manager.logdir) if err != nil { logger.Errorf("failed to write config file: %v", err) return nil, err } templateParams := []string{ "--debug", // Debug errors in the cloud image "--userdata", userDataFilename, // Our groovey cloud-init "--hostid", name, // Use the container name as the hostid "-r", series, } // Create the container. logger.Tracef("create the container") if err := container.Create(configFile, defaultTemplate, templateParams...); err != nil { logger.Errorf("lxc container creation failed: %v", err) return nil, err } // Make sure that the mount dir has been created. logger.Tracef("make the mount dir for the shard logs") if err := os.MkdirAll(internalLogDir(name), 0755); err != nil { logger.Errorf("failed to create internal /var/log/juju mount dir: %v", err) return nil, err } logger.Tracef("lxc container created") // Now symlink the config file into the restart directory. containerConfigFile := filepath.Join(lxcContainerDir, name, "config") if err := os.Symlink(containerConfigFile, restartSymlink(name)); err != nil { return nil, err } logger.Tracef("auto-restart link created") // Start the lxc container with the appropriate settings for grabbing the // console output and a log file. consoleFile := filepath.Join(directory, "console.log") container.SetLogFile(filepath.Join(directory, "container.log"), golxc.LogDebug) logger.Tracef("start the container") // We explicitly don't pass through the config file to the container.Start // method as we have passed it through at container creation time. This // is necessary to get the appropriate rootfs reference without explicitly // setting it ourselves. if err = container.Start("", consoleFile); err != nil { logger.Errorf("container failed to start: %v", err) return nil, err } logger.Tracef("container started") return &lxcInstance{name}, nil }
func Configure(cfg *MachineConfig, c *cloudinit.Config) (*cloudinit.Config, error) { if err := verifyConfig(cfg); err != nil { return nil, err } c.AddSSHAuthorizedKeys(cfg.AuthorizedKeys) c.AddPackage("git") // Perfectly reasonable to install lxc on environment instances and kvm // containers. if cfg.MachineContainerType != instance.LXC { c.AddPackage("lxc") } c.AddScripts( "set -xe", // ensure we run all the scripts or abort. 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. c.AddScripts( "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)), ) // TODO (thumper): work out how to pass the logging config to the children debugFlag := "" // TODO: disable debug mode by default when the system is stable. if true { debugFlag = " --debug" } if err := cfg.addLogging(c); err != nil { return nil, err } // We add the machine agent's configuration info // before running bootstrap-state so that bootstrap-state // has a chance to rerwrite it to change the password. // It would be cleaner to change bootstrap-state to // be responsible for starting the machine agent itself, // but this would not be backwardly compatible. machineTag := names.MachineTag(cfg.MachineId) _, err := cfg.addAgentInfo(c, machineTag) if err != nil { return nil, err } if cfg.StateServer { if cfg.NeedMongoPPA() { c.AddAptSource("ppa:juju/experimental", "1024R/C8068B11") } c.AddPackage("mongodb-server") certKey := string(cfg.StateServerCert) + string(cfg.StateServerKey) c.AddFile(cfg.dataFile("server.pem"), certKey, 0600) if err := cfg.addMongoToBoot(c); 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 := cfg.addAgentInfo(c, "bootstrap") if err != nil { return nil, err } c.AddScripts( fmt.Sprintf("echo %s > %s", shquote(cfg.StateInfoURL), BootstrapStateURLFile), cfg.jujuTools()+"/jujud bootstrap-state"+ " --data-dir "+shquote(cfg.DataDir)+ " --env-config "+shquote(base64yaml(cfg.Config))+ " --constraints "+shquote(cfg.Constraints.String())+ debugFlag, "rm -rf "+shquote(acfg.Dir()), ) } if err := cfg.addMachineAgentToBoot(c, machineTag, cfg.MachineId, debugFlag); 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 }