Merge lp:~themue/pyjuju/go-state-remove-relation into lp:pyjuju/go

Proposed by Frank Mueller
Status: Merged
Approved by: Gustavo Niemeyer
Approved revision: 165
Merged at revision: 207
Proposed branch: lp:~themue/pyjuju/go-state-remove-relation
Merge into: lp:pyjuju/go
Prerequisite: lp:~themue/pyjuju/go-state-add-relation
Diff against target: 131 lines (+71/-4)
5 files modified
state/export_test.go (+13/-3)
state/relation.go (+5/-0)
state/state.go (+13/-0)
state/state_test.go (+35/-0)
state/topology.go (+5/-1)
To merge this branch: bzr merge lp:~themue/pyjuju/go-state-remove-relation
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+108037@code.launchpad.net

Description of the change

state: Added the method RemoveRelation() to State.

Relation is now an interface defining the single
method relationKey() to allow a function signature
like today in Python. The implementation is now
named relation and can be accessed via type
assertion.

https://codereview.appspot.com/6250076/

To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

https://codereview.appspot.com/6250076/diff/1/state/relation.go
File state/relation.go (right):

https://codereview.appspot.com/6250076/diff/1/state/relation.go#newcode52
state/relation.go:52: relationKey() string
Why is this being turned into an interface that has a single
implementation and no chance of ever being implemented by anyone outside
of this package?

I've read the description of the CL, but it still makes no sense to me.

https://codereview.appspot.com/6250076/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

> Any other solution idea is welcome. Due to the simple logic it can
also be split
> into the two methods RemoveRelation() and RemoveServiceRelation().

+1

https://codereview.appspot.com/6250076/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

On 2012/05/31 10:22:16, niemeyer wrote:
> > Any other solution idea is welcome. Due to the simple logic it can
also be
> split
> > into the two methods RemoveRelation() and RemoveServiceRelation().

> +1

Actually, sorry, that's misleading. I suggest adding a Relation() method
to the ServiceRelation type.

https://codereview.appspot.com/6250076/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

