Merge lp:~javier.collado/utah/bug1061011 into lp:utah

Proposed by Javier Collado
Status: Merged
Merged at revision: 702
Proposed branch: lp:~javier.collado/utah/bug1061011
Merge into: lp:utah
Diff against target: 188 lines (+92/-38)
3 files modified
tests/test_preseed.py (+46/-0)
utah/preseed.py (+40/-36)
utah/provisioning/provisioning.py (+6/-2)
To merge this branch: bzr merge lp:~javier.collado/utah/bug1061011
Reviewer Review Type Date Requested Status
Max Brustkern (community) Approve
Review via email: mp+128497@code.launchpad.net

Description of the change

As agreed in the bugs comments, this branch adds a new exception to the preseed
module, so that the traceback when a duplicated question name is found in the
preseed file is clearer. In particular, the new traceback would be something
like this:

  File "/home/javi/code/bzr/utah/bug1061011/utah/preseed.py", line 134, in section_updated
    raise DuplicatedQuestionName(new_text)
utah.preseed.DuplicatedQuestionName: partman/confirm

In addition to this a new module with a few simple test cases have been added
to the tests/ directory (they can be executed with nosetests tests/). I'm not
sure if that's the best location for server test cases, but I haven't found any
other test cases and this one looks fine to me since the files under tests/
aren't included in the binary package. Anyway, under tests/ there's already a
script that is used to run the client test cases, but I guess we can have both
things together.

In the future, we could also have a jenkins job to run the test cases for the
server as there is already one to run the test cases for the client.

To post a comment you must log in.
Revision history for this message
Max Brustkern (nuclearbob) wrote :

I like that new exception. If you've already tested this, we should be good to go ahead and merge, otherwise, I'll try to test it.

review: Approve
Revision history for this message
Javier Collado (javier.collado) wrote :

@Max

