Merge lp:~themue/juju-core/020-cmd-status-subordinates into lp:~juju/juju-core/trunk
- 020-cmd-status-subordinates
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | William Reade |
Proposed branch: | lp:~themue/juju-core/020-cmd-status-subordinates |
Merge into: | lp:~juju/juju-core/trunk |
Diff against target: |
303 lines (+212/-9) 2 files modified
cmd/juju/status.go (+57/-9) cmd/juju/status_test.go (+155/-0) |
To merge this branch: | bzr merge lp:~themue/juju-core/020-cmd-status-subordinates |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+159339@code.launchpad.net |
Commit message
Description of the change
status: added the display of subordinates
Added the display of subordinated units below their
associated units and "subordinate-to" for the
subordinated services.
William Reade (fwereade) wrote : | # |
William Reade (fwereade) wrote : | # |
LGTM wrt output. Code improvements appreciated but not required.
https:/
File cmd/juju/status.go (right):
https:/
cmd/juju/
"units")
I remain suspicious of this, but the output looks sane, we can worry
about this another day.
https:/
cmd/juju/
Why are we doing this over and over again for every unit?
https:/
File cmd/juju/
https:/
cmd/juju/
those logs",
:)
https:/
cmd/juju/
Do we have anything for testing non-agent-alive units?
https:/
cmd/juju/
Please don't do this. Create the relation separately, and get it here
with Relation.
Dimiter Naydenov (dimitern) wrote : | # |
Somewhat grudgy LGTM, although I don't like how the tests and
readability are both deteriorating. The output is fine, that's what's
important and both issues above can be fixed in a follow up.
https:/
File cmd/juju/status.go (right):
https:/
cmd/juju/
principal unit names
readability here really could be improved, but i'm not blocking this for
that.
https:/
File cmd/juju/
https:/
cmd/juju/
this is *DEFINITELY WRONG*, yet another copy of exactly the same test
driver function as TestStatusAllFo
in a follow up though, not blocking.
William Reade (fwereade) wrote : | # |
Frank, sorry: rogpeppe got carried away and proposed a branch that
cleans up the structure so much it's worth the duplicated effort IMO...
so NOT LGTM. Your tests live on, though, and are much appreciated.
Preview Diff
1 | === modified file 'cmd/juju/status.go' |
2 | --- cmd/juju/status.go 2013-04-16 23:57:53 +0000 |
3 | +++ cmd/juju/status.go 2013-04-17 15:27:23 +0000 |
4 | @@ -2,6 +2,7 @@ |
5 | |
6 | import ( |
7 | "fmt" |
8 | + "strings" |
9 | |
10 | "launchpad.net/gnuflag" |
11 | "launchpad.net/juju-core/cmd" |
12 | @@ -178,13 +179,52 @@ |
13 | // processServices gathers information about services. |
14 | func processServices(services map[string]*state.Service) (statusMap, error) { |
15 | servicesMap := make(statusMap) |
16 | - for _, s := range services { |
17 | - servicesMap[s.Name()] = checkError(processService(s)) |
18 | + // prinToSubUnitMap will collect mappings from principal unit names |
19 | + // to their subordinate units name. |
20 | + prinToSubUnitMap := make(statusMap) |
21 | + // subToPrinServicesMap will collect mappings from subordinate service |
22 | + // names to a slice of their principal service names. |
23 | + subToPrinServicesMap := make(statusMap) |
24 | + // 1st pass: iterate over the services. |
25 | + for _, service := range services { |
26 | + servicesMap[service.Name()] = checkError(processService(service, prinToSubUnitMap, subToPrinServicesMap)) |
27 | + } |
28 | + // 2nd pass: post-process the subordinates. |
29 | + unitsMapByUnitName := func(unitName string) statusMap { |
30 | + serviceName := strings.Split(unitName, "/")[0] |
31 | + unitsMap := servicesMap[serviceName].(statusMap)["units"] |
32 | + return unitsMap.(statusMap) |
33 | + } |
34 | + // Put the subordinate units data below their principal units. |
35 | + for prinUnitName, subUnitNameTmp := range prinToSubUnitMap { |
36 | + subUnitName := subUnitNameTmp.(string) |
37 | + prinUnitsMap := unitsMapByUnitName(prinUnitName) |
38 | + subUnitsMap := unitsMapByUnitName(subUnitName) |
39 | + if prinUnitsMap[prinUnitName].(statusMap)["subordinates"] == nil { |
40 | + prinUnitsMap[prinUnitName].(statusMap)["subordinates"] = make(statusMap) |
41 | + } |
42 | + subordinatesMap := prinUnitsMap[prinUnitName].(statusMap)["subordinates"].(statusMap) |
43 | + subUnitMap := make(statusMap) |
44 | + subUnitMap["agent-state"] = subUnitsMap[subUnitName].(statusMap)["agent-state"] |
45 | + if info, ok := subUnitsMap[subUnitName].(statusMap)["agent-state-info"]; ok { |
46 | + subUnitMap["agent-state-info"] = info |
47 | + } |
48 | + subordinatesMap[subUnitName] = subUnitMap |
49 | + prinUnitsMap[prinUnitName].(statusMap)["subordinates"] = subordinatesMap |
50 | + } |
51 | + // Add the principals to the subordinated services. |
52 | + for serviceName, subToNames := range subToPrinServicesMap { |
53 | + subToSet := set.NewStrings(subToNames.([]string)...) |
54 | + subToValues := subToSet.SortedValues() |
55 | + servicesMap[serviceName].(statusMap)["subordinate-to"] = subToValues |
56 | + // Drop the unit data after it has been merged in the loop above. |
57 | + // It isn't needed in the subordinated service anymore. |
58 | + delete(servicesMap[serviceName].(statusMap), "units") |
59 | } |
60 | return servicesMap, nil |
61 | } |
62 | |
63 | -func processService(service *state.Service) (statusMap, error) { |
64 | +func processService(service *state.Service, prinToSubUnitMap, subToPrinServicesMap statusMap) (statusMap, error) { |
65 | serviceMap := make(statusMap) |
66 | ch, _, err := service.Charm() |
67 | if err != nil { |
68 | @@ -193,14 +233,12 @@ |
69 | serviceMap["charm"] = ch.String() |
70 | serviceMap["exposed"] = service.IsExposed() |
71 | |
72 | - // TODO(dfc) service.IsSubordinate() ? |
73 | - |
74 | units, err := service.AllUnits() |
75 | if err != nil { |
76 | return nil, err |
77 | } |
78 | |
79 | - if u := checkError(processUnits(units)); len(u) > 0 { |
80 | + if u := checkError(processUnits(units, prinToSubUnitMap, subToPrinServicesMap)); len(u) > 0 { |
81 | serviceMap["units"] = u |
82 | } |
83 | |
84 | @@ -211,15 +249,15 @@ |
85 | return serviceMap, nil |
86 | } |
87 | |
88 | -func processUnits(units []*state.Unit) (statusMap, error) { |
89 | +func processUnits(units []*state.Unit, prinToSubUnitMap, subToPrinServicesMap statusMap) (statusMap, error) { |
90 | unitsMap := make(statusMap) |
91 | for _, unit := range units { |
92 | - unitsMap[unit.Name()] = checkError(processUnit(unit)) |
93 | + unitsMap[unit.Name()] = checkError(processUnit(unit, prinToSubUnitMap, subToPrinServicesMap)) |
94 | } |
95 | return unitsMap, nil |
96 | } |
97 | |
98 | -func processUnit(unit *state.Unit) (statusMap, error) { |
99 | +func processUnit(unit *state.Unit, prinToSubUnitMap, subToPrinServicesMap statusMap) (statusMap, error) { |
100 | unitMap := make(statusMap) |
101 | |
102 | if addr, ok := unit.PublicAddress(); ok { |
103 | @@ -244,6 +282,16 @@ |
104 | } |
105 | processStatus(unitMap, status, info, agentAlive, unitDead) |
106 | |
107 | + for _, subName := range unit.SubordinateNames() { |
108 | + prinToSubUnitMap[unit.Name()] = subName |
109 | + subNameParts := strings.Split(subName, "/") |
110 | + svcName := subNameParts[0] |
111 | + if subToPrinServicesMap[svcName] == nil { |
112 | + subToPrinServicesMap[svcName] = []string{} |
113 | + } |
114 | + subToPrinServicesMap[svcName] = append(subToPrinServicesMap[svcName].([]string), unit.ServiceName()) |
115 | + } |
116 | + |
117 | return unitMap, nil |
118 | } |
119 | |
120 | |
121 | === modified file 'cmd/juju/status_test.go' |
122 | --- cmd/juju/status_test.go 2013-04-16 07:48:25 +0000 |
123 | +++ cmd/juju/status_test.go 2013-04-17 15:27:23 +0000 |
124 | @@ -532,6 +532,109 @@ |
125 | ), |
126 | } |
127 | |
128 | +var subordinatesTests = []testCase{ |
129 | + test( |
130 | + "one service with one subordinate service", |
131 | + addMachine{"0", state.JobManageEnviron}, |
132 | + startAliveMachine{"0"}, |
133 | + setMachineStatus{"0", params.StatusStarted, ""}, |
134 | + addCharm{"wordpress"}, |
135 | + addCharm{"mysql"}, |
136 | + addCharm{"logging"}, |
137 | + |
138 | + addService{"wordpress", "wordpress"}, |
139 | + setServiceExposed{"wordpress", true}, |
140 | + addMachine{"1", state.JobHostUnits}, |
141 | + startAliveMachine{"1"}, |
142 | + setMachineStatus{"1", params.StatusStarted, ""}, |
143 | + addAliveUnit{"wordpress", "1"}, |
144 | + setUnitStatus{"wordpress/0", params.StatusStarted, ""}, |
145 | + |
146 | + addService{"mysql", "mysql"}, |
147 | + setServiceExposed{"mysql", true}, |
148 | + addMachine{"2", state.JobHostUnits}, |
149 | + startAliveMachine{"2"}, |
150 | + setMachineStatus{"2", params.StatusStarted, ""}, |
151 | + addAliveUnit{"mysql", "2"}, |
152 | + setUnitStatus{"mysql/0", params.StatusStarted, ""}, |
153 | + |
154 | + addService{"logging", "logging"}, |
155 | + setServiceExposed{"logging", true}, |
156 | + |
157 | + relateServices{"wordpress", "mysql"}, |
158 | + relateServices{"wordpress", "logging"}, |
159 | + relateServices{"mysql", "logging"}, |
160 | + |
161 | + addSubordinate{"wordpress/0", "logging"}, |
162 | + addSubordinate{"mysql/0", "logging"}, |
163 | + |
164 | + setUnitsAlive{"logging"}, |
165 | + setUnitStatus{"logging/0", params.StatusStarted, ""}, |
166 | + setUnitStatus{"logging/1", params.StatusError, "somehow lost in all those logs"}, |
167 | + |
168 | + expect{ |
169 | + "multiples related peer units", |
170 | + M{ |
171 | + "machines": M{ |
172 | + "0": machine0, |
173 | + "1": machine1, |
174 | + "2": machine2, |
175 | + }, |
176 | + "services": M{ |
177 | + "wordpress": M{ |
178 | + "charm": "local:series/wordpress-3", |
179 | + "exposed": true, |
180 | + "units": M{ |
181 | + "wordpress/0": M{ |
182 | + "machine": "1", |
183 | + "agent-state": "started", |
184 | + "subordinates": M{ |
185 | + "logging/0": M{ |
186 | + "agent-state": "started", |
187 | + }, |
188 | + }, |
189 | + }, |
190 | + }, |
191 | + "relations": M{ |
192 | + "db": L{"mysql"}, |
193 | + "logging-dir": L{"logging"}, |
194 | + }, |
195 | + }, |
196 | + "mysql": M{ |
197 | + "charm": "local:series/mysql-1", |
198 | + "exposed": true, |
199 | + "units": M{ |
200 | + "mysql/0": M{ |
201 | + "machine": "2", |
202 | + "agent-state": "started", |
203 | + "subordinates": M{ |
204 | + "logging/1": M{ |
205 | + "agent-state": "error", |
206 | + "agent-state-info": "somehow lost in all those logs", |
207 | + }, |
208 | + }, |
209 | + }, |
210 | + }, |
211 | + "relations": M{ |
212 | + "server": L{"wordpress"}, |
213 | + "juju-info": L{"logging"}, |
214 | + }, |
215 | + }, |
216 | + "logging": M{ |
217 | + "charm": "local:series/logging-1", |
218 | + "exposed": true, |
219 | + "relations": M{ |
220 | + "logging-directory": L{"wordpress"}, |
221 | + "info": L{"mysql"}, |
222 | + }, |
223 | + "subordinate-to": L{"mysql", "wordpress"}, |
224 | + }, |
225 | + }, |
226 | + }, |
227 | + }, |
228 | + ), |
229 | +} |
230 | + |
231 | // TODO(dfc) test failing components by destructively mutating the state under the hood |
232 | |
233 | type addMachine struct { |
234 | @@ -672,6 +775,28 @@ |
235 | ctx.pingers[u.Name()] = pinger |
236 | } |
237 | |
238 | +type setUnitsAlive struct { |
239 | + serviceName string |
240 | +} |
241 | + |
242 | +func (sua setUnitsAlive) step(c *C, ctx *context) { |
243 | + s, err := ctx.st.Service(sua.serviceName) |
244 | + c.Assert(err, IsNil) |
245 | + us, err := s.AllUnits() |
246 | + c.Assert(err, IsNil) |
247 | + for _, u := range us { |
248 | + pinger, err := u.SetAgentAlive() |
249 | + c.Assert(err, IsNil) |
250 | + ctx.st.StartSync() |
251 | + err = u.WaitAgentAlive(200 * time.Millisecond) |
252 | + c.Assert(err, IsNil) |
253 | + agentAlive, err := u.AgentAlive() |
254 | + c.Assert(err, IsNil) |
255 | + c.Assert(agentAlive, Equals, true) |
256 | + ctx.pingers[u.Name()] = pinger |
257 | + } |
258 | +} |
259 | + |
260 | type setUnitStatus struct { |
261 | unitName string |
262 | status params.Status |
263 | @@ -707,6 +832,24 @@ |
264 | c.Assert(err, IsNil) |
265 | } |
266 | |
267 | +type addSubordinate struct { |
268 | + prinUnit string |
269 | + subService string |
270 | +} |
271 | + |
272 | +func (as addSubordinate) step(c *C, ctx *context) { |
273 | + u, err := ctx.st.Unit(as.prinUnit) |
274 | + c.Assert(err, IsNil) |
275 | + eps, err := ctx.st.InferEndpoints([]string{u.ServiceName(), as.subService}) |
276 | + c.Assert(err, IsNil) |
277 | + rel, err := ctx.st.EndpointsRelation(eps...) |
278 | + c.Assert(err, IsNil) |
279 | + ru, err := rel.Unit(u) |
280 | + c.Assert(err, IsNil) |
281 | + err = ru.EnterScope(nil) |
282 | + c.Assert(err, IsNil) |
283 | +} |
284 | + |
285 | type expect struct { |
286 | what string |
287 | output M |
288 | @@ -761,3 +904,15 @@ |
289 | }() |
290 | } |
291 | } |
292 | + |
293 | +func (s *StatusSuite) TestSubordinates(c *C) { |
294 | + for i, t := range subordinatesTests { |
295 | + c.Log("test %d: %s", i, t.summary) |
296 | + func() { |
297 | + // Prepare context and run all steps to setup. |
298 | + ctx := s.newContext() |
299 | + defer s.resetContext(c, ctx) |
300 | + ctx.run(c, t.steps) |
301 | + }() |
302 | + } |
303 | +} |
Excellent start, but a couple of tweaks needed. Only the ones that
actually affect output are showstoppers though.
https:/ /codereview. appspot. com/8824043/ diff/1/ cmd/juju/ status. go
File cmd/juju/status.go (left):
https:/ /codereview. appspot. com/8824043/ diff/1/ cmd/juju/ status. go#oldcode183 status. go:183: }
cmd/juju/
This all feels more complex than it needs to be, but if it can support
the changes requested in the other file I'm not too bothered today.
https:/ /codereview. appspot. com/8824043/ diff/1/ cmd/juju/ status. go
File cmd/juju/status.go (right):
https:/ /codereview. appspot. com/8824043/ diff/1/ cmd/juju/ status. go#newcode271 status. go:271: if unit.IsPrincipal() {
cmd/juju/
Not required.
https:/ /codereview. appspot. com/8824043/ diff/1/ cmd/juju/ status_ test.go status_ test.go (right):
File cmd/juju/
https:/ /codereview. appspot. com/8824043/ diff/1/ cmd/juju/ status_ test.go# newcode570 status_ test.go: 570: setUnitStatus{ "logging/ 1", StatusStarted, ""},
cmd/juju/
params.
Maybe just have one of these with a non-"" status?
https:/ /codereview. appspot. com/8824043/ diff/1/ cmd/juju/ status_ test.go# newcode622 status_ test.go: 622: "units": M{
cmd/juju/
logging should not show its own units.
https:/ /codereview. appspot. com/8824043/ diff/1/ cmd/juju/ status_ test.go# newcode845 status_ test.go: 845: func (rsws relateServicesW ithScope) step(c
cmd/juju/
*C, ctx *context) {
I think the relateServices should be separate, and there should be a
separate
type addSubordinate struct {
principal string
subService string
}
...that does roughly the same, but using the specific correct unit
rather than just picking the same one every time.
https:/ /codereview. appspot. com/8824043/