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
=== modified file 'cmd/juju/status.go'
--- cmd/juju/status.go 2013-04-16 23:57:53 +0000
+++ cmd/juju/status.go 2013-04-17 15:27:23 +0000
@@ -2,6 +2,7 @@
22
3import (3import (
4 "fmt"4 "fmt"
5 "strings"
56
6 "launchpad.net/gnuflag"7 "launchpad.net/gnuflag"
7 "launchpad.net/juju-core/cmd"8 "launchpad.net/juju-core/cmd"
@@ -178,13 +179,52 @@
178// processServices gathers information about services.179// processServices gathers information about services.
179func processServices(services map[string]*state.Service) (statusMap, error) {180func processServices(services map[string]*state.Service) (statusMap, error) {
180 servicesMap := make(statusMap)181 servicesMap := make(statusMap)
181 for _, s := range services {182 // prinToSubUnitMap will collect mappings from principal unit names
182 servicesMap[s.Name()] = checkError(processService(s))183 // to their subordinate units name.
184 prinToSubUnitMap := make(statusMap)
185 // subToPrinServicesMap will collect mappings from subordinate service
186 // names to a slice of their principal service names.
187 subToPrinServicesMap := make(statusMap)
188 // 1st pass: iterate over the services.
189 for _, service := range services {
190 servicesMap[service.Name()] = checkError(processService(service, prinToSubUnitMap, subToPrinServicesMap))
191 }
192 // 2nd pass: post-process the subordinates.
193 unitsMapByUnitName := func(unitName string) statusMap {
194 serviceName := strings.Split(unitName, "/")[0]
195 unitsMap := servicesMap[serviceName].(statusMap)["units"]
196 return unitsMap.(statusMap)
197 }
198 // Put the subordinate units data below their principal units.
199 for prinUnitName, subUnitNameTmp := range prinToSubUnitMap {
200 subUnitName := subUnitNameTmp.(string)
201 prinUnitsMap := unitsMapByUnitName(prinUnitName)
202 subUnitsMap := unitsMapByUnitName(subUnitName)
203 if prinUnitsMap[prinUnitName].(statusMap)["subordinates"] == nil {
204 prinUnitsMap[prinUnitName].(statusMap)["subordinates"] = make(statusMap)
205 }
206 subordinatesMap := prinUnitsMap[prinUnitName].(statusMap)["subordinates"].(statusMap)
207 subUnitMap := make(statusMap)
208 subUnitMap["agent-state"] = subUnitsMap[subUnitName].(statusMap)["agent-state"]
209 if info, ok := subUnitsMap[subUnitName].(statusMap)["agent-state-info"]; ok {
210 subUnitMap["agent-state-info"] = info
211 }
212 subordinatesMap[subUnitName] = subUnitMap
213 prinUnitsMap[prinUnitName].(statusMap)["subordinates"] = subordinatesMap
214 }
215 // Add the principals to the subordinated services.
216 for serviceName, subToNames := range subToPrinServicesMap {
217 subToSet := set.NewStrings(subToNames.([]string)...)
218 subToValues := subToSet.SortedValues()
219 servicesMap[serviceName].(statusMap)["subordinate-to"] = subToValues
220 // Drop the unit data after it has been merged in the loop above.
221 // It isn't needed in the subordinated service anymore.
222 delete(servicesMap[serviceName].(statusMap), "units")
183 }223 }
184 return servicesMap, nil224 return servicesMap, nil
185}225}
186226
187func processService(service *state.Service) (statusMap, error) {227func processService(service *state.Service, prinToSubUnitMap, subToPrinServicesMap statusMap) (statusMap, error) {
188 serviceMap := make(statusMap)228 serviceMap := make(statusMap)
189 ch, _, err := service.Charm()229 ch, _, err := service.Charm()
190 if err != nil {230 if err != nil {
@@ -193,14 +233,12 @@
193 serviceMap["charm"] = ch.String()233 serviceMap["charm"] = ch.String()
194 serviceMap["exposed"] = service.IsExposed()234 serviceMap["exposed"] = service.IsExposed()
195235
196 // TODO(dfc) service.IsSubordinate() ?
197
198 units, err := service.AllUnits()236 units, err := service.AllUnits()
199 if err != nil {237 if err != nil {
200 return nil, err238 return nil, err
201 }239 }
202240
203 if u := checkError(processUnits(units)); len(u) > 0 {241 if u := checkError(processUnits(units, prinToSubUnitMap, subToPrinServicesMap)); len(u) > 0 {
204 serviceMap["units"] = u242 serviceMap["units"] = u
205 }243 }
206244
@@ -211,15 +249,15 @@
211 return serviceMap, nil249 return serviceMap, nil
212}250}
213251
214func processUnits(units []*state.Unit) (statusMap, error) {252func processUnits(units []*state.Unit, prinToSubUnitMap, subToPrinServicesMap statusMap) (statusMap, error) {
215 unitsMap := make(statusMap)253 unitsMap := make(statusMap)
216 for _, unit := range units {254 for _, unit := range units {
217 unitsMap[unit.Name()] = checkError(processUnit(unit))255 unitsMap[unit.Name()] = checkError(processUnit(unit, prinToSubUnitMap, subToPrinServicesMap))
218 }256 }
219 return unitsMap, nil257 return unitsMap, nil
220}258}
221259
222func processUnit(unit *state.Unit) (statusMap, error) {260func processUnit(unit *state.Unit, prinToSubUnitMap, subToPrinServicesMap statusMap) (statusMap, error) {
223 unitMap := make(statusMap)261 unitMap := make(statusMap)
224262
225 if addr, ok := unit.PublicAddress(); ok {263 if addr, ok := unit.PublicAddress(); ok {
@@ -244,6 +282,16 @@
244 }282 }
245 processStatus(unitMap, status, info, agentAlive, unitDead)283 processStatus(unitMap, status, info, agentAlive, unitDead)
246284
285 for _, subName := range unit.SubordinateNames() {
286 prinToSubUnitMap[unit.Name()] = subName
287 subNameParts := strings.Split(subName, "/")
288 svcName := subNameParts[0]
289 if subToPrinServicesMap[svcName] == nil {
290 subToPrinServicesMap[svcName] = []string{}
291 }
292 subToPrinServicesMap[svcName] = append(subToPrinServicesMap[svcName].([]string), unit.ServiceName())
293 }
294
247 return unitMap, nil295 return unitMap, nil
248}296}
249297
250298
=== modified file 'cmd/juju/status_test.go'
--- cmd/juju/status_test.go 2013-04-16 07:48:25 +0000
+++ cmd/juju/status_test.go 2013-04-17 15:27:23 +0000
@@ -532,6 +532,109 @@
532 ),532 ),
533}533}
534534
535var subordinatesTests = []testCase{
536 test(
537 "one service with one subordinate service",
538 addMachine{"0", state.JobManageEnviron},
539 startAliveMachine{"0"},
540 setMachineStatus{"0", params.StatusStarted, ""},
541 addCharm{"wordpress"},
542 addCharm{"mysql"},
543 addCharm{"logging"},
544
545 addService{"wordpress", "wordpress"},
546 setServiceExposed{"wordpress", true},
547 addMachine{"1", state.JobHostUnits},
548 startAliveMachine{"1"},
549 setMachineStatus{"1", params.StatusStarted, ""},
550 addAliveUnit{"wordpress", "1"},
551 setUnitStatus{"wordpress/0", params.StatusStarted, ""},
552
553 addService{"mysql", "mysql"},
554 setServiceExposed{"mysql", true},
555 addMachine{"2", state.JobHostUnits},
556 startAliveMachine{"2"},
557 setMachineStatus{"2", params.StatusStarted, ""},
558 addAliveUnit{"mysql", "2"},
559 setUnitStatus{"mysql/0", params.StatusStarted, ""},
560
561 addService{"logging", "logging"},
562 setServiceExposed{"logging", true},
563
564 relateServices{"wordpress", "mysql"},
565 relateServices{"wordpress", "logging"},
566 relateServices{"mysql", "logging"},
567
568 addSubordinate{"wordpress/0", "logging"},
569 addSubordinate{"mysql/0", "logging"},
570
571 setUnitsAlive{"logging"},
572 setUnitStatus{"logging/0", params.StatusStarted, ""},
573 setUnitStatus{"logging/1", params.StatusError, "somehow lost in all those logs"},
574
575 expect{
576 "multiples related peer units",
577 M{
578 "machines": M{
579 "0": machine0,
580 "1": machine1,
581 "2": machine2,
582 },
583 "services": M{
584 "wordpress": M{
585 "charm": "local:series/wordpress-3",
586 "exposed": true,
587 "units": M{
588 "wordpress/0": M{
589 "machine": "1",
590 "agent-state": "started",
591 "subordinates": M{
592 "logging/0": M{
593 "agent-state": "started",
594 },
595 },
596 },
597 },
598 "relations": M{
599 "db": L{"mysql"},
600 "logging-dir": L{"logging"},
601 },
602 },
603 "mysql": M{
604 "charm": "local:series/mysql-1",
605 "exposed": true,
606 "units": M{
607 "mysql/0": M{
608 "machine": "2",
609 "agent-state": "started",
610 "subordinates": M{
611 "logging/1": M{
612 "agent-state": "error",
613 "agent-state-info": "somehow lost in all those logs",
614 },
615 },
616 },
617 },
618 "relations": M{
619 "server": L{"wordpress"},
620 "juju-info": L{"logging"},
621 },
622 },
623 "logging": M{
624 "charm": "local:series/logging-1",
625 "exposed": true,
626 "relations": M{
627 "logging-directory": L{"wordpress"},
628 "info": L{"mysql"},
629 },
630 "subordinate-to": L{"mysql", "wordpress"},
631 },
632 },
633 },
634 },
635 ),
636}
637
535// TODO(dfc) test failing components by destructively mutating the state under the hood638// TODO(dfc) test failing components by destructively mutating the state under the hood
536639
537type addMachine struct {640type addMachine struct {
@@ -672,6 +775,28 @@
672 ctx.pingers[u.Name()] = pinger775 ctx.pingers[u.Name()] = pinger
673}776}
674777
778type setUnitsAlive struct {
779 serviceName string
780}
781
782func (sua setUnitsAlive) step(c *C, ctx *context) {
783 s, err := ctx.st.Service(sua.serviceName)
784 c.Assert(err, IsNil)
785 us, err := s.AllUnits()
786 c.Assert(err, IsNil)
787 for _, u := range us {
788 pinger, err := u.SetAgentAlive()
789 c.Assert(err, IsNil)
790 ctx.st.StartSync()
791 err = u.WaitAgentAlive(200 * time.Millisecond)
792 c.Assert(err, IsNil)
793 agentAlive, err := u.AgentAlive()
794 c.Assert(err, IsNil)
795 c.Assert(agentAlive, Equals, true)
796 ctx.pingers[u.Name()] = pinger
797 }
798}
799
675type setUnitStatus struct {800type setUnitStatus struct {
676 unitName string801 unitName string
677 status params.Status802 status params.Status
@@ -707,6 +832,24 @@
707 c.Assert(err, IsNil)832 c.Assert(err, IsNil)
708}833}
709834
835type addSubordinate struct {
836 prinUnit string
837 subService string
838}
839
840func (as addSubordinate) step(c *C, ctx *context) {
841 u, err := ctx.st.Unit(as.prinUnit)
842 c.Assert(err, IsNil)
843 eps, err := ctx.st.InferEndpoints([]string{u.ServiceName(), as.subService})
844 c.Assert(err, IsNil)
845 rel, err := ctx.st.EndpointsRelation(eps...)
846 c.Assert(err, IsNil)
847 ru, err := rel.Unit(u)
848 c.Assert(err, IsNil)
849 err = ru.EnterScope(nil)
850 c.Assert(err, IsNil)
851}
852
710type expect struct {853type expect struct {
711 what string854 what string
712 output M855 output M
@@ -761,3 +904,15 @@
761 }()904 }()
762 }905 }
763}906}
907
908func (s *StatusSuite) TestSubordinates(c *C) {
909 for i, t := range subordinatesTests {
910 c.Log("test %d: %s", i, t.summary)
911 func() {
912 // Prepare context and run all steps to setup.
913 ctx := s.newContext()
914 defer s.resetContext(c, ctx)
915 ctx.run(c, t.steps)
916 }()
917 }
918}

Subscribers

People subscribed via source and target branches