Merge ~chad.smith/cloud-init:ubuntu/devel into cloud-init:ubuntu/devel

Proposed by Chad Smith
Status: Merged
Merged at revision: 51bc1f6a58d13fee344d2f52e16dcec38368f3be
Proposed branch: ~chad.smith/cloud-init:ubuntu/devel
Merge into: cloud-init:ubuntu/devel
Diff against target: 207 lines (+92/-15)
5 files modified
cloudinit/config/cc_resizefs.py (+7/-0)
cloudinit/sources/DataSourceAzure.py (+8/-1)
debian/changelog (+9/-0)
tests/unittests/test_datasource/test_azure.py (+28/-6)
tests/unittests/test_handler/test_handler_resizefs.py (+40/-8)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
cloud-init Commiters Pending
Review via email: mp+358884@code.launchpad.net

Commit message

Sync upstream snapshot for disco release. Includes an Azure pre-provisioning bugfix

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:51bc1f6a58d13fee344d2f52e16dcec38368f3be
https://jenkins.ubuntu.com/server/job/cloud-init-ci/451/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    IN_PROGRESS: Declarative: Post Actions

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

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py
2index 2edddd0..076b9d5 100644
3--- a/cloudinit/config/cc_resizefs.py
4+++ b/cloudinit/config/cc_resizefs.py
5@@ -197,6 +197,13 @@ def maybe_get_writable_device_path(devpath, info, log):
6 if devpath.startswith('gpt/'):
7 log.debug('We have a gpt label - just go ahead')
8 return devpath
9+ # Alternatively, our device could simply be a name as returned by gpart,
10+ # such as da0p3
11+ if not devpath.startswith('/dev/') and not os.path.exists(devpath):
12+ fulldevpath = '/dev/' + devpath.lstrip('/')
13+ log.debug("'%s' doesn't appear to be a valid device path. Trying '%s'",
14+ devpath, fulldevpath)
15+ devpath = fulldevpath
16
17 try:
18 statret = os.stat(devpath)
19diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
20index 9e8a1a8..2a3e567 100644
21--- a/cloudinit/sources/DataSourceAzure.py
22+++ b/cloudinit/sources/DataSourceAzure.py
23@@ -526,6 +526,13 @@ class DataSourceAzure(sources.DataSource):
24 report_ready = bool(not os.path.isfile(REPORTED_READY_MARKER_FILE))
25 LOG.debug("Start polling IMDS")
26
27+ def exc_cb(msg, exception):
28+ if isinstance(exception, UrlError) and exception.code == 404:
29+ return True
30+ # If we get an exception while trying to call IMDS, we
31+ # call DHCP and setup the ephemeral network to acquire the new IP.
32+ return False
33+
34 while True:
35 try:
36 # Save our EphemeralDHCPv4 context so we avoid repeated dhcp
37@@ -540,7 +547,7 @@ class DataSourceAzure(sources.DataSource):
38 self._report_ready(lease=lease)
39 report_ready = False
40 return readurl(url, timeout=1, headers=headers,
41- exception_cb=retry_on_url_exc, infinite=True,
42+ exception_cb=exc_cb, infinite=True,
43 log_req_resp=False).contents
44 except UrlError:
45 # Teardown our EphemeralDHCPv4 context on failure as we retry
46diff --git a/debian/changelog b/debian/changelog
47index a85c8cc..f93ba9c 100644
48--- a/debian/changelog
49+++ b/debian/changelog
50@@ -1,3 +1,12 @@
51+cloud-init (18.4-24-g8f812a15-0ubuntu1) disco; urgency=medium
52+
53+ * New upstream snapshot.
54+ - azure: _poll_imds only retry on 404. Fail on Timeout (LP: #1803598)
55+ - resizefs: Prefix discovered devpath with '/dev/' when path does not
56+ exist [Igor Galić]
57+
58+ -- Chad Smith <chad.smith@canonical.com> Thu, 15 Nov 2018 16:11:20 -0700
59+
60 cloud-init (18.4-22-g6062595b-0ubuntu1) disco; urgency=medium
61
62 * New upstream snapshot.
63diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
64index 56484b2..5ea7ae5 100644
65--- a/tests/unittests/test_datasource/test_azure.py
66+++ b/tests/unittests/test_datasource/test_azure.py
67@@ -1687,22 +1687,44 @@ class TestPreprovisioningPollIMDS(CiTestCase):
68 self.paths = helpers.Paths({'cloud_dir': self.tmp})
69 dsaz.BUILTIN_DS_CONFIG['data_dir'] = self.waagent_d
70
71- @mock.patch(MOCKPATH + 'util.write_file')
72- def test_poll_imds_calls_report_ready(self, write_f, report_ready_func,
73+ @mock.patch(MOCKPATH + 'EphemeralDHCPv4')
74+ def test_poll_imds_re_dhcp_on_timeout(self, m_dhcpv4, report_ready_func,
75 fake_resp, m_dhcp, m_net):
76- """The poll_imds will call report_ready after creating marker file."""
77- report_marker = self.tmp_path('report_marker', self.tmp)
78+ """The poll_imds will retry DHCP on IMDS timeout."""
79+ report_file = self.tmp_path('report_marker', self.tmp)
80 lease = {
81 'interface': 'eth9', 'fixed-address': '192.168.2.9',
82 'routers': '192.168.2.1', 'subnet-mask': '255.255.255.0',
83 'unknown-245': '624c3620'}
84 m_dhcp.return_value = [lease]
85+
86+ dhcp_ctx = mock.MagicMock(lease=lease)
87+ dhcp_ctx.obtain_lease.return_value = lease
88+ m_dhcpv4.return_value = dhcp_ctx
89+
90+ self.tries = 0
91+
92+ def fake_timeout_once(**kwargs):
93+ self.tries += 1
94+ if self.tries == 1:
95+ raise requests.Timeout('Fake connection timeout')
96+ elif self.tries == 2:
97+ response = requests.Response()
98+ response.status_code = 404
99+ raise requests.exceptions.HTTPError(
100+ "fake 404", response=response)
101+ # Third try should succeed and stop retries or redhcp
102+ return mock.MagicMock(status_code=200, text="good", content="good")
103+
104+ fake_resp.side_effect = fake_timeout_once
105+
106 dsa = dsaz.DataSourceAzure({}, distro=None, paths=self.paths)
107- mock_path = (MOCKPATH + 'REPORTED_READY_MARKER_FILE')
108- with mock.patch(mock_path, report_marker):
109+ with mock.patch(MOCKPATH + 'REPORTED_READY_MARKER_FILE', report_file):
110 dsa._poll_imds()
111 self.assertEqual(report_ready_func.call_count, 1)
112 report_ready_func.assert_called_with(lease=lease)
113+ self.assertEqual(2, m_dhcpv4.call_count, 'Expected 2 DHCP calls')
114+ self.assertEqual(3, self.tries, 'Expected 3 total reads from IMDS')
115
116 def test_poll_imds_report_ready_false(self, report_ready_func,
117 fake_resp, m_dhcp, m_net):
118diff --git a/tests/unittests/test_handler/test_handler_resizefs.py b/tests/unittests/test_handler/test_handler_resizefs.py
119index feca56c..6ebacb1 100644
120--- a/tests/unittests/test_handler/test_handler_resizefs.py
121+++ b/tests/unittests/test_handler/test_handler_resizefs.py
122@@ -173,6 +173,38 @@ class TestResizefs(CiTestCase):
123
124 self.assertEqual(('zpool', 'online', '-e', 'vmzroot', disk), ret)
125
126+ @mock.patch('cloudinit.util.is_container', return_value=False)
127+ @mock.patch('cloudinit.util.get_mount_info')
128+ @mock.patch('cloudinit.util.get_device_info_from_zpool')
129+ @mock.patch('cloudinit.util.parse_mount')
130+ def test_handle_modern_zfsroot(self, mount_info, zpool_info, parse_mount,
131+ is_container):
132+ devpth = 'zroot/ROOT/default'
133+ disk = 'da0p3'
134+ fs_type = 'zfs'
135+ mount_point = '/'
136+
137+ mount_info.return_value = (devpth, fs_type, mount_point)
138+ zpool_info.return_value = disk
139+ parse_mount.return_value = (devpth, fs_type, mount_point)
140+
141+ cfg = {'resize_rootfs': True}
142+
143+ def fake_stat(devpath):
144+ if devpath == disk:
145+ raise OSError("not here")
146+ FakeStat = namedtuple(
147+ 'FakeStat', ['st_mode', 'st_size', 'st_mtime']) # minimal stat
148+ return FakeStat(25008, 0, 1) # fake char block device
149+
150+ with mock.patch('cloudinit.config.cc_resizefs.do_resize') as dresize:
151+ with mock.patch('cloudinit.config.cc_resizefs.os.stat') as m_stat:
152+ m_stat.side_effect = fake_stat
153+ handle('cc_resizefs', cfg, _cloud=None, log=LOG, args=[])
154+
155+ self.assertEqual(('zpool', 'online', '-e', 'zroot', '/dev/' + disk),
156+ dresize.call_args[0][0])
157+
158
159 class TestRootDevFromCmdline(CiTestCase):
160
161@@ -246,39 +278,39 @@ class TestMaybeGetDevicePathAsWritableBlock(CiTestCase):
162
163 def test_maybe_get_writable_device_path_does_not_exist(self):
164 """When devpath does not exist, a warning is logged."""
165- info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none'
166+ info = 'dev=/dev/I/dont/exist mnt_point=/ path=/dev/none'
167 devpath = wrap_and_call(
168 'cloudinit.config.cc_resizefs.util',
169 {'is_container': {'return_value': False}},
170- maybe_get_writable_device_path, '/I/dont/exist', info, LOG)
171+ maybe_get_writable_device_path, '/dev/I/dont/exist', info, LOG)
172 self.assertIsNone(devpath)
173 self.assertIn(
174- "WARNING: Device '/I/dont/exist' did not exist."
175+ "WARNING: Device '/dev/I/dont/exist' did not exist."
176 ' cannot resize: %s' % info,
177 self.logs.getvalue())
178
179 def test_maybe_get_writable_device_path_does_not_exist_in_container(self):
180 """When devpath does not exist in a container, log a debug message."""
181- info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none'
182+ info = 'dev=/dev/I/dont/exist mnt_point=/ path=/dev/none'
183 devpath = wrap_and_call(
184 'cloudinit.config.cc_resizefs.util',
185 {'is_container': {'return_value': True}},
186- maybe_get_writable_device_path, '/I/dont/exist', info, LOG)
187+ maybe_get_writable_device_path, '/dev/I/dont/exist', info, LOG)
188 self.assertIsNone(devpath)
189 self.assertIn(
190- "DEBUG: Device '/I/dont/exist' did not exist in container."
191+ "DEBUG: Device '/dev/I/dont/exist' did not exist in container."
192 ' cannot resize: %s' % info,
193 self.logs.getvalue())
194
195 def test_maybe_get_writable_device_path_raises_oserror(self):
196 """When unexpected OSError is raises by os.stat it is reraised."""
197- info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none'
198+ info = 'dev=/dev/I/dont/exist mnt_point=/ path=/dev/none'
199 with self.assertRaises(OSError) as context_manager:
200 wrap_and_call(
201 'cloudinit.config.cc_resizefs',
202 {'util.is_container': {'return_value': True},
203 'os.stat': {'side_effect': OSError('Something unexpected')}},
204- maybe_get_writable_device_path, '/I/dont/exist', info, LOG)
205+ maybe_get_writable_device_path, '/dev/I/dont/exist', info, LOG)
206 self.assertEqual(
207 'Something unexpected', str(context_manager.exception))
208

Subscribers

People subscribed via source and target branches