Merge lp:~bjornt/maas/peer_proxy into lp:~maas-committers/maas/trunk
- peer_proxy
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Björn Tillenius | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 6092 | ||||
Proposed branch: | lp:~bjornt/maas/peer_proxy | ||||
Merge into: | lp:~maas-committers/maas/trunk | ||||
Prerequisite: | lp:~bjornt/maas/get-apt-proxy | ||||
Diff against target: |
646 lines (+363/-16) 17 files modified
src/maasserver/api/tests/test_maas.py (+11/-0) src/maasserver/compose_preseed.py (+2/-1) src/maasserver/forms/settings.py (+12/-0) src/maasserver/models/config.py (+1/-0) src/maasserver/proxyconfig.py (+17/-0) src/maasserver/service_monitor.py (+2/-1) src/maasserver/tests/test_compose_preseed.py (+23/-11) src/maasserver/tests/test_preseed.py (+36/-0) src/maasserver/tests/test_proxyconfig.py (+68/-0) src/maasserver/tests/test_service_monitor.py (+21/-0) src/maasserver/triggers/system.py (+42/-0) src/maasserver/triggers/testing.py (+14/-0) src/maasserver/triggers/tests/test_init.py (+2/-0) src/maasserver/triggers/tests/test_system_listener.py (+97/-1) src/provisioningserver/templates/proxy/maas-proxy.conf.template (+4/-0) src/provisioningserver/utils/service_monitor.py (+4/-2) src/provisioningserver/utils/tests/test_service_monitor.py (+7/-0) |
||||
To merge this branch: | bzr merge lp:~bjornt/maas/peer_proxy | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike Pontillo (community) | Approve | ||
Review via email: mp+325758@code.launchpad.net |
Commit message
Add support for using the http proxy as a peer proxy.
If use_peer_proxy is True in the settings, the built-in http proxy will
be configured to use the specified http proxy as a cache peer. So that
machines will go through the built-in proxy, which in turn will go
through the external http proxy to download packages.
It's possible to configure use_peer_proxy via the API for now. The UI
will come later.
Description of the change
Add support for using the http proxy as a peer proxy.
If use_peer_proxy is True in the settings, the built-in http proxy will
be configured to use the specified http proxy as a cache peer. So that
machines will go through the built-in proxy, which in turn will go
through the external http proxy to download packages.
It's possible to configure use_peer_proxy via the API for now. The UI
will come later.
Björn Tillenius (bjornt) wrote : | # |
Ah, yes, it does fixes that bug. I didn't know about it, but I have linked it now.
Mike Pontillo (mpontillo) wrote : | # |
Thanks. I can't tell how you linked the bug; can you be sure to link it from a bzr commit, if you didn't already? (such as by doing 'bzr commit --unchanged -m "Link bug." --fixes lp:1276945 && bzr push') That way "Fix Committed" on the bug will automatically be set when it lands.
Oh, also consider adding the second paragraph in your description to the commit message.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~bjornt/maas/peer_proxy into lp:maas failed. Below is the output from the failed tests.
Hit:1 http://
Hit:2 http://
Get:3 http://
Get:4 http://
Fetched 204 kB in 1s (173 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
authbind is already the newest version (2.1.1+nmu1).
avahi-utils is already the newest version (0.6.32~
build-essential is already the newest version (12.1ubuntu2).
debhelper is already the newest version (9.20160115ubun
distro-info is already the newest version (0.14build1).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1ubuntu3).
make is already the newest version (4.1-6).
postgresql is already the newest version (9.5+173).
psmisc is already the newest version (22.21-2.1build1).
pxelinux is already the newest version (3:6.03+
python-formencode is already the newest version (1.3.0-0ubuntu5).
python-lxml is already the newest version (3.5.0-1build1).
python-netaddr is already the newest version (0.7.18-1).
python-netifaces is already the newest version (0.10.4-0.1build2).
python-psycopg2 is already the newest version (2.6.1-1build2).
python-simplejson is already ...
Preview Diff
1 | === modified file 'src/maasserver/api/tests/test_maas.py' |
2 | --- src/maasserver/api/tests/test_maas.py 2017-02-17 14:23:04 +0000 |
3 | +++ src/maasserver/api/tests/test_maas.py 2017-06-16 13:49:45 +0000 |
4 | @@ -281,3 +281,14 @@ |
5 | self.assertThat( |
6 | PackageRepository.get_ports_archive().url, |
7 | Equals(ports_archive)) |
8 | + |
9 | + def test_set_config_use_peer_proxy(self): |
10 | + self.become_admin() |
11 | + response = self.client.post( |
12 | + reverse('maas_handler'), { |
13 | + "op": "set_config", |
14 | + "name": "use_peer_proxy", |
15 | + "value": True, |
16 | + }) |
17 | + self.assertEqual(http.client.OK, response.status_code) |
18 | + self.assertTrue(Config.objects.get_config("use_peer_proxy")) |
19 | |
20 | === modified file 'src/maasserver/compose_preseed.py' |
21 | --- src/maasserver/compose_preseed.py 2017-06-15 16:04:10 +0000 |
22 | +++ src/maasserver/compose_preseed.py 2017-06-16 13:49:45 +0000 |
23 | @@ -37,7 +37,8 @@ |
24 | http_proxy = Config.objects.get_config("http_proxy") |
25 | if http_proxy is not None: |
26 | http_proxy = http_proxy.strip() |
27 | - if http_proxy: |
28 | + use_peer_proxy = Config.objects.get_config("use_peer_proxy") |
29 | + if http_proxy and not use_peer_proxy: |
30 | return http_proxy |
31 | else: |
32 | return compose_URL( |
33 | |
34 | === modified file 'src/maasserver/forms/settings.py' |
35 | --- src/maasserver/forms/settings.py 2017-04-24 16:47:00 +0000 |
36 | +++ src/maasserver/forms/settings.py 2017-06-16 13:49:45 +0000 |
37 | @@ -225,6 +225,18 @@ |
38 | "downloading boot images.") |
39 | } |
40 | }, |
41 | + 'use_peer_proxy': { |
42 | + 'default': False, |
43 | + 'form': forms.BooleanField, |
44 | + 'form_kwargs': { |
45 | + 'label': "Use the built-in proxy with an external proxy as a peer", |
46 | + 'required': False, |
47 | + 'help_text': ( |
48 | + "If enable_http_proxy is set, the built-in proxy will be " |
49 | + "configured to use http_proxy as a peer proxy. The deployed " |
50 | + "machines will be configured to use the built-in proxy.") |
51 | + } |
52 | + }, |
53 | 'http_proxy': { |
54 | 'default': None, |
55 | 'form': forms.URLField, |
56 | |
57 | === modified file 'src/maasserver/models/config.py' |
58 | --- src/maasserver/models/config.py 2017-04-07 21:52:35 +0000 |
59 | +++ src/maasserver/models/config.py 2017-06-16 13:49:45 +0000 |
60 | @@ -69,6 +69,7 @@ |
61 | 'default_osystem': DEFAULT_OS.name, |
62 | 'default_distro_series': DEFAULT_OS.get_default_release(), |
63 | 'enable_http_proxy': True, |
64 | + 'use_peer_proxy': False, |
65 | 'http_proxy': None, |
66 | 'upstream_dns': None, |
67 | 'dnssec_validation': "auto", |
68 | |
69 | === modified file 'src/maasserver/proxyconfig.py' |
70 | --- src/maasserver/proxyconfig.py 2017-05-02 19:00:58 +0000 |
71 | +++ src/maasserver/proxyconfig.py 2017-06-16 13:49:45 +0000 |
72 | @@ -13,8 +13,10 @@ |
73 | import os |
74 | import socket |
75 | import sys |
76 | +from urllib.parse import urlparse |
77 | |
78 | from django.conf import settings |
79 | +from maasserver.models import Config |
80 | from maasserver.models.subnet import Subnet |
81 | from maasserver.service_monitor import service_monitor |
82 | from maasserver.utils.orm import transactional |
83 | @@ -67,6 +69,10 @@ |
84 | def write_config(): |
85 | allowed_subnets = Subnet.objects.filter(allow_proxy=True) |
86 | cidrs = [subnet.cidr for subnet in allowed_subnets] |
87 | + |
88 | + http_proxy = Config.objects.get_config("http_proxy") |
89 | + upstream_proxy_enabled = ( |
90 | + Config.objects.get_config("use_peer_proxy") and http_proxy) |
91 | context = { |
92 | 'allowed': allowed_subnets, |
93 | 'modified': str(datetime.date.today()), |
94 | @@ -76,7 +82,18 @@ |
95 | 'snap_path': snappy.get_snap_path(), |
96 | 'snap_data_path': snappy.get_snap_data_path(), |
97 | 'snap_common_path': snappy.get_snap_common_path(), |
98 | + 'upstream_peer_proxy': upstream_proxy_enabled, |
99 | } |
100 | + |
101 | + proxy_enabled = Config.objects.get_config("enable_http_proxy") |
102 | + if proxy_enabled and upstream_proxy_enabled: |
103 | + http_proxy_hostname = urlparse(http_proxy).hostname |
104 | + http_proxy_port = urlparse(http_proxy).port |
105 | + context.update({ |
106 | + 'upstream_proxy_address': http_proxy_hostname, |
107 | + 'upstream_proxy_port': http_proxy_port, |
108 | + }) |
109 | + |
110 | template_path = locate_template('proxy', MAAS_PROXY_CONF_TEMPLATE) |
111 | template = tempita.Template.from_filename( |
112 | template_path, encoding="UTF-8") |
113 | |
114 | === modified file 'src/maasserver/service_monitor.py' |
115 | --- src/maasserver/service_monitor.py 2016-10-18 16:57:31 +0000 |
116 | +++ src/maasserver/service_monitor.py 2017-06-16 13:49:45 +0000 |
117 | @@ -52,7 +52,8 @@ |
118 | # Avoid recursive import. |
119 | from maasserver import proxyconfig |
120 | if (Config.objects.get_config("enable_http_proxy") and |
121 | - Config.objects.get_config("http_proxy")): |
122 | + Config.objects.get_config("http_proxy") and |
123 | + not Config.objects.get_config("use_peer_proxy")): |
124 | return (SERVICE_STATE.OFF, |
125 | "disabled, alternate proxy is configured in settings.") |
126 | elif proxyconfig.is_config_present() is False: |
127 | |
128 | === modified file 'src/maasserver/tests/test_compose_preseed.py' |
129 | --- src/maasserver/tests/test_compose_preseed.py 2017-06-15 16:04:10 +0000 |
130 | +++ src/maasserver/tests/test_compose_preseed.py 2017-06-16 13:49:45 +0000 |
131 | @@ -51,26 +51,37 @@ |
132 | rack='2001:db8::1', |
133 | result='http://[2001:db8::1]:8000/', |
134 | enable=True, |
135 | + use_peer_proxy=False, |
136 | http_proxy='')), |
137 | ("ipv4", dict( |
138 | rack='10.0.1.1', |
139 | result='http://10.0.1.1:8000/', |
140 | enable=True, |
141 | - http_proxy='')), |
142 | - ("name", dict( |
143 | - rack='example.com', |
144 | - result='http://example.com:8000/', |
145 | - enable=True, |
146 | - http_proxy='')), |
147 | - ("override", dict( |
148 | - rack='wrong.com', |
149 | - result='http://example.com:111/', |
150 | - enable=True, |
151 | - http_proxy='http://example.com:111/')), |
152 | + use_peer_proxy=False, |
153 | + http_proxy='')), |
154 | + ("builtin", dict( |
155 | + rack='region.example.com', |
156 | + result='http://region.example.com:8000/', |
157 | + enable=True, |
158 | + use_peer_proxy=False, |
159 | + http_proxy='')), |
160 | + ("external", dict( |
161 | + rack='region.example.com', |
162 | + result='http://proxy.example.com:111/', |
163 | + enable=True, |
164 | + use_peer_proxy=False, |
165 | + http_proxy='http://proxy.example.com:111/')), |
166 | + ("peer-proxy", dict( |
167 | + rack='region.example.com', |
168 | + result='http://region.example.com:8000/', |
169 | + enable=True, |
170 | + use_peer_proxy=True, |
171 | + http_proxy='http://proxy.example.com:111/')), |
172 | ("disabled", dict( |
173 | rack='example.com', |
174 | result=None, |
175 | enable=False, |
176 | + use_peer_proxy=False, |
177 | http_proxy='')), |
178 | ) |
179 | |
180 | @@ -89,6 +100,7 @@ |
181 | interface=True, status=NODE_STATUS.COMMISSIONING) |
182 | Config.objects.set_config("enable_http_proxy", self.enable) |
183 | Config.objects.set_config("http_proxy", self.http_proxy) |
184 | + Config.objects.set_config("use_peer_proxy", self.use_peer_proxy) |
185 | actual = get_apt_proxy(node.get_boot_rack_controller()) |
186 | self.assertEqual(self.result, actual) |
187 | |
188 | |
189 | === modified file 'src/maasserver/tests/test_preseed.py' |
190 | --- src/maasserver/tests/test_preseed.py 2017-04-26 19:02:50 +0000 |
191 | +++ src/maasserver/tests/test_preseed.py 2017-06-16 13:49:45 +0000 |
192 | @@ -34,6 +34,7 @@ |
193 | BootResource, |
194 | Config, |
195 | PackageRepository, |
196 | + signals, |
197 | ) |
198 | from maasserver.preseed import ( |
199 | compose_curtin_archive_config, |
200 | @@ -1658,6 +1659,12 @@ |
201 | These tests check that the preseed templates render and 'look right'. |
202 | """ |
203 | |
204 | + def setUp(self): |
205 | + super().setUp() |
206 | + # We don't want to test that the bootsources get updated. |
207 | + self.addCleanup(signals.bootsources.signals.enable) |
208 | + signals.bootsources.signals.disable() |
209 | + |
210 | def assertSystemInfo(self, config): |
211 | self.assertThat(config, ContainsDict({ |
212 | 'system_info': MatchesDict({ |
213 | @@ -1747,6 +1754,35 @@ |
214 | self.assertAptConfig(preseed, apt_proxy) |
215 | self.assertSystemInfo(preseed) |
216 | |
217 | + def test_get_enlist_userdata_no_proxy(self): |
218 | + Config.objects.set_config('enable_http_proxy', False) |
219 | + Config.objects.set_config('http_proxy', 'http://example.com:3128') |
220 | + preseed = yaml.safe_load(get_enlist_userdata()) |
221 | + self.assertIsNone(preseed['apt_proxy']) |
222 | + self.assertNotIn('proxy', preseed['apt']) |
223 | + |
224 | + def test_get_enlist_userdata_use_builtin_proxy(self): |
225 | + Config.objects.set_config('enable_http_proxy', True) |
226 | + Config.objects.set_config('http_proxy', '') |
227 | + preseed = yaml.safe_load(get_enlist_userdata()) |
228 | + self.assertEqual('http://localhost:8000/', preseed['apt_proxy']) |
229 | + self.assertEqual('http://localhost:8000/', preseed['apt']['proxy']) |
230 | + |
231 | + def test_get_enlist_userdata_use_external_proxy(self): |
232 | + Config.objects.set_config('enable_http_proxy', True) |
233 | + Config.objects.set_config('http_proxy', 'http://example.com:3128/') |
234 | + preseed = yaml.safe_load(get_enlist_userdata()) |
235 | + self.assertEqual('http://example.com:3128/', preseed['apt_proxy']) |
236 | + self.assertEqual('http://example.com:3128/', preseed['apt']['proxy']) |
237 | + |
238 | + def test_get_enlist_userdata_use_peer_proxy(self): |
239 | + Config.objects.set_config('enable_http_proxy', True) |
240 | + Config.objects.set_config('use_peer_proxy', True) |
241 | + Config.objects.set_config('http_proxy', 'http://example.com:3128/') |
242 | + preseed = yaml.safe_load(get_enlist_userdata()) |
243 | + self.assertEqual('http://localhost:8000/', preseed['apt_proxy']) |
244 | + self.assertEqual('http://localhost:8000/', preseed['apt']['proxy']) |
245 | + |
246 | def test_get_preseed_returns_commissioning_preseed(self): |
247 | node = factory.make_Node_with_Interface_on_Subnet( |
248 | primary_rack=self.rpc_rack_controller, |
249 | |
250 | === modified file 'src/maasserver/tests/test_proxyconfig.py' |
251 | --- src/maasserver/tests/test_proxyconfig.py 2017-05-02 18:42:35 +0000 |
252 | +++ src/maasserver/tests/test_proxyconfig.py 2017-06-16 13:49:45 +0000 |
253 | @@ -6,11 +6,13 @@ |
254 | __all__ = [] |
255 | |
256 | import os |
257 | +from pathlib import Path |
258 | |
259 | from crochet import wait_for |
260 | from django.conf import settings |
261 | from fixtures import EnvironmentVariableFixture |
262 | from maasserver import proxyconfig |
263 | +from maasserver.models import Config |
264 | from maasserver.testing.factory import factory |
265 | from maasserver.testing.testcase import ( |
266 | MAASServerTestCase, |
267 | @@ -58,6 +60,7 @@ |
268 | def setUp(self): |
269 | super(TestProxyUpdateConfig, self).setUp() |
270 | self.tmpdir = self.make_dir() |
271 | + self.proxy_path = Path(self.tmpdir) / proxyconfig.MAAS_PROXY_CONF_NAME |
272 | self.service_monitor = self.patch(proxyconfig, "service_monitor") |
273 | self.useFixture( |
274 | EnvironmentVariableFixture('MAAS_PROXY_CONFIG_DIR', self.tmpdir)) |
275 | @@ -86,6 +89,71 @@ |
276 | |
277 | @wait_for_reactor |
278 | @inlineCallbacks |
279 | + def test__with_use_peer_proxy_with_http_proxy(self): |
280 | + self.patch(settings, "PROXY_CONNECT", True) |
281 | + yield deferToDatabase( |
282 | + transactional(Config.objects.set_config), |
283 | + "enable_http_proxy", True) |
284 | + yield deferToDatabase( |
285 | + transactional(Config.objects.set_config), |
286 | + "use_peer_proxy", True) |
287 | + yield deferToDatabase( |
288 | + transactional(Config.objects.set_config), |
289 | + "http_proxy", "http://example.com:8000/") |
290 | + yield deferToDatabase(self.make_subnet, allow_proxy=False) |
291 | + yield deferToDatabase(self.make_subnet) |
292 | + yield proxyconfig.proxy_update_config(reload_proxy=False) |
293 | + cache_peer_line = ( |
294 | + "cache_peer example.com parent 8000 0 no-query default") |
295 | + with self.proxy_path.open() as proxy_file: |
296 | + lines = [line.strip() for line in proxy_file.readlines()] |
297 | + self.assertIn('never_direct allow all', lines) |
298 | + self.assertIn(cache_peer_line, lines) |
299 | + |
300 | + @wait_for_reactor |
301 | + @inlineCallbacks |
302 | + def test__with_use_peer_proxy_without_http_proxy(self): |
303 | + self.patch(settings, "PROXY_CONNECT", True) |
304 | + yield deferToDatabase( |
305 | + transactional(Config.objects.set_config), |
306 | + "enable_http_proxy", True) |
307 | + yield deferToDatabase( |
308 | + transactional(Config.objects.set_config), |
309 | + "use_peer_proxy", True) |
310 | + yield deferToDatabase( |
311 | + transactional(Config.objects.set_config), |
312 | + "http_proxy", "") |
313 | + yield deferToDatabase(self.make_subnet, allow_proxy=False) |
314 | + yield deferToDatabase(self.make_subnet) |
315 | + yield proxyconfig.proxy_update_config(reload_proxy=False) |
316 | + with self.proxy_path.open() as proxy_file: |
317 | + lines = [line.strip() for line in proxy_file.readlines()] |
318 | + self.assertNotIn('never_direct allow all', lines) |
319 | + self.assertNotIn('cache_peer', lines) |
320 | + |
321 | + @wait_for_reactor |
322 | + @inlineCallbacks |
323 | + def test__without_use_peer_proxy(self): |
324 | + self.patch(settings, "PROXY_CONNECT", True) |
325 | + yield deferToDatabase( |
326 | + transactional(Config.objects.set_config), |
327 | + "enable_http_proxy", True) |
328 | + yield deferToDatabase( |
329 | + transactional(Config.objects.set_config), |
330 | + "use_peer_proxy", False) |
331 | + yield deferToDatabase( |
332 | + transactional(Config.objects.set_config), |
333 | + "http_proxy", "http://example.com:8000/") |
334 | + yield deferToDatabase(self.make_subnet, allow_proxy=False) |
335 | + yield deferToDatabase(self.make_subnet) |
336 | + yield proxyconfig.proxy_update_config(reload_proxy=False) |
337 | + with self.proxy_path.open() as proxy_file: |
338 | + lines = [line.strip() for line in proxy_file.readlines()] |
339 | + self.assertNotIn('never_direct allow all', lines) |
340 | + self.assertNotIn('cache_peer', lines) |
341 | + |
342 | + @wait_for_reactor |
343 | + @inlineCallbacks |
344 | def test__calls_reloadService(self): |
345 | self.patch(settings, "PROXY_CONNECT", True) |
346 | yield deferToDatabase(self.make_subnet) |
347 | |
348 | === modified file 'src/maasserver/tests/test_service_monitor.py' |
349 | --- src/maasserver/tests/test_service_monitor.py 2017-01-28 00:51:47 +0000 |
350 | +++ src/maasserver/tests/test_service_monitor.py 2017-06-16 13:49:45 +0000 |
351 | @@ -131,3 +131,24 @@ |
352 | (SERVICE_STATE.OFF, |
353 | 'disabled, alternate proxy is configured in settings.'), |
354 | expected_state) |
355 | + |
356 | + @wait_for_reactor |
357 | + @inlineCallbacks |
358 | + def test_getExpectedState_returns_on_for_proxy_on_and_set_peer_proxy(self): |
359 | + # Disable boot source cache signals. |
360 | + self.addCleanup(bootsources.signals.enable) |
361 | + bootsources.signals.disable() |
362 | + |
363 | + service = self.make_proxy_service() |
364 | + yield deferToDatabase( |
365 | + transactional(Config.objects.set_config), |
366 | + "enable_http_proxy", True) |
367 | + yield deferToDatabase( |
368 | + transactional(Config.objects.set_config), |
369 | + "use_peer_proxy", True) |
370 | + yield deferToDatabase( |
371 | + transactional(Config.objects.set_config), |
372 | + "http_proxy", factory.make_url()) |
373 | + self.patch(proxyconfig, "is_config_present").return_value = True |
374 | + expected_state = yield maybeDeferred(service.getExpectedState) |
375 | + self.assertEqual((SERVICE_STATE.ON, None), expected_state) |
376 | |
377 | === modified file 'src/maasserver/triggers/system.py' |
378 | --- src/maasserver/triggers/system.py 2017-05-05 15:30:51 +0000 |
379 | +++ src/maasserver/triggers/system.py 2017-06-16 13:49:45 +0000 |
380 | @@ -1050,6 +1050,38 @@ |
381 | """) |
382 | |
383 | |
384 | +# Triggered when the proxy settings are updated. |
385 | +PEER_PROXY_CONFIG_INSERT = dedent("""\ |
386 | + CREATE OR REPLACE FUNCTION sys_proxy_config_use_peer_proxy_insert() |
387 | + RETURNS trigger as $$ |
388 | + BEGIN |
389 | + IF (NEW.name = 'enable_proxy' OR |
390 | + NEW.name = 'use_peer_proxy' OR |
391 | + NEW.name = 'http_proxy') THEN |
392 | + PERFORM pg_notify('sys_proxy', ''); |
393 | + END IF; |
394 | + RETURN NEW; |
395 | + END; |
396 | + $$ LANGUAGE plpgsql; |
397 | + """) |
398 | + |
399 | + |
400 | +# Triggered when the proxy settings are updated. |
401 | +PEER_PROXY_CONFIG_UPDATE = dedent("""\ |
402 | + CREATE OR REPLACE FUNCTION sys_proxy_config_use_peer_proxy_update() |
403 | + RETURNS trigger as $$ |
404 | + BEGIN |
405 | + IF (NEW.name = 'enable_proxy' OR |
406 | + NEW.name = 'use_peer_proxy' OR |
407 | + NEW.name = 'http_proxy') THEN |
408 | + PERFORM pg_notify('sys_proxy', ''); |
409 | + END IF; |
410 | + RETURN NEW; |
411 | + END; |
412 | + $$ LANGUAGE plpgsql; |
413 | + """) |
414 | + |
415 | + |
416 | def render_sys_dns_procedure(proc_name, on_delete=False): |
417 | """Render a database procedure that creates a new DNS publication. |
418 | |
419 | @@ -1338,3 +1370,13 @@ |
420 | register_trigger( |
421 | "maasserver_subnet", |
422 | "sys_proxy_subnet_delete", "delete") |
423 | + |
424 | + # - Config/http_proxy (when use_peer_proxy) |
425 | + register_procedure(PEER_PROXY_CONFIG_INSERT) |
426 | + register_trigger( |
427 | + "maasserver_config", "sys_proxy_config_use_peer_proxy_insert", |
428 | + "insert") |
429 | + register_procedure(PEER_PROXY_CONFIG_UPDATE) |
430 | + register_trigger( |
431 | + "maasserver_config", "sys_proxy_config_use_peer_proxy_update", |
432 | + "update") |
433 | |
434 | === modified file 'src/maasserver/triggers/testing.py' |
435 | --- src/maasserver/triggers/testing.py 2017-05-05 14:47:35 +0000 |
436 | +++ src/maasserver/triggers/testing.py 2017-06-16 13:49:45 +0000 |
437 | @@ -19,6 +19,7 @@ |
438 | Pod, |
439 | ) |
440 | from maasserver.models.cacheset import CacheSet |
441 | +from maasserver.models.config import Config |
442 | from maasserver.models.dhcpsnippet import DHCPSnippet |
443 | from maasserver.models.dnsdata import DNSData |
444 | from maasserver.models.dnspublication import DNSPublication |
445 | @@ -751,6 +752,19 @@ |
446 | def reload_object(self, obj): |
447 | return reload_object(obj) |
448 | |
449 | + @transactional |
450 | + def create_config(self, name, value): |
451 | + config, freshly_created = Config.objects.get_or_create( |
452 | + name=name, defaults=dict(value=value)) |
453 | + assert freshly_created, "Config already created." |
454 | + return config |
455 | + |
456 | + @transactional |
457 | + def set_config(self, name, value): |
458 | + config = Config.objects.get(name=name) |
459 | + config.value = value |
460 | + config.save() |
461 | + |
462 | |
463 | class DNSHelpersMixin: |
464 | """Helper to get the zone serial and to assert it was incremented.""" |
465 | |
466 | === modified file 'src/maasserver/triggers/tests/test_init.py' |
467 | --- src/maasserver/triggers/tests/test_init.py 2017-04-17 17:04:50 +0000 |
468 | +++ src/maasserver/triggers/tests/test_init.py 2017-06-16 13:49:45 +0000 |
469 | @@ -123,6 +123,8 @@ |
470 | "config_config_create_notify", |
471 | "config_config_delete_notify", |
472 | "config_config_update_notify", |
473 | + "config_sys_proxy_config_use_peer_proxy_insert", |
474 | + "config_sys_proxy_config_use_peer_proxy_update", |
475 | "dhcpsnippet_dhcpsnippet_create_notify", |
476 | "dhcpsnippet_dhcpsnippet_delete_notify", |
477 | "dhcpsnippet_dhcpsnippet_update_notify", |
478 | |
479 | === modified file 'src/maasserver/triggers/tests/test_system_listener.py' |
480 | --- src/maasserver/triggers/tests/test_system_listener.py 2017-05-05 15:11:21 +0000 |
481 | +++ src/maasserver/triggers/tests/test_system_listener.py 2017-06-16 13:49:45 +0000 |
482 | @@ -3713,7 +3713,7 @@ |
483 | % (json.dumps(kms_host_old), json.dumps(kms_host_new)))) |
484 | |
485 | |
486 | -class TestProxySubnetListener( |
487 | +class TestProxyListener( |
488 | MAASTransactionServerTestCase, TransactionalHelpersMixin): |
489 | """End-to-end test for the proxy triggers code.""" |
490 | |
491 | @@ -3787,3 +3787,99 @@ |
492 | yield dv.get(timeout=2) |
493 | finally: |
494 | yield listener.stopService() |
495 | + |
496 | + @wait_for_reactor |
497 | + @inlineCallbacks |
498 | + def test_sends_message_for_config_insert_enable_proxy(self): |
499 | + yield deferToDatabase(register_system_triggers) |
500 | + dv = DeferredValue() |
501 | + listener = self.make_listener_without_delay() |
502 | + listener.register( |
503 | + "sys_proxy", lambda *args: dv.set(args)) |
504 | + yield listener.startService() |
505 | + try: |
506 | + yield deferToDatabase(self.create_config, "enable_proxy", True) |
507 | + yield dv.get(timeout=2) |
508 | + finally: |
509 | + yield listener.stopService() |
510 | + |
511 | + @wait_for_reactor |
512 | + @inlineCallbacks |
513 | + def test_sends_message_for_config_insert_use_peer_proxy(self): |
514 | + yield deferToDatabase(register_system_triggers) |
515 | + dv = DeferredValue() |
516 | + listener = self.make_listener_without_delay() |
517 | + listener.register( |
518 | + "sys_proxy", lambda *args: dv.set(args)) |
519 | + yield listener.startService() |
520 | + try: |
521 | + yield deferToDatabase(self.create_config, "use_peer_proxy", True) |
522 | + yield dv.get(timeout=2) |
523 | + finally: |
524 | + yield listener.stopService() |
525 | + |
526 | + @wait_for_reactor |
527 | + @inlineCallbacks |
528 | + def test_sends_message_for_config_insert_http_proxy(self): |
529 | + yield deferToDatabase(register_system_triggers) |
530 | + dv = DeferredValue() |
531 | + listener = self.make_listener_without_delay() |
532 | + listener.register( |
533 | + "sys_proxy", lambda *args: dv.set(args)) |
534 | + yield listener.startService() |
535 | + try: |
536 | + yield deferToDatabase( |
537 | + self.create_config, "http_proxy", "http://proxy.example.com") |
538 | + yield dv.get(timeout=2) |
539 | + finally: |
540 | + yield listener.stopService() |
541 | + |
542 | + @wait_for_reactor |
543 | + @inlineCallbacks |
544 | + def test_sends_message_for_config_update_enable_proxy(self): |
545 | + yield deferToDatabase(register_system_triggers) |
546 | + yield deferToDatabase(self.create_config, "enable_proxy", True) |
547 | + dv = DeferredValue() |
548 | + listener = self.make_listener_without_delay() |
549 | + listener.register( |
550 | + "sys_proxy", lambda *args: dv.set(args)) |
551 | + yield listener.startService() |
552 | + try: |
553 | + yield deferToDatabase(self.set_config, "enable_proxy", False) |
554 | + yield dv.get(timeout=2) |
555 | + finally: |
556 | + yield listener.stopService() |
557 | + |
558 | + @wait_for_reactor |
559 | + @inlineCallbacks |
560 | + def test_sends_message_for_config_update_use_peer_proxy(self): |
561 | + yield deferToDatabase(register_system_triggers) |
562 | + yield deferToDatabase(self.create_config, "use_peer_proxy", True) |
563 | + dv = DeferredValue() |
564 | + listener = self.make_listener_without_delay() |
565 | + listener.register( |
566 | + "sys_proxy", lambda *args: dv.set(args)) |
567 | + yield listener.startService() |
568 | + try: |
569 | + yield deferToDatabase(self.set_config, "use_peer_proxy", False) |
570 | + yield dv.get(timeout=2) |
571 | + finally: |
572 | + yield listener.stopService() |
573 | + |
574 | + @wait_for_reactor |
575 | + @inlineCallbacks |
576 | + def test_sends_message_for_config_update_http_proxy(self): |
577 | + yield deferToDatabase(register_system_triggers) |
578 | + yield deferToDatabase( |
579 | + self.create_config, "http_proxy", "http://proxy1.example.com") |
580 | + dv = DeferredValue() |
581 | + listener = self.make_listener_without_delay() |
582 | + listener.register( |
583 | + "sys_proxy", lambda *args: dv.set(args)) |
584 | + yield listener.startService() |
585 | + try: |
586 | + yield deferToDatabase( |
587 | + self.set_config, "http_proxy", "http://proxy2.example.com") |
588 | + yield dv.get(timeout=2) |
589 | + finally: |
590 | + yield listener.stopService() |
591 | |
592 | === modified file 'src/provisioningserver/templates/proxy/maas-proxy.conf.template' |
593 | --- src/provisioningserver/templates/proxy/maas-proxy.conf.template 2017-05-12 16:23:16 +0000 |
594 | +++ src/provisioningserver/templates/proxy/maas-proxy.conf.template 2017-06-16 13:49:45 +0000 |
595 | @@ -57,3 +57,7 @@ |
596 | cache_log /var/log/maas/proxy/cache.log |
597 | cache_store_log /var/log/maas/proxy/store.log |
598 | {{endif}} |
599 | +{{if upstream_peer_proxy}} |
600 | +cache_peer {{upstream_proxy_address}} parent {{upstream_proxy_port}} 0 no-query default |
601 | +never_direct allow all |
602 | +{{endif}} |
603 | |
604 | === modified file 'src/provisioningserver/utils/service_monitor.py' |
605 | --- src/provisioningserver/utils/service_monitor.py 2017-02-17 14:23:04 +0000 |
606 | +++ src/provisioningserver/utils/service_monitor.py 2017-06-16 13:49:45 +0000 |
607 | @@ -299,17 +299,19 @@ |
608 | |
609 | @asynchronous |
610 | @inlineCallbacks |
611 | - def restartService(self, name): |
612 | + def restartService(self, name, if_on=False): |
613 | """Restart service. |
614 | |
615 | Service will only be restarted if its expected state is ON. |
616 | `ServiceNotOnError` will be raised if restart is called and the |
617 | - services expected state is not ON. |
618 | + services expected state is not ON, except if if_on is True. |
619 | """ |
620 | service = self.getServiceByName(name) |
621 | expected_state, _ = yield maybeDeferred(service.getExpectedState) |
622 | _check_service_state_expected(expected_state) |
623 | if expected_state != SERVICE_STATE.ON: |
624 | + if if_on: |
625 | + return |
626 | raise ServiceNotOnError( |
627 | "Service '%s' is not expected to be on, unable to restart." % ( |
628 | service.service_name)) |
629 | |
630 | === modified file 'src/provisioningserver/utils/tests/test_service_monitor.py' |
631 | --- src/provisioningserver/utils/tests/test_service_monitor.py 2017-02-17 14:23:04 +0000 |
632 | +++ src/provisioningserver/utils/tests/test_service_monitor.py 2017-06-16 13:49:45 +0000 |
633 | @@ -378,6 +378,13 @@ |
634 | yield service_monitor.reloadService(fake_service.name) |
635 | |
636 | @inlineCallbacks |
637 | + def test__reloadService_returns_when_if_on(self): |
638 | + fake_service = make_fake_service(SERVICE_STATE.OFF) |
639 | + service_monitor = self.make_service_monitor([fake_service]) |
640 | + yield service_monitor.restartService(fake_service.name, if_on=True) |
641 | + # No exception expected. |
642 | + |
643 | + @inlineCallbacks |
644 | def test__reloadService_calls_ensureService_then_reloads(self): |
645 | fake_service = make_fake_service(SERVICE_STATE.ON) |
646 | service_monitor = self.make_service_monitor([fake_service]) |
Does this fix bug #1276945? If so, can you link the bug?
Other than that, looks good to me. Nice that you updated the trigger, too; sometimes that's overlooked and it causes problems.