// changed ensures that the named unit is deployed, recalled, or removed, as // indicated by its state. func (d *Deployer) changed(unitName string) error { unitTag := names.UnitTag(unitName) // Determine unit life state, and whether we're responsible for it. logger.Infof("checking unit %q", unitName) var life params.Life unit, err := d.st.Unit(unitTag) if params.IsCodeNotFoundOrCodeUnauthorized(err) { life = params.Dead } else if err != nil { return err } else { life = unit.Life() } // Deployed units must be removed if they're Dead, or if the deployer // is no longer responsible for them. if d.deployed.Contains(unitName) { if life == params.Dead { if err := d.recall(unitName); err != nil { return err } } } // The only units that should be deployed are those that (1) we are responsible // for and (2) are Alive -- if we're responsible for a Dying unit that is not // yet deployed, we should remove it immediately rather than undergo the hassle // of deploying a unit agent purely so it can set itself to Dead. if !d.deployed.Contains(unitName) { if life == params.Alive { return d.deploy(unit) } else if unit != nil { return d.remove(unit) } } return nil }
func (st *State) checkCanUpgrade(currentVersion, newVersion string) error { matchCurrent := "^" + regexp.QuoteMeta(currentVersion) + "-" matchNew := "^" + regexp.QuoteMeta(newVersion) + "-" // Get all machines and units with a different or empty version. sel := bson.D{{"$or", []bson.D{ {{"tools", bson.D{{"$exists", false}}}}, {{"$and", []bson.D{ {{"tools.version", bson.D{{"$not", bson.RegEx{matchCurrent, ""}}}}}, {{"tools.version", bson.D{{"$not", bson.RegEx{matchNew, ""}}}}}, }}}, }}} var agentTags []string for _, collection := range []*mgo.Collection{st.machines, st.units} { var doc struct { Id string `bson:"_id"` } iter := collection.Find(sel).Select(bson.D{{"_id", 1}}).Iter() for iter.Next(&doc) { switch collection.Name { case "machines": agentTags = append(agentTags, names.MachineTag(doc.Id)) case "units": agentTags = append(agentTags, names.UnitTag(doc.Id)) } } if err := iter.Err(); err != nil { return err } } if len(agentTags) > 0 { return newVersionInconsistentError(version.MustParse(currentVersion), agentTags) } return nil }
// upstartService returns an upstart.Service corresponding to the specified // unit. func (ctx *SimpleContext) upstartService(unitName string) *upstart.Service { tag := names.UnitTag(unitName) svcName := "jujud-" + tag svc := upstart.NewService(svcName) svc.InitDir = ctx.initDir return svc }
// GetPrincipal returns the result of calling PrincipalName() and // converting it to a tag, on each given unit. func (u *UniterAPI) GetPrincipal(args params.Entities) (params.StringBoolResults, error) { result := params.StringBoolResults{ Results: make([]params.StringBoolResult, len(args.Entities)), } canAccess, err := u.accessUnit() if err != nil { return params.StringBoolResults{}, err } for i, entity := range args.Entities { err := common.ErrPerm if canAccess(entity.Tag) { var unit *state.Unit unit, err = u.getUnit(entity.Tag) if err == nil { principal, ok := unit.PrincipalName() if principal != "" { result.Results[i].Result = names.UnitTag(principal) } result.Results[i].Ok = ok } } result.Results[i].Error = common.ServerError(err) } return result, 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 (s *unitSuite) TestInvalidUnitTagFormats(c *gc.C) { for i, test := range unitNameTests { if !test.valid { c.Logf("test %d: %q", i, test.pattern) expect := fmt.Sprintf("%q is not a valid unit name", test.pattern) testUnitTag := func() { names.UnitTag(test.pattern) } c.Assert(testUnitTag, gc.PanicMatches, expect) } } }
func (fix *SimpleToolsFixture) checkUnitRemoved(c *gc.C, name string) { tag := names.UnitTag(name) confPath, agentDir, toolsDir := fix.paths(tag) for _, path := range []string{confPath, agentDir, toolsDir} { _, err := ioutil.ReadFile(path) if err == nil { c.Log("Warning: %q not removed as expected", path) } else { c.Assert(err, jc.Satisfies, os.IsNotExist) } } }
func (u *UniterAPI) destroySubordinates(principal *state.Unit) error { subordinates := principal.SubordinateNames() for _, subName := range subordinates { unit, err := u.getUnit(names.UnitTag(subName)) if err != nil { return err } if err = unit.Destroy(); err != nil { return err } } 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.UnitTag(unitName) 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) }
// NewDeployerAPI creates a new server-side DeployerAPI facade. func NewDeployerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*DeployerAPI, error) { if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } getAuthFunc := func() (common.AuthFunc, error) { // Get all units of the machine and cache them. thisMachineTag := authorizer.GetAuthTag() units, err := getAllUnits(st, thisMachineTag) if err != nil { return nil, err } // Then we just check if the unit is already known. return func(tag string) bool { for _, unit := range units { if names.UnitTag(unit) == tag { return true } } return false }, nil } getCanWatch := func() (common.AuthFunc, error) { return authorizer.AuthOwner, nil } return &DeployerAPI{ Remover: common.NewRemover(st, true, getAuthFunc), PasswordChanger: common.NewPasswordChanger(st, getAuthFunc), LifeGetter: common.NewLifeGetter(st, getAuthFunc), StateAddresser: common.NewStateAddresser(st), APIAddresser: common.NewAPIAddresser(st, resources), UnitsWatcher: common.NewUnitsWatcher(st, resources, getCanWatch), st: st, resources: resources, authorizer: authorizer, }, nil }
func (fix *SimpleToolsFixture) checkUnitInstalled(c *gc.C, name, password string) { tag := names.UnitTag(name) uconfPath, _, toolsDir := fix.paths(tag) uconfData, err := ioutil.ReadFile(uconfPath) c.Assert(err, gc.IsNil) uconf := string(uconfData) var execLine string for _, line := range strings.Split(uconf, "\n") { if strings.HasPrefix(line, "exec ") { execLine = line break } } if execLine == "" { c.Fatalf("no command found in %s:\n%s", uconfPath, uconf) } logPath := filepath.Join(fix.logDir, tag+".log") jujudPath := filepath.Join(toolsDir, "jujud") for _, pat := range []string{ "^exec " + jujudPath + " unit ", " --unit-name " + name + " ", " >> " + logPath + " 2>&1$", } { match, err := regexp.MatchString(pat, execLine) c.Assert(err, gc.IsNil) if !match { c.Fatalf("failed to match:\n%s\nin:\n%s", pat, execLine) } } conf, err := agent.ReadConfig(agent.ConfigPath(fix.dataDir, tag)) c.Assert(err, gc.IsNil) c.Assert(conf.Tag(), gc.Equals, tag) c.Assert(conf.DataDir(), gc.Equals, fix.dataDir) jujudData, err := ioutil.ReadFile(jujudPath) c.Assert(err, gc.IsNil) c.Assert(string(jujudData), gc.Equals, fakeJujud) }
// ReadSettings returns a map holding the settings of the unit with the // supplied name within this relation. An error will be returned if the // relation no longer exists, or if the unit's service is not part of the // relation, or the settings are invalid; but mere non-existence of the // unit is not grounds for an error, because the unit settings are // guaranteed to persist for the lifetime of the relation, regardless // of the lifetime of the unit. func (ru *RelationUnit) ReadSettings(uname string) (params.RelationSettings, error) { tag := names.UnitTag(uname) var results params.RelationSettingsResults args := params.RelationUnitPairs{ RelationUnitPairs: []params.RelationUnitPair{{ Relation: ru.relation.tag, LocalUnit: ru.unit.tag, RemoteUnit: tag, }}, } err := ru.st.call("ReadRemoteSettings", args, &results) if err != nil { return nil, err } if len(results.Results) != 1 { return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) } result := results.Results[0] if result.Error != nil { return nil, result.Error } return result.Settings, nil }
func (c *RunCommand) Init(args []string) error { // make sure we aren't in an existing hook context if contextId, err := getenv("JUJU_CONTEXT_ID"); err == nil && contextId != "" { return fmt.Errorf("juju-run cannot be called from within a hook, have context %q", contextId) } if !c.noContext { if len(args) < 1 { return fmt.Errorf("missing unit-name") } c.unit, args = args[0], args[1:] // If the command line param is a unit id (like service/2) we need to // change it to the unit tag as that is the format of the agent directory // on disk (unit-service-2). if names.IsUnit(c.unit) { c.unit = names.UnitTag(c.unit) } } if len(args) < 1 { return fmt.Errorf("missing commands") } c.commands, args = args[0], args[1:] return cmd.CheckEmpty(args) }
// unitsChanged responds to changes to the assigned units. func (fw *Firewaller) unitsChanged(change *unitsChange) error { changed := []*unitData{} for _, name := range change.units { unit, err := fw.st.Unit(names.UnitTag(name)) if err != nil && !params.IsCodeNotFound(err) { return err } var machineTag string if unit != nil { machineTag, err = unit.AssignedMachine() if params.IsCodeNotFound(err) { continue } else if err != nil && !params.IsCodeNotAssigned(err) { return err } } if unitd, known := fw.unitds[name]; known { knownMachineTag := fw.unitds[name].machined.tag if unit == nil || unit.Life() == params.Dead || machineTag != knownMachineTag { fw.forgetUnit(unitd) changed = append(changed, unitd) logger.Debugf("stopped watching unit %s", name) } } else if unit != nil && unit.Life() != params.Dead && fw.machineds[machineTag] != nil { err = fw.startUnit(unit, machineTag) if err != nil { return err } changed = append(changed, fw.unitds[name]) logger.Debugf("started watching unit %s", name) } } if err := fw.flushUnits(changed); err != nil { return errgo.Annotate(err, "cannot change firewall ports") } return nil }
func (a *UnitAgent) Tag() string { return names.UnitTag(a.UnitName) }
func (ctx *SimpleContext) DeployUnit(unitName, initialPassword string) (err error) { // Check sanity. svc := ctx.upstartService(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.UnitTag(unitName) dataDir := ctx.agentConfig.DataDir() logDir := ctx.agentConfig.LogDir() _, err = tools.ChangeAgentTools(dataDir, tag, version.Current) toolsDir := tools.ToolsDir(dataDir, tag) 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+".log") cmd := strings.Join([]string{ path.Join(toolsDir, "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. uconf := &upstart.Conf{ Service: *svc, Desc: "juju unit agent for " + unitName, Cmd: cmd, Out: logPath, Env: map[string]string{ osenv.JujuContainerTypeEnvKey: containerType, }, } return uconf.Install() }
func (s *unitSuite) TestUnitTag(c *gc.C) { c.Assert(names.UnitTag("wordpress/2"), gc.Equals, "unit-wordpress-2") }
// Tag returns a name identifying the unit 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 (u *Unit) Tag() string { return names.UnitTag(u.Name()) }
func (f *fakeUnitAgent) Tag() string { return names.UnitTag(f.unitName) }
func (c *HooksContext) ClientFileLock() string { basename := fmt.Sprintf("juju-%s-debug-hooks", names.UnitTag(c.Unit)) return filepath.Join(c.FlockDir, basename) }