launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #11730
[Merge] lp:~jtv/maas/customize-config into lp:maas
Jeroen T. Vermeulen has proposed merging lp:~jtv/maas/customize-config into lp:maas.
Requested reviews:
MAAS Maintainers (maas-maintainers)
For more details, see:
https://code.launchpad.net/~jtv/maas/customize-config/+merge/123477
Based on discussion with Scott, and reviews from predecessors to this branch.
Jeroen
--
https://code.launchpad.net/~jtv/maas/customize-config/+merge/123477
Your team MAAS Maintainers is requested to review the proposed merge of lp:~jtv/maas/customize-config into lp:maas.
=== modified file 'src/provisioningserver/__main__.py'
--- src/provisioningserver/__main__.py 2012-09-03 05:20:39 +0000
+++ src/provisioningserver/__main__.py 2012-09-10 04:49:20 +0000
@@ -12,6 +12,7 @@
__metaclass__ = type
+import provisioningserver.customize_config
import provisioningserver.dhcp.writer
import provisioningserver.pxe.install_bootloader
import provisioningserver.pxe.install_image
@@ -21,17 +22,16 @@
)
+script_commands = {
+ 'atomic-write': AtomicWriteScript,
+ 'customize-config': provisioningserver.customize_config,
+ 'generate-dhcp-config': provisioningserver.dhcp.writer,
+ 'install-pxe-bootloader': provisioningserver.pxe.install_bootloader,
+ 'install-pxe-image': provisioningserver.pxe.install_image,
+}
+
+
main = MainScript(__doc__)
-main.register(
- "install-pxe-bootloader",
- provisioningserver.pxe.install_bootloader)
-main.register(
- "install-pxe-image",
- provisioningserver.pxe.install_image)
-main.register(
- "generate-dhcp-config",
- provisioningserver.dhcp.writer)
-main.register(
- "atomic-write",
- AtomicWriteScript)
+for name, command in sorted(script_commands.items()):
+ main.register(name, command)
main()
=== added file 'src/provisioningserver/customize_config.py'
--- src/provisioningserver/customize_config.py 1970-01-01 00:00:00 +0000
+++ src/provisioningserver/customize_config.py 2012-09-10 04:49:20 +0000
@@ -0,0 +1,49 @@
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Management command: customize a config file.
+
+Use this when there's absolutely no way around adding a custom MAAS section
+to an existing config file. It appends the custom section on first run, but
+on subsequent runs, replaces the existing custom section in-place.
+"""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = [
+ 'add_arguments',
+ 'run',
+ ]
+
+import sys
+
+from provisioningserver.utils import write_custom_config_section
+
+
+def add_arguments(parser):
+ parser.add_argument(
+ 'file', metavar='FILE',
+ help="Configuration file that you want to customize.")
+ parser.add_argument(
+ '--encoding', dest='encoding', default='utf-8',
+ help="Encoding to use when reading and writing config.")
+
+
+def run(args):
+ """Customize a config file.
+
+ Reads a custom configuration section from standard input, and the given
+ configuration file. Prints to standard output a copy of the file with
+ the custom section appended, or substituted for an existing custom
+ section if there already was one.
+ """
+ with open(args.file, 'rb') as original_file:
+ original_text = original_file.read().decode(args.encoding)
+ custom_section = sys.stdin.read().decode(args.encoding)
+ new_text = write_custom_config_section(original_text, custom_section)
+ sys.stdout.write(new_text.encode(args.encoding))
=== added file 'src/provisioningserver/tests/test_customize_config.py'
--- src/provisioningserver/tests/test_customize_config.py 1970-01-01 00:00:00 +0000
+++ src/provisioningserver/tests/test_customize_config.py 2012-09-10 04:49:20 +0000
@@ -0,0 +1,85 @@
+# Copyright 2012 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for customize_config."""
+
+from __future__ import (
+ absolute_import,
+ print_function,
+ unicode_literals,
+ )
+
+__metaclass__ = type
+__all__ = []
+
+from argparse import ArgumentParser
+from io import BytesIO
+import os.path
+from subprocess import (
+ PIPE,
+ Popen,
+ )
+import sys
+from textwrap import dedent
+
+from maastesting.factory import factory
+from maastesting.testcase import TestCase
+import provisioningserver
+from provisioningserver import customize_config
+from provisioningserver.utils import maas_custom_config_markers
+
+
+def locate_dev_root():
+ """Return root of development source tree."""
+ return os.path.join(
+ os.path.dirname(provisioningserver.__file__),
+ os.pardir, os.pardir)
+
+
+class TestCustomizeConfig(TestCase):
+
+ def run_command(self, input_file, stdin):
+ self.patch(sys, 'stdin', BytesIO(stdin.encode('utf-8')))
+ self.patch(sys, 'stdout', BytesIO())
+ parser = ArgumentParser()
+ customize_config.add_arguments(parser)
+ parsed_args = parser.parse_args((input_file, ))
+ customize_config.run(parsed_args)
+
+ def test_runs_as_script(self):
+ original_text = factory.getRandomString()
+ original_file = self.make_file(original_text)
+ script = "%s/bin/maas-provision" % locate_dev_root()
+ command = Popen(
+ [script, "customize-config", original_file],
+ stdin=PIPE, stdout=PIPE,
+ env=dict(PYTHONPATH=":".join(sys.path)))
+ command.communicate(original_text)
+ self.assertEqual(0, command.returncode)
+
+ def test_produces_sensible_text(self):
+ header, footer = maas_custom_config_markers
+ original_file = self.make_file(contents="Original text here.")
+
+ self.run_command(original_file, stdin="Custom section here.")
+
+ sys.stdout.seek(0)
+ expected = dedent("""\
+ Original text here.
+ %s
+ Custom section here.
+ %s
+ """) % (header, footer)
+ output = sys.stdout.read()
+ self.assertEqual(expected, output.decode('utf-8'))
+
+ def test_does_not_modify_original(self):
+ original_text = factory.getRandomString().encode('ascii')
+ original_file = self.make_file(contents=original_text)
+
+ self.run_command(original_file, factory.getRandomString())
+
+ with open(original_file, 'rb') as reread_file:
+ contents_after = reread_file.read()
+
+ self.assertEqual(original_text, contents_after)