Merge lp:~robru/cupstream2distro/new-config-class into lp:cupstream2distro

Proposed by Robert Bruce Park
Status: Merged
Merged at revision: 1346
Proposed branch: lp:~robru/cupstream2distro/new-config-class
Merge into: lp:cupstream2distro
Diff against target: 694 lines (+116/-175)
11 files modified
Makefile (+1/-1)
citrain/recipes/manager.py (+1/-3)
citrain/setup_citrain.py (+7/-13)
cupstream2distro/settings.py (+6/-50)
cupstream2distro/silomanager.py (+34/-32)
cupstream2distro/utils.py (+17/-2)
tests/unit/test_recipe_manager.py (+4/-6)
tests/unit/test_script_build.py (+1/-2)
tests/unit/test_script_setup_citrain.py (+11/-24)
tests/unit/test_silomanager.py (+28/-42)
tests/unit/test_utils.py (+6/-0)
To merge this branch: bzr merge lp:~robru/cupstream2distro/new-config-class
Reviewer Review Type Date Requested Status
Robert Bruce Park (community) Approve
Review via email: mp+283566@code.launchpad.net

Commit message

New Config class for reading config from disk.

To post a comment you must log in.
Revision history for this message
Robert Bruce Park (robru) wrote :

Needs tests, and not yet tested in staging.

1345. By Robert Bruce Park

Rebase on trunk.

1346. By Robert Bruce Park

100% tests.

Revision history for this message
Robert Bruce Park (robru) wrote :

works great in staging.

review: Approve
1347. By Robert Bruce Park

