Merge lp:~abentley/ci-director/remove-ci-director into lp:ci-director

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 191
Proposed branch: lp:~abentley/ci-director/remove-ci-director
Merge into: lp:ci-director
Diff against target: 430 lines (+0/-347)
2 files modified
cidirector/cidirector.py (+0/-91)
cidirector/tests/test_cidirector.py (+0/-256)
To merge this branch: bzr merge lp:~abentley/ci-director/remove-ci-director
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+316256@code.launchpad.net

Commit message

Start removing obsolete ci-director functionality.

Description of the change

This branch begins removing obsolete functionality from CI Director.

Now that cidirector.py is no longer being run, only functionality imported by start_builds.py or update_outcomes.py is still needed.

This branch removes the execute bit, the shebang, and the symlink, since ci-director will no longer be executible.

It removes main(), build_revision(), schedule_builds(), and their associated tests.

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed symlink 'ci-director'
2=== target was u'cidirector/cidirector.py'
3=== modified file 'cidirector/cidirector.py' (properties changed: +x to -x)
4--- cidirector/cidirector.py 2017-01-23 15:37:35 +0000
5+++ cidirector/cidirector.py 2017-02-02 17:57:29 +0000
6@@ -1,4 +1,3 @@
7-#!/usr/bin/env python
8 from argparse import ArgumentParser
9 from collections import namedtuple
10 from contextlib import contextmanager
11@@ -15,8 +14,6 @@
12 import re
13 import shlex
14 from smtplib import SMTP
15-from socket import setdefaulttimeout
16-import sys
17 from time import sleep
18 import urllib2
19
20@@ -47,11 +44,9 @@
21 PENDING,
22 SUCCEEDED,
23 StateFile,
24- StateFileInUse,
25 TERMINAL_STATUS,
26 )
27 from utility import (
28- log_exceptions,
29 S3Storage,
30 )
31
32@@ -865,61 +860,6 @@
33 active.extend(item['task']['name'] for item in queued)
34 return active
35
36- def schedule_builds(self, server_info):
37- """Request builds for appropriate jobs.
38-
39- Can schedule BuildRevisionJob, ResourcefulJob, RecordResultJob.
40- :param server_info: ServerInfo for the current Jenkins.
41- """
42- self.adopt_builds(server_info)
43- active_jobs = set(self.get_active_jobs(server_info))
44- self.logger.info('Active jobs: %s', ', '.join(sorted(active_jobs)))
45- candidate_jobs = []
46- self.update_builds()
47- judge = ResultJudge(self.logger, self.state_file, self.jenkins)
48- all_results_ready = judge.finalize(self.mailer)
49- job = RecordResultJob(
50- self.logger, self.state_file, self.jenkins, self.mailer)
51- judged = job.maybe_build()
52- if all_results_ready or judged:
53- self.state_file.finish_revision(datetime.utcnow())
54- # Disabled in favour of update-outcomes.
55- # self.state_file.build_info_to_s3()
56- resourcefuljobs = ResourcefulJob.get_available_jobs(
57- server_info, self.jenkins, self.state_file, self.logger)
58- for r_job in resourcefuljobs:
59- candidate_jobs.append(r_job)
60- candidate_jobs.append(BuildRevisionJob(
61- self.repo_path, self.branches, self.ignore_branches, self.logger,
62- self.state_file, self.jenkins))
63- for job in candidate_jobs:
64- if job.maybe_build(active_jobs):
65- # We create a new set so that tests that access the parameters
66- # of maybe_build don't get mutated results.
67- active_jobs = active_jobs.union({job.job_id})
68- if should_update_health(self.jenkins, active_jobs):
69- jobs = list_cloud_health(server_info, self.jenkins, self.logger)
70- self.update_cloud_health(jobs)
71-
72- def adopt_builds(self, server_info):
73- """Adopt any builds for the build-revision not already recorded."""
74- # We happen to know that branch will not be used here.
75- job_instances = [BuildRevisionJob(self.repo_path, None, None,
76- self.logger, self.state_file, self.jenkins)]
77- job_instances.extend(
78- Job.make_subclass(si_job['name'], self.logger, self.state_file,
79- self.jenkins)
80- for si_job in server_info.info['jobs'])
81- for job in job_instances:
82- if job is None:
83- continue
84- job_info = JobInfo.from_jenkins(self.jenkins, job.job_id)
85- if job_info.current_build_number is None:
86- continue
87- build_info = self.jenkins.get_build_info(
88- job.job_id, job_info.current_build_number)
89- job.adopt_build(build_info)
90-
91 def update_builds(self):
92 """Record the status of jobs that are not currently building."""
93 self.logger.info('Updating job outcomes.')
94@@ -958,19 +898,6 @@
95 version, job, status, info['timestamp'], info['duration'])
96
97
98-def build_revision(branches, ignore_branches):
99- """Top-level logic for building jobs.
100-
101- For a supplied list of branches, it
102- - Uses the config to access Jenkins, and loads the current state.
103- - Runs jobs using CIDirector.
104- - Saves the updated state.
105- """
106- with CIDirector.stateful(branches, ignore_branches) as director:
107- server_info = ServerInfo(director.jenkins.get_info())
108- director.schedule_builds(server_info)
109-
110-
111 def list_cloud_health(si, jenkins, logger):
112 """Iterate through CloudHealthJobs for every relevent job on Jenkins."""
113 for job in si.info['jobs']:
114@@ -1028,21 +955,3 @@
115 root_logger.addHandler(s_handler)
116 if verbose:
117 root_logger.setLevel(logging.INFO)
118-
119-
120-def main(argv=None):
121- setdefaulttimeout(30)
122- args = get_arg_parser().parse_args(argv)
123- setup_logging(args.log_path, args.log_count, args.verbose)
124- logger = logging.getLogger('cidirector')
125- with log_exceptions(logger) as result:
126- try:
127- build_revision(args.branch, args.ignore)
128- except StateFileInUse:
129- logger.warning('State file already in use.')
130- return 1
131- return result.exit_status
132-
133-
134-if __name__ == '__main__':
135- sys.exit(main())
136
137=== modified file 'cidirector/tests/test_cidirector.py'
138--- cidirector/tests/test_cidirector.py 2017-01-23 15:37:35 +0000
139+++ cidirector/tests/test_cidirector.py 2017-02-02 17:57:29 +0000
140@@ -6,7 +6,6 @@
141 )
142 import logging
143 import os
144-import sys
145 from StringIO import StringIO
146 from textwrap import dedent
147 from unittest import TestCase
148@@ -30,7 +29,6 @@
149 get_buildvars_file,
150 Job,
151 JobInfo,
152- main,
153 Mailer,
154 NoSuchBranch,
155 list_cloud_health,
156@@ -306,154 +304,6 @@
157 self.assertEqual(['foo', 'bar', 'baz', 'qux'],
158 director.get_active_jobs(si))
159
160- def make_schedule_builds_director(self, branches, active=True,
161- published=True, server_info=None):
162- state_file = StateFile()
163- state_file.start_revision('a', 1, 'b', '1.6', 2)
164- if published:
165- state_file.publication_job().update_from_build_result('SUCCESS')
166- version = 2 if active else 3
167- jenkins_dict = {
168- PUBLISH_REVISION: {
169- 'build_number': 5,
170- 'description': '[ci-director]\n requires: build-revision'},
171- REVISION_RESULTS: {'build_number': 1},
172- 'aws-deploy': {
173- 'build_number': 5,
174- 'description': '[ci-director]\n requires: build-revision'},
175- }
176- if server_info is not None:
177- for entry in server_info.info['jobs']:
178- jenkins_dict.setdefault(entry['name'], {
179- 'build_number': 277,
180- })
181- jenkins = FakeJenkins(jenkins_dict, current_version=version)
182- if not active:
183- jenkins.set_build_result(BUILD_REVISION, 2, 'SUCCESS')
184- return CIDirector(jenkins, state_file, branches,
185- repo_path='hubgit.com/mumu/mumu')
186-
187- def test_schedule_builds_uses_get_active_jobs(self):
188- director = CIDirector(FakeJenkins({
189- REVISION_RESULTS: {'build_number': 2}
190- }, current_version=5), StateFile())
191- server_info = make_server_info('idle')
192- director.state_file.start_revision('a', 54, 'b', '1.25', 5)
193- # get_determine_result calls get_available_jobs, but doesn't count
194- # because it doesn't run those jobs.
195- with patch.object(
196- ResultJudge, 'get_candidate_determine_result_jobs',
197- return_value=[]):
198- with patch.object(
199- director, 'get_active_jobs',
200- return_value=[BUILD_REVISION]) as gaj_mock:
201- with patch.object(director.state_file, 'build_info_to_s3'):
202- director.schedule_builds(server_info)
203- gaj_mock.assert_called_with(server_info)
204-
205- def test_schedule_builds_respects_branch_order(self):
206- bar_url = 'gitbranch:bar:hubgit.com/mumu/mumu'
207- foo_url = 'gitbranch:foo:hubgit.com/mumu/mumu'
208- director = self.make_schedule_builds_director([bar_url, foo_url])
209- ls_remote_out = dedent("""\
210- rev-1\trefs/heads/bar
211- rev-2\trefs/heads/foo
212- """)
213- with patch('subprocess.check_output', return_value=ls_remote_out):
214- director.schedule_builds(make_server_info('idle'))
215- self.assertEqual(
216- director.jenkins.calls['build_job'],
217- [((BUILD_REVISION, {
218- 'branch': bar_url, 'revision': 'rev-1'
219- }), {})])
220- director = self.make_schedule_builds_director([foo_url, bar_url])
221- with patch('subprocess.check_output', return_value=ls_remote_out):
222- director.schedule_builds(make_server_info('idle'))
223- self.assertEqual(
224- director.jenkins.calls['build_job'],
225- [((BUILD_REVISION, {
226- 'branch': foo_url, 'revision': 'rev-2'
227- }), {})])
228-
229- def test_schedule_builds_updates_builds(self):
230- director = self.make_schedule_builds_director([])
231- with patch.object(director, 'update_builds') as ub_mock:
232- with patch('subprocess.check_output'):
233- director.schedule_builds(make_server_info('idle'))
234- ub_mock.assert_called_with()
235-
236- def test_schedule_builds_records_results(self):
237- director = self.make_schedule_builds_director([])
238- with patch.object(RecordResultJob, 'maybe_build') as mb_mock:
239- with patch.object(director.state_file, 'build_info_to_s3'):
240- with patch('subprocess.check_output'):
241- director.schedule_builds(make_server_info('idle'))
242- mb_mock.assert_called_with()
243-
244- def test_schedule_builds_publishes(self):
245- director = self.make_schedule_builds_director([], active=True)
246- with patch.object(ResourcefulJob, 'maybe_build') as mb_mock:
247- si = make_server_info('idle', buildable=[PUBLISH_REVISION])
248- director.schedule_builds(si)
249- mb_mock.assert_called_with(set())
250-
251- def test_schedule_builds_inactive_not_publishes(self):
252- director = self.make_schedule_builds_director([], active=False)
253- with patch.object(ResourcefulJob, 'maybe_build') as mb_mock:
254- with patch('subprocess.check_output'):
255- director.schedule_builds(make_server_info('idle'))
256- self.assertEqual(mb_mock.call_count, 0)
257-
258- def test_schedule_builds_schedules_resourceful_tests_unique(self):
259- server_info = make_server_info('idle', buildable=['package-foo'])
260- director = self.make_schedule_builds_director(
261- [], server_info=server_info)
262- description = "[ci-director]\n requires: build-revision"
263- director.jenkins.job_info['package-foo']['description'] = description
264- with patch.object(ResourcefulJob, 'maybe_build') as mb_mock:
265- director.schedule_builds(server_info)
266- mb_mock.assert_called_once_with(set())
267-
268- def test_schedule_builds_adopts_builds(self):
269- director = self.make_schedule_builds_director([])
270- server_info = make_server_info('idle')
271- with patch.object(director, 'adopt_builds') as ab_mock:
272- with patch('subprocess.check_output'):
273- director.schedule_builds(server_info)
274- ab_mock.assert_called_with(server_info)
275-
276- def test_schedule_builds_calls_send_final_mail(self):
277- director = self.make_schedule_builds_director([])
278- server_info = make_server_info('idle')
279- with patch.object(ResultJudge, 'finalize') as finalize_mock:
280- with patch.object(director.state_file, 'build_info_to_s3'):
281- with patch('subprocess.check_output'):
282- director.schedule_builds(server_info)
283- finalize_mock.assert_called_with(director.mailer)
284-
285- def test_schedule_builds_no_write_results_to_s3_without_final_data(self):
286- director = self.make_schedule_builds_director([])
287- with patch.object(ResultJudge, 'finalize', return_value=False):
288- with patch.object(RecordResultJob, 'maybe_build',
289- return_value=False):
290- with patch.object(director.state_file,
291- 'build_info_to_s3') as s3_mock:
292- with patch('subprocess.check_output'):
293- director.schedule_builds(make_server_info('idle'))
294- self.assertEqual(0, s3_mock.call_count)
295-
296- def test_schedule_builds_completes_unfinished_tests_with_new_rev(self):
297- # A test suite is finished even when a new revision is available.
298- server_info = make_server_info(
299- 'idle', buildable=['aws-deploy'])
300- director = self.make_schedule_builds_director(
301- ['branch-url'], server_info=server_info, published=True)
302- director.schedule_builds(server_info)
303- # build_job is not called for the build-revision job.
304- self.assertEqual(
305- [(('aws-deploy', {'revision_build': 2}), {})],
306- director.jenkins.calls['build_job'])
307-
308 def test_from_config(self):
309 director = CIDirector.from_config({
310 'jenkins_url': 'http://192.168.1.1:8080',
311@@ -646,57 +496,6 @@
312 director.update_builds()
313 self.assertEqual(sfj.get_status(), SUCCEEDED)
314
315- def test_adopt_builds(self):
316- jenkins = FakeJenkins({
317- 'foo-deploy': {
318- 'build_number': 5,
319- 'description': '[ci-director]\n requires: build-revision'},
320- BuildRevisionJob.job_id: {'build_number': 2},
321- })
322- sf = self.get_state_file()
323- director = CIDirector(jenkins, sf)
324- with patch.object(ResourcefulJob, 'adopt_build') as ab_mock:
325- director.adopt_builds(make_server_info('idle'))
326- self.assertEqual(ab_mock.call_count, 0)
327- director.adopt_builds(
328- make_server_info(
329- 'idle', buildable=['foo-deploy']))
330- ab_mock.assert_called_with({
331- 'result': 'SUCCESS',
332- 'number': 5,
333- 'building': False,
334- 'artifacts': [],
335- 'timestamp': 0,
336- 'url': 'fakettp://fake.fake/job/foo-deploy/5'
337- })
338-
339- def test_adopt_builds_unbuilt(self):
340- info = ServerInfo({'jobs': [{
341- 'name': 'aws-deploy',
342- }]})
343- jenkins = FakeJenkins({
344- BuildRevisionJob.job_id: {'build_number': 2},
345- 'aws-deploy': {
346- 'build_number': 2,
347- 'description': '[ci-director]\n requires: build-revision',
348- 'lastBuild': None}
349- })
350- jenkins.job_info['aws-deploy'] = {'lastBuild': None}
351- sf = StateFile()
352- director = CIDirector(jenkins, sf)
353- director.adopt_builds(info)
354- self.assertEqual(
355- [c[0][0] for c in jenkins.calls['get_build_info']],
356- ['build-revision'])
357- jenkins.job_info['aws-deploy'] = {
358- 'lastBuild': {'number': 1},
359- 'description': '[ci-director]\n requires: build-revision'}
360- jenkins.build_info['aws-deploy'] = {1: {}}
361- director.adopt_builds(info)
362- self.assertEqual(
363- [c[0][0] for c in jenkins.calls['get_build_info']],
364- ['build-revision', 'build-revision', 'aws-deploy'])
365-
366 def update_cloud_health_check(self, result, status):
367 foo = CloudHealthJob(name='foo', substrate='bar', last_completed=4)
368 jenkins = make_cloud_health_jenkins([foo])
369@@ -2295,61 +2094,6 @@
370 })
371
372
373-class TestMain(TestCase):
374-
375- def setUp(self):
376- self.logger = logging.getLogger()
377- self.orig_handlers = self.logger.handlers
378- self.logger.handlers = []
379- self.orig_level = self.logger.level
380-
381- def tearDown(self):
382- self.logger.handlers = self.orig_handlers
383- self.logger.level = self.orig_level
384-
385- def test_main_default(self):
386- with patch('cidirector.cidirector.build_revision',
387- autospec=True) as br_mock:
388- with patch('cidirector.cidirector.RotatingFileHandler'
389- ) as rfh_mock:
390- ret = main(['b1', 'b2'])
391- self.assertEqual(ret, 0)
392- br_mock.assert_called_with(['b1', 'b2'], [])
393- self.assertEqual(1, br_mock.call_count)
394- root_logger = logging.getLogger()
395- self.assertEqual(logging.WARNING, root_logger.level)
396- rfh_mock.assert_called_once_with(
397- 'ci-director.log', backupCount=2, maxBytes=1024 * 1024)
398- self.assertIs(sys.stderr, root_logger.handlers[1].stream)
399-
400- def test_main_logging_config(self):
401- with patch('cidirector.cidirector.build_revision'):
402- with temp_dir() as log_dir:
403- log_path = '{}/ci-director.log'.format(log_dir)
404- ret = main(['-v', '--log-path', log_path, '--log-count',
405- '5', 'b1'])
406- self.assertEqual(ret, 0)
407- root_logger = logging.getLogger()
408- self.assertEqual(logging.INFO, root_logger.level)
409- self.assertEqual(5, root_logger.handlers[0].backupCount)
410- self.assertEqual(log_path, root_logger.handlers[0].baseFilename)
411-
412- def test_main_logs_exception(self):
413- with test_logger() as (log, log_stream):
414- with patch('cidirector.cidirector.setup_logging', autospec=True):
415- with patch('cidirector.cidirector.build_revision',
416- side_effect=ValueError("mishap")):
417- ret = main(["branch"])
418- self.assertEqual(ret, 1)
419- log_pattern = (
420- "(?s)\\Aexception during build revision:\n"
421- "Traceback \\(most recent call last\\):\n"
422- ".*\n"
423- "ValueError: mishap\n\\Z"
424- )
425- self.assertRegexpMatches(log_stream.getvalue(), log_pattern)
426-
427-
428 def make_description(tags=None, section='ci-director'):
429 lines = ['[{}]\n'.format(section)]
430 if tags is not None:

Subscribers

People subscribed via source and target branches