Merge lp:~themue/juju-core/go-state-relation-endpoint-verification into lp:~juju/juju-core/trunk

Proposed by Frank Mueller
Status: Rejected
Rejected by: Gustavo Niemeyer
Proposed branch: lp:~themue/juju-core/go-state-relation-endpoint-verification
Merge into: lp:~juju/juju-core/trunk
Diff against target: 120 lines (+57/-8)
3 files modified
state/relation.go (+21/-1)
state/state.go (+11/-7)
state/state_test.go (+25/-0)
To merge this branch: bzr merge lp:~themue/juju-core/go-state-relation-endpoint-verification
Reviewer Review Type Date Requested Status
The Go Language Gophers Pending
Review via email: mp+109171@code.launchpad.net

Description of the change

state: Improved verification of relation endpoints.

AddRelation() consists of several writings to ZooKeeper. Now
the whole verification is done before the writings start.

https://codereview.appspot.com/6305067/

To post a comment you must log in.
Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM, although i'm sure i don't understand all the implications.

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

https://codereview.appspot.com/6305067/diff/1/state/relation.go#newcode63
state/relation.go:63: return fmt.Errorf("endpoint has invalid role: %q",
e.RelationRole)
s/://

generally we use ":" for indicating sub-errors, not for information
within a particular error.

https://codereview.appspot.com/6305067/

Revision history for this message
William Reade (fwereade) wrote :

I'm not really convinced this helps us much. The only sane way to sanely
construct a RelationEndpoint is from a service+charm in the first
place... are we worried that we'll get bad data from those, or that
ill-disciplined programmers will be constructing them at random and
flinging them at the wall to see what sticks?

(I presume we're really worried about something else I haven't thought
of. Elaborate please?)

https://codereview.appspot.com/6305067/

Revision history for this message
William Reade (fwereade) wrote :

I still doubt this will have great value in practice, but LGTM.

https://codereview.appspot.com/6305067/

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

On 2012/06/11 08:22:47, fwereade wrote:
> I still doubt this will have great value in practice, but LGTM.

Sorry Frank, I have to agree with William, even though the suggestion to
prevent this from happening was mine in the first place.

My misguided suggestion was based on the fact I had wrong memories
regarding the way we handle "deploy serviceA:foo serviceB:bar" commands.
We don't create the relation endpoint directly, but rather call a method
to resolve what we call the *descriptor* "<service>[:<relname>]". This
is the place that must have that validation to prevent errors from the
user from leaking through.

I apologize for wasting your time on this.

https://codereview.appspot.com/6305067/

Unmerged revisions

214. By Frank Mueller

state: Improved verification of endpoint before relation is added.

213. By Roger Peppe

state: store units inside their respective services

Making this change simplifies a lot of code - no need
to keep a separate sequence numbering system for units,
for example.

