Exemple #1
0
func (s *RelationerSuite) SetUpTest(c *gc.C) {
	s.JujuConnSuite.SetUpTest(c)
	var err error
	s.svc = s.AddTestingService(c, "u", s.AddTestingCharm(c, "riak"))
	c.Assert(err, gc.IsNil)
	rels, err := s.svc.Relations()
	c.Assert(err, gc.IsNil)
	c.Assert(rels, gc.HasLen, 1)
	s.rel = rels[0]
	_, unit := s.AddRelationUnit(c, "u/0")
	s.dirPath = c.MkDir()
	s.dir, err = relation.ReadStateDir(s.dirPath, s.rel.Id())
	c.Assert(err, gc.IsNil)
	s.hooks = make(chan hook.Info)

	password, err := utils.RandomPassword()
	c.Assert(err, gc.IsNil)
	err = unit.SetPassword(password)
	c.Assert(err, gc.IsNil)
	s.st = s.OpenAPIAs(c, unit.Tag(), password)
	s.uniter = s.st.Uniter()
	c.Assert(s.uniter, gc.NotNil)

	apiUnit, err := s.uniter.Unit(unit.Tag())
	c.Assert(err, gc.IsNil)
	apiRel, err := s.uniter.Relation(s.rel.Tag())
	c.Assert(err, gc.IsNil)
	s.apiRelUnit, err = apiRel.Unit(apiUnit)
	c.Assert(err, gc.IsNil)
}
Exemple #2
0
func (s *StateDirSuite) TestWrite(c *gc.C) {
	for i, t := range writeTests {
		c.Logf("test %d", i)
		basedir := c.MkDir()
		setUpDir(c, basedir, "123", map[string]string{
			"foo-1": "change-version: 0\n",
			"foo-2": "change-version: 0\n",
		})
		dir, err := relation.ReadStateDir(basedir, 123)
		c.Assert(err, jc.ErrorIsNil)
		for i, hi := range t.hooks {
			c.Logf("  hook %d", i)
			if i == len(t.hooks)-1 && t.err != "" {
				err = dir.State().Validate(hi)
				expect := fmt.Sprintf(`inappropriate %q for %q: %s`, hi.Kind, hi.RemoteUnit, t.err)
				c.Assert(err, gc.ErrorMatches, expect)
			} else {
				err = dir.State().Validate(hi)
				c.Assert(err, jc.ErrorIsNil)
				err = dir.Write(hi)
				c.Assert(err, jc.ErrorIsNil)
				// Check that writing the same change again is OK.
				err = dir.Write(hi)
				c.Assert(err, jc.ErrorIsNil)
			}
		}
		members := t.members
		if members == nil && !t.deleted {
			members = defaultMembers
		}
		assertState(c, dir, basedir, 123, members, t.pending, t.deleted)
	}
}
Exemple #3
0
// restoreRelations reconciles the local relation state dirs with the
// remote state of the corresponding relations.
func (u *Uniter) restoreRelations() error {
	joinedRelations, err := u.getJoinedRelations()
	if err != nil {
		return err
	}
	knownDirs, err := relation.ReadAllStateDirs(u.relationsDir)
	if err != nil {
		return err
	}
	for id, dir := range knownDirs {
		if rel, ok := joinedRelations[id]; ok {
			if err := u.addRelation(rel, dir); err != nil {
				return err
			}
		} else if err := dir.Remove(); err != nil {
			return err
		}
	}
	for id, rel := range joinedRelations {
		if _, ok := knownDirs[id]; ok {
			continue
		}
		dir, err := relation.ReadStateDir(u.relationsDir, id)
		if err != nil {
			return err
		}
		if err := u.addRelation(rel, dir); err != nil {
			return err
		}
	}
	return nil
}
Exemple #4
0
func (s *RelationerImplicitSuite) TestImplicitRelationer(c *gc.C) {
	// Create a relationer for an implicit endpoint (mysql:juju-info).
	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
	u, err := mysql.AddUnit()
	c.Assert(err, jc.ErrorIsNil)
	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
	c.Assert(err, jc.ErrorIsNil)
	err = u.AssignToMachine(machine)
	c.Assert(err, jc.ErrorIsNil)
	err = machine.SetProviderAddresses(network.NewScopedAddress("blah", network.ScopeCloudLocal))
	c.Assert(err, jc.ErrorIsNil)
	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
	eps, err := s.State.InferEndpoints("logging", "mysql")
	c.Assert(err, jc.ErrorIsNil)
	rel, err := s.State.AddRelation(eps...)
	c.Assert(err, jc.ErrorIsNil)
	relsDir := c.MkDir()
	dir, err := relation.ReadStateDir(relsDir, rel.Id())
	c.Assert(err, jc.ErrorIsNil)

	password, err := utils.RandomPassword()
	c.Assert(err, jc.ErrorIsNil)
	err = u.SetPassword(password)
	c.Assert(err, jc.ErrorIsNil)
	st := s.OpenAPIAs(c, u.Tag(), password)
	uniterState, err := st.Uniter()
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(uniterState, gc.NotNil)

	apiUnit, err := uniterState.Unit(u.Tag().(names.UnitTag))
	c.Assert(err, jc.ErrorIsNil)
	apiRel, err := uniterState.Relation(rel.Tag().(names.RelationTag))
	c.Assert(err, jc.ErrorIsNil)
	apiRelUnit, err := apiRel.Unit(apiUnit)
	c.Assert(err, jc.ErrorIsNil)

	r := relation.NewRelationer(apiRelUnit, dir)
	c.Assert(r, jc.Satisfies, (*relation.Relationer).IsImplicit)

	// Hooks are not allowed.
	f := func() { r.PrepareHook(hook.Info{}) }
	c.Assert(f, gc.PanicMatches, "implicit relations must not run hooks")
	f = func() { r.CommitHook(hook.Info{}) }
	c.Assert(f, gc.PanicMatches, "implicit relations must not run hooks")

	// Set it to Dying; check that the dir is removed immediately.
	err = r.SetDying()
	c.Assert(err, jc.ErrorIsNil)
	path := strconv.Itoa(rel.Id())
	ft.Removed{path}.Check(c, relsDir)

	err = rel.Destroy()
	c.Assert(err, jc.ErrorIsNil)
	err = rel.Refresh()
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
}
Exemple #5
0
func (s *StateDirSuite) TestRemove(c *gc.C) {
	basedir := c.MkDir()
	dir, err := relation.ReadStateDir(basedir, 1)
	c.Assert(err, gc.IsNil)
	err = dir.Ensure()
	c.Assert(err, gc.IsNil)
	err = dir.Remove()
	c.Assert(err, gc.IsNil)
	err = dir.Remove()
	c.Assert(err, gc.IsNil)

	setUpDir(c, basedir, "99", map[string]string{
		"foo-1": "change-version: 0\n",
	})
	dir, err = relation.ReadStateDir(basedir, 99)
	c.Assert(err, gc.IsNil)
	err = dir.Remove()
	c.Assert(err, gc.ErrorMatches, ".*: directory not empty")
}
Exemple #6
0
func (s *StateDirSuite) TestRemove(c *gc.C) {
	basedir := c.MkDir()
	dir, err := relation.ReadStateDir(basedir, 1)
	c.Assert(err, jc.ErrorIsNil)
	err = dir.Ensure()
	c.Assert(err, jc.ErrorIsNil)
	err = dir.Remove()
	c.Assert(err, jc.ErrorIsNil)
	err = dir.Remove()
	c.Assert(err, jc.ErrorIsNil)

	setUpDir(c, basedir, "99", map[string]string{
		"foo-1": "change-version: 0\n",
	})
	dir, err = relation.ReadStateDir(basedir, 99)
	c.Assert(err, jc.ErrorIsNil)
	err = dir.Remove()
	// Windows message is The directory is not empty
	// Unix message is directory not empty
	c.Assert(err, gc.ErrorMatches, ".* directory (is )?not empty.?")
}
Exemple #7
0
func (s *StateDirSuite) TestBadRelations(c *gc.C) {
	for i, t := range badRelationsTests {
		c.Logf("test %d", i)
		basedir := c.MkDir()
		reldir := setUpDir(c, basedir, "123", t.contents)
		for _, subdir := range t.subdirs {
			setUpDir(c, reldir, subdir, nil)
		}
		_, err := relation.ReadStateDir(basedir, 123)
		expect := `cannot load relation state from ".*": ` + t.err
		c.Assert(err, gc.ErrorMatches, expect)
	}
}
Exemple #8
0
func assertState(c *gc.C, dir *relation.StateDir, relsdir string, relationId int, members msi, pending string, deleted bool) {
	expect := &relation.State{
		RelationId:     relationId,
		Members:        map[string]int64(members),
		ChangedPending: pending,
	}
	c.Assert(dir.State(), gc.DeepEquals, expect)
	if deleted {
		_, err := os.Stat(filepath.Join(relsdir, strconv.Itoa(relationId)))
		c.Assert(err, jc.Satisfies, os.IsNotExist)
	} else {
		fresh, err := relation.ReadStateDir(relsdir, relationId)
		c.Assert(err, jc.ErrorIsNil)
		c.Assert(fresh.State(), gc.DeepEquals, expect)
	}
}
Exemple #9
0
func (s *StateDirSuite) TestReadStateDirValid(c *gc.C) {
	basedir := c.MkDir()
	reldir := setUpDir(c, basedir, "123", map[string]string{
		"foo-bar-1":           "change-version: 99\n",
		"foo-bar-1.preparing": "change-version: 100\n",
		"baz-qux-7":           "change-version: 101\nchanged-pending: true\n",
		"nonsensical":         "blah",
		"27":                  "blah",
	})
	setUpDir(c, reldir, "ignored", nil)

	dir, err := relation.ReadStateDir(basedir, 123)
	c.Assert(err, jc.ErrorIsNil)
	state := dir.State()
	c.Assert(state.RelationId, gc.Equals, 123)
	c.Assert(msi(state.Members), gc.DeepEquals, msi{"foo-bar/1": 99, "baz-qux/7": 101})
	c.Assert(state.ChangedPending, gc.Equals, "baz-qux/7")
}
Exemple #10
0
func (s *StateDirSuite) TestReadStateDirEmpty(c *gc.C) {
	basedir := c.MkDir()
	reldir := filepath.Join(basedir, "123")

	dir, err := relation.ReadStateDir(basedir, 123)
	c.Assert(err, jc.ErrorIsNil)
	state := dir.State()
	c.Assert(state.RelationId, gc.Equals, 123)
	c.Assert(msi(state.Members), gc.DeepEquals, msi{})
	c.Assert(state.ChangedPending, gc.Equals, "")

	_, err = os.Stat(reldir)
	c.Assert(err, jc.Satisfies, os.IsNotExist)

	err = dir.Ensure()
	c.Assert(err, jc.ErrorIsNil)
	fi, err := os.Stat(reldir)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(fi, jc.Satisfies, os.FileInfo.IsDir)
}
Exemple #11
0
// init reconciles the local relation state dirs with the remote state of
// the corresponding relations. It's only expected to be called while a
// *relations is being created.
func (r *relations) init() error {
	joinedRelationTags, err := r.unit.JoinedRelations()
	if err != nil {
		return errors.Trace(err)
	}
	joinedRelations := make(map[int]*uniter.Relation)
	for _, tag := range joinedRelationTags {
		relation, err := r.st.Relation(tag)
		if err != nil {
			return errors.Trace(err)
		}
		joinedRelations[relation.Id()] = relation
	}
	knownDirs, err := relation.ReadAllStateDirs(r.relationsDir)
	if err != nil {
		return errors.Trace(err)
	}
	for id, dir := range knownDirs {
		if rel, ok := joinedRelations[id]; ok {
			if err := r.add(rel, dir); err != nil {
				return errors.Trace(err)
			}
		} else if err := dir.Remove(); err != nil {
			return errors.Trace(err)
		}
	}
	for id, rel := range joinedRelations {
		if _, ok := knownDirs[id]; ok {
			continue
		}
		dir, err := relation.ReadStateDir(r.relationsDir, id)
		if err != nil {
			return errors.Trace(err)
		}
		if err := r.add(rel, dir); err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}
Exemple #12
0
func (s *RelationerImplicitSuite) TestImplicitRelationer(c *gc.C) {
	// Create a relationer for an implicit endpoint (mysql:juju-info).
	mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
	u, err := mysql.AddUnit()
	c.Assert(err, gc.IsNil)
	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	err = u.AssignToMachine(machine)
	c.Assert(err, gc.IsNil)
	err = machine.SetAddresses(network.NewAddress("blah", network.ScopeCloudLocal))
	c.Assert(err, gc.IsNil)
	logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
	eps, err := s.State.InferEndpoints([]string{"logging", "mysql"})
	c.Assert(err, gc.IsNil)
	rel, err := s.State.AddRelation(eps...)
	c.Assert(err, gc.IsNil)
	relsDir := c.MkDir()
	dir, err := relation.ReadStateDir(relsDir, rel.Id())
	c.Assert(err, gc.IsNil)
	hooks := make(chan hook.Info)

	password, err := utils.RandomPassword()
	c.Assert(err, gc.IsNil)
	err = u.SetPassword(password)
	c.Assert(err, gc.IsNil)
	st := s.OpenAPIAs(c, u.Tag(), password)
	uniterState := st.Uniter()
	c.Assert(uniterState, gc.NotNil)

	apiUnit, err := uniterState.Unit(u.Tag())
	c.Assert(err, gc.IsNil)
	apiRel, err := uniterState.Relation(rel.Tag())
	c.Assert(err, gc.IsNil)
	apiRelUnit, err := apiRel.Unit(apiUnit)
	c.Assert(err, gc.IsNil)

	r := uniter.NewRelationer(apiRelUnit, dir, hooks)
	c.Assert(r, jc.Satisfies, (*uniter.Relationer).IsImplicit)

	// Join the relation.
	err = r.Join()
	c.Assert(err, gc.IsNil)
	sub, err := logging.Unit("logging/0")
	c.Assert(err, gc.IsNil)

	// Join the other side; check no hooks are sent.
	r.StartHooks()
	defer func() { c.Assert(r.StopHooks(), gc.IsNil) }()
	subru, err := rel.Unit(sub)
	c.Assert(err, gc.IsNil)
	err = subru.EnterScope(map[string]interface{}{"some": "data"})
	c.Assert(err, gc.IsNil)
	s.State.StartSync()
	select {
	case <-time.After(coretesting.ShortWait):
	case <-hooks:
		c.Fatalf("unexpected hook generated")
	}

	// Set it to Dying; check that the dir is removed immediately.
	err = r.SetDying()
	c.Assert(err, gc.IsNil)
	path := strconv.Itoa(rel.Id())
	ft.Removed{path}.Check(c, relsDir)

	// Check that it left scope, by leaving scope on the other side and destroying
	// the relation.
	err = subru.LeaveScope()
	c.Assert(err, gc.IsNil)
	err = rel.Destroy()
	c.Assert(err, gc.IsNil)
	err = rel.Refresh()
	c.Assert(err, jc.Satisfies, errors.IsNotFound)

	// Verify that no other hooks were sent at any stage.
	select {
	case <-hooks:
		c.Fatalf("unexpected hook generated")
	default:
	}
}
Exemple #13
0
// updateRelations responds to changes in the life states of the relations
// with the supplied ids. If any id corresponds to an alive relation not
// known to the unit, the uniter will join that relation and return its
// relationer in the added list.
func (u *Uniter) updateRelations(ids []int) (added []*Relationer, err error) {
	for _, id := range ids {
		if r, found := u.relationers[id]; found {
			rel := r.ru.Relation()
			if err := rel.Refresh(); err != nil {
				return nil, fmt.Errorf("cannot update relation %q: %v", rel, err)
			}
			if rel.Life() == params.Dying {
				if err := r.SetDying(); err != nil {
					return nil, err
				} else if r.IsImplicit() {
					delete(u.relationers, id)
				}
			}
			continue
		}
		// Relations that are not alive are simply skipped, because they
		// were not previously known anyway.
		rel, err := u.st.RelationById(id)
		if err != nil {
			if params.IsCodeNotFoundOrCodeUnauthorized(err) {
				continue
			}
			return nil, err
		}
		if rel.Life() != params.Alive {
			continue
		}
		// Make sure we ignore relations not implemented by the unit's charm.
		ch, err := corecharm.ReadDir(u.charmPath)
		if err != nil {
			return nil, err
		}
		if ep, err := rel.Endpoint(); err != nil {
			return nil, err
		} else if !ep.ImplementedBy(ch) {
			logger.Warningf("skipping relation with unknown endpoint %q", ep.Name)
			continue
		}
		dir, err := relation.ReadStateDir(u.relationsDir, id)
		if err != nil {
			return nil, err
		}
		err = u.addRelation(rel, dir)
		if err == nil {
			added = append(added, u.relationers[id])
			continue
		}
		e := dir.Remove()
		if !params.IsCodeCannotEnterScope(err) {
			return nil, err
		}
		if e != nil {
			return nil, e
		}
	}
	if ok, err := u.unit.IsPrincipal(); err != nil {
		return nil, err
	} else if ok {
		return added, nil
	}
	// If no Alive relations remain between a subordinate unit's service
	// and its principal's service, the subordinate must become Dying.
	keepAlive := false
	for _, r := range u.relationers {
		scope := r.ru.Endpoint().Scope
		if scope == corecharm.ScopeContainer && !r.dying {
			keepAlive = true
			break
		}
	}
	if !keepAlive {
		if err := u.unit.Destroy(); err != nil {
			return nil, err
		}
	}
	return added, nil
}
Exemple #14
0
// Update is part of the Relations interface.
func (r *relations) Update(ids []int) error {
	for _, id := range ids {
		if relationer, found := r.relationers[id]; found {
			rel := relationer.ru.Relation()
			if err := rel.Refresh(); err != nil {
				return errors.Annotatef(err, "cannot update relation %q", rel)
			}
			if rel.Life() == params.Dying {
				if err := r.setDying(id); err != nil {
					return errors.Trace(err)
				}
			}
			continue
		}
		// Relations that are not alive are simply skipped, because they
		// were not previously known anyway.
		rel, err := r.st.RelationById(id)
		if err != nil {
			if params.IsCodeNotFoundOrCodeUnauthorized(err) {
				continue
			}
			return errors.Trace(err)
		}
		if rel.Life() != params.Alive {
			continue
		}
		// Make sure we ignore relations not implemented by the unit's charm.
		ch, err := corecharm.ReadCharmDir(r.charmDir)
		if err != nil {
			return errors.Trace(err)
		}
		if ep, err := rel.Endpoint(); err != nil {
			return errors.Trace(err)
		} else if !ep.ImplementedBy(ch) {
			logger.Warningf("skipping relation with unknown endpoint %q", ep.Name)
			continue
		}
		dir, err := relation.ReadStateDir(r.relationsDir, id)
		if err != nil {
			return errors.Trace(err)
		}
		err = r.add(rel, dir)
		if err == nil {
			r.relationers[id].StartHooks()
			continue
		}
		e := dir.Remove()
		if !params.IsCodeCannotEnterScope(err) {
			return errors.Trace(err)
		}
		if e != nil {
			return errors.Trace(e)
		}
	}
	if ok, err := r.unit.IsPrincipal(); err != nil {
		return errors.Trace(err)
	} else if ok {
		return nil
	}
	// If no Alive relations remain between a subordinate unit's service
	// and its principal's service, the subordinate must become Dying.
	for _, relationer := range r.relationers {
		scope := relationer.ru.Endpoint().Scope
		if scope == corecharm.ScopeContainer && !relationer.dying {
			return nil
		}
	}
	return r.unit.Destroy()
}