func (s *StateDirSuite) TestWrite(c *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, IsNil) 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, ErrorMatches, expect) } else { err = dir.State().Validate(hi) c.Assert(err, IsNil) err = dir.Write(hi) c.Assert(err, IsNil) // Check that writing the same change again is OK. err = dir.Write(hi) c.Assert(err, IsNil) } } members := t.members if members == nil && !t.deleted { members = defaultMembers } assertState(c, dir, basedir, 123, members, t.pending, t.deleted) } }
func (s *StateDirSuite) TestRemove(c *C) { basedir := c.MkDir() dir, err := relation.ReadStateDir(basedir, 1) c.Assert(err, IsNil) err = dir.Ensure() c.Assert(err, IsNil) err = dir.Remove() c.Assert(err, IsNil) err = dir.Remove() c.Assert(err, IsNil) setUpDir(c, basedir, "99", map[string]string{ "foo-1": "change-version: 0\n", }) dir, err = relation.ReadStateDir(basedir, 99) c.Assert(err, IsNil) err = dir.Remove() c.Assert(err, ErrorMatches, ".*: directory not empty") }
func (s *StateDirSuite) TestBadRelations(c *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, ErrorMatches, expect) } }
func (s *RelationerSuite) SetUpTest(c *C) { s.JujuConnSuite.SetUpTest(c) ch := s.AddTestingCharm(c, "dummy") var err error s.svc, err = s.State.AddService("u", ch) c.Assert(err, IsNil) s.rel, err = s.State.AddRelation(state.Endpoint{ "u", "ifce", "my-relation", state.RolePeer, charm.ScopeGlobal, }) c.Assert(err, IsNil) s.ru = s.AddRelationUnit(c, "u/0") s.dir, err = relation.ReadStateDir(c.MkDir(), s.rel.Id()) c.Assert(err, IsNil) s.hooks = make(chan hook.Info) }
func (s *RelationerSuite) SetUpTest(c *C) { s.JujuConnSuite.SetUpTest(c) var err error s.svc, err = s.State.AddService("u", s.AddTestingCharm(c, "riak")) c.Assert(err, IsNil) rels, err := s.svc.Relations() c.Assert(err, IsNil) c.Assert(rels, HasLen, 1) s.rel = rels[0] s.ru = s.AddRelationUnit(c, "u/0") s.dirPath = c.MkDir() s.dir, err = relation.ReadStateDir(s.dirPath, s.rel.Id()) c.Assert(err, IsNil) s.hooks = make(chan hook.Info) }
func assertState(c *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(), DeepEquals, expect) if deleted { _, err := os.Stat(filepath.Join(relsdir, strconv.Itoa(relationId))) c.Assert(os.IsNotExist(err), Equals, true) } else { fresh, err := relation.ReadStateDir(relsdir, relationId) c.Assert(err, IsNil) c.Assert(fresh.State(), DeepEquals, expect) } }
func (s *StateDirSuite) TestReadStateDirValid(c *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, IsNil) state := dir.State() c.Assert(state.RelationId, Equals, 123) c.Assert(msi(state.Members), DeepEquals, msi{"foo-bar/1": 99, "baz-qux/7": 101}) c.Assert(state.ChangedPending, Equals, "baz-qux/7") }
func (s *StateDirSuite) TestReadStateDirEmpty(c *C) { basedir := c.MkDir() reldir := filepath.Join(basedir, "123") dir, err := relation.ReadStateDir(basedir, 123) c.Assert(err, IsNil) state := dir.State() c.Assert(state.RelationId, Equals, 123) c.Assert(msi(state.Members), DeepEquals, msi{}) c.Assert(state.ChangedPending, Equals, "") _, err = os.Stat(reldir) c.Assert(os.IsNotExist(err), Equals, true) err = dir.Ensure() c.Assert(err, IsNil) fi, err := os.Stat(reldir) c.Assert(err, IsNil) c.Assert(fi.IsDir(), Equals, true) }
func (s *RelationerImplicitSuite) TestImplicitRelationer(c *C) { // Create a relationer for an implicit endpoint (mysql:juju-info). mysql, err := s.State.AddService("mysql", s.AddTestingCharm(c, "mysql")) c.Assert(err, IsNil) u, err := mysql.AddUnit() c.Assert(err, IsNil) err = u.SetPrivateAddress("blah") c.Assert(err, IsNil) logging, err := s.State.AddService("logging", s.AddTestingCharm(c, "logging")) c.Assert(err, IsNil) eps, err := s.State.InferEndpoints([]string{"logging", "mysql"}) c.Assert(err, IsNil) rel, err := s.State.AddRelation(eps...) c.Assert(err, IsNil) ru, err := rel.Unit(u) c.Assert(err, IsNil) relsDir := c.MkDir() dir, err := relation.ReadStateDir(relsDir, rel.Id()) c.Assert(err, IsNil) hooks := make(chan hook.Info) r := uniter.NewRelationer(ru, dir, hooks) c.Assert(r.IsImplicit(), Equals, true) // Join the relationer; check the dir was created and scope was entered. err = r.Join() c.Assert(err, IsNil) fi, err := os.Stat(filepath.Join(relsDir, strconv.Itoa(rel.Id()))) c.Assert(err, IsNil) c.Assert(fi.IsDir(), Equals, true) sub, err := logging.Unit("logging/0") c.Assert(err, IsNil) err = sub.SetPrivateAddress("blah") c.Assert(err, IsNil) // Join the other side; check no hooks are sent. r.StartHooks() defer func() { c.Assert(r.StopHooks(), IsNil) }() subru, err := rel.Unit(sub) c.Assert(err, IsNil) err = subru.EnterScope(map[string]interface{}{"some": "data"}) c.Assert(err, IsNil) s.State.StartSync() select { case <-time.After(50 * time.Millisecond): case <-hooks: c.Fatalf("unexpected hook generated") } // Set it to Dying; check that the dir is removed. err = r.SetDying() c.Assert(err, IsNil) _, err = os.Stat(filepath.Join(relsDir, strconv.Itoa(rel.Id()))) c.Assert(os.IsNotExist(err), Equals, true) // Check that it left scope, by leaving scope on the other side and destroying // the relation. err = subru.LeaveScope() c.Assert(err, IsNil) err = rel.Destroy() c.Assert(err, IsNil) err = rel.Refresh() c.Assert(state.IsNotFound(err), Equals, true) // Verify that no other hooks were sent at any stage. select { case <-hooks: c.Fatalf("unexpected hook generated") default: } }
// 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() == state.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.Relation(id) if err != nil { if state.IsNotFound(err) { continue } return nil, err } if rel.Life() != state.Alive { 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 err != state.ErrCannotEnterScope { return nil, err } if e != nil { return nil, e } } if u.unit.IsPrincipal() { 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().RelationScope if scope == corecharm.ScopeContainer && !r.dying { keepAlive = true break } } if !keepAlive { if err := u.unit.EnsureDying(); err != nil { return nil, err } } return added, nil }