Merge lp:~allenap/maas/fix-makefile-write-dhcp-stuff into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 624
Proposed branch: lp:~allenap/maas/fix-makefile-write-dhcp-stuff
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 173 lines (+15/-66)
4 files modified
Makefile (+1/-6)
buildout.cfg (+1/-7)
src/provisioningserver/dhcp/tests/test_writer.py (+7/-40)
src/provisioningserver/dhcp/writer.py (+6/-13)
To merge this branch: bzr merge lp:~allenap/maas/fix-makefile-write-dhcp-stuff
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+109324@code.launchpad.net

Commit message

Tweak the way the DHCPConfigWriter is plumbed in.

Description of the change

This changes a few bits about the new DHCPConfigWriter.

- Renames write_dhcp_config.py to writer.py. It's in the
  provisioningserver.dhcp module, and it hardly needs explaining that
  it's about configuration.

- Removes the write_dhcp_config script entirely. It's not necessary -
  use bin/py -m provisioningserver/dhcp/writer instead - and is
  misleading because packages cannot use it anyway as it is generated
  by buildout.

- Remove all references to write_dhcp_config from Makefile.

- Remove the test for the write_dhcp_config script.

- Use stdout.write() to emit the config instead of print(); the latter
  adds an extra newline.

- Encodes the config as ASCII for the paranoid.

- Drive-by: restricts the scripts that the [database] part in
  buildout.cfg creates to just the bin/database script. Previously it
  was also generating a bin/postgresfixture script.

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

Looks good. +15/-66 without loosing functionality, that's nice.

Maybe we can wait for Julian to clarify things about [0] but I'll leave it up to you because, in my opinion, this is all good.

[0]

> Removes the write_dhcp_config script entirely. It's not necessary -
> use bin/py -m provisioningserver/dhcp/writer instead - and is
> misleading because packages cannot use it anyway as it is generated
> by buildout.

I agree with you. Since Julian is away I can only speculate here but this script can only be a development convenience and if it's the case then using the command as you suggest is, I think, good enough.

So far, when we had the need to expose a command, the usual way to do that has been to create a Django command. Now, unless I'm mistaken, this is part of the provisioning server because it will be run by the workers. We haven't talked about this but the workers will probably need to install a specific maas package (something like 'maas-worker') rather that the server 'maas' package and this worker package will only contain the worker code (i.e. the module 'provisioningserver'). This implies that if we want to ship scripts in this worker package we will have to come up with a new strategy because the Django MAAS application (and thus CLI) won't be installed there.

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

If these scripts are only needed by packaging then we can just use the
python -m ... forms. For users, it may be worth having a single
front-end script, e.g. maas-provision or maas-worker, that follows the
verb pattern, or subparsers as argparse calls it. In other words,
something like:

  maas-provision verb --verb-specific-option

Or, more specifically:

  maas-provision dhcp-config --subnet ...

Revision history for this message
Julian Edwards (julian-edwards) wrote :

On Friday 08 Jun 2012 12:49:33 you wrote:
> Review: Approve
>
> Looks good. +15/-66 without loosing functionality, that's nice.

'losing'

> Maybe we can wait for Julian to clarify things about [0] but I'll leave it
> up to you because, in my opinion, this is all good.
>
> [0]
>
> > Removes the write_dhcp_config script entirely. It's not necessary -
> >
> > use bin/py -m provisioningserver/dhcp/writer instead - and is
> > misleading because packages cannot use it anyway as it is generated
> > by buildout.
>
> I agree with you. Since Julian is away I can only speculate here but this
> script can only be a development convenience and if it's the case then
> using the command as you suggest is, I think, good enough.

I'm kinda unhappy about this change, I'd have at least expected you to talk to
me before just removing my work. I put the generated script in as a
development convenience, because the bin/py example above is pretty hideous to
remember and type. I intended the package to use the module's script as-is
however, because it will have set up paths already.

Also, the branch has removed a test which checks the stdout output, and the
new name "writer" is inaccurate. If you want to split hairs, it's
"write_config".

I'd appreciate it if you'd put the script back, either buildout-generated or a
shortcut using the bin/py method.

Why did you remove the build dependency from the check target? Regardless of
my changes it seems a good idea.