Fix test in trusty.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2016-01-10 10:12:24 +0000
3+++ Makefile 2016-01-23 01:33:24 +0000
4@@ -7,7 +7,7 @@
5 find -name '*.xml*' | xargs xmllint --noout
6 find -name '*.py' | xargs python3 -m pyflakes
7 find -name '*.py' | xargs python3 -m pep8 --ignore=E731 # Don't tell me what to do
8- find -name '*.py' | xargs python3 -m pylint --persistent=no --disable attribute-defined-outside-init,broad-except,deprecated-method,fixme,import-error,invalid-name,no-init,no-member,no-self-use,no-value-for-parameter,protected-access,redefined-variable-type,star-args,too-few-public-methods,too-many-arguments,too-many-instance-attributes,too-many-public-methods,unbalanced-tuple-unpacking,unused-argument,unused-variable
9+ find -name '*.py' | xargs python3 -m pylint --persistent=no --disable attribute-defined-outside-init,broad-except,deprecated-method,fixme,import-error,invalid-name,no-init,no-member,no-self-use,no-value-for-parameter,protected-access,redefined-variable-type,star-args,too-few-public-methods,too-many-arguments,too-many-instance-attributes,too-many-public-methods,unbalanced-tuple-unpacking,unsubscriptable-object,unused-argument,unused-variable
10
11 performance:
12 BEAT_THE_CLOCK=500 make check
13
14=== modified file 'citrain/recipes/manager.py'
15--- citrain/recipes/manager.py 2015-11-22 13:37:37 +0000
16+++ citrain/recipes/manager.py 2016-01-23 01:33:24 +0000
17@@ -28,9 +28,7 @@
18 from citrain.recipes.secondary import Secondary
19 from citrain.recipes.sourcesync import SourceSync
20 from citrain.recipes.binarysync import BinarySync
21-from cupstream2distro.settings import STABLE_OVERLAY_PPA
22 from cupstream2distro.utils import env, log_value_of
23-from cupstream2distro.launchpadmanager import lp
24 from cupstream2distro.errors import CITrainError
25
26
27@@ -83,11 +81,11 @@
28 dest = self.silo_state.dest
29 ppa = self.silo_state.ppa
30 dual = self.silo_state.dual
31+ overlay = self.silo_state.stable_overlay
32 for Build, source_names in self.types.items():
33 for name in sorted(source_names):
34 build = self.builds[name] = Build(name, series, dest, ppa)
35 if dual:
36- overlay = lp.get_ppa(STABLE_OVERLAY_PPA)
37 secondary = Secondary if Build is Merge else Manual
38 second = secondary(name, dual, overlay, ppa, build)
39 self.builds['zzz_' + name] = second
40
41=== modified file 'citrain/setup_citrain.py'
42--- citrain/setup_citrain.py 2016-01-05 22:03:49 +0000
43+++ citrain/setup_citrain.py 2016-01-23 01:33:24 +0000
44@@ -33,17 +33,15 @@
45
46 from cupstream2distro.launchpadmanager import lp
47 from cupstream2distro.utils import (
48+ Config,
49 log_value_of,
50 run_script,
51 suppress,
52 utf8_open,
53 )
54 from cupstream2distro.settings import (
55- ALL_SILO_NAMES,
56 CITRAIN_BINDIR,
57- PPA_TEAM,
58 ROOT_CU2D,
59- SILO_NAME_LIST,
60 SILO_NAMES_FILE,
61 SILO_RSYNCDIR,
62 SILOS_DIR,
63@@ -56,18 +54,15 @@
64
65 def discover_silos():
66 """Query Launchpad to see how many silos are available."""
67- SILO_NAME_LIST.clear()
68- while ALL_SILO_NAMES:
69- ALL_SILO_NAMES.pop()
70- for ppa in lp.people[PPA_TEAM].ppas:
71+ silonames = []
72+ for ppa in lp.people[Config['ppa.team']].ppas:
73 dist = ppa.distribution_link.split('/')[-1]
74 siloname = '{}/{}'.format(dist, ppa.name)
75 if '/landing-' in siloname and '-deleted' not in siloname:
76- SILO_NAME_LIST.setdefault(dist, [])
77- SILO_NAME_LIST[dist].append(siloname)
78- ALL_SILO_NAMES.append(siloname)
79+ silonames.append(siloname)
80 with utf8_open(SILO_NAMES_FILE, 'w') as filename:
81- filename.write(json.dumps(sorted(ALL_SILO_NAMES)))
82+ filename.write(json.dumps(sorted(silonames)))
83+ return silonames
84
85
86 def template(template_name):
87@@ -114,8 +109,7 @@
88
89 def main():
90 """Generate all Jenkins XML job files."""
91- discover_silos()
92- for siloname in ALL_SILO_NAMES:
93+ for siloname in discover_silos():
94 setup_silo(siloname)
95 setup_job('cyphermox-test')
96 setup_job('prepare-silo')
97
98=== modified file 'cupstream2distro/settings.py'
99--- cupstream2distro/settings.py 2016-01-20 02:32:00 +0000
100+++ cupstream2distro/settings.py 2016-01-23 01:33:24 +0000
101@@ -20,22 +20,10 @@
102 Doesn't import anything from any other part of the project.
103 """
104
105-import json
106-import codecs
107 import logging
108
109 from os import environ
110 from os.path import dirname, expanduser, isdir, join, realpath
111-from collections import defaultdict
112-
113-
114-def read_from_disk(filename):
115- """Read a value from a file on disk."""
116- try:
117- with codecs.open(filename, encoding='utf-8') as data:
118- return data.read().strip()
119- except (IOError, OSError):
120- return None
121
122
123 logging.basicConfig(
124@@ -56,46 +44,14 @@
125
126 ROOT_CU2D = dirname(dirname(realpath(__file__)))
127
128-PPA_TEAM_FILE = join(HOME_DIR, 'ppa.team')
129-PPA_TEAM = read_from_disk(PPA_TEAM_FILE) or 'ci-train-staging-area'
130-LANDING_SCHEME = PPA_TEAM + '/{}'
131-
132-STABLE_OVERLAY_PPA = PPA_TEAM + '/ubuntu/stable-phone-overlay'
133-
134-
135-def read_silo_names(filename):
136- """Read the list of available silo PPA names from a file on disk."""
137- silos = []
138- bydistro = defaultdict(list)
139- try:
140- silos = json.loads(read_from_disk(filename))
141- for siloname in silos:
142- bydistro[siloname.split('/')[0]].append(siloname)
143- except (IOError, OSError, ValueError, TypeError):
144- logging.warning('Silo name list not found, run setup_citrain.py.')
145- logging.debug(str(silos))
146- logging.debug(str(bydistro))
147- return silos, dict(bydistro)
148-
149-
150 SILO_NAMES_FILE = expanduser('~/.ci-train-silo-names')
151-ALL_SILO_NAMES, SILO_NAME_LIST = read_silo_names(SILO_NAMES_FILE)
152-
153-SILO_BUILDPPA_SCHEME = PPA_TEAM + '/{}/{}'
154+
155+
156 SILOS_DIR = expanduser('~/silos')
157 SILO_RSYNCDIR = '~/out'
158 CITRAIN_BINDIR = join(ROOT_CU2D, 'citrain')
159
160-
161-# These paths are hard-coded in lp:cupstream2distro/charm, don't change them
162-BILETO_TOKEN_PATH = expanduser('~/bileto.token')
163-BILETO_TOKEN = read_from_disk(BILETO_TOKEN_PATH)
164-
165-BILETO_IP_PATH = expanduser('~/bileto.ip')
166-BILETO_IP = read_from_disk(BILETO_IP_PATH)
167-
168-BILETO_GET = 'http://{}:8080/v1/ticket/{{}}'.format(BILETO_IP)
169-BILETO_API = 'http://{}:8080/v1/tickets?token={}'.format(
170- BILETO_IP, BILETO_TOKEN)
171-COMMENT_API = 'http://{}:8080/v1/comment?token={}'.format(
172- BILETO_IP, BILETO_TOKEN)
173+# Default values for config files read from disk.
174+DEFAULTS = {
175+ 'ppa.team': 'ci-train-staging-area',
176+}
177
178=== modified file 'cupstream2distro/silomanager.py'
179--- cupstream2distro/silomanager.py 2016-01-23 00:38:01 +0000
180+++ cupstream2distro/silomanager.py 2016-01-23 01:33:24 +0000
181@@ -38,17 +38,11 @@
182 from lazr.restfulclient.errors import PreconditionFailed, NotFound
183
184 from cupstream2distro.branchhandling import get_package_name_from_branch
185+from cupstream2distro.settings import SILO_NAMES_FILE, SILOS_DIR
186 from cupstream2distro.errors import CITrainError, PrepError
187 from cupstream2distro.launchpadmanager import lp
188-from cupstream2distro.settings import (
189- BILETO_API,
190- BILETO_GET,
191- LANDING_SCHEME,
192- SILO_NAME_LIST,
193- SILOS_DIR,
194- STABLE_OVERLAY_PPA,
195-)
196 from cupstream2distro.utils import (
197+ Config,
198 env,
199 memoize,
200 log_value_of,
201@@ -112,9 +106,11 @@
202
203 def update_bileto(**kwargs):
204 """Update a ticket in Bileto."""
205+ api = 'http://{}:8080/v1/tickets?token={}'.format(
206+ Config['bileto.ip'], Config['bileto.token'])
207 try:
208 response = requests.post(
209- BILETO_API, data=json.dumps(kwargs), headers=JSON)
210+ api, data=json.dumps(kwargs), headers=JSON)
211 except IOError as err:
212 logging.error('Failed to contact Bileto: %s', scrub(str(err)))
213 return
214@@ -140,14 +136,25 @@
215 REQUEST_ID_GLOB = REQUEST_ID_FILE.format('*')
216 _lock_fd = None
217
218+ @staticmethod
219+ @memoize
220+ def discover_ppas(distribution='ubuntu'):
221+ """Read the cached list of PPAs we have."""
222+ try:
223+ silos = json.loads(Config[SILO_NAMES_FILE])
224+ except (IOError, OSError, ValueError, TypeError):
225+ raise PrepError('Silo name list not found, run setup_citrain.py')
226+ log_value_of.silos()
227+ return [silo for silo in silos if silo.startswith(distribution + '/')]
228+
229 @classmethod
230 def find_first_available(cls, distribution='ubuntu'):
231 """Return a siloname for an unassigned silo."""
232 log_value_of.distribution()
233- logging.debug(SILO_NAME_LIST[distribution])
234- shuffle(SILO_NAME_LIST[distribution])
235- logging.debug(SILO_NAME_LIST[distribution])
236- for siloname in SILO_NAME_LIST[distribution]:
237+ silonames = cls.discover_ppas(distribution)
238+ shuffle(silonames)
239+ log_value_of.silonames('Shuffled')
240+ for siloname in silonames:
241 log_value_of.siloname('Checking if available')
242 filename = join(SILOS_DIR, siloname, cls.REQUEST_ID_GLOB)
243 if not glob(filename):
244@@ -215,7 +222,6 @@
245 def assign(self, requestid):
246 """Assign a new requestid to an available silo."""
247 self.requestid = requestid
248- self.validate_permissions()
249 self.find_existing_assignment()
250 if not self.siloname:
251 self.siloname = SiloState.find_first_available(self._distribution)
252@@ -223,18 +229,6 @@
253 self.set_status('Ready to build')
254 logging.info(self.summarize(status=False))
255
256- def validate_permissions(self):
257- """Ensure the user has permission to assign."""
258- logging.info('Checking LP teams for %s', env.BUILD_USER_ID)
259- memberships = lp.people[env.BUILD_USER_ID].memberships_details
260- teams = {member.team_link.split('~')[-1] for member in memberships}
261- if not teams & {'ubuntu-core-dev', 'ci-train-ppa-service'}:
262- assigned = len(glob(join(
263- SILOS_DIR, self._distribution, '*', self.REQUEST_ID_GLOB)))
264- total = len(SILO_NAME_LIST[self._distribution])
265- if total - assigned < 5:
266- raise PrepError('Low on silos: Ask a trainguard to assign.')
267-
268 def find_existing_assignment(self):
269 """Find & re-use existing assignment if any."""
270 rid = self.requestid
271@@ -273,7 +267,9 @@
272 """Fetch request from Bileto."""
273 rid = self.requestid
274 log_value_of.rid('Loading from Bileto')
275- response = requests.get(BILETO_GET.format(rid)).json()
276+ response = requests.get(
277+ 'http://{}:8080/v1/ticket/{}'.format(Config['bileto.ip'], rid)
278+ ).json()
279 self._bileto = (response.get('requests') or [{}])[0]
280 creator = self.creator
281 log_value_of.creator('Bileto says')
282@@ -292,7 +288,7 @@
283 raise PrepError('Field "{}" is mandatory.'.format(field))
284 if not (self._merge_proposals or self.sources or self._sync_request):
285 raise PrepError('Need either merges, sources, or a sync.')
286- if self._distribution not in SILO_NAME_LIST:
287+ if self._distribution not in ('ubuntu',):
288 raise PrepError('{} is unsupported.'.format(self._distribution))
289 if self.dual:
290 if self._sync_request:
291@@ -417,7 +413,7 @@
292 def ppa(self):
293 """Return the lplib object representing the silo ppa."""
294 siloname = self.siloname or self._siloname
295- return lp.get_ppa(LANDING_SCHEME.format(siloname))
296+ return lp.get_ppa('{}/{}'.format(Config['ppa.team'], siloname))
297
298 @property
299 @memoize
300@@ -453,11 +449,17 @@
301
302 @property
303 @memoize
304+ def stable_overlay(self):
305+ """Return launchpadlib object representing stable overlay ppa."""
306+ return lp.get_ppa(Config['ppa.team'] + '/ubuntu/stable-phone-overlay')
307+
308+ @property
309+ @memoize
310 def dest(self):
311 """Return launchpadlib object representing the destination archive."""
312 series = self._series
313 if series == 'wily+vivid':
314- return lp.get_ppa(STABLE_OVERLAY_PPA)
315+ return self.stable_overlay
316 if self._dest and '+' not in series:
317 return lp.get_ppa(self._dest)
318 return lp.distributions[self._distribution].main_archive
319@@ -478,9 +480,9 @@
320 elif archive.isdigit():
321 siloname = '{}/landing-{:03d}'.format(
322 self._distribution, int(archive))
323- return lp.get_ppa(LANDING_SCHEME.format(siloname))
324+ return lp.get_ppa('{}/{}'.format(Config['ppa.team'], siloname))
325 elif archive == 'stable-overlay':
326- return lp.get_ppa(STABLE_OVERLAY_PPA)
327+ return self.stable_overlay
328 elif archive:
329 with suppress(KeyError):
330 return lp.distributions[archive].main_archive
331
332=== modified file 'cupstream2distro/utils.py'
333--- cupstream2distro/utils.py 2016-01-10 10:12:24 +0000
334+++ cupstream2distro/utils.py 2016-01-23 01:33:24 +0000
335@@ -22,7 +22,7 @@
336 import subprocess
337
338 import os
339-from os.path import exists, isfile, join
340+from os.path import exists, expanduser, isfile, join
341
342 from hashlib import sha256
343 from functools import wraps
344@@ -33,7 +33,7 @@
345 from six import text_type
346 from six.moves.urllib.parse import unquote
347
348-from cupstream2distro.settings import SILOS_DIR
349+from cupstream2distro.settings import DEFAULTS, SILOS_DIR
350
351
352 TOKEN_SCRUB = re.compile('token=[a-z0-9-]+', flags=re.IGNORECASE)
353@@ -160,6 +160,21 @@
354 return 'Environment Variables:\n' + pformat(dict(os.environ))
355
356
357+@singleton
358+class Config:
359+ """Read configuration values from disk."""
360+ dirs = ('/etc/cupstream2distro', expanduser('~'))
361+
362+ @memoize
363+ def __getitem__(self, key):
364+ """Read configuration value from disk."""
365+ for root in self.dirs:
366+ with suppress(FileNotFoundError):
367+ with utf8_open(join(root, key)) as data:
368+ return data.read().strip()
369+ return DEFAULTS.get(key)
370+
371+
372 def SILO_DIR(*parts):
373 """Return the directory of the currently active silo."""
374 assert env.SILONAME
375
376=== modified file 'tests/unit/test_recipe_manager.py'
377--- tests/unit/test_recipe_manager.py 2016-01-10 10:12:24 +0000
378+++ tests/unit/test_recipe_manager.py 2016-01-23 01:33:24 +0000
379@@ -95,13 +95,11 @@
380 source.assert_called_once_with(
381 'b', silo_state.series, silo_state.dest, silo_state.ppa)
382
383- @patch('citrain.recipes.manager.lp')
384 @patch('citrain.recipes.manager.Merge')
385 @patch('citrain.recipes.manager.Manual')
386 @patch('citrain.recipes.manager.Secondary')
387- def test_manager_instantiate_build_dual(self, second, man, merge, lp):
388+ def test_manager_instantiate_build_dual(self, second, man, merge):
389 """Create Secondary build objects."""
390- overlay = lp.get_ppa.return_value
391 dual = Mock()
392 dual.name = 'vivid'
393 state = Mock(
394@@ -124,11 +122,11 @@
395 'zzz_c': man.return_value,
396 })
397 self.assertEqual(sorted(second.mock_calls), [
398- call('a', dual, overlay, state.ppa, bman.builds['a']),
399+ call('a', dual, state.stable_overlay, state.ppa, bman.builds['a']),
400 ])
401 self.assertEqual(sorted(man.mock_calls), [
402- call('b', dual, overlay, state.ppa, bman.builds['b']),
403- call('c', dual, overlay, state.ppa, bman.builds['c']),
404+ call('b', dual, state.stable_overlay, state.ppa, bman.builds['b']),
405+ call('c', dual, state.stable_overlay, state.ppa, bman.builds['c']),
406 ])
407
408 @patch('citrain.recipes.manager.BuildBase')
409
410=== modified file 'tests/unit/test_script_build.py'
411--- tests/unit/test_script_build.py 2016-01-15 00:57:08 +0000
412+++ tests/unit/test_script_build.py 2016-01-23 01:33:24 +0000
413@@ -56,8 +56,7 @@
414 bman.do()
415 self.assertEqual(bman.names, {'a', 'b', 'd'})
416
417- @patch('citrain.recipes.manager.lp')
418- def test_buildmanager_instantiate_build_objects_twins(self, lp):
419+ def test_buildmanager_instantiate_build_objects_twins(self):
420 """Always add twins for all builds."""
421 sources = ['qtmir', 'qtmir-gles', 'foo']
422 silo_state = Mock(all_projects=sources, sources=sources, mps=[])
423
424=== modified file 'tests/unit/test_script_setup_citrain.py'
425--- tests/unit/test_script_setup_citrain.py 2016-01-10 10:12:24 +0000
426+++ tests/unit/test_script_setup_citrain.py 2016-01-23 01:33:24 +0000
427@@ -17,18 +17,15 @@
428 """Tests for CI Train Setup script."""
429
430 import glob
431-import json
432
433 import os
434 from os.path import join
435
436-from collections import defaultdict
437 from mock import Mock, call
438
439 from tests.unit import CITrainScriptTestCase
440
441-from cupstream2distro.utils import utf8_open
442-from cupstream2distro.settings import PPA_TEAM, read_silo_names
443+from cupstream2distro.utils import Config, utf8_open
444
445 # Unused import necessary for code coverage reporting
446 from citrain import setup_citrain
447@@ -39,33 +36,18 @@
448 """Test CI Train Setup Script."""
449 scriptname = 'setup_citrain.py'
450
451- def test_read_silo_names(self):
452- """Ensure that we can read the silo name list from disk."""
453- listfile = join(self.tempdir, 'list')
454- expected = ['ubuntu/one', 'ubuntu/two']
455- with utf8_open(listfile, 'w') as data:
456- data.write(json.dumps(expected))
457- silos, distro = read_silo_names(listfile)
458- self.assertEqual(silos, expected)
459- self.assertEqual(distro, dict(ubuntu=silos))
460-
461 def test_discover_silos(self):
462 """Ensure that we can query launchpad for existing PPAs."""
463 alpha = Mock(distribution_link='foo/ubuntu')
464 alpha.name = 'landing-000'
465 omega = Mock(distribution_link='foo/ubuntu')
466 omega.name = 'landing-999'
467- self.script.lp.people = {PPA_TEAM: Mock(ppas=[alpha, omega])}
468- self.script.SILO_NAME_LIST = defaultdict(list)
469+ self.script.lp.people = {Config['ppa.team']: Mock(ppas=[alpha, omega])}
470 self.script.SILO_NAMES_FILE = '/tmp/ci-train-silo-names'
471- self.script.discover_silos()
472- self.assertEqual(self.script.ALL_SILO_NAMES, [
473+ self.assertEqual(self.script.discover_silos(), [
474 'ubuntu/landing-000',
475 'ubuntu/landing-999',
476 ])
477- self.assertEqual(
478- self.script.SILO_NAME_LIST,
479- dict(ubuntu=self.script.ALL_SILO_NAMES))
480
481 def test_template_names(self):
482 """Ensure we can find template filenames properly."""
483@@ -128,12 +110,15 @@
484
485 def test_main(self):
486 """Tie deployment all together."""
487- self.script.discover_silos = Mock()
488+ silos = [
489+ 'ubuntu/landing-{:03d}'.format(x) for x in range(0, 10)
490+ ]
491+ self.script.discover_silos = Mock(return_value=silos)
492 self.script.setup_silo = Mock()
493 self.script.setup_job = Mock()
494 self.assertEqual(self.script.main(), 0)
495 self.assertEqual(self.script.setup_silo.mock_calls, [
496- call('ubuntu/landing-{:03d}'.format(x)) for x in range(0, 10)
497+ call(silo) for silo in silos
498 ])
499 self.assertEqual(self.script.setup_job.mock_calls, [
500 call('cyphermox-test'),
501@@ -169,7 +154,9 @@
502 yield val.format(num)
503
504 expected = sorted(gen_expected())
505- self.script.discover_silos = Mock()
506+ self.script.discover_silos = Mock(return_value=[
507+ 'ubuntu/landing-{:03d}'.format(x) for x in range(0, 10)
508+ ])
509 self.script.JOBS_DIR = self.tempdir
510 self.script.os.makedirs = os.makedirs
511 self.assertEqual(self.script.main(), 0)
512
513=== modified file 'tests/unit/test_silomanager.py'
514--- tests/unit/test_silomanager.py 2016-01-23 00:38:01 +0000
515+++ tests/unit/test_silomanager.py 2016-01-23 01:33:24 +0000
516@@ -126,10 +126,11 @@
517 def body(silo_state):
518 """Raise a little hell."""
519 raise PrepError('Boo!')
520+ PrepError.prefix = 'Bailed: '
521 self.assertEqual(stock_main(body), PrepError.code)
522 self.assertEqual(state_mock.mock_calls, [
523 call(self.tempdir, primary=True),
524- call().set_status('Assignment failed: Boo!'),
525+ call().set_status('Bailed: Boo!'),
526 ])
527
528 def test_silostate_init(self):
529@@ -159,10 +160,9 @@
530
531 @patch('cupstream2distro.silomanager.SiloState.summarize')
532 @patch('cupstream2distro.silomanager.SiloState.set_status')
533- @patch('cupstream2distro.silomanager.SiloState.validate_permissions')
534 @patch('cupstream2distro.silomanager.SiloState.find_existing_assignment')
535 @patch('cupstream2distro.silomanager.SiloState.find_first_available')
536- def test_silostate_assign(self, first, ass_new, valid, save, summ):
537+ def test_silostate_assign(self, first, ass_new, save, summ):
538 """Do something sensible during silo assignment."""
539 state = SiloState()
540 state._bileto['status'] = 'orig'
541@@ -172,37 +172,10 @@
542 state.assign('REQUEST_ID')
543 self.assertEqual(state.requestid, 'REQUEST_ID')
544 state.set_status.assert_called_once_with('Ready to build')
545- valid.assert_called_once_with()
546 ass_new.assert_called_once_with()
547 first.assert_called_once_with('blahp')
548 summ.assert_called_once_with(status=False)
549
550- @patch('cupstream2distro.silomanager.lp')
551- @patch('cupstream2distro.silomanager.glob')
552- def test_silostate_validate_permissions(self, glob_mock, lp_mock):
553- """Only allow assigning if there's lots of silos free."""
554- env.BUILD_USER_ID = 'bobru'
555- bobru = Mock(memberships_details=[
556- Mock(team_link='http://example.com/~ci-train-ppa-service'),
557- ])
558- lp_mock.people = dict(bobru=bobru)
559- self.state.validate_permissions()
560- self.assertEqual(glob_mock.mock_calls, [])
561-
562- @patch('cupstream2distro.silomanager.lp')
563- @patch('cupstream2distro.silomanager.glob')
564- @patch('cupstream2distro.silomanager.SILO_NAME_LIST', dict(ubuntu=[]))
565- def test_silostate_validate_permissions_fail(self, glob_mock, lp_mock):
566- """Only allow assigning if there's lots of silos free."""
567- env.BUILD_USER_ID = 'bobru'
568- bobru = Mock(memberships_details=[])
569- lp_mock.people = dict(bobru=bobru)
570- glob_mock.return_value = ['/path/to/foo'] * 60
571- with self.assertRaisesRegexp(PrepError, 'Low on silos'):
572- self.state.validate_permissions()
573- glob_mock.assert_called_once_with(
574- self.tempdir + '/ubuntu/*/request_id_*')
575-
576 def test_silostate_requestid(self):
577 """Ensure requestid property behaves sensible."""
578 func = 'cupstream2distro.silomanager.join'
579@@ -303,10 +276,26 @@
580 self.state.find_existing_assignment()
581 self.assertIsNone(self.state.siloname)
582
583+ @patch('cupstream2distro.silomanager.json.loads')
584+ def test_silostate_discover_ppas(self, json_mock):
585+ """Read silo name list from disk."""
586+ json_mock.return_value = ['ubuntu-rtm/001', 'ubuntu/002']
587+ self.assertEqual(SiloState.discover_ppas(), ['ubuntu/002'])
588+
589+ @patch('cupstream2distro.silomanager.json.loads')
590+ def test_silostate_discover_ppas_broken(self, json_mock):
591+ """Read silo name list from disk."""
592+ json_mock.side_effect = ValueError
593+ with self.assertRaises(PrepError):
594+ SiloState.discover_ppas('broke')
595+
596+ @patch('cupstream2distro.silomanager.SiloState.discover_ppas')
597 @patch('cupstream2distro.silomanager.os.makedirs')
598 @patch('cupstream2distro.silomanager.glob')
599- def test_silostate_find_first_available(self, glob_mock, mkdir):
600+ def test_silostate_find_first_available(self, glob_mock, mkdir, disco):
601 """Find the first unassigned silo."""
602+ disco.return_value = [
603+ 'ubuntu/landing-{:03d}'.format(x) for x in range(0, 10)]
604 glob_mock.return_value = ['/path/to/file']
605 with self.assertRaisesRegexp(PrepError, 'No silos are avail'):
606 SiloState.find_first_available()
607@@ -315,21 +304,21 @@
608 '/ubuntu/landing-{:03d}/request_id_*'.format(x))
609 for x in range(0, 10)
610 ]
611- self.assertEqual(len(expected), len(glob_mock.mock_calls))
612- for a_call in expected:
613- self.assertIn(a_call, glob_mock.mock_calls)
614+ self.assertListEqual(expected, sorted(glob_mock.mock_calls))
615 self.assertEqual(mkdir.mock_calls, [])
616
617+ @patch('cupstream2distro.silomanager.SiloState.discover_ppas')
618 @patch('cupstream2distro.silomanager.shuffle', sorted)
619 @patch('cupstream2distro.silomanager.os.makedirs')
620 @patch('cupstream2distro.silomanager.glob')
621- def test_silostate_find_first_available_found(self, glob_mock, mkdir):
622+ def test_silostate_find_first_available_found(self, glob, mkdir, disco):
623 """Find first unassgined silo with one available."""
624- glob_mock.return_value = []
625+ disco.return_value = ['ubuntu/landing-000']
626+ glob.return_value = []
627 self.assertEqual(
628 'ubuntu/landing-000',
629 SiloState.find_first_available(distribution='ubuntu'))
630- self.assertEqual(glob_mock.mock_calls, [
631+ self.assertEqual(glob.mock_calls, [
632 call(self.tempdir + '/ubuntu/landing-000/request_id_*'),
633 ])
634 mkdir.assert_called_once_with(
635@@ -474,7 +463,6 @@
636
637 @patch('cupstream2distro.silomanager.SiloState.sources', [])
638 @patch('cupstream2distro.silomanager.SiloState.requestid', '121')
639- @patch('cupstream2distro.silomanager.BILETO_API', 'example.com')
640 def test_silostate_push_to_bileto(self):
641 """Ensure that we push status messages to Bileto."""
642 env.BUILD_USER_ID = 'robru'
643@@ -485,12 +473,11 @@
644 self.assertIn('"request_id": "121"', data)
645 self.assertIn('"siloname": "ubuntu/landing-123"', data)
646 self.assertEqual(silomanager.requests.post.mock_calls, [
647- call('example.com', data=data,
648+ call('http://None:8080/v1/tickets?token=None', data=data,
649 headers={'content-type': 'application/json'}),
650 ])
651
652 @patch('cupstream2distro.silomanager.SiloState.requestid', '121')
653- @patch('cupstream2distro.silomanager.BILETO_API', 'example.com')
654 @patch('cupstream2distro.silomanager.SiloState.sources', [])
655 @patch('cupstream2distro.silomanager.requests')
656 @patch('cupstream2distro.silomanager.logging')
657@@ -505,7 +492,6 @@
658
659 @patch('cupstream2distro.silomanager.SiloState.sources', [])
660 @patch('cupstream2distro.silomanager.SiloState.requestid', '121')
661- @patch('cupstream2distro.silomanager.BILETO_API', 'example.com')
662 def test_silostate_push_to_bileto_bad_response(self):
663 """Report failures from Bileto."""
664 self.state.status = 'Packages built'
665@@ -515,7 +501,7 @@
666 self.assertIn('"status": "Packages built"', data)
667 self.assertIn('"request_id": "121"', data)
668 self.assertEqual(silomanager.requests.post.mock_calls, [
669- call('example.com', data=data,
670+ call('http://None:8080/v1/tickets?token=None', data=data,
671 headers={'content-type': 'application/json'}),
672 ])
673
674
675=== modified file 'tests/unit/test_utils.py'
676--- tests/unit/test_utils.py 2015-10-28 19:40:12 +0000
677+++ tests/unit/test_utils.py 2016-01-23 01:33:24 +0000
678@@ -23,6 +23,7 @@
679
680 from cupstream2distro.utils import (
681 SILO_DIR,
682+ Config,
683 call,
684 env,
685 log_call,
686@@ -217,3 +218,8 @@
687 for line, new in utf8_inplace(path):
688 new.write(line)
689 self.assertEqual(os.stat(path).st_mode, orig_mode)
690+
691+ def test_config(self):
692+ """Ensure that we can read config values from disk."""
693+ Config.dirs = ('/etc/',)
694+ self.assertTrue(Config['passwd'].startswith('root'))

Subscribers

People subscribed via source and target branches