Merge ~smoser/cloud-init:cleanup/consistent-exception_cb into cloud-init:master

Proposed by Scott Moser
Status: Work in progress
Proposed branch: ~smoser/cloud-init:cleanup/consistent-exception_cb
Merge into: cloud-init:master
Diff against target: 162 lines (+30/-33)
5 files modified
cloudinit/ec2_utils.py (+6/-10)
cloudinit/sources/DataSourceAzure.py (+2/-2)
cloudinit/sources/DataSourceScaleway.py (+9/-5)
cloudinit/sources/helpers/openstack.py (+5/-7)
cloudinit/url_helper.py (+8/-9)
Reviewer Review Type Date Requested Status
Scott Moser Abstain
Chad Smith Needs Fixing
Server Team CI bot continuous-integration Approve
Review via email: mp+342007@code.launchpad.net

Commit message

Make exception_cb consistent in readurl and wait_for_url.

This message needs improving.

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:42a9b8e599167b2a8f004873a99b1ab00f346530
https://jenkins.ubuntu.com/server/job/cloud-init-ci/920/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/920/rebuild

review: Approve (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) :
Revision history for this message
Chad Smith (chad.smith) wrote :

_raise_cause_if_not_found is broken as it expects 3 parameters. I think we need to also scrub the other callsites to clarify that param1 in exc_cb is "args" not msg". And we need some additional unit test coverage on readurl w/ exception_cb to make this less risky overall (and to show examples of the intended behavior of exception_cb).

Holding this up until after SRU is cut to avoid adding risk to SRU.

review: Needs Fixing
Revision history for this message
Scott Moser (smoser) wrote :

OK.
A bit of digging.

readurl and wait_for_url currently call exceptin_cb differently.
currently:
- readurl calls exception_cb(req_args, exception)
   req_args: a copy of kwargs that went to requests.request.
   exception: the exception that occurred
- wait_for_url calls exception_cb(status_msg, exception)
   status_msg: a string like "Calling <url> failed [0/120s]: <why>".
   exception: the exception that occurred.

I'll dig and see if the callers ever actually use the argument
that isnt 'exception'. And then see if we can't do something consistent
there.

so this needs some work still.

review: Approve
Revision history for this message
Scott Moser (smoser) :
review: Abstain
Revision history for this message
Scott Moser (smoser) wrote :

due to last comment, marking wip

Unmerged commits

42a9b8e... by Scott Moser

Make exception_cb consistent in readurl and wait_for_url.

