Merge lp:~doanac/uci-engine/lander-code-layout into lp:uci-engine

Proposed by Andy Doan
Status: Merged
Approved by: Andy Doan
Approved revision: 636
Merged at revision: 658
Proposed branch: lp:~doanac/uci-engine/lander-code-layout
Merge into: lp:uci-engine
Diff against target: 398 lines (+144/-74)
10 files modified
charms/precise/lander/charm-helpers.yaml (+1/-0)
charms/precise/lander/config.yaml (+5/-10)
charms/precise/lander/hooks/hooks.py (+78/-52)
charms/precise/lander/unit_tests/test_hooks.py (+48/-1)
ci-utils/ci_utils/amqp_utils.py (+4/-2)
juju-deployer/configs/lander_http_vhost (+1/-1)
juju-deployer/lander.yaml.tmpl (+2/-3)
lander/bin/json_status_cgi.py (+2/-2)
lander/lander/run_worker.py (+2/-2)
testing/run_tests.py (+1/-1)
To merge this branch: bzr merge lp:~doanac/uci-engine/lander-code-layout
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Francis Ginther Approve
Celso Providelo (community) Approve
Review via email: mp+225046@code.launchpad.net

Commit message

lander charm: reorganize code layout and improve install logic

This does a couple of things that were hard to do piece by piece. The
change is a bit big but:

The main goal was getting our code to be in its own directory so it
can start to be treated like read-only. We have an idea in the future
of code rollbacks, so this fits in with that model. This now deploys
code to:

 /srv/<service>/code/r<revno>_<sha1>

 and we maintain a symlink to the latest code with:

  /srv/<service>/code/current

While doing this I got rid of all the bzr-branch logic we no longer
use. This reduces complexity.

I'll also converted our tarball logic to use charmhelpers.fetch.

While changing so much, I also decided to improve our code-extraction
logic by moving it to our config-changed logic. This means that
we now have the ability to update code by just changing the payload
value of the service.

Description of the change

I originally worked on this for the restish-charm. However, thomi is landing an MP that would be broken by this:

 https://code.launchpad.net/~doanac/uci-engine/restish-code-layout/+merge/224520

While waiting, I copied the logic from that MP to give us the same ability in the lander.

This gives us the ability to update/change code for a service with simple "juju set" commands.

ev - has created an asana task that would expand upon this allowing specific units to be at specific code levels. In the interest of a somewhat readable MP, I'd like to do that as follow-up work on this.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:633
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/969/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/969/rebuild

review: Approve (continuous-integration)
Revision history for this message
Andy Doan (doanac) wrote :

gonna - make a couple of minor changes after discussion with cprov and fginther

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:634
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/972/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/972/rebuild

review: Approve (continuous-integration)
Revision history for this message
Celso Providelo (cprov) wrote :

Andy,

I am worried about the lack of abstraction to access disk-resources in lander, but that's not enough to block this change to land and experiment "upgrading" with the new code layout.

Hopefully accessing lander juju-relations and lander-logs will get clearer later.

review: Approve
Revision history for this message
Ubuntu CI Bot (uci-bot) wrote :
Download full text (124.6 KiB)

The attempt to merge lp:~doanac/uci-engine/lander-code-layout into lp:uci-engine failed. Below is the output from the failed tests.

