Merge lp:~abentley/ci-director/remove-ci-director into lp:ci-director
- remove-ci-director
- Merge into trunk
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 |
Related bugs: |
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.
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: |
Thank you.