Merge ~blake-rouse/maas:improved-dns-reload into maas:master
- Git
- lp:~blake-rouse/maas
- improved-dns-reload
- Merge into master
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Mike Pontillo | ||||||||
Approved revision: | 83d0b4200ba97eacc5f56b1c26a97928172bf959 | ||||||||
Merge reported by: | MAAS Lander | ||||||||
Merged at revision: | not available | ||||||||
Proposed branch: | ~blake-rouse/maas:improved-dns-reload | ||||||||
Merge into: | maas:master | ||||||||
Diff against target: |
332 lines (+196/-5) 4 files modified
src/maasserver/dns/config.py (+6/-0) src/maasserver/dns/tests/test_config.py (+19/-0) src/maasserver/region_controller.py (+46/-3) src/maasserver/tests/test_region_controller.py (+125/-2) |
||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike Pontillo (community) | Approve | ||
Review via email: mp+329260@code.launchpad.net |
Description of the change
Blake Rouse (blake-rouse) : | # |
Mike Pontillo (mpontillo) wrote : | # |
Thanks for the replies. Sounds good.
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b improved-dns-reload lp:~blake-rouse/maas into -b master lp:~maas-committers/maas
STATUS: FAILED BUILD
LOG: http://
Felipe Reyes (freyes) wrote : | # |
Hello,
I'm trying to understand how this patch "throttles bind9 reloads", if I'm reading this correctly, this will only check (after rndc reload is ran) that the reload succeeded querying and verifying the serial is the expected one.
If there is a storm of dhcp requests and produces tenths of events (e.g. commissioning many nodes at the same time, or a rack coming back from a power failure), bind9 will still be reloaded for everyone of those events even if they are within the same second.
Best,
Mike Pontillo (mpontillo) wrote : | # |
@freyes, it was my understanding after looking at the code with Blake that the surrounding code in MAAS already prevents concurrent reloads. (That is, the `rdnc` command sends a message to the BIND process, the BIND process asynchronously processes it, and control is returned to MAAS almost immediately.) This patch just ensures that the next reload starts after the previous reload has completed.
Felipe Reyes (freyes) wrote : | # |
there are no concurrent reloads, but there is no throttling, e.g. if 2 events within the same second arrive, maas will run 'rndc reload' twice, while it could just run it once at the end of processing of the second event (the last that finishes it task), that's what it's suggested in the bug description:
""" - Throttle consecutive updates. (For example, when the first DNS change occurs, schedule a task for a few seconds in the future which will update DNS, and don't ever schedule more additional updates while that task is pending.)"""
Mike Pontillo (mpontillo) wrote : | # |
That's true. My bug description did overstep a little bit in that it described how I would have personally fixed the issue. Since this patch doesn't address that aspect of the solution, I'll probably need to change the title/description and file another bug for the "excessive reloads" aspect.
Mike Pontillo (mpontillo) wrote : | # |
I've filed bug #1712205 to address the idea that we should batch multiple consecutive DNS changes into a single reload.
Andres Rodriguez (andreserl) wrote : | # |
We should double check, but I thought Maas already did that (before this
bugfix?)
On Mon, Aug 21, 2017 at 5:36 PM Mike Pontillo <email address hidden>
wrote:
> I've filed bug #1712205 to address the idea that we should batch multiple
> consecutive DNS changes into a single reload.
> --
> https:/
> Your team MAAS Committers is subscribed to branch maas:master.
>
--
Andres Rodriguez
Engineering Manager, MAAS
Canonical USA, Inc.
Mike Pontillo (mpontillo) wrote : | # |
What MAAS already did was to prevent **concurrent outstanding reload requests**. That is, if the "rndc reload" command was already being executed and another DNS change came in, MAAS would (even before this branch) not try to issue *another* reload at the same time. However, the "rndc reload" returns so quickly (as it asynchronously causes BIND to start reloading) that we could still effectively have two concurrent reloads prior to this branch landing.
Mike Pontillo (mpontillo) wrote : | # |
After discussing this again, Blake reminded me that there is an implicit throttle here when we pause for a second to await the DNS query timeout while we check, then pause for two seconds while we wait for the zone to reload.
So this branch *will* implicitly thottle reload requests, unless BIND reloads extremely fast and MAAS checks and confirms the zone before any of the pauses occur.
Felipe Reyes (freyes) wrote : | # |
Mike, thanks for addressing my comments, and specially for filing another bug for the throttling part. I will keep an eye on that one ;)
Best,
Blake Rouse (blake-rouse) wrote : | # |
Felipe,
The throttling bug has been changed, as its not a throttling issue. My branch introduces throttling based on how the DNS service handles the culling of updates.
But I am looking into improving the DNS triggers so less DNS updates are performed.
Felipe Reyes (freyes) wrote : | # |
Yes, I got it ;)
Preview Diff
1 | diff --git a/src/maasserver/dns/config.py b/src/maasserver/dns/config.py | |||
2 | index 855cc47..e27773a 100644 | |||
3 | --- a/src/maasserver/dns/config.py | |||
4 | +++ b/src/maasserver/dns/config.py | |||
5 | @@ -88,6 +88,12 @@ def dns_update_all_zones(reload_retry=False): | |||
6 | 88 | else: | 88 | else: |
7 | 89 | bind_reload() | 89 | bind_reload() |
8 | 90 | 90 | ||
9 | 91 | # Return the current serial and list of domain names. | ||
10 | 92 | return serial, [ | ||
11 | 93 | domain.name | ||
12 | 94 | for domain in domains | ||
13 | 95 | ] | ||
14 | 96 | |||
15 | 91 | 97 | ||
16 | 92 | def get_upstream_dns(): | 98 | def get_upstream_dns(): |
17 | 93 | """Return the IP addresses of configured upstream DNS servers. | 99 | """Return the IP addresses of configured upstream DNS servers. |
18 | diff --git a/src/maasserver/dns/tests/test_config.py b/src/maasserver/dns/tests/test_config.py | |||
19 | index 029e874..c163420 100644 | |||
20 | --- a/src/maasserver/dns/tests/test_config.py | |||
21 | +++ b/src/maasserver/dns/tests/test_config.py | |||
22 | @@ -53,6 +53,7 @@ from testtools.matchers import ( | |||
23 | 53 | Contains, | 53 | Contains, |
24 | 54 | Equals, | 54 | Equals, |
25 | 55 | FileContains, | 55 | FileContains, |
26 | 56 | MatchesSetwise, | ||
27 | 56 | MatchesStructure, | 57 | MatchesStructure, |
28 | 57 | ) | 58 | ) |
29 | 58 | 59 | ||
30 | @@ -302,6 +303,24 @@ class TestDNSConfigModifications(TestDNSServer): | |||
31 | 302 | port=self.bind.config.port, commands=[ns_record, '+short']) | 303 | port=self.bind.config.port, commands=[ns_record, '+short']) |
32 | 303 | self.assertEqual(ip, ip_of_ns_record) | 304 | self.assertEqual(ip, ip_of_ns_record) |
33 | 304 | 305 | ||
34 | 306 | def test_dns_update_all_zones_returns_serial_and_domains(self): | ||
35 | 307 | self.patch(settings, 'DNS_CONNECT', True) | ||
36 | 308 | domain = factory.make_Domain() | ||
37 | 309 | # These domains should not show up. Just to test we create them. | ||
38 | 310 | for _ in range(3): | ||
39 | 311 | factory.make_Domain(authoritative=False) | ||
40 | 312 | node, static = self.create_node_with_static_ip(domain=domain) | ||
41 | 313 | fake_serial = random.randint(1, 1000) | ||
42 | 314 | self.patch( | ||
43 | 315 | dns_config_module, | ||
44 | 316 | "current_zone_serial").return_value = fake_serial | ||
45 | 317 | serial, domains = dns_update_all_zones() | ||
46 | 318 | self.assertThat(serial, Equals(fake_serial)) | ||
47 | 319 | self.assertThat(domains, MatchesSetwise(*[ | ||
48 | 320 | Equals(domain.name) | ||
49 | 321 | for domain in Domain.objects.filter(authoritative=True) | ||
50 | 322 | ])) | ||
51 | 323 | |||
52 | 305 | 324 | ||
53 | 306 | class TestDNSDynamicIPAddresses(TestDNSServer): | 325 | class TestDNSDynamicIPAddresses(TestDNSServer): |
54 | 307 | """Allocated nodes with IP addresses in the dynamic range get a DNS | 326 | """Allocated nodes with IP addresses in the dynamic range get a DNS |
55 | diff --git a/src/maasserver/region_controller.py b/src/maasserver/region_controller.py | |||
56 | index f3a51f3..0f9e181 100644 | |||
57 | --- a/src/maasserver/region_controller.py | |||
58 | +++ b/src/maasserver/region_controller.py | |||
59 | @@ -31,16 +31,25 @@ from provisioningserver.logger import LegacyLogger | |||
60 | 31 | from provisioningserver.utils.twisted import ( | 31 | from provisioningserver.utils.twisted import ( |
61 | 32 | asynchronous, | 32 | asynchronous, |
62 | 33 | FOREVER, | 33 | FOREVER, |
63 | 34 | pause, | ||
64 | 34 | ) | 35 | ) |
65 | 35 | from twisted.application.service import Service | 36 | from twisted.application.service import Service |
66 | 36 | from twisted.internet import reactor | 37 | from twisted.internet import reactor |
68 | 37 | from twisted.internet.defer import DeferredList | 38 | from twisted.internet.defer import ( |
69 | 39 | DeferredList, | ||
70 | 40 | inlineCallbacks, | ||
71 | 41 | ) | ||
72 | 38 | from twisted.internet.task import LoopingCall | 42 | from twisted.internet.task import LoopingCall |
73 | 43 | from twisted.names.client import Resolver | ||
74 | 39 | 44 | ||
75 | 40 | 45 | ||
76 | 41 | log = LegacyLogger() | 46 | log = LegacyLogger() |
77 | 42 | 47 | ||
78 | 43 | 48 | ||
79 | 49 | class DNSReloadError(Exception): | ||
80 | 50 | """Error raised when the bind never fully reloads the zone.""" | ||
81 | 51 | |||
82 | 52 | |||
83 | 44 | class RegionControllerService(Service): | 53 | class RegionControllerService(Service): |
84 | 45 | """ | 54 | """ |
85 | 46 | A service that controllers external services that are in MAAS's control on | 55 | A service that controllers external services that are in MAAS's control on |
86 | @@ -64,6 +73,9 @@ class RegionControllerService(Service): | |||
87 | 64 | self.needsDNSUpdate = False | 73 | self.needsDNSUpdate = False |
88 | 65 | self.needsProxyUpdate = False | 74 | self.needsProxyUpdate = False |
89 | 66 | self.postgresListener = postgresListener | 75 | self.postgresListener = postgresListener |
90 | 76 | self.dnsResolver = Resolver( | ||
91 | 77 | resolv=None, servers=[('127.0.0.1', 53)], | ||
92 | 78 | timeout=(1,), reactor=clock) | ||
93 | 67 | 79 | ||
94 | 68 | @asynchronous(timeout=FOREVER) | 80 | @asynchronous(timeout=FOREVER) |
95 | 69 | def startService(self): | 81 | def startService(self): |
96 | @@ -108,9 +120,12 @@ class RegionControllerService(Service): | |||
97 | 108 | if self.needsDNSUpdate: | 120 | if self.needsDNSUpdate: |
98 | 109 | self.needsDNSUpdate = False | 121 | self.needsDNSUpdate = False |
99 | 110 | d = deferToDatabase(transactional(dns_update_all_zones)) | 122 | d = deferToDatabase(transactional(dns_update_all_zones)) |
100 | 123 | d.addCallback(self._check_serial) | ||
101 | 111 | d.addCallback( | 124 | d.addCallback( |
104 | 112 | lambda _: log.msg( | 125 | lambda domains: log.msg( |
105 | 113 | "Successfully configured DNS.")) | 126 | "Successfully configured DNS " |
106 | 127 | "authoritative zones: %s." % ( | ||
107 | 128 | ', '.join(domains)))) | ||
108 | 114 | d.addErrback( | 129 | d.addErrback( |
109 | 115 | log.err, | 130 | log.err, |
110 | 116 | "Failed configuring DNS.") | 131 | "Failed configuring DNS.") |
111 | @@ -131,3 +146,31 @@ class RegionControllerService(Service): | |||
112 | 131 | self.processingDefer = None | 146 | self.processingDefer = None |
113 | 132 | else: | 147 | else: |
114 | 133 | return DeferredList(defers) | 148 | return DeferredList(defers) |
115 | 149 | |||
116 | 150 | @inlineCallbacks | ||
117 | 151 | def _check_serial(self, result): | ||
118 | 152 | """Check that the serial of the domain is updated.""" | ||
119 | 153 | serial, domain_names = result | ||
120 | 154 | not_matching_domains = set(domain_names) | ||
121 | 155 | loop = 0 | ||
122 | 156 | while len(not_matching_domains) > 0 and loop != 30: | ||
123 | 157 | for domain in list(not_matching_domains): | ||
124 | 158 | try: | ||
125 | 159 | answers, _, _ = yield self.dnsResolver.lookupAuthority( | ||
126 | 160 | domain) | ||
127 | 161 | except (ValueError, TimeoutError): | ||
128 | 162 | answers = [] | ||
129 | 163 | if len(answers) > 0: | ||
130 | 164 | if int(answers[0].payload.serial) == int(serial): | ||
131 | 165 | not_matching_domains.remove(domain) | ||
132 | 166 | loop += 1 | ||
133 | 167 | yield pause(2) | ||
134 | 168 | # 30 retries with 2 second pauses (aka. 60 seconds) has passed and | ||
135 | 169 | # there still is a domain that has the wrong serial. For now just | ||
136 | 170 | # raise the error, in the future we should take action and force | ||
137 | 171 | # restart bind. | ||
138 | 172 | if len(not_matching_domains) > 0: | ||
139 | 173 | raise DNSReloadError( | ||
140 | 174 | "Failed to reload DNS; serial mismatch " | ||
141 | 175 | "on domains %s" % ', '.join(not_matching_domains)) | ||
142 | 176 | return domain_names | ||
143 | diff --git a/src/maasserver/tests/test_region_controller.py b/src/maasserver/tests/test_region_controller.py | |||
144 | index 9dbe4da..a4e83d2 100644 | |||
145 | --- a/src/maasserver/tests/test_region_controller.py | |||
146 | +++ b/src/maasserver/tests/test_region_controller.py | |||
147 | @@ -5,6 +5,7 @@ | |||
148 | 5 | 5 | ||
149 | 6 | __all__ = [] | 6 | __all__ = [] |
150 | 7 | 7 | ||
151 | 8 | import random | ||
152 | 8 | from unittest.mock import ( | 9 | from unittest.mock import ( |
153 | 9 | ANY, | 10 | ANY, |
154 | 10 | call, | 11 | call, |
155 | @@ -14,7 +15,10 @@ from unittest.mock import ( | |||
156 | 14 | 15 | ||
157 | 15 | from crochet import wait_for | 16 | from crochet import wait_for |
158 | 16 | from maasserver import region_controller | 17 | from maasserver import region_controller |
160 | 17 | from maasserver.region_controller import RegionControllerService | 18 | from maasserver.region_controller import ( |
161 | 19 | DNSReloadError, | ||
162 | 20 | RegionControllerService, | ||
163 | 21 | ) | ||
164 | 18 | from maasserver.testing.factory import factory | 22 | from maasserver.testing.factory import factory |
165 | 19 | from maasserver.testing.testcase import MAASServerTestCase | 23 | from maasserver.testing.testcase import MAASServerTestCase |
166 | 20 | from maastesting.matchers import ( | 24 | from maastesting.matchers import ( |
167 | @@ -22,6 +26,7 @@ from maastesting.matchers import ( | |||
168 | 22 | MockCallsMatch, | 26 | MockCallsMatch, |
169 | 23 | MockNotCalled, | 27 | MockNotCalled, |
170 | 24 | ) | 28 | ) |
171 | 29 | from testtools import ExpectedException | ||
172 | 25 | from testtools.matchers import MatchesStructure | 30 | from testtools.matchers import MatchesStructure |
173 | 26 | from twisted.internet import reactor | 31 | from twisted.internet import reactor |
174 | 27 | from twisted.internet.defer import ( | 32 | from twisted.internet.defer import ( |
175 | @@ -29,6 +34,12 @@ from twisted.internet.defer import ( | |||
176 | 29 | inlineCallbacks, | 34 | inlineCallbacks, |
177 | 30 | succeed, | 35 | succeed, |
178 | 31 | ) | 36 | ) |
179 | 37 | from twisted.names.dns import ( | ||
180 | 38 | A, | ||
181 | 39 | Record_SOA, | ||
182 | 40 | RRHeader, | ||
183 | 41 | SOA, | ||
184 | 42 | ) | ||
185 | 32 | 43 | ||
186 | 33 | 44 | ||
187 | 34 | wait_for_reactor = wait_for(30) # 30 seconds. | 45 | wait_for_reactor = wait_for(30) # 30 seconds. |
188 | @@ -166,16 +177,28 @@ class TestRegionControllerService(MAASServerTestCase): | |||
189 | 166 | def test_process_updates_zones(self): | 177 | def test_process_updates_zones(self): |
190 | 167 | service = RegionControllerService(sentinel.listener) | 178 | service = RegionControllerService(sentinel.listener) |
191 | 168 | service.needsDNSUpdate = True | 179 | service.needsDNSUpdate = True |
192 | 180 | dns_result = ( | ||
193 | 181 | random.randint(1, 1000), [ | ||
194 | 182 | factory.make_name('domain') | ||
195 | 183 | for _ in range(3) | ||
196 | 184 | ]) | ||
197 | 169 | mock_dns_update_all_zones = self.patch( | 185 | mock_dns_update_all_zones = self.patch( |
198 | 170 | region_controller, "dns_update_all_zones") | 186 | region_controller, "dns_update_all_zones") |
199 | 187 | mock_dns_update_all_zones.return_value = dns_result | ||
200 | 188 | mock_check_serial = self.patch(service, "_check_serial") | ||
201 | 189 | mock_check_serial.return_value = succeed(dns_result[1]) | ||
202 | 171 | mock_msg = self.patch( | 190 | mock_msg = self.patch( |
203 | 172 | region_controller.log, "msg") | 191 | region_controller.log, "msg") |
204 | 173 | service.startProcessing() | 192 | service.startProcessing() |
205 | 174 | yield service.processingDefer | 193 | yield service.processingDefer |
206 | 175 | self.assertThat(mock_dns_update_all_zones, MockCalledOnceWith()) | 194 | self.assertThat(mock_dns_update_all_zones, MockCalledOnceWith()) |
207 | 195 | self.assertThat(mock_check_serial, MockCalledOnceWith(dns_result)) | ||
208 | 176 | self.assertThat( | 196 | self.assertThat( |
209 | 177 | mock_msg, | 197 | mock_msg, |
211 | 178 | MockCalledOnceWith("Successfully configured DNS.")) | 198 | MockCalledOnceWith( |
212 | 199 | "Successfully configured DNS " | ||
213 | 200 | "authoritative zones: %s." % ( | ||
214 | 201 | ', '.join(dns_result[1])))) | ||
215 | 179 | 202 | ||
216 | 180 | @wait_for_reactor | 203 | @wait_for_reactor |
217 | 181 | @inlineCallbacks | 204 | @inlineCallbacks |
218 | @@ -236,8 +259,16 @@ class TestRegionControllerService(MAASServerTestCase): | |||
219 | 236 | service = RegionControllerService(sentinel.listener) | 259 | service = RegionControllerService(sentinel.listener) |
220 | 237 | service.needsDNSUpdate = True | 260 | service.needsDNSUpdate = True |
221 | 238 | service.needsProxyUpdate = True | 261 | service.needsProxyUpdate = True |
222 | 262 | dns_result = ( | ||
223 | 263 | random.randint(1, 1000), [ | ||
224 | 264 | factory.make_name('domain') | ||
225 | 265 | for _ in range(3) | ||
226 | 266 | ]) | ||
227 | 239 | mock_dns_update_all_zones = self.patch( | 267 | mock_dns_update_all_zones = self.patch( |
228 | 240 | region_controller, "dns_update_all_zones") | 268 | region_controller, "dns_update_all_zones") |
229 | 269 | mock_dns_update_all_zones.return_value = dns_result | ||
230 | 270 | mock_check_serial = self.patch(service, "_check_serial") | ||
231 | 271 | mock_check_serial.return_value = succeed(dns_result[1]) | ||
232 | 241 | mock_proxy_update_config = self.patch( | 272 | mock_proxy_update_config = self.patch( |
233 | 242 | region_controller, "proxy_update_config") | 273 | region_controller, "proxy_update_config") |
234 | 243 | mock_proxy_update_config.return_value = succeed(None) | 274 | mock_proxy_update_config.return_value = succeed(None) |
235 | @@ -245,5 +276,97 @@ class TestRegionControllerService(MAASServerTestCase): | |||
236 | 245 | yield service.processingDefer | 276 | yield service.processingDefer |
237 | 246 | self.assertThat( | 277 | self.assertThat( |
238 | 247 | mock_dns_update_all_zones, MockCalledOnceWith()) | 278 | mock_dns_update_all_zones, MockCalledOnceWith()) |
239 | 279 | self.assertThat(mock_check_serial, MockCalledOnceWith(dns_result)) | ||
240 | 248 | self.assertThat( | 280 | self.assertThat( |
241 | 249 | mock_proxy_update_config, MockCalledOnceWith(reload_proxy=True)) | 281 | mock_proxy_update_config, MockCalledOnceWith(reload_proxy=True)) |
242 | 282 | |||
243 | 283 | def make_soa_result(self, serial): | ||
244 | 284 | return RRHeader( | ||
245 | 285 | type=SOA, cls=A, ttl=30, payload=Record_SOA(serial=serial)) | ||
246 | 286 | |||
247 | 287 | @wait_for_reactor | ||
248 | 288 | def test__check_serial_doesnt_raise_error_on_successful_serial_match(self): | ||
249 | 289 | service = RegionControllerService(sentinel.listener) | ||
250 | 290 | result_serial = random.randint(1, 1000) | ||
251 | 291 | formatted_serial = '{0:10d}'.format(result_serial) | ||
252 | 292 | dns_names = [ | ||
253 | 293 | factory.make_name('domain') | ||
254 | 294 | for _ in range(3) | ||
255 | 295 | ] | ||
256 | 296 | # Mock pause so test runs faster. | ||
257 | 297 | self.patch(region_controller, "pause").return_value = succeed(None) | ||
258 | 298 | mock_lookup = self.patch(service.dnsResolver, "lookupAuthority") | ||
259 | 299 | mock_lookup.side_effect = [ | ||
260 | 300 | # First pass no results. | ||
261 | 301 | succeed(([], [], [])), | ||
262 | 302 | succeed(([], [], [])), | ||
263 | 303 | succeed(([], [], [])), | ||
264 | 304 | # First domain valid result. | ||
265 | 305 | succeed(([self.make_soa_result(result_serial)], [], [])), | ||
266 | 306 | succeed(([], [], [])), | ||
267 | 307 | succeed(([], [], [])), | ||
268 | 308 | # Second domain wrong serial. | ||
269 | 309 | succeed(([self.make_soa_result(result_serial - 1)], [], [])), | ||
270 | 310 | succeed(([], [], [])), | ||
271 | 311 | # Third domain correct serial. | ||
272 | 312 | succeed(([], [], [])), | ||
273 | 313 | succeed(([self.make_soa_result(result_serial)], [], [])), | ||
274 | 314 | # Second domain correct serial. | ||
275 | 315 | succeed(([self.make_soa_result(result_serial)], [], [])), | ||
276 | 316 | ] | ||
277 | 317 | # Error should not be raised. | ||
278 | 318 | return service._check_serial((formatted_serial, dns_names)) | ||
279 | 319 | |||
280 | 320 | @wait_for_reactor | ||
281 | 321 | @inlineCallbacks | ||
282 | 322 | def test__check_serial_raise_error_after_30_tries(self): | ||
283 | 323 | service = RegionControllerService(sentinel.listener) | ||
284 | 324 | result_serial = random.randint(1, 1000) | ||
285 | 325 | formatted_serial = '{0:10d}'.format(result_serial) | ||
286 | 326 | dns_names = [ | ||
287 | 327 | factory.make_name('domain') | ||
288 | 328 | for _ in range(3) | ||
289 | 329 | ] | ||
290 | 330 | # Mock pause so test runs faster. | ||
291 | 331 | self.patch(region_controller, "pause").return_value = succeed(None) | ||
292 | 332 | mock_lookup = self.patch(service.dnsResolver, "lookupAuthority") | ||
293 | 333 | mock_lookup.side_effect = lambda *args: succeed(([], [], [])) | ||
294 | 334 | # Error should not be raised. | ||
295 | 335 | with ExpectedException(DNSReloadError): | ||
296 | 336 | yield service._check_serial((formatted_serial, dns_names)) | ||
297 | 337 | |||
298 | 338 | @wait_for_reactor | ||
299 | 339 | @inlineCallbacks | ||
300 | 340 | def test__check_serial_handles_ValueError(self): | ||
301 | 341 | service = RegionControllerService(sentinel.listener) | ||
302 | 342 | result_serial = random.randint(1, 1000) | ||
303 | 343 | formatted_serial = '{0:10d}'.format(result_serial) | ||
304 | 344 | dns_names = [ | ||
305 | 345 | factory.make_name('domain') | ||
306 | 346 | for _ in range(3) | ||
307 | 347 | ] | ||
308 | 348 | # Mock pause so test runs faster. | ||
309 | 349 | self.patch(region_controller, "pause").return_value = succeed(None) | ||
310 | 350 | mock_lookup = self.patch(service.dnsResolver, "lookupAuthority") | ||
311 | 351 | mock_lookup.side_effect = ValueError() | ||
312 | 352 | # Error should not be raised. | ||
313 | 353 | with ExpectedException(DNSReloadError): | ||
314 | 354 | yield service._check_serial((formatted_serial, dns_names)) | ||
315 | 355 | |||
316 | 356 | @wait_for_reactor | ||
317 | 357 | @inlineCallbacks | ||
318 | 358 | def test__check_serial_handles_TimeoutError(self): | ||
319 | 359 | service = RegionControllerService(sentinel.listener) | ||
320 | 360 | result_serial = random.randint(1, 1000) | ||
321 | 361 | formatted_serial = '{0:10d}'.format(result_serial) | ||
322 | 362 | dns_names = [ | ||
323 | 363 | factory.make_name('domain') | ||
324 | 364 | for _ in range(3) | ||
325 | 365 | ] | ||
326 | 366 | # Mock pause so test runs faster. | ||
327 | 367 | self.patch(region_controller, "pause").return_value = succeed(None) | ||
328 | 368 | mock_lookup = self.patch(service.dnsResolver, "lookupAuthority") | ||
329 | 369 | mock_lookup.side_effect = TimeoutError() | ||
330 | 370 | # Error should not be raised. | ||
331 | 371 | with ExpectedException(DNSReloadError): | ||
332 | 372 | yield service._check_serial((formatted_serial, dns_names)) |
Looks good so far; I have some comments and questions inline below.