This message needs improving.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/ec2_utils.py b/cloudinit/ec2_utils.py
2index dc3f0fc..18553f0 100644
3--- a/cloudinit/ec2_utils.py
4+++ b/cloudinit/ec2_utils.py
5@@ -134,9 +134,9 @@ class MetadataMaterializer(object):
6 return joined
7
8
9-def _skip_retry_on_codes(status_codes, _request_args, cause):
10- """Returns False if cause.code is in status_codes."""
11- return cause.code not in status_codes
12+def _raise_cause_if_not_found(status_codes, _request_args, cause):
13+ if cause.code not in SKIP_USERDATA_CODES:
14+ raise cause
15
16
17 def get_instance_userdata(api_version='latest',
18@@ -148,13 +148,9 @@ def get_instance_userdata(api_version='latest',
19 try:
20 # It is ok for userdata to not exist (thats why we are stopping if
21 # NOT_FOUND occurs) and just in that case returning an empty string.
22- exception_cb = functools.partial(_skip_retry_on_codes,
23- SKIP_USERDATA_CODES)
24- response = util.read_file_or_url(ud_url,
25- ssl_details=ssl_details,
26- timeout=timeout,
27- retries=retries,
28- exception_cb=exception_cb)
29+ response = util.read_file_or_url(
30+ ud_url, ssl_details=ssl_details, timeout=timeout, retries=retries,
31+ exception_cb=_raise_cause_if_not_found)
32 user_data = response.contents
33 except url_helper.UrlError as e:
34 if e.code not in SKIP_USERDATA_CODES:
35diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
36index 0ee622e..b2244f5 100644
37--- a/cloudinit/sources/DataSourceAzure.py
38+++ b/cloudinit/sources/DataSourceAzure.py
39@@ -452,10 +452,10 @@ class DataSourceAzure(sources.DataSource):
40
41 def exc_cb(msg, exception):
42 if isinstance(exception, UrlError) and exception.code == 404:
43- return True
44+ raise exception
45 # If we get an exception while trying to call IMDS, we
46 # call DHCP and setup the ephemeral network to acquire the new IP.
47- return False
48+ return
49
50 need_report = report_ready
51 while True:
52diff --git a/cloudinit/sources/DataSourceScaleway.py b/cloudinit/sources/DataSourceScaleway.py
53index e2502b0..a89e125 100644
54--- a/cloudinit/sources/DataSourceScaleway.py
55+++ b/cloudinit/sources/DataSourceScaleway.py
56@@ -103,21 +103,25 @@ def query_data_api_once(api_address, timeout, requests_session):
57 ports below 1024). If requests raises ConnectionError (EADDRINUSE), the
58 caller should retry to call this function on an other port.
59 """
60+ def _exc_cb(msg, exception):
61+ """raise the provided exception if it is not 404."""
62+ if (exception.code == 404 or
63+ isinstance(exception.cause,
64+ requests.exceptions.ConnectionError)):
65+ raise exception
66+
67 try:
68 resp = url_helper.readurl(
69 api_address,
70 data=None,
71 timeout=timeout,
72- # It's the caller's responsability to recall this function in case
73+ # It's the caller's responsibility to recall this function in case
74 # of exception. Don't let url_helper.readurl() retry by itself.
75 retries=0,
76 session=requests_session,
77 # If the error is a HTTP/404 or a ConnectionError, go into raise
78 # block below and don't bother retrying.
79- exception_cb=lambda _, exc: exc.code != 404 and (
80- not isinstance(exc.cause, requests.exceptions.ConnectionError)
81- )
82- )
83+ exception_cb=_exc_cb)
84 return util.decode_binary(resp.contents)
85 except url_helper.UrlError as exc:
86 # Empty user data.
87diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py
88index 26f3168..b29a00e 100644
89--- a/cloudinit/sources/helpers/openstack.py
90+++ b/cloudinit/sources/helpers/openstack.py
91@@ -456,17 +456,15 @@ class MetadataReader(BaseReader):
92 try:
93 code = int(cause.code)
94 if code >= 400:
95- return False
96+ return
97 except (TypeError, ValueError):
98 # Older versions of requests didn't have a code.
99 pass
100- return True
101+ raise cause
102
103- response = url_helper.readurl(path,
104- retries=self.retries,
105- ssl_details=self.ssl_details,
106- timeout=self.timeout,
107- exception_cb=should_retry_cb)
108+ response = url_helper.readurl(
109+ path, retries=self.retries, ssl_details=self.ssl_details,
110+ timeout=self.timeout, exception_cb=should_retry_cb)
111 if decode:
112 return response.contents.decode()
113 else:
114diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py
115index 03a573a..26b42ba 100644
116--- a/cloudinit/url_helper.py
117+++ b/cloudinit/url_helper.py
118@@ -260,11 +260,11 @@ def readurl(url, data=None, timeout=None, retries=0, sec_between=1,
119 # ssl exceptions are not going to get fixed by waiting a
120 # few seconds
121 break
122- if exception_cb and not exception_cb(req_args.copy(), excps[-1]):
123- # if an exception callback was given, it should return True
124- # to continue retrying and False to break and re-raise the
125- # exception
126- break
127+ if exception_cb:
128+ # Any exceptions that an exception_cb raises (including
129+ # re-raising this one) are not caught here.
130+ exception_cb(req_args.copy(), excps[-1])
131+
132 if (infinite and sec_between > 0) or \
133 (i + 1 < manual_tries and sec_between > 0):
134 LOG.debug("Please wait %s seconds while we wait to try again",
135@@ -290,6 +290,8 @@ def wait_for_url(urls, max_wait=None, timeout=None,
136 for request.
137 exception_cb: call method with 2 arguments 'msg' (per status_cb) and
138 'exception', the exception that occurred.
139+ if this method raises exception, then that will not be
140+ caught.
141 sleep_time_cb: call method with 2 arguments (response, loop_n) that
142 generates the next sleep time.
143
144@@ -375,9 +377,6 @@ def wait_for_url(urls, max_wait=None, timeout=None,
145 reason)
146 status_cb(status_msg)
147 if exception_cb:
148- # This can be used to alter the headers that will be sent
149- # in the future, for example this is what the MAAS datasource
150- # does.
151 exception_cb(msg=status_msg, exception=url_exc)
152
153 if timeup(max_wait, start_time):
154@@ -487,7 +486,7 @@ class OauthUrlHelper(object):
155 ret = None
156 try:
157 if extra_exception_cb:
158- ret = extra_exception_cb(msg, exception)
159+ extra_exception_cb(msg, exception)
160 finally:
161 self.exception_cb(msg, exception)
162 return ret

Subscribers

People subscribed via source and target branches