Merge lp:~wallyworld/juju-core/watcher-address-issues into lp:juju-core/1.18

Proposed by Ian Booth
Status: Merged
Approved by: Ian Booth
Approved revision: no longer in the source branch.
Merged at revision: 2257
Proposed branch: lp:~wallyworld/juju-core/watcher-address-issues
Merge into: lp:juju-core/1.18
Diff against target: 154 lines (+79/-19)
3 files modified
state/machine.go (+16/-11)
state/megawatcher.go (+25/-8)
state/megawatcher_internal_test.go (+38/-0)
To merge this branch: bzr merge lp:~wallyworld/juju-core/watcher-address-issues
Reviewer Review Type Date Requested Status
Francesco Banconi (community) Approve
Review via email: mp+213968@code.launchpad.net

Commit message

Ensure unit addresses populated

Change the internals of the megawatcher to ensure
that machine addresses are correct and that the
public and private addresses for untis come from
the assigned machine.

Description of the change

Ensure unit addresses populated

Change the internals of the megawatcher to ensure
that machine addresses are correct and that the
public and private addresses for untis come from
the assigned machine.

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

LGTM if frankban's happy as well.

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

frankban makes a good point, that the comment in :8-9 is not correct -- we don't get them for the local provider, but we should do everywhere else, via instancepoller; and I think we probably *should* fix local.Instance to use fancy lxc-ls such that we *do* get provider addresses as well.

Revision history for this message
Francesco Banconi (frankban) wrote :

Hi Ian, this branch looks good with some comments below.

As I mentioned in the bug, not sure why LXC addresses have empty scopes.
I'll try to describe my concerns: API clients are often interested in the public address of a unit/machine.
When using cloud providers, fetching them is trivial:
  - just pick the first "ipv4" address with "public" NetworkScope.
When using local envs, currently is more tricky, and I am not sure about the logic: the scope is empty so,
the logic above might become:
  - try to find an "ipv4" address with "public" NetworkScope
  - if none is found, pick the first "ipv4" address with an empty scope, it must be the public one.
Is this correct? If so, we can surely make quickstart/GUI work like that. Anyway, it does not seem the best client story we can have.

8 + // As of 1.18, provider addresses are not gathered anymore so this field
9 + // will not be populated. It is retained for backwards compatibility.

This does not seem to reflect reality. AFAICT provider addresses are still used and populated for top level machines.

25 + addresses := make([]instance.Address, len(merged))
26 + for _, address := range merged {
27 + addresses = append(addresses, address)
28 + }

It seems we are creating the addresses with a specific length but then we append to it. This way we have len(merged) empty addresses at the beginning of the slice. I guess we should either assign by index or start with a 0 length slice.

37 +// Note: As of 1.18, provider addresses are no longer populated.

Same as above.

Revision history for this message
John A Meinel (jameinel) wrote :

(01:54:07 PM) jam: fwereade: I'm thinking we need james' patch to land on the 1.18 branch as well, right?
(01:54:54 PM) jam: frankban: I believe LXC has an empty scope because we can't tell whether it is public or private.
(01:54:58 PM) jam: It is just the address we got from eth0
(01:55:03 PM) jam: on Clouds that is actually a private address
(01:55:13 PM) jam: for LXC it is probably the "most public address we can get"
(01:55:24 PM) jam: though still since it is on LXCBR0 it is probably a private address.

Revision history for this message
Ian Booth (wallyworld) wrote :

> Hi Ian, this branch looks good with some comments below.
>
> As I mentioned in the bug, not sure why LXC addresses have empty scopes.
> I'll try to describe my concerns: API clients are often interested in the
> public address of a unit/machine.
> When using cloud providers, fetching them is trivial:
> - just pick the first "ipv4" address with "public" NetworkScope.
> When using local envs, currently is more tricky, and I am not sure about the
> logic: the scope is empty so,
> the logic above might become:
> - try to find an "ipv4" address with "public" NetworkScope
> - if none is found, pick the first "ipv4" address with an empty scope, it
> must be the public one.
> Is this correct? If so, we can surely make quickstart/GUI work like that.
> Anyway, it does not seem the best client story we can have.
>

The current logic to put public/private address on the unit item in the mega watcher calls Pubic/PrivateAddress() on state.Unit. These methods look at the addresses gathered by the instance poller and stored on the machhne and return the ones considered to be most public/private respectively. For the local provider, I'm not sure what the instance poller does, I'd have to check.

