~lutostag/ubuntu/trusty/maas/1.5.2+packagefix

« back to all changes in this revision

Viewing changes to src/provisioningserver/tests/test_upgrade_cluster.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2014-03-28 10:43:53 UTC
  • mto: This revision was merged to the branch mainline in revision 57.
  • Revision ID: package-import@ubuntu.com-20140328104353-ekpolg0pm5xnvq2s
Tags: upstream-1.5+bzr2204
ImportĀ upstreamĀ versionĀ 1.5+bzr2204

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2014 Canonical Ltd.  This software is licensed under the
 
2
# GNU Affero General Public License version 3 (see the file LICENSE).
 
3
 
 
4
"""Tests for the `upgrade-cluster` command."""
 
5
 
 
6
from __future__ import (
 
7
    absolute_import,
 
8
    print_function,
 
9
    unicode_literals,
 
10
    )
 
11
 
 
12
str = None
 
13
 
 
14
__metaclass__ = type
 
15
__all__ = []
 
16
 
 
17
from argparse import ArgumentParser
 
18
from os import makedirs
 
19
import os.path
 
20
from textwrap import dedent
 
21
 
 
22
from maastesting.factory import factory
 
23
from maastesting.matchers import (
 
24
    MockCalledOnceWith,
 
25
    MockNotCalled,
 
26
    )
 
27
from maastesting.testcase import MAASTestCase
 
28
from mock import Mock
 
29
from provisioningserver import upgrade_cluster
 
30
from provisioningserver.config import BootConfig
 
31
from provisioningserver.testing.config import (
 
32
    BootConfigFixture,
 
33
    ConfigFixture,
 
34
    )
 
35
from testtools.matchers import (
 
36
    FileContains,
 
37
    StartsWith,
 
38
    )
 
39
 
 
40
 
 
41
class TestUpgradeCluster(MAASTestCase):
 
42
    """Tests for the `upgrade-cluster` command itself."""
 
43
 
 
44
    def run_command(self):
 
45
        parser = ArgumentParser()
 
46
        upgrade_cluster.add_arguments(parser)
 
47
        upgrade_cluster.run(parser.parse_args(()))
 
48
 
 
49
    def patch_upgrade_hooks(self, hooks=None):
 
50
        """Temporarily replace the upgrade hooks."""
 
51
        if hooks is None:
 
52
            hooks = []
 
53
        self.patch(upgrade_cluster, 'UPGRADE_HOOKS', hooks)
 
54
 
 
55
    def test_calls_hooks(self):
 
56
        upgrade_hook = Mock()
 
57
        self.patch_upgrade_hooks([upgrade_hook])
 
58
        self.run_command()
 
59
        self.assertThat(upgrade_hook, MockCalledOnceWith())
 
60
 
 
61
    def test_calls_hooks_in_order(self):
 
62
        calls = []
 
63
 
 
64
        # Define some hooks.  They will be run in the order in which they are
 
65
        # listed (not in the order in which they are defined, or alphabetical
 
66
        # order, or any other order).
 
67
 
 
68
        def last_hook():
 
69
            calls.append('last')
 
70
 
 
71
        def first_hook():
 
72
            calls.append('first')
 
73
 
 
74
        def middle_hook():
 
75
            calls.append('middle')
 
76
 
 
77
        self.patch_upgrade_hooks([first_hook, middle_hook, last_hook])
 
78
        self.run_command()
 
79
        self.assertEqual(['first', 'middle', 'last'], calls)
 
80
 
 
81
 
 
82
class TestGenerateBootResourcesConfig(MAASTestCase):
 
83
    """Tests for the `generate_boot_resources_config` upgrade."""
 
84
 
 
85
    def patch_rewrite_boot_resources_config(self):
 
86
        """Patch `rewrite_boot_resources_config` with a mock."""
 
87
        return self.patch(upgrade_cluster, 'rewrite_boot_resources_config')
 
88
 
 
89
    def patch_boot_config(self, config):
 
90
        """Replace the bootresources config with the given fake."""
 
91
        fixture = BootConfigFixture(config)
 
92
        self.useFixture(fixture)
 
93
        path = fixture.filename
 
94
        self.patch(upgrade_cluster, 'locate_config').return_value = path
 
95
        return path
 
96
 
 
97
    def test_hook_does_nothing_if_configure_me_is_False(self):
 
98
        self.patch_boot_config({'boot': {'configure_me': False}})
 
99
        rewrite_config = self.patch_rewrite_boot_resources_config()
 
100
        upgrade_cluster.generate_boot_resources_config()
 
101
        self.assertThat(rewrite_config, MockNotCalled())
 
102
 
 
103
    def test_hook_does_nothing_if_configure_me_is_missing(self):
 
104
        self.patch_boot_config({'boot': {}})
 
105
        rewrite_config = self.patch_rewrite_boot_resources_config()
 
106
        upgrade_cluster.generate_boot_resources_config()
 
107
        self.assertThat(rewrite_config, MockNotCalled())
 
