Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 1 | # Copyright 2013 Rackspace Australia |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| 4 | # not use this file except in compliance with the License. You may obtain |
| 5 | # a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 12 | # License for the specific language governing permissions and limitations |
| 13 | # under the License. |
| 14 | |
| 15 | |
| 16 | import copy |
| 17 | import json |
| 18 | import logging |
| 19 | import os |
Jan Kundrát | 834733b | 2015-06-12 01:50:40 +0200 | [diff] [blame] | 20 | import tempfile |
Jan Kundrát | 2221d48 | 2014-11-28 01:54:34 +0100 | [diff] [blame] | 21 | import pkg_resources |
| 22 | import socket |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 23 | import uuid |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 24 | |
Joshua Hesketh | 6224554 | 2014-01-16 18:00:56 +1100 | [diff] [blame] | 25 | from turbo_hipster.lib import common |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 26 | from turbo_hipster.lib import utils |
| 27 | |
| 28 | |
| 29 | class Task(object): |
Joshua Hesketh | 9cd2f93 | 2014-03-05 16:49:22 +1100 | [diff] [blame] | 30 | """ A base object for running a job (aka Task) """ |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 31 | log = logging.getLogger("task") |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 32 | |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 33 | def __init__(self, worker_server, job_name, job_config): |
| 34 | # TODO(jhesketh): remove the need for worker_server here |
Joshua Hesketh | 96adb28 | 2014-03-25 16:26:45 +1100 | [diff] [blame] | 35 | self.worker_server = worker_server |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 36 | # NOTE(jhesketh): job_config may be in the old format where name |
| 37 | # refers to the plugin and function is the job name. Thus these should |
| 38 | # never be used in a job, instead use the provided job_name. |
| 39 | self.job_config = job_config |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 40 | self.job_name = job_name |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 41 | self._reset() |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 42 | |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 43 | # Define the number of steps we will do to determine our progress. |
| 44 | self.total_steps = 0 |
| 45 | |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 46 | def _cleanup(self): |
| 47 | if self.log_handler: |
| 48 | self.log.removeHandler(self.log_handler) |
| 49 | self.log_handler.flush() |
| 50 | self.log_handler.close() |
Joshua Hesketh | 91f4ff6 | 2015-02-24 16:21:24 +1100 | [diff] [blame] | 51 | if ('shutdown-th' in self.job_config and |
Joshua Hesketh | a4b178d | 2015-06-04 14:13:31 +1000 | [diff] [blame] | 52 | self.job_config['shutdown-th']): |
Joshua Hesketh | 91f4ff6 | 2015-02-24 16:21:24 +1100 | [diff] [blame] | 53 | self.worker_server.shutdown_gracefully() |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 54 | |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 55 | def _reset(self): |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 56 | self.job = None |
| 57 | self.job_arguments = None |
| 58 | self.work_data = None |
| 59 | self.cancelled = False |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 60 | self.success = True |
| 61 | self.messages = [] |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 62 | self.current_step = 0 |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 63 | self.log_handler = None |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 64 | self.th_uuid = str(uuid.uuid4())[-12:] |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 65 | |
| 66 | def _prep_working_dir(self): |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 67 | # Use the th_uuid so that if the same job is somehow taken twice from |
| 68 | # zuul we won't re-use zuul's uuid. This shouldn't happen but if it |
| 69 | # does it prevents overwriting previous results |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 70 | self.job_working_dir = os.path.join( |
| 71 | self.worker_server.config['jobs_working_dir'], |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 72 | self.th_uuid, |
| 73 | self.job_arguments['LOG_PATH'] |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 74 | ) |
| 75 | self.job_results_dir = os.path.join( |
| 76 | self.job_working_dir, |
| 77 | 'results' |
| 78 | ) |
| 79 | self.task_output_log = os.path.join( |
| 80 | self.job_results_dir, |
| 81 | 'task_output.log' |
| 82 | ) |
| 83 | |
| 84 | if not os.path.isdir(os.path.dirname(self.task_output_log)): |
| 85 | os.makedirs(os.path.dirname(self.task_output_log)) |
| 86 | |
| 87 | def _setup_task_logging(self): |
| 88 | self.log_handler = logging.FileHandler(self.task_output_log) |
| 89 | log_formatter = logging.Formatter('%(asctime)s %(message)s') |
| 90 | self.log_handler.setFormatter(log_formatter) |
| 91 | self.log.addHandler(self.log_handler) |
| 92 | self.log.setLevel(logging.DEBUG) |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 93 | |
| 94 | def start_job(self, job): |
| 95 | self._reset() |
| 96 | self.job = job |
| 97 | |
| 98 | if self.job is not None: |
| 99 | try: |
| 100 | self.job_arguments = \ |
| 101 | json.loads(self.job.arguments.decode('utf-8')) |
| 102 | self.log.debug("Got job from ZUUL %s" % self.job_arguments) |
| 103 | |
| 104 | # Send an initial WORK_DATA and WORK_STATUS packets |
| 105 | self._send_work_data() |
| 106 | |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 107 | # Prep working dirs |
| 108 | self._prep_working_dir() |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 109 | |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 110 | # Now we have working dirs we can log the job details to a file |
| 111 | self._setup_task_logging() |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 112 | |
| 113 | except Exception as e: |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 114 | # If something failed during this section we have been unable |
| 115 | # to log to file. As such raise an exception to gearman |
| 116 | self.log.exception("Failure during setup") |
| 117 | self.log.exception(e) |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 118 | if not self.cancelled: |
Joshua Hesketh | 6e20b16 | 2014-04-09 13:09:19 +1000 | [diff] [blame] | 119 | self.success = False |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 120 | self.messages.append('FAILURE during the job setup') |
| 121 | self.messages.append('Exception: %s' % e) |
| 122 | self._send_work_data() |
| 123 | self.job.sendWorkException(str(e).encode('utf-8')) |
| 124 | # No point trying the job, lets return here |
Joshua Hesketh | f4b2038 | 2015-02-24 17:35:07 +1100 | [diff] [blame] | 125 | self._send_final_results() |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 126 | return |
| 127 | |
| 128 | # From here we can log exceptions |
| 129 | try: |
| 130 | # Execute the job_steps |
| 131 | self.do_job_steps() |
| 132 | except Exception as e: |
| 133 | # Log the problem |
| 134 | if not self.cancelled: |
| 135 | self.success = False |
| 136 | self.log.exception('Something failed running the job!') |
| 137 | self.messages.append('FAILURE running the job') |
| 138 | self.messages.append('Exception: %s' % e) |
| 139 | # Don't return from here as we can continue uploading the |
| 140 | # logs |
| 141 | |
| 142 | try: |
| 143 | self._cleanup() |
| 144 | self._upload_results() |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 145 | except Exception as e: |
| 146 | # If something failed during this section we have been unable |
| 147 | # to upload the log. As such raise an exception to gearman |
| 148 | self.log.exception("Failure during cleanup and upload") |
| 149 | self.log.exception(e) |
| 150 | if not self.cancelled: |
| 151 | self.success = False |
| 152 | self.messages.append('FAILURE during cleanup and log ' |
| 153 | 'upload') |
Joshua Hesketh | 6e20b16 | 2014-04-09 13:09:19 +1000 | [diff] [blame] | 154 | self.messages.append('Exception: %s' % e) |
| 155 | self._send_work_data() |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 156 | self.job.sendWorkException(str(e).encode('utf-8')) |
Joshua Hesketh | f4b2038 | 2015-02-24 17:35:07 +1100 | [diff] [blame] | 157 | finally: |
| 158 | # Finally, send updated work data and completed packets |
| 159 | self._send_final_results() |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 160 | |
Joshua Hesketh | 38a1718 | 2014-03-05 14:19:38 +1100 | [diff] [blame] | 161 | def stop_working(self, number=None): |
| 162 | # Check the number is for this job instance (None will cancel all) |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 163 | # (makes it possible to run multiple workers with this task |
| 164 | # on this server) |
Joshua Hesketh | 38a1718 | 2014-03-05 14:19:38 +1100 | [diff] [blame] | 165 | if number is None or number == self.job.unique: |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 166 | self.log.debug("We've been asked to stop by our gearman manager") |
| 167 | self.cancelled = True |
| 168 | # TODO: Work out how to kill current step |
| 169 | |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 170 | def _get_work_data(self): |
| 171 | if self.work_data is None: |
| 172 | hostname = os.uname()[1] |
Jan Kundrát | 2221d48 | 2014-11-28 01:54:34 +0100 | [diff] [blame] | 173 | fqdn = socket.getfqdn() |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 174 | self.work_data = dict( |
| 175 | name=self.job_name, |
| 176 | number=self.job.unique, |
| 177 | manager='turbo-hipster-manager-%s' % hostname, |
| 178 | url='http://localhost', |
Jan Kundrát | 2221d48 | 2014-11-28 01:54:34 +0100 | [diff] [blame] | 179 | worker_hostname=hostname, |
| 180 | worker_fqdn=fqdn, |
| 181 | worker_program='turbo-hipster', |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 182 | ) |
Jan Kundrát | 2221d48 | 2014-11-28 01:54:34 +0100 | [diff] [blame] | 183 | try: |
| 184 | self.work_data['worker_version'] = ( |
| 185 | pkg_resources.get_distribution('turbo_hipster').version |
| 186 | ) |
| 187 | except pkg_resources.DistributionNotFound: |
| 188 | # Package isn't installed; I do not think that manually |
| 189 | # attempting to extract version in some ad-hoc manner would be |
| 190 | # worth it -> just ignore this. |
| 191 | pass |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 192 | return self.work_data |
| 193 | |
| 194 | def _send_work_data(self): |
| 195 | """ Send the WORK DATA in json format for job """ |
| 196 | self.log.debug("Send the work data response: %s" % |
| 197 | json.dumps(self._get_work_data())) |
Joshua Hesketh | 3cda79f | 2014-01-31 13:10:29 +1100 | [diff] [blame] | 198 | if self.success: |
| 199 | self.work_data['result'] = 'SUCCESS' |
| 200 | else: |
| 201 | self.work_data['result'] = '\n'.join(self.messages) |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 202 | self.job.sendWorkData(json.dumps(self._get_work_data())) |
| 203 | |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 204 | def _send_final_results(self): |
| 205 | self._send_work_data() |
| 206 | |
Joshua Hesketh | b5f99b6 | 2014-01-30 16:03:19 +1100 | [diff] [blame] | 207 | if self.success: |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 208 | self.job.sendWorkComplete( |
| 209 | json.dumps(self._get_work_data())) |
| 210 | else: |
| 211 | self.job.sendWorkFail() |
| 212 | |
Joshua Hesketh | e76a0dd | 2014-01-16 17:57:45 +1100 | [diff] [blame] | 213 | def _do_next_step(self): |
| 214 | """ Send a WORK_STATUS command to the gearman server. |
| 215 | This can provide a progress bar. """ |
| 216 | |
| 217 | # Each opportunity we should check if we need to stop |
| 218 | if self.cancelled: |
| 219 | self.work_data['result'] = "Failed: Job cancelled" |
| 220 | self.job.sendWorkStatus(self.current_step, self.total_steps) |
| 221 | self.job.sendWorkFail() |
| 222 | raise Exception('Job cancelled') |
| 223 | |
| 224 | self.current_step += 1 |
| 225 | self.job.sendWorkStatus(self.current_step, self.total_steps) |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 226 | |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 227 | def _upload_results(self): |
| 228 | """Upload the contents of the working dir either using the instructions |
| 229 | provided by zuul and/or our configuration""" |
| 230 | |
| 231 | self.log.debug("Process the resulting files (upload/push)") |
| 232 | |
Joshua Hesketh | 05baf01 | 2014-12-02 16:33:09 +1100 | [diff] [blame] | 233 | dir_list = os.listdir(self.job_results_dir) |
| 234 | path_list = [os.path.join(self.job_results_dir, i) for i in dir_list] |
| 235 | |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 236 | if 'publish_logs' in self.worker_server.config: |
Joshua Hesketh | 05baf01 | 2014-12-02 16:33:09 +1100 | [diff] [blame] | 237 | index_url = utils.push_files( |
| 238 | self.job_arguments['LOG_PATH'], path_list, |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 239 | self.worker_server.config['publish_logs']) |
| 240 | self.log.debug("Index URL found at %s" % index_url) |
| 241 | self.work_data['url'] = index_url |
| 242 | |
| 243 | if 'ZUUL_EXTRA_SWIFT_URL' in self.job_arguments: |
| 244 | # Upload to zuul's url as instructed |
| 245 | utils.zuul_swift_upload(self.job_working_dir, self.job_arguments) |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 246 | self.work_data['url'] = self.job_arguments['LOG_PATH'] |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 247 | |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 248 | |
| 249 | class ShellTask(Task): |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 250 | log = logging.getLogger("task.shell_task") |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 251 | |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 252 | def __init__(self, worker_server, job_name, job_config): |
| 253 | super(ShellTask, self).__init__(worker_server, job_name, job_config) |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 254 | # Define the number of steps we will do to determine our progress. |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 255 | self.total_steps = 5 |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 256 | |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 257 | def _reset(self): |
| 258 | super(ShellTask, self)._reset() |
| 259 | self.git_path = None |
Joshua Hesketh | c73328c | 2014-01-18 16:09:54 +1100 | [diff] [blame] | 260 | self.job_working_dir = None |
| 261 | self.shell_output_log = None |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 262 | self.git_prep_log = None |
Jan Kundrát | 834733b | 2015-06-12 01:50:40 +0200 | [diff] [blame] | 263 | self.output_summary = None |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 264 | |
Joshua Hesketh | 235b13d | 2014-01-30 15:14:04 +1100 | [diff] [blame] | 265 | def do_job_steps(self): |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 266 | self.log.info('Step 1: Setup environment') |
| 267 | self._setup_environment() |
Joshua Hesketh | c73328c | 2014-01-18 16:09:54 +1100 | [diff] [blame] | 268 | |
Joshua Hesketh | 96adb28 | 2014-03-25 16:26:45 +1100 | [diff] [blame] | 269 | self.log.info('Step 2: Checkout updates from git') |
Joshua Hesketh | 1f2d1a2 | 2014-01-30 15:41:21 +1100 | [diff] [blame] | 270 | self._grab_patchset(self.job_arguments) |
| 271 | |
Joshua Hesketh | 96adb28 | 2014-03-25 16:26:45 +1100 | [diff] [blame] | 272 | self.log.info('Step 3: Run shell script') |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 273 | self._execute_script() |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 274 | |
Joshua Hesketh | 96adb28 | 2014-03-25 16:26:45 +1100 | [diff] [blame] | 275 | self.log.info('Step 4: Analyse logs for errors') |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 276 | self._parse_and_check_results() |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 277 | |
Joshua Hesketh | aae9786 | 2014-12-02 15:19:14 +1100 | [diff] [blame] | 278 | self.log.info('Step 5: Handle the results') |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 279 | self._handle_results() |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 280 | |
Joshua Hesketh | 96adb28 | 2014-03-25 16:26:45 +1100 | [diff] [blame] | 281 | self.log.info('Step 6: Handle extra actions such as shutting down') |
| 282 | self._handle_cleanup() |
| 283 | |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 284 | @common.task_step |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 285 | def _setup_environment(self): |
| 286 | self.git_prep_log = os.path.join( |
| 287 | self.job_results_dir, |
| 288 | 'git_prep.log' |
Joshua Hesketh | 1f2d1a2 | 2014-01-30 15:41:21 +1100 | [diff] [blame] | 289 | ) |
| 290 | self.shell_output_log = os.path.join( |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 291 | self.job_results_dir, |
Joshua Hesketh | 1f2d1a2 | 2014-01-30 15:41:21 +1100 | [diff] [blame] | 292 | 'shell_output.log' |
| 293 | ) |
Jan Kundrát | 834733b | 2015-06-12 01:50:40 +0200 | [diff] [blame] | 294 | self.output_summary = tempfile.mkstemp() |
Joshua Hesketh | 9b2c312 | 2015-02-26 13:18:37 +1100 | [diff] [blame] | 295 | self.log.info('Working on node %s' % (os.uname()[1])) |
Joshua Hesketh | 1f2d1a2 | 2014-01-30 15:41:21 +1100 | [diff] [blame] | 296 | |
Joshua Hesketh | 1f2d1a2 | 2014-01-30 15:41:21 +1100 | [diff] [blame] | 297 | @common.task_step |
| 298 | def _grab_patchset(self, job_args): |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 299 | """ Checkout the reference into config['git_working_dir'] """ |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 300 | |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 301 | # TODO(jhesketh): Use the zuul cloner stuff instead :-) |
| 302 | |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 303 | self.log.debug("Grab the patchset we want to test against") |
Joshua Hesketh | 96adb28 | 2014-03-25 16:26:45 +1100 | [diff] [blame] | 304 | local_path = os.path.join(self.worker_server.config['git_working_dir'], |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 305 | self.th_uuid, job_args['ZUUL_PROJECT']) |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 306 | if not os.path.exists(local_path): |
| 307 | os.makedirs(local_path) |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 308 | |
Joshua Hesketh | bad0b1f | 2015-06-03 23:01:30 +1000 | [diff] [blame] | 309 | env = os.environ |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 310 | git_args = copy.deepcopy(job_args) |
Joshua Hesketh | bad0b1f | 2015-06-03 23:01:30 +1000 | [diff] [blame] | 311 | env.update(git_args) |
Joshua Hesketh | 9177876 | 2014-01-16 18:24:46 +1100 | [diff] [blame] | 312 | |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 313 | cmd = os.path.join( |
| 314 | os.path.join(os.path.dirname(os.path.abspath(__file__)), |
| 315 | 'gerrit-git-prep.sh') |
| 316 | ) |
Joshua Hesketh | 96adb28 | 2014-03-25 16:26:45 +1100 | [diff] [blame] | 317 | cmd += ' ' + self.worker_server.config['zuul_server']['gerrit_site'] |
Joshua Hesketh | 1c8d2df | 2014-05-05 15:48:31 +1000 | [diff] [blame] | 318 | cmd += ' ' + self.worker_server.config['zuul_server']['git_origin'] |
Joshua Hesketh | 91f4ff6 | 2015-02-24 16:21:24 +1100 | [diff] [blame] | 319 | |
| 320 | # NOTE(jhesketh): The most common problem is the git remote timing out |
| 321 | # Retry cloning multiple times before raising a failure. |
| 322 | tries = 0 |
| 323 | return_code = 1 |
| 324 | while return_code != 0: |
| 325 | tries += 1 |
Joshua Hesketh | f265245 | 2015-06-05 20:14:38 +1000 | [diff] [blame] | 326 | env.update({'GIT_CURL_VERBOSE': '1', 'GIT_TRACE': '1'}) |
Joshua Hesketh | 91f4ff6 | 2015-02-24 16:21:24 +1100 | [diff] [blame] | 327 | return_code = utils.execute_to_log(cmd, self.git_prep_log, |
Joshua Hesketh | bad0b1f | 2015-06-03 23:01:30 +1000 | [diff] [blame] | 328 | env=env, cwd=local_path) |
| 329 | if tries == 2: |
Joshua Hesketh | 5ef6227 | 2015-05-01 11:59:36 +1000 | [diff] [blame] | 330 | # Try upping the post buffer. See: |
| 331 | # http://stackoverflow.com/questions/6842687/ |
| 332 | # the-remote-end-hung-up-unexpectedly-while-git-cloning |
| 333 | utils.execute_to_log( |
| 334 | "git config --global http.postBuffer 1048576000", |
Joshua Hesketh | bad0b1f | 2015-06-03 23:01:30 +1000 | [diff] [blame] | 335 | self.git_prep_log, env=env, cwd=local_path) |
| 336 | if tries >= 4: |
Joshua Hesketh | 91f4ff6 | 2015-02-24 16:21:24 +1100 | [diff] [blame] | 337 | break |
| 338 | if return_code != 0: |
Joshua Hesketh | 9b2c312 | 2015-02-26 13:18:37 +1100 | [diff] [blame] | 339 | cmd = 'ifconfig' |
| 340 | utils.execute_to_log(cmd, self.git_prep_log) |
Joshua Hesketh | 91f4ff6 | 2015-02-24 16:21:24 +1100 | [diff] [blame] | 341 | raise Exception("Failed to fetch patchset") |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 342 | self.git_path = local_path |
| 343 | return local_path |
| 344 | |
| 345 | @common.task_step |
| 346 | def _execute_script(self): |
| 347 | # Run script |
Joshua Hesketh | 62721fb | 2014-12-02 13:22:10 +1100 | [diff] [blame] | 348 | cmd = self.job_config['shell_script'] |
Joshua Hesketh | c73328c | 2014-01-18 16:09:54 +1100 | [diff] [blame] | 349 | cmd += ( |
| 350 | (' %(git_path)s %(job_working_dir)s %(unique_id)s') |
| 351 | % { |
| 352 | 'git_path': self.git_path, |
| 353 | 'job_working_dir': self.job_working_dir, |
| 354 | 'unique_id': self.job.unique |
| 355 | } |
| 356 | ) |
Jan Kundrát | 977a654 | 2014-11-27 23:16:36 +0100 | [diff] [blame] | 357 | |
| 358 | env_args = copy.deepcopy(os.environ) |
| 359 | env_args.update(self.job_arguments) |
Jan Kundrát | 3e3deef | 2014-11-28 02:20:12 +0100 | [diff] [blame] | 360 | if self.job.name.startswith('build:'): |
| 361 | env_args['TH_JOB_NAME'] = self.job.name[len('build:'):] |
| 362 | else: |
| 363 | env_args['TH_JOB_NAME'] = self.job.name |
Jan Kundrát | 834733b | 2015-06-12 01:50:40 +0200 | [diff] [blame] | 364 | env_args['TH_RESULT_FILE'] = self.output_summary[1] |
Jan Kundrát | 977a654 | 2014-11-27 23:16:36 +0100 | [diff] [blame] | 365 | |
Joshua Hesketh | c73328c | 2014-01-18 16:09:54 +1100 | [diff] [blame] | 366 | self.script_return_code = utils.execute_to_log( |
| 367 | cmd, |
Jan Kundrát | 977a654 | 2014-11-27 23:16:36 +0100 | [diff] [blame] | 368 | self.shell_output_log, |
| 369 | env=env_args |
Joshua Hesketh | c73328c | 2014-01-18 16:09:54 +1100 | [diff] [blame] | 370 | ) |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 371 | |
| 372 | @common.task_step |
| 373 | def _parse_and_check_results(self): |
| 374 | if self.script_return_code > 0: |
| 375 | self.success = False |
Jan Kundrát | 834733b | 2015-06-12 01:50:40 +0200 | [diff] [blame] | 376 | with os.fdopen(self.output_summary[0]) as fp: |
| 377 | line = fp.readline().strip() |
| 378 | if len(line) and not line.startswith('SUCCESS'): |
| 379 | self.messages.append(line) |
Joshua Hesketh | 81f87ed | 2014-01-18 15:24:48 +1100 | [diff] [blame] | 380 | self.messages.append('Return code from test script was non-zero ' |
| 381 | '(%d)' % self.script_return_code) |
| 382 | |
| 383 | @common.task_step |
Joshua Hesketh | 96adb28 | 2014-03-25 16:26:45 +1100 | [diff] [blame] | 384 | def _handle_cleanup(self): |
| 385 | """Handle and cleanup functions. Shutdown if requested to so that no |
| 386 | further jobs are ran if the environment is dirty.""" |
Jan Kundrát | 834733b | 2015-06-12 01:50:40 +0200 | [diff] [blame] | 387 | |
| 388 | try: |
| 389 | os.remove(self.output_summary[1]) |
| 390 | except OSError: |
| 391 | pass |
Joshua Hesketh | d5d7a21 | 2014-10-29 17:42:59 +1100 | [diff] [blame] | 392 | |
| 393 | @common.task_step |
| 394 | def _handle_results(self): |
| 395 | pass |