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