108
 
 
109
    def test_hook_rewrites_if_configure_me_is_True(self):
 
110
        config_file = self.patch_boot_config({'boot': {'configure_me': True}})
 
111
        rewrite_config = self.patch_rewrite_boot_resources_config()
 
112
        upgrade_cluster.generate_boot_resources_config()
 
113
        self.assertThat(rewrite_config, MockCalledOnceWith(config_file))
 
114
 
 
115
    def test_find_old_imports_returns_empty_if_no_tftproot(self):
 
116
        non_dir = os.path.join(self.make_dir(), factory.make_name('nonesuch'))
 
117
        self.assertEqual(set(), upgrade_cluster.find_old_imports(non_dir))
 
118
 
 
119
    def test_find_old_imports_returns_empty_if_tftproot_is_empty(self):
 
120
        self.assertEqual(
 
121
            set(),
 
122
            upgrade_cluster.find_old_imports(self.make_dir()))
 
123
 
 
124
    def test_find_old_imports_finds_image(self):
 
125
        tftproot = self.make_dir()
 
126
        arch = factory.make_name('arch')
 
127
        subarch = factory.make_name('subarch')
 
128
        release = factory.make_name('release')
 
129
        purpose = factory.make_name('purpose')
 
130
        makedirs(os.path.join(tftproot, arch, subarch, release, purpose))
 
131
        self.assertEqual(
 
132
            {(arch, subarch, release)},
 
133
            upgrade_cluster.find_old_imports(tftproot))
 
134
 
 
135
    def test_generate_selections_returns_None_if_no_images_found(self):
 
136
        self.assertIsNone(upgrade_cluster.generate_selections([]))
 
137
 
 
138
    def test_generate_selections_matches_image(self):
 
139
        arch = factory.make_name('arch')
 
140
        subarch = factory.make_name('subarch')
 
141
        release = factory.make_name('release')
 
142
        self.assertEqual(
 
143
            [
 
144
                {
 
145
                    'release': release,
 
146
                    'arches': [arch],
 
147
                    'subarches': [subarch],
 
148
                },
 
149
            ],
 
150
            upgrade_cluster.generate_selections([(arch, subarch, release)]))
 
151
 
 
152
    def test_generate_selections_sorts_output(self):
 
153
        images = [
 
154
            (
 
155
                factory.make_name('arch'),
 
156
                factory.make_name('subarch'),
 
157
                factory.make_name('release'),
 
158
            )
 
159
            for _ in range(3)
 
160
            ]
 
161
        self.assertEqual(
 
162
            upgrade_cluster.generate_selections(sorted(images)),
 
163
            upgrade_cluster.generate_selections(sorted(images, reverse=True)))
 
164
 
 
165
    def test_generate_updated_config_clears_configure_me_if_no_images(self):
 
166
        config = {'boot': {'configure_me': True, 'sources': []}}
 
167
        self.assertNotIn(
 
168
            'configure_me',
 
169
            upgrade_cluster.generate_updated_config(config, None)['boot'])
 
170
 
 
171
    def test_generate_updated_config_clears_configure_me_if_has_images(self):
 
172
        image = (
 
173
            factory.make_name('arch'),
 
174
            factory.make_name('subarch'),
 
175
            factory.make_name('release'),
 
176
            )
 
177
        config = {'boot': {'configure_me': True, 'sources': []}}
 
178
        self.assertNotIn(
 
179
            'configure_me',
 
180
            upgrade_cluster.generate_updated_config(config, [image])['boot'])
 
181
 
 
182
    def test_generate_updated_config_leaves_static_entries_intact(self):
 
183
        storage = factory.make_name('storage')
 
184
        path = factory.make_name('path')
 
185
        keyring = factory.make_name('keyring')
 
186
        config = {
 
187
            'boot': {
 
188
                'configure_me': True,
 
189
                'storage': storage,
 
190
                'sources': [
 
191
                    {
 
192
                        'path': path,
 
193
                        'keyring': keyring,
 
194
                    },
 
195
                    ],
 
196
                },
 
197
            }
 
198
        # Set configure_me; generate_updated_config expects it.
 
199
        config['boot']['configure_me'] = True
 
200
 
 
201
        result = upgrade_cluster.generate_updated_config(config, [])
 
202
        self.assertEqual(storage, result['boot']['storage'])
 
203
        self.assertEqual(path, result['boot']['sources'][0]['path'])
 
204
        self.assertEqual(keyring, result['boot']['sources'][0]['keyring'])
 
205
 
 
206
    def test_generate_updated_config_updates_sources(self):
 
207
        arch = factory.make_name('arch')
 
208
        subarch = factory.make_name('subarch')
 
209
        release = factory.make_name('release')
 
210
        path1 = factory.make_name('path')
 
211
        path2 = factory.make_name('path')
 
212
        config = {
 
213
            'boot': {
 
214
                'configure_me': True,
 
215
                # There are two sources.  Both will have their selections set.
 
216
                'sources': [
 
217
                    {'path': path1},
 
218
                    {'path': path2}
 
219
                    ],
 
220
                },
 
221
            }
 
