• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright 2021 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Utilities to file bugs."""
7
8import base64
9import datetime
10import enum
11import json
12import os
13from typing import Any, Dict, List, Optional
14
15X20_PATH = '/google/data/rw/teams/c-compiler-chrome/prod_bugs'
16
17
18class WellKnownComponents(enum.IntEnum):
19  """A listing of "well-known" components recognized by our infra."""
20  CrOSToolchainPublic = -1
21  CrOSToolchainPrivate = -2
22
23
24def _WriteBugJSONFile(object_type: str, json_object: Dict[str, Any]):
25  """Writes a JSON file to X20_PATH with the given bug-ish object."""
26  final_object = {
27      'type': object_type,
28      'value': json_object,
29  }
30
31  # The name of this has two parts:
32  # - An easily sortable time, to provide uniqueness and let our service send
33  #   things in the order they were put into the outbox.
34  # - 64 bits of entropy, so two racing bug writes don't clobber the same file.
35  now = datetime.datetime.utcnow().isoformat('T', 'seconds') + 'Z'
36  entropy = base64.urlsafe_b64encode(os.getrandom(8))
37  entropy_str = entropy.rstrip(b'=').decode('utf-8')
38  file_path = os.path.join(X20_PATH, f'{now}_{entropy_str}.json')
39
40  temp_path = file_path + '.in_progress'
41  try:
42    with open(temp_path, 'w') as f:
43      json.dump(final_object, f)
44    os.rename(temp_path, file_path)
45  except:
46    os.remove(temp_path)
47    raise
48  return file_path
49
50
51def AppendToExistingBug(bug_id: int, body: str):
52  """Sends a reply to an existing bug."""
53  _WriteBugJSONFile('AppendToExistingBugRequest', {
54      'body': body,
55      'bug_id': bug_id,
56  })
57
58
59def CreateNewBug(component_id: int,
60                 title: str,
61                 body: str,
62                 assignee: Optional[str] = None,
63                 cc: Optional[List[str]] = None):
64  """Sends a request to create a new bug.
65
66  Args:
67    component_id: The component ID to add. Anything from WellKnownComponents
68      also works.
69    title: Title of the bug. Must be nonempty.
70    body: Body of the bug. Must be nonempty.
71    assignee: Assignee of the bug. Must be either an email address, or a
72      "well-known" assignee (detective, mage).
73    cc: A list of emails to add to the CC list. Must either be an email
74      address, or a "well-known" individual (detective, mage).
75  """
76  obj = {
77      'component_id': component_id,
78      'subject': title,
79      'body': body,
80  }
81
82  if assignee:
83    obj['assignee'] = assignee
84
85  if cc:
86    obj['cc'] = cc
87
88  _WriteBugJSONFile('FileNewBugRequest', obj)
89
90
91def SendCronjobLog(cronjob_name: str, failed: bool, message: str):
92  """Sends the record of a cronjob to our bug infra.
93
94  cronjob_name: The name of the cronjob. Expected to remain consistent over
95    time.
96  failed: Whether the job failed or not.
97  message: Any seemingly relevant context. This is pasted verbatim in a bug, if
98    the cronjob infra deems it worthy.
99  """
100  _WriteBugJSONFile('ChrotomationCronjobUpdate', {
101      'name': cronjob_name,
102      'message': message,
103      'failed': failed,
104  })
105