Merge lp:~themue/pyjuju/go-state-add-relation into lp:pyjuju/go
- go-state-add-relation
- Merge into go
Status: | Merged |
---|---|
Merged at revision: | 200 |
Proposed branch: | lp:~themue/pyjuju/go-state-add-relation |
Merge into: | lp:pyjuju/go |
Prerequisite: | lp:~themue/pyjuju/go-state-topology-relation-endpoints |
Diff against target: |
280 lines (+250/-3) 3 files modified
state/relation.go (+33/-0) state/state.go (+117/-0) state/state_test.go (+100/-3) |
To merge this branch: | bzr merge lp:~themue/pyjuju/go-state-add-relation |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+106652@code.launchpad.net |
Commit message
Description of the change
state: Added two methods to add relations to State.
AddClientServer
AddPeerRelation() a peer endpoint. They return the relation and
both service relations for client/server or the one for peer.
The seperation into two methods with well defined arguments make
it easier to ensure a valid relation creation.
Gustavo Niemeyer (niemeyer) wrote : | # |
Gustavo Niemeyer (niemeyer) wrote : | # |
https:/
File state/relation.go (right):
https:/
state/relation.
more services.
// Relation represents a link between services, or within a
// service (in the case of peer relations).
https:/
state/relation.
one or more services.
// ServiceRelation represents an established relation from
// the viewpoint of a participant service.
https:/
state/relation.
s/key/relationKey/; given the type name, this would be ambiguous.
https:/
state/relation.
relationScope, relationRole, relationName, for the same reason.
Also on the methods below please (RelationRole, RelationName, etc).
https:/
state/relation.
s/a/the/
https:/
state/relation.
// RelationRole returns the service role within the relation.
https:/
state/relation.
// RelationName returns the name this relation has within the service.
https:/
File state/state.go (right):
https:/
state/state.go:257: if relationKey != "" {
This is ignoring err. Even if it works, I'd prefer if we were more
conservative about it:
if _, ok := err.(*NoRelatio
return false, nil
}
if err != nil {
return false, err
}
return true, err
https:/
state/state.go:266: // addRelation creates the relation node depending
on the given scope.
I don't get what "depending on the given scope" is trying to tell me.
addRelation also seems like a bad name for this function. There's both
addRelation and AddRelation, and they are very different beasts.
https:/
state/state.go:274: // Creation is per container for container scoped
relations and
I don't quite get what this comment is saying either.
https:/
state/state.go:276: if scope == ScopeGlobal {
This seems weird. Why is it only introducing the settings if the
relation is global? I don't know if that makes sense, and if it does we
certainly need a clear comment here explaining the background.
https:/
state/state.go:286: // and endpoint.
s/and endpoint/endpoint/, comments above hold here and in the function
below too.
Gustavo Niemeyer (niemeyer) wrote : | # |
The code diff has a lot of unrelated code. Please let me know once the
code is clean and may be reviewed.
Gustavo Niemeyer (niemeyer) wrote : | # |
LGTM, but see the comments please:
https:/
File state/state.go (right):
https:/
state/state.go:276: if scope == ScopeGlobal {
On 2012/05/31 16:23:59, TheMue wrote:
> On 2012/05/30 22:35:42, niemeyer wrote:
> > This seems weird. Why is it only introducing the settings if the
relation is
> > global? I don't know if that makes sense, and if it does we
certainly need a
> > clear comment here explaining the background.
> I tried to make the comments more clear, they have been almost a 1:1
copy of the
> Python code. We should talk about the container scope and if the whole
method(s)
> can't be modified to handle this too. So far I don't know enough about
the
> meaning of the scope and have to analyze it first.
Okay, this branch sounds fine besides what we already talked about and
the minor points, but please do understand the scope logic. As you have
seen in the relation+topology story, writing logic when you have no idea
of what is going on is hard.
https:/
state/state.go:334: relationKey, err := s.addRelation(
On 2012/05/31 16:23:59, TheMue wrote:
> On 2012/05/30 22:35:42, niemeyer wrote:
> > Have we verified that the endpoints make sense at this point? It
looks like
> > we've checked that there is a good number of them, but I can't see
it
> verifying
> > that the endpoints make sense (service names, relation names,
interface names,
> > etc). It'd be bad to be creating state in ZooKeeper just to later
tell
> something
> > trivial to the user that we could have verified upfront. We
shouldn't be
> > duplicating this logic, though.
> Today this isn't done. I would like to do it in a new small branch
after this
> and RemoveRelation() are in.
I filed a bug to keep track of this and assigned to you:
https:/
https:/
state/state.go:369: Service: serviceRelation
On 2012/05/31 16:23:59, TheMue wrote:
> On 2012/05/30 22:35:42, niemeyer wrote:
> > serviceRelation has a public API that we can use it here, rather
than fiddling
> > with its internals assuming we know what's going on.
> >
> > Same below.
> The name can be retrieved with RelationName() but the service key is
internal so
> far. Shall a public accessor be added?
No, you're right. serviceKey is internal.
https:/
File state/state.go (right):
https:/
state/state.go:263: // container occurs in ServiceRelation
That's a lot nicer, thanks.
Would you mind to just drop the () as we tend to refer to methods by
their name without the call syntax.
https:/
state/state.go:279: if scope == ScopeGlobal {
That's a bit strange. This method only does anything at all if the scope
is not global, and the scope is only provided so it can be checked here
for not doing anything at all.
Ins...
- 164. By Frank Mueller
-
state: Merge of trunk after LGTM.
Preview Diff
1 | === modified file 'state/relation.go' | |||
2 | --- state/relation.go 2012-05-25 16:48:30 +0000 | |||
3 | +++ state/relation.go 2012-06-01 12:44:24 +0000 | |||
4 | @@ -46,3 +46,36 @@ | |||
5 | 46 | func (e RelationEndpoint) String() string { | 46 | func (e RelationEndpoint) String() string { |
6 | 47 | return e.ServiceName + ":" + e.RelationName | 47 | return e.ServiceName + ":" + e.RelationName |
7 | 48 | } | 48 | } |
8 | 49 | |||
9 | 50 | // Relation represents a link between services, or within a | ||
10 | 51 | // service (in the case of peer relations). | ||
11 | 52 | type Relation struct { | ||
12 | 53 | st *State | ||
13 | 54 | key string | ||
14 | 55 | } | ||
15 | 56 | |||
16 | 57 | // ServiceRelation represents an established relation from | ||
17 | 58 | // the viewpoint of a participant service. | ||
18 | 59 | type ServiceRelation struct { | ||
19 | 60 | st *State | ||
20 | 61 | relationKey string | ||
21 | 62 | serviceKey string | ||
22 | 63 | relationScope RelationScope | ||
23 | 64 | relationRole RelationRole | ||
24 | 65 | relationName string | ||
25 | 66 | } | ||
26 | 67 | |||
27 | 68 | // RelationScope returns the scope of the relation. | ||
28 | 69 | func (r *ServiceRelation) RelationScope() RelationScope { | ||
29 | 70 | return r.relationScope | ||
30 | 71 | } | ||
31 | 72 | |||
32 | 73 | // RelationRole returns the service role within the relation. | ||
33 | 74 | func (r *ServiceRelation) RelationRole() RelationRole { | ||
34 | 75 | return r.relationRole | ||
35 | 76 | } | ||
36 | 77 | |||
37 | 78 | // RelationName returns the name this relation has within the service. | ||
38 | 79 | func (r *ServiceRelation) RelationName() string { | ||
39 | 80 | return r.relationName | ||
40 | 81 | } | ||
41 | 49 | 82 | ||
42 | === modified file 'state/state.go' | |||
43 | --- state/state.go 2012-05-29 23:11:41 +0000 | |||
44 | +++ state/state.go 2012-06-01 12:44:24 +0000 | |||
45 | @@ -250,3 +250,120 @@ | |||
46 | 250 | } | 250 | } |
47 | 251 | return service.Unit(name) | 251 | return service.Unit(name) |
48 | 252 | } | 252 | } |
49 | 253 | |||
50 | 254 | // addRelationNode creates the relation node. | ||
51 | 255 | func (s *State) addRelationNode(scope RelationScope) (string, error) { | ||
52 | 256 | path, err := s.zk.Create("/relations/relation-", "", zookeeper.SEQUENCE, zkPermAll) | ||
53 | 257 | if err != nil { | ||
54 | 258 | return "", err | ||
55 | 259 | } | ||
56 | 260 | relationKey := strings.Split(path, "/")[2] | ||
57 | 261 | // Create the settings node only if the scope is global. | ||
58 | 262 | // In case of container scoped relations the creation per | ||
59 | 263 | // container occurs in ServiceRelation.AddUnit. | ||
60 | 264 | if scope == ScopeGlobal { | ||
61 | 265 | _, err = s.zk.Create(path+"/settings", "", 0, zkPermAll) | ||
62 | 266 | if err != nil { | ||
63 | 267 | return "", err | ||
64 | 268 | } | ||
65 | 269 | } | ||
66 | 270 | return relationKey, nil | ||
67 | 271 | } | ||
68 | 272 | |||
69 | 273 | // addRelationEndpointNode creates the endpoint role node below its relation node | ||
70 | 274 | // for the given relation endpoint. | ||
71 | 275 | func (s *State) addRelationEndpointNode(relationKey string, endpoint RelationEndpoint) error { | ||
72 | 276 | path := fmt.Sprintf("/relations/%s/%s", relationKey, string(endpoint.RelationRole)) | ||
73 | 277 | _, err := s.zk.Create(path, "", 0, zkPermAll) | ||
74 | 278 | return err | ||
75 | 279 | } | ||
76 | 280 | |||
77 | 281 | // AddRelation creates a new relation with the given endpoints. | ||
78 | 282 | func (s *State) AddRelation(endpoints ...RelationEndpoint) (*Relation, []*ServiceRelation, error) { | ||
79 | 283 | switch len(endpoints) { | ||
80 | 284 | case 1: | ||
81 | 285 | if endpoints[0].RelationRole != RolePeer { | ||
82 | 286 | return nil, nil, fmt.Errorf("can't add non-peer relation with a single service") | ||
83 | 287 | } | ||
84 | 288 | case 2: | ||
85 | 289 | if !endpoints[0].CanRelateTo(&endpoints[1]) { | ||
86 | 290 | return nil, nil, fmt.Errorf("can't add relation between %s and %s", endpoints[0], endpoints[1]) | ||
87 | 291 | } | ||
88 | 292 | default: | ||
89 | 293 | return nil, nil, fmt.Errorf("can't add relations between %d services", len(endpoints)) | ||
90 | 294 | } | ||
91 | 295 | t, err := readTopology(s.zk) | ||
92 | 296 | if err != nil { | ||
93 | 297 | return nil, nil, err | ||
94 | 298 | } | ||
95 | 299 | // Check if the relation already exists. | ||
96 | 300 | relationKey, err := t.RelationKey(endpoints...) | ||
97 | 301 | if err != nil { | ||
98 | 302 | if _, ok := err.(*NoRelationError); !ok { | ||
99 | 303 | return nil, nil, err | ||
100 | 304 | } | ||
101 | 305 | } | ||
102 | 306 | if relationKey != "" { | ||
103 | 307 | return nil, nil, fmt.Errorf("relation already exists") | ||
104 | 308 | } | ||
105 | 309 | scope := ScopeGlobal | ||
106 | 310 | for _, endpoint := range endpoints { | ||
107 | 311 | if endpoint.RelationScope == ScopeContainer { | ||
108 | 312 | scope = ScopeContainer | ||
109 | 313 | break | ||
110 | 314 | } | ||
111 | 315 | } | ||
112 | 316 | // Add a new relation node depending on the scope. Afterwards | ||
113 | 317 | // create a node and a service relation per endpoint. | ||
114 | 318 | relationKey, err = s.addRelationNode(scope) | ||
115 | 319 | if err != nil { | ||
116 | 320 | return nil, nil, err | ||
117 | 321 | } | ||
118 | 322 | serviceRelations := []*ServiceRelation{} | ||
119 | 323 | for _, endpoint := range endpoints { | ||
120 | 324 | serviceKey, err := t.ServiceKey(endpoint.ServiceName) | ||
121 | 325 | if err != nil { | ||
122 | 326 | return nil, nil, err | ||
123 | 327 | } | ||
124 | 328 | // The relation endpoint node is only created if the scope is | ||
125 | 329 | // global. In case of container scoped relations the creation | ||
126 | 330 | // per container occurs in ServiceRelation.AddUnit. | ||
127 | 331 | if scope == ScopeGlobal { | ||
128 | 332 | if err = s.addRelationEndpointNode(relationKey, endpoint); err != nil { | ||
129 | 333 | return nil, nil, err | ||
130 | 334 | } | ||
131 | 335 | } | ||
132 | 336 | serviceRelations = append(serviceRelations, &ServiceRelation{ | ||
133 | 337 | st: s, | ||
134 | 338 | relationKey: relationKey, | ||
135 | 339 | serviceKey: serviceKey, | ||
136 | 340 | relationScope: endpoint.RelationScope, | ||
137 | 341 | relationRole: endpoint.RelationRole, | ||
138 | 342 | relationName: endpoint.RelationName, | ||
139 | 343 | }) | ||
140 | 344 | } | ||
141 | 345 | // Add relation to topology. | ||
142 | 346 | addRelation := func(t *topology) error { | ||
143 | 347 | relation := &topoRelation{ | ||
144 | 348 | Interface: endpoints[0].Interface, | ||
145 | 349 | Scope: scope, | ||
146 | 350 | Services: map[RelationRole]*topoRelationService{}, | ||
147 | 351 | } | ||
148 | 352 | for _, serviceRelation := range serviceRelations { | ||
149 | 353 | if !t.HasService(serviceRelation.serviceKey) { | ||
150 | 354 | return fmt.Errorf("state for service %q has changed", serviceRelation.serviceKey) | ||
151 | 355 | } | ||
152 | 356 | service := &topoRelationService{ | ||
153 | 357 | Service: serviceRelation.serviceKey, | ||
154 | 358 | RelationName: serviceRelation.RelationName(), | ||
155 | 359 | } | ||
156 | 360 | relation.Services[serviceRelation.RelationRole()] = service | ||
157 | 361 | } | ||
158 | 362 | return t.AddRelation(relationKey, relation) | ||
159 | 363 | } | ||
160 | 364 | err = retryTopologyChange(s.zk, addRelation) | ||
161 | 365 | if err != nil { | ||
162 | 366 | return nil, nil, err | ||
163 | 367 | } | ||
164 | 368 | return &Relation{s, relationKey}, serviceRelations, nil | ||
165 | 369 | } | ||
166 | 253 | 370 | ||
167 | === modified file 'state/state_test.go' | |||
168 | --- state/state_test.go 2012-05-30 01:11:22 +0000 | |||
169 | +++ state/state_test.go 2012-06-01 12:44:24 +0000 | |||
170 | @@ -1,6 +1,3 @@ | |||
171 | 1 | // launchpad.net/juju/go/state | ||
172 | 2 | // | ||
173 | 3 | // Copyright (c) 2011-2012 Canonical Ltd. | ||
174 | 4 | package state_test | 1 | package state_test |
175 | 5 | 2 | ||
176 | 6 | import ( | 3 | import ( |
177 | @@ -1017,3 +1014,103 @@ | |||
178 | 1017 | c.Assert(err, IsNil) | 1014 | c.Assert(err, IsNil) |
179 | 1018 | c.Assert(alive, Equals, false) | 1015 | c.Assert(alive, Equals, false) |
180 | 1019 | } | 1016 | } |
181 | 1017 | |||
182 | 1018 | func (s *StateSuite) TestAddRelation(c *C) { | ||
183 | 1019 | dummy := s.addDummyCharm(c) | ||
184 | 1020 | // Provider and requirer. | ||
185 | 1021 | s.st.AddService("mysqldb", dummy) | ||
186 | 1022 | s.st.AddService("wordpress", dummy) | ||
187 | 1023 | mysqlep := state.RelationEndpoint{"mysqldb", "blog", "db", state.RoleProvider, state.ScopeGlobal} | ||
188 | 1024 | blogep := state.RelationEndpoint{"wordpress", "blog", "db", state.RoleRequirer, state.ScopeGlobal} | ||
189 | 1025 | relation, serviceRelations, err := s.st.AddRelation(blogep, mysqlep) | ||
190 | 1026 | c.Assert(err, IsNil) | ||
191 | 1027 | c.Assert(relation, NotNil) | ||
192 | 1028 | c.Assert(serviceRelations, HasLen, 2) | ||
193 | 1029 | c.Assert(serviceRelations[0].RelationScope(), Equals, state.ScopeGlobal) | ||
194 | 1030 | c.Assert(serviceRelations[0].RelationRole(), Equals, state.RoleRequirer) | ||
195 | 1031 | c.Assert(serviceRelations[1].RelationScope(), Equals, state.ScopeGlobal) | ||
196 | 1032 | c.Assert(serviceRelations[1].RelationRole(), Equals, state.RoleProvider) | ||
197 | 1033 | c.Assert(serviceRelations[0].RelationName(), Equals, serviceRelations[1].RelationName()) | ||
198 | 1034 | // Peer. | ||
199 | 1035 | s.st.AddService("riak", dummy) | ||
200 | 1036 | riakep := state.RelationEndpoint{"riak", "ring", "cache", state.RolePeer, state.ScopeGlobal} | ||
201 | 1037 | relation, serviceRelations, err = s.st.AddRelation(riakep) | ||
202 | 1038 | c.Assert(err, IsNil) | ||
203 | 1039 | c.Assert(relation, NotNil) | ||
204 | 1040 | c.Assert(serviceRelations, HasLen, 1) | ||
205 | 1041 | c.Assert(serviceRelations[0].RelationScope(), Equals, state.ScopeGlobal) | ||
206 | 1042 | c.Assert(serviceRelations[0].RelationRole(), Equals, state.RolePeer) | ||
207 | 1043 | c.Assert(serviceRelations[0].RelationName(), Equals, "cache") | ||
208 | 1044 | |||
209 | 1045 | } | ||
210 | 1046 | |||
211 | 1047 | func (s *StateSuite) TestAddRelationMissingService(c *C) { | ||
212 | 1048 | dummy := s.addDummyCharm(c) | ||
213 | 1049 | s.st.AddService("mysqldb", dummy) | ||
214 | 1050 | mysqlep := state.RelationEndpoint{"mysqldb", "blog", "db", state.RoleProvider, state.ScopeGlobal} | ||
215 | 1051 | blogep := state.RelationEndpoint{"wordpress", "blog", "db", state.RoleRequirer, state.ScopeGlobal} | ||
216 | 1052 | _, _, err := s.st.AddRelation(blogep, mysqlep) | ||
217 | 1053 | c.Assert(err, ErrorMatches, `service with name "wordpress" not found`) | ||
218 | 1054 | } | ||
219 | 1055 | |||
220 | 1056 | func (s *StateSuite) TestAddRelationMissingEndpoint(c *C) { | ||
221 | 1057 | dummy := s.addDummyCharm(c) | ||
222 | 1058 | s.st.AddService("mysqldb", dummy) | ||
223 | 1059 | mysqlep := state.RelationEndpoint{"mysqldb", "blog", "db", state.RoleProvider, state.ScopeGlobal} | ||
224 | 1060 | _, _, err := s.st.AddRelation(mysqlep) | ||
225 | 1061 | c.Assert(err, ErrorMatches, `can't add non-peer relation with a single service`) | ||
226 | 1062 | } | ||
227 | 1063 | |||
228 | 1064 | func (s *StateSuite) TestAddClientServerDifferentRoles(c *C) { | ||
229 | 1065 | dummy := s.addDummyCharm(c) | ||
230 | 1066 | s.st.AddService("mysqldb", dummy) | ||
231 | 1067 | s.st.AddService("riak", dummy) | ||
232 | 1068 | mysqlep := state.RelationEndpoint{"mysqldb", "ifce", "db", state.RoleRequirer, state.ScopeGlobal} | ||
233 | 1069 | riakep := state.RelationEndpoint{"riak", "ring", "cache", state.RolePeer, state.ScopeGlobal} | ||
234 | 1070 | _, _, err := s.st.AddRelation(mysqlep, riakep) | ||
235 | 1071 | c.Assert(err, ErrorMatches, `can't add relation between mysqldb:db and riak:cache`) | ||
236 | 1072 | } | ||
237 | 1073 | |||
238 | 1074 | func (s *StateSuite) TestAddRelationDifferentInterfaces(c *C) { | ||
239 | 1075 | dummy := s.addDummyCharm(c) | ||
240 | 1076 | s.st.AddService("mysqldb", dummy) | ||
241 | 1077 | s.st.AddService("wordpress", dummy) | ||
242 | 1078 | mysqlep := state.RelationEndpoint{"mysqldb", "ifce-a", "db", state.RoleProvider, state.ScopeGlobal} | ||
243 | 1079 | blogep := state.RelationEndpoint{"wordpress", "ifce-b", "db", state.RoleRequirer, state.ScopeGlobal} | ||
244 | 1080 | _, _, err := s.st.AddRelation(blogep, mysqlep) | ||
245 | 1081 | c.Assert(err, ErrorMatches, `can't add relation between wordpress:db and mysqldb:db`) | ||
246 | 1082 | } | ||
247 | 1083 | |||
248 | 1084 | func (s *StateSuite) TestAddClientServerRelationTwice(c *C) { | ||
249 | 1085 | dummy := s.addDummyCharm(c) | ||
250 | 1086 | // Provider and requirer. | ||
251 | 1087 | s.st.AddService("mysqldb", dummy) | ||
252 | 1088 | s.st.AddService("wordpress", dummy) | ||
253 | 1089 | mysqlep := state.RelationEndpoint{"mysqldb", "blog", "db", state.RoleProvider, state.ScopeGlobal} | ||
254 | 1090 | blogep := state.RelationEndpoint{"wordpress", "blog", "db", state.RoleRequirer, state.ScopeGlobal} | ||
255 | 1091 | _, _, err := s.st.AddRelation(blogep, mysqlep) | ||
256 | 1092 | c.Assert(err, IsNil) | ||
257 | 1093 | _, _, err = s.st.AddRelation(blogep, mysqlep) | ||
258 | 1094 | c.Assert(err, ErrorMatches, `relation already exists`) | ||
259 | 1095 | // Peer. | ||
260 | 1096 | s.st.AddService("riak", dummy) | ||
261 | 1097 | riakep := state.RelationEndpoint{"riak", "ring", "cache", state.RolePeer, state.ScopeGlobal} | ||
262 | 1098 | _, _, err = s.st.AddRelation(riakep) | ||
263 | 1099 | c.Assert(err, IsNil) | ||
264 | 1100 | _, _, err = s.st.AddRelation(riakep) | ||
265 | 1101 | c.Assert(err, ErrorMatches, `relation already exists`) | ||
266 | 1102 | } | ||
267 | 1103 | |||
268 | 1104 | func (s *StateSuite) TestAddPeerRelationIllegalEndpointNumber(c *C) { | ||
269 | 1105 | dummy := s.addDummyCharm(c) | ||
270 | 1106 | s.st.AddService("mysqldb", dummy) | ||
271 | 1107 | s.st.AddService("wordpress", dummy) | ||
272 | 1108 | s.st.AddService("riak", dummy) | ||
273 | 1109 | mysqlep := state.RelationEndpoint{"mysqldb", "ifce", "cache", state.RoleProvider, state.ScopeGlobal} | ||
274 | 1110 | blogep := state.RelationEndpoint{"wordpress", "ifce", "cache", state.RoleRequirer, state.ScopeGlobal} | ||
275 | 1111 | riakep := state.RelationEndpoint{"riak", "blog", "cache", state.RolePeer, state.ScopeGlobal} | ||
276 | 1112 | _, _, err := s.st.AddRelation() | ||
277 | 1113 | c.Assert(err, ErrorMatches, `can't add relations between 0 services`) | ||
278 | 1114 | _, _, err = s.st.AddRelation(mysqlep, blogep, riakep) | ||
279 | 1115 | c.Assert(err, ErrorMatches, `can't add relations between 3 services`) | ||
280 | 1116 | } |
https:/ /codereview. appspot. com/6223055/ diff/1/ state/relation. go
File state/relation.go (right):
https:/ /codereview. appspot. com/6223055/ diff/1/ state/relation. go#newcode60 go:60: func (r *Relation) Key() string {
state/relation.
The internal key is internal. :-)
https:/ /codereview. appspot. com/6223055/ diff/1/ state/relation. go#newcode73 go:73: // Key returns the internal key of a relation.
state/relation.
Ditto.
https:/ /codereview. appspot. com/6223055/ diff/1/ state/relation. go#newcode78 go:78: // ServiceKey returns the service key of a
state/relation.
relation.
Ditto.
https:/ /codereview. appspot. com/6223055/ diff/1/ state/state. go
File state/state.go (right):
https:/ /codereview. appspot. com/6223055/ diff/1/ state/state. go#newcode239 RelationRole != RoleClient {
state/state.go:239: if clientEp.
As a general and vague comment, visually these functions do not look
nice. Maybe there's no way to avoid the complexity, but please look for
opportunities to separate logical blocks visually, and perhaps using
functions is adequate.
I'll hold-off on reviewing this until we sort out the pre-req, since
there are necessary changes there that will certainly affect this.
https:/ /codereview. appspot. com/6223055/