Revision history for this message
Gavin Panella (allenap) wrote :

I guess I probably ought not to have done this branch... I was just
itching to do something and I didn't have the presence of mind to back
off. Anyway, point by point:

> I'm kinda unhappy about this change, I'd have at least expected you
> to talk to me before just removing my work.

I figured you were away for the weekend now and probably unavailable,
it's easy to undo stuff, and I am hoping to seek forgiveness instead
of getting advance permission.

> I put the generated script in as a development convenience, because
> the bin/py example above is pretty hideous to remember and type. I
> intended the package to use the module's script as-is however,
> because it will have set up paths already.

The extra section in buildout.cfg wasn't needed for example, though
that's not much of an issue. Really I felt that this was the wrong way
to go about exposing commands. The conveniences we create for
ourselves as developers will inevitably flow downstream, and I would
like to avoid that in this case. The `python -m module` pattern
prevents PATH pollution and also keeps us aware that exposing
provisioning commands to users is not a well solved problem.

The Django side of MAAS has management commands. I think the
provisioning side should have something similar, a "command verb"
pattern, like django-admin or bzr for example.

Also, if we want to expose non-developer scripts, we ought to put them
in setup.py instead of buildout.cfg. I think this'll make them easier
for packagers to use.

> Also, the branch has removed a test which checks the stdout output,

I didn't realise that was what it was trying to test. I'll add a test
for this, but I'll use self.patch(sys, "stdout", ...) instead, if
that's okay?

> and the new name "writer" is inaccurate. If you want to split hairs,
> it's "write_config".

Every other package and module name, afaict, is a noun. Another
natural place for DHCPConfigWriter could be
provisioninserver.dhcp.scripts?

> I'd appreciate it if you'd put the script back, either
> buildout-generated or a shortcut using the bin/py method.

Okay.

> Why did you remove the build dependency from the check target?
> Regardless of my changes it seems a good idea.

Yeah, it is a good idea. This was actually the thing that got me
started on this branch: the massive rubber hammer to squash a single
missing dependency.

