Merge lp:~free.ekanayaka/landscape-client/juju-machine-info into lp:~landscape/landscape-client/trunk

Proposed by Free Ekanayaka
Status: Merged
Approved by: Free Ekanayaka
Approved revision: 786
Merged at revision: 783
Proposed branch: lp:~free.ekanayaka/landscape-client/juju-machine-info
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 332 lines (+68/-37)
9 files modified
landscape/broker/registration.py (+1/-1)
landscape/broker/service.py (+2/-2)
landscape/broker/tests/helpers.py (+1/-11)
landscape/broker/tests/test_exchange.py (+1/-2)
landscape/broker/tests/test_registration.py (+0/-1)
landscape/lib/juju.py (+29/-9)
landscape/lib/tests/test_juju.py (+23/-10)
landscape/monitor/jujuinfo.py (+9/-1)
landscape/monitor/tests/test_jujuinfo.py (+2/-0)
To merge this branch: bzr merge lp:~free.ekanayaka/landscape-client/juju-machine-info
Reviewer Review Type Date Requested Status
Adam Collard (community) Approve
Alberto Donato (community) Approve
Review via email: mp+231895@code.launchpad.net

Commit message

This branch changes the get_juju_info utility to return information
about the Juju machine ID the client runs in. Since we're in the
middle of the transition, both the old info with unit names and the
new info with the machine ID are returned. When the transition completes
in the server too the old info can be dropped.

Description of the change

This branch changes the get_juju_info utility to return information
about the Juju machine ID the client runs in. Since we're in the
middle of the transition, both the old info with unit names and the
new info with the machine ID are returned. When the transition completes
in the server too the old info can be dropped.

To post a comment you must log in.
784. By Free Ekanayaka

Fix failing tests

Revision history for this message
Adam Collard (adam-collard) wrote :

3 inline comments

review: Needs Information
Revision history for this message
Alberto Donato (ack) wrote :

Code looks good, just a question:

what's the plan for the charm? Will we just add the "machine-id" and keep "unit-name" for backwards compatibility?

review: Needs Information
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Alberto: for the charm we don't need backward compatibility since never released and LDS with support for Juju integration. So the current code in the charm is basically dead code from the user perspective, and we can change it to send just machine-id. Said that, for sake of our internal migration we'll keep both for a bit, till we complete the process.

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Thanks Adam, replied/fixed all three issues.

785. By Free Ekanayaka

Address review comments

Revision history for this message
Alberto Donato (ack) wrote :

+1!

review: Approve
Revision history for this message
Adam Collard (adam-collard) wrote :

Thanks Free.

Lint issue:

landscape/broker/tests/test_registration.py:5: 'SERVER_API' imported but unused

Otherwise looks good. +1

review: Approve
786. By Free Ekanayaka

