Merge lp:~jtv/maas/bootresources-rewrite-marker into lp:~maas-maintainers/maas/new-import-script-integration

Proposed by Jeroen T. Vermeulen
Status: Merged
Merged at revision: 2174
Proposed branch: lp:~jtv/maas/bootresources-rewrite-marker
Merge into: lp:~maas-maintainers/maas/new-import-script-integration
Diff against target: 184 lines (+102/-3)
5 files modified
etc/maas/bootresources.yaml (+15/-0)
src/provisioningserver/config.py (+6/-0)
src/provisioningserver/tests/test_config.py (+1/-0)
src/provisioningserver/tests/test_upgrade_cluster.py (+56/-2)
src/provisioningserver/upgrade_cluster.py (+24/-1)
To merge this branch: bzr merge lp:~jtv/maas/bootresources-rewrite-marker
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+212584@code.launchpad.net

This proposal supersedes a proposal from 2014-03-25.

Commit message

First step towards writing a bootresources.yaml during cluster upgrade, based on locally available old-style boot images.

Description of the change

Pre-imp was with Julian. I'm keeping this to small steps, because there's going to be quite some diff and I'm messing with production filesystems here.

In this first step, I add a config item to bootresources.yaml: "configure_me" — meaning "please either edit me manually or overwrite me automatically."

The rewrite_boot_resources_config stub will look for old-style boot images on the cluster's filesystem (reviving the old code for list_boot_images as a special-purpose migration helper) and rewrite bootresources.yaml with sources/selections to get similar downloads from the new script.

Our cluster upgrade mechanism requires the upgrade code to be able to tell whether it's supposed to do anything or not. And so, when the configuration is the default (which will be just fine for most users), it needs to figure out whether it is that way because that's how we believe the user wants it, or whether it's simply because nothing has been done about it yet. To that end, I added a configure_me item. After a rewrite, that item will be gone forever.

Testing this led me down some dead ends. The ConfigFixture is not suitable for patching configurations other than pserv.yaml, and attempts to fix it led right down a rabbit hole. And so I'm solving this problem ad hoc, which turns out not to be all that bad.

Jeroen

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) wrote :

Looks good. The usage of the 'configure_me' config item is a bit messy, but I don't see a better way, and at least it's explicit.

[0]

'configure_me' is a bit vague? How about 'auto_configure_legacy' to convey the idea that this is going to be done 'automatically' by MAAS and that this is to cope with legacy installations?

[1]

16 + # If the setting is True, the upgrade procedure will look for downloaded
17 + # boot images that predate the current, Simplestreams-based import
18 + # mechanism, and rewrite the configuration based on what it finds. The new
19 + # configuration will import the new-style equivalents of the images that
20 + # were imported in the old setup.

Maybe mention that, if someone installed fresh on Trusty or up, there is no need to worry about any of this? Ideally, I'd have the packaging code remove that bit for fresh Trusty installations. I think this whole thing can be pretty confusing for users.

Revision history for this message
Raphaël Badin (rvb) :
review: Approve
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

> [0]
>
> 'configure_me' is a bit vague? How about 'auto_configure_legacy' to convey
> the idea that this is going to be done 'automatically' by MAAS and that this
> is to cope with legacy installations?

I had a long and descriptive name originally, but it was too specific: users who know what they're doing may still want to edit the config manually instead. The name says that nobody has touched the file yet, and somebody needs to.

The meaning of configure_me is basically "if True, your config will be rewritten on upgrade." The rewrite removes the configure_me flag, even on a fresh install where there is no migration to be done. It's aggressive, but it's also very simple.

Under normal circumstances, the user never sees any of this. The upgrade hook runs during installation, and it rewrites the config. The rewrite defines sources and selections, but also removes the configure_me flag. So even if the user does need to edit this file, by that time the flag and any mention of it are already gone. It's documented for development, and for debugging of broken installs, not for configuration.

