diff options
author | Max Illfelder <illfelder@google.com> | 2016-11-09 12:45:06 -0800 |
---|---|---|
committer | Max Illfelder <illfelder@google.com> | 2016-11-09 12:45:06 -0800 |
commit | 3cb1e5e85c1d268d99103100164264747f030c88 (patch) | |
tree | 6754b763f36405457ec5ad728ed4cec90e733830 | |
parent | 14a50a6217a39e1f50f2ebade802cf76500a673a (diff) | |
parent | a33d244812a53c8c5ff974ce3225f4f540ad7e59 (diff) | |
download | google-compute-image-packages-3cb1e5e85c1d268d99103100164264747f030c88.tar.gz |
Merge branch 'development'
14 files changed, 169 insertions, 54 deletions
@@ -227,6 +227,11 @@ using a configuration file. To make configuration changes, add settings to `/etc/default/instance_configs.cfg.template`. Settings are not overridden in the guest. +Linux distributions looking to include their own defaults can specify settings +in `/etc/default/instance_configs.cfg.distro`. These settings will not override +`/etc/default/instance_configs.cfg.template`. This enables distribution settings +that do not override user configuration during package update. + The following are valid user configuration options. Section | Option | Value diff --git a/google_compute_engine/accounts/accounts_daemon.py b/google_compute_engine/accounts/accounts_daemon.py index 5b9779d..fd20f80 100755 --- a/google_compute_engine/accounts/accounts_daemon.py +++ b/google_compute_engine/accounts/accounts_daemon.py @@ -19,6 +19,7 @@ import datetime import json import logging.handlers import optparse +import random from google_compute_engine import config_manager from google_compute_engine import file_utils @@ -51,7 +52,9 @@ class AccountsDaemon(object): try: with file_utils.LockFile(LOCKFILE): self.logger.info('Starting Google Accounts daemon.') - self.watcher.WatchMetadata(self.HandleAccounts, recursive=True) + timeout = 60 + random.randint(0, 30) + self.watcher.WatchMetadata( + self.HandleAccounts, recursive=True, timeout=timeout) except (IOError, OSError) as e: self.logger.warning(str(e)) diff --git a/google_compute_engine/accounts/accounts_utils.py b/google_compute_engine/accounts/accounts_utils.py index 4096086..fbdd5ef 100644 --- a/google_compute_engine/accounts/accounts_utils.py +++ b/google_compute_engine/accounts/accounts_utils.py @@ -76,10 +76,16 @@ class AccountsUtils(object): self.logger.warning('Could not create the sudoers group. %s.', str(e)) if not os.path.exists(self.google_sudoers_file): - with open(self.google_sudoers_file, 'w') as group: - message = '%{0} ALL=(ALL:ALL) NOPASSWD:ALL'.format( - self.google_sudoers_group) - group.write(message) + try: + with open(self.google_sudoers_file, 'w') as group: + message = '%{0} ALL=(ALL:ALL) NOPASSWD:ALL'.format( + self.google_sudoers_group) + group.write(message) + except IOError as e: + self.logger.error( + 'Could not write sudoers file. %s. %s', + self.google_sudoers_file, str(e)) + return file_utils.SetPermissions( self.google_sudoers_file, mode=0o440, uid=0, gid=0) diff --git a/google_compute_engine/accounts/tests/accounts_daemon_test.py b/google_compute_engine/accounts/tests/accounts_daemon_test.py index 3fd830d..2b6700b 100644 --- a/google_compute_engine/accounts/tests/accounts_daemon_test.py +++ b/google_compute_engine/accounts/tests/accounts_daemon_test.py @@ -59,7 +59,7 @@ class AccountsDaemonTest(unittest.TestCase): mock.call.lock.LockFile().__enter__(), mock.call.logger.Logger().info(mock.ANY), mock.call.watcher.MetadataWatcher().WatchMetadata( - mock_handle, recursive=True), + mock_handle, recursive=True, timeout=mock.ANY), mock.call.lock.LockFile().__exit__(None, None, None), ] self.assertEqual(mocks.mock_calls, expected_calls) diff --git a/google_compute_engine/accounts/tests/accounts_utils_test.py b/google_compute_engine/accounts/tests/accounts_utils_test.py index c10d963..7401831 100644 --- a/google_compute_engine/accounts/tests/accounts_utils_test.py +++ b/google_compute_engine/accounts/tests/accounts_utils_test.py @@ -148,6 +148,25 @@ class AccountsUtilsTest(unittest.TestCase): ] self.assertEqual(mocks.mock_calls, expected_calls) + @mock.patch('google_compute_engine.accounts.accounts_utils.os.path.exists') + def testCreateSudoersGroupWriteError(self, mock_exists): + mock_open = mock.mock_open() + mocks = mock.Mock() + mocks.attach_mock(mock_exists, 'exists') + mocks.attach_mock(self.mock_utils._GetGroup, 'group') + mocks.attach_mock(self.mock_logger, 'logger') + self.mock_utils._GetGroup.return_value = True + mock_exists.return_value = False + mock_open.side_effect = IOError() + + accounts_utils.AccountsUtils._CreateSudoersGroup(self.mock_utils) + expected_calls = [ + mock.call.group(self.sudoers_group), + mock.call.exists(self.sudoers_file), + mock.call.logger.error(mock.ANY, self.sudoers_file, mock.ANY), + ] + self.assertEqual(mocks.mock_calls, expected_calls) + @mock.patch('google_compute_engine.accounts.accounts_utils.pwd') def testGetUser(self, mock_pwd): mock_pwd.getpwnam.return_value = 'Test' diff --git a/google_compute_engine/ip_forwarding/ip_forwarding_daemon.py b/google_compute_engine/ip_forwarding/ip_forwarding_daemon.py index 04c0e09..45b0ce7 100755 --- a/google_compute_engine/ip_forwarding/ip_forwarding_daemon.py +++ b/google_compute_engine/ip_forwarding/ip_forwarding_daemon.py @@ -16,8 +16,9 @@ """Manage IP forwarding on a Google Compute Engine instance. Fetch a list of public endpoint IPs from the metadata server, compare it with -the IPs configured on eth0, and add or remove addresses from eth0 to make them -match. Only remove those which match our proto code. +the IPs configured the associated interfaces, and add or remove addresses from +the interfaces to make them match. Only remove those which match our proto +code. Command used to add IPs: ip route add to local $IP/32 dev eth0 proto 66 @@ -27,6 +28,7 @@ Command used to fetch list of configured IPs: import logging.handlers import optparse +import random from google_compute_engine import config_manager from google_compute_engine import file_utils @@ -61,9 +63,10 @@ class IpForwardingDaemon(object): try: with file_utils.LockFile(LOCKFILE): self.logger.info('Starting Google IP Forwarding daemon.') + timeout = 60 + random.randint(0, 30) self.watcher.WatchMetadata( self.HandleNetworkInterfaces, metadata_key=self.network_interfaces, - recursive=True) + recursive=True, timeout=timeout) except (IOError, OSError) as e: self.logger.warning(str(e)) @@ -81,7 +84,7 @@ class IpForwardingDaemon(object): if not to_add and not to_remove: return self.logger.info( - 'Changing %s forwarded IPs from %s to %s by adding %s and removing %s.', + 'Changing %s IPs from %s to %s by adding %s and removing %s.', interface, configured or None, desired or None, to_add or None, to_remove or None) @@ -125,14 +128,16 @@ class IpForwardingDaemon(object): """Called when network interface metadata changes. Args: - result: string, the metadata response with the new network interfaces. + result: dict, the metadata response with the new network interfaces. """ for network_interface in result: mac_address = network_interface.get('mac') interface = self.network_utils.GetNetworkInterface(mac_address) + ip_addresses = [] if interface: - forwarded_ips = network_interface.get('forwardedIps') - self._HandleForwardedIps(forwarded_ips, interface) + ip_addresses.extend(network_interface.get('forwardedIps', [])) + ip_addresses.extend(network_interface.get('ipAliases', [])) + self._HandleForwardedIps(ip_addresses, interface) else: message = 'Network interface not found for MAC address: %s.' self.logger.warning(message, mac_address) diff --git a/google_compute_engine/ip_forwarding/ip_forwarding_utils.py b/google_compute_engine/ip_forwarding/ip_forwarding_utils.py index c7f6cdf..201d326 100644 --- a/google_compute_engine/ip_forwarding/ip_forwarding_utils.py +++ b/google_compute_engine/ip_forwarding/ip_forwarding_utils.py @@ -19,6 +19,7 @@ import re import subprocess IP_REGEX = re.compile(r'\A(\d{1,3}\.){3}\d{1,3}\Z') +IP_ALIAS_REGEX = re.compile(r'\A(\d{1,3}\.){3}\d{1,3}/\d{1,2}\Z') class IpForwardingUtils(object): @@ -92,7 +93,7 @@ class IpForwardingUtils(object): addresses = [] forwarded_ips = forwarded_ips or [] for ip in forwarded_ips: - if ip and IP_REGEX.match(ip): + if ip and (IP_REGEX.match(ip) or IP_ALIAS_REGEX.match(ip)): addresses.append(ip) else: self.logger.warning('Could not parse IP address: "%s".', ip) @@ -119,7 +120,8 @@ class IpForwardingUtils(object): address: string, the IP address to configure. interface: string, the output device to use. """ - args = ['add', 'to', 'local', '%s/32' % address] + address = address if IP_ALIAS_REGEX.match(address) else '%s/32' % address + args = ['add', 'to', 'local', address] options = self._CreateRouteOptions(dev=interface) self._RunIpRoute(args=args, options=options) @@ -130,6 +132,7 @@ class IpForwardingUtils(object): address: string, the IP address to configure. interface: string, the output device to use. """ - args = ['delete', 'to', 'local', '%s/32' % address] + address = address if IP_ALIAS_REGEX.match(address) else '%s/32' % address + args = ['delete', 'to', 'local', address] options = self._CreateRouteOptions(dev=interface) self._RunIpRoute(args=args, options=options) diff --git a/google_compute_engine/ip_forwarding/tests/ip_forwarding_daemon_test.py b/google_compute_engine/ip_forwarding/tests/ip_forwarding_daemon_test.py index 71dee68..4c0f32f 100644 --- a/google_compute_engine/ip_forwarding/tests/ip_forwarding_daemon_test.py +++ b/google_compute_engine/ip_forwarding/tests/ip_forwarding_daemon_test.py @@ -66,7 +66,8 @@ class IpForwardingDaemonTest(unittest.TestCase): mock.call.lock.LockFile().__enter__(), mock.call.logger.Logger().info(mock.ANY), mock.call.watcher.MetadataWatcher().WatchMetadata( - mock_handle, metadata_key=metadata_key, recursive=True), + mock_handle, metadata_key=metadata_key, recursive=True, + timeout=mock.ANY), mock.call.lock.LockFile().__exit__(None, None, None), ] self.assertEqual(mocks.mock_calls, expected_calls) @@ -179,23 +180,26 @@ class IpForwardingDaemonTest(unittest.TestCase): mocks.attach_mock(self.mock_network_utils, 'network') mocks.attach_mock(self.mock_setup, 'setup') self.mock_network_utils.GetNetworkInterface.side_effect = [ - 'eth0', 'eth1', 'eth2', None] + 'eth0', 'eth1', 'eth2', 'eth3', None] result = [ - {'mac': '1', 'forwardedIps': 'a'}, - {'mac': '2', 'forwardedIps': 'b'}, - {'mac': '3'}, - {'forwardedIps': 'c'}, + {'mac': '1', 'forwardedIps': ['a']}, + {'mac': '2', 'forwardedIps': ['b'], 'ipAliases': ['banana']}, + {'mac': '3', 'ipAliases': ['cherry']}, + {'mac': '4'}, + {'forwardedIps': ['d'], 'ipAliases': ['date']}, ] ip_forwarding_daemon.IpForwardingDaemon.HandleNetworkInterfaces( self.mock_setup, result) expected_calls = [ mock.call.network.GetNetworkInterface('1'), - mock.call.setup._HandleForwardedIps('a', 'eth0'), + mock.call.setup._HandleForwardedIps(['a'], 'eth0'), mock.call.network.GetNetworkInterface('2'), - mock.call.setup._HandleForwardedIps('b', 'eth1'), + mock.call.setup._HandleForwardedIps(['b', 'banana'], 'eth1'), mock.call.network.GetNetworkInterface('3'), - mock.call.setup._HandleForwardedIps(None, 'eth2'), + mock.call.setup._HandleForwardedIps(['cherry'], 'eth2'), + mock.call.network.GetNetworkInterface('4'), + mock.call.setup._HandleForwardedIps([], 'eth3'), mock.call.network.GetNetworkInterface(None), mock.call.setup.logger.warning(mock.ANY, None), ] diff --git a/google_compute_engine/ip_forwarding/tests/ip_forwarding_utils_test.py b/google_compute_engine/ip_forwarding/tests/ip_forwarding_utils_test.py index 18774a6..5543d13 100644 --- a/google_compute_engine/ip_forwarding/tests/ip_forwarding_utils_test.py +++ b/google_compute_engine/ip_forwarding/tests/ip_forwarding_utils_test.py @@ -137,6 +137,12 @@ class IpForwardingUtilsTest(unittest.TestCase): '1.1.1.a': False, None: False, '1.0.0.0': True, + '1.1.1.1/1': True, + '1.1.1.1/11': True, + '123.123.123.123/1': True, + '123.123.123.123/123': False, + '123.123.123.123/a': False, + '123.123.123.123/': False, } input_ips = forwarded_ips.keys() valid_ips = [ip for ip, valid in forwarded_ips.items() if valid] @@ -175,6 +181,18 @@ class IpForwardingUtilsTest(unittest.TestCase): mock_run.assert_called_once_with( args=['add', 'to', 'local', '1.1.1.1/32'], options=self.options) + def testAddIpAlias(self): + mock_options = mock.Mock() + mock_options.return_value = self.options + mock_run = mock.Mock() + self.mock_utils._CreateRouteOptions = mock_options + self.mock_utils._RunIpRoute = mock_run + + self.mock_utils.AddForwardedIp('1.1.1.1/24', 'interface') + mock_options.assert_called_once_with(dev='interface') + mock_run.assert_called_once_with( + args=['add', 'to', 'local', '1.1.1.1/24'], options=self.options) + def testRemoveForwardedIp(self): mock_options = mock.Mock() mock_options.return_value = self.options @@ -187,6 +205,18 @@ class IpForwardingUtilsTest(unittest.TestCase): mock_run.assert_called_once_with( args=['delete', 'to', 'local', '1.1.1.1/32'], options=self.options) + def testRemoveAliasIp(self): + mock_options = mock.Mock() + mock_options.return_value = self.options + mock_run = mock.Mock() + self.mock_utils._CreateRouteOptions = mock_options + self.mock_utils._RunIpRoute = mock_run + + self.mock_utils.RemoveForwardedIp('1.1.1.1/24', 'interface') + mock_options.assert_called_once_with(dev='interface') + mock_run.assert_called_once_with( + args=['delete', 'to', 'local', '1.1.1.1/24'], options=self.options) + if __name__ == '__main__': unittest.main() diff --git a/google_compute_engine/metadata_scripts/script_retriever.py b/google_compute_engine/metadata_scripts/script_retriever.py index a5c77a5..2568c8b 100644 --- a/google_compute_engine/metadata_scripts/script_retriever.py +++ b/google_compute_engine/metadata_scripts/script_retriever.py @@ -50,6 +50,14 @@ class ScriptRetriever(object): Returns: string, the path to the file storing the metadata script. """ + try: + subprocess.check_call( + ['which', 'gsutil'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except subprocess.CalledProcessError: + self.logger.warning( + 'gsutil is not installed, cannot download items from Google Storage.') + return None + dest_file = tempfile.NamedTemporaryFile(dir=dest_dir, delete=False) dest_file.close() dest = dest_file.name diff --git a/google_compute_engine/metadata_scripts/tests/script_retriever_test.py b/google_compute_engine/metadata_scripts/tests/script_retriever_test.py index 9fa1e79..60701d4 100644 --- a/google_compute_engine/metadata_scripts/tests/script_retriever_test.py +++ b/google_compute_engine/metadata_scripts/tests/script_retriever_test.py @@ -34,6 +34,15 @@ class ScriptRetrieverTest(unittest.TestCase): self.mock_logger, self.script_type) @mock.patch('google_compute_engine.metadata_scripts.script_retriever.subprocess.check_call') + def testDownloadGsNoExec(self, mock_call): + mock_call.side_effect = subprocess.CalledProcessError('foo', 'bar') + gs_url = 'gs://fake/url' + self.assertIsNone(self.retriever._DownloadGsUrl(gs_url, self.dest_dir)) + mock_call.assert_called_once_with( + ['which', 'gsutil'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.mock_logger.warning.assert_called_once_with(mock.ANY) + + @mock.patch('google_compute_engine.metadata_scripts.script_retriever.subprocess.check_call') @mock.patch('google_compute_engine.metadata_scripts.script_retriever.tempfile.NamedTemporaryFile') def testDownloadGsUrl(self, mock_tempfile, mock_call): gs_url = 'gs://fake/url' @@ -44,7 +53,7 @@ class ScriptRetrieverTest(unittest.TestCase): mock_tempfile.assert_called_once_with(dir=self.dest_dir, delete=False) mock_tempfile.close.assert_called_once_with() self.mock_logger.info.assert_called_once_with(mock.ANY, gs_url, self.dest) - mock_call.assert_called_once_with(['gsutil', 'cp', gs_url, self.dest]) + mock_call.assert_called_with(['gsutil', 'cp', gs_url, self.dest]) self.mock_logger.warning.assert_not_called() @mock.patch('google_compute_engine.metadata_scripts.script_retriever.subprocess.check_call') @@ -53,7 +62,7 @@ class ScriptRetrieverTest(unittest.TestCase): gs_url = 'gs://fake/url' mock_tempfile.return_value = mock_tempfile mock_tempfile.name = self.dest - mock_call.side_effect = subprocess.CalledProcessError(1, 'Test') + mock_call.side_effect = [0, subprocess.CalledProcessError(1, 'Test')] self.assertIsNone(self.retriever._DownloadGsUrl(gs_url, self.dest_dir)) self.assertEqual(self.mock_logger.warning.call_count, 1) @@ -63,7 +72,7 @@ class ScriptRetrieverTest(unittest.TestCase): gs_url = 'gs://fake/url' mock_tempfile.return_value = mock_tempfile mock_tempfile.name = self.dest - mock_call.side_effect = Exception('Error.') + mock_call.side_effect = [0, Exception('Error.')] self.assertIsNone(self.retriever._DownloadGsUrl(gs_url, self.dest_dir)) self.assertEqual(self.mock_logger.warning.call_count, 1) diff --git a/google_compute_engine/metadata_watcher.py b/google_compute_engine/metadata_watcher.py index e7da49b..3ff648d 100644 --- a/google_compute_engine/metadata_watcher.py +++ b/google_compute_engine/metadata_watcher.py @@ -76,12 +76,13 @@ class MetadataWatcher(object): self.timeout = timeout @RetryOnUnavailable - def _GetMetadataRequest(self, metadata_url, params=None): + def _GetMetadataRequest(self, metadata_url, params=None, timeout=None): """Performs a GET request with the metadata headers. Args: metadata_url: string, the URL to perform a GET request on. params: dictionary, the query parameters in the GET request. + timeout: int, timeout in seconds for metadata requests. Returns: HTTP response from the GET request. @@ -94,7 +95,8 @@ class MetadataWatcher(object): url = '%s?%s' % (metadata_url, params) request = urlrequest.Request(url, headers=headers) request_opener = urlrequest.build_opener(urlrequest.ProxyHandler({})) - return request_opener.open(request, timeout=self.timeout*1.1) + timeout = timeout or self.timeout + return request_opener.open(request, timeout=timeout*1.1) def _UpdateEtag(self, response): """Update the etag from an API response. @@ -110,13 +112,15 @@ class MetadataWatcher(object): self.etag = etag return etag_updated - def _GetMetadataUpdate(self, metadata_key='', recursive=True, wait=True): + def _GetMetadataUpdate( + self, metadata_key='', recursive=True, wait=True, timeout=None): """Request the contents of metadata server and deserialize the response. Args: metadata_key: string, the metadata key to watch for changes. recursive: bool, True if we should recursively watch for metadata changes. wait: bool, True if we should wait for a metadata change. + timeout: int, timeout in seconds for returning metadata output. Returns: json, the deserialized contents of the metadata server. @@ -127,27 +131,33 @@ class MetadataWatcher(object): 'alt': 'json', 'last_etag': self.etag, 'recursive': recursive, - 'timeout_sec': self.timeout, + 'timeout_sec': timeout or self.timeout, 'wait_for_change': wait, } while True: - response = self._GetMetadataRequest(metadata_url, params=params) + response = self._GetMetadataRequest( + metadata_url, params=params, timeout=timeout) etag_updated = self._UpdateEtag(response) - if wait and not etag_updated: + if wait and not etag_updated and not timeout: # Retry until the etag is updated. continue else: - # Waiting for change is not required or the etag is updated. + # One of the following are true: + # - Waiting for change is not required. + # - The etag is updated. + # - The user specified a request timeout. break return json.loads(response.read().decode('utf-8')) - def _HandleMetadataUpdate(self, metadata_key='', recursive=True, wait=True): + def _HandleMetadataUpdate( + self, metadata_key='', recursive=True, wait=True, timeout=None): """Wait for a successful metadata response. Args: metadata_key: string, the metadata key to watch for changes. recursive: bool, True if we should recursively watch for metadata changes. wait: bool, True if we should wait for a metadata change. + timeout: int, timeout in seconds for returning metadata output. Returns: json, the deserialized contents of the metadata server. @@ -156,7 +166,8 @@ class MetadataWatcher(object): while True: try: return self._GetMetadataUpdate( - metadata_key=metadata_key, recursive=recursive, wait=wait) + metadata_key=metadata_key, recursive=recursive, wait=wait, + timeout=timeout) except (httpclient.HTTPException, socket.error, urlerror.URLError) as e: if isinstance(e, type(exception)): continue @@ -164,31 +175,36 @@ class MetadataWatcher(object): exception = e self.logger.exception('GET request error retrieving metadata.') - def WatchMetadata(self, handler, metadata_key='', recursive=True): + def WatchMetadata( + self, handler, metadata_key='', recursive=True, timeout=None): """Watch for changes to the contents of the metadata server. Args: handler: callable, a function to call with the updated metadata contents. metadata_key: string, the metadata key to watch for changes. recursive: bool, True if we should recursively watch for metadata changes. + timeout: int, timeout in seconds for returning metadata output. """ while True: response = self._HandleMetadataUpdate( - metadata_key=metadata_key, recursive=recursive, wait=True) + metadata_key=metadata_key, recursive=recursive, wait=True, + timeout=timeout) try: handler(response) except Exception as e: self.logger.exception('Exception calling the response handler. %s.', e) - def GetMetadata(self, metadata_key='', recursive=True): + def GetMetadata(self, metadata_key='', recursive=True, timeout=None): """Retrieve the contents of metadata server for a metadata key. Args: metadata_key: string, the metadata key to watch for changes. recursive: bool, True if we should recursively watch for metadata changes. + timeout: int, timeout in seconds for returning metadata output. Returns: json, the deserialized contents of the metadata server or None if error. """ return self._HandleMetadataUpdate( - metadata_key=metadata_key, recursive=recursive, wait=False) + metadata_key=metadata_key, recursive=recursive, wait=False, + timeout=timeout) diff --git a/google_compute_engine/tests/metadata_watcher_test.py b/google_compute_engine/tests/metadata_watcher_test.py index a1e5240..2245711 100644 --- a/google_compute_engine/tests/metadata_watcher_test.py +++ b/google_compute_engine/tests/metadata_watcher_test.py @@ -191,7 +191,8 @@ class MetadataWatcherTest(unittest.TestCase): self.assertEqual(self.mock_watcher._GetMetadataUpdate(), {}) self.assertEqual(self.mock_watcher.etag, 1) - mock_response.assert_called_once_with(request_url, params=self.params) + mock_response.assert_called_once_with( + request_url, params=self.params, timeout=None) def testGetMetadataUpdateArgs(self): mock_response = mock.Mock() @@ -205,9 +206,10 @@ class MetadataWatcherTest(unittest.TestCase): request_url = os.path.join(self.url, metadata_key) self.mock_watcher._GetMetadataUpdate( - metadata_key=metadata_key, recursive=False, wait=False) + metadata_key=metadata_key, recursive=False, wait=False, timeout=60) self.assertEqual(self.mock_watcher.etag, 0) - mock_response.assert_called_once_with(request_url, params=self.params) + mock_response.assert_called_once_with( + request_url, params=self.params, timeout=60) def testGetMetadataUpdateWait(self): self.params['last_etag'] = 1 @@ -225,7 +227,9 @@ class MetadataWatcherTest(unittest.TestCase): self.mock_watcher._GetMetadataUpdate() self.assertEqual(self.mock_watcher.etag, 2) - expected_calls = [mock.call(request_url, params=self.params)] * 3 + expected_calls = [ + mock.call(request_url, params=self.params, timeout=None), + ] * 3 self.assertEqual(mock_response.mock_calls, expected_calls) def testHandleMetadataUpdate(self): @@ -235,7 +239,7 @@ class MetadataWatcherTest(unittest.TestCase): self.assertEqual(self.mock_watcher.GetMetadata(), {}) mock_response.assert_called_once_with( - metadata_key='', recursive=True, wait=False) + metadata_key='', recursive=True, wait=False, timeout=None) self.mock_watcher.logger.exception.assert_not_called() def testHandleMetadataUpdateException(self): @@ -250,10 +254,13 @@ class MetadataWatcherTest(unittest.TestCase): self.assertEqual( self.mock_watcher._HandleMetadataUpdate( - metadata_key=metadata_key, recursive=recursive, wait=wait), + metadata_key=metadata_key, recursive=recursive, wait=wait, + timeout=None), {}) expected_calls = [ - mock.call(metadata_key=metadata_key, recursive=recursive, wait=wait), + mock.call( + metadata_key=metadata_key, recursive=recursive, wait=wait, + timeout=None), ] * 4 self.assertEqual(mock_response.mock_calls, expected_calls) expected_calls = [mock.call.exception(mock.ANY)] * 2 @@ -272,7 +279,7 @@ class MetadataWatcherTest(unittest.TestCase): self.mock_watcher.WatchMetadata(mock_handler, recursive=recursive) mock_handler.assert_called_once_with({}) mock_response.assert_called_once_with( - metadata_key='', recursive=recursive, wait=True) + metadata_key='', recursive=recursive, wait=True, timeout=None) def testWatchMetadataException(self): mock_response = mock.Mock() @@ -286,7 +293,7 @@ class MetadataWatcherTest(unittest.TestCase): self.mock_watcher.WatchMetadata( None, metadata_key=metadata_key, recursive=recursive) mock_response.assert_called_once_with( - metadata_key=metadata_key, recursive=recursive, wait=True) + metadata_key=metadata_key, recursive=recursive, wait=True, timeout=None) def testGetMetadata(self): mock_response = mock.Mock() @@ -295,7 +302,7 @@ class MetadataWatcherTest(unittest.TestCase): self.assertEqual(self.mock_watcher.GetMetadata(), {}) mock_response.assert_called_once_with( - metadata_key='', recursive=True, wait=False) + metadata_key='', recursive=True, wait=False, timeout=None) self.mock_watcher.logger.exception.assert_not_called() def testGetMetadataArgs(self): @@ -306,10 +313,10 @@ class MetadataWatcherTest(unittest.TestCase): recursive = False response = self.mock_watcher.GetMetadata( - metadata_key=metadata_key, recursive=recursive) + metadata_key=metadata_key, recursive=recursive, timeout=60) self.assertEqual(response, {}) mock_response.assert_called_once_with( - metadata_key=metadata_key, recursive=False, wait=False) + metadata_key=metadata_key, recursive=False, wait=False, timeout=60) self.mock_watcher.logger.exception.assert_not_called() @@ -25,7 +25,7 @@ setuptools.setup( author_email='gc-team@google.com', description='Google Compute Engine', include_package_data=True, - install_requires=['boto'], + install_requires=['boto', 'setuptools'], license='Apache Software License', long_description='Google Compute Engine guest environment.', name='google-compute-engine', |