5
from cloudinit.sources.helpers import azure as azure_helper
6
from ..helpers import TestCase
9
from unittest import mock
14
from contextlib import ExitStack
16
from contextlib2 import ExitStack
19
GOAL_STATE_TEMPLATE = """\
20
<?xml version="1.0" encoding="utf-8"?>
21
<GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
22
xsi:noNamespaceSchemaLocation="goalstate10.xsd">
23
<Version>2012-11-30</Version>
24
<Incarnation>{incarnation}</Incarnation>
26
<ExpectedState>Started</ExpectedState>
27
<StopRolesDeadlineHint>300000</StopRolesDeadlineHint>
31
<ExpectHealthReport>FALSE</ExpectHealthReport>
34
<ContainerId>{container_id}</ContainerId>
37
<InstanceId>{instance_id}</InstanceId>
38
<State>Started</State>
40
<HostingEnvironmentConfig>
41
http://100.86.192.70:80/...hostingEnvironmentConfig...
42
</HostingEnvironmentConfig>
43
<SharedConfig>{shared_config_url}</SharedConfig>
45
http://100.86.192.70:80/...extensionsConfig...
47
<FullConfig>http://100.86.192.70:80/...fullConfig...</FullConfig>
48
<Certificates>{certificates_url}</Certificates>
49
<ConfigName>68ce47.0.68ce47.0.utl-trusty--292258.1.xml</ConfigName>
58
class TestReadAzureSharedConfig(unittest.TestCase):
60
def test_valid_content(self):
61
xml = """<?xml version="1.0" encoding="utf-8"?>
63
<Deployment name="MY_INSTANCE_ID">
64
<Service name="myservice"/>
65
<ServiceInstance name="INSTANCE_ID.0" guid="{abcd-uuid}" />
67
<Incarnation number="1"/>
69
ret = azure_helper.iid_from_shared_config_content(xml)
70
self.assertEqual("MY_INSTANCE_ID", ret)
73
class TestFindEndpoint(TestCase):
76
super(TestFindEndpoint, self).setUp()
78
self.addCleanup(patches.close)
80
self.load_file = patches.enter_context(
81
mock.patch.object(azure_helper.util, 'load_file'))
83
def test_missing_file(self):
84
self.load_file.side_effect = IOError
85
self.assertRaises(IOError,
86
azure_helper.WALinuxAgentShim.find_endpoint)
88
def test_missing_special_azure_line(self):
89
self.load_file.return_value = ''
90
self.assertRaises(Exception,
91
azure_helper.WALinuxAgentShim.find_endpoint)
93
def _build_lease_content(self, ip_address, use_hex=True):
94
ip_address_repr = ':'.join(
95
[hex(int(part)).replace('0x', '')
96
for part in ip_address.split('.')])
98
ip_address_repr = struct.pack(
99
'>L', int(ip_address_repr.replace(':', ''), 16))
100
ip_address_repr = '"{0}"'.format(ip_address_repr.decode('utf-8'))
103
' interface "eth0";',
104
' option unknown-245 {0};'.format(ip_address_repr),
107
def test_hex_string(self):
108
ip_address = '98.76.54.32'
109
file_content = self._build_lease_content(ip_address)
110
self.load_file.return_value = file_content
111
self.assertEqual(ip_address,
112
azure_helper.WALinuxAgentShim.find_endpoint())
114
def test_hex_string_with_single_character_part(self):
115
ip_address = '4.3.2.1'
116
file_content = self._build_lease_content(ip_address)
117
self.load_file.return_value = file_content
118
self.assertEqual(ip_address,
119
azure_helper.WALinuxAgentShim.find_endpoint())
121
def test_packed_string(self):
122
ip_address = '98.76.54.32'
123
file_content = self._build_lease_content(ip_address, use_hex=False)
124
self.load_file.return_value = file_content
125
self.assertEqual(ip_address,
126
azure_helper.WALinuxAgentShim.find_endpoint())
128
def test_latest_lease_used(self):
129
ip_addresses = ['4.3.2.1', '98.76.54.32']
130
file_content = '\n'.join([self._build_lease_content(ip_address)
131
for ip_address in ip_addresses])
132
self.load_file.return_value = file_content
133
self.assertEqual(ip_addresses[-1],
134
azure_helper.WALinuxAgentShim.find_endpoint())
137
class TestGoalStateParsing(TestCase):
139
default_parameters = {
141
'container_id': 'MyContainerId',
142
'instance_id': 'MyInstanceId',
143
'shared_config_url': 'MySharedConfigUrl',
144
'certificates_url': 'MyCertificatesUrl',
147
def _get_goal_state(self, http_client=None, **kwargs):
148
if http_client is None:
149
http_client = mock.MagicMock()
150
parameters = self.default_parameters.copy()
151
parameters.update(kwargs)
152
xml = GOAL_STATE_TEMPLATE.format(**parameters)
153
if parameters['certificates_url'] is None:
155
for line in xml.splitlines():
156
if 'Certificates' in line:
158
new_xml_lines.append(line)
159
xml = '\n'.join(new_xml_lines)
160
return azure_helper.GoalState(xml, http_client)
162
def test_incarnation_parsed_correctly(self):
164
goal_state = self._get_goal_state(incarnation=incarnation)
165
self.assertEqual(incarnation, goal_state.incarnation)
167
def test_container_id_parsed_correctly(self):
168
container_id = 'TestContainerId'
169
goal_state = self._get_goal_state(container_id=container_id)
170
self.assertEqual(container_id, goal_state.container_id)
172
def test_instance_id_parsed_correctly(self):
173
instance_id = 'TestInstanceId'
174
goal_state = self._get_goal_state(instance_id=instance_id)
175
self.assertEqual(instance_id, goal_state.instance_id)
177
def test_shared_config_xml_parsed_and_fetched_correctly(self):
178
http_client = mock.MagicMock()
179
shared_config_url = 'TestSharedConfigUrl'
180
goal_state = self._get_goal_state(
181
http_client=http_client, shared_config_url=shared_config_url)
182
shared_config_xml = goal_state.shared_config_xml
183
self.assertEqual(1, http_client.get.call_count)
184
self.assertEqual(shared_config_url, http_client.get.call_args[0][0])
185
self.assertEqual(http_client.get.return_value.contents,
188
def test_certificates_xml_parsed_and_fetched_correctly(self):
189
http_client = mock.MagicMock()
190
certificates_url = 'TestSharedConfigUrl'
191
goal_state = self._get_goal_state(
192
http_client=http_client, certificates_url=certificates_url)
193
certificates_xml = goal_state.certificates_xml
194
self.assertEqual(1, http_client.get.call_count)
195
self.assertEqual(certificates_url, http_client.get.call_args[0][0])
196
self.assertTrue(http_client.get.call_args[1].get('secure', False))
197
self.assertEqual(http_client.get.return_value.contents,
200
def test_missing_certificates_skips_http_get(self):
201
http_client = mock.MagicMock()
202
goal_state = self._get_goal_state(
203
http_client=http_client, certificates_url=None)
204
certificates_xml = goal_state.certificates_xml
205
self.assertEqual(0, http_client.get.call_count)
206
self.assertIsNone(certificates_xml)
209
class TestAzureEndpointHttpClient(TestCase):
212
'x-ms-agent-name': 'WALinuxAgent',
213
'x-ms-version': '2012-11-30',
217
super(TestAzureEndpointHttpClient, self).setUp()
218
patches = ExitStack()
219
self.addCleanup(patches.close)
221
self.read_file_or_url = patches.enter_context(
222
mock.patch.object(azure_helper.util, 'read_file_or_url'))
224
def test_non_secure_get(self):
225
client = azure_helper.AzureEndpointHttpClient(mock.MagicMock())
227
response = client.get(url, secure=False)
228
self.assertEqual(1, self.read_file_or_url.call_count)
229
self.assertEqual(self.read_file_or_url.return_value, response)
230
self.assertEqual(mock.call(url, headers=self.regular_headers),
231
self.read_file_or_url.call_args)
233
def test_secure_get(self):
235
certificate = mock.MagicMock()
236
expected_headers = self.regular_headers.copy()
237
expected_headers.update({
238
"x-ms-cipher-name": "DES_EDE3_CBC",
239
"x-ms-guest-agent-public-x509-cert": certificate,
241
client = azure_helper.AzureEndpointHttpClient(certificate)
242
response = client.get(url, secure=True)
243
self.assertEqual(1, self.read_file_or_url.call_count)
244
self.assertEqual(self.read_file_or_url.return_value, response)
245
self.assertEqual(mock.call(url, headers=expected_headers),
246
self.read_file_or_url.call_args)
249
data = mock.MagicMock()
251
client = azure_helper.AzureEndpointHttpClient(mock.MagicMock())
252
response = client.post(url, data=data)
253
self.assertEqual(1, self.read_file_or_url.call_count)
254
self.assertEqual(self.read_file_or_url.return_value, response)
256
mock.call(url, data=data, headers=self.regular_headers),
257
self.read_file_or_url.call_args)
259
def test_post_with_extra_headers(self):
261
client = azure_helper.AzureEndpointHttpClient(mock.MagicMock())
262
extra_headers = {'test': 'header'}
263
client.post(url, extra_headers=extra_headers)
264
self.assertEqual(1, self.read_file_or_url.call_count)
265
expected_headers = self.regular_headers.copy()
266
expected_headers.update(extra_headers)
268
mock.call(mock.ANY, data=mock.ANY, headers=expected_headers),
269
self.read_file_or_url.call_args)
272
class TestOpenSSLManager(TestCase):
275
super(TestOpenSSLManager, self).setUp()
276
patches = ExitStack()
277
self.addCleanup(patches.close)
279
self.subp = patches.enter_context(
280
mock.patch.object(azure_helper.util, 'subp'))
282
self.open = patches.enter_context(
283
mock.patch('__builtin__.open'))
285
self.open = patches.enter_context(
286
mock.patch('builtins.open'))
288
@mock.patch.object(azure_helper, 'cd', mock.MagicMock())
289
@mock.patch.object(azure_helper.tempfile, 'mkdtemp')
290
def test_openssl_manager_creates_a_tmpdir(self, mkdtemp):
291
manager = azure_helper.OpenSSLManager()
292
self.assertEqual(mkdtemp.return_value, manager.tmpdir)
294
def test_generate_certificate_uses_tmpdir(self):
297
def capture_directory(*args, **kwargs):
298
subp_directory['path'] = os.getcwd()
300
self.subp.side_effect = capture_directory
301
manager = azure_helper.OpenSSLManager()
302
self.assertEqual(manager.tmpdir, subp_directory['path'])
304
@mock.patch.object(azure_helper, 'cd', mock.MagicMock())
305
@mock.patch.object(azure_helper.tempfile, 'mkdtemp', mock.MagicMock())
306
@mock.patch.object(azure_helper.util, 'del_dir')
307
def test_clean_up(self, del_dir):
308
manager = azure_helper.OpenSSLManager()
310
self.assertEqual([mock.call(manager.tmpdir)], del_dir.call_args_list)
313
class TestWALinuxAgentShim(TestCase):
316
super(TestWALinuxAgentShim, self).setUp()
317
patches = ExitStack()
318
self.addCleanup(patches.close)
320
self.AzureEndpointHttpClient = patches.enter_context(
321
mock.patch.object(azure_helper, 'AzureEndpointHttpClient'))
322
self.find_endpoint = patches.enter_context(
324
azure_helper.WALinuxAgentShim, 'find_endpoint'))
325
self.GoalState = patches.enter_context(
326
mock.patch.object(azure_helper, 'GoalState'))
327
self.iid_from_shared_config_content = patches.enter_context(
328
mock.patch.object(azure_helper, 'iid_from_shared_config_content'))
329
self.OpenSSLManager = patches.enter_context(
330
mock.patch.object(azure_helper, 'OpenSSLManager'))
331
patches.enter_context(
332
mock.patch.object(azure_helper.time, 'sleep', mock.MagicMock()))
334
def test_http_client_uses_certificate(self):
335
shim = azure_helper.WALinuxAgentShim()
336
shim.register_with_azure_and_fetch_data()
338
[mock.call(self.OpenSSLManager.return_value.certificate)],
339
self.AzureEndpointHttpClient.call_args_list)
341
def test_correct_url_used_for_goalstate(self):
342
self.find_endpoint.return_value = 'test_endpoint'
343
shim = azure_helper.WALinuxAgentShim()
344
shim.register_with_azure_and_fetch_data()
345
get = self.AzureEndpointHttpClient.return_value.get
347
[mock.call('http://test_endpoint/machine/?comp=goalstate')],
350
[mock.call(get.return_value.contents,
351
self.AzureEndpointHttpClient.return_value)],
352
self.GoalState.call_args_list)
354
def test_certificates_used_to_determine_public_keys(self):
355
shim = azure_helper.WALinuxAgentShim()
356
data = shim.register_with_azure_and_fetch_data()
358
[mock.call(self.GoalState.return_value.certificates_xml)],
359
self.OpenSSLManager.return_value.parse_certificates.call_args_list)
361
self.OpenSSLManager.return_value.parse_certificates.return_value,
364
def test_absent_certificates_produces_empty_public_keys(self):
365
self.GoalState.return_value.certificates_xml = None
366
shim = azure_helper.WALinuxAgentShim()
367
data = shim.register_with_azure_and_fetch_data()
368
self.assertEqual([], data['public-keys'])
370
def test_instance_id_returned_in_data(self):
371
shim = azure_helper.WALinuxAgentShim()
372
data = shim.register_with_azure_and_fetch_data()
374
[mock.call(self.GoalState.return_value.shared_config_xml)],
375
self.iid_from_shared_config_content.call_args_list)
376
self.assertEqual(self.iid_from_shared_config_content.return_value,
379
def test_correct_url_used_for_report_ready(self):
380
self.find_endpoint.return_value = 'test_endpoint'
381
shim = azure_helper.WALinuxAgentShim()
382
shim.register_with_azure_and_fetch_data()
383
expected_url = 'http://test_endpoint/machine?comp=health'
385
[mock.call(expected_url, data=mock.ANY, extra_headers=mock.ANY)],
386
self.AzureEndpointHttpClient.return_value.post.call_args_list)
388
def test_goal_state_values_used_for_report_ready(self):
389
self.GoalState.return_value.incarnation = 'TestIncarnation'
390
self.GoalState.return_value.container_id = 'TestContainerId'
391
self.GoalState.return_value.instance_id = 'TestInstanceId'
392
shim = azure_helper.WALinuxAgentShim()
393
shim.register_with_azure_and_fetch_data()
395
self.AzureEndpointHttpClient.return_value.post.call_args[1]['data']
397
self.assertIn('TestIncarnation', posted_document)
398
self.assertIn('TestContainerId', posted_document)
399
self.assertIn('TestInstanceId', posted_document)
401
def test_clean_up_can_be_called_at_any_time(self):
402
shim = azure_helper.WALinuxAgentShim()
405
def test_clean_up_will_clean_up_openssl_manager_if_instantiated(self):
406
shim = azure_helper.WALinuxAgentShim()
407
shim.register_with_azure_and_fetch_data()
410
1, self.OpenSSLManager.return_value.clean_up.call_count)
412
def test_failure_to_fetch_goalstate_bubbles_up(self):
413
class SentinelException(Exception):
415
self.AzureEndpointHttpClient.return_value.get.side_effect = (
417
shim = azure_helper.WALinuxAgentShim()
418
self.assertRaises(SentinelException,
419
shim.register_with_azure_and_fetch_data)
422
class TestGetMetadataFromFabric(TestCase):
424
@mock.patch.object(azure_helper, 'WALinuxAgentShim')
425
def test_data_from_shim_returned(self, shim):
426
ret = azure_helper.get_metadata_from_fabric()
428
shim.return_value.register_with_azure_and_fetch_data.return_value,
431
@mock.patch.object(azure_helper, 'WALinuxAgentShim')
432
def test_success_calls_clean_up(self, shim):
433
azure_helper.get_metadata_from_fabric()
434
self.assertEqual(1, shim.return_value.clean_up.call_count)
436
@mock.patch.object(azure_helper, 'WALinuxAgentShim')
437
def test_failure_in_registration_calls_clean_up(self, shim):
438
class SentinelException(Exception):
440
shim.return_value.register_with_azure_and_fetch_data.side_effect = (
442
self.assertRaises(SentinelException,
443
azure_helper.get_metadata_from_fabric)
444
self.assertEqual(1, shim.return_value.clean_up.call_count)