> 8 + // As of 1.18, provider addresses are not gathered anymore so
> this field
> 9 + // will not be populated. It is retained for backwards
> compatibility.
>
> This does not seem to reflect reality. AFAICT provider addresses are still
> used and populated for top level machines.
>

I think there's crossed wires here. My interpretation of "provider addresses" is the address getters on the provider itself and these have been removed. Maybe that's not what you mean. I'll remove the comment.

> 25 + addresses := make([]instance.Address, len(merged))
> 26 + for _, address := range merged {
> 27 + addresses = append(addresses, address)
> 28 + }
>
> It seems we are creating the addresses with a specific length but then we
> append to it. This way we have len(merged) empty addresses at the beginning of
> the slice. I guess we should either assign by index or start with a 0 length
> slice.
>

Fixed.

> 37 +// Note: As of 1.18, provider addresses are no longer populated.
>
> Same as above.

Removed comment.

Revision history for this message
Francesco Banconi (frankban) wrote :

LGTM, thank you!

review: Approve
Revision history for this message
Ian Booth (wallyworld) wrote :

FWIW, I deployed wordpress and juju-gui using local provider and the gui correctly showed the public addresses of both units (wordpress and juju-gui)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'state/machine.go'
--- state/machine.go 2014-03-26 11:34:48 +0000
+++ state/machine.go 2014-04-03 10:11:00 +0000
@@ -813,23 +813,28 @@
813 return ok813 return ok
814}814}
815815
816func mergedAddresses(machineAddresses, providerAddresses []address) []instance.Address {
817 merged := make(map[string]instance.Address)
818 for _, address := range machineAddresses {
819 merged[address.Value] = address.InstanceAddress()
820 }
821 for _, address := range providerAddresses {
822 merged[address.Value] = address.InstanceAddress()
823 }
824 addresses := make([]instance.Address, 0, len(merged))
825 for _, address := range merged {
826 addresses = append(addresses, address)
827 }
828 return addresses
829}
830
816// Addresses returns any hostnames and ips associated with a machine,831// Addresses returns any hostnames and ips associated with a machine,
817// determined both by the machine itself, and by asking the provider.832// determined both by the machine itself, and by asking the provider.
818//833//
819// The addresses returned by the provider shadow any of the addresses834// The addresses returned by the provider shadow any of the addresses
820// that the machine reported with the same address value.835// that the machine reported with the same address value.
821func (m *Machine) Addresses() (addresses []instance.Address) {836func (m *Machine) Addresses() (addresses []instance.Address) {
822 merged := make(map[string]instance.Address)837 return mergedAddresses(m.doc.MachineAddresses, m.doc.Addresses)
823 for _, address := range m.doc.MachineAddresses {
824 merged[address.Value] = address.InstanceAddress()
825 }
826 for _, address := range m.doc.Addresses {
827 merged[address.Value] = address.InstanceAddress()
828 }
829 for _, address := range merged {
830 addresses = append(addresses, address)
831 }
832 return
833}838}
834839
835// SetAddresses records any addresses related to the machine, sourced840// SetAddresses records any addresses related to the machine, sourced
836841
=== modified file 'state/megawatcher.go'
--- state/megawatcher.go 2014-03-21 09:56:17 +0000
+++ state/megawatcher.go 2014-04-03 10:11:00 +0000
@@ -32,7 +32,7 @@
32 Life: params.Life(m.Life.String()),32 Life: params.Life(m.Life.String()),
33 Series: m.Series,33 Series: m.Series,
34 Jobs: paramsJobsFromJobs(m.Jobs),34 Jobs: paramsJobsFromJobs(m.Jobs),
35 Addresses: addressesToInstanceAddresses(m.Addresses),35 Addresses: mergedAddresses(m.MachineAddresses, m.Addresses),
36 SupportedContainers: m.SupportedContainers,36 SupportedContainers: m.SupportedContainers,
37 SupportedContainersKnown: m.SupportedContainersKnown,37 SupportedContainersKnown: m.SupportedContainersKnown,
38 }38 }
@@ -87,13 +87,11 @@
8787
88func (u *backingUnit) updated(st *State, store *multiwatcher.Store, id interface{}) error {88func (u *backingUnit) updated(st *State, store *multiwatcher.Store, id interface{}) error {
89 info := &params.UnitInfo{89 info := &params.UnitInfo{
90 Name: u.Name,90 Name: u.Name,
91 Service: u.Service,91 Service: u.Service,
92 Series: u.Series,92 Series: u.Series,
93 PublicAddress: u.PublicAddress,93 MachineId: u.MachineId,
94 PrivateAddress: u.PrivateAddress,94 Ports: u.Ports,
95 MachineId: u.MachineId,
96 Ports: u.Ports,
97 }95 }
98 if u.CharmURL != nil {96 if u.CharmURL != nil {
99 info.CharmURL = u.CharmURL.String()97 info.CharmURL = u.CharmURL.String()
@@ -114,10 +112,29 @@
114 info.Status = oldInfo.Status112 info.Status = oldInfo.Status
115 info.StatusInfo = oldInfo.StatusInfo113 info.StatusInfo = oldInfo.StatusInfo
116 }114 }
115 publicAddress, privateAddress, err := getUnitAddresses(st, u.Name)
116 if err != nil {
117 return err
118 }
119 info.PublicAddress = publicAddress
120 info.PrivateAddress = privateAddress
117 store.Update(info)121 store.Update(info)
118 return nil122 return nil
119}123}
120124
125// getUnitAddresses returns the public and private addresses on a given unit.
126// As of 1.18, the addresses are stored on the assigned machine but we retain
127// this approach for backwards compatibility.
128func getUnitAddresses(st *State, unitName string) (publicAddress, privateAddress string, err error) {
129 u, err := st.Unit(unitName)
130 if err != nil {
131 return "", "", err
132 }
133 publicAddress, _ = u.PublicAddress()
134 privateAddress, _ = u.PrivateAddress()
135 return publicAddress, privateAddress, nil
136}
137
121func (svc *backingUnit) removed(st *State, store *multiwatcher.Store, id interface{}) error {138func (svc *backingUnit) removed(st *State, store *multiwatcher.Store, id interface{}) error {
122 store.Remove(params.EntityId{139 store.Remove(params.EntityId{
123 Kind: "unit",140 Kind: "unit",
124141
=== modified file 'state/megawatcher_internal_test.go'
--- state/megawatcher_internal_test.go 2014-03-21 11:00:41 +0000
+++ state/megawatcher_internal_test.go 2014-04-03 10:11:00 +0000
@@ -420,6 +420,44 @@
420 StatusInfo: "another failure",420 StatusInfo: "another failure",
421 },421 },
422 },422 },
423 }, {
424 about: "unit addresses are read from the assigned machine for recent Juju releases",
425 setUp: func(c *gc.C, st *State) {
426 wordpress := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress"))
427 u, err := wordpress.AddUnit()
428 c.Assert(err, gc.IsNil)
429 err = u.OpenPort("tcp", 12345)
430 c.Assert(err, gc.IsNil)
431 m, err := st.AddMachine("quantal", JobHostUnits)
432 c.Assert(err, gc.IsNil)
433 err = u.AssignToMachine(m)
434 c.Assert(err, gc.IsNil)
435 publicAddress := instance.NewAddress("public")
436 publicAddress.NetworkScope = instance.NetworkPublic
437 privateAddress := instance.NewAddress("private")
438 privateAddress.NetworkScope = instance.NetworkCloudLocal
439 err = m.SetAddresses([]instance.Address{publicAddress, privateAddress})
440 c.Assert(err, gc.IsNil)
441 err = u.SetStatus(params.StatusError, "failure", nil)
442 c.Assert(err, gc.IsNil)
443 },
444 change: watcher.Change{
445 C: "units",
446 Id: "wordpress/0",
447 },
448 expectContents: []params.EntityInfo{
449 &params.UnitInfo{
450 Name: "wordpress/0",
451 Service: "wordpress",
452 Series: "quantal",
453 PublicAddress: "public",
454 PrivateAddress: "private",
455 MachineId: "0",
456 Ports: []instance.Port{{"tcp", 12345}},
457 Status: params.StatusError,
458 StatusInfo: "failure",
459 },
460 },
423 },461 },
424 // Service changes462 // Service changes
425 {463 {

Subscribers

People subscribed via source and target branches

to all changes: