40
class TestConfigFixture(MAASTestCase):
41
"""Tests for `provisioningserver.testing.config.ConfigFixture`."""
47
class ExampleConfig(ConfigBase):
48
"""An example configuration schema.
50
It derives from :class:`ConfigBase` and has a metaclass derived from
51
:class:`ConfigMeta`, just as a "real" schema must.
54
class __metaclass__(ConfigMeta):
55
envvar = "MAAS_TESTING_SETTINGS"
56
default = "example.yaml"
58
something = formencode.validators.String(if_missing="*missing*")
61
class ExampleConfigFixture(ConfigFixtureBase):
62
"""A fixture to help with testing :class:`ExampleConfig`."""
64
schema = ExampleConfig
67
class TestConfigFixtureBase(MAASTestCase):
68
"""Tests for `ConfigFixtureBase`."""
43
70
def exercise_fixture(self, fixture):
44
# ConfigFixture arranges a minimal configuration on disk, and exports
45
# the configuration filename to the environment so that subprocesses
71
# ConfigFixtureBase arranges a minimal configuration on disk,
72
# and exports the configuration filename to the environment so
73
# that subprocesses can find it.
48
75
self.assertThat(fixture.dir, DirExists())
49
76
self.assertThat(fixture.filename, FileExists())
51
{"MAAS_PROVISIONING_SETTINGS": fixture.filename},
78
{fixture.schema.envvar: fixture.filename},
54
fixture.filename, os.environ["MAAS_PROVISIONING_SETTINGS"])
81
fixture.filename, os.environ[fixture.schema.envvar])
55
82
with open(fixture.filename, "rb") as stream:
56
83
self.assertEqual(fixture.config, yaml.safe_load(stream))
58
85
def test_use_minimal(self):
59
# With no arguments, ConfigFixture arranges a minimal configuration.
60
fixture = ConfigFixture()
86
# With no arguments, ConfigFixtureBase arranges a minimal
88
fixture = ExampleConfigFixture()
61
89
self.exercise_fixture(fixture)
63
91
def test_use_with_config(self):
64
# Given a configuration, ConfigFixture can arrange a minimal global
65
# configuration with the additional options merged in.
66
dummy_logfile = factory.make_name("logfile")
67
fixture = ConfigFixture({"logfile": dummy_logfile})
68
self.assertEqual(dummy_logfile, fixture.config["logfile"])
92
# Given a configuration, ConfigFixtureBase can arrange a minimal
93
# global configuration with the additional options merged in.
94
something = self.getUniqueString("something")
95
fixture = ExampleConfigFixture({"something": something})
96
self.assertEqual(something, fixture.config["something"])
69
97
self.exercise_fixture(fixture)
72
class TestConfig_DEFAULT_FILENAME(MAASTestCase):
73
"""Tests for `provisioningserver.config.Config.DEFAULT_FILENAME`."""
100
class TestConfigMeta_DEFAULT_FILENAME(MAASTestCase):
101
"""Tests for `provisioningserver.config.ConfigBase.DEFAULT_FILENAME`."""
75
def set_MAAS_PROVISIONING_SETTINGS(self, filepath=None):
76
"""Continue this test with a given `MAAS_PROVISIONING_SETTINGS`."""
103
def set_envvar(self, filepath=None):
104
"""Continue this test with a given environment variable."""
77
105
self.useFixture(EnvironmentVariableFixture(
78
"MAAS_PROVISIONING_SETTINGS", filepath))
106
ExampleConfig.envvar, filepath))
80
108
def set_MAAS_CONFIG_DIR(self, dirpath=None):
81
109
"""Continue this test with a given `MAAS_CONFIG_DIR`."""
82
110
self.useFixture(EnvironmentVariableFixture("MAAS_CONFIG_DIR", dirpath))
84
def make_config(self, name='pserv.yaml'):
85
"""Create a `pserv.yaml` in a directory of its own."""
86
return self.make_file(name=name)
112
def make_config(self):
113
"""Create a config file in a directory of its own."""
114
return self.make_file(name=ExampleConfig.default)
88
116
def test_gets_filename_from_MAAS_PROVISIONING_SETTNGS(self):
89
117
dummy_filename = factory.make_name("config")
90
118
self.set_MAAS_CONFIG_DIR(None)
91
self.set_MAAS_PROVISIONING_SETTINGS(dummy_filename)
92
self.assertEqual(dummy_filename, Config.DEFAULT_FILENAME)
119
self.set_envvar(dummy_filename)
120
self.assertEqual(dummy_filename, ExampleConfig.DEFAULT_FILENAME)
94
122
def test_falls_back_to_MAAS_CONFIG_DIR(self):
95
123
config_file = self.make_config()
96
124
self.set_MAAS_CONFIG_DIR(os.path.dirname(config_file))
97
self.set_MAAS_PROVISIONING_SETTINGS(None)
98
self.assertEqual(config_file, Config.DEFAULT_FILENAME)
125
self.set_envvar(None)
126
self.assertEqual(config_file, ExampleConfig.DEFAULT_FILENAME)
100
128
def test_MAAS_PROVISIONING_SETTINGS_trumps_MAAS_CONFIG_DIR(self):
101
129
provisioning_settings = factory.make_name("config")
102
130
self.set_MAAS_CONFIG_DIR(os.path.dirname(self.make_config()))
103
self.set_MAAS_PROVISIONING_SETTINGS(provisioning_settings)
104
self.assertEqual(provisioning_settings, Config.DEFAULT_FILENAME)
131
self.set_envvar(provisioning_settings)
133
provisioning_settings,
134
ExampleConfig.DEFAULT_FILENAME)
106
136
def test_defaults_to_global_config(self):
107
137
self.set_MAAS_CONFIG_DIR(None)
108
self.set_MAAS_PROVISIONING_SETTINGS(None)
109
self.assertEqual('/etc/maas/pserv.yaml', Config.DEFAULT_FILENAME)
138
self.set_envvar(None)
140
'/etc/maas/%s' % ExampleConfig.default,
141
ExampleConfig.DEFAULT_FILENAME)
111
143
def test_set(self):
112
144
dummy_filename = factory.make_name("config")
113
Config.DEFAULT_FILENAME = dummy_filename
114
self.assertEqual(dummy_filename, Config.DEFAULT_FILENAME)
145
ExampleConfig.DEFAULT_FILENAME = dummy_filename
146
self.assertEqual(dummy_filename, ExampleConfig.DEFAULT_FILENAME)
116
148
def test_delete(self):
117
149
self.set_MAAS_CONFIG_DIR(None)
118
self.set_MAAS_PROVISIONING_SETTINGS(None)
119
Config.DEFAULT_FILENAME = factory.make_name("config")
120
del Config.DEFAULT_FILENAME
150
self.set_envvar(None)
151
ExampleConfig.DEFAULT_FILENAME = factory.make_name("config")
152
del ExampleConfig.DEFAULT_FILENAME
121
153
# The filename reverts; see test_get_with_environment_empty.
122
self.assertEqual("/etc/maas/pserv.yaml", Config.DEFAULT_FILENAME)
155
"/etc/maas/%s" % ExampleConfig.default,
156
ExampleConfig.DEFAULT_FILENAME)
123
157
# The delete does not fail when called multiple times.
124
del Config.DEFAULT_FILENAME
158
del ExampleConfig.DEFAULT_FILENAME
161
class TestConfigBase(MAASTestCase):
162
"""Tests for `provisioningserver.config.ConfigBase`."""
164
def make_config_data(self):
165
"""Return random config data for `ExampleConfig`."""
166
return {'something': factory.make_name('value')}
168
def make_config_file(self, name=None, data=None):
169
"""Write a YAML config file, and return its path."""
171
name = factory.make_name('config') + '.yaml'
173
data = self.make_config_data()
174
return self.make_file(name=name, contents=yaml.safe_dump(data))
176
def test_get_defaults_returns_default_config(self):
177
# The default configuration is production-ready.
178
observed = ExampleConfig.get_defaults()
179
self.assertEqual({"something": "*missing*"}, observed)
181
def test_get_defaults_ignores_settings(self):
182
self.useFixture(ExampleConfigFixture(self.make_config_data()))
183
observed = ExampleConfig.get_defaults()
184
self.assertEqual({"something": "*missing*"}, observed)
186
def test_parse(self):
187
# Configuration can be parsed from a snippet of YAML.
188
observed = ExampleConfig.parse(b'something: "important"\n')
189
self.assertEqual("important", observed["something"])
192
# Configuration can be loaded and parsed from a file.
194
something: "important"
196
filename = self.make_file(contents=config)
197
observed = ExampleConfig.load(filename)
198
self.assertEqual({"something": "important"}, observed)
200
def test_load_defaults_to_default_filename(self):
201
data = self.make_config_data()
202
filename = self.make_config_file(name='config.yaml', data=data)
203
self.patch(ExampleConfig, 'DEFAULT_FILENAME', filename)
204
self.assertEqual(data, ExampleConfig.load())
206
def test_load_from_cache_loads_config(self):
207
data = self.make_config_data()
208
filename = self.make_config_file(data=data)
209
self.assertEqual(data, ExampleConfig.load_from_cache(filename))
211
def test_load_from_cache_uses_defaults(self):
212
filename = self.make_file(contents='')
214
ExampleConfig.get_defaults(),
215
ExampleConfig.load_from_cache(filename))
217
def test_load_from_cache_caches_each_file_separately(self):
218
config1 = self.make_file(contents=yaml.safe_dump({'something': "1"}))
219
config2 = self.make_file(contents=yaml.safe_dump({'something': "2"}))
223
ExampleConfig.load_from_cache(config1))
226
ExampleConfig.load_from_cache(config2))
228
def test_load_from_cache_reloads_from_cache_not_from_file(self):
229
# A config loaded by Config.load_from_cache() is never reloaded.
230
filename = self.make_config_file()
231
config_before = ExampleConfig.load_from_cache(filename)
233
config_after = ExampleConfig.load_from_cache(filename)
234
self.assertEqual(config_before, config_after)
236
def test_load_from_cache_caches_immutable_copy(self):
237
filename = self.make_config_file()
239
first_load = ExampleConfig.load_from_cache(filename)
240
second_load = ExampleConfig.load_from_cache(filename)
242
self.assertEqual(first_load, second_load)
243
self.assertIsNot(first_load, second_load)
244
first_load['something'] = factory.make_name('newthing')
245
self.assertNotEqual(first_load['something'], second_load['something'])
247
def test_flush_cache_without_filename_empties_cache(self):
248
filename = self.make_config_file()
249
ExampleConfig.load_from_cache(filename)
251
ExampleConfig.flush_cache()
252
error = self.assertRaises(
254
ExampleConfig.load_from_cache, filename)
255
self.assertEqual(errno.ENOENT, error.errno)
257
def test_flush_cache_flushes_specific_file(self):
258
filename = self.make_config_file()
259
ExampleConfig.load_from_cache(filename)
261
ExampleConfig.flush_cache(filename)
262
error = self.assertRaises(
264
ExampleConfig.load_from_cache, filename)
265
self.assertEqual(errno.ENOENT, error.errno)
267
def test_flush_cache_retains_other_files(self):
268
flushed_file = self.make_config_file()
269
cached_file = self.make_config_file()
270
ExampleConfig.load_from_cache(flushed_file)
271
cached_config = ExampleConfig.load_from_cache(cached_file)
272
os.unlink(cached_file)
273
ExampleConfig.flush_cache(flushed_file)
276
ExampleConfig.load_from_cache(cached_file))
278
def test_flush_cache_ignores_uncached_files(self):
279
data = self.make_config_data()
280
filename = self.make_config_file(data=data)
281
ExampleConfig.flush_cache(filename)
282
self.assertEqual(data, ExampleConfig.load_from_cache(filename))
284
def test_field(self):
285
self.assertIs(ExampleConfig, ExampleConfig.field())
287
ExampleConfig.fields["something"],
288
ExampleConfig.field("something"))
290
def test_save_and_load_interoperate(self):
291
something = self.getUniqueString()
292
saved_file = self.make_file()
294
ExampleConfig.save({'something': something}, saved_file)
295
loaded_config = ExampleConfig.load(saved_file)
296
self.assertEqual(something, loaded_config['something'])
298
def test_save_saves_yaml_file(self):
299
config = {'something': self.getUniqueString()}
300
saved_file = self.make_file()
302
ExampleConfig.save(config, saved_file)
304
with open(saved_file, 'rb') as written_file:
305
loaded_config = yaml.safe_load(written_file)
306
self.assertEqual(config, loaded_config)
308
def test_save_defaults_to_default_filename(self):
309
something = self.getUniqueString()
310
filename = self.make_file(name="config.yaml")
311
self.patch(ExampleConfig, 'DEFAULT_FILENAME', filename)
313
ExampleConfig.save({'something': something})
316
{'something': something},
317
ExampleConfig.load(filename))
319
def test_create_backup_creates_backup(self):
320
something = self.getUniqueString()
321
filename = self.make_file(name="config.yaml")
322
config = {'something': something}
323
yaml_config = yaml.safe_dump(config)
324
self.patch(ExampleConfig, 'DEFAULT_FILENAME', filename)
325
ExampleConfig.save(config)
327
ExampleConfig.create_backup('test')
329
backup_name = "%s.%s.bak" % (filename, 'test')
330
self.assertThat(backup_name, FileContains(yaml_config))
127
333
class TestConfig(MAASTestCase):
128
334
"""Tests for `provisioningserver.config.Config`."""
130
336
default_production_config = {
132
'architectures': None,
134
'images_directory': '/var/lib/maas/ephemeral',
139
338
'host': 'localhost',
265
387
partial(Config.parse, config),
266
388
Raises(expected))
268
def test_field(self):
269
self.assertIs(Config, Config.field())
270
self.assertIs(Config.fields["tftp"], Config.field("tftp"))
272
Config.fields["tftp"].fields["root"],
273
Config.field("tftp", "root"))
275
def test_save_and_load_interoperate(self):
276
logfile = self.make_file(name='test.log')
277
saved_file = self.make_file()
279
Config.save({'logfile': logfile}, saved_file)
280
loaded_config = Config.load(saved_file)
281
self.assertEqual(logfile, loaded_config['logfile'])
283
def test_save_saves_yaml_file(self):
284
config = {'logfile': self.make_file()}
285
saved_file = self.make_file()
287
Config.save(config, saved_file)
289
with open(saved_file, 'rb') as written_file:
290
loaded_config = yaml.load(written_file)
291
self.assertEqual(config, loaded_config)
293
def test_save_defaults_to_default_filename(self):
294
logfile = self.make_file(name='test.log')
295
filename = self.make_file(name="config.yaml")
296
self.patch(Config, 'DEFAULT_FILENAME', filename)
298
Config.save({'logfile': logfile})
300
self.assertEqual(logfile, Config.load(filename)['logfile'])
302
def test_create_backup_creates_backup(self):
303
logfile = self.make_file(name='test.log')
304
filename = self.make_file(name="config.yaml")
305
config = {'logfile': logfile}
306
yaml_config = yaml.safe_dump(config)
307
self.patch(Config, 'DEFAULT_FILENAME', filename)
310
Config.create_backup('test')
312
backup_name = "%s.%s.bak" % (filename, 'test')
313
self.assertThat(backup_name, FileContains(yaml_config))
391
class TestBootConfig(MAASTestCase):
392
"""Tests for `provisioningserver.config.BootConfig`."""
394
default_production_config = {
399
'http://maas.ubuntu.com/images/ephemeral/releases/'),
401
'/usr/share/keyrings/ubuntu-cloudimage-keyring.gpg'),
412
'storage': '/var/lib/maas/boot-resources/',
413
'configure_me': False,
417
default_development_config = {
422
'http://maas.ubuntu.com/images/ephemeral-v2/daily/'),
424
'/usr/share/keyrings/ubuntu-cloudimage-keyring.gpg'),
427
'arches': ['i386', 'amd64'],
429
'subarches': ['generic'],
436
'http://maas.ubuntu.com/'
437
'images/ephemeral-v2/releases/'),
439
'/usr/share/keyrings/ubuntu-cloudimage-keyring.gpg'),
442
'arches': ['i386', 'amd64'],
443
'release': 'precise',
444
'subarches': ['generic'],
445
'labels': ['release'],
450
'storage': '/var/lib/maas/boot-resources/',
451
'configure_me': True,
455
def test_get_defaults_returns_default_config(self):
456
# The default configuration is production-ready.
457
observed = BootConfig.get_defaults()
458
self.assertEqual(self.default_production_config, observed)
460
def test_load_example(self):
461
# The example configuration is designed for development.
462
filename = os.path.join(root, "etc", "maas", "bootresources.yaml")
464
self.default_development_config,
465
BootConfig.load(filename))