<niemeyer> TheMue: I see you're renaming from zkRelation to
topoRelation.. are tests broken at the moment without that?
<TheMue> niemeyer: Without renaming it has been broken.
<TheMue> niemeyer: Yes.
<niemeyer> TheMue: Ok :(
<niemeyer> TheMue: Trunk is being broken way too often :(
<niemeyer> 382 t.RemoveRelation(relation.key)
<niemeyer> 383 return nil
<niemeyer> TheMue: Is RemoveRelation returning no errorr?
<TheMue> niemeyer: No, it's only removing inside a map. Today it's also
not checked at that point if it exists in that dir.
<niemeyer> TheMue: OK
<niemeyer> / Relation returns the relation with key from the topology.
<niemeyer> func (t *topology) Relation(key string) (*topoRelation,
error) {
<niemeyer> if t.topology.Relations == nil ||
t.topology.Relations[key] == nil {
<niemeyer> return nil, fmt.Errorf("relation %q does not
exist", key)
<niemeyer> TheMue: We don't want the user to be getting this error
message
<TheMue> niemeyer: Oh, please wait.Just seen an assert_relation there.
Will add it too.
<niemeyer> TheMue: The relation key is completely uninteresting as far
as a user is concerned
<niemeyer> TheMue: The state method should verify the state in those
cases, and error early with a sensible message
<TheMue> niemeyer: OK
<niemeyer> TheMue: If we get an Relation from relation we should display
prefixed with something saying that an unexpected action happened while
doing whatever
<niemeyer> TheMue: In which case the relation key might go out, but we
have no idea about why it was failing since we've checked it early
<niemeyer> TheMue: So it's debugging rather than something we'd like the
user to be commonly looking at
<TheMue> niemeyer: IC. Do you note it in the review?
<niemeyer> TheMue: I'll copy & paste what I just said

https://codereview.appspot.com/6250076/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

https://codereview.appspot.com/6250076/diff/6032/state/relation.go
File state/relation.go (right):

https://codereview.appspot.com/6250076/diff/6032/state/relation.go#newcode84
state/relation.go:84: func (r *ServiceRelation) Relation() *Relation {
This needs a test.

https://codereview.appspot.com/6250076/diff/6032/state/state_test.go
File state/state_test.go (right):

https://codereview.appspot.com/6250076/diff/6032/state/state_test.go#newcode1136
state/state_test.go:1136: func (s *StateSuite)
TestRemoveRServiceRelation(c *C) {
Please drop this test. RemoveRelation is tested above. We don't have to
test that RemoveRelation works with the result of
ServiceRelation.Relation. We have to test that ServiceRelation.Relation,
by itself, works.

https://codereview.appspot.com/6250076/diff/6032/state/topology.go
File state/topology.go (right):

https://codereview.appspot.com/6250076/diff/6032/state/topology.go#newcode403
state/topology.go:403: return nil, fmt.Errorf("relation does not exist")
Please revert this change. The goal isn't to prevent this from showing
the key. The goal is to make sure that the high level layer presents a
*good* high-level error.

Going through the state code I see that errors continue to be all over
the place. We've talked about this before, and didn't really do it. Now
may be the right time to stop and go back to fix our debt in the error
management of the state code.

We can go ahead and integrate this, but please do revert this change so
that it continues to show the proper key in the low-level interface
error.

In the high-level interface, please add a TODO so we remember it's
broken.

https://codereview.appspot.com/6250076/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

By the way, as a suggestion for testing ServiceRelation.Relation, just a
simple HasRelation check with a positive and a negative test should do.

https://codereview.appspot.com/6250076/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Unfortunately the CL is dirty again with unrelated changes.

Please let me know once it's ready for another look.

https://codereview.appspot.com/6250076/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

LGTM, thanks. Just a trivial before submitting:

https://codereview.appspot.com/6250076/diff/17003/state/state.go
File state/state.go (right):

https://codereview.appspot.com/6250076/diff/17003/state/state.go#newcode377
state/state.go:377: // TODO: Improve high-level errors, no low-level
passing.
Please move this to within the function. Doesn't make sense to have it
exposed in the API documentation.

https://codereview.appspot.com/6250076/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'state/export_test.go'
--- state/export_test.go 2012-05-28 23:15:30 +0000
+++ state/export_test.go 2012-06-06 13:08:23 +0000
@@ -1,6 +1,3 @@
1// launchpad.net/juju/state
2//
3// Copyright (c) 2011-2012 Canonical Ltd.
4package state1package state
52
6import (3import (
@@ -27,4 +24,17 @@
27 return readConfigNode(st.zk, path)24 return readConfigNode(st.zk, path)
28}25}
2926
27// HasRelation tests if the topology contains a relation.
28func HasRelation(st *State, relation *Relation) (bool, error) {
29 t, err := readTopology(st.zk)
30 if err != nil {
31 return false, err
32 }
33 _, err = t.Relation(relation.key)
34 if err != nil {
35 return false, err
36 }
37 return true, nil
38}
39
30func Diff(a, b []string) []string { return diff(a, b) }40func Diff(a, b []string) []string { return diff(a, b) }
3141
=== modified file 'state/relation.go'
--- state/relation.go 2012-05-31 16:19:15 +0000
+++ state/relation.go 2012-06-06 13:08:23 +0000
@@ -79,3 +79,8 @@
79func (r *ServiceRelation) RelationName() string {79func (r *ServiceRelation) RelationName() string {
80 return r.relationName80 return r.relationName
81}81}
82
83// Relation returns the relation for this service relation.
84func (r *ServiceRelation) Relation() *Relation {
85 return &Relation{r.st, r.relationKey}
86}
8287
=== modified file 'state/state.go'
--- state/state.go 2012-06-05 21:31:50 +0000
+++ state/state.go 2012-06-06 13:08:23 +0000
@@ -372,3 +372,16 @@
372 }372 }
373 return &Relation{s, relationKey}, serviceRelations, nil373 return &Relation{s, relationKey}, serviceRelations, nil
374}374}
375
376// RemoveRelation removes the relation.
377// TODO: Improve high-level errors, no low-level passing.
378func (s *State) RemoveRelation(relation *Relation) error {
379 removeRelation := func(t *topology) error {
380 _, err := t.Relation(relation.key)
381 if err != nil {
382 return fmt.Errorf("can't remove relation: %v", err)
383 }
384 return t.RemoveRelation(relation.key)
385 }
386 return retryTopologyChange(s.zk, removeRelation)
387}
375388
=== modified file 'state/state_test.go'
--- state/state_test.go 2012-06-06 10:17:40 +0000
+++ state/state_test.go 2012-06-06 13:08:23 +0000
@@ -1085,7 +1085,24 @@
1085 c.Assert(serviceRelations[0].RelationScope(), Equals, state.ScopeGlobal)1085 c.Assert(serviceRelations[0].RelationScope(), Equals, state.ScopeGlobal)
1086 c.Assert(serviceRelations[0].RelationRole(), Equals, state.RolePeer)1086 c.Assert(serviceRelations[0].RelationRole(), Equals, state.RolePeer)
1087 c.Assert(serviceRelations[0].RelationName(), Equals, "cache")1087 c.Assert(serviceRelations[0].RelationName(), Equals, "cache")
1088}
10881089
1090func (s *StateSuite) TestServiceRelationRelation(c *C) {
1091 dummy := s.addDummyCharm(c)
1092 s.st.AddService("riak", dummy)
1093 riakep := state.RelationEndpoint{"riak", "ring", "cache", state.RolePeer, state.ScopeGlobal}
1094 relation, serviceRelations, err := s.st.AddRelation(riakep)
1095 c.Assert(err, IsNil)
1096 c.Assert(relation, NotNil)
1097 c.Assert(serviceRelations, HasLen, 1)
1098 ok, err := state.HasRelation(s.st, serviceRelations[0].Relation())
1099 c.Assert(err, IsNil)
1100 c.Assert(ok, Equals, true)
1101 // Negative test.
1102 err = s.st.RemoveRelation(relation)
1103 c.Assert(err, IsNil)
1104 _, err = state.HasRelation(s.st, serviceRelations[0].Relation())
1105 c.Assert(err, ErrorMatches, `relation "relation-0000000000" does not exist`)
1089}1106}
10901107
1091func (s *StateSuite) TestAddRelationMissingService(c *C) {1108func (s *StateSuite) TestAddRelationMissingService(c *C) {
@@ -1169,3 +1186,21 @@
1169 c.Assert(err, IsNil)1186 c.Assert(err, IsNil)
1170 c.Assert(env.Map(), DeepEquals, map[string]interface{}{"type": "dummy", "name": "foo"})1187 c.Assert(env.Map(), DeepEquals, map[string]interface{}{"type": "dummy", "name": "foo"})
1171}1188}
1189
1190func (s *StateSuite) TestRemoveRelation(c *C) {
1191 dummy := s.addDummyCharm(c)
1192 s.st.AddService("riak", dummy)
1193 riakep := state.RelationEndpoint{"riak", "blog", "cache", state.RolePeer, state.ScopeGlobal}
1194 relation, _, err := s.st.AddRelation(riakep)
1195 c.Assert(err, IsNil)
1196 c.Assert(relation, NotNil)
1197 hasRelation, err := state.HasRelation(s.st, relation)
1198 c.Assert(err, IsNil)
1199 c.Assert(hasRelation, Equals, true)
1200 err = s.st.RemoveRelation(relation)
1201 hasRelation, err = state.HasRelation(s.st, relation)
1202 c.Assert(hasRelation, Equals, false)
1203 c.Assert(err, ErrorMatches, `relation "relation-0000000000" does not exist`)
1204 err = s.st.RemoveRelation(relation)
1205 c.Assert(err, ErrorMatches, `can't remove relation: relation "relation-0000000000" does not exist`)
1206}
11721207
=== modified file 'state/topology.go'
--- state/topology.go 2012-05-31 09:48:26 +0000
+++ state/topology.go 2012-06-06 13:08:23 +0000
@@ -445,8 +445,12 @@
445}445}
446446
447// RemoveRelation removes the relation with key from the topology.447// RemoveRelation removes the relation with key from the topology.
448func (t *topology) RemoveRelation(key string) {448func (t *topology) RemoveRelation(key string) error {
449 if err := t.assertRelation(key); err != nil {
450 return err
451 }
449 delete(t.topology.Relations, key)452 delete(t.topology.Relations, key)
453 return nil
450}454}
451455
452// RelationsForService returns all relations that the service456// RelationsForService returns all relations that the service

Subscribers

People subscribed via source and target branches