222
        result = upgrade_cluster.generate_updated_config(
 
223
            config, [(arch, subarch, release)])
 
224
        self.assertEqual(
 
225
            [
 
226
                {
 
227
                    'path': path1,
 
228
                    'selections': [
 
229
                        {
 
230
                            'release': release,
 
231
                            'arches': [arch],
 
232
                            'subarches': [subarch],
 
233
                        },
 
234
                        ],
 
235
                },
 
236
                {
 
237
                    'path': path2,
 
238
                    'selections': [
 
239
                        {
 
240
                            'release': release,
 
241
                            'arches': [arch],
 
242
                            'subarches': [subarch],
 
243
                        },
 
244
                        ],
 
245
                },
 
246
            ],
 
247
            result['boot']['sources'])
 
248
 
 
249
    def test_generate_updated_config_does_not_touch_sources_if_no_images(self):
 
250
        path = factory.make_name('path')
 
251
        arches = [factory.make_name('arch') for _ in range(2)]
 
252
        config = {
 
253
            'boot': {
 
254
                'configure_me': True,
 
255
                'sources': [
 
256
                    {
 
257
                        'path': path,
 
258
                        'selections': [{'arches': arches}],
 
259
                    },
 
260
                    ],
 
261
                },
 
262
            }
 
263
        no_images = set()
 
264
        result = upgrade_cluster.generate_updated_config(config, no_images)
 
265
        self.assertEqual(
 
266
            [
 
267
                {
 
268
                    'path': path,
 
269
                    'selections': [{'arches': arches}],
 
270
                },
 
271
            ],
 
272
            result['boot']['sources'])
 
273
 
 
274
    def test_extract_top_comment_reads_up_to_first_non_comment_text(self):
 
275
        header = dedent("""\
 
276
            # Comment.
 
277
 
 
278
            # Comment after blank line.
 
279
                  # Indented comment.
 
280
            """)
 
281
        filename = self.make_file(contents=(header + 'text#'))
 
282
        self.assertEqual(header, upgrade_cluster.extract_top_comment(filename))
 
283
 
 
284
    def test_update_config_file_rewrites_file_in_place(self):
 
285
        old_storage = factory.make_name('old')
 
286
        new_storage = factory.make_name('new')
 
287
        original_file = dedent("""\
 
288
            # Top comment.
 
289
            boot:
 
290
              configure_me: True
 
291
              storage: %s
 
292
            """) % old_storage
 
293
        expected_file = dedent("""\
 
294
            # Top comment.
 
295
            boot:
 
296
              storage: %s
 
297
            """) % new_storage
 
298
        config_file = self.make_file(contents=original_file)
 
299
 
 
300
        upgrade_cluster.update_config_file(
 
301
            config_file, {'boot': {'storage': new_storage}})
 
302
 
 
303
        self.assertThat(config_file, FileContains(expected_file))
 
304
 
 
305
    def test_update_config_file_flushes_config_cache(self):
 
306
        self.patch(BootConfig, 'flush_cache')
 
307
        config_file = self.make_file()
 
308
        upgrade_cluster.update_config_file(config_file, {})
 
309
        self.assertThat(
 
310
            BootConfig.flush_cache, MockCalledOnceWith(config_file))
 
311
 
 
312
    def test_rewrite_boot_resources_config_integrates(self):
 
313
        tftproot = self.make_dir()
 
314
        # Fake pre-existing images in a pre-Simplestreams TFTP directory tree.
 
315
        self.useFixture(ConfigFixture({'tftp': {'root': tftproot}}))
 
316
        arch = factory.make_name('arch')
 
317
        subarch = factory.make_name('subarch')
 
318
        release = factory.make_name('release')
 
319
        purpose = factory.make_name('purpose')
 
320
        makedirs(os.path.join(tftproot, arch, subarch, release, purpose))
 
321
 
 
322
        config_file = self.make_file(contents=dedent("""\
 
323
            # Boot resources configuration file.
 
324
            #
 
325
            # Configuration follows.
 
326
 
 
327
            boot:
 
328
              # This setting will be removed during rewrite.
 
329
              configure_me: True
 
330
 
 
331
              storage: "/var/lib/maas/boot-resources/"
 
332
 
 
333
              sources:
 
334
                - path: "http://maas.ubuntu.com/images/somewhere"
 
335
                  keyring: "/usr/share/keyrings/ubuntu-cloudimage-keyring.gpg"
 
336
 
 
337
                  selections:
 
338
                  - release: "trusty"
 
339
            """))
 
340
 
 
341
        upgrade_cluster.rewrite_boot_resources_config(config_file)
 
342
 
 
343
        self.assertThat(
 
344
            config_file,
 
345
            FileContains(matcher=StartsWith(dedent("""\
 
346
                # Boot resources configuration file.
 
347
                #
 
348
                # Configuration follows.
 
349
 
 
350
                boot:
 
351
                """))))