| #!/usr/bin/env python3 |
| # Copyright 2021 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Utilities to file bugs.""" |
| |
| import base64 |
| import datetime |
| import enum |
| import json |
| import os |
| from typing import Any, Dict, List, Optional |
| |
| X20_PATH = '/google/data/rw/teams/c-compiler-chrome/prod_bugs' |
| |
| |
| class WellKnownComponents(enum.IntEnum): |
| """A listing of "well-known" components recognized by our infra.""" |
| CrOSToolchainPublic = -1 |
| CrOSToolchainPrivate = -2 |
| |
| |
| def _WriteBugJSONFile(object_type: str, json_object: Dict[str, Any]): |
| """Writes a JSON file to X20_PATH with the given bug-ish object.""" |
| final_object = { |
| 'type': object_type, |
| 'value': json_object, |
| } |
| |
| # The name of this has two parts: |
| # - An easily sortable time, to provide uniqueness and let our service send |
| # things in the order they were put into the outbox. |
| # - 64 bits of entropy, so two racing bug writes don't clobber the same file. |
| now = datetime.datetime.utcnow().isoformat('T', 'seconds') + 'Z' |
| entropy = base64.urlsafe_b64encode(os.getrandom(8)) |
| entropy_str = entropy.rstrip(b'=').decode('utf-8') |
| file_path = os.path.join(X20_PATH, f'{now}_{entropy_str}.json') |
| |
| temp_path = file_path + '.in_progress' |
| try: |
| with open(temp_path, 'w') as f: |
| json.dump(final_object, f) |
| os.rename(temp_path, file_path) |
| except: |
| os.remove(temp_path) |
| raise |
| return file_path |
| |
| |
| def AppendToExistingBug(bug_id: int, body: str): |
| """Sends a reply to an existing bug.""" |
| _WriteBugJSONFile('AppendToExistingBugRequest', { |
| 'body': body, |
| 'bug_id': bug_id, |
| }) |
| |
| |
| def CreateNewBug(component_id: int, |
| title: str, |
| body: str, |
| assignee: Optional[str] = None, |
| cc: Optional[List[str]] = None): |
| """Sends a request to create a new bug. |
| |
| Args: |
| component_id: The component ID to add. Anything from WellKnownComponents |
| also works. |
| title: Title of the bug. Must be nonempty. |
| body: Body of the bug. Must be nonempty. |
| assignee: Assignee of the bug. Must be either an email address, or a |
| "well-known" assignee (detective, mage). |
| cc: A list of emails to add to the CC list. Must either be an email |
| address, or a "well-known" individual (detective, mage). |
| """ |
| obj = { |
| 'component_id': component_id, |
| 'subject': title, |
| 'body': body, |
| } |
| |
| if assignee: |
| obj['assignee'] = assignee |
| |
| if cc: |
| obj['cc'] = cc |
| |
| _WriteBugJSONFile('FileNewBugRequest', obj) |
| |
| |
| def SendCronjobLog(cronjob_name: str, failed: bool, message: str): |
| """Sends the record of a cronjob to our bug infra. |
| |
| cronjob_name: The name of the cronjob. Expected to remain consistent over |
| time. |
| failed: Whether the job failed or not. |
| message: Any seemingly relevant context. This is pasted verbatim in a bug, if |
| the cronjob infra deems it worthy. |
| """ |
| _WriteBugJSONFile('ChrotomationCronjobUpdate', { |
| 'name': cronjob_name, |
| 'message': message, |
| 'failed': failed, |
| }) |