So, given that testing a full build is good, but that it's worthwhile
exercising the dependencies in Makefile too, I'll change it to do a
full build *after* running tests.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2012-06-08 00:23:22 +0000
3+++ Makefile 2012-06-08 10:31:19 +0000
4@@ -21,7 +21,6 @@
5 bin/twistd.pserv bin/test.pserv \
6 bin/twistd.txlongpoll \
7 bin/py bin/ipy \
8- bin/write_dhcp_config \
9 $(js_enums)
10
11 all: build doc
12@@ -70,10 +69,6 @@
13 bin/buildout install repl
14 @touch --no-create bin/py bin/ipy
15
16-bin/write_dhcp_config: bin/buildout buildout.cfg versions.cfg setup.py
17- bin/buildout install write-dhcp-config
18- @touch --no-create $@
19-
20 test: bin/test.maas bin/test.maastesting bin/test.pserv $(js_enums)
21 bin/test.maas
22 bin/test.maastesting
23@@ -95,7 +90,7 @@
24 lint-js:
25 @find $(sources) -type f -print0 | xargs -r0 $(pocketlint)
26
27-check: clean build test
28+check: clean test
29
30 docs/api.rst: bin/maas src/maasserver/api.py syncdb
31 bin/maas generate_api_doc > $@
32
33=== modified file 'buildout.cfg'
34--- buildout.cfg 2012-06-07 05:54:29 +0000
35+++ buildout.cfg 2012-06-08 10:31:19 +0000
36@@ -9,7 +9,6 @@
37 repl
38 sphinx
39 txlongpoll
40- write-dhcp-config
41 extensions = buildout-versions
42 buildout_versions_file = versions.cfg
43 versions = versions
44@@ -80,6 +79,7 @@
45 extra-paths = ${common:extra-paths}
46 interpreter =
47 entry-points = database=postgresfixture.main:main
48+scripts = database
49
50 [maas]
51 recipe = zc.recipe.egg
52@@ -214,9 +214,3 @@
53 txamqp
54 entry-points = twistd.txlongpoll=twisted.scripts.twistd:run
55 scripts = twistd.txlongpoll
56-
57-[write-dhcp-config]
58-recipe = z3c.recipe.scripts
59-extra-paths = ${common:extra-paths}
60-entry-points = write_dhcp_config=provisioningserver.dhcp.write_dhcp_config:run
61-scripts = write_dhcp_config
62
63=== renamed file 'src/provisioningserver/dhcp/tests/test_write_dhcp_config.py' => 'src/provisioningserver/dhcp/tests/test_writer.py'
64--- src/provisioningserver/dhcp/tests/test_write_dhcp_config.py 2012-06-07 07:53:25 +0000
65+++ src/provisioningserver/dhcp/tests/test_writer.py 2012-06-08 10:31:19 +0000
66@@ -1,7 +1,7 @@
67 # Copyright 2012 Canonical Ltd. This software is licensed under the
68 # GNU Affero General Public License version 3 (see the file LICENSE).
69
70-"""Tests for write-dhcp-config.py"""
71+"""Tests for `provisioningserver.dhcp.writer`."""
72
73 from __future__ import (
74 absolute_import,
75@@ -13,19 +13,15 @@
76 __all__ = []
77
78 import os
79-import subprocess
80
81 from maastesting.matchers import ContainsAll
82 from maastesting.testcase import TestCase
83-from testtools.matchers import (
84- MatchesStructure,
85- )
86-
87-from provisioningserver.dhcp.write_dhcp_config import DHCPConfigWriter
88-
89-
90-class TestModule(TestCase):
91- """Test the write-dhcp-config module."""
92+from provisioningserver.dhcp.writer import DHCPConfigWriter
93+from testtools.matchers import MatchesStructure
94+
95+
96+class TestDHCPConfigWriter(TestCase):
97+ """Test `DHCPConfigWriter`."""
98
99 def test_arg_setup(self):
100 writer = DHCPConfigWriter()
101@@ -92,32 +88,3 @@
102 writer.run(test_args)
103
104 self.assertTrue(os.path.exists(outfile))
105-
106-
107-class TestScriptExecutable(TestCase):
108- """Test that the actual script is executable."""
109-
110- def test_script(self):
111- test_args = [
112- '--subnet', 'subnet',
113- '--subnet-mask', 'subnet-mask',
114- '--next-server', 'next-server',
115- '--broadcast-address', 'broadcast-address',
116- '--dns-servers', 'dns-servers',
117- '--gateway', 'gateway',
118- '--low-range', 'low-range',
119- '--high-range', 'high-range',
120- ]
121-
122- exe = [os.path.join(
123- os.path.dirname(__file__),
124- os.pardir, os.pardir, os.pardir, os.pardir,
125- "bin", "write_dhcp_config")]
126-
127- exe.extend(test_args)
128- output = subprocess.check_output(exe)
129-
130- contains_all_params = ContainsAll(
131- ['subnet', 'subnet-mask', 'next-server', 'broadcast-address',
132- 'dns-servers', 'gateway', 'low-range', 'high-range'])
133- self.assertThat(output, contains_all_params)
134
135=== renamed file 'src/provisioningserver/dhcp/write_dhcp_config.py' => 'src/provisioningserver/dhcp/writer.py'
136--- src/provisioningserver/dhcp/write_dhcp_config.py 2012-06-07 07:53:25 +0000
137+++ src/provisioningserver/dhcp/writer.py 2012-06-08 10:31:19 +0000
138@@ -12,8 +12,8 @@
139 __metaclass__ = type
140 __all__ = []
141
142-
143 import argparse
144+from sys import stdout
145
146 from provisioningserver.dhcp import config
147
148@@ -73,20 +73,13 @@
149 def run(self, argv=None):
150 """Generate the config and write to stdout or a file as required."""
151 args = self.parse_args(argv)
152- output = self.generate(args)
153- outfile = args.out_file
154- if outfile is not None:
155- with open(outfile, "w") as f:
156+ output = self.generate(args).encode("ascii")
157+ if args.out_file is not None:
158+ with open(args.out_file, "wb") as f:
159 f.write(output)
160 else:
161- print(output)
162-
163-
164-def run():
165- """Entry point for scripts."""
166- writer = DHCPConfigWriter()
167- writer.run()
168+ stdout.write(output)
169
170
171 if __name__ == "__main__":
172- run()
173+ DHCPConfigWriter().run()