(original proposal at https://codereview.appspot.com/6247066/)

R=
CC=
https://codereview.appspot.com/6300060

212. By Gustavo Niemeyer

.lbox: update to juju-core

211. By Gustavo Niemeyer

Translated all paths to use the juju-core project in Launchpad.

210. By Frank Mueller

state: Changed relation service mapping in topology.

The first approach mapped roles to service key and relation
name opposite to todays Python implementation. This reduces
the flexibility for future purposes (e.g. multiple peers).
So the mapping has now been changed back from service keys
to relation role and name.

R=
CC=
https://codereview.appspot.com/6268049

209. By Gustavo Niemeyer

state: rename Environment method to EnvironConfig

R=TheMue, fwereade
CC=
https://codereview.appspot.com/6295048

208. By William Reade

subordinate service units can now be added to principal service units

This entails the following changes:

* new Service.AddUnitSubordinateTo method, which requires a principal unit
* topology.AddUnit now takes an additional principalKey string argument,
  which will be empty for a principal unit;
* new topology.UnitPrincipalKey, which returns the principal unit key for a
  subordinate unit or an error for a principal unit;
* new Unit.IsPrincipal method, which returns whether the unit is a principal
  unit;
* checks to prevent subordinate units being assigned directly to machines.

Most of the diff lines are a result of the change to topology.AddUnit;
sorry for the noise.

R=TheMue, niemeyer
CC=
https://codereview.appspot.com/6268050

207. By Frank Mueller

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.

R=niemeyer
CC=
https://codereview.appspot.com/6250076

206. By William Reade

trivial: fix inconsistency in deploy command documentation

R=
CC=
https://codereview.appspot.com/6304045

205. By William Reade

add AssignmentPolicy to Environ

This is very likely to migrate to a Settings type at some stage, but I don't
think that's justified yet.

R=niemeyer
CC=
https://codereview.appspot.com/6259062

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'state/relation.go'
2--- state/relation.go 2012-06-01 10:33:35 +0000
3+++ state/relation.go 2012-06-07 15:36:23 +0000
4@@ -1,6 +1,8 @@
5 package state
6
7-import ()
8+import (
9+ "fmt"
10+)
11
12 // RelationRole defines the role of a relation endpoint.
13 type RelationRole string
14@@ -47,6 +49,24 @@
15 return e.ServiceName + ":" + e.RelationName
16 }
17
18+// checkValues performs basic tests on the endpoint data without
19+// checking the validity of identifiers.
20+func (e *RelationEndpoint) checkValues() error {
21+ switch {
22+ case e.ServiceName == "":
23+ return fmt.Errorf("endpoint has empty service name")
24+ case e.Interface == "":
25+ return fmt.Errorf("endpoint has empty interface")
26+ case e.RelationName == "":
27+ return fmt.Errorf("endpoint has empty relation name")
28+ case e.RelationRole != RoleProvider && e.RelationRole != RoleRequirer && e.RelationRole != RolePeer:
29+ return fmt.Errorf("endpoint has invalid role: %q", e.RelationRole)
30+ case e.RelationScope != ScopeGlobal && e.RelationScope != ScopeContainer:
31+ return fmt.Errorf("endpoint has invalid scope: %q", e.RelationScope)
32+ }
33+ return nil
34+}
35+
36 // Relation represents a link between services, or within a
37 // service (in the case of peer relations).
38 type Relation struct {
39
40=== modified file 'state/state.go'
41--- state/state.go 2012-06-07 11:51:54 +0000
42+++ state/state.go 2012-06-07 15:36:23 +0000
43@@ -317,11 +317,19 @@
44 return nil, nil, fmt.Errorf("relation already exists")
45 }
46 scope := ScopeGlobal
47+ serviceKeys := make(map[RelationEndpoint]string)
48 for _, endpoint := range endpoints {
49 if endpoint.RelationScope == ScopeContainer {
50 scope = ScopeContainer
51- break
52- }
53+ }
54+ if err := endpoint.checkValues(); err != nil {
55+ return nil, nil, err
56+ }
57+ serviceKey, err := t.ServiceKey(endpoint.ServiceName)
58+ if err != nil {
59+ return nil, nil, err
60+ }
61+ serviceKeys[endpoint] = serviceKey
62 }
63 // Add a new relation node depending on the scope. Afterwards
64 // create a node and a service relation per endpoint.
65@@ -331,10 +339,6 @@
66 }
67 serviceRelations := []*ServiceRelation{}
68 for _, endpoint := range endpoints {
69- serviceKey, err := t.ServiceKey(endpoint.ServiceName)
70- if err != nil {
71- return nil, nil, err
72- }
73 // The relation endpoint node is only created if the scope is
74 // global. In case of container scoped relations the creation
75 // per container occurs in ServiceRelation.AddUnit.
76@@ -346,7 +350,7 @@
77 serviceRelations = append(serviceRelations, &ServiceRelation{
78 st: s,
79 relationKey: relationKey,
80- serviceKey: serviceKey,
81+ serviceKey: serviceKeys[endpoint],
82 relationScope: endpoint.RelationScope,
83 relationRole: endpoint.RelationRole,
84 relationName: endpoint.RelationName,
85
86=== modified file 'state/state_test.go'
87--- state/state_test.go 2012-06-07 11:51:54 +0000
88+++ state/state_test.go 2012-06-07 15:36:23 +0000
89@@ -1223,6 +1223,31 @@
90 c.Assert(err, ErrorMatches, `can't add relations between 3 services`)
91 }
92
93+func (s *StateSuite) TestAddRelationInvalidEndpoints(c *C) {
94+ dummy := s.addDummyCharm(c)
95+ s.st.AddService("riak", dummy)
96+ // Empty service name.
97+ riakep := state.RelationEndpoint{"", "ring", "cache", state.RolePeer, state.ScopeGlobal}
98+ _, _, err := s.st.AddRelation(riakep)
99+ c.Assert(err, ErrorMatches, `endpoint has empty service name`)
100+ // Empty interface.
101+ riakep = state.RelationEndpoint{"riak", "", "cache", state.RolePeer, state.ScopeGlobal}
102+ _, _, err = s.st.AddRelation(riakep)
103+ c.Assert(err, ErrorMatches, `endpoint has empty interface`)
104+ // Empty relation name.
105+ riakep = state.RelationEndpoint{"riak", "ring", "", state.RolePeer, state.ScopeGlobal}
106+ _, _, err = s.st.AddRelation(riakep)
107+ c.Assert(err, ErrorMatches, `endpoint has empty relation name`)
108+ // Invalid role.
109+ riakep = state.RelationEndpoint{"riak", "ring", "cache", state.RelationRole("foo"), state.ScopeGlobal}
110+ _, _, err = s.st.AddRelation(riakep)
111+ c.Assert(err, ErrorMatches, `can't add non-peer relation with a single service`)
112+ // Invalid role.
113+ riakep = state.RelationEndpoint{"riak", "ring", "cache", state.RolePeer, state.RelationScope("foo")}
114+ _, _, err = s.st.AddRelation(riakep)
115+ c.Assert(err, ErrorMatches, `endpoint has invalid scope: "foo"`)
116+}
117+
118 func (s *StateSuite) TestEnvironConfig(c *C) {
119 path, err := s.zkConn.Create("/environment", "type: dummy\nname: foo\n", 0, zookeeper.WorldACL(zookeeper.PERM_ALL))
120 c.Assert(err, IsNil)

Subscribers

People subscribed via source and target branches