Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions cloudinit/sources/DataSourceGCE.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,20 @@

class GoogleMetadataFetcher(object):

def __init__(self, metadata_address):
def __init__(self, metadata_address, num_retries, sec_between_retries):
self.metadata_address = metadata_address
self.num_retries = num_retries
self.sec_between_retries = sec_between_retries

def get_value(self, path, is_text, is_recursive=False):
value = None
try:
url = self.metadata_address + path
if is_recursive:
url += '/?recursive=True'
resp = url_helper.readurl(url=url, headers=HEADERS)
resp = url_helper.readurl(url=url, headers=HEADERS,
retries=self.num_retries,
sec_between=self.sec_between_retries)
except url_helper.UrlError as exc:
msg = "url %s raised exception %s"
LOG.debug(msg, path, exc)
Expand Down Expand Up @@ -68,9 +72,11 @@ def __init__(self, sys_cfg, distro, paths):
self.metadata_address = self.ds_cfg['metadata_url']

def _get_data(self):
url_params = self.get_url_params()
ret = util.log_time(
LOG.debug, 'Crawl of GCE metadata service',
read_md, kwargs={'address': self.metadata_address})
read_md, kwargs={'address': self.metadata_address,
'url_params': url_params})

if not ret['success']:
if ret['platform_reports_gce']:
Expand Down Expand Up @@ -176,7 +182,7 @@ def _parse_public_keys(public_keys_data, default_user=None):
return public_keys


def read_md(address=None, platform_check=True):
def read_md(address=None, url_params=None, platform_check=True):

if address is None:
address = MD_V1_URL
Expand All @@ -203,8 +209,9 @@ def read_md(address=None, platform_check=True):
('instance-data', ('instance/attributes',), False, False, True),
('project-data', ('project/attributes',), False, False, True),
]

metadata_fetcher = GoogleMetadataFetcher(address)
metadata_fetcher = GoogleMetadataFetcher(address,
url_params.num_retries,
url_params.sec_between_retries)
md = {}
# Iterate over url_map keys to get metadata items.
for (mkey, paths, required, is_text, is_recursive) in url_map:
Expand Down
23 changes: 18 additions & 5 deletions cloudinit/sources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ def redact_sensitive_keys(metadata, redact_value=REDACT_SENSITIVE_VALUE):


URLParams = namedtuple(
'URLParms', ['max_wait_seconds', 'timeout_seconds', 'num_retries'])
'URLParms', ['max_wait_seconds', 'timeout_seconds',
'num_retries', 'sec_between_retries'])


class DataSource(CloudInitPickleMixin, metaclass=abc.ABCMeta):
Expand Down Expand Up @@ -175,9 +176,10 @@ class DataSource(CloudInitPickleMixin, metaclass=abc.ABCMeta):
NetworkConfigSource.ds)

# read_url_params
url_max_wait = -1 # max_wait < 0 means do not wait
url_timeout = 10 # timeout for each metadata url read attempt
url_retries = 5 # number of times to retry url upon 404
url_max_wait = -1 # max_wait < 0 means do not wait
url_timeout = 10 # timeout for each metadata url read attempt
url_retries = 5 # number of times to retry url upon 404
url_sec_between_retries = 1 # amount of seconds to wait between retries

# The datasource defines a set of supported EventTypes during which
# the datasource can react to changes in metadata and regenerate
Expand Down Expand Up @@ -422,7 +424,18 @@ def get_url_params(self):
LOG, "Config retries '%s' is not an int, using default '%s'",
self.ds_cfg.get('retries'), retries)

return URLParams(max_wait, timeout, retries)
sec_between_retries = self.url_sec_between_retries
try:
sec_between_retries = int(self.ds_cfg.get(
"sec_between_retries",
self.url_sec_between_retries))
except Exception:
util.logexc(
LOG, "Config sec_between_retries '%s' is not an int,"
" using default '%s'",
self.ds_cfg.get("sec_between_retries"), sec_between_retries)

return URLParams(max_wait, timeout, retries, sec_between_retries)

