def _AddAdditionalDetailsToBug(self, bug_id, alerts): """Adds additional data to the bug as a comment. Adds the link to /group_report and bug_id as well as the names of the bots that triggered the alerts, and a milestone label. Args: bug_id: Bug ID number. alerts: The Alert entities being associated with this bug. """ base_url = '%s/group_report' % _GetServerURL() bug_page_url = '%s?bug_id=%s' % (base_url, bug_id) alerts_url = '%s?keys=%s' % (base_url, _UrlsafeKeys(alerts)) comment = 'All graphs for this bug:\n %s\n\n' % bug_page_url comment += 'Original alerts at time of bug-filing:\n %s\n' % alerts_url bot_names = alert.GetBotNamesFromAlerts(alerts) if bot_names: comment += '\n\nBot(s) for this bug\'s original alert(s):\n\n' comment += '\n'.join(sorted(bot_names)) else: comment += '\nCould not extract bot names from the list of alerts.' http = oauth2_decorator.decorator.http() service = issue_tracker_service.IssueTrackerService(http=http) service.AddBugComment(bug_id, comment)
def get(self): """The get handler method is called from a cron job. It expects no parameters and has no output. It checks all current bisect try jobs and send comments to an issue on the issue tracker if a bisect job has completed. """ credentials = rietveld_service.Credentials( rietveld_service.GetDefaultRietveldConfig(), rietveld_service.PROJECTHOSTING_SCOPE) issue_tracker = issue_tracker_service.IssueTrackerService( additional_credentials=credentials) jobs_to_check = try_job.TryJob.query( try_job.TryJob.status == 'started').fetch() for job in jobs_to_check: try: if job.use_buildbucket: logging.info('Checking job %s with Buildbucket job ID %s.', job.key.id(), getattr(job, 'buildbucket_job_id', None)) else: logging.info('Checking job %s with Rietveld issue ID %s.', job.key.id(), getattr(job, 'rietveld_issue_id', None)) _CheckJob(job, issue_tracker) except Exception as e: # pylint: disable=broad-except logging.error('Caught Exception %s: %s', type(e).__name__, e)
def _FetchBugs(self): http = oauth2_decorator.DECORATOR.http() issue_tracker = issue_tracker_service.IssueTrackerService(http=http) response = issue_tracker.List(q='opened-after:today-5', label='Type-Bug-Regression,Performance', sort='-id') return response.get('items', []) if response else []
def get(self): """The get handler method is called from a cron job. It expects no parameters and has no output. It checks all current bisect try jobs and send comments to an issue on the issue tracker if a bisect job has completed. """ credentials = utils.ServiceAccountCredentials() issue_tracker = issue_tracker_service.IssueTrackerService( additional_credentials=credentials) # Set privilege so we can also fetch internal try_job entities. datastore_hooks.SetPrivilegedRequest() jobs_to_check = try_job.TryJob.query( try_job.TryJob.status.IN(['started', 'pending'])).fetch() all_successful = True for job in jobs_to_check: try: _CheckJob(job, issue_tracker) except Exception as e: # pylint: disable=broad-except logging.error('Caught Exception %s: %s\n%s', type(e).__name__, e, traceback.format_exc()) all_successful = False if all_successful: utils.TickMonitoringCustomMetric('UpdateBugWithResults')
def testNewBug_Success_NewBugReturnsId(self): service = issue_tracker_service.IssueTrackerService() service._ExecuteRequest = mock.Mock(return_value={'id': 333}) bug_id = service.NewBug('Bug title', 'body', owner='*****@*****.**') self.assertEqual(1, service._ExecuteRequest.call_count) self.assertEqual(333, bug_id)
def testNewBug_Failure_NewBugReturnsNone(self): service = issue_tracker_service.IssueTrackerService() service._ExecuteRequest = mock.Mock(return_value={}) bug_id = service.NewBug('Bug title', 'body', owner='*****@*****.**') self.assertEqual(1, service._ExecuteRequest.call_count) self.assertIsNone(bug_id)
def _CreateBug(self, summary, description, labels, components, urlsafe_keys): """Creates a bug, associates it with the alerts, sends a HTML response. Args: summary: The new bug summary string. description: The new bug description string. labels: List of label strings for the new bug. components: List of component strings for the new bug. urlsafe_keys: Comma-separated alert keys in urlsafe format. """ alert_keys = [ndb.Key(urlsafe=k) for k in urlsafe_keys.split(',')] alerts = ndb.get_multi(alert_keys) if not description: description = 'See the link to graphs below.' milestone_label = _MilestoneLabel(alerts) if milestone_label: labels.append(milestone_label) # Only project members (@chromium.org accounts) can be owners of bugs. owner = self.request.get('owner') if owner and not owner.endswith('@chromium.org'): self.RenderHtml( 'bug_result.html', {'error': 'Owner email address must end with @chromium.org.'}) return http = oauth2_decorator.DECORATOR.http() service = issue_tracker_service.IssueTrackerService(http=http) bug_id = service.NewBug(summary, description, labels=labels, components=components, owner=owner) if not bug_id: self.RenderHtml('bug_result.html', {'error': 'Error creating bug!'}) return bug_data.Bug(id=bug_id).put() for alert_entity in alerts: alert_entity.bug_id = bug_id ndb.put_multi(alerts) comment_body = _AdditionalDetails(bug_id, alerts) service.AddBugComment(bug_id, comment_body) template_params = {'bug_id': bug_id} if all(k.kind() == 'Anomaly' for k in alert_keys): bisect_result = auto_bisect.StartNewBisectForBug(bug_id) if 'error' in bisect_result: template_params['bisect_error'] = bisect_result['error'] else: template_params.update(bisect_result) self.RenderHtml('bug_result.html', template_params)
def testAddBugComment_Basic(self): service = issue_tracker_service.IssueTrackerService() service._MakeCommentRequest = mock.Mock() self.assertTrue(service.AddBugComment(12345, 'The comment')) self.assertEqual(1, service._MakeCommentRequest.call_count) service._MakeCommentRequest.assert_called_with(12345, { 'updates': {}, 'content': 'The comment' })
def _CommentOnRecoveredBug(cls, bug_id): """Adds a comment and close the bug on Issue tracker.""" bug = ndb.Key('Bug', bug_id).get() if bug.status != bug_data.BUG_STATUS_OPENED: return bug.status = bug_data.BUG_STATUS_RECOVERED bug.put() comment = cls._RecoveredBugComment(bug_id) issue_tracker = issue_tracker_service.IssueTrackerService( additional_credentials=utils.ServiceAccountCredentials()) issue_tracker.AddBugComment(bug_id, comment)
def testMakeCommentRequest_UserDoesNotExist_RetryMakeCommentRequest(self): service = issue_tracker_service.IssueTrackerService() error_content = { 'error': { 'message': 'The user does not exist', 'code': 404 } } service._ExecuteRequest = mock.Mock(side_effect=errors.HttpError( mock.Mock( return_value={'status': 404}), json.dumps(error_content))) service.AddBugComment(12345, 'The comment', owner='*****@*****.**') self.assertEqual(2, service._ExecuteRequest.call_count)
def testNewBug_UsesExpectedParams(self): service = issue_tracker_service.IssueTrackerService() service._MakeCreateRequest = mock.Mock() service.NewBug('Bug title', 'body', owner='*****@*****.**') service._MakeCreateRequest.assert_called_with({ 'title': 'Bug title', 'summary': 'Bug title', 'description': 'body', 'labels': [], 'status': 'Assigned', 'owner': { 'name': '*****@*****.**' }, })
def testAddBugComment_MergeBug(self): service = issue_tracker_service.IssueTrackerService() service._MakeCommentRequest = mock.Mock() self.assertTrue(service.AddBugComment(12345, 'Dupe', merge_issue=54321)) self.assertEqual(1, service._MakeCommentRequest.call_count) service._MakeCommentRequest.assert_called_with( 12345, { 'updates': { 'status': 'Duplicate', 'mergedInto': 54321, }, 'content': 'Dupe' })
def _CommentOnRecoveredBug(cls, bug_id): """Adds a comment and close the bug on Issue tracker.""" bug = ndb.Key('Bug', bug_id).get() if bug.status != bug_data.BUG_STATUS_OPENED: return bug.status = bug_data.BUG_STATUS_RECOVERED bug.put() comment = cls._RecoveredBugComment(bug_id) credentials = rietveld_service.Credentials( rietveld_service.GetDefaultRietveldConfig(), rietveld_service.PROJECTHOSTING_SCOPE) issue_tracker = issue_tracker_service.IssueTrackerService( additional_credentials=credentials) issue_tracker.AddBugComment(bug_id, comment)
def testMakeCommentRequest_IssueDeleted_ReturnsTrue(self): service = issue_tracker_service.IssueTrackerService() error_content = { 'error': { 'message': 'User is not allowed to view this issue 12345', 'code': 403 } } service._ExecuteRequest = mock.Mock(side_effect=errors.HttpError( mock.Mock( return_value={'status': 403}), json.dumps(error_content))) comment_posted = service.AddBugComment(12345, 'The comment', owner='*****@*****.**') self.assertEqual(1, service._ExecuteRequest.call_count) self.assertEqual(True, comment_posted)
def testAddBugComment_WithOptionalParameters(self): service = issue_tracker_service.IssueTrackerService() service._MakeCommentRequest = mock.Mock() self.assertTrue( service.AddBugComment(12345, 'Some other comment', status='Fixed', labels=['Foo'], cc_list=['*****@*****.**'])) self.assertEqual(1, service._MakeCommentRequest.call_count) service._MakeCommentRequest.assert_called_with( 12345, { 'updates': { 'status': 'Fixed', 'cc': ['*****@*****.**'], 'labels': ['Foo'], }, 'content': 'Some other comment' })
def PerformBisect(bisect_job): """Starts the bisect job. This creates a patch, uploads it, then tells Rietveld to try the patch. TODO(qyearsley): If we want to use other tryservers sometimes in the future, then we need to have some way to decide which one to use. This could perhaps be passed as part of the bisect bot name, or guessed from the bisect bot name. Args: bisect_job: A TryJob entity. Returns: A dictionary containing the result; if successful, this dictionary contains the field "issue_id" and "issue_url", otherwise it contains "error". Raises: AssertionError: Bot or config not set as expected. request_handler.InvalidInputError: Some property of the bisect job is invalid. """ assert bisect_job.bot and bisect_job.config if not bisect_job.key: bisect_job.put() if bisect_job.use_buildbucket: result = _PerformBuildbucketBisect(bisect_job) else: result = _PerformLegacyBisect(bisect_job) if 'error' in result: bisect_job.run_count += 1 bisect_job.SetFailed() comment = 'Bisect job failed to kick off' elif result.get('issue_url'): comment = 'Started bisect job %s' % result['issue_url'] else: comment = 'Started bisect job: %s' % result if bisect_job.bug_id: issue_tracker = issue_tracker_service.IssueTrackerService( additional_credentials=utils.ServiceAccountCredentials()) issue_tracker.AddBugComment(bisect_job.bug_id, comment, send_email=False) return result
def get(self): """The get handler method is called from a cron job. It expects no parameters and has no output. It checks all current bisect try jobs and send comments to an issue on the issue tracker if a bisect job has completed. """ credentials = rietveld_service.Credentials( rietveld_service.GetDefaultRietveldConfig(), rietveld_service.PROJECTHOSTING_SCOPE) issue_tracker = issue_tracker_service.IssueTrackerService( additional_credentials=credentials) # Set privilege so we can also fetch internal try_job entities. datastore_hooks.SetPrivilegedRequest() jobs_to_check = try_job.TryJob.query( try_job.TryJob.status == 'started').fetch() all_successful = True for job in jobs_to_check: try: if job.use_buildbucket: logging.info('Checking job %s with Buildbucket job ID %s.', job.key.id(), getattr(job, 'buildbucket_job_id', None)) else: logging.info('Checking job %s with Rietveld issue ID %s.', job.key.id(), getattr(job, 'rietveld_issue_id', None)) _CheckJob(job, issue_tracker) except Exception as e: # pylint: disable=broad-except logging.error('Caught Exception %s: %s\n%s', type(e).__name__, e, traceback.format_exc()) all_successful = False if all_successful: utils.TickMonitoringCustomMetric('UpdateBugWithResults')
def testAddBugComment_WithNoBug_ReturnsFalse(self): service = issue_tracker_service.IssueTrackerService() service._MakeCommentRequest = mock.Mock() self.assertFalse(service.AddBugComment(None, 'Some comment')) self.assertFalse(service.AddBugComment(-1, 'Some comment'))
def testAddBugComment_Error(self, mock_logging_error): service = issue_tracker_service.IssueTrackerService() service._ExecuteRequest = mock.Mock(return_value=None) self.assertFalse(service.AddBugComment(12345, 'My bug comment')) self.assertEqual(1, service._ExecuteRequest.call_count) self.assertEqual(1, mock_logging_error.call_count)