| OLD | NEW |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import copy | 5 import copy |
| 6 import datetime | 6 import datetime |
| 7 import os | 7 import os |
| 8 import time | 8 import time |
| 9 import unittest | 9 import unittest |
| 10 | 10 |
| 11 import endpoints |
| 11 import gae_ts_mon | 12 import gae_ts_mon |
| 12 import mock | 13 import mock |
| 13 import webapp2 | 14 import webapp2 |
| 14 | 15 |
| 15 from infra_libs.ts_mon import config | 16 from infra_libs.ts_mon import config |
| 16 from infra_libs.ts_mon import shared | 17 from infra_libs.ts_mon import shared |
| 17 from infra_libs.ts_mon.common import http_metrics | 18 from infra_libs.ts_mon.common import http_metrics |
| 18 from infra_libs.ts_mon.common import interface | 19 from infra_libs.ts_mon.common import interface |
| 19 from infra_libs.ts_mon.common import monitors | 20 from infra_libs.ts_mon.common import monitors |
| 20 from infra_libs.ts_mon.common import targets | 21 from infra_libs.ts_mon.common import targets |
| 21 from infra_libs.ts_mon.common.test import stubs | 22 from infra_libs.ts_mon.common.test import stubs |
| 23 from protorpc import message_types |
| 24 from protorpc import remote |
| 22 from testing_utils import testing | 25 from testing_utils import testing |
| 23 | 26 |
| 24 | 27 |
| 25 class InitializeTest(testing.AppengineTestCase): | 28 class InitializeTest(testing.AppengineTestCase): |
| 26 def setUp(self): | 29 def setUp(self): |
| 27 super(InitializeTest, self).setUp() | 30 super(InitializeTest, self).setUp() |
| 28 | 31 |
| 29 config.reset_for_unittest() | 32 config.reset_for_unittest() |
| 30 target = targets.TaskTarget('test_service', 'test_job', | 33 target = targets.TaskTarget('test_service', 'test_job', |
| 31 'test_region', 'test_host') | 34 'test_region', 'test_host') |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 111 counter.increment() | 114 counter.increment() |
| 112 self.assertEqual(5, gauge.get()) | 115 self.assertEqual(5, gauge.get()) |
| 113 self.assertEqual(1, counter.get()) | 116 self.assertEqual(1, counter.get()) |
| 114 | 117 |
| 115 config._reset_cumulative_metrics() | 118 config._reset_cumulative_metrics() |
| 116 self.assertEqual(5, gauge.get()) | 119 self.assertEqual(5, gauge.get()) |
| 117 self.assertIsNone(counter.get()) | 120 self.assertIsNone(counter.get()) |
| 118 | 121 |
| 119 def test_flush_metrics_no_task_num(self): | 122 def test_flush_metrics_no_task_num(self): |
| 120 # We are not assigned task_num yet; cannot send metrics. | 123 # We are not assigned task_num yet; cannot send metrics. |
| 121 time_now = datetime.datetime(2016, 2, 8, 1, 0) | 124 time_now = 10000 |
| 122 more_than_min_ago = time_now - datetime.timedelta(seconds=61) | 125 datetime_now = datetime.datetime.utcfromtimestamp(time_now) |
| 126 more_than_min_ago = datetime_now - datetime.timedelta(seconds=61) |
| 123 interface.state.last_flushed = more_than_min_ago | 127 interface.state.last_flushed = more_than_min_ago |
| 124 entity = shared.get_instance_entity() | 128 entity = shared.get_instance_entity() |
| 125 entity.task_num = -1 | 129 entity.task_num = -1 |
| 126 interface.state.target.task_num = -1 | 130 interface.state.target.task_num = -1 |
| 127 self.assertFalse(config.flush_metrics_if_needed(time_fn=lambda: time_now)) | 131 self.assertFalse(config.flush_metrics_if_needed(time_fn=lambda: time_now)) |
| 128 | 132 |
| 129 def test_flush_metrics_no_task_num_too_long(self): | 133 def test_flush_metrics_no_task_num_too_long(self): |
| 130 # We are not assigned task_num for too long; cannot send metrics. | 134 # We are not assigned task_num for too long; cannot send metrics. |
| 131 time_now = datetime.datetime(2016, 2, 8, 1, 0) | 135 time_now = 10000 |
| 132 too_long_ago = time_now - datetime.timedelta( | 136 datetime_now = datetime.datetime.utcfromtimestamp(time_now) |
| 137 too_long_ago = datetime_now - datetime.timedelta( |
| 133 seconds=shared.INSTANCE_EXPECTED_TO_HAVE_TASK_NUM_SEC+1) | 138 seconds=shared.INSTANCE_EXPECTED_TO_HAVE_TASK_NUM_SEC+1) |
| 134 interface.state.last_flushed = too_long_ago | 139 interface.state.last_flushed = too_long_ago |
| 135 entity = shared.get_instance_entity() | 140 entity = shared.get_instance_entity() |
| 136 entity.task_num = -1 | 141 entity.task_num = -1 |
| 137 entity.last_updated = too_long_ago | 142 entity.last_updated = too_long_ago |
| 138 interface.state.target.task_num = -1 | 143 interface.state.target.task_num = -1 |
| 139 self.assertFalse(config.flush_metrics_if_needed(time_fn=lambda: time_now)) | 144 self.assertFalse(config.flush_metrics_if_needed(time_fn=lambda: time_now)) |
| 140 | 145 |
| 141 def test_flush_metrics_purged(self): | 146 def test_flush_metrics_purged(self): |
| 142 # We lost our task_num; cannot send metrics. | 147 # We lost our task_num; cannot send metrics. |
| 143 time_now = datetime.datetime(2016, 2, 8, 1, 0) | 148 time_now = 10000 |
| 144 more_than_min_ago = time_now - datetime.timedelta(seconds=61) | 149 datetime_now = datetime.datetime.utcfromtimestamp(time_now) |
| 150 more_than_min_ago = datetime_now - datetime.timedelta(seconds=61) |
| 145 interface.state.last_flushed = more_than_min_ago | 151 interface.state.last_flushed = more_than_min_ago |
| 146 entity = shared.get_instance_entity() | 152 entity = shared.get_instance_entity() |
| 147 entity.task_num = -1 | 153 entity.task_num = -1 |
| 148 interface.state.target.task_num = 2 | 154 interface.state.target.task_num = 2 |
| 149 self.assertFalse(config.flush_metrics_if_needed(time_fn=lambda: time_now)) | 155 self.assertFalse(config.flush_metrics_if_needed(time_fn=lambda: time_now)) |
| 150 | 156 |
| 151 def test_flush_metrics_too_early(self): | 157 def test_flush_metrics_too_early(self): |
| 152 # Too early to send metrics. | 158 # Too early to send metrics. |
| 153 time_now = datetime.datetime(2016, 2, 8, 1, 0) | 159 time_now = 10000 |
| 154 less_than_min_ago = time_now - datetime.timedelta(seconds=59) | 160 datetime_now = datetime.datetime.utcfromtimestamp(time_now) |
| 161 less_than_min_ago = datetime_now - datetime.timedelta(seconds=59) |
| 155 interface.state.last_flushed = less_than_min_ago | 162 interface.state.last_flushed = less_than_min_ago |
| 156 entity = shared.get_instance_entity() | 163 entity = shared.get_instance_entity() |
| 157 entity.task_num = 2 | 164 entity.task_num = 2 |
| 158 self.assertFalse(config.flush_metrics_if_needed(time_fn=lambda: time_now)) | 165 self.assertFalse(config.flush_metrics_if_needed(time_fn=lambda: time_now)) |
| 159 | 166 |
| 160 @mock.patch('infra_libs.ts_mon.common.interface.flush', autospec=True) | 167 @mock.patch('infra_libs.ts_mon.common.interface.flush', autospec=True) |
| 161 def test_flush_metrics_successfully(self, mock_flush): | 168 def test_flush_metrics_successfully(self, mock_flush): |
| 162 # We have task_num and due for sending metrics. | 169 # We have task_num and due for sending metrics. |
| 163 time_now = datetime.datetime(2016, 2, 8, 1, 0) | 170 time_now = 10000 |
| 164 more_than_min_ago = time_now - datetime.timedelta(seconds=61) | 171 datetime_now = datetime.datetime.utcfromtimestamp(time_now) |
| 172 more_than_min_ago = datetime_now - datetime.timedelta(seconds=61) |
| 165 interface.state.last_flushed = more_than_min_ago | 173 interface.state.last_flushed = more_than_min_ago |
| 166 entity = shared.get_instance_entity() | 174 entity = shared.get_instance_entity() |
| 167 entity.task_num = 2 | 175 entity.task_num = 2 |
| 168 # Global metrics must be erased after flush. | 176 # Global metrics must be erased after flush. |
| 169 test_global_metric = gae_ts_mon.GaugeMetric('test') | 177 test_global_metric = gae_ts_mon.GaugeMetric('test') |
| 170 test_global_metric.set(42) | 178 test_global_metric.set(42) |
| 171 shared.register_global_metrics([test_global_metric]) | 179 shared.register_global_metrics([test_global_metric]) |
| 172 self.assertEqual(42, test_global_metric.get()) | 180 self.assertEqual(42, test_global_metric.get()) |
| 173 self.assertTrue(config.flush_metrics_if_needed(time_fn=lambda: time_now)) | 181 self.assertTrue(config.flush_metrics_if_needed(time_fn=lambda: time_now)) |
| 174 self.assertEqual(None, test_global_metric.get()) | 182 self.assertEqual(None, test_global_metric.get()) |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 218 def get(self): | 226 def get(self): |
| 219 self.response.write('success!') | 227 self.response.write('success!') |
| 220 | 228 |
| 221 app = webapp2.WSGIApplication([('/', Handler)]) | 229 app = webapp2.WSGIApplication([('/', Handler)]) |
| 222 config.instrument_wsgi_application(app, time_fn=self.fake_time) | 230 config.instrument_wsgi_application(app, time_fn=self.fake_time) |
| 223 | 231 |
| 224 app.get_response('/') | 232 app.get_response('/') |
| 225 | 233 |
| 226 fields = {'name': '^/$', 'status': 200, 'is_robot': False} | 234 fields = {'name': '^/$', 'status': 200, 'is_robot': False} |
| 227 self.assertEqual(1, http_metrics.server_response_status.get(fields)) | 235 self.assertEqual(1, http_metrics.server_response_status.get(fields)) |
| 228 self.assertEqual(3000, http_metrics.server_durations.get(fields).sum) | 236 self.assertLessEqual(3000, http_metrics.server_durations.get(fields).sum) |
| 229 self.assertEqual( | 237 self.assertEqual( |
| 230 len('success!'), http_metrics.server_response_bytes.get(fields).sum) | 238 len('success!'), http_metrics.server_response_bytes.get(fields).sum) |
| 231 | 239 |
| 232 def test_abort(self): | 240 def test_abort(self): |
| 233 class Handler(webapp2.RequestHandler): | 241 class Handler(webapp2.RequestHandler): |
| 234 def get(self): | 242 def get(self): |
| 235 self.abort(417) | 243 self.abort(417) |
| 236 | 244 |
| 237 app = webapp2.WSGIApplication([('/', Handler)]) | 245 app = webapp2.WSGIApplication([('/', Handler)]) |
| 238 config.instrument_wsgi_application(app) | 246 config.instrument_wsgi_application(app) |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 | 349 |
| 342 app = webapp2.WSGIApplication([('/', Handler)]) | 350 app = webapp2.WSGIApplication([('/', Handler)]) |
| 343 config.instrument_wsgi_application(app) | 351 config.instrument_wsgi_application(app) |
| 344 | 352 |
| 345 app.get_response('/', POST='foo') | 353 app.get_response('/', POST='foo') |
| 346 | 354 |
| 347 fields = {'name': '^/$', 'status': 200, 'is_robot': False} | 355 fields = {'name': '^/$', 'status': 200, 'is_robot': False} |
| 348 self.assertEqual(1, http_metrics.server_response_status.get(fields)) | 356 self.assertEqual(1, http_metrics.server_response_status.get(fields)) |
| 349 self.assertEqual( | 357 self.assertEqual( |
| 350 len('foo'), http_metrics.server_request_bytes.get(fields).sum) | 358 len('foo'), http_metrics.server_request_bytes.get(fields).sum) |
| 359 |
| 360 |
| 361 class FakeTime(object): |
| 362 def __init__(self): |
| 363 self.timestamp_now = 1000.0 |
| 364 |
| 365 def time_fn(self): |
| 366 self.timestamp_now += 0.2 |
| 367 return self.timestamp_now |
| 368 |
| 369 |
| 370 @endpoints.api(name='testapi', version='v1') |
| 371 class TestEndpoint(remote.Service): |
| 372 |
| 373 @gae_ts_mon.instrument_endpoint(time_fn=FakeTime().time_fn) |
| 374 @endpoints.method(message_types.VoidMessage, message_types.VoidMessage, |
| 375 name='method_good') |
| 376 def do_good(self, request): |
| 377 return request |
| 378 |
| 379 @gae_ts_mon.instrument_endpoint(time_fn=FakeTime().time_fn) |
| 380 @endpoints.method(message_types.VoidMessage, message_types.VoidMessage, |
| 381 name='method_bad') |
| 382 def do_bad(self, request): |
| 383 raise Exception |
| 384 |
| 385 @gae_ts_mon.instrument_endpoint(time_fn=FakeTime().time_fn) |
| 386 @endpoints.method(message_types.VoidMessage, message_types.VoidMessage, |
| 387 name='method_400') |
| 388 def do_400(self, request): |
| 389 raise endpoints.BadRequestException('Bad request') |
| 390 |
| 391 |
| 392 class InstrumentEndpointTest(testing.EndpointsTestCase): |
| 393 api_service_cls = TestEndpoint |
| 394 |
| 395 def setUp(self): |
| 396 super(InstrumentEndpointTest, self).setUp() |
| 397 |
| 398 config.reset_for_unittest() |
| 399 target = targets.TaskTarget('test_service', 'test_job', |
| 400 'test_region', 'test_host') |
| 401 self.mock_state = interface.State(target=target) |
| 402 self.mock_state.metrics = copy.copy(interface.state.metrics) |
| 403 self.endpoint_name = '/_ah/spi/TestEndpoint.%s' |
| 404 mock.patch('infra_libs.ts_mon.common.interface.state', |
| 405 new=self.mock_state).start() |
| 406 |
| 407 mock.patch('infra_libs.ts_mon.common.monitors.PubSubMonitor', |
| 408 autospec=True).start() |
| 409 |
| 410 def tearDown(self): |
| 411 config.reset_for_unittest() |
| 412 self.assertEqual([], list(shared.global_metrics_callbacks)) |
| 413 mock.patch.stopall() |
| 414 super(InstrumentEndpointTest, self).tearDown() |
| 415 |
| 416 def test_good(self): |
| 417 self.call_api('do_good') |
| 418 fields = {'name': self.endpoint_name % 'method_good', |
| 419 'status': 200, 'is_robot': False} |
| 420 self.assertEqual(1, http_metrics.server_response_status.get(fields)) |
| 421 self.assertLessEqual(200, http_metrics.server_durations.get(fields).sum) |
| 422 |
| 423 def test_bad(self): |
| 424 with self.call_should_fail('500 Internal Server Error'): |
| 425 self.call_api('do_bad') |
| 426 fields = {'name': self.endpoint_name % 'method_bad', |
| 427 'status': 500, 'is_robot': False} |
| 428 self.assertEqual(1, http_metrics.server_response_status.get(fields)) |
| 429 self.assertLessEqual(200, http_metrics.server_durations.get(fields).sum) |
| 430 |
| 431 def test_400(self): |
| 432 with self.call_should_fail('400 Bad Request'): |
| 433 self.call_api('do_400') |
| 434 fields = {'name': self.endpoint_name % 'method_400', |
| 435 'status': 400, 'is_robot': False} |
| 436 self.assertEqual(1, http_metrics.server_response_status.get(fields)) |
| 437 self.assertLessEqual(200, http_metrics.server_durations.get(fields).sum) |
| 438 |
| 439 @mock.patch('gae_ts_mon.config.need_to_flush_metrics', autospec=True, |
| 440 return_value=False) |
| 441 def test_no_flush(self, _fake): |
| 442 # For branch coverage. |
| 443 self.call_api('do_good') |
| 444 fields = {'name': self.endpoint_name % 'method_good', |
| 445 'status': 200, 'is_robot': False} |
| 446 self.assertEqual(1, http_metrics.server_response_status.get(fields)) |
| 447 self.assertLessEqual(200, http_metrics.server_durations.get(fields).sum) |
| OLD | NEW |