Merge lp:~themue/juju-core/020-cmd-status-subordinates into lp:~juju/juju-core/trunk

Proposed by Frank Mueller
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
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+159339@code.launchpad.net

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.

https://codereview.appspot.com/8824043/

To post a comment you must log in.
Revision history for this message
William Reade (fwereade) wrote :

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
cmd/juju/status.go:183: }
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
cmd/juju/status.go:271: if unit.IsPrincipal() {
Not required.

https://codereview.appspot.com/8824043/diff/1/cmd/juju/status_test.go
File cmd/juju/status_test.go (right):

https://codereview.appspot.com/8824043/diff/1/cmd/juju/status_test.go#newcode570
cmd/juju/status_test.go:570: setUnitStatus{"logging/1",
params.StatusStarted, ""},
Maybe just have one of these with a non-"" status?

https://codereview.appspot.com/8824043/diff/1/cmd/juju/status_test.go#newcode622
cmd/juju/status_test.go:622: "units": M{
logging should not show its own units.

https://codereview.appspot.com/8824043/diff/1/cmd/juju/status_test.go#newcode845
cmd/juju/status_test.go:845: func (rsws relateServicesWithScope) step(c
*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/

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

LGTM wrt output. Code improvements appreciated but not required.

https://codereview.appspot.com/8824043/diff/6001/cmd/juju/status.go
File cmd/juju/status.go (right):

https://codereview.appspot.com/8824043/diff/6001/cmd/juju/status.go#newcode214
cmd/juju/status.go:214: delete(servicesMap[serviceName].(statusMap),
"units")
I remain suspicious of this, but the output looks sane, we can worry
about this another day.

https://codereview.appspot.com/8824043/diff/6001/cmd/juju/status.go#newcode283
cmd/juju/status.go:283: subTo = subToMap[svcName].([]string)
Why are we doing this over and over again for every unit?

https://codereview.appspot.com/8824043/diff/6001/cmd/juju/status_test.go
File cmd/juju/status_test.go (right):

https://codereview.appspot.com/8824043/diff/6001/cmd/juju/status_test.go#newcode610
cmd/juju/status_test.go:610: "agent-state-info": "somehow lost in all
those logs",
:)

https://codereview.appspot.com/8824043/diff/6001/cmd/juju/status_test.go#newcode780
cmd/juju/status_test.go:780: s, err := ctx.st.Service(sua.serviceName)
Do we have anything for testing non-agent-alive units?

https://codereview.appspot.com/8824043/diff/6001/cmd/juju/status_test.go#newcode842
cmd/juju/status_test.go:842: rel, err := ctx.st.AddRelation(eps...)
Please don't do this. Create the relation separately, and get it here
with Relation.

https://codereview.appspot.com/8824043/

Revision history for this message
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://codereview.appspot.com/8824043/diff/12001/cmd/juju/status.go
File cmd/juju/status.go (right):

https://codereview.appspot.com/8824043/diff/12001/cmd/juju/status.go#newcode182
cmd/juju/status.go:182: // prinToSubUnitMap will collect mappings from
principal unit names
readability here really could be improved, but i'm not blocking this for
that.

https://codereview.appspot.com/8824043/diff/12001/cmd/juju/status_test.go
File cmd/juju/status_test.go (right):

https://codereview.appspot.com/8824043/diff/12001/cmd/juju/status_test.go#newcode907
cmd/juju/status_test.go:907:
this is *DEFINITELY WRONG*, yet another copy of exactly the same test
driver function as TestStatusAllFormats and TestRelations. I'll fix this
in a follow up though, not blocking.

https://codereview.appspot.com/8824043/

Revision history for this message
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.

https://codereview.appspot.com/8824043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches