1
# Copyright 2014-2015 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Tests for `MAAS_URL` configuration update code."""
6
from __future__ import (
17
from argparse import ArgumentParser
18
from random import randint
19
from textwrap import dedent
21
from maastesting.factory import factory
22
from maastesting.matchers import (
26
from maastesting.testcase import MAASTestCase
31
from provisioningserver import configure_maas_url
32
from provisioningserver.configure_maas_url import substitute_pserv_yaml_line
33
from testtools.matchers import (
39
class TestRewriteConfigFile(MAASTestCase):
41
def test__rewrites_file(self):
42
path = self.make_file(contents='foo\n')
43
configure_maas_url.rewrite_config_file(path, lambda line: 'bar')
44
self.assertThat(path, FileContains('bar\n'))
46
def test__sets_access_permissions(self):
47
writer = self.patch(configure_maas_url, 'atomic_write')
49
path = self.make_file()
50
configure_maas_url.rewrite_config_file(
51
path, lambda line: line, mode=mode)
52
self.assertThat(writer, MockCalledOnceWith(ANY, path, mode=mode))
54
def test__preserves_trailing_newline(self):
55
path = self.make_file(contents='x\n')
56
configure_maas_url.rewrite_config_file(path, lambda line: line)
57
self.assertThat(path, FileContains('x\n'))
59
def test__ensures_trailing_newline(self):
60
path = self.make_file(contents='x')
61
configure_maas_url.rewrite_config_file(path, lambda line: line)
62
self.assertThat(path, FileContains('x\n'))
65
class TestUpdateMAASClusterConf(MAASTestCase):
67
def patch_file(self, content):
68
"""Inject a fake `/etc/maas/maas_cluster.conf`."""
69
path = self.make_file(name='maas_cluster.conf', contents=content)
70
self.patch(configure_maas_url, 'MAAS_CLUSTER_CONF', path)
73
def test__updates_realistic_file(self):
74
config_file = self.patch_file(dedent("""\
76
MAAS_URL="http://10.9.8.7/MAAS"
77
CLUSTER_UUID="5d02950e-6318-8195-ac3e-e6ccb12673c5"
79
configure_maas_url.update_maas_cluster_conf('http://1.2.3.4/MAAS')
82
FileContains(dedent("""\
84
MAAS_URL="http://1.2.3.4/MAAS"
85
CLUSTER_UUID="5d02950e-6318-8195-ac3e-e6ccb12673c5"
88
def test__updates_quoted_value(self):
89
old_url = factory.make_url()
90
new_url = factory.make_url()
91
config_file = self.patch_file('MAAS_URL="%s"\n' % old_url)
92
configure_maas_url.update_maas_cluster_conf(new_url)
95
FileContains('MAAS_URL="%s"\n' % new_url))
97
def test__updates_unquoted_value(self):
98
old_url = factory.make_url()
99
new_url = factory.make_url()
100
config_file = self.patch_file('MAAS_URL=%s\n' % old_url)
101
configure_maas_url.update_maas_cluster_conf(new_url)
104
FileContains('MAAS_URL="%s"\n' % new_url))
106
def test__leaves_other_lines_unchanged(self):
107
old_content = '#MAAS_URL="%s"\n' % factory.make_url()
108
config_file = self.patch_file(old_content)
109
configure_maas_url.update_maas_cluster_conf(factory.make_url())
110
self.assertThat(config_file, FileContains(old_content))
113
class TestExtractHost(MAASTestCase):
115
def test__extracts_hostname(self):
116
host = factory.make_name('host').lower()
117
port = factory.pick_port()
120
configure_maas_url.extract_host('http://%s/path' % host))
123
configure_maas_url.extract_host('http://%s:%d' % (host, port)))
125
def test__extracts_IPv4_address(self):
126
host = factory.make_ipv4_address()
127
port = factory.pick_port()
130
configure_maas_url.extract_host('http://%s' % host))
133
configure_maas_url.extract_host('http://%s:%d' % (host, port)))
135
def test__extracts_IPv6_address(self):
136
host = factory.make_ipv6_address()
137
port = factory.pick_port()
140
configure_maas_url.extract_host('http://[%s]' % host))
143
configure_maas_url.extract_host('http://[%s]:%d' % (host, port)))
145
def test__extracts_IPv6_address_with_zone_index(self):
147
factory.make_ipv6_address() +
149
factory.make_name('zone').lower())
150
port = factory.pick_port()
153
configure_maas_url.extract_host('http://[%s]' % host))
156
configure_maas_url.extract_host('http://[%s]:%d' % (host, port)))
159
class TestSubstitutePservYamlLine(MAASTestCase):
161
def make_generator_line(self, url):
162
return " generator: %s" % url
164
def test__replaces_hostname_generator_URL(self):
165
old_host = factory.make_name('old-host')
166
new_host = factory.make_name('new-host')
167
input_line = self.make_generator_line('http://%s' % old_host)
169
self.make_generator_line('http://%s' % new_host),
170
substitute_pserv_yaml_line(new_host, input_line))
172
def test__replaces_IPv4_generator_URL(self):
173
old_host = factory.make_ipv4_address()
174
new_host = factory.make_name('new-host')
175
input_line = self.make_generator_line('http://%s' % old_host)
177
self.make_generator_line('http://%s' % new_host),
178
substitute_pserv_yaml_line(new_host, input_line))
180
def test__replaces_IPv6_generator_URL(self):
181
old_host = factory.make_ipv6_address()
182
new_host = factory.make_name('new-host')
183
input_line = self.make_generator_line('http://[%s]' % old_host)
185
self.make_generator_line('http://%s' % new_host),
186
substitute_pserv_yaml_line(new_host, input_line))
188
def test__replaces_IPv6_generator_URL_with_zone_index(self):
190
factory.make_ipv6_address() +
192
factory.make_name('zone')
194
new_host = factory.make_name('new-host')
195
input_line = self.make_generator_line('http://[%s]' % old_host)
197
self.make_generator_line('http://%s' % new_host),
198
substitute_pserv_yaml_line(new_host, input_line))
200
def test__inserts_IPv6_with_brackets(self):
201
old_host = factory.make_name('old-host')
202
new_host = '[%s]' % factory.make_ipv6_address()
203
input_line = self.make_generator_line('http://%s' % old_host)
205
self.make_generator_line('http://%s' % new_host),
206
substitute_pserv_yaml_line(new_host, input_line))
208
def test__inserts_IPv6_without_brackets(self):
209
old_host = factory.make_name('old-host')
210
new_host = factory.make_ipv6_address()
211
input_line = self.make_generator_line('http://%s' % old_host)
213
self.make_generator_line('http://[%s]' % new_host),
214
substitute_pserv_yaml_line(new_host, input_line))
216
def test__preserves_port_after_simple_host(self):
217
port = factory.pick_port()
218
old_host = factory.make_name('old-host')
219
new_host = factory.make_name('new-host')
220
input_line = self.make_generator_line(
221
'http://%s:%d' % (old_host, port))
223
self.make_generator_line('http://%s:%d' % (new_host, port)),
224
substitute_pserv_yaml_line(new_host, input_line))
226
def test__preserves_port_with_IPv6(self):
227
port = factory.pick_port()
228
old_host = factory.make_ipv6_address()
229
new_host = factory.make_name('new-host')
230
input_line = self.make_generator_line(
231
'http://[%s]:%d' % (old_host, port))
233
self.make_generator_line('http://%s:%d' % (new_host, port)),
234
substitute_pserv_yaml_line(new_host, input_line))
236
def test__preserves_port_with_IPv6_and_zone_index(self):
237
port = factory.pick_port()
239
factory.make_ipv6_address() +
241
factory.make_name('zone')
243
new_host = factory.make_name('new-host')
244
input_line = self.make_generator_line(
245
'http://[%s]:%d' % (old_host, port))
247
self.make_generator_line('http://%s:%d' % (new_host, port)),
248
substitute_pserv_yaml_line(new_host, input_line))
250
def test__preserves_other_line(self):
251
line = '#' + self.make_generator_line(factory.make_url())
254
substitute_pserv_yaml_line(factory.make_name('host'), line))
256
def test__preserves_indentation(self):
257
spaces = ' ' * randint(0, 10)
258
input_line = spaces + 'generator: %s' % factory.make_url()
259
output_line = substitute_pserv_yaml_line(
260
factory.make_name('host'), input_line)
261
self.assertThat(output_line, StartsWith(spaces + 'generator:'))
263
def test__preserves_trailing_comments(self):
264
comment = " # Trailing comment."
265
old_host = factory.make_name('old-host')
266
new_host = factory.make_name('new-host')
267
input_line = self.make_generator_line('http://%s' % old_host) + comment
269
self.make_generator_line('http://%s' % new_host) + comment,
270
substitute_pserv_yaml_line(new_host, input_line))
273
class TestUpdatePservYaml(MAASTestCase):
275
def patch_file(self, content):
276
"""Inject a fake `/etc/maas/pserv.yaml`."""
277
path = self.make_file(name='pserv.yaml', contents=content)
278
self.patch(configure_maas_url, 'PSERV_YAML', path)
281
def test__updates_realistic_file(self):
282
old_host = factory.make_name('old-host')
283
new_host = factory.make_name('new-host')
284
config_file = self.patch_file(dedent("""\
285
## TFTP configuration.
287
## The URL to be contacted to generate PXE configurations.
288
generator: http://%s/MAAS/api/1.0/pxeconfig/
290
configure_maas_url.update_pserv_yaml(new_host)
293
FileContains(dedent("""\
294
## TFTP configuration.
296
## The URL to be contacted to generate PXE configurations.
297
generator: http://%s/MAAS/api/1.0/pxeconfig/
301
class TestAddArguments(MAASTestCase):
303
def test__accepts_maas_url(self):
304
url = factory.make_url()
305
parser = ArgumentParser()
306
configure_maas_url.add_arguments(parser)
307
args = parser.parse_args([url])
308
self.assertEqual(url, args.maas_url)
311
class TestRun(MAASTestCase):
313
def make_args(self, maas_url):
315
args.maas_url = maas_url
318
def patch_read_file(self):
319
return self.patch(configure_maas_url, 'read_text_file')
321
def patch_write_file(self):
322
return self.patch(configure_maas_url, 'atomic_write')
324
def test__updates_maas_cluster_conf(self):
325
reader = self.patch_read_file()
326
writer = self.patch_write_file()
327
url = factory.make_url()
328
configure_maas_url.run(self.make_args(url))
329
self.assertThat(reader, MockAnyCall('/etc/maas/maas_cluster.conf'))
332
MockAnyCall(ANY, '/etc/maas/maas_cluster.conf', mode=0640))
334
def test__updates_pserv_yaml(self):
335
reader = self.patch_read_file()
336
writer = self.patch_write_file()
337
url = factory.make_url()
338
configure_maas_url.run(self.make_args(url))
339
self.assertThat(reader, MockAnyCall('/etc/maas/pserv.yaml'))
342
MockAnyCall(ANY, '/etc/maas/pserv.yaml', mode=0644))
344
def test__passes_host_to_update_pserv_yaml(self):
345
self.patch_read_file()
346
self.patch_write_file()
347
update_pserv_yaml = self.patch(configure_maas_url, 'update_pserv_yaml')
348
host = factory.make_name('host').lower()
349
url = factory.make_url(netloc=host)
350
configure_maas_url.run(self.make_args(url))
351
self.assertThat(update_pserv_yaml, MockCalledOnceWith(host))