xref: /aosp_15_r20/external/autotest/site_utils/pubsub_utils.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright 2016 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Li"""Script to archive old Autotest results to Google Storage.
6*9c5db199SXin Li
7*9c5db199SXin LiUses gsutil to archive files to the configured Google Storage bucket.
8*9c5db199SXin LiUpon successful copy, the local results directory is deleted.
9*9c5db199SXin Li"""
10*9c5db199SXin Li
11*9c5db199SXin Lifrom __future__ import print_function
12*9c5db199SXin Li
13*9c5db199SXin Liimport logging
14*9c5db199SXin Liimport os
15*9c5db199SXin Li
16*9c5db199SXin Lifrom apiclient import discovery
17*9c5db199SXin Lifrom apiclient import errors
18*9c5db199SXin Lifrom oauth2client.client import ApplicationDefaultCredentialsError
19*9c5db199SXin Lifrom oauth2client.client import GoogleCredentials
20*9c5db199SXin Li
21*9c5db199SXin Li# Cloud service
22*9c5db199SXin LiPUBSUB_SERVICE_NAME = 'pubsub'
23*9c5db199SXin LiPUBSUB_VERSION = 'v1beta2'
24*9c5db199SXin LiPUBSUB_SCOPES = ['https://www.googleapis.com/auth/pubsub']
25*9c5db199SXin Li# number of retry to publish an event.
26*9c5db199SXin LiDEFAULT_PUBSUB_NUM_RETRIES = 3
27*9c5db199SXin Li
28*9c5db199SXin Liclass PubSubException(Exception):
29*9c5db199SXin Li    """Exception to be raised when the test to push to prod failed."""
30*9c5db199SXin Li    pass
31*9c5db199SXin Li
32*9c5db199SXin Li
33*9c5db199SXin Liclass PubSubClient(object):
34*9c5db199SXin Li    """A generic pubsub client."""
35*9c5db199SXin Li    def __init__(self, credential_file=None):
36*9c5db199SXin Li        """Constructor for PubSubClient.
37*9c5db199SXin Li
38*9c5db199SXin Li        Args:
39*9c5db199SXin Li          credential_file: The credential filename.
40*9c5db199SXin Li
41*9c5db199SXin Li        Raises:
42*9c5db199SXin Li          PubSubException if the credential file does not exist or corrupted.
43*9c5db199SXin Li        """
44*9c5db199SXin Li        if not credential_file:
45*9c5db199SXin Li            raise PubSubException('You need to specify a credential file.')
46*9c5db199SXin Li        self.credential_file = credential_file
47*9c5db199SXin Li        self.credential = self._get_credential()
48*9c5db199SXin Li
49*9c5db199SXin Li    def _get_credential(self):
50*9c5db199SXin Li        """Gets the pubsub service api handle."""
51*9c5db199SXin Li        if not os.path.isfile(self.credential_file):
52*9c5db199SXin Li            logging.error('No credential file found')
53*9c5db199SXin Li            raise PubSubException('Credential file does not exist:' +
54*9c5db199SXin Li                                  self.credential_file)
55*9c5db199SXin Li        try:
56*9c5db199SXin Li            credential = GoogleCredentials.from_stream(self.credential_file)
57*9c5db199SXin Li            if credential.create_scoped_required():
58*9c5db199SXin Li                credential = credential.create_scoped(PUBSUB_SCOPES)
59*9c5db199SXin Li            return credential
60*9c5db199SXin Li        except ApplicationDefaultCredentialsError as ex:
61*9c5db199SXin Li            logging.exception('Failed to get credential:%s', ex)
62*9c5db199SXin Li        except errors.Error as e:
63*9c5db199SXin Li            logging.exception('Failed to get the pubsub service handle:%s', e)
64*9c5db199SXin Li
65*9c5db199SXin Li        raise PubSubException('Credential file %s does not exists:' %
66*9c5db199SXin Li                              self.credential_file)
67*9c5db199SXin Li
68*9c5db199SXin Li    def _get_pubsub_service(self):
69*9c5db199SXin Li        try:
70*9c5db199SXin Li            return discovery.build(PUBSUB_SERVICE_NAME, PUBSUB_VERSION,
71*9c5db199SXin Li                                   credentials=self.credential)
72*9c5db199SXin Li        except errors.Error as e:
73*9c5db199SXin Li            logging.exception('Failed to get pubsub resource object:%s', e)
74*9c5db199SXin Li            raise PubSubException('Failed to get pubsub resource object')
75*9c5db199SXin Li
76*9c5db199SXin Li    def publish_notifications(self, topic, messages=None):
77*9c5db199SXin Li        """Publishes a test result notification to a given pubsub topic.
78*9c5db199SXin Li
79*9c5db199SXin Li        @param topic: The Cloud pubsub topic.
80*9c5db199SXin Li        @param messages: A list of notification messages.
81*9c5db199SXin Li
82*9c5db199SXin Li        @returns A list of pubsub message ids, and empty if fails.
83*9c5db199SXin Li
84*9c5db199SXin Li        @raises PubSubException if failed to publish the notification.
85*9c5db199SXin Li        """
86*9c5db199SXin Li        if not messages:
87*9c5db199SXin Li            return None
88*9c5db199SXin Li
89*9c5db199SXin Li        pubsub = self._get_pubsub_service()
90*9c5db199SXin Li        try:
91*9c5db199SXin Li            body = {'messages': messages}
92*9c5db199SXin Li            resp = pubsub.projects().topics().publish(
93*9c5db199SXin Li                topic=topic, body=body).execute(
94*9c5db199SXin Li                    num_retries=DEFAULT_PUBSUB_NUM_RETRIES)
95*9c5db199SXin Li            msgIds = []
96*9c5db199SXin Li            if resp:
97*9c5db199SXin Li                msgIds = resp.get('messageIds')
98*9c5db199SXin Li                if msgIds:
99*9c5db199SXin Li                    logging.debug('Published notification message')
100*9c5db199SXin Li                else:
101*9c5db199SXin Li                    logging.error('Failed to published notification message')
102*9c5db199SXin Li            return msgIds
103*9c5db199SXin Li        except errors.Error as e:
104*9c5db199SXin Li            logging.exception('Failed to publish test result notification:%s',
105*9c5db199SXin Li                    e)
106*9c5db199SXin Li            raise PubSubException('Failed to publish the notification')
107