1
# Copyright 2014 Microsoft Corporation
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
# you may not use this file except in compliance with the License.
5
# You may obtain a copy of the License at
7
# http://www.apache.org/licenses/LICENSE-2.0
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
15
# Requires Python 2.4+ and Openssl 1.0+
18
import azurelinuxagent.common.utils.fileutil as fileutil
20
from tests.protocol.mockwiredata import *
22
from azurelinuxagent.common.exception import *
23
from azurelinuxagent.common.protocol import get_protocol_util
24
from azurelinuxagent.common.protocol.restapi import ExtHandlerStatus, \
28
VMStatus, ExtHandler, \
30
from azurelinuxagent.ga.exthandlers import *
31
from azurelinuxagent.common.protocol.wire import WireProtocol
33
class TestHandlerStateMigration(AgentTestCase):
35
AgentTestCase.setUp(self)
37
handler_name = "Not.A.Real.Extension"
38
handler_version = "1.2.3"
40
self.ext_handler = ExtHandler(handler_name)
41
self.ext_handler.properties.version = handler_version
42
self.ext_handler_i = ExtHandlerInstance(self.ext_handler, "dummy protocol")
44
self.handler_state = "Enabled"
45
self.handler_status = ExtHandlerStatus(
47
version=handler_version,
49
message="Uninteresting message")
52
def _prepare_handler_state(self):
53
handler_state_path = os.path.join(
56
self.ext_handler_i.get_full_name())
57
os.makedirs(handler_state_path)
59
os.path.join(handler_state_path, "state"),
62
os.path.join(handler_state_path, "status"),
63
json.dumps(get_properties(self.handler_status)))
66
def _prepare_handler_config(self):
67
handler_config_path = os.path.join(
69
self.ext_handler_i.get_full_name(),
71
os.makedirs(handler_config_path)
74
def test_migration_migrates(self):
75
self._prepare_handler_state()
76
self._prepare_handler_config()
78
migrate_handler_state()
80
self.assertEquals(self.ext_handler_i.get_handler_state(), self.handler_state)
82
self.ext_handler_i.get_handler_status().status,
83
self.handler_status.status)
86
def test_migration_skips_if_empty(self):
87
self._prepare_handler_config()
89
migrate_handler_state()
92
os.path.isfile(os.path.join(self.ext_handler_i.get_conf_dir(), "HandlerState")))
94
os.path.isfile(os.path.join(self.ext_handler_i.get_conf_dir(), "HandlerStatus")))
97
def test_migration_cleans_up(self):
98
self._prepare_handler_state()
99
self._prepare_handler_config()
101
migrate_handler_state()
103
self.assertFalse(os.path.isdir(os.path.join(conf.get_lib_dir(), "handler_state")))
106
def test_migration_does_not_overwrite(self):
107
self._prepare_handler_state()
108
self._prepare_handler_config()
113
message = "A message"
114
self.assertNotEquals(state, self.handler_state)
115
self.assertNotEquals(status, self.handler_status.status)
116
self.assertNotEquals(code, self.handler_status.code)
117
self.assertNotEquals(message, self.handler_status.message)
119
self.ext_handler_i.set_handler_state(state)
120
self.ext_handler_i.set_handler_status(status=status, code=code, message=message)
122
migrate_handler_state()
124
self.assertEquals(self.ext_handler_i.get_handler_state(), state)
125
handler_status = self.ext_handler_i.get_handler_status()
126
self.assertEquals(handler_status.status, status)
127
self.assertEquals(handler_status.code, code)
128
self.assertEquals(handler_status.message, message)
131
@patch("shutil.move", side_effect=Exception)
132
def test_migration_ignores_move_errors(self, shutil_mock):
133
self._prepare_handler_state()
134
self._prepare_handler_config()
137
migrate_handler_state()
138
except Exception as e:
139
self.assertTrue(False, "Unexpected exception: {0}".format(str(e)))
142
@patch("shutil.rmtree", side_effect=Exception)
143
def test_migration_ignores_tree_remove_errors(self, shutil_mock):
144
self._prepare_handler_state()
145
self._prepare_handler_config()
148
migrate_handler_state()
149
except Exception as e:
150
self.assertTrue(False, "Unexpected exception: {0}".format(str(e)))
153
@patch("azurelinuxagent.common.protocol.wire.CryptUtil")
154
@patch("azurelinuxagent.common.utils.restutil.http_get")
155
class TestExtension(AgentTestCase):
157
def _assert_handler_status(self, report_vm_status, expected_status,
158
expected_ext_count, version):
159
self.assertTrue(report_vm_status.called)
160
args, kw = report_vm_status.call_args
162
self.assertNotEquals(0, len(vm_status.vmAgent.extensionHandlers))
163
handler_status = vm_status.vmAgent.extensionHandlers[0]
164
self.assertEquals(expected_status, handler_status.status)
165
self.assertEquals("OSTCExtensions.ExampleHandlerLinux",
167
self.assertEquals(version, handler_status.version)
168
self.assertEquals(expected_ext_count, len(handler_status.extensions))
171
def _assert_no_handler_status(self, report_vm_status):
172
self.assertTrue(report_vm_status.called)
173
args, kw = report_vm_status.call_args
175
self.assertEquals(0, len(vm_status.vmAgent.extensionHandlers))
178
def _create_mock(self, test_data, mock_http_get, MockCryptUtil):
179
"""Test enable/disable/uninstall of an extension"""
180
handler = get_exthandlers_handler()
182
#Mock protocol to return test data
183
mock_http_get.side_effect = test_data.mock_http_get
184
MockCryptUtil.side_effect = test_data.mock_crypt_util
186
protocol = WireProtocol("foo.bar")
188
protocol.report_ext_status = MagicMock()
189
protocol.report_vm_status = MagicMock()
191
handler.protocol_util.get_protocol = Mock(return_value=protocol)
192
return handler, protocol
194
def test_ext_handler(self, *args):
195
test_data = WireProtocolData(DATA_FILE)
196
exthandlers_handler, protocol = self._create_mock(test_data, *args)
198
#Test enable scenario.
199
exthandlers_handler.run()
200
self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0")
201
self._assert_ext_status(protocol.report_ext_status, "success", 0)
203
#Test goal state not changed
204
exthandlers_handler.run()
205
self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0")
207
#Test goal state changed
208
test_data.goal_state = test_data.goal_state.replace("<Incarnation>1<",
210
test_data.ext_conf = test_data.ext_conf.replace("seqNo=\"0\"",
212
exthandlers_handler.run()
213
self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0")
214
self._assert_ext_status(protocol.report_ext_status, "success", 1)
217
test_data.goal_state = test_data.goal_state.replace("<Incarnation>2<",
219
test_data.ext_conf = test_data.ext_conf.replace("1.0.0", "1.1.0")
220
test_data.ext_conf = test_data.ext_conf.replace("seqNo=\"1\"",
222
exthandlers_handler.run()
223
self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.1.0")
224
self._assert_ext_status(protocol.report_ext_status, "success", 2)
227
test_data.goal_state = test_data.goal_state.replace("<Incarnation>3<",
229
test_data.ext_conf = test_data.ext_conf.replace("enabled", "disabled")
230
exthandlers_handler.run()
231
self._assert_handler_status(protocol.report_vm_status, "NotReady",
235
test_data.goal_state = test_data.goal_state.replace("<Incarnation>4<",
237
test_data.ext_conf = test_data.ext_conf.replace("disabled", "uninstall")
238
exthandlers_handler.run()
239
self._assert_no_handler_status(protocol.report_vm_status)
241
#Test uninstall again!
242
test_data.goal_state = test_data.goal_state.replace("<Incarnation>5<",
244
exthandlers_handler.run()
245
self._assert_no_handler_status(protocol.report_vm_status)
247
def test_ext_handler_no_settings(self, *args):
248
test_data = WireProtocolData(DATA_FILE_EXT_NO_SETTINGS)
249
exthandlers_handler, protocol = self._create_mock(test_data, *args)
251
exthandlers_handler.run()
252
self._assert_handler_status(protocol.report_vm_status, "Ready", 0, "1.0.0")
254
def test_ext_handler_no_public_settings(self, *args):
255
test_data = WireProtocolData(DATA_FILE_EXT_NO_PUBLIC)
256
exthandlers_handler, protocol = self._create_mock(test_data, *args)
258
exthandlers_handler.run()
259
self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0")
261
def test_ext_handler_no_ext(self, *args):
262
test_data = WireProtocolData(DATA_FILE_NO_EXT)
263
exthandlers_handler, protocol = self._create_mock(test_data, *args)
265
#Assert no extension handler status
266
exthandlers_handler.run()
267
self._assert_no_handler_status(protocol.report_vm_status)
269
@patch('azurelinuxagent.ga.exthandlers.add_event')
270
def test_ext_handler_download_failure(self, mock_add_event, *args):
271
test_data = WireProtocolData(DATA_FILE)
272
exthandlers_handler, protocol = self._create_mock(test_data, *args)
273
protocol.download_ext_handler_pkg = Mock(side_effect=ProtocolError)
275
exthandlers_handler.run()
276
args, kw = mock_add_event.call_args
277
self.assertEquals(False, kw['is_success'])
278
self.assertEquals("OSTCExtensions.ExampleHandlerLinux", kw['name'])
279
self.assertEquals("Download", kw['op'])
281
@patch('azurelinuxagent.ga.exthandlers.fileutil')
282
def test_ext_handler_io_error(self, mock_fileutil, *args):
283
test_data = WireProtocolData(DATA_FILE)
284
exthandlers_handler, protocol = self._create_mock(test_data, *args)
286
mock_fileutil.write_file.return_value = IOError("Mock IO Error")
287
exthandlers_handler.run()
289
def _assert_ext_status(self, report_ext_status, expected_status,
291
self.assertTrue(report_ext_status.called)
292
args, kw = report_ext_status.call_args
293
ext_status = args[-1]
294
self.assertEquals(expected_status, ext_status.status)
295
self.assertEquals(expected_seq_no, ext_status.sequenceNumber)
297
def test_ext_handler_no_reporting_status(self, *args):
298
test_data = WireProtocolData(DATA_FILE)
299
exthandlers_handler, protocol = self._create_mock(test_data, *args)
300
exthandlers_handler.run()
301
self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0")
303
#Remove status file and re-run collecting extension status
304
status_file = os.path.join(self.tmp_dir,
305
"OSTCExtensions.ExampleHandlerLinux-1.0.0",
306
"status", "0.status")
307
self.assertTrue(os.path.isfile(status_file))
308
os.remove(status_file)
310
exthandlers_handler.run()
311
self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0")
312
self._assert_ext_status(protocol.report_ext_status, "error", 0)
314
def test_ext_handler_version_decide_autoupgrade_internalversion(self, *args):
315
for internal in [False, True]:
316
for autoupgrade in [False, True]:
318
config_version = '1.2.0'
319
decision_version = '1.2.0'
321
datafile = DATA_FILE_EXT_AUTOUPGRADE_INTERNALVERSION
323
datafile = DATA_FILE_EXT_INTERNALVERSION
325
config_version = '1.0.0'
327
datafile = DATA_FILE_EXT_AUTOUPGRADE
328
decision_version = '1.1.0'
331
decision_version = '1.0.0'
333
_, protocol = self._create_mock(WireProtocolData(datafile), *args)
334
ext_handlers, _ = protocol.get_ext_handlers()
335
self.assertEqual(1, len(ext_handlers.extHandlers))
336
ext_handler = ext_handlers.extHandlers[0]
337
self.assertEqual('OSTCExtensions.ExampleHandlerLinux', ext_handler.name)
338
self.assertEqual(config_version, ext_handler.properties.version, "config version.")
339
ExtHandlerInstance(ext_handler, protocol).decide_version()
340
self.assertEqual(decision_version, ext_handler.properties.version, "decision version.")
342
def test_ext_handler_version_decide_between_minor_versions(self, *args):
344
Using v2.x~v4.x for unit testing
345
Available versions via manifest XML (I stands for internal):
346
2.0.0, 2.1.0, 2.1.1, 2.2.0, 2.3.0(I), 2.4.0(I), 3.0, 3.1, 4.0.0.0, 4.0.0.1, 4.1.0.0
347
See tests/data/wire/manifest.xml for possible versions
350
# (installed_version, config_version, exptected_version, autoupgrade_expected_version)
352
(None, '2.0', '2.0.0', '2.2.0'),
353
(None, '2.0.0', '2.0.0', '2.2.0'),
354
('1.0', '1.0.0', '1.0.0', '1.1.0'),
355
(None, '2.1.0', '2.1.1', '2.2.0'),
356
(None, '2.2.0', '2.2.0', '2.2.0'),
357
(None, '2.3.0', '2.3.0', '2.3.0'),
358
(None, '2.4.0', '2.4.0', '2.4.0'),
359
(None, '3.0', '3.0', '3.1'),
360
(None, '4.0', '4.0.0.1', '4.1.0.0'),
363
_, protocol = self._create_mock(WireProtocolData(DATA_FILE), *args)
365
version_uri.uri = 'http://some/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml'
367
for (installed_version, config_version, expected_version, autoupgrade_expected_version) in cases:
369
ext_handler.properties = Mock()
370
ext_handler.name = 'OSTCExtensions.ExampleHandlerLinux'
371
ext_handler.versionUris = [version_uri]
372
ext_handler.properties.version = config_version
374
ext_handler_instance = ExtHandlerInstance(ext_handler, protocol)
375
ext_handler_instance.get_installed_version = Mock(return_value=installed_version)
377
ext_handler_instance.decide_version()
378
self.assertEqual(expected_version, ext_handler.properties.version)
380
ext_handler.properties.version = config_version
381
ext_handler.properties.upgradePolicy = 'auto'
383
ext_handler_instance = ExtHandlerInstance(ext_handler, protocol)
384
ext_handler_instance.get_installed_version = Mock(return_value=installed_version)
386
ext_handler_instance.decide_version()
387
self.assertEqual(autoupgrade_expected_version, ext_handler.properties.version)
390
if __name__ == '__main__':