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
1=== modified file 'state/machine.go'
2--- state/machine.go 2014-03-26 11:34:48 +0000
3+++ state/machine.go 2014-04-03 10:11:00 +0000
4@@ -813,23 +813,28 @@
5 return ok
6 }
7
8+func mergedAddresses(machineAddresses, providerAddresses []address) []instance.Address {
9+ merged := make(map[string]instance.Address)
10+ for _, address := range machineAddresses {
11+ merged[address.Value] = address.InstanceAddress()
12+ }
13+ for _, address := range providerAddresses {
14+ merged[address.Value] = address.InstanceAddress()
15+ }
16+ addresses := make([]instance.Address, 0, len(merged))
17+ for _, address := range merged {
18+ addresses = append(addresses, address)
19+ }
20+ return addresses
21+}
22+
23 // Addresses returns any hostnames and ips associated with a machine,
24 // determined both by the machine itself, and by asking the provider.
25 //
26 // The addresses returned by the provider shadow any of the addresses
27 // that the machine reported with the same address value.
28 func (m *Machine) Addresses() (addresses []instance.Address) {
29- merged := make(map[string]instance.Address)
30- for _, address := range m.doc.MachineAddresses {
31- merged[address.Value] = address.InstanceAddress()
32- }
33- for _, address := range m.doc.Addresses {
34- merged[address.Value] = address.InstanceAddress()
35- }
36- for _, address := range merged {
37- addresses = append(addresses, address)
38- }
39- return
40+ return mergedAddresses(m.doc.MachineAddresses, m.doc.Addresses)
41 }
42
43 // SetAddresses records any addresses related to the machine, sourced
44
45=== modified file 'state/megawatcher.go'
46--- state/megawatcher.go 2014-03-21 09:56:17 +0000
47+++ state/megawatcher.go 2014-04-03 10:11:00 +0000
48@@ -32,7 +32,7 @@
49 Life: params.Life(m.Life.String()),
50 Series: m.Series,
51 Jobs: paramsJobsFromJobs(m.Jobs),
52- Addresses: addressesToInstanceAddresses(m.Addresses),
53+ Addresses: mergedAddresses(m.MachineAddresses, m.Addresses),
54 SupportedContainers: m.SupportedContainers,
55 SupportedContainersKnown: m.SupportedContainersKnown,
56 }
57@@ -87,13 +87,11 @@
58
59 func (u *backingUnit) updated(st *State, store *multiwatcher.Store, id interface{}) error {
60 info := &params.UnitInfo{
61- Name: u.Name,
62- Service: u.Service,
63- Series: u.Series,
64- PublicAddress: u.PublicAddress,
65- PrivateAddress: u.PrivateAddress,
66- MachineId: u.MachineId,
67- Ports: u.Ports,
68+ Name: u.Name,
69+ Service: u.Service,
70+ Series: u.Series,
71+ MachineId: u.MachineId,
72+ Ports: u.Ports,
73 }
74 if u.CharmURL != nil {
75 info.CharmURL = u.CharmURL.String()
76@@ -114,10 +112,29 @@
77 info.Status = oldInfo.Status
78 info.StatusInfo = oldInfo.StatusInfo
79 }
80+ publicAddress, privateAddress, err := getUnitAddresses(st, u.Name)
81+ if err != nil {
82+ return err
83+ }
84+ info.PublicAddress = publicAddress
85+ info.PrivateAddress = privateAddress
86 store.Update(info)
87 return nil
88 }
89
90+// getUnitAddresses returns the public and private addresses on a given unit.
91+// As of 1.18, the addresses are stored on the assigned machine but we retain
92+// this approach for backwards compatibility.
93+func getUnitAddresses(st *State, unitName string) (publicAddress, privateAddress string, err error) {
94+ u, err := st.Unit(unitName)
95+ if err != nil {
96+ return "", "", err
97+ }
98+ publicAddress, _ = u.PublicAddress()
99+ privateAddress, _ = u.PrivateAddress()
100+ return publicAddress, privateAddress, nil
101+}
102+
103 func (svc *backingUnit) removed(st *State, store *multiwatcher.Store, id interface{}) error {
104 store.Remove(params.EntityId{
105 Kind: "unit",
106
107=== modified file 'state/megawatcher_internal_test.go'
108--- state/megawatcher_internal_test.go 2014-03-21 11:00:41 +0000
109+++ state/megawatcher_internal_test.go 2014-04-03 10:11:00 +0000
110@@ -420,6 +420,44 @@
111 StatusInfo: "another failure",
112 },
113 },
114+ }, {
115+ about: "unit addresses are read from the assigned machine for recent Juju releases",
116+ setUp: func(c *gc.C, st *State) {
117+ wordpress := AddTestingService(c, st, "wordpress", AddTestingCharm(c, st, "wordpress"))
118+ u, err := wordpress.AddUnit()
119+ c.Assert(err, gc.IsNil)
120+ err = u.OpenPort("tcp", 12345)
121+ c.Assert(err, gc.IsNil)
122+ m, err := st.AddMachine("quantal", JobHostUnits)
123+ c.Assert(err, gc.IsNil)
124+ err = u.AssignToMachine(m)
125+ c.Assert(err, gc.IsNil)
126+ publicAddress := instance.NewAddress("public")
127+ publicAddress.NetworkScope = instance.NetworkPublic
128+ privateAddress := instance.NewAddress("private")
129+ privateAddress.NetworkScope = instance.NetworkCloudLocal
130+ err = m.SetAddresses([]instance.Address{publicAddress, privateAddress})
131+ c.Assert(err, gc.IsNil)
132+ err = u.SetStatus(params.StatusError, "failure", nil)
133+ c.Assert(err, gc.IsNil)
134+ },
135+ change: watcher.Change{
136+ C: "units",
137+ Id: "wordpress/0",
138+ },
139+ expectContents: []params.EntityInfo{
140+ &params.UnitInfo{
141+ Name: "wordpress/0",
142+ Service: "wordpress",
143+ Series: "quantal",
144+ PublicAddress: "public",
145+ PrivateAddress: "private",
146+ MachineId: "0",
147+ Ports: []instance.Port{{"tcp", 12345}},
148+ Status: params.StatusError,
149+ StatusInfo: "failure",
150+ },
151+ },
152 },
153 // Service changes
154 {

Subscribers

People subscribed via source and target branches

to all changes: