示例#1
0
  def test_polling_in_danger_of_overrunning_alert(self):
    mock_time = mocks.MockTime()
    with mock.patch('time.time', new=mock_time.time):
      def slow_operation():
        mock_time.mock_tick(8)

      with mock.patch('requests.post') as mock_post:
        monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[], raw_args=[
          'http://test.com',
          '[email protected],[email protected]',
          '[email protected]',
          '--poll_period_s=10',
          '--min_poll_padding_period_s=5',
          '--mailgun_messages_url=http://test.com/send_email',
          '--mailgun_api_key=1234567890',
        ])
        monitor.start(slow_operation)

        monitor.poll_timer.mock_tick(1)
        mock_post.assert_called_once_with(
            'http://test.com/send_email',
            auth=('api', '1234567890'),
            data={
              'from': '*****@*****.**',
              'to': '[email protected], [email protected]',
              'subject': '[ALERT] Test monitor is in danger of overrunning',
              'html': mock.ANY,
            },
            timeout=10)
        filtered_html = re.sub(r'\s+', ' ', mock_post.call_args[1]['data']['html'])
        self.assertIn('the polling method is taking only 2.0s less than the polling period',
                      filtered_html)
示例#2
0
  def test_alert(self):
    with mock.patch('requests.post') as mock_post:
      monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[], raw_args=[
        'http://test.com',
        '[email protected],[email protected]',
        '[email protected]',
        '--mailgun_messages_url=http://test.com/send_email',
        '--mailgun_api_key=1234567890',
      ])
      monitor.start(lambda: None)

      monitor.alert('Test subject', 'test', {'a': 'string'})

      mock_post.assert_called_once_with(
          'http://test.com/send_email',
          auth=('api', '1234567890'),
          data={
            'from': '*****@*****.**',
            'to': '[email protected], [email protected]',
            'subject': '[ALERT] Test subject',
            'html': mock.ANY,
          },
          timeout=10)
      filtered_html = re.sub(r'\s+', ' ', mock_post.call_args[1]['data']['html'])
      self.assertIn('Test message with string replacement', filtered_html)
      self.assertIn('Check on this monitor\'s status', filtered_html)
      self.assertIn('Silence this alert for', filtered_html)
      self.assertIn('Unsilence this alert', filtered_html)
示例#3
0
  def test_parse_args_with_additional_arg_defs(self):
    monitor.parse_args('Test monitor', 'Test description', [{
      'name': '--arg_a',
      'dest': 'arg_a',
      'default': 'default-a',
      'help': 'A simple string arg.',
    }, {
      'name': '--arg_b',
      'dest': 'renamed_arg_b',
      'default': 3.141,
      'type': float,
      'help': 'The simple float arg.',
    }, {
      'name': '--arg_c',
      'dest': 'arg_c',
      'default': 'default',
      'help': 'The simple default arg.',
    }], [
      'http://test.com',
      '--arg_a=non-default-a',
      '--arg_b=1.618',
    ])

    self.assertEqual(monitor.args.arg_a, 'non-default-a')
    self.assertEqual(monitor.args.renamed_arg_b, 1.618)
    self.assertEqual(monitor.args.arg_c, 'default')
示例#4
0
  def test_silence_while_already_silenced_resets_timer(self):
    poll = mock.Mock()
    monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[], raw_args=[
      'http://test.com',
      '--poll_period_s=10',
      '--min_poll_padding_period_s=5',
    ])
    monitor.start(poll)

    monitor.poll_timer.mock_tick(1)
    poll.assert_called_once()

    poll.reset_mock()
    monitor.poll_timer.mock_tick(10)
    poll.assert_called_once()

    poll.reset_mock()
    monitor.poll_timer.mock_tick(5)
    monitor.silence(60 * 60)

    monitor.poll_timer.mock_tick(30 * 60)
    monitor.silence_timer.mock_tick(30 * 60)
    poll.assert_not_called()

    monitor.silence(60 * 60)

    monitor.poll_timer.mock_tick(60 * 60 - 5)
    monitor.silence_timer.mock_tick(60 * 60 - 5)
    poll.assert_not_called()

    monitor.poll_timer.mock_tick(5)
    monitor.silence_timer.mock_tick(5)
    poll.assert_called_once()
示例#5
0
  def test_polling_with_multiple_poll_fns(self):
    poll_0, poll_1 = mock.Mock(), mock.Mock()
    monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[], raw_args=[
      'http://test.com',
      '--poll_period_s=10',
      '--min_poll_padding_period_s=5',
    ])
    monitor.start([poll_0, poll_1])

    poll_0.assert_not_called()
    poll_1.assert_not_called()
    monitor.poll_timer.mock_tick(0.5)
    poll_0.assert_not_called()
    poll_1.assert_not_called()

    monitor.poll_timer.mock_tick(0.5)
    poll_0.assert_called_once()
    poll_1.assert_called_once()

    for i in xrange(3):
      poll_0.reset_mock()
      poll_1.reset_mock()
      monitor.poll_timer.mock_tick(9)
      poll_0.assert_not_called()
      poll_1.assert_not_called()

      monitor.poll_timer.mock_tick(1)
      poll_0.assert_called_once()
      poll_1.assert_called_once()
示例#6
0
  def test_handle_logs_invalid_level(self):
    monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[],
                       raw_args=['http://test.com'])
    monitor.start(lambda: None)

    response = self.server.get('/logs/invalid')
    filtered_html = re.sub(r'\s+', ' ', response.data)    
    self.assertIn('Invalid log level: "INVALID"', filtered_html)
示例#7
0
 def test_handle_silence_with_complex_duration(self):
   with mock.patch('monitor.silence') as mock_silence:
     monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[],
                        raw_args=['http://test.com'])
     monitor.start(lambda: None)
     
     response = self.server.get('/silence/1h30m15s')
     mock_silence.assert_called_once_with((60 * 60) + (30 * 60) + (15))
     filtered_html = re.sub(r'\s+', ' ', response.data)    
     self.assertIn('Silenced for 1h30m15s.', filtered_html)
示例#8
0
  def test_handle_unsilence_when_already_unsilenced(self):
    with mock.patch('monitor.unsilence', return_value=False) as mock_unsilence:
      monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[],
                         raw_args=['http://test.com'])
      monitor.start(lambda: None)

      response = self.server.get('/unsilence')
      mock_unsilence.assert_called_once_with()
      filtered_html = re.sub(r'\s+', ' ', response.data)
      self.assertIn('Already unsilenced.', filtered_html)
示例#9
0
  def test_handle_logs_error_level(self):
    with mock.patch('monitor.open', return_value=io.BytesIO(b'logs record')) as mock_open:
      monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[],
                         raw_args=['http://test.com', '--log_file_prefix=/tmp/test_monitor'])
      monitor.start(lambda: None)

      response = self.server.get('/logs/error')
      mock_open.assert_called_once_with('/tmp/test_monitor.ERROR.log', 'r')
      filtered_html = re.sub(r'\s+', ' ', response.data)    
      self.assertIn('logs record', filtered_html)
示例#10
0
  def test_render_page(self):
    monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[],
                       raw_args=['http://test.com'])

    with monitor.server.app_context():
      html = monitor.render_page('error', 'Error', {'message': 'message', 'traceback': 'traceback'})
      filtered_html = re.sub(r'\s+', ' ', html)
      self.assertEquals(
          filtered_html,
          '<!DOCTYPE html> <html> <head> <title>Test monitor - Error</title> </head> <body> '
          'message <pre>traceback</pre> </body> </html>')
示例#11
0
  def test_polling_with_no_poll_fns(self):
    monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[], raw_args=[
      'http://test.com',
      '--poll_period_s=10',
      '--min_poll_padding_period_s=5',
    ])
    monitor.start()

    monitor.poll_timer.mock_tick(0.5)

    with self.assertRaises(NotImplementedError):
      monitor.poll_timer.mock_tick(0.5)