Running cm...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
Updating source dependencies...
uploading webui-content.tgz to swift
Checking juju status
Preparing local branch upload...
Uploading local branch, fingerprint 096f5a75d81880fb5db22111f1b6b8872cff7209
Private PPAs only is DISABLED (CI_PRIVATE_PPAS_ONLY: 0)
Building charm: chroot-builder
Building charm: rabbitmq-worker
Building charm: lander
Building charm: system-image-server
Building charm: python-django
Building charm: restish
Building charm: webui
Running juju-deployer -v -c /tmp/tmpGx9DVq/deployer/branch-source-builder.yaml -c /tmp/tmpGx9DVq/deployer/gatekeeper.yaml -c /tmp/tmpGx9DVq/deployer/image-builder.yaml -c /tmp/tmpGx9DVq/deployer/lander.yaml -c /tmp/tmpGx9DVq/deployer/nf-stats-service.yaml -c /tmp/tmpGx9DVq/deployer/ppa-assigner.yaml -c /tmp/tmpGx9DVq/deployer/production-only.yaml -c /tmp/tmpGx9DVq/deployer/publisher.yaml -c /tmp/tmpGx9DVq/deployer/relations.yaml -c /tmp/tmpGx9DVq/deployer/test-runner.yaml -c /tmp/tmpGx9DVq/deployer/ticket-system.yaml -c /tmp/tmpGx9DVq/deployer/webui.yaml ci-airline
Tests running...
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testConnectFailed ... OK (0.001 secs)
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testProcessQueue ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testRunForever ... OK (0.102 secs)
ci-utils.ci_utils.tests.test_amqp.TestAMQP.testSent ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp.TestProgressTrigger.testProgress ... OK (0.001 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testCancel ... OK (0.108 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testNoQueue ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testNoTicket ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageCalledProcessError ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageFail ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageKilled ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageSimple ... OK (0.002 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestAMQPWorker.testOnMessageUnexpected ... OK (0.003 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestTimer.testCBRuns ... OK (0.021 secs)
ci-utils.ci_utils.tests.test_amqp_worker.TestTimer.testCanCancel ... OK (0.000 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreConfig.test_invalid_auth_config ... OK (0.000 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreConfig.test_valid_auth_config ... OK (0.000 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreFileName.test_get_file_name ... OK (0.000 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreFileName.test_get_file_name_same_directory ... OK (0.000 secs)
ci-utils.ci_utils.tests.test_data_store.TestDataStoreFileName.test_get...

Revision history for this message
Evan (ev) wrote :

======================================================================
FAIL: testing.test_style.TestPep8.test_pep8_conformance
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/dev/shm/tmpH5DVmw/local/lib/python2.7/site-packages/ucitests-0.1.3-py2.7.egg/ucitests/styles.py", line 84, in test_pep8_conformance
    '\n'.join(self.report._msgs))
  File "/usr/lib/python2.7/unittest/case.py", line 515, in assertEqual
    assertion_func(first, second, msg=msg)
  File "/usr/lib/python2.7/unittest/case.py", line 744, in assertListEqual
    self.assertSequenceEqual(list1, list2, msg, seq_type=list)
  File "/usr/lib/python2.7/unittest/case.py", line 726, in assertSequenceEqual
    self.fail(msg)
  File "/usr/lib/python2.7/unittest/case.py", line 412, in fail
    raise self.failureException(msg)
AssertionError: bin/../testing/run_tests.py:418:1: W293 blank line contains whitespace

Revision history for this message
Francis Ginther (fginther) wrote :

I had the same comment as Celso, these ../../../../ paths are a hard to keep straight. But I agree this isn't the place to address that.

Also, a good chunk of the code in lander/hooks/hooks.py looks like it would be used in other charms as well. Does this eventually belong in our own charm helpers? I really have no good suggestions for addressing this given how charms work other then copy/paste.

review: Approve
Revision history for this message
Andy Doan (doanac) wrote :

On 07/09/2014 11:48 AM, Francis Ginther wrote:
> Also, a good chunk of the code in lander/hooks/hooks.py looks like it would be used in other charms as well. Does this eventually belong in our own charm helpers? I really have no good suggestions for addressing this given how charms work other then copy/paste.

yeah- this sucks. we could consolidate a lot of charm code. if we had
our own charm helper.

635. By Andy Doan

merge with trunk

636. By Andy Doan

fix pep8 issue to get tarmac working again

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :

PASSED: Continuous integration, rev:636
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1033/
Executed test runs:

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/uci-engine-ci/1033/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'charms/precise/lander/charm-helpers.yaml'
2--- charms/precise/lander/charm-helpers.yaml 2014-01-30 11:47:15 +0000
3+++ charms/precise/lander/charm-helpers.yaml 2014-07-09 16:56:25 +0000
4@@ -9,3 +9,4 @@
5 include:
6 - core
7 - fetch
8+ - payload.archive
9
10=== modified file 'charms/precise/lander/config.yaml'
11--- charms/precise/lander/config.yaml 2014-05-01 08:34:49 +0000
12+++ charms/precise/lander/config.yaml 2014-07-09 16:56:25 +0000
13@@ -1,17 +1,12 @@
14 options:
15- branch:
16- type: string
17- description: "BZR branch the service lives in"
18- revno:
19- type: string
20- description: "Revision or tag to branch from"
21- tarball:
22+ current_code:
23 type: string
24 description: A URL to a tarball of the code
25- vcs:
26- default: "branch"
27+ available_code:
28 type: string
29- description: "Grab code from a bzr 'branch' or a URL to a 'tarball'"
30+ description: |
31+ A list of code deployment URLs to keep around. Any versions found locally
32+ not in this list will be removed.
33 install_root:
34 type: string
35 default: "/srv/"
36
37=== modified file 'charms/precise/lander/hooks/hooks.py'
38--- charms/precise/lander/hooks/hooks.py 2014-06-30 12:07:54 +0000
39+++ charms/precise/lander/hooks/hooks.py 2014-07-09 16:56:25 +0000
40@@ -16,8 +16,8 @@
41
42 import base64
43 import os
44+import re
45 import shutil
46-import subprocess
47 import sys
48 import textwrap
49
50@@ -47,6 +47,10 @@
51 return os.path.join(core.hookenv.config()['install_root'], unit)
52
53
54+def _code_dir(revno='current'):
55+ return os.path.join(_service_dir(), 'code/%s' % revno)
56+
57+
58 def _upstart_log_dir():
59 return os.path.join(_service_dir(), 'logs')
60
61@@ -71,56 +75,71 @@
62 fetch.apt_install(package, fatal=True)
63
64
65-def _install_from_tarball(config, upgrade):
66- juju_info('grabbing service from tarball...')
67- sdir = _service_dir()
68- if not upgrade:
69- if os.path.exists(sdir):
70- juju_info('deleting pre-existing service directory: %s' % sdir)
71- shutil.rmtree(sdir)
72- os.mkdir(sdir)
73- cmd = 'curl %s | tar -xzC %s' % (config['tarball'], sdir)
74- subprocess.check_call(cmd, shell=True)
75-
76-
77-def _install_from_bzr(config, upgrade):
78- juju_info('grabbing service from bzr...')
79-
80- sdir = _service_dir()
81- rev = config.get('revno', '')
82-
83- if upgrade:
84- args = ['bzr', 'pull']
85- if rev:
86- args.extend(['-r', rev])
87- subprocess.check_call(args, cwd=sdir)
88- return
89-
90- if os.path.exists(sdir):
91- juju_info('deleting pre-existing service directory: %s' % sdir)
92- shutil.rmtree(sdir)
93-
94- args = ['bzr', 'branch']
95- if rev:
96- args.extend(['-r', rev])
97- args.append(config['branch'])
98- args.append(sdir)
99- subprocess.check_call(args)
100-
101-
102-@hooks.hook()
103-def install(upgrade=False):
104+def _get_revno(src):
105+ revno = os.path.basename(src).split('.tgz')[0]
106+ revfile = os.path.join(src, '.uploaded_version_info')
107+ if os.path.exists(revfile):
108+ # revline will be: branch@617 - 2014-06-25 11:50:44 -0500
109+ revline = open(revfile).read()
110+ match = re.match(r'.*@(\d+)', revline)
111+ if match:
112+ revno = 'r' + match.group(1) + '_' + revno
113+ return revno
114+
115+
116+def _payload_local(payload):
117+ '''Figure out if the payload is already available locally'''
118+ name = payload.rsplit('/', 1)[1]
119+ name = name.split('.tgz')[0]
120+ for dirname in os.listdir(_code_dir('')):
121+ if dirname == 'current':
122+ continue
123+ if name == dirname.split('_', 1)[1]:
124+ return dirname
125+
126+
127+def _payload_extract(payload):
128+ juju_info('grabbing %s' % payload)
129+ src = fetch.install_remote(payload)
130+
131+ revno = _get_revno(src)
132+
133+ core.host.mkdir(_code_dir('..'))
134+
135+ # charmhelpers puts the payload in an odd place, move it to our code_dir
136+ dst = _code_dir(revno)
137+ shutil.move(src, dst)
138+
139+
140+def _payloads_not_configured(avail):
141+ '''returns a list of payloads no longer configured for the charm'''
142+ configured_dirs = [_payload_local(x) for x in avail]
143+ all_dirs = [x for x in os.listdir(_code_dir('')) if x != 'current']
144+ return set(all_dirs) - set(configured_dirs)
145+
146+
147+def handle_code():
148+ '''deploy all new payloads configured for the charm. return list of
149+ payloads to cleanup.'''
150 config = core.hookenv.config()
151- if config.get('vcs') == 'tarball':
152- _install_from_tarball(config, upgrade)
153- else:
154- _install_from_bzr(config, upgrade)
155-
156-
157-@hooks.hook()
158-def upgrade_charm():
159- install(True)
160- restart_upstart()
161+ avail = [x for x in config['available_code'].split(' ') if x.strip()]
162+ new_current = config['current_code']
163+ if new_current not in avail:
164+ raise ValueError(
165+ '"current_code" not configured to a value in "available_code"')
166+
167+ for payload in avail:
168+ if not _payload_local(payload):
169+ _payload_extract(payload)
170+
171+ current = _code_dir()
172+ if os.path.exists(current):
173+ os.unlink(current)
174+ dst = _payload_local(new_current)
175+ juju_info('linking %s as current' % dst)
176+ os.symlink(dst, current)
177+
178+ return _payloads_not_configured(avail)
179
180
181 def create_unit_config():
182@@ -147,7 +166,7 @@
183
184 env JUJU_UNIT_IDX={idx}
185
186- chdir {sdir}
187+ chdir {cdir}
188
189 # setuid/setgid don't work because we need to do mkdir as root
190 # this is the easiest work-around:
191@@ -161,7 +180,7 @@
192 ''')
193 params = {
194 'main': config['main'],
195- 'sdir': _service_dir(),
196+ 'cdir': _code_dir(),
197 'uid': config.get('uid', 'nobody'),
198 'gid': config.get('gid', 'nogroup'),
199 'idx': core.hookenv.local_unit().split('/')[1],
200@@ -184,11 +203,18 @@
201
202 @hooks.hook()
203 def config_changed():
204+ core.host.mkdir(_code_dir(''))
205+
206 add_dependencies()
207 create_unit_config()
208 create_upstart()
209+ todelete = handle_code()
210+
211 restart_upstart()
212
213+ for x in todelete:
214+ shutil.rmtree(_code_dir(x))
215+
216
217 @hooks.hook()
218 @hooks.hook('lander-relation-changed')
219
220=== removed symlink 'charms/precise/lander/hooks/install'
221=== target was u'hooks.py'
222=== removed symlink 'charms/precise/lander/hooks/upgrade-charm'
223=== target was u'hooks.py'
224=== modified file 'charms/precise/lander/unit_tests/test_hooks.py'
225--- charms/precise/lander/unit_tests/test_hooks.py 2014-06-26 20:02:54 +0000
226+++ charms/precise/lander/unit_tests/test_hooks.py 2014-07-09 16:56:25 +0000
227@@ -12,7 +12,6 @@
228
229 # You should have received a copy of the GNU Affero General Public License
230 # along with this program. If not, see <http://www.gnu.org/licenses/>.
231-import json
232 import mock
233 import os
234 import unittest
235@@ -47,6 +46,7 @@
236 'charmhelpers.core.host.mkdir',
237 'charmhelpers.fetch.apt_install',
238 'charmhelpers.fetch.configure_sources',
239+ 'charmhelpers.fetch.install_remote',
240 ]
241
242
243@@ -83,6 +83,53 @@
244 self.config.return_value['install_root'] = self.tmpdir
245
246
247+class TestInstallHook(JujuTestCase):
248+ def setUp(self):
249+ super(TestInstallHook, self).setUp()
250+ m = mock.patch('hooks._service_dir')
251+ m.start().return_value = self.tmpdir
252+ self.addCleanup(m.stop)
253+ os.mkdir(os.path.join(self.tmpdir, 'code'))
254+
255+ def test_payload_local(self):
256+ self.assertIsNone(hooks._payload_local('http://no_such_sha1.tgz'))
257+ name = 'r123_sha1hash'
258+ os.mkdir(hooks._code_dir(name))
259+ self.assertEqual(name, hooks._payload_local('http://sha1hash.tgz'))
260+
261+ def test_payload_not_configured(self):
262+ local = ['r1_aa', 'r2_bb', 'r3_cc', 'r4_dd']
263+ configured = ['http://aa.tgz', 'http://cc.tgz', 'http://dd.tgz']
264+ os.mkdir(hooks._code_dir()) # create a directory for "current"
265+ for x in local:
266+ os.mkdir(hooks._code_dir(x))
267+ not_configured = hooks._payloads_not_configured(configured)
268+ self.assertEquals(not_configured, set(['r2_bb']))
269+
270+ @mock.patch('hooks._payload_extract')
271+ def test_handle_code_noop(self, payload_extract):
272+ local = ['r1_aa', 'r2_bb']
273+ self.config.return_value['current_code'] = 'http://bb.tgz'
274+ self.config.return_value['available_code'] = '''
275+ http://aa.tgz http://bb.tgz'''
276+ for x in local:
277+ os.mkdir(hooks._code_dir(x))
278+ hooks.handle_code()
279+ self.assertEqual(0, payload_extract.call_count)
280+ self.assertEqual('r2_bb', os.readlink(hooks._code_dir()))
281+
282+ @mock.patch('hooks._payload_extract')
283+ def test_handle_code(self, payload_extract):
284+ self.config.return_value['current_code'] = 'http://bb.tgz'
285+ self.config.return_value['available_code'] = 'http://bb.tgz'
286+
287+ payload_extract.side_effect = lambda x: os.mkdir(
288+ hooks._code_dir('r2_bb'))
289+ self.assertEqual(set([]), hooks.handle_code())
290+ self.assertEqual(1, payload_extract.call_count)
291+ self.assertEqual('r2_bb', os.readlink(hooks._code_dir()))
292+
293+
294 class TestLanderHook(JujuTestCase):
295 def setUp(self):
296 super(TestLanderHook, self).setUp()
297
298=== modified file 'ci-utils/ci_utils/amqp_utils.py'
299--- ci-utils/ci_utils/amqp_utils.py 2014-06-16 13:18:53 +0000
300+++ ci-utils/ci_utils/amqp_utils.py 2014-07-09 16:56:25 +0000
301@@ -45,8 +45,10 @@
302 '''Load the rabbit config created by the charm'''
303 config = None
304 try:
305- # try and find the config file, should be in the root of our bzr branch
306- f = os.path.join(HERE, '../../amqp_config.py')
307+ f = os.path.join(HERE, '../../../../amqp_config.py')
308+ # TODO remove once all charms have converted to new code layout
309+ if not os.path.exists(f):
310+ f = os.path.join(HERE, '../../amqp_config.py')
311 if os.path.exists(f):
312 import imp
313 config = imp.load_source('amqp_config', f)
314
315=== modified file 'juju-deployer/configs/lander_http_vhost'
316--- juju-deployer/configs/lander_http_vhost 2014-06-23 19:14:23 +0000
317+++ juju-deployer/configs/lander_http_vhost 2014-07-09 16:56:25 +0000
318@@ -10,7 +10,7 @@
319 Allow from all
320 </Directory>
321
322- ScriptAlias /json_status /srv/ci-airline-lander/lander/bin/json_status_cgi.py
323+ ScriptAlias /json_status /srv/ci-airline-lander/code/current/lander/bin/json_status_cgi.py
324 <Location /json_status>
325 Options +ExecCGI
326 Allow from all
327
328=== modified file 'juju-deployer/lander.yaml.tmpl'
329--- juju-deployer/lander.yaml.tmpl 2014-06-16 13:18:53 +0000
330+++ juju-deployer/lander.yaml.tmpl 2014-07-09 16:56:25 +0000
331@@ -16,9 +16,8 @@
332 ci-airline-lander:
333 charm: lander
334 options:
335- vcs: ${CI_CODE_SOURCE}
336- branch: ${CI_BRANCH}
337- tarball: ${CI_PAYLOAD_URL}
338+ current_code: ${CI_PAYLOAD_URL}
339+ available_code: ${CI_PAYLOAD_URL}
340 sources: ${CI_PPA}
341 packages: "python-amqplib python-swiftclient lazr.enum"
342 unit-config: include-base64://configs/unit_config.yaml
343
344=== modified file 'lander/bin/json_status_cgi.py'
345--- lander/bin/json_status_cgi.py 2014-06-30 12:07:54 +0000
346+++ lander/bin/json_status_cgi.py 2014-07-09 16:56:25 +0000
347@@ -33,7 +33,7 @@
348 # Service names sometimes change, and this is hard to find and know to
349 # fix otherwise
350 service = os.path.abspath(
351- os.path.join(os.path.dirname(__file__), '../../'))
352+ os.path.join(os.path.dirname(__file__), '../../../../'))
353 service = os.path.basename(service)
354 state = subprocess.check_output(
355 ['/sbin/status', service], stderr=subprocess.STDOUT)
356@@ -47,7 +47,7 @@
357 status.add_true_false('lander worker', state, 'start/running' in state)
358 status.add_deployed_revision()
359
360-relations_dir = '../../juju-relations'
361+relations_dir = '../../../../juju-relations'
362 exist = os.path.exists(relations_dir)
363 if exist:
364 ppa = ts = False
365
366=== modified file 'lander/lander/run_worker.py'
367--- lander/lander/run_worker.py 2014-06-30 12:07:54 +0000
368+++ lander/lander/run_worker.py 2014-07-09 16:56:25 +0000
369@@ -29,14 +29,14 @@
370 from ci_utils import amqp_utils, data_store, dump_stack, unit_config
371
372 _here = os.path.abspath(os.path.dirname(__file__))
373-SERVICE_PATH = os.path.join(_here, '../../juju-relations')
374+SERVICE_PATH = os.path.join(_here, '../../../../juju-relations')
375 PROCESS_TICKET = os.path.join(_here, '../bin/lander_process_ticket.py')
376
377 logger = lander.get_logger('lander_next_ticket')
378
379
380 def _get_parser():
381- logdir = os.path.join(_here, '../../lander-logs')
382+ logdir = os.path.join(_here, '../../../../lander-logs')
383 parser = argparse.ArgumentParser(
384 description='Wraps the REST API calls with a progress queue monitor.')
385 parser.add_argument('--service-name',
386
387=== modified file 'testing/run_tests.py'
388--- testing/run_tests.py 2014-07-09 12:49:37 +0000
389+++ testing/run_tests.py 2014-07-09 16:56:25 +0000
390@@ -415,7 +415,7 @@
391 '--no-skip', action='store_true',
392 dest='no_skip',
393 help='Errors if at lest one test is skipped.')
394-
395+
396
397 def main(args, stdout, stderr):
398 # Interpret user wishes

Subscribers

People subscribed via source and target branches