Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2015 The LUCI Authors. All rights reserved. | 1 # Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 """Abstract stream interface for representing recipe runs. | 5 """Abstract stream interface for representing recipe runs. |
| 6 | 6 |
| 7 We need to create streams for steps (and substeps) and also LOG_LINE steps. | 7 We need to create streams for steps (and substeps) and also LOG_LINE steps. |
| 8 LogDog will implement LOG_LINE steps as real logs (i.e. uniformly), but | 8 LogDog will implement LOG_LINE steps as real logs (i.e. uniformly), but |
| 9 annotations will implement them differently from normal logs, so we need | 9 annotations will implement them differently from normal logs, so we need |
| 10 a way to distinguish. | 10 a way to distinguish. |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 86 raise NotImplementedError() | 86 raise NotImplementedError() |
| 87 | 87 |
| 88 def set_build_property(self, key, value): | 88 def set_build_property(self, key, value): |
| 89 raise NotImplementedError() | 89 raise NotImplementedError() |
| 90 | 90 |
| 91 def trigger(self, trigger_spec): | 91 def trigger(self, trigger_spec): |
| 92 raise NotImplementedError() | 92 raise NotImplementedError() |
| 93 | 93 |
| 94 def make_step_stream(self, name, **kwargs): | 94 def make_step_stream(self, name, **kwargs): |
| 95 """Shorthand for creating a step stream from a step configuration dict.""" | 95 """Shorthand for creating a step stream from a step configuration dict.""" |
| 96 assert '\n' not in name, 'Stream name must not contain a newline.' | |
| 96 kwargs['name'] = name | 97 kwargs['name'] = name |
| 97 return self.new_step_stream(recipe_api.StepConfig.create(**kwargs)) | 98 return self.new_step_stream(recipe_api.StepConfig.create(**kwargs)) |
| 98 | 99 |
| 99 def new_step_stream(self, step_config): | 100 def new_step_stream(self, step_config): |
| 100 """Creates a new StepStream in this engine. | 101 """Creates a new StepStream in this engine. |
| 101 | 102 |
| 102 The step will be considered started at the moment this method is called. | 103 The step will be considered started at the moment this method is called. |
| 103 | 104 |
| 104 TODO(luqui): allow_subannotations is a bit of a hack, whether to allow | 105 TODO(luqui): allow_subannotations is a bit of a hack, whether to allow |
| 105 annotations that this step emits through to the annotator (True), or | 106 annotations that this step emits through to the annotator (True), or |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 class StepStream(StreamEngine.StepStream): | 308 class StepStream(StreamEngine.StepStream): |
| 308 def __init__(self, engine, step_name): | 309 def __init__(self, engine, step_name): |
| 309 super(StreamEngineInvariants.StepStream, self).__init__() | 310 super(StreamEngineInvariants.StepStream, self).__init__() |
| 310 self._engine = engine | 311 self._engine = engine |
| 311 self._step_name = step_name | 312 self._step_name = step_name |
| 312 self._open = True | 313 self._open = True |
| 313 self._logs = {} | 314 self._logs = {} |
| 314 self._status = 'SUCCESS' | 315 self._status = 'SUCCESS' |
| 315 | 316 |
| 316 def write_line(self, line): | 317 def write_line(self, line): |
| 317 assert '\n' not in line | 318 assert '\n' not in line, 'Individual line must not contain a newline.' |
| 318 assert self._open | 319 assert self._open |
| 319 | 320 |
| 320 def close(self): | 321 def close(self): |
| 321 assert self._open | 322 assert self._open |
| 322 for log_name, log in self._logs.iteritems(): | 323 for log_name, log in self._logs.iteritems(): |
| 323 assert not log._open, 'Log %s still open when closing step %s' % ( | 324 assert not log._open, 'Log %s still open when closing step %s' % ( |
| 324 log_name, self._step_name) | 325 log_name, self._step_name) |
| 325 self._open = False | 326 self._open = False |
| 326 | 327 |
| 327 def new_log_stream(self, log_name): | 328 def new_log_stream(self, log_name): |
| 328 assert self._open | 329 assert self._open |
| 329 assert log_name not in self._logs, 'Log %s already exists in step %s' % ( | 330 assert log_name not in self._logs, 'Log %s already exists in step %s' % ( |
| 330 log_name, self._step_name) | 331 log_name, self._step_name) |
| 332 assert '\n' not in log_name, 'Log name must not contain a newline.' | |
| 331 ret = self._engine.LogStream(self, log_name) | 333 ret = self._engine.LogStream(self, log_name) |
| 332 self._logs[log_name] = ret | 334 self._logs[log_name] = ret |
| 333 return ret | 335 return ret |
| 334 | 336 |
| 335 def add_step_text(self, text): | 337 def add_step_text(self, text): |
| 336 pass | 338 assert '\n' not in text, 'Step text must not contain a newline.' |
| 337 | 339 |
| 338 def add_step_summary_text(self, text): | 340 def add_step_summary_text(self, text): |
| 339 pass | 341 assert '\n' not in text, 'Step summary text must not contain a newline.' |
| 340 | 342 |
| 341 def add_step_link(self, name, url): | 343 def add_step_link(self, name, url): |
| 342 assert isinstance(name, basestring), 'Link name %s is not a string' % name | 344 assert isinstance(name, basestring), 'Link name %s is not a string' % name |
| 343 assert isinstance(url, basestring), 'Link url %s is not a string' % url | 345 assert '\n' not in name, 'Link name must not contain a newline.' |
| 346 assert isinstance(url, basestring), 'Link URL %s is not a string' % url | |
| 347 assert '\n' not in url, 'Link URL must not contain a newline.' | |
|
iannucci
2017/04/05 02:40:23
Since these values are user-controlled (i.e. they
dnj
2017/04/05 15:08:07
Done.
| |
| 344 | 348 |
| 345 def set_step_status(self, status): | 349 def set_step_status(self, status): |
| 346 assert status in ('SUCCESS', 'WARNING', 'FAILURE', 'EXCEPTION') | 350 assert status in ('SUCCESS', 'WARNING', 'FAILURE', 'EXCEPTION') |
| 347 if status == 'SUCCESS': | 351 if status == 'SUCCESS': |
| 348 # A constraint imposed by the annotations implementation | 352 # A constraint imposed by the annotations implementation |
| 349 assert self._status == 'SUCCESS', ( | 353 assert self._status == 'SUCCESS', ( |
| 350 'Cannot set successful status after status is %s' % self._status) | 354 'Cannot set successful status after status is %s' % self._status) |
| 351 self._status = status | 355 self._status = status |
| 352 | 356 |
| 353 def set_build_property(self, key, value): | 357 def set_build_property(self, key, value): |
| 354 pass | 358 assert '\n' not in key, 'Property key must not contain a newline.' |
| 359 assert '\n' not in value, 'Property value must not contain a newline.' | |
| 360 json.loads(value) # value must be a valud JSON object. | |
|
iannucci
2017/04/05 02:40:23
nit: valud
dnj
2017/04/05 15:08:07
Done.
| |
| 355 | 361 |
| 356 def trigger(self, spec): | 362 def trigger(self, spec): |
| 357 assert '\n' not in spec # Spec must fit on one line. | 363 assert '\n' not in spec, 'Spec must not contain a newline.' |
| 358 json.loads(spec) # Spec must be a valid json object. | 364 json.loads(spec) # Spec must be a valid JSON object. |
| 359 | 365 |
| 360 class LogStream(StreamEngine.Stream): | 366 class LogStream(StreamEngine.Stream): |
| 361 def __init__(self, step_stream, log_name): | 367 def __init__(self, step_stream, log_name): |
| 362 self._step_stream = step_stream | 368 self._step_stream = step_stream |
| 363 self._log_name = log_name | 369 self._log_name = log_name |
| 364 self._open = True | 370 self._open = True |
| 365 | 371 |
| 366 def write_line(self, line): | 372 def write_line(self, line): |
| 367 assert '\n' not in line | 373 assert '\n' not in line, 'Inividual line must not contain a newline.' |
| 368 assert self._step_stream._open | 374 assert self._step_stream._open |
| 369 assert self._open | 375 assert self._open |
| 370 | 376 |
| 371 def close(self): | 377 def close(self): |
| 372 assert self._step_stream._open | 378 assert self._step_stream._open |
| 373 assert self._open | 379 assert self._open |
| 374 self._open = False | 380 self._open = False |
| 375 | 381 |
| 376 def new_step_stream(self, step_config): | 382 def new_step_stream(self, step_config): |
| 377 assert step_config.name not in self._streams, ( | 383 assert step_config.name not in self._streams, ( |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 530 string (which means it might not be valid ascii), we decode the string with | 536 string (which means it might not be valid ascii), we decode the string with |
| 531 the 'replace' error mode, which replaces invalid characters with a suitable | 537 the 'replace' error mode, which replaces invalid characters with a suitable |
| 532 replacement character. | 538 replacement character. |
| 533 """ | 539 """ |
| 534 try: | 540 try: |
| 535 return str(s) | 541 return str(s) |
| 536 except UnicodeEncodeError: | 542 except UnicodeEncodeError: |
| 537 return s.encode('utf-8', 'replace') | 543 return s.encode('utf-8', 'replace') |
| 538 except UnicodeDecodeError: | 544 except UnicodeDecodeError: |
| 539 return s.decode('utf-8', 'replace') | 545 return s.decode('utf-8', 'replace') |
| OLD | NEW |