Merge lp:~jtv/maas/curtin-preseed-helpers into lp:~maas-committers/maas/trunk

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: no longer in the source branch.
Merged at revision: 3118
Proposed branch: lp:~jtv/maas/curtin-preseed-helpers
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 156 lines (+147/-0)
2 files modified
src/provisioningserver/utils/curtin.py (+51/-0)
src/provisioningserver/utils/tests/test_curtin.py (+96/-0)
To merge this branch: bzr merge lp:~jtv/maas/curtin-preseed-helpers
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+236275@code.launchpad.net

Commit message

Two helpers that I need for generating Curtin preseeds: compose_write_text_file and compose_mv_command.

Description of the change

Part of the work for writing a node's network configuration on the server side.

Jeroen

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

I don't really know how compose_write_text_file() is going to be used, but it looks sane, as do the tests :)

review: Approve
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

I should have covered the basics better. This data is for passing into Curtin as a Curtin preseed — not to be confused with a cloud-init preseed, which is incredibly similar but subtly different. Most of the documentation I have is examples, so for the file permissions I just stuck to a format I knew worked.

Thanks for the review.

Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (19.1 KiB)

The attempt to merge lp:~jtv/maas/curtin-preseed-helpers into lp:maas failed. Below is the output from the failed tests.

Ign http://security.ubuntu.com trusty-security InRelease
Hit http://security.ubuntu.com trusty-security Release.gpg
Hit http://security.ubuntu.com trusty-security Release
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Ign http://nova.clouds.archive.ubuntu.com trusty-updates InRelease
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Hit http://nova.clouds.archive.ubuntu.com trusty-updates Release.gpg
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Hit http://nova.clouds.archive.ubuntu.com trusty-updates Release
Hit http://security.ubuntu.com trusty-security/main Sources
Hit http://security.ubuntu.com trusty-security/universe Sources
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en
Ign http://nova.clouds.archive.ubuntu.com trusty/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en_US
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
     --no-install-recommends install apache2 authbind bind9 bind9utils build-essential bzr-builddeb curl daemontools debhelper dh-apport distro-info dnsutils firefox freeipmi-tools ipython isc-dhcp-common libjs-raphael libjs-yui3-full libjs-yui3-min libpq-dev make pep8 postgresql pyflakes python-amqplib python-bzrlib python-celery python-convoy python-crochet python-cssselect python-curtin python-dev python-distro-info python-django python-django-piston python-django-south python-djorm-ext-pgarray python-docutils python-extras python-fixtures python-flake8 python-formencode python-hivex python-httplib2 python-jinja2 python-jsonschema python-lockfile python-lxml python-mimeparse python-mock python-netaddr python-netifaces python-nose python-oauth python-oops python-oops-amqp python-oops-datedir-repo python-oops-twisted python-oops-wsgi python-openssl python-paramiko python-pexpect python-pip python-pocket-lint python-psycopg2 python-pyinotify python-seamicroclient python-simplejson python-simplestreams python-sphinx pyth...

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Apparently unrelated failure: timeout waiting for BIND to start.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/provisioningserver/utils/curtin.py'
2--- src/provisioningserver/utils/curtin.py 1970-01-01 00:00:00 +0000
3+++ src/provisioningserver/utils/curtin.py 2014-09-29 02:51:51 +0000
4@@ -0,0 +1,51 @@
5+# Copyright 2014 Canonical Ltd. This software is licensed under the
6+# GNU Affero General Public License version 3 (see the file LICENSE).
7+
8+"""Utilities related to Curtin."""
9+
10+from __future__ import (
11+ absolute_import,
12+ print_function,
13+ unicode_literals,
14+ )
15+
16+str = None
17+
18+__metaclass__ = type
19+__all__ = [
20+ 'compose_mv_command',
21+ 'compose_write_text_file',
22+ ]
23+
24+
25+def compose_write_text_file(path, content, owner='root:root',
26+ permissions=0600):
27+ """Return preseed for uploading a text file to the install target.
28+
29+ Use this to write files into the filesystem that Curtin is installing. The
30+ result goes into a `write_files` preseed entry.
31+ """
32+ return {
33+ 'path': path,
34+ 'content': content,
35+ 'owner': owner,
36+ 'permissions': '0%o' % permissions,
37+ }
38+
39+
40+def compose_mv_command(source, dest):
41+ """Return preseed for running the `mv` command in the install target.
42+
43+ Use this for moving files around in the filesystem that Curtin is
44+ installing. The result goes in a preseed entry for running commands, such
45+ as an entry in `late_commands` dict.
46+ """
47+ return [
48+ 'curtin',
49+ 'in-target',
50+ '--',
51+ 'mv',
52+ '--',
53+ source,
54+ dest,
55+ ]
56
57=== added file 'src/provisioningserver/utils/tests/test_curtin.py'
58--- src/provisioningserver/utils/tests/test_curtin.py 1970-01-01 00:00:00 +0000
59+++ src/provisioningserver/utils/tests/test_curtin.py 2014-09-29 02:51:51 +0000
60@@ -0,0 +1,96 @@
61+# Copyright 2014 Canonical Ltd. This software is licensed under the
62+# GNU Affero General Public License version 3 (see the file LICENSE).
63+
64+"""Tests for Curtin-related utilities."""
65+
66+from __future__ import (
67+ absolute_import,
68+ print_function,
69+ unicode_literals,
70+ )
71+
72+str = None
73+
74+__metaclass__ = type
75+__all__ = []
76+
77+from maastesting.factory import factory
78+from maastesting.testcase import MAASTestCase
79+from provisioningserver.utils.curtin import (
80+ compose_mv_command,
81+ compose_write_text_file,
82+ )
83+from testtools.matchers import (
84+ AllMatch,
85+ ContainsAll,
86+ IsInstance,
87+ )
88+
89+
90+class TestComposeMvCommand(MAASTestCase):
91+
92+ def test__returns_command_list(self):
93+ command = compose_mv_command(
94+ factory.make_name('source'), factory.make_name('dest'))
95+ self.expectThat(command, IsInstance(list))
96+ self.expectThat(command, AllMatch(IsInstance(unicode)))
97+
98+ def test__runs_command_in_target(self):
99+ command = compose_mv_command(
100+ factory.make_name('source'), factory.make_name('dest'))
101+ self.assertEqual(['curtin', 'in-target', '--'], command[:3])
102+
103+ def test__moves_file(self):
104+ source = factory.make_name('source')
105+ dest = factory.make_name('dest')
106+ self.assertEqual(
107+ ['mv', '--', source, dest],
108+ compose_mv_command(source, dest)[-4:])
109+
110+
111+class TestComposeWriteTextFile(MAASTestCase):
112+
113+ def test__returns_complete_write_file_dict(self):
114+ preseed = compose_write_text_file(
115+ factory.make_name('file'), factory.make_name('content'))
116+ self.expectThat(preseed, IsInstance(dict))
117+ self.expectThat(
118+ preseed.keys(),
119+ ContainsAll(['path', 'content', 'owner', 'permissions']))
120+
121+ def test__obeys_path_param(self):
122+ path = factory.make_name('path')
123+ preseed = compose_write_text_file(path, factory.make_name('content'))
124+ self.assertEqual(path, preseed['path'])
125+
126+ def test__obeys_content_param(self):
127+ content = factory.make_name('content')
128+ preseed = compose_write_text_file(factory.make_name('path'), content)
129+ self.assertEqual(content, preseed['content'])
130+
131+ def test__defaults_owner_to_root(self):
132+ preseed = compose_write_text_file(
133+ factory.make_name('file'), factory.make_name('content'))
134+ self.assertEqual('root:root', preseed['owner'])
135+
136+ def test__obeys_owner_param(self):
137+ owner = '%s:%s' % (
138+ factory.make_name('user'),
139+ factory.make_name('group'),
140+ )
141+ preseed = compose_write_text_file(
142+ factory.make_name('file'), factory.make_name('content'),
143+ owner=owner)
144+ self.assertEqual(owner, preseed['owner'])
145+
146+ def test__defaults_permissions_to_0600(self):
147+ preseed = compose_write_text_file(
148+ factory.make_name('file'), factory.make_name('content'))
149+ self.assertEqual('0600', preseed['permissions'])
150+
151+ def test__obeys_permissions_param(self):
152+ permissions = 0123
153+ preseed = compose_write_text_file(
154+ factory.make_name('file'), factory.make_name('content'),
155+ permissions=permissions)
156+ self.assertEqual('0123', preseed['permissions'])