示例#12
0
def start(raw_args=sys.argv[1:]):
    monitor.parse_args(
        'Geofence monitor',
        'Monitors cars, triggering an email alert if any leave their prescribed geofences.',
        raw_arg_defs=[{
            'name':
            'car_ids',
            'type':
            parse_ids,
            'nargs':
            '+',
            'help':
            'The car IDs to monitor. IDs can be specified as single IDs or ID ranges such as "2-8"',
        }, {
            'name':
            '--car_status_url',
            'dest':
            'car_status_url',
            'default':
            'http://skurt-interview-api.herokuapp.com/carStatus/%s',
            'help':
            'The URL pattern for the car status endpoint, with "%%s" to indicate the id insertion '
            'point',
        }, {
            'name':
            '--max_query_qps',
            'dest':
            'query_delay_s',
            'default':
            1,
            'type':
            lambda arg: 1 / float(arg),
            'help':
            'The maximum QPS with which to query the server for individual car statuses',
        }, {
            'name':
            '--google_maps_api_key',
            'dest':
            'google_maps_api_key',
            'default':
            'AIzaSyDwHlJG6aS98VZPPOyv7hm1BHPnvwURink',
            'help':
            'The API key for Google Static Maps, used to embed car location maps in geofence '
            'alert emails',
        }],
        raw_args=raw_args)
    # Flatten the car_ids args into a single sorted list of unique IDs.
    monitor.args.car_ids = sorted(
        set(itertools.chain.from_iterable(monitor.args.car_ids)))

    monitor.start(poll)
示例#13
0
  def test_parse_args_defaults(self):
    monitor.parse_args('Test monitor', 'Test description', [], ['http://test.com'])

    self.assertEqual(monitor.args.alert_emails, ['Cameron Behar <*****@*****.**>'])
    self.assertEqual(monitor.args.monitor_email,
                     'Test monitor <*****@*****.**>')
    self.assertEqual(monitor.args.poll_period_s, 300)
    self.assertEqual(monitor.args.min_poll_padding_period_s, 10)
    self.assertEqual(monitor.args.mailgun_messages_url,
                     'https://api.mailgun.net/v3/sandboxf3f15ea9e4c743199c24cb3b628208c0.mailgun'
                     '.org/messages')
    self.assertEqual(monitor.args.mailgun_api_key, '')
    self.assertEqual(monitor.args.port, 5000)
    self.assertEqual(monitor.args.log_file_prefix, 'test_monitor')
    self.assertEqual(monitor.args.log_level, logging.INFO)
示例#14
0
  def test_handle_args(self):
    monitor.parse_args('Test monitor', 'Test description', [{
      'name': '--arg_a',
      'dest': 'arg_a',
      'default': 'default-a',
      'help': 'A simple string arg.',
    }, {
      'name': '--arg_b',
      'dest': 'renamed_arg_b',
      'default': 3.141,
      'type': float,
      'help': 'The simple float arg.',
    }, {
      'name': '--arg_c',
      'dest': 'arg_c',
      'default': 'default',
      'help': 'The simple default arg.',
    }], [
      'http://test.com',
      '--arg_a=non-default-a',
      '--arg_b=1.618',
      '[email protected],[email protected]',
      '[email protected]',
      '--poll_period_s=10',
      '--min_poll_padding_period_s=0',
      '--mailgun_messages_url=http://test.com/send_email',
      '--mailgun_api_key=123456789',
      '--port=8080',
      '--log_file_prefix=other_monitor',
      '--log=DEBUG',
    ])

    response = self.server.get('/args')
    self.assertIn(
        '--alert_emails=[&#39;[email protected]&#39;, &#39;[email protected]&#39;]\n'
        '--arg_a=non-default-a\n'
        '--arg_c=default\n'
        '--log_file_prefix=other_monitor\n'
        '--log_level=10\n'
        '--mailgun_api_key=123456789\n'
        '--mailgun_messages_url=http://test.com/send_email\n'
        '--min_poll_padding_period_s=0.0\n'
        '[email protected]\n'
        '--monitor_url=http://test.com\n'
        '--poll_period_s=10.0\n'
        '--port=8080\n'
        '--renamed_arg_b=1.618\n',
        response.data)
