| # Copyright 2015 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import json |
| import os |
| import requests |
| import tempfile |
| import unittest |
| |
| import mock |
| |
| from testing_support import auto_stub |
| |
| from infra_libs.ts_mon import config |
| from infra_libs.ts_mon.common import interface |
| from infra_libs.ts_mon.common import standard_metrics |
| from infra_libs.ts_mon.common import monitors |
| from infra_libs.ts_mon.common import targets |
| from infra_libs.ts_mon.common.test import stubs |
| import infra_libs |
| |
| |
| DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data') |
| |
| |
| class GlobalsTest(auto_stub.TestCase): |
| |
| def setUp(self): |
| super(GlobalsTest, self).setUp() |
| self.mock(config, 'load_machine_config', lambda x: {}) |
| |
| def tearDown(self): |
| # It's important to call close() before un-setting the mock state object, |
| # because any FlushThread started by the test is stored in that mock state |
| # and needs to be stopped before running any other tests. |
| interface.close() |
| # This should probably live in interface.close() |
| interface.state = interface.State() |
| super(GlobalsTest, self).tearDown() |
| |
| @mock.patch('requests.get', autospec=True) |
| @mock.patch('socket.getfqdn', autospec=True) |
| def test_pubsub_monitor_args(self, fake_fqdn, fake_get): |
| fake_fqdn.return_value = 'slave1-a1.reg.tld' |
| fake_get.return_value.side_effect = requests.exceptions.ConnectionError |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args([ |
| '--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-endpoint', 'pubsub://invalid-project/invalid-topic']) |
| |
| config.process_argparse_options(args) |
| |
| self.assertIsInstance(interface.state.global_monitor, |
| monitors.PubSubMonitor) |
| |
| self.assertIsInstance(interface.state.target, targets.DeviceTarget) |
| self.assertEquals(interface.state.target.hostname, 'slave1-a1') |
| self.assertEquals(interface.state.target.region, 'reg') |
| self.assertEquals(args.ts_mon_flush, 'auto') |
| self.assertIsNotNone(interface.state.flush_thread) |
| self.assertTrue(standard_metrics.up.get()) |
| |
| @mock.patch('requests.get', autospec=True) |
| @mock.patch('socket.getfqdn', autospec=True) |
| @mock.patch('infra_libs.ts_mon.common.monitors.HttpsMonitor.' |
| '_load_credentials', autospec=True) |
| def test_https_monitor_args(self, _load_creds, fake_fqdn, fake_get): |
| print [_load_creds, fake_fqdn, fake_get] |
| fake_fqdn.return_value = 'slave1-a1.reg.tld' |
| fake_get.return_value.side_effect = requests.exceptions.ConnectionError |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args([ |
| '--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-endpoint', 'https://test/random:insert']) |
| |
| config.process_argparse_options(args) |
| |
| self.assertIsInstance(interface.state.global_monitor, |
| monitors.HttpsMonitor) |
| |
| self.assertIsInstance(interface.state.target, targets.DeviceTarget) |
| self.assertEquals(interface.state.target.hostname, 'slave1-a1') |
| self.assertEquals(interface.state.target.region, 'reg') |
| self.assertEquals(args.ts_mon_flush, 'auto') |
| self.assertIsNotNone(interface.state.flush_thread) |
| self.assertTrue(standard_metrics.up.get()) |
| |
| @mock.patch('requests.get', autospec=True) |
| @mock.patch('socket.getfqdn', autospec=True) |
| def test_default_target_uppercase_fqdn(self, fake_fqdn, fake_get): |
| fake_fqdn.return_value = 'SLAVE1-A1.REG.TLD' |
| fake_get.return_value.side_effect = requests.exceptions.ConnectionError |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args([ |
| '--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-endpoint', 'unsupported://www.googleapis.com/some/api']) |
| config.process_argparse_options(args) |
| self.assertIsInstance(interface.state.target, targets.DeviceTarget) |
| self.assertEquals(interface.state.target.hostname, 'slave1-a1') |
| self.assertEquals(interface.state.target.region, 'reg') |
| |
| @mock.patch('requests.get', autospec=True) |
| @mock.patch('socket.getfqdn', autospec=True) |
| def test_default_target_fqdn_without_domain(self, fake_fqdn, fake_get): |
| fake_fqdn.return_value = 'SLAVE1-A1' |
| fake_get.return_value.side_effect = requests.exceptions.ConnectionError |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args([ |
| '--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-endpoint', 'unsupported://www.googleapis.com/some/api']) |
| config.process_argparse_options(args) |
| self.assertIsInstance(interface.state.target, targets.DeviceTarget) |
| self.assertEquals(interface.state.target.hostname, 'slave1-a1') |
| self.assertEquals(interface.state.target.region, '') |
| |
| @mock.patch('requests.get', autospec=True) |
| @mock.patch('socket.getfqdn', autospec=True) |
| def test_fallback_monitor_args(self, fake_fqdn, fake_get): |
| fake_fqdn.return_value = 'foo' |
| fake_get.return_value.side_effect = requests.exceptions.ConnectionError |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args([ |
| '--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-endpoint', 'unsupported://www.googleapis.com/some/api']) |
| config.process_argparse_options(args) |
| |
| self.assertIsInstance(interface.state.global_monitor, |
| monitors.NullMonitor) |
| |
| @mock.patch('requests.get', autospec=True) |
| @mock.patch('socket.getfqdn', autospec=True) |
| def test_explicit_disable_args(self, fake_fqdn, fake_get): |
| fake_fqdn.return_value = 'foo' |
| fake_get.return_value.side_effect = requests.exceptions.ConnectionError |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args([ |
| '--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-endpoint', 'none']) |
| config.process_argparse_options(args) |
| |
| self.assertIsInstance(interface.state.global_monitor, |
| monitors.NullMonitor) |
| |
| @mock.patch('requests.get', autospec=True) |
| @mock.patch('socket.getfqdn', autospec=True) |
| def test_manual_flush(self, fake_fqdn, fake_get): |
| fake_fqdn.return_value = 'foo' |
| fake_get.return_value.side_effect = requests.exceptions.ConnectionError |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-flush', 'manual']) |
| |
| config.process_argparse_options(args) |
| self.assertIsNone(interface.state.flush_thread) |
| |
| @mock.patch('infra_libs.ts_mon.common.monitors.PubSubMonitor', autospec=True) |
| def test_pubsub_args(self, fake_monitor): |
| singleton = mock.Mock() |
| fake_monitor.return_value = singleton |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-endpoint', 'pubsub://mytopic/myproject']) |
| config.process_argparse_options(args) |
| fake_monitor.assert_called_once_with( |
| '/path/to/creds.p8.json', 'mytopic', 'myproject', |
| use_instrumented_http=True) |
| self.assertIs(interface.state.global_monitor, singleton) |
| |
| @mock.patch('infra_libs.ts_mon.common.monitors.PubSubMonitor', autospec=True) |
| def test_pubsub_without_credentials(self, fake_monitor): |
| # safety net, not supposed to be called. |
| singleton = mock.Mock() |
| fake_monitor.return_value = singleton |
| |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-config-file', |
| os.path.join(DATA_DIR, 'empty-config-file.json'), |
| '--ts-mon-endpoint', 'pubsub://mytopic/myproject']) |
| config.process_argparse_options(args) |
| self.assertIsInstance(interface.state.global_monitor, monitors.NullMonitor) |
| |
| @mock.patch('infra_libs.ts_mon.common.monitors.DebugMonitor', auto_spec=True) |
| def test_dryrun_args(self, fake_monitor): |
| singleton = mock.Mock() |
| fake_monitor.return_value = singleton |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-endpoint', 'file://foo.txt']) |
| config.process_argparse_options(args) |
| fake_monitor.assert_called_once_with('foo.txt') |
| self.assertIs(interface.state.global_monitor, singleton) |
| |
| def test_device_args(self): |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-target-type', 'device', |
| '--ts-mon-device-region', 'reg', |
| '--ts-mon-device-role', 'role', |
| '--ts-mon-device-network', 'net', |
| '--ts-mon-device-hostname', 'host']) |
| config.process_argparse_options(args) |
| self.assertEqual(interface.state.target.region, 'reg') |
| self.assertEqual(interface.state.target.role, 'role') |
| self.assertEqual(interface.state.target.network, 'net') |
| self.assertEqual(interface.state.target.hostname, 'host') |
| |
| def test_autogen_device_args(self): |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-target-type', 'device', |
| '--ts-mon-device-region', 'reg', |
| '--ts-mon-device-role', 'role', |
| '--ts-mon-device-network', 'net', |
| '--ts-mon-device-hostname', 'host', |
| '--ts-mon-autogen-hostname']) |
| config.process_argparse_options(args) |
| self.assertEqual(interface.state.target.region, 'reg') |
| self.assertEqual(interface.state.target.role, 'role') |
| self.assertEqual(interface.state.target.network, 'net') |
| self.assertEqual(interface.state.target.hostname, 'autogen:host') |
| |
| def test_autogen_device_config(self): |
| self.mock(config, 'load_machine_config', lambda x: { |
| 'autogen_hostname': True, |
| 'credentials': '/path/to/creds.p8.json', |
| 'endpoint': 'test://endpoint'}) |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args([ |
| '--ts-mon-target-type', 'device', |
| '--ts-mon-device-region', 'reg', |
| '--ts-mon-device-role', 'role', |
| '--ts-mon-device-network', 'net', |
| '--ts-mon-device-hostname', 'host']) |
| config.process_argparse_options(args) |
| self.assertEqual(interface.state.target.region, 'reg') |
| self.assertEqual(interface.state.target.role, 'role') |
| self.assertEqual(interface.state.target.network, 'net') |
| self.assertEqual(interface.state.target.hostname, 'autogen:host') |
| |
| |
| def test_task_args(self): |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-target-type', 'task', |
| '--ts-mon-task-service-name', 'serv', |
| '--ts-mon-task-job-name', 'job', |
| '--ts-mon-task-region', 'reg', |
| '--ts-mon-task-hostname', 'host', |
| '--ts-mon-task-number', '1']) |
| config.process_argparse_options(args) |
| self.assertEqual(interface.state.target.service_name, 'serv') |
| self.assertEqual(interface.state.target.job_name, 'job') |
| self.assertEqual(interface.state.target.region, 'reg') |
| self.assertEqual(interface.state.target.hostname, 'host') |
| self.assertEqual(interface.state.target.task_num, 1) |
| |
| def test_autogen_task_args(self): |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-target-type', 'task', |
| '--ts-mon-task-service-name', 'serv', |
| '--ts-mon-task-job-name', 'job', |
| '--ts-mon-task-region', 'reg', |
| '--ts-mon-task-hostname', 'host', |
| '--ts-mon-task-number', '1', |
| '--ts-mon-autogen-hostname']) |
| config.process_argparse_options(args) |
| self.assertEqual(interface.state.target.service_name, 'serv') |
| self.assertEqual(interface.state.target.job_name, 'job') |
| self.assertEqual(interface.state.target.region, 'reg') |
| self.assertEqual(interface.state.target.hostname, 'autogen:host') |
| self.assertEqual(interface.state.target.task_num, 1) |
| |
| def test_autogen_task_config(self): |
| self.mock(config, 'load_machine_config', lambda x: { |
| 'autogen_hostname': True, |
| 'credentials': '/path/to/creds.p8.json', |
| 'endpoint': 'test://endpoint'}) |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-target-type', 'task', |
| '--ts-mon-task-service-name', 'serv', |
| '--ts-mon-task-job-name', 'job', |
| '--ts-mon-task-region', 'reg', |
| '--ts-mon-task-hostname', 'host', |
| '--ts-mon-task-number', '1']) |
| config.process_argparse_options(args) |
| self.assertEqual(interface.state.target.service_name, 'serv') |
| self.assertEqual(interface.state.target.job_name, 'job') |
| self.assertEqual(interface.state.target.region, 'reg') |
| self.assertEqual(interface.state.target.hostname, 'autogen:host') |
| self.assertEqual(interface.state.target.task_num, 1) |
| |
| def test_task_args_missing_service_name(self): |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-target-type', 'task', |
| '--ts-mon-task-job-name', 'job', |
| '--ts-mon-task-region', 'reg', |
| '--ts-mon-task-hostname', 'host', |
| '--ts-mon-task-number', '1']) |
| with self.assertRaises(SystemExit): |
| config.process_argparse_options(args) |
| |
| def test_task_args_missing_job_name(self): |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-credentials', '/path/to/creds.p8.json', |
| '--ts-mon-target-type', 'task', |
| '--ts-mon-task-service-name', 'serv', |
| '--ts-mon-task-region', 'reg', |
| '--ts-mon-task-hostname', 'host', |
| '--ts-mon-task-number', '1']) |
| with self.assertRaises(SystemExit): |
| config.process_argparse_options(args) |
| |
| def test_metric_name_prefix(self): |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args(['--ts-mon-metric-name-prefix', '/test/random/']) |
| config.process_argparse_options(args) |
| self.assertEqual('/test/random/', interface.state.metric_name_prefix) |
| |
| @mock.patch('infra_libs.ts_mon.common.monitors.NullMonitor', autospec=True) |
| def test_no_args(self, fake_monitor): |
| singleton = mock.Mock() |
| fake_monitor.return_value = singleton |
| p = argparse.ArgumentParser() |
| config.add_argparse_options(p) |
| args = p.parse_args([]) |
| config.process_argparse_options(args) |
| self.assertEqual(1, len(fake_monitor.mock_calls)) |
| self.assertEqual('/chrome/infra/', interface.state.metric_name_prefix) |
| self.assertIs(interface.state.global_monitor, singleton) |
| |
| @mock.patch('requests.get', autospec=True) |
| def test_gce_region(self, mock_get): |
| r = mock_get.return_value |
| r.status_code = 200 |
| r.text = 'projects/182615506979/zones/us-central1-f' |
| |
| self.assertEquals('us-central1-f', config._default_region('foo.golo')) |
| |
| @mock.patch('requests.get', autospec=True) |
| def test_gce_region_timeout(self, mock_get): |
| mock_get.side_effect = requests.exceptions.Timeout |
| self.assertEquals('golo', config._default_region('foo.golo')) |
| |
| @mock.patch('requests.get', autospec=True) |
| def test_gce_region_404(self, mock_get): |
| r = mock_get.return_value |
| r.status_code = 404 |
| |
| self.assertEquals('golo', config._default_region('foo.golo')) |
| |
| |
| class ConfigTest(unittest.TestCase): |
| |
| def test_load_machine_config(self): |
| with infra_libs.temporary_directory() as temp_dir: |
| filename = os.path.join(temp_dir, 'config') |
| with open(filename, 'w') as fh: |
| json.dump({'foo': 'bar'}, fh) |
| |
| self.assertEquals({'foo': 'bar'}, config.load_machine_config(filename)) |
| |
| def test_load_machine_config_bad(self): |
| with infra_libs.temporary_directory() as temp_dir: |
| filename = os.path.join(temp_dir, 'config') |
| with open(filename, 'w') as fh: |
| fh.write('not a json file') |
| |
| with self.assertRaises(ValueError): |
| config.load_machine_config(filename) |
| |
| def test_load_machine_config_not_exists(self): |
| self.assertEquals({}, config.load_machine_config('does not exist')) |