> [1]
>
> 16 + # If the setting is True, the upgrade procedure will look for
> downloaded
> 17 + # boot images that predate the current, Simplestreams-based import
> 18 + # mechanism, and rewrite the configuration based on what it finds.
> The new
> 19 + # configuration will import the new-style equivalents of the images
> that
> 20 + # were imported in the old setup.
>
> Maybe mention that, if someone installed fresh on Trusty or up, there is no
> need to worry about any of this? Ideally, I'd have the packaging code remove
> that bit for fresh Trusty installations. I think this whole thing can be
> pretty confusing for users.

We don't want to put more of this stuff in the packaging, especially if we're also going to be editing the same file from the cluster upgrade hooks. That's just doubling the risks. But yes, the setting is designed to be completely removed from the file during upgrade, not just to have its value changed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'etc/maas/bootresources.yaml'
2--- etc/maas/bootresources.yaml 2014-03-25 03:03:48 +0000
3+++ etc/maas/bootresources.yaml 2014-03-25 11:14:18 +0000
4@@ -3,6 +3,21 @@
5 ##
6
7 boot:
8+ ## Marker: this configuration has not been initialised yet.
9+ #
10+ # If this is set to True during installation or a later upgrade, it means
11+ # that this configuration file has not been edited by a human or by MAAS
12+ # itself. When you edit this configuration to suit your needs, be sure to
13+ # remove this setting, or change it to False, so that a future upgrade will
14+ # know not to overwrite the configuration file.
15+ #
16+ # If the setting is True, the upgrade procedure will look for downloaded
17+ # boot images that predate the current, Simplestreams-based import
18+ # mechanism, and rewrite the configuration based on what it finds. The new
19+ # configuration will import the new-style equivalents of the images that
20+ # were imported in the old setup.
21+ configure_me = True
22+
23 ## Storage location for boot images.
24 #
25 # These images can get quite large: some files are hundreds of megabytes,
26
27=== modified file 'src/provisioningserver/config.py'
28--- src/provisioningserver/config.py 2014-03-24 09:51:27 +0000
29+++ src/provisioningserver/config.py 2014-03-25 11:14:18 +0000
30@@ -70,6 +70,7 @@
31 )
32 from formencode.declarative import DeclarativeMeta
33 from formencode.validators import (
34+ Bool,
35 Int,
36 RequireIfPresent,
37 Set,
38@@ -169,6 +170,11 @@
39 sources = ForEach(
40 ConfigBootSource, if_missing=[ConfigBootSource.to_python({})])
41
42+ # Marker in the bootresources.yaml file: if True, the file has not been
43+ # edited yet and needs to be either configured with initial choices, or
44+ # rewritten based on previously downloaded boot images.
45+ configure_me = Bool(if_missing=False)
46+
47
48 class ConfigMeta(DeclarativeMeta):
49 """Metaclass for the root configuration schema."""
50
51=== modified file 'src/provisioningserver/tests/test_config.py'
52--- src/provisioningserver/tests/test_config.py 2014-03-24 09:34:44 +0000
53+++ src/provisioningserver/tests/test_config.py 2014-03-25 11:14:18 +0000
54@@ -152,6 +152,7 @@
55 },
56 ],
57 'storage': '/var/lib/maas/boot-resources/',
58+ 'configure_me': False,
59 },
60 'broker': {
61 'host': 'localhost',
62
63=== modified file 'src/provisioningserver/tests/test_upgrade_cluster.py'
64--- src/provisioningserver/tests/test_upgrade_cluster.py 2014-03-18 04:25:00 +0000
65+++ src/provisioningserver/tests/test_upgrade_cluster.py 2014-03-25 11:14:18 +0000
66@@ -25,10 +25,17 @@
67 import os.path
68
69 from maastesting.factory import factory
70-from maastesting.matchers import MockCalledOnceWith
71+from maastesting.matchers import (
72+ MockCalledOnceWith,
73+ MockNotCalled,
74+ )
75 from maastesting.testcase import MAASTestCase
76-from mock import Mock
77+from mock import (
78+ ANY,
79+ Mock,
80+ )
81 from provisioningserver import upgrade_cluster
82+from provisioningserver.config import Config
83 from provisioningserver.pxe.install_image import install_image
84 from provisioningserver.testing.config import ConfigFixture
85 from testtools.matchers import (
86@@ -580,3 +587,50 @@
87 entries_before = listdir(tftproot)
88 upgrade_cluster.add_label_directory_level_to_boot_images()
89 self.assertItemsEqual(entries_before, listdir(tftproot))
90+
91+
92+class TestGenerateBootResourcesConfig(MAASTestCase):
93+ """Tests for the `generate_boot_resources_config` upgrade."""
94+
95+ def patch_rewrite_boot_resources_config(self):
96+ """Patch `rewrite_boot_resources_config` with a mock."""
97+ return self.patch(upgrade_cluster, 'rewrite_boot_resources_config')
98+
99+ def patch_config(self, config):
100+ """Patch the `bootresources.yaml` config with a given dict."""
101+ original_load = Config.load_from_cache
102+
103+ @classmethod
104+ def fake_config_load(cls, filename=None):
105+ """Fake `Config.load_from_cache`.
106+
107+ Returns a susbtitute for `bootresources.yaml`, but defers to the
108+ original implementation for other files. This means we can still
109+ patch the original, and it means we'll probably get a tell-tale
110+ error if any code underneath the tests accidentally tries to
111+ load pserv.yaml.
112+ """
113+ if os.path.basename(filename) == 'bootresources.yaml':
114+ return config
115+ else:
116+ return original_load(Config, filename=filename)
117+
118+ self.patch(Config, 'load_from_cache', fake_config_load)
119+
120+ def test_does_nothing_if_configure_me_is_False(self):
121+ self.patch_config({'boot': {'configure_me': False}})
122+ rewrite_config = self.patch_rewrite_boot_resources_config()
123+ upgrade_cluster.generate_boot_resources_config()
124+ self.assertThat(rewrite_config, MockNotCalled())
125+
126+ def test_does_nothing_if_configure_me_is_missing(self):
127+ self.patch_config({'boot': {}})
128+ rewrite_config = self.patch_rewrite_boot_resources_config()
129+ upgrade_cluster.generate_boot_resources_config()
130+ self.assertThat(rewrite_config, MockNotCalled())
131+
132+ def test_rewrites_if_configure_me_is_True(self):
133+ self.patch_config({'boot': {'configure_me': True}})
134+ rewrite_config = self.patch_rewrite_boot_resources_config()
135+ upgrade_cluster.generate_boot_resources_config()
136+ self.assertThat(rewrite_config, MockCalledOnceWith(ANY))
137
138=== modified file 'src/provisioningserver/upgrade_cluster.py'
139--- src/provisioningserver/upgrade_cluster.py 2014-03-18 04:10:37 +0000
140+++ src/provisioningserver/upgrade_cluster.py 2014-03-25 11:14:18 +0000
141@@ -44,7 +44,10 @@
142 from shutil import rmtree
143
144 from provisioningserver.config import Config
145-from provisioningserver.utils import ensure_dir
146+from provisioningserver.utils import (
147+ ensure_dir,
148+ locate_config,
149+ )
150
151
152 logger = getLogger(__name__)
153@@ -246,6 +249,25 @@
154 move_real_boot_image(tftproot, image)
155
156
157+def rewrite_boot_resources_config(config_file):
158+ """Rewrite the `bootresources.yaml` configuration."""
159+
160+
161+def generate_boot_resources_config():
162+ """Upgrade hook: rewrite `bootresources.yaml` based on boot images.
163+
164+ This finds boot images downloaded into the old, pre-Simplestreams tftp
165+ root, and writes a boot-resources configuration to import a similar set of
166+ images using Simplestreams.
167+ """
168+ config_file = locate_config('bootresources.yaml')
169+ boot_resources = Config.load_from_cache(config_file)
170+ if not boot_resources['boot'].get('configure_me', False):
171+ # Already configured.
172+ return
173+ rewrite_boot_resources_config(config_file)
174+
175+
176 # Upgrade hooks, from oldest to newest. The hooks are callables, taking no
177 # arguments. They are called in order.
178 #
179@@ -253,6 +275,7 @@
180 # no record of previous upgrades.
181 UPGRADE_HOOKS = [
182 add_label_directory_level_to_boot_images,
183+ generate_boot_resources_config,
184 ]
185
186

Subscribers

People subscribed via source and target branches