Fix lint

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'landscape/broker/registration.py'
--- landscape/broker/registration.py 2014-07-16 18:04:22 +0000
+++ landscape/broker/registration.py 2014-08-28 07:37:26 +0000
@@ -126,7 +126,7 @@
126126
127 def _get_juju_data(self):127 def _get_juju_data(self):
128 """Load Juju information."""128 """Load Juju information."""
129 juju_info = get_juju_info(self._config)129 juju_info = get_juju_info(self._config)[0]
130 if juju_info is None:130 if juju_info is None:
131 return None131 return None
132 self._juju_data = juju_info # A list of dicts132 self._juju_data = juju_info # A list of dicts
133133
=== modified file 'landscape/broker/service.py'
--- landscape/broker/service.py 2013-05-07 22:47:06 +0000
+++ landscape/broker/service.py 2014-08-28 07:37:26 +0000
@@ -1,7 +1,7 @@
1"""Deployment code for the monitor."""1"""Deployment code for the monitor."""
22
3import os3import os
4from landscape.lib.fetch import fetch_async4
5from landscape.service import LandscapeService, run_landscape_service5from landscape.service import LandscapeService, run_landscape_service
6from landscape.amp import ComponentPublisher6from landscape.amp import ComponentPublisher
7from landscape.broker.registration import RegistrationHandler, Identity7from landscape.broker.registration import RegistrationHandler, Identity
@@ -61,7 +61,7 @@
61 self.reactor, self.identity, self.exchanger, config)61 self.reactor, self.identity, self.exchanger, config)
62 self.registration = RegistrationHandler(62 self.registration = RegistrationHandler(
63 config, self.identity, self.reactor, self.exchanger, self.pinger,63 config, self.identity, self.reactor, self.exchanger, self.pinger,
64 self.message_store, fetch_async)64 self.message_store)
65 self.broker = BrokerServer(self.config, self.reactor, self.exchanger,65 self.broker = BrokerServer(self.config, self.reactor, self.exchanger,
66 self.registration, self.message_store,66 self.registration, self.message_store,
67 self.pinger)67 self.pinger)
6868
=== modified file 'landscape/broker/tests/helpers.py'
--- landscape/broker/tests/helpers.py 2014-06-09 08:18:05 +0000
+++ landscape/broker/tests/helpers.py 2014-08-28 07:37:26 +0000
@@ -7,7 +7,6 @@
7"""7"""
8import os8import os
99
10from landscape.lib.fetch import fetch_async
11from landscape.lib.persist import Persist10from landscape.lib.persist import Persist
12from landscape.watchdog import bootstrap_list11from landscape.watchdog import bootstrap_list
13from landscape.reactor import FakeReactor12from landscape.reactor import FakeReactor
@@ -112,20 +111,12 @@
112 The following attributes will be set in your test case:111 The following attributes will be set in your test case:
113112
114 - C{handler}: A L{RegistrationHandler}.113 - C{handler}: A L{RegistrationHandler}.
115
116 - C{fetch_func}: The C{fetch_async} function used by the C{handler}, it
117 can be customised by test cases.
118 """114 """
119115
120 def set_up(self, test_case):116 def set_up(self, test_case):
121 super(RegistrationHelper, self).set_up(test_case)117 super(RegistrationHelper, self).set_up(test_case)
122 test_case.pinger = Pinger(test_case.reactor, test_case.identity,118 test_case.pinger = Pinger(test_case.reactor, test_case.identity,
123 test_case.exchanger, test_case.config)119 test_case.exchanger, test_case.config)
124
125 def fetch_func(*args, **kwargs):
126 return test_case.fetch_func(*args, **kwargs)
127
128 test_case.fetch_func = fetch_async
129 test_case.config.cloud = getattr(test_case, "cloud", False)120 test_case.config.cloud = getattr(test_case, "cloud", False)
130 if hasattr(test_case, "juju_contents"):121 if hasattr(test_case, "juju_contents"):
131 if not os.path.exists(test_case.config.juju_directory):122 if not os.path.exists(test_case.config.juju_directory):
@@ -135,8 +126,7 @@
135 dirname=test_case.config.juju_directory, suffix=".json")126 dirname=test_case.config.juju_directory, suffix=".json")
136 test_case.handler = RegistrationHandler(127 test_case.handler = RegistrationHandler(
137 test_case.config, test_case.identity, test_case.reactor,128 test_case.config, test_case.identity, test_case.reactor,
138 test_case.exchanger, test_case.pinger, test_case.mstore,129 test_case.exchanger, test_case.pinger, test_case.mstore)
139 fetch_async=fetch_func)
140130
141131
142class BrokerServerHelper(RegistrationHelper):132class BrokerServerHelper(RegistrationHelper):
143133
=== modified file 'landscape/broker/tests/test_exchange.py'
--- landscape/broker/tests/test_exchange.py 2014-07-16 11:41:29 +0000
+++ landscape/broker/tests/test_exchange.py 2014-08-28 07:37:26 +0000
@@ -1,7 +1,6 @@
1from landscape import SERVER_API, CLIENT_API1from landscape import SERVER_API, CLIENT_API
2from landscape.lib.persist import Persist2from landscape.lib.persist import Persist
3from landscape.lib.hashlib import md53from landscape.lib.hashlib import md5
4from landscape.lib.fetch import fetch_async
5from landscape.schema import Message, Int4from landscape.schema import Message, Int
6from landscape.broker.config import BrokerConfiguration5from landscape.broker.config import BrokerConfiguration
7from landscape.broker.exchange import get_accepted_types_diff, MessageExchange6from landscape.broker.exchange import get_accepted_types_diff, MessageExchange
@@ -1061,7 +1060,7 @@
1061 # message types that we want to catch as well1060 # message types that we want to catch as well
1062 self.handler = RegistrationHandler(1061 self.handler = RegistrationHandler(
1063 self.config, self.identity, self.reactor, self.exchanger,1062 self.config, self.identity, self.reactor, self.exchanger,
1064 self.pinger, self.mstore, fetch_async)1063 self.pinger, self.mstore)
10651064
1066 def test_register_accepted_message_type(self):1065 def test_register_accepted_message_type(self):
1067 self.exchanger.register_client_accepted_message_type("type-B")1066 self.exchanger.register_client_accepted_message_type("type-B")
10681067
=== modified file 'landscape/broker/tests/test_registration.py'
--- landscape/broker/tests/test_registration.py 2014-07-24 12:21:06 +0000
+++ landscape/broker/tests/test_registration.py 2014-08-28 07:37:26 +0000
@@ -2,7 +2,6 @@
2import logging2import logging
3import socket3import socket
44
5from landscape import SERVER_API
6from landscape.broker.registration import (5from landscape.broker.registration import (
7 InvalidCredentialsError, Identity)6 InvalidCredentialsError, Identity)
8from landscape.tests.helpers import LandscapeTest7from landscape.tests.helpers import LandscapeTest
98
=== modified file 'landscape/lib/juju.py'
--- landscape/lib/juju.py 2014-06-11 08:47:29 +0000
+++ landscape/lib/juju.py 2014-08-28 07:37:26 +0000
@@ -9,22 +9,28 @@
99
10def get_juju_info(config):10def get_juju_info(config):
11 """11 """
12 Returns the list of Juju info or C{None} if the path referenced from12 Returns available Juju info or C{None} if the path referenced from
13 L{config} is not a valid directory.13 L{config} is not a valid directory.
1414
15 The list of juju info is constructed by appending all the contents of15 XXX At the moment this function returns a 2-tuple because we're
16 transitioning from unit-computer associations to machine-computer
17 associations. Once the transition is completed in the server, the
18 old format can be dropped.
19
20 The list of old juju info is constructed by appending all the contents of
16 *.json files found in the path referenced from the L{config}.21 *.json files found in the path referenced from the L{config}.
17 """22 """
18 juju_directory = config.juju_directory23 juju_directory = config.juju_directory
19 legacy_juju_file = config.juju_filename24 legacy_juju_file = config.juju_filename
2025
26 new_juju_info = {}
21 juju_info_list = []27 juju_info_list = []
22 juju_file_list = glob("%s/*.json" % juju_directory)28 juju_file_list = glob("%s/*.json" % juju_directory)
2329
24 if os.path.exists(legacy_juju_file):30 if os.path.exists(legacy_juju_file):
25 juju_file_list.append(legacy_juju_file)31 juju_file_list.append(legacy_juju_file)
2632
27 for juju_file in juju_file_list:33 for index, juju_file in enumerate(juju_file_list):
2834
29 json_contents = read_file(juju_file)35 json_contents = read_file(juju_file)
30 try:36 try:
@@ -36,11 +42,25 @@
36 logging.exception(42 logging.exception(
37 "Error attempting to read JSON from %s" % juju_file)43 "Error attempting to read JSON from %s" % juju_file)
38 return None44 return None
39 else:45
40 if "api-addresses" in juju_info:46 if "api-addresses" in juju_info:
41 split = juju_info["api-addresses"].split()47 split = juju_info["api-addresses"].split()
42 juju_info["api-addresses"] = split48 juju_info["api-addresses"] = split
43 juju_info_list.append(juju_info)49
50 # Strip away machine-id, which is not understood by the old format
51 machine_id = juju_info.pop("machine-id", None)
52
53 if index == 0 and machine_id is not None:
54 # We care only about the first file, as the machine ID is the same
55 # for all
56 new_juju_info["environment-uuid"] = juju_info["environment-uuid"]
57 new_juju_info["api-addresses"] = juju_info["api-addresses"]
58 new_juju_info["machine-id"] = machine_id
59
60 juju_info_list.append(juju_info)
4461
45 juju_info_list.sort(key=lambda x: x["unit-name"])62 juju_info_list.sort(key=lambda x: x["unit-name"])
46 return juju_info_list or None63
64 if juju_info_list:
65 return juju_info_list, new_juju_info
66 return None
4767
=== modified file 'landscape/lib/tests/test_juju.py'
--- landscape/lib/tests/test_juju.py 2014-06-11 08:47:29 +0000
+++ landscape/lib/tests/test_juju.py 2014-08-28 07:37:26 +0000
@@ -6,11 +6,13 @@
66
77
8SAMPLE_JUJU_INFO = json.dumps({"environment-uuid": "DEAD-BEEF",8SAMPLE_JUJU_INFO = json.dumps({"environment-uuid": "DEAD-BEEF",
9 "machine-id": "1",
9 "unit-name": "service/0",10 "unit-name": "service/0",
10 "api-addresses": "10.0.3.1:17070",11 "api-addresses": "10.0.3.1:17070",
11 "private-address": "127.0.0.1"})12 "private-address": "127.0.0.1"})
1213
13SAMPLE_JUJU_INFO_2 = json.dumps({"environment-uuid": "DEAD-BEEF",14SAMPLE_JUJU_INFO_2 = json.dumps({"environment-uuid": "DEAD-BEEF",
15 "machine-id": "1",
14 "unit-name": "service-2/0",16 "unit-name": "service-2/0",
15 "api-addresses": "10.0.3.2:17070",17 "api-addresses": "10.0.3.2:17070",
16 "private-address": "127.0.0.1"})18 "private-address": "127.0.0.1"})
@@ -28,11 +30,23 @@
28 return self.makeFile(30 return self.makeFile(
29 contents, dirname=self.stub_config.juju_directory, suffix=".json")31 contents, dirname=self.stub_config.juju_directory, suffix=".json")
3032
33 def test_get_juju_info(self):
34 """
35 L{get_juju_info} parses JSON data from the '*.json' files in the
36 juju_directory. A single file is present.
37 """
38 self._create_tmp_juju_file(SAMPLE_JUJU_INFO)
39 _, juju_info = get_juju_info(self.stub_config)
40 self.assertEqual(
41 {u"environment-uuid": "DEAD-BEEF",
42 u"machine-id": "1",
43 u"api-addresses": ["10.0.3.1:17070"]}, juju_info)
44
31 def test_get_juju_info_sample_data(self):45 def test_get_juju_info_sample_data(self):
32 """L{get_juju_info} parses JSON data from the '*.json' files in the46 """L{get_juju_info} parses JSON data from the '*.json' files in the
33 juju_directory. A single file is present."""47 juju_directory. A single file is present."""
34 self._create_tmp_juju_file(SAMPLE_JUJU_INFO)48 self._create_tmp_juju_file(SAMPLE_JUJU_INFO)
35 juju_info = get_juju_info(self.stub_config)49 juju_info, _ = get_juju_info(self.stub_config)
36 self.assertEqual([50 self.assertEqual([
37 {u"environment-uuid": "DEAD-BEEF",51 {u"environment-uuid": "DEAD-BEEF",
38 u"unit-name": "service/0",52 u"unit-name": "service/0",
@@ -45,7 +59,7 @@
45 landscape-client charm."""59 landscape-client charm."""
46 self.stub_config = self.Config(self.makeDir(), self.makeFile(60 self.stub_config = self.Config(self.makeDir(), self.makeFile(
47 content=SAMPLE_JUJU_INFO, basename="juju-info", suffix=".json"))61 content=SAMPLE_JUJU_INFO, basename="juju-info", suffix=".json"))
48 juju_info = get_juju_info(self.stub_config)62 juju_info, _ = get_juju_info(self.stub_config)
49 self.assertEqual([63 self.assertEqual([
50 {u"environment-uuid": "DEAD-BEEF",64 {u"environment-uuid": "DEAD-BEEF",
51 u"unit-name": "service/0",65 u"unit-name": "service/0",
@@ -59,7 +73,7 @@
59 """73 """
60 self._create_tmp_juju_file(SAMPLE_JUJU_INFO)74 self._create_tmp_juju_file(SAMPLE_JUJU_INFO)
61 self._create_tmp_juju_file(SAMPLE_JUJU_INFO_2)75 self._create_tmp_juju_file(SAMPLE_JUJU_INFO_2)
62 juju_info = get_juju_info(self.stub_config)76 juju_info, _ = get_juju_info(self.stub_config)
63 self.assertEqual([77 self.assertEqual([
64 {u"environment-uuid": "DEAD-BEEF",78 {u"environment-uuid": "DEAD-BEEF",
65 u"unit-name": "service-2/0",79 u"unit-name": "service-2/0",
@@ -77,7 +91,7 @@
77 self._create_tmp_juju_file(SAMPLE_JUJU_INFO)91 self._create_tmp_juju_file(SAMPLE_JUJU_INFO)
78 self.makeFile(SAMPLE_JUJU_INFO_2, suffix=".txt",92 self.makeFile(SAMPLE_JUJU_INFO_2, suffix=".txt",
79 dirname=self.stub_config.juju_directory)93 dirname=self.stub_config.juju_directory)
80 juju_info = get_juju_info(self.stub_config)94 juju_info, _ = get_juju_info(self.stub_config)
81 self.assertEqual([95 self.assertEqual([
82 {u"environment-uuid": "DEAD-BEEF",96 {u"environment-uuid": "DEAD-BEEF",
83 u"unit-name": "service/0",97 u"unit-name": "service/0",
@@ -89,10 +103,9 @@
89 If L{get_juju_info} is called with a configuration pointing to103 If L{get_juju_info} is called with a configuration pointing to
90 an empty file, it returns C{None}.104 an empty file, it returns C{None}.
91 """105 """
106 self.log_helper.ignore_errors(ValueError)
92 self._create_tmp_juju_file("")107 self._create_tmp_juju_file("")
93 juju_info = get_juju_info(self.stub_config)108 self.assertIs(None, get_juju_info(self.stub_config))
94 self.log_helper.ignore_errors(ValueError)
95 self.assertEqual(juju_info, None)
96 self.assertIn("Error attempting to read JSON", self.logfile.getvalue())109 self.assertIn("Error attempting to read JSON", self.logfile.getvalue())
97110
98 def test_get_juju_info_no_json_file(self):111 def test_get_juju_info_no_json_file(self):
@@ -100,19 +113,19 @@
100 If L{get_juju_info} is called with a configuration pointing to113 If L{get_juju_info} is called with a configuration pointing to
101 a directory containing no json files, it returns None.114 a directory containing no json files, it returns None.
102 """115 """
103 juju_info = get_juju_info(self.stub_config)116 self.assertIs(None, get_juju_info(self.stub_config))
104 self.assertIs(juju_info, None)
105117
106 def test_get_juju_info_multiple_endpoints(self):118 def test_get_juju_info_multiple_endpoints(self):
107 """L{get_juju_info} turns space separated API addresses into a list."""119 """L{get_juju_info} turns space separated API addresses into a list."""
108 juju_multiple_endpoints = json.dumps({120 juju_multiple_endpoints = json.dumps({
109 "environment-uuid": "DEAD-BEEF",121 "environment-uuid": "DEAD-BEEF",
122 "machine-id": "0",
110 "unit-name": "service/0",123 "unit-name": "service/0",
111 "api-addresses": "10.0.3.1:17070 10.0.3.2:18080",124 "api-addresses": "10.0.3.1:17070 10.0.3.2:18080",
112 "private-address": "127.0.0.1"})125 "private-address": "127.0.0.1"})
113126
114 self._create_tmp_juju_file(juju_multiple_endpoints)127 self._create_tmp_juju_file(juju_multiple_endpoints)
115 juju_info = get_juju_info(self.stub_config)128 juju_info, _ = get_juju_info(self.stub_config)
116 self.assertEqual([129 self.assertEqual([
117 {"environment-uuid": "DEAD-BEEF",130 {"environment-uuid": "DEAD-BEEF",
118 "unit-name": "service/0",131 "unit-name": "service/0",
119132
=== modified file 'landscape/monitor/jujuinfo.py'
--- landscape/monitor/jujuinfo.py 2014-06-30 18:40:29 +0000
+++ landscape/monitor/jujuinfo.py 2014-08-28 07:37:26 +0000
@@ -5,7 +5,12 @@
55
66
7class JujuInfo(MonitorPlugin):7class JujuInfo(MonitorPlugin):
8 """Plugin for reporting Juju information."""8 """Plugin for reporting Juju information.
9
10 XXX this plugin is going to be dropped when the transition from
11 unit-computer association to machine-computer association is
12 completed on the server.
13 """
914
10 persist_name = "juju-info"15 persist_name = "juju-info"
11 scope = "juju"16 scope = "juju"
@@ -35,6 +40,9 @@
35 """40 """
36 juju_info = get_juju_info(self.registry.config)41 juju_info = get_juju_info(self.registry.config)
3742
43 if juju_info:
44 juju_info = juju_info[0]
45
38 if juju_info != self._persist.get("juju-info"):46 if juju_info != self._persist.get("juju-info"):
39 self._persist.set("juju-info", juju_info)47 self._persist.set("juju-info", juju_info)
40 message = {"type": "juju-units-info",48 message = {"type": "juju-units-info",
4149
=== modified file 'landscape/monitor/tests/test_jujuinfo.py'
--- landscape/monitor/tests/test_jujuinfo.py 2014-06-27 11:02:24 +0000
+++ landscape/monitor/tests/test_jujuinfo.py 2014-08-28 07:37:26 +0000
@@ -6,6 +6,7 @@
66
77
8SAMPLE_JUJU_INFO = json.dumps({"environment-uuid": "DEAD-BEEF",8SAMPLE_JUJU_INFO = json.dumps({"environment-uuid": "DEAD-BEEF",
9 "machine-id": "1",
9 "unit-name": "juju-unit-name",10 "unit-name": "juju-unit-name",
10 "api-addresses": "10.0.3.1:17070",11 "api-addresses": "10.0.3.1:17070",
11 "private-address": "127.0.0.1"})12 "private-address": "127.0.0.1"})
@@ -72,6 +73,7 @@
7273
73 self.makeFile(74 self.makeFile(
74 json.dumps({"environment-uuid": "FEED-BEEF",75 json.dumps({"environment-uuid": "FEED-BEEF",
76 "machine-id": "1",
75 "unit-name": "changed-unit-name",77 "unit-name": "changed-unit-name",
76 "api-addresses": "10.0.3.2:17070",78 "api-addresses": "10.0.3.2:17070",
77 "private-address": "127.0.1.1"}),79 "private-address": "127.0.1.1"}),

Subscribers

People subscribed via source and target branches

to all changes: