// NewPaths returns the set of filesystem paths that the supplied unit should // use, given the supplied root juju data directory path. func NewPaths(dataDir string, unitTag names.UnitTag) Paths { join := filepath.Join baseDir := join(dataDir, "agents", unitTag.String()) stateDir := join(baseDir, "state") socket := func(name string, abstract bool) string { if version.Current.OS == version.Windows { return fmt.Sprintf(`\\.\pipe\%s-%s`, unitTag, name) } path := join(baseDir, name+".socket") if abstract { path = "@" + path } return path } toolsDir := tools.ToolsDir(dataDir, unitTag.String()) return Paths{ ToolsDir: filepath.FromSlash(toolsDir), Runtime: RuntimePaths{ JujuRunSocket: socket("run", false), JujucServerSocket: socket("agent", true), }, State: StatePaths{ CharmDir: join(baseDir, "charm"), OperationsFile: join(stateDir, "uniter"), RelationsDir: join(stateDir, "relations"), BundlesDir: join(stateDir, "bundles"), DeployerDir: join(stateDir, "deployer"), StorageDir: join(stateDir, "storage"), MetricsSpoolDir: join(stateDir, "spool", "metrics"), }, } }
func (ctx *SimpleContext) RecallUnit(unitName string) error { svc, err := ctx.findInitSystemJob(unitName) if err != nil { return errors.Trace(err) } installed, err := svc.Installed() if err != nil { return errors.Trace(err) } if !installed { return errors.Errorf("unit %q is not deployed", unitName) } if err := svc.Stop(); err != nil { return err } if err := svc.Remove(); err != nil { return err } tag := names.NewUnitTag(unitName) dataDir := ctx.agentConfig.DataDir() agentDir := agent.Dir(dataDir, tag) // Recursivley change mode to 777 on windows to avoid // Operation not permitted errors when deleting the agentDir err = recursiveChmod(agentDir, os.FileMode(0777)) if err != nil { return err } if err := os.RemoveAll(agentDir); err != nil { return err } // TODO(dfc) should take a Tag toolsDir := tools.ToolsDir(dataDir, tag.String()) return os.Remove(toolsDir) }
func (fix *SimpleToolsFixture) paths(tag string) (confPath, agentDir, toolsDir string) { confName := fmt.Sprintf("jujud-%s.conf", tag) confPath = filepath.Join(fix.initDir, confName) agentDir = agent.Dir(fix.dataDir, tag) toolsDir = tools.ToolsDir(fix.dataDir, tag) return }
func (s *ToolsSuite) SetUpTest(c *gc.C) { s.dataDir = c.MkDir() s.toolsDir = tools.SharedToolsDir(s.dataDir, version.Current) err := os.MkdirAll(s.toolsDir, 0755) c.Assert(err, gc.IsNil) err = symlink.New(s.toolsDir, tools.ToolsDir(s.dataDir, "unit-u-123")) c.Assert(err, gc.IsNil) }
func (t *ToolsSuite) TestChangeAgentTools(c *gc.C) { files := []*testing.TarFile{ testing.NewTarFile("jujuc", agenttools.DirPerm, "juju executable"), testing.NewTarFile("jujud", agenttools.DirPerm, "jujuc executable"), } data, checksum := testing.TarGz(files...) testTools := &coretest.Tools{ URL: "http://foo/bar1", Version: version.MustParseBinary("1.2.3-quantal-amd64"), Size: int64(len(data)), SHA256: checksum, } err := agenttools.UnpackTools(t.dataDir, testTools, bytes.NewReader(data)) c.Assert(err, jc.ErrorIsNil) gotTools, err := agenttools.ChangeAgentTools(t.dataDir, "testagent", testTools.Version) c.Assert(err, jc.ErrorIsNil) c.Assert(*gotTools, gc.Equals, *testTools) assertDirNames(c, t.toolsDir(), []string{"1.2.3-quantal-amd64", "testagent"}) assertDirNames(c, agenttools.ToolsDir(t.dataDir, "testagent"), []string{"jujuc", "jujud", agenttools.ToolsFile}) // Upgrade again to check that the link replacement logic works ok. files2 := []*testing.TarFile{ testing.NewTarFile("quantal", agenttools.DirPerm, "foo content"), testing.NewTarFile("amd64", agenttools.DirPerm, "bar content"), } data2, checksum2 := testing.TarGz(files2...) tools2 := &coretest.Tools{ URL: "http://foo/bar2", Version: version.MustParseBinary("1.2.4-quantal-amd64"), Size: int64(len(data2)), SHA256: checksum2, } err = agenttools.UnpackTools(t.dataDir, tools2, bytes.NewReader(data2)) c.Assert(err, jc.ErrorIsNil) gotTools, err = agenttools.ChangeAgentTools(t.dataDir, "testagent", tools2.Version) c.Assert(err, jc.ErrorIsNil) c.Assert(*gotTools, gc.Equals, *tools2) assertDirNames(c, t.toolsDir(), []string{"1.2.3-quantal-amd64", "1.2.4-quantal-amd64", "testagent"}) assertDirNames(c, agenttools.ToolsDir(t.dataDir, "testagent"), []string{"quantal", "amd64", agenttools.ToolsFile}) }
func (a *MachineAgent) createJujudSymlinks(dataDir string) error { jujud := filepath.Join(tools.ToolsDir(dataDir, a.Tag().String()), jujunames.Jujud) for _, link := range []string{jujuRun, jujuDumpLogs} { err := a.createSymlink(jujud, link) if err != nil { return errors.Annotatef(err, "failed to create %s symlink", link) } } return nil }
// updateBackupMachineTag updates the paths that are stored in the backup // to the current machine. This path is tied, among other factors, to the // machine tag. // Eventually this will change: when backups hold relative paths. func updateBackupMachineTag(oldTag, newTag names.Tag) error { oldTagString := oldTag.String() newTagString := newTag.String() if oldTagString == newTagString { return nil } oldTagPath := path.Join(agent.DefaultPaths.DataDir, oldTagString) newTagPath := path.Join(agent.DefaultPaths.DataDir, newTagString) oldToolsDir := tools.ToolsDir(agent.DefaultPaths.DataDir, oldTagString) oldLink, err := filepath.EvalSymlinks(oldToolsDir) os.Rename(oldTagPath, newTagPath) newToolsDir := tools.ToolsDir(agent.DefaultPaths.DataDir, newTagString) newToolsPath := strings.Replace(oldLink, oldTagPath, newTagPath, -1) err = symlink.Replace(newToolsDir, newToolsPath) return errors.Annotatef(err, "cannot set the new tools path") }
func (s *ToolsSuite) SetUpTest(c *gc.C) { s.dataDir = c.MkDir() s.toolsDir = tools.SharedToolsDir(s.dataDir, version.Binary{ Number: version.Current, Arch: arch.HostArch(), Series: series.HostSeries(), }) err := os.MkdirAll(s.toolsDir, 0755) c.Assert(err, jc.ErrorIsNil) err = symlink.New(s.toolsDir, tools.ToolsDir(s.dataDir, "unit-u-123")) c.Assert(err, jc.ErrorIsNil) }
func (s *UpgraderSuite) TestChangeAgentTools(c *gc.C) { oldTools := &coretools.Tools{ Version: version.MustParseBinary("1.2.3-quantal-amd64"), } stor := s.DefaultToolsStorage newToolsBinary := "5.4.3-precise-amd64" newTools := envtesting.PrimeTools(c, stor, s.DataDir(), s.Environ.Config().AgentStream(), version.MustParseBinary(newToolsBinary)) s.PatchValue(&version.Current, newTools.Version) err := envtools.MergeAndWriteMetadata(stor, "released", "released", coretools.List{newTools}, envtools.DoNotWriteMirrors) c.Assert(err, jc.ErrorIsNil) ugErr := &upgrader.UpgradeReadyError{ AgentName: "anAgent", OldTools: oldTools.Version, NewTools: newTools.Version, DataDir: s.DataDir(), } err = ugErr.ChangeAgentTools() c.Assert(err, jc.ErrorIsNil) target := agenttools.ToolsDir(s.DataDir(), newToolsBinary) link, err := symlink.Read(agenttools.ToolsDir(s.DataDir(), "anAgent")) c.Assert(err, jc.ErrorIsNil) c.Assert(link, jc.SamePath, target) }
func (w *windowsConfigure) addMachineAgentToBoot(tag string) error { // Make the agent run via a symbolic link to the actual tools // directory, so it can upgrade itself without needing to change // the upstart script. toolsDir := tools.ToolsDir(w.mcfg.DataDir, tag) w.conf.AddScripts( fmt.Sprintf( `cmd.exe /C mklink /D %s %v`, w.renderer.FromSlash(toolsDir), w.mcfg.Tools.Version), ) name := w.mcfg.MachineAgentServiceName cmds := w.machineAgentWindowsService(name, toolsDir) w.conf.AddScripts(cmds...) return nil }
func (ctx *SimpleContext) RecallUnit(unitName string) error { svc := ctx.findUpstartJob(unitName) if svc == nil || !svc.Installed() { return fmt.Errorf("unit %q is not deployed", unitName) } if err := svc.StopAndRemove(); err != nil { return err } tag := names.NewUnitTag(unitName).String() dataDir := ctx.agentConfig.DataDir() agentDir := agent.Dir(dataDir, tag) if err := os.RemoveAll(agentDir); err != nil { return err } toolsDir := tools.ToolsDir(dataDir, tag) return os.Remove(toolsDir) }
func (cfg *MachineConfig) addMachineAgentToBoot(c *cloudinit.Config, tag, machineId string) error { // Make the agent run via a symbolic link to the actual tools // directory, so it can upgrade itself without needing to change // the upstart script. toolsDir := agenttools.ToolsDir(cfg.DataDir, tag) // TODO(dfc) ln -nfs, so it doesn't fail if for some reason that the target already exists c.AddScripts(fmt.Sprintf("ln -s %v %s", cfg.Tools.Version, shquote(toolsDir))) name := cfg.MachineAgentServiceName conf := upstart.MachineAgentUpstartService(name, toolsDir, cfg.DataDir, cfg.LogDir, tag, machineId, nil) cmds, err := conf.InstallCommands() if err != nil { return errors.Annotatef(err, "cannot make cloud-init upstart script for the %s agent", tag) } c.AddRunCmd(cloudinit.LogProgressCmd("Starting Juju machine agent (%s)", name)) c.AddScripts(cmds...) return nil }
func (s *UpgraderSuite) TestChangeAgentTools(c *gc.C) { oldTools := &coretools.Tools{ Version: version.MustParseBinary("1.2.3-quantal-amd64"), } stor := s.Conn.Environ.Storage() newTools := envtesting.PrimeTools(c, stor, s.DataDir(), version.MustParseBinary("5.4.3-precise-amd64")) s.PatchValue(&version.Current, newTools.Version) err := envtools.MergeAndWriteMetadata(stor, coretools.List{newTools}, envtools.DoNotWriteMirrors) c.Assert(err, gc.IsNil) ugErr := &upgrader.UpgradeReadyError{ AgentName: "anAgent", OldTools: oldTools.Version, NewTools: newTools.Version, DataDir: s.DataDir(), } err = ugErr.ChangeAgentTools() c.Assert(err, gc.IsNil) link, err := symlink.Read(agenttools.ToolsDir(s.DataDir(), "anAgent")) c.Assert(err, gc.IsNil) c.Assert(link, gc.Equals, newTools.Version.String()) }
func (ctx *SimpleContext) DeployUnit(unitName, initialPassword string) (err error) { // Check sanity. renderer, err := shell.NewRenderer("") if err != nil { return errors.Trace(err) } svc, err := ctx.service(unitName, renderer) if err != nil { return errors.Trace(err) } installed, err := svc.Installed() if err != nil { return errors.Trace(err) } if installed { return fmt.Errorf("unit %q is already deployed", unitName) } // Link the current tools for use by the new agent. tag := names.NewUnitTag(unitName) dataDir := ctx.agentConfig.DataDir() logDir := ctx.agentConfig.LogDir() current := version.Binary{ Number: version.Current, Arch: arch.HostArch(), Series: series.HostSeries(), } toolsDir := tools.ToolsDir(dataDir, tag.String()) defer removeOnErr(&err, toolsDir) _, err = tools.ChangeAgentTools(dataDir, tag.String(), current) if err != nil { return errors.Trace(err) } result, err := ctx.api.ConnectionInfo() if err != nil { return errors.Trace(err) } logger.Debugf("state addresses: %q", result.StateAddresses) logger.Debugf("API addresses: %q", result.APIAddresses) containerType := ctx.agentConfig.Value(agent.ContainerType) namespace := ctx.agentConfig.Value(agent.Namespace) conf, err := agent.NewAgentConfig( agent.AgentConfigParams{ Paths: agent.Paths{ DataDir: dataDir, LogDir: logDir, MetricsSpoolDir: agent.DefaultPaths.MetricsSpoolDir, }, UpgradedToVersion: version.Current, Tag: tag, Password: initialPassword, Nonce: "unused", Model: ctx.agentConfig.Model(), // TODO: remove the state addresses here and test when api only. StateAddresses: result.StateAddresses, APIAddresses: result.APIAddresses, CACert: ctx.agentConfig.CACert(), Values: map[string]string{ agent.ContainerType: containerType, agent.Namespace: namespace, }, }) if err != nil { return errors.Trace(err) } if err := conf.Write(); err != nil { return err } defer removeOnErr(&err, conf.Dir()) // Install an init service that runs the unit agent. if err := service.InstallAndStart(svc); err != nil { return errors.Trace(err) } return nil }
func (fix *SimpleToolsFixture) paths(tag names.Tag) (agentDir, toolsDir string) { agentDir = agent.Dir(fix.dataDir, tag) toolsDir = tools.ToolsDir(fix.dataDir, tag.String()) return }
// ToolsDir returns the path to the agent's tools dir. func (ai AgentInfo) ToolsDir(renderer shell.Renderer) string { return renderer.FromSlash(tools.ToolsDir(ai.DataDir, ai.name)) }
func (p *collectPaths) GetToolsDir() string { return filepath.FromSlash(tools.ToolsDir(p.dataDir, p.unitTag.String())) }
func (u *Uniter) init(unitTag string) (err error) { tag, err := names.ParseUnitTag(unitTag) if err != nil { return err } u.unit, err = u.st.Unit(tag) if err != nil { return err } if u.unit.Life() == params.Dead { // If we started up already dead, we should not progress further. If we // become Dead immediately after starting up, we may well complete any // operations in progress before detecting it; but that race is fundamental // and inescapable, whereas this one is not. return worker.ErrTerminateAgent } if err = u.setupLocks(); err != nil { return err } u.toolsDir = tools.ToolsDir(u.dataDir, unitTag) if err := EnsureJujucSymlinks(u.toolsDir); err != nil { return err } u.baseDir = filepath.Join(u.dataDir, "agents", unitTag) u.relationsDir = filepath.Join(u.baseDir, "state", "relations") if err := os.MkdirAll(u.relationsDir, 0755); err != nil { return err } serviceTag, err := names.ParseServiceTag(u.unit.ServiceTag()) if err != nil { return err } u.service, err = u.st.Service(serviceTag) if err != nil { return err } var env *uniter.Environment env, err = u.st.Environment() if err != nil { return err } u.uuid = env.UUID() u.envName = env.Name() u.relationers = map[int]*Relationer{} u.relationHooks = make(chan hook.Info) u.charmPath = filepath.Join(u.baseDir, "charm") deployerPath := filepath.Join(u.baseDir, "state", "deployer") bundles := charm.NewBundlesDir(filepath.Join(u.baseDir, "state", "bundles")) u.deployer, err = charm.NewDeployer(u.charmPath, deployerPath, bundles) if err != nil { return fmt.Errorf("cannot create deployer: %v", err) } u.sf = NewStateFile(filepath.Join(u.baseDir, "state", "uniter")) u.rand = rand.New(rand.NewSource(time.Now().Unix())) // If we start trying to listen for juju-run commands before we have valid // relation state, surprising things will come to pass. if err := u.restoreRelations(); err != nil { return err } runListenerSocketPath := filepath.Join(u.baseDir, RunListenerFile) logger.Debugf("starting juju-run listener on unix:%s", runListenerSocketPath) u.runListener, err = NewRunListener(u, runListenerSocketPath) if err != nil { return err } // The socket needs to have permissions 777 in order for other users to use it. return os.Chmod(runListenerSocketPath, 0777) }
func (ctx *SimpleContext) DeployUnit(unitName, initialPassword string) (err error) { // Check sanity. svc := ctx.service(unitName) if svc.Installed() { return fmt.Errorf("unit %q is already deployed", unitName) } // Link the current tools for use by the new agent. tag := names.NewUnitTag(unitName) dataDir := ctx.agentConfig.DataDir() logDir := ctx.agentConfig.LogDir() // TODO(dfc) _, err = tools.ChangeAgentTools(dataDir, tag.String(), version.Current) // TODO(dfc) toolsDir := tools.ToolsDir(dataDir, tag.String()) defer removeOnErr(&err, toolsDir) result, err := ctx.api.ConnectionInfo() if err != nil { return err } logger.Debugf("state addresses: %q", result.StateAddresses) logger.Debugf("API addresses: %q", result.APIAddresses) containerType := ctx.agentConfig.Value(agent.ContainerType) namespace := ctx.agentConfig.Value(agent.Namespace) conf, err := agent.NewAgentConfig( agent.AgentConfigParams{ DataDir: dataDir, LogDir: logDir, UpgradedToVersion: version.Current.Number, Tag: tag, Password: initialPassword, Nonce: "unused", // TODO: remove the state addresses here and test when api only. StateAddresses: result.StateAddresses, APIAddresses: result.APIAddresses, CACert: ctx.agentConfig.CACert(), Values: map[string]string{ agent.ContainerType: containerType, agent.Namespace: namespace, }, }) if err != nil { return err } if err := conf.Write(); err != nil { return err } defer removeOnErr(&err, conf.Dir()) // Install an upstart job that runs the unit agent. logPath := path.Join(logDir, tag.String()+".log") cmd := strings.Join([]string{ filepath.FromSlash(path.Join(toolsDir, jujunames.Jujud)), "unit", "--data-dir", dataDir, "--unit-name", unitName, "--debug", // TODO: propagate debug state sensibly }, " ") // TODO(thumper): 2013-09-02 bug 1219630 // As much as I'd like to remove JujuContainerType now, it is still // needed as MAAS still needs it at this stage, and we can't fix // everything at once. sconf := common.Conf{ Desc: "juju unit agent for " + unitName, Cmd: cmd, Out: logPath, Env: map[string]string{ osenv.JujuContainerTypeEnvKey: containerType, }, InitDir: ctx.initDir, } svc.UpdateConfig(sconf) return svc.Install() }