From be7063d53cb3a139c26c2c585d888781cf75ab10 Mon Sep 17 00:00:00 2001
From: sidpremkumar <sid.premkumar@gmail.com>
Date: Oct 30 2019 20:51:24 +0000
Subject: Add catch for github api limit


---

diff --git a/sync2jira/main.py b/sync2jira/main.py
index dab17bf..e5af266 100644
--- a/sync2jira/main.py
+++ b/sync2jira/main.py
@@ -25,6 +25,7 @@ Run with systemd, please.
 import logging
 import warnings
 import traceback
+from time import sleep
 
 import fedmsg
 import fedmsg.config
@@ -134,6 +135,7 @@ def listen(config):
         idx = msg['msg_id']
         suffix = ".".join(topic.split('.')[3:])
         log.debug("Encountered %r %r %r", suffix, topic, idx)
+
         if suffix not in handlers:
             continue
 
@@ -150,7 +152,7 @@ def listen(config):
         d.sync_with_jira(issue, config)
 
 
-def initialize(config):
+def initialize(config, testing=False):
     """
     Initial initialization needed to sync any upstream \
     repo with JIRA. Goes through all issues and \
@@ -158,6 +160,7 @@ def initialize(config):
     created.
 
     :param Dict config: Config dict for JIRA
+    :param Bool testing: Flag to indicate if we are testing. Default false
     :returns: Nothing
     """
     log.info("   Running initialization to sync all issues from upstream to jira")
@@ -173,12 +176,28 @@ def initialize(config):
     log.info("   Done with pagure initialization.")
 
     for upstream in mapping.get('github', {}).keys():
-        for issue in u.github_issues(upstream, config):
-            try:
-                d.sync_with_jira(issue, config)
-            except Exception:
-                log.error("   Failed on %r", issue)
-                raise
+        # Try and except for github API limit
+        try:
+            for issue in u.github_issues(upstream, config):
+                try:
+                    d.sync_with_jira(issue, config)
+                except Exception:
+                    log.error("   Failed on %r", issue)
+                    raise
+        except Exception as e:
+            if "API rate limit exceeded" in e.__str__():
+                # If we've hit out API limit:
+                # Sleep for 1 hour and call our function again
+                log.info("   Hit Github API limit. Sleeping for 1 hour...")
+                sleep(3600)
+                if not testing:
+                    initialize(config)
+                return
+            else:
+                if not config['sync2jira']['develop']:
+                    # Only send the failure email if we are not developing
+                    report_failure(config)
+                    raise
     log.info("   Done with github initialization.")
 
 
@@ -193,6 +212,7 @@ def main():
     config = load_config()
     logging.basicConfig(level=logging.INFO)
     warnings.simplefilter("ignore")
+    config['validate_signatures'] = False
 
     try:
         if config['sync2jira'].get('initialize'):
diff --git a/tests/test_main.py b/tests/test_main.py
index d9ab496..0abf7a7 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -33,7 +33,8 @@ class TestMain(unittest.TestCase):
                     'github': {'key_github': 'value1'}
                 },
                 'initialize': True,
-                'listen': True
+                'listen': True,
+                'develop': False,
             },
         }
 
@@ -205,6 +206,62 @@ class TestMain(unittest.TestCase):
 
     @mock.patch(PATH + 'u')
     @mock.patch(PATH + 'd')
+    @mock.patch(PATH + 'sleep')
+    @mock.patch(PATH + 'report_failure')
+    def test_initialize_api_limit(self,
+                                  mock_report_failure,
+                                  mock_sleep,
+                                  mock_d,
+                                  mock_u):
+        """
+        This tests 'initialize' where we get an GitHub API limit error.
+        """
+        # Set up return values
+        mock_error = MagicMock(side_effect=Exception('API rate limit exceeded'))
+        mock_u.pagure_issues.return_value = ['mock_issue_pagure']
+        mock_u.github_issues.side_effect = mock_error
+
+        # Call the function
+        m.initialize(self.mock_config, testing=True)
+
+        # Assert everything was called correctly
+        mock_u.pagure_issues.assert_called_with('key_pagure', self.mock_config)
+        mock_d.sync_with_jira.assert_any_call('mock_issue_pagure', self.mock_config)
+        mock_u.github_issues.assert_called_with('key_github', self.mock_config)
+        mock_sleep.assert_called_with(3600)
+        mock_report_failure.assert_not_called()
+
+    @mock.patch(PATH + 'u')
+    @mock.patch(PATH + 'd')
+    @mock.patch(PATH + 'sleep')
+    @mock.patch(PATH + 'report_failure')
+    def test_initialize_github_error(self,
+                                     mock_report_failure,
+                                     mock_sleep,
+                                     mock_d,
+                                     mock_u):
+        """
+        This tests 'initialize' where we get a GitHub API (not limit) error.
+        """
+        # Set up return values
+        mock_error = MagicMock(side_effect=Exception('Random Error'))
+        mock_u.pagure_issues.return_value = ['mock_issue_pagure']
+        mock_u.github_issues.side_effect = mock_error
+
+        # Call the function
+        with self.assertRaises(Exception):
+            m.initialize(self.mock_config, testing=True)
+
+        # Assert everything was called correctly
+        mock_u.pagure_issues.assert_called_with('key_pagure', self.mock_config)
+        mock_d.sync_with_jira.assert_any_call('mock_issue_pagure', self.mock_config)
+        mock_u.github_issues.assert_called_with('key_github', self.mock_config)
+        mock_sleep.assert_not_called()
+        mock_report_failure.assert_called_with(self.mock_config)
+
+
+    @mock.patch(PATH + 'u')
+    @mock.patch(PATH + 'd')
     @mock.patch(PATH + 'fedmsg')
     def test_listen_no_handlers(self,
                                 mock_fedmsg,