示例#15
0
  def test_polling_unhandled_exception_alert(self):
    def unhandled_exception():
      raise Exception('unhandled exception')
  
    with mock.patch('requests.post') as mock_post:
      monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[], raw_args=[
        'http://test.com',
        '[email protected],[email protected]',
        '[email protected]',
        '--poll_period_s=10',
        '--min_poll_padding_period_s=5',
        '--mailgun_messages_url=http://test.com/send_email',
        '--mailgun_api_key=1234567890',
      ])
      monitor.start(unhandled_exception)

      monitor.poll_timer.mock_tick(1)
      mock_post.assert_called_once_with(
          'http://test.com/send_email',
          auth=('api', '1234567890'),
          data={
            'from': '*****@*****.**',
            'to': '[email protected], [email protected]',
            'subject': '[ALERT] Test monitor encountered an exception',
            'html': mock.ANY,
          },
          timeout=10)

      # We can't test the actual email text here because it contains traceback line numbers, which
      # are liable to change with modifications to this file and monitor.py. First we'll need to
      # filter the HTML to make it more stable over time.
      filtered_html = mock_post.call_args[1]['data']['html']
      filtered_html = re.sub(r'\s+', ' ', filtered_html)
      filtered_html = re.sub(r'line\s\d+', 'line #', filtered_html)
      filtered_html = re.sub(r'File\s&#34;[^&]+&#34;', 'File &#34;/home/script.py&#34;', filtered_html)
      self.assertIn('Unhandled exception in Test monitor\'s poll function:', filtered_html)
      self.assertIn('Traceback (most recent call last): '
                    'File &#34;/home/script.py&#34;, line #, in poll '
                    'poll_fn() '
                    'File &#34;/home/script.py&#34;, line #, in unhandled_exception '
                    'raise Exception(&#39;unhandled exception&#39;) '
                    'Exception: unhandled exception',
                    filtered_html)
示例#16
0
  def test_unsilence_when_already_unsilenced(self):
    poll = mock.Mock()
    monitor.parse_args('Test monitor', 'Test description', raw_arg_defs=[], raw_args=[
      'http://test.com',
      '--poll_period_s=10',
      '--min_poll_padding_period_s=5',
    ])
    monitor.start(poll)

    monitor.poll_timer.mock_tick(1)
    poll.assert_called_once()

    poll.reset_mock()
    monitor.poll_timer.mock_tick(5)
    self.assertFalse(monitor.unsilence())
    poll.assert_not_called()

    poll.reset_mock()
    monitor.poll_timer.mock_tick(5)
    poll.assert_called_once()
示例#17
0
def start(raw_args=sys.argv[1:]):
    monitor.parse_args(
        'Ok monitor',
        "Monitors another monitor's /ok endpoint, triggering an email alert if for any reason it "
        "can't be reached.",
        raw_arg_defs=[{
            'name': 'server_url',
            'help': 'The URL of the server to be monitored',
        }, {
            'name':
            '--ok_timeout_s',
            'dest':
            'ok_timeout_s',
            'default':
            10,
            'type':
            float,
            'help':
            'The maximum period (in seconds) before timing out an /ok request',
        }],
        raw_args=raw_args)
    monitor.start(poll)
示例#18
0
  def test_parse_args_with_complex_args(self):
    monitor.parse_args('Test monitor', 'Test description', [], [
      'http://test.com',
      '[email protected],[email protected]',
      '[email protected]',
      '--poll_period_s=10',
      '--min_poll_padding_period_s=0',
      '--mailgun_messages_url=http://test.com/send_email',
      '--mailgun_api_key=123456789',
      '--port=8080',
      '--log_file_prefix=other_monitor',
      '--log=DEBUG',
    ])

    self.assertEqual(monitor.args.alert_emails, ['*****@*****.**', '*****@*****.**'])
    self.assertEqual(monitor.args.monitor_email, '*****@*****.**')
    self.assertEqual(monitor.args.poll_period_s, 10.0)
    self.assertEqual(monitor.args.min_poll_padding_period_s, 0.0)
    self.assertEqual(monitor.args.mailgun_messages_url, 'http://test.com/send_email')
    self.assertEqual(monitor.args.mailgun_api_key, '123456789')
    self.assertEqual(monitor.args.port, 8080)
    self.assertEqual(monitor.args.log_file_prefix, 'other_monitor')
    self.assertEqual(monitor.args.log_level, logging.DEBUG)