Yes, I already tested it, so I'm merging it.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'tests/test_preseed.py'
2--- tests/test_preseed.py 1970-01-01 00:00:00 +0000
3+++ tests/test_preseed.py 2012-10-08 13:46:36 +0000
4@@ -0,0 +1,46 @@
5+from utah.preseed import (
6+ Preseed,
7+ Section,
8+ BlankSection,
9+ CommentSection,
10+ ConfigurationSection,
11+ DuplicatedQuestionName,
12+ )
13+
14+import unittest
15+
16+
17+class TestPreseedLoadDump(unittest.TestCase):
18+ """
19+ Minimal load/dump test cases
20+ """
21+ BASIC_PRESEED = """# Comment
22+
23+d-i passwd/username string utah
24+"""
25+
26+ DUPLICATED_QUESTION_NAME_PRESEED = """d-i passwd/username string utah
27+d-i passwd/username string utah
28+"""
29+
30+ def test_load(self):
31+ """Load basic preseed"""
32+ preseed = Preseed(self.BASIC_PRESEED.splitlines())
33+
34+ self.assertEqual(len(preseed.sections), 3)
35+ section_types = (CommentSection, BlankSection, ConfigurationSection)
36+ for index, section_type in enumerate(section_types):
37+ self.assertIsInstance(preseed.sections[index], section_type)
38+
39+ def test_dump(self):
40+ """Dump basic preseed"""
41+ preseed = Preseed()
42+ preseed.append(Section.new(preseed, '# Comment'))
43+ preseed.append(Section.new(preseed, ''))
44+ preseed.append(Section.new(preseed, 'd-i passwd/username string utah'))
45+ self.assertEqual(preseed.dump(), self.BASIC_PRESEED)
46+
47+ def test_duplicated_question_name(self):
48+ """Exception raised on duplicated question name"""
49+ with self.assertRaises(DuplicatedQuestionName):
50+ Preseed(self.DUPLICATED_QUESTION_NAME_PRESEED.splitlines())
51
52=== modified file 'utah/preseed.py'
53--- utah/preseed.py 2012-09-06 12:34:28 +0000
54+++ utah/preseed.py 2012-10-08 13:46:36 +0000
55@@ -8,12 +8,13 @@
56 """
57 Read/Write preseed files easily
58 """
59- def __init__(self, filename):
60+ def __init__(self, lines=None):
61 # Used to access quickly to configuration sections by question name
62 self._qnames = {}
63-
64- self.filename = filename
65- self.read(filename)
66+ if lines is not None:
67+ self.load(lines)
68+ else:
69+ self.sections = []
70
71 def __getitem__(self, key):
72 """
73@@ -37,42 +38,38 @@
74
75 return key in self._qnames
76
77- def read(self, filename=None):
78- """
79- Read a whole preseed file and parse its configuration lines
80- """
81- if filename is not None:
82- self.filename = filename
83- else:
84- filename = self.filename
85+ def load(self, lines):
86+ """
87+ Parse preseed configuration lines
88
89+ :param lines: Any iterable that yields preseed file configuration lines
90+ :type lines: iterable
91+ """
92 self.sections = []
93- with open(filename) as f:
94- # One line might be made of multiple lines
95- # that are continued with '\\'
96- output_lines = []
97- for input_line in f:
98- input_line = input_line.rstrip('\n')
99- output_lines.append(input_line)
100-
101- # Line is finished only when no continuation character is found
102- if not input_line.endswith('\\'):
103- new_section = Section.new(self, output_lines)
104- self.append(new_section)
105- output_lines = []
106-
107- def write(self, filename=None):
108+ # One line might be made of multiple lines
109+ # that are continued with '\\'
110+ output_lines = []
111+ for input_line in lines:
112+ input_line = input_line.rstrip('\n')
113+ output_lines.append(input_line)
114+
115+ # Line is finished only when no continuation character is found
116+ if not input_line.endswith('\\'):
117+ new_section = Section.new(self, output_lines)
118+ self.append(new_section)
119+ output_lines = []
120+
121+ return self
122+
123+ def dump(self):
124 """
125+ Dump preseed configuration statements
126 Write the modified file to the same or a new location
127+
128+ :returns: Formatted preseed configuration lines
129+ :rtype: string
130 """
131- if filename is not None:
132- self.filename = filename
133- else:
134- filename = self.filename
135-
136- with open(filename, 'w') as f:
137- for section in self.sections:
138- f.write('{}'.format(section))
139+ return ''.join(str(section) for section in self.sections)
140
141 def append(self, new_section, ref_section=None):
142 """
143@@ -133,7 +130,8 @@
144 """
145 if property_name == 'qname':
146 new_text = new_value.text
147- assert new_text not in self._qnames
148+ if new_text in self._qnames:
149+ raise DuplicatedQuestionName(new_text)
150
151 if old_value is not None:
152 old_text = old_value.text
153@@ -350,3 +348,9 @@
154
155 def property_updated(self, property_name, old_value, new_value):
156 self.parent.section_updated(self, property_name, old_value, new_value)
157+
158+
159+class DuplicatedQuestionName(Exception):
160+ """
161+ Exception raised when a question name is found more than once in a preseed
162+ """
163
164=== modified file 'utah/provisioning/provisioning.py'
165--- utah/provisioning/provisioning.py 2012-10-04 11:49:14 +0000
166+++ utah/provisioning/provisioning.py 2012-10-08 13:46:36 +0000
167@@ -787,7 +787,8 @@
168 self.logger.info('Setting up preseed')
169 if tmpdir is None:
170 tmpdir = self.tmpdir
171- preseed = Preseed(self.preseed)
172+ with open(self.preseed) as f:
173+ preseed = Preseed(f)
174 if 'preseed/late_command' in preseed:
175 question = preseed['preseed/late_command']
176 if self.installtype == 'desktop':
177@@ -833,7 +834,10 @@
178 question = preseed['passwd/username']
179 question.value = config.user
180
181- preseed.write(os.path.join(tmpdir, 'initrd.d', 'preseed.cfg'))
182+ output_preseed_filename = os.path.join(tmpdir,
183+ 'initrd.d', 'preseed.cfg')
184+ with open(output_preseed_filename, 'w') as f:
185+ f.write(preseed.dump())
186
187 if self.installtype == 'desktop':
188 self.logger.info('Inserting preseed into casper')

Subscribers

People subscribed via source and target branches