def get_userdata(self, apply_filter=False):
if self.userdata is None:
Expand Down
17 changes: 11 additions & 6 deletions cloudinit/sources/tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,16 @@ def test_datasource_get_url_params_defaults(self):
self.assertEqual(params.max_wait_seconds, self.datasource.url_max_wait)
self.assertEqual(params.timeout_seconds, self.datasource.url_timeout)
self.assertEqual(params.num_retries, self.datasource.url_retries)
self.assertEqual(params.sec_between_retries,
self.datasource.url_sec_between_retries)

def test_datasource_get_url_params_subclassed(self):
"""Subclasses can override get_url_params defaults."""
sys_cfg = {'datasource': {'MyTestSubclass': {'key2': False}}}
distro = 'distrotest' # generally should be a Distro object
datasource = DataSourceTestSubclassNet(sys_cfg, distro, self.paths)
expected = (datasource.url_max_wait, datasource.url_timeout,
datasource.url_retries)
datasource.url_retries, datasource.url_sec_between_retries)
url_params = datasource.get_url_params()
self.assertNotEqual(self.datasource.get_url_params(), url_params)
self.assertEqual(expected, url_params)
Expand All @@ -114,14 +116,16 @@ def test_datasource_get_url_params_ds_config_override(self):
sys_cfg = {
'datasource': {
'MyTestSubclass': {
'max_wait': '1', 'timeout': '2', 'retries': '3'}}}
'max_wait': '1', 'timeout': '2',
'retries': '3', 'sec_between_retries': 4
}}}
datasource = DataSourceTestSubclassNet(
sys_cfg, self.distro, self.paths)
expected = (1, 2, 3)
expected = (1, 2, 3, 4)
url_params = datasource.get_url_params()
self.assertNotEqual(
(datasource.url_max_wait, datasource.url_timeout,
datasource.url_retries),
datasource.url_retries, datasource.url_sec_between_retries),
url_params)
self.assertEqual(expected, url_params)

Expand All @@ -130,7 +134,8 @@ def test_datasource_get_url_params_is_zero_or_greater(self):
# Set an override that is below 0 which gets ignored.
sys_cfg = {'datasource': {'_undef': {'timeout': '-1'}}}
datasource = DataSource(sys_cfg, self.distro, self.paths)
(_max_wait, timeout, _retries) = datasource.get_url_params()
(_max_wait, timeout, _retries,
_sec_between_retries) = datasource.get_url_params()
self.assertEqual(0, timeout)

def test_datasource_get_url_uses_defaults_on_errors(self):
Expand All @@ -142,7 +147,7 @@ def test_datasource_get_url_uses_defaults_on_errors(self):
datasource = DataSource(sys_cfg, self.distro, self.paths)
url_params = datasource.get_url_params()
expected = (datasource.url_max_wait, datasource.url_timeout,
datasource.url_retries)
datasource.url_retries, datasource.url_sec_between_retries)
self.assertEqual(expected, url_params)
logs = self.logs.getvalue()
expected_logs = [
Expand Down
22 changes: 22 additions & 0 deletions doc/rtd/topics/datasources/gce.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ to provide ``public-keys``.
``user-data`` and ``user-data-encoding`` can be provided to cloud-init by
setting those custom metadata keys for an *instance*.

Configuration
-------------
The following configuration can be set for the datasource in system
configuration (in `/etc/cloud/cloud.cfg` or `/etc/cloud/cloud.cfg.d/`).

The settings that may be configured are:

* **retries**: The number of retries that should be done for an http request.
This value is used only after metadata_url is selected. (default: 5)
* **sec_between_retries**: The amount of wait time between the retries when
crawling the metadata service. (default: 1)


An example configuration with the default values is provided below:

.. sourcecode:: yaml

datasource:
GCE:
retries: 5
sec_between_retries: 1

.. _GCE metadata docs: https://cloud.google.com/compute/docs/storing-retrieving-metadata#querying

.. vi: textwidth=78
1 change: 1 addition & 0 deletions tools/.github-cla-signers
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ timothegenzmer
tnt-dev
tomponline
tsanghan
vteratipally
Vultaire
WebSpider
xiachen-rh
Expand Down