diff options
Diffstat (limited to 'tests')
25 files changed, 1744 insertions, 147 deletions
diff --git a/tests/common/osutil/test_bigip.py b/tests/common/osutil/test_bigip.py new file mode 100644 index 0000000..4d1b006 --- /dev/null +++ b/tests/common/osutil/test_bigip.py @@ -0,0 +1,352 @@ +# Copyright 2016 F5 Networks Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +import os +import socket +import time + +import azurelinuxagent.common.osutil.bigip as osutil +import azurelinuxagent.common.osutil.default as default +import azurelinuxagent.common.utils.shellutil as shellutil + +from azurelinuxagent.common.exception import OSUtilError +from tests.tools import * + + +class TestBigIpOSUtil_wait_until_mcpd_is_initialized(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + @patch.object(logger, "info", return_value=None) + def test_success(self, *args): + result = osutil.BigIpOSUtil._wait_until_mcpd_is_initialized( + osutil.BigIpOSUtil() + ) + self.assertEqual(result, True) + + # There are two logger calls in the mcpd wait function. The second + # occurs after mcpd is found to be "up" + self.assertEqual(args[0].call_count, 2) + + @patch.object(shellutil, "run", return_value=1) + @patch.object(logger, "info", return_value=None) + @patch.object(time, "sleep", return_value=None) + def test_failure(self, *args): + self.assertRaises( + OSUtilError, + osutil.BigIpOSUtil._wait_until_mcpd_is_initialized, + osutil.BigIpOSUtil() + ) + + +class TestBigIpOSUtil_save_sys_config(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + @patch.object(logger, "error", return_value=None) + def test_success(self, *args): + result = osutil.BigIpOSUtil._save_sys_config(osutil.BigIpOSUtil()) + self.assertEqual(result, 0) + self.assertEqual(args[0].call_count, 0) + + @patch.object(shellutil, "run", return_value=1) + @patch.object(logger, "error", return_value=None) + def test_failure(self, *args): + result = osutil.BigIpOSUtil._save_sys_config(osutil.BigIpOSUtil()) + self.assertEqual(result, 1) + self.assertEqual(args[0].call_count, 1) + + +class TestBigIpOSUtil_get_dhcp_pid(AgentTestCase): + + @patch.object(shellutil, "run_get_output", return_value=(0, 8623)) + def test_success(self, *args): + result = osutil.BigIpOSUtil.get_dhcp_pid(osutil.BigIpOSUtil()) + self.assertEqual(result, 8623) + + @patch.object(shellutil, "run_get_output", return_value=(1, 'foo')) + def test_failure(self, *args): + result = osutil.BigIpOSUtil.get_dhcp_pid(osutil.BigIpOSUtil()) + self.assertEqual(result, None) + + +class TestBigIpOSUtil_useradd(AgentTestCase): + + @patch.object(osutil.BigIpOSUtil, 'get_userentry', return_value=None) + @patch.object(shellutil, "run_get_output") + def test_success(self, *args): + args[0].return_value = (0, None) + result = osutil.BigIpOSUtil.useradd( + osutil.BigIpOSUtil(), 'foo', expiration=None + ) + self.assertEqual(result, 0) + + @patch.object(osutil.BigIpOSUtil, 'get_userentry', return_value=None) + def test_user_already_exists(self, *args): + args[0].return_value = 'admin' + result = osutil.BigIpOSUtil.useradd( + osutil.BigIpOSUtil(), 'admin', expiration=None + ) + self.assertEqual(result, None) + + @patch.object(shellutil, "run", return_value=1) + def test_failure(self, *args): + self.assertRaises( + OSUtilError, + osutil.BigIpOSUtil.useradd, + osutil.BigIpOSUtil(), 'foo', expiration=None + ) + + +class TestBigIpOSUtil_chpasswd(AgentTestCase): + + @patch.object(shellutil, "run_get_output", return_value=(0, None)) + @patch.object(osutil.BigIpOSUtil, 'get_userentry', return_value=True) + @patch.object(osutil.BigIpOSUtil, 'is_sys_user', return_value=False) + @patch.object(osutil.BigIpOSUtil, '_save_sys_config', return_value=None) + def test_success(self, *args): + result = osutil.BigIpOSUtil.chpasswd( + osutil.BigIpOSUtil(), 'admin', 'password', crypt_id=6, salt_len=10 + ) + self.assertEqual(result, 0) + self.assertEqual(args[0].call_count, 1) + self.assertEqual(args[0].call_count, 1) + + @patch.object(osutil.BigIpOSUtil, 'is_sys_user', return_value=True) + def test_is_sys_user(self, *args): + self.assertRaises( + OSUtilError, + osutil.BigIpOSUtil.chpasswd, + osutil.BigIpOSUtil(), 'admin', 'password', crypt_id=6, salt_len=10 + ) + + @patch.object(shellutil, "run_get_output", return_value=(1, None)) + @patch.object(osutil.BigIpOSUtil, 'is_sys_user', return_value=False) + def test_failed_to_set_user_password(self, *args): + self.assertRaises( + OSUtilError, + osutil.BigIpOSUtil.chpasswd, + osutil.BigIpOSUtil(), 'admin', 'password', crypt_id=6, salt_len=10 + ) + + @patch.object(shellutil, "run_get_output", return_value=(0, None)) + @patch.object(osutil.BigIpOSUtil, 'is_sys_user', return_value=False) + @patch.object(osutil.BigIpOSUtil, 'get_userentry', return_value=None) + def test_failed_to_get_user_entry(self, *args): + self.assertRaises( + OSUtilError, + osutil.BigIpOSUtil.chpasswd, + osutil.BigIpOSUtil(), 'admin', 'password', crypt_id=6, salt_len=10 + ) + + +class TestBigIpOSUtil_get_dvd_device(AgentTestCase): + + @patch.object(os, "listdir", return_value=['tty1','cdrom0']) + def test_success(self, *args): + result = osutil.BigIpOSUtil.get_dvd_device( + osutil.BigIpOSUtil(), '/dev' + ) + self.assertEqual(result, '/dev/cdrom0') + + @patch.object(os, "listdir", return_value=['foo', 'bar']) + def test_failure(self, *args): + self.assertRaises( + OSUtilError, + osutil.BigIpOSUtil.get_dvd_device, + osutil.BigIpOSUtil(), '/dev' + ) + + +class TestBigIpOSUtil_restart_ssh_service(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + def test_success(self, *args): + result = osutil.BigIpOSUtil.restart_ssh_service( + osutil.BigIpOSUtil() + ) + self.assertEqual(result, 0) + + +class TestBigIpOSUtil_stop_agent_service(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + def test_success(self, *args): + result = osutil.BigIpOSUtil.stop_agent_service( + osutil.BigIpOSUtil() + ) + self.assertEqual(result, 0) + + +class TestBigIpOSUtil_start_agent_service(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + def test_success(self, *args): + result = osutil.BigIpOSUtil.start_agent_service( + osutil.BigIpOSUtil() + ) + self.assertEqual(result, 0) + + +class TestBigIpOSUtil_register_agent_service(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + def test_success(self, *args): + result = osutil.BigIpOSUtil.register_agent_service( + osutil.BigIpOSUtil() + ) + self.assertEqual(result, 0) + + +class TestBigIpOSUtil_unregister_agent_service(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + def test_success(self, *args): + result = osutil.BigIpOSUtil.unregister_agent_service( + osutil.BigIpOSUtil() + ) + self.assertEqual(result, 0) + + +class TestBigIpOSUtil_set_hostname(AgentTestCase): + + @patch.object(os.path, "exists", return_value=False) + def test_success(self, *args): + result = osutil.BigIpOSUtil.set_hostname( + osutil.BigIpOSUtil(), None + ) + self.assertEqual(args[0].call_count, 0) + self.assertEqual(result, None) + + +class TestBigIpOSUtil_set_dhcp_hostname(AgentTestCase): + + @patch.object(os.path, "exists", return_value=False) + def test_success(self, *args): + result = osutil.BigIpOSUtil.set_dhcp_hostname( + osutil.BigIpOSUtil(), None + ) + self.assertEqual(args[0].call_count, 0) + self.assertEqual(result, None) + + +class TestBigIpOSUtil_get_first_if(AgentTestCase): + + @patch.object(osutil.BigIpOSUtil, + '_format_single_interface_name', return_value=b'eth0') + def test_success(self, *args): + ifname, ipaddr = osutil.BigIpOSUtil().get_first_if() + self.assertTrue(ifname.startswith('eth')) + self.assertTrue(ipaddr is not None) + try: + socket.inet_aton(ipaddr) + except socket.error: + self.fail("not a valid ip address") + + @patch.object(osutil.BigIpOSUtil, + '_format_single_interface_name', return_value=b'loenp0s3') + def test_success(self, *args): + ifname, ipaddr = osutil.BigIpOSUtil().get_first_if() + self.assertFalse(ifname.startswith('eth')) + self.assertTrue(ipaddr is not None) + try: + socket.inet_aton(ipaddr) + except socket.error: + self.fail("not a valid ip address") + + +class TestBigIpOSUtil_mount_dvd(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + @patch.object(time, "sleep", return_value=None) + @patch.object(osutil.BigIpOSUtil, + '_wait_until_mcpd_is_initialized', return_value=None) + @patch.object(default.DefaultOSUtil, 'mount_dvd', return_value=None) + def test_success(self, *args): + osutil.BigIpOSUtil.mount_dvd( + osutil.BigIpOSUtil(), max_retry=6, chk_err=True + ) + self.assertEqual(args[0].call_count, 1) + self.assertEqual(args[1].call_count, 1) + + +class TestBigIpOSUtil_set_admin_access_to_ip(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + @patch.object(osutil.BigIpOSUtil, + '_set_accept_admin_access_to_ip', return_value=None) + @patch.object(osutil.BigIpOSUtil, + '_set_drop_admin_access_to_ip', return_value=None) + def test_success(self, *args): + osutil.BigIpOSUtil.set_admin_access_to_ip( + osutil.BigIpOSUtil(), '192.168.10.10' + ) + self.assertEqual(args[0].call_count, 1) + self.assertEqual(args[1].call_count, 1) + + @patch.object(shellutil, "run", return_value=0) + def test_accept_access(self, *args): + osutil.BigIpOSUtil._set_accept_admin_access_to_ip( + osutil.BigIpOSUtil(), '192.168.10.10' + ) + self.assertEqual(args[0].call_count, 2) + + @patch.object(shellutil, "run", return_value=0) + def test_drop_access(self, *args): + osutil.BigIpOSUtil._set_drop_admin_access_to_ip( + osutil.BigIpOSUtil(), '192.168.10.10' + ) + self.assertEqual(args[0].call_count, 2) + + +class TestBigIpOSUtil_route_add(AgentTestCase): + + @patch.object(shellutil, "run", return_value=0) + def test_success(self, *args): + osutil.BigIpOSUtil.route_add( + osutil.BigIpOSUtil(), '10.10.10.0', '255.255.255.0', '10.10.10.1' + ) + self.assertEqual(args[0].call_count, 1) + + +class TestBigIpOSUtil_device_for_ide_port(AgentTestCase): + + @patch.object(time, "sleep", return_value=None) + @patch.object(os.path, "exists", return_value=False) + @patch.object(default.DefaultOSUtil, + 'device_for_ide_port', return_value=None) + def test_success_waiting(self, *args): + osutil.BigIpOSUtil.device_for_ide_port( + osutil.BigIpOSUtil(), '5' + ) + self.assertEqual(args[0].call_count, 1) + self.assertEqual(args[1].call_count, 99) + self.assertEqual(args[2].call_count, 99) + + @patch.object(time, "sleep", return_value=None) + @patch.object(os.path, "exists", return_value=True) + @patch.object(default.DefaultOSUtil, + 'device_for_ide_port', return_value=None) + def test_success_immediate(self, *args): + osutil.BigIpOSUtil.device_for_ide_port( + osutil.BigIpOSUtil(), '5' + ) + self.assertEqual(args[0].call_count, 1) + self.assertEqual(args[1].call_count, 1) + self.assertEqual(args[2].call_count, 0) + + +if __name__ == '__main__': + unittest.main()
\ No newline at end of file diff --git a/tests/common/osutil/test_default.py b/tests/common/osutil/test_default.py index d9d00f6..d982b7e 100644 --- a/tests/common/osutil/test_default.py +++ b/tests/common/osutil/test_default.py @@ -142,5 +142,27 @@ class TestOSUtil(AgentTestCase): self.assertTrue(endpoint is not None) self.assertEqual(endpoint, "second") + def test_get_total_mem(self): + """ + Validate the returned value matches to the one retrieved by invoking shell command + """ + cmd = "grep MemTotal /proc/meminfo |awk '{print $2}'" + ret = shellutil.run_get_output(cmd) + if ret[0] == 0: + self.assertEqual(int(ret[1]) / 1024, get_osutil().get_total_mem()) + else: + self.fail("Cannot retrieve total memory using shell command.") + + def test_get_processor_cores(self): + """ + Validate the returned value matches to the one retrieved by invoking shell command + """ + cmd = "grep 'processor.*:' /proc/cpuinfo |wc -l" + ret = shellutil.run_get_output(cmd) + if ret[0] == 0: + self.assertEqual(int(ret[1]), get_osutil().get_processor_cores()) + else: + self.fail("Cannot retrieve number of process cores using shell command.") + if __name__ == '__main__': unittest.main() diff --git a/tests/common/test_version.py b/tests/common/test_version.py index 6a4dc38..306ea16 100644 --- a/tests/common/test_version.py +++ b/tests/common/test_version.py @@ -20,12 +20,14 @@ from __future__ import print_function import copy import glob import json +import mock import os import platform import random import subprocess import sys import tempfile +import textwrap import zipfile from tests.protocol.mockwiredata import * @@ -34,6 +36,7 @@ from tests.tools import * import azurelinuxagent.common.conf as conf import azurelinuxagent.common.logger as logger import azurelinuxagent.common.utils.fileutil as fileutil +import azurelinuxagent.common.version as version from azurelinuxagent.common.utils.flexible_version import FlexibleVersion from azurelinuxagent.common.version import * @@ -63,3 +66,109 @@ class TestCurrentAgentName(AgentTestCase): self.assertEqual(agent, current_agent) self.assertEqual(version, str(current_version)) return + +class TestGetF5Platforms(AgentTestCase): + def test_get_f5_platform_bigip_12_1_1(self): + version_file = textwrap.dedent(""" + Product: BIG-IP + Version: 12.1.1 + Build: 0.0.184 + Sequence: 12.1.1.0.0.184.0 + BaseBuild: 0.0.184 + Edition: Final + Date: Thu Aug 11 17:09:01 PDT 2016 + Built: 160811170901 + Changelist: 1874858 + JobID: 705993""") + + mo = mock.mock_open(read_data=version_file) + with patch(open_patch(), mo): + platform = version.get_f5_platform() + self.assertTrue(platform[0] == 'bigip') + self.assertTrue(platform[1] == '12.1.1') + self.assertTrue(platform[2] == 'bigip') + self.assertTrue(platform[3] == 'BIG-IP') + + def test_get_f5_platform_bigip_12_1_0_hf1(self): + version_file = textwrap.dedent(""" + Product: BIG-IP + Version: 12.1.0 + Build: 1.0.1447 + Sequence: 12.1.0.1.0.1447.0 + BaseBuild: 0.0.1434 + Edition: Hotfix HF1 + Date: Wed Jun 8 13:41:59 PDT 2016 + Built: 160608134159 + Changelist: 1773831 + JobID: 673467""") + + mo = mock.mock_open(read_data=version_file) + with patch(open_patch(), mo): + platform = version.get_f5_platform() + self.assertTrue(platform[0] == 'bigip') + self.assertTrue(platform[1] == '12.1.0') + self.assertTrue(platform[2] == 'bigip') + self.assertTrue(platform[3] == 'BIG-IP') + + def test_get_f5_platform_bigip_12_0_0(self): + version_file = textwrap.dedent(""" + Product: BIG-IP + Version: 12.0.0 + Build: 0.0.606 + Sequence: 12.0.0.0.0.606.0 + BaseBuild: 0.0.606 + Edition: Final + Date: Fri Aug 21 13:29:22 PDT 2015 + Built: 150821132922 + Changelist: 1486072 + JobID: 536212""") + + mo = mock.mock_open(read_data=version_file) + with patch(open_patch(), mo): + platform = version.get_f5_platform() + self.assertTrue(platform[0] == 'bigip') + self.assertTrue(platform[1] == '12.0.0') + self.assertTrue(platform[2] == 'bigip') + self.assertTrue(platform[3] == 'BIG-IP') + + def test_get_f5_platform_iworkflow_2_0_1(self): + version_file = textwrap.dedent(""" + Product: iWorkflow + Version: 2.0.1 + Build: 0.0.9842 + Sequence: 2.0.1.0.0.9842.0 + BaseBuild: 0.0.9842 + Edition: Final + Date: Sat Oct 1 22:52:08 PDT 2016 + Built: 161001225208 + Changelist: 1924048 + JobID: 734712""") + + mo = mock.mock_open(read_data=version_file) + with patch(open_patch(), mo): + platform = version.get_f5_platform() + self.assertTrue(platform[0] == 'iworkflow') + self.assertTrue(platform[1] == '2.0.1') + self.assertTrue(platform[2] == 'iworkflow') + self.assertTrue(platform[3] == 'iWorkflow') + + def test_get_f5_platform_bigiq_5_1_0(self): + version_file = textwrap.dedent(""" + Product: BIG-IQ + Version: 5.1.0 + Build: 0.0.631 + Sequence: 5.1.0.0.0.631.0 + BaseBuild: 0.0.631 + Edition: Final + Date: Thu Sep 15 19:55:43 PDT 2016 + Built: 160915195543 + Changelist: 1907534 + JobID: 726344""") + + mo = mock.mock_open(read_data=version_file) + with patch(open_patch(), mo): + platform = version.get_f5_platform() + self.assertTrue(platform[0] == 'bigiq') + self.assertTrue(platform[1] == '5.1.0') + self.assertTrue(platform[2] == 'bigiq') + self.assertTrue(platform[3] == 'BIG-IQ') diff --git a/tests/daemon/test_resourcedisk.py b/tests/daemon/test_resourcedisk.py new file mode 100644 index 0000000..d2fc8fb --- /dev/null +++ b/tests/daemon/test_resourcedisk.py @@ -0,0 +1,43 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +from tests.tools import * +from azurelinuxagent.common.exception import * +from azurelinuxagent.daemon import * +from azurelinuxagent.daemon.resourcedisk.default import ResourceDiskHandler + +class TestResourceDisk(AgentTestCase): + def test_mount_flags_empty(self): + partition = '/dev/sdb1' + mountpoint = '/mnt/resource' + options = None + expected = 'mount /dev/sdb1 /mnt/resource' + rdh = ResourceDiskHandler() + mount_string = rdh.get_mount_string(options, partition, mountpoint) + self.assertEqual(expected, mount_string) + + def test_mount_flags_many(self): + partition = '/dev/sdb1' + mountpoint = '/mnt/resource' + options = 'noexec,noguid,nodev' + expected = 'mount -o noexec,noguid,nodev /dev/sdb1 /mnt/resource' + rdh = ResourceDiskHandler() + mount_string = rdh.get_mount_string(options, partition, mountpoint) + self.assertEqual(expected, mount_string) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/data/ga/WALinuxAgent-2.1.5.zip b/tests/data/ga/WALinuxAgent-2.1.5.zip Binary files differdeleted file mode 100644 index b1f2684..0000000 --- a/tests/data/ga/WALinuxAgent-2.1.5.zip +++ /dev/null diff --git a/tests/data/ga/WALinuxAgent-2.2.0.zip b/tests/data/ga/WALinuxAgent-2.2.0.zip Binary files differnew file mode 100644 index 0000000..4f28a83 --- /dev/null +++ b/tests/data/ga/WALinuxAgent-2.2.0.zip diff --git a/tests/data/metadata/certificates.json b/tests/data/metadata/certificates.json index 4db7a06..5c8889f 100644 --- a/tests/data/metadata/certificates.json +++ b/tests/data/metadata/certificates.json @@ -2,6 +2,6 @@ "certificates":[{ "name":"foo", "thumbprint":"bar", - "certificateDataUri":"baz" + "certificateDataUri":"certificates_data" }] } diff --git a/tests/data/metadata/certificates_data.json b/tests/data/metadata/certificates_data.json new file mode 100644 index 0000000..eee8028 --- /dev/null +++ b/tests/data/metadata/certificates_data.json @@ -0,0 +1,74 @@ +{"certificateData":"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 +h+249Wj0Bw=="}
\ No newline at end of file diff --git a/tests/data/metadata/trans_cert b/tests/data/metadata/trans_cert new file mode 100644 index 0000000..d560ae2 --- /dev/null +++ b/tests/data/metadata/trans_cert @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBzCCAe+gAwIBAgIJANujJuVt5eC8MA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNV +BAMMDkxpbnV4VHJhbnNwb3J0MCAXDTE0MTAyNDA3MjgwN1oYDzIxMDQwNzEyMDcy +ODA3WjAZMRcwFQYDVQQDDA5MaW51eFRyYW5zcG9ydDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANPcJAkd6V5NeogSKjIeTXOWC5xzKTyuJPt4YZMVSosU +0lI6a0wHp+g2fP22zrVswW+QJz6AVWojIEqLQup3WyCXZTv8RUblHnIjkvX/+J/G +aLmz0G5JzZIpELL2C8IfQLH2IiPlK9LOQH00W74WFcK3QqcJ6Kw8GcVaeSXT1r7X +QcGMqEjcWJkpKLoMJv3LMufE+JMdbXDUGY+Ps7Zicu8KXvBPaKVsc6H2jrqBS8et +jXbzLyrezTUDz45rmyRJzCO5Sk2pohuYg73wUykAUPVxd7L8WnSyqz1v4zrObqnw +BAyor67JR/hjTBfjFOvd8qFGonfiv2Vnz9XsYFTZsXECAwEAAaNQME4wHQYDVR0O +BBYEFL8i/sehpGV6IEDX7F0WQHQ/ZXOyMB8GA1UdIwQYMBaAFL8i/sehpGV6IEDX +7F0WQHQ/ZXOyMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMPLrimT +Gptu5pLRHPT8OFRN+skNSkepYaUaJuq6cSKxLumSYkD8++rohu+1+a7t1YNjjNSJ +8ohRAynRJ7aRqwBmyX2OPLRpOfyRZwR0rcFfAMORm/jOE6WBdqgYD2L2b+tZplGt +/QqgQzebaekXh/032FK4c74Zg5r3R3tfNSUMG6nLauWzYHbQ5SCdkuQwV0ehGqh5 +VF1AOdmz4CC2237BNznDFQhkeU0LrqqAoE/hv5ih7klJKZdS88rOYEnVJsFFJb0g +qaycXjOm5Khgl4hKrd+DBD/qj4IVVzsmdpFli72k6WLBHGOXusUGo/3isci2iAIt +DsfY6XGSEIhZnA4= +-----END CERTIFICATE----- diff --git a/tests/data/metadata/trans_prv b/tests/data/metadata/trans_prv new file mode 100644 index 0000000..063cf15 --- /dev/null +++ b/tests/data/metadata/trans_prv @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDT3CQJHeleTXqI +EioyHk1zlguccyk8riT7eGGTFUqLFNJSOmtMB6foNnz9ts61bMFvkCc+gFVqIyBK +i0Lqd1sgl2U7/EVG5R5yI5L1//ifxmi5s9BuSc2SKRCy9gvCH0Cx9iIj5SvSzkB9 +NFu+FhXCt0KnCeisPBnFWnkl09a+10HBjKhI3FiZKSi6DCb9yzLnxPiTHW1w1BmP +j7O2YnLvCl7wT2ilbHOh9o66gUvHrY128y8q3s01A8+Oa5skScwjuUpNqaIbmIO9 +8FMpAFD1cXey/Fp0sqs9b+M6zm6p8AQMqK+uyUf4Y0wX4xTr3fKhRqJ34r9lZ8/V +7GBU2bFxAgMBAAECggEBAM4hsfog3VAAyIieS+npq+gbhH6bWfMNaTQ3g5CNNbMu +9hhFeOJHzKnWYjSlamgBQhAfTN+2E+Up+iAtcVUZ/lMumrQLlwgMo1vgmvu5Kxmh +/YE5oEG+k0JzrCjD1trwd4zvc3ZDYyk/vmVTzTOc311N248UyArUiyqHBbq1a4rP +tJhCLn2c4S7flXGF0MDVGZyV9V7J8N8leq/dRGMB027Li21T+B4mPHXa6b8tpRPL +4vc8sHoUJDa2/+mFDJ2XbZfmlgd3MmIPlRn1VWoW7mxgT/AObsPl7LuQx7+t80Wx +hIMjuKUHRACQSLwHxJ3SQRFWp4xbztnXSRXYuHTscLUCgYEA//Uu0qIm/FgC45yG +nXtoax4+7UXhxrsWDEkbtL6RQ0TSTiwaaI6RSQcjrKDVSo/xo4ZySTYcRgp5GKlI +CrWyNM+UnIzTNbZOtvSIAfjxYxMsq1vwpTlOB5/g+cMukeGg39yUlrjVNoFpv4i6 +9t4yYuEaF4Vww0FDd2nNKhhW648CgYEA0+UYH6TKu03zDXqFpwf4DP2VoSo8OgfQ +eN93lpFNyjrfzvxDZkGF+7M/ebyYuI6hFplVMu6BpgpFP7UVJpW0Hn/sXkTq7F1Q +rTJTtkTp2+uxQVP/PzSOqK0Twi5ifkfoEOkPkNNtTiXzwCW6Qmmcvln2u893pyR5 +gqo5BHR7Ev8CgYAb7bXpN9ZHLJdMHLU3k9Kl9YvqOfjTxXA3cPa79xtEmsrTys4q +4HuL22KSII6Fb0VvkWkBAg19uwDRpw78VC0YxBm0J02Yi8b1AaOhi3dTVzFFlWeh +r6oK/PAAcMKxGkyCgMAZ3hstsltGkfXMoBwhW+yL6nyOYZ2p9vpzAGrjkwKBgQDF +0huzbyXVt/AxpTEhv07U0enfjI6tnp4COp5q8zyskEph8yD5VjK/yZh5DpmFs6Kw +dnYUFpbzbKM51tToMNr3nnYNjEnGYVfwWgvNHok1x9S0KLcjSu3ki7DmmGdbfcYq +A2uEyd5CFyx5Nr+tQOwUyeiPbiFG6caHNmQExLoiAQKBgFPy9H8///xsadYmZ18k +r77R2CvU7ArxlLfp9dr19aGYKvHvnpsY6EuChkWfy8Xjqn3ogzgrHz/rn3mlGUpK +vbtwtsknAHtTbotXJwfaBZv2RGgGRr3DzNo6ll2Aez0lNblZFXq132h7+y5iLvar +4euORaD/fuM4UPlR5mN+bypU +-----END PRIVATE KEY----- diff --git a/tests/data/wire/ext_conf.xml b/tests/data/wire/ext_conf.xml index 0b7c528..508fb98 100644 --- a/tests/data/wire/ext_conf.xml +++ b/tests/data/wire/ext_conf.xml @@ -22,5 +22,5 @@ <RuntimeSettings seqNo="0">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK","publicSettings":{"foo":"bar"}}}]}</RuntimeSettings> </Plugin> </PluginSettings> -<StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&sp=rw&se=9999-01-01&sk=key1&sv=2014-02-14&sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob></Extensions> +<StatusUploadBlob statusBlobType="BlockBlob">https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&sp=rw&se=9999-01-01&sk=key1&sv=2014-02-14&sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob></Extensions> diff --git a/tests/data/wire/ext_conf_missing_family.xml b/tests/data/wire/ext_conf_missing_family.xml new file mode 100644 index 0000000..4010077 --- /dev/null +++ b/tests/data/wire/ext_conf_missing_family.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<Extensions version="1.0.0.0" goalStateIncarnation="1"> + <GuestAgentExtension + xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> + <GAFamilies> + <GAFamily> + <Name>Prod</Name> + <Uris /> + </GAFamily> + <GAFamily> + <Name>Test</Name> + <Uris> + <Uri>https://rdfepirv2bl2prdstr01.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://rdfepirv2bl2prdstr02.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://rdfepirv2bl2prdstr03.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://rdfepirv2bl2prdstr04.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://rdfepirv2bl3prdstr01.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://rdfepirv2bl3prdstr02.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://rdfepirv2bl3prdstr03.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://zrdfepirv2bl4prdstr01.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://zrdfepirv2bl4prdstr03.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://zrdfepirv2bl5prdstr02.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://zrdfepirv2bl5prdstr04.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://zrdfepirv2bl5prdstr06.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://zrdfepirv2bl5prdstr09a.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + <Uri>https://zrdfepirv2bl6prdstr02a.blob.core.windows.net/7d89d439b79f4452950452399add2c90/Microsoft.OSTCLinuxAgent_Test_useast_manifest.xml</Uri> + </Uris> + </GAFamily> + </GAFamilies> + <Location>eastus</Location> + </GuestAgentExtension> + <StatusUploadBlob statusBlobType="BlockBlob">https://walaautoasmeastus.blob.core.windows.net/vhds/walaautos73small.walaautos73small.walaautos73small.status?sr=b&sp=rw&se=9999-01-01&sk=key1&sv=2014-02-14&sig=u%2BCA2Cxb7ticiEBRIW8HWgNW7gl2NPuOGQl0u95ApQE%3D</StatusUploadBlob> +</Extensions> diff --git a/tests/data/wire/goal_state.xml b/tests/data/wire/goal_state.xml index 960444b..0fd144f 100644 --- a/tests/data/wire/goal_state.xml +++ b/tests/data/wire/goal_state.xml @@ -20,6 +20,7 @@ <Certificates>http://certificatesuri/</Certificates> <ExtensionsConfig>http://extensionsconfiguri/</ExtensionsConfig> <FullConfig>http://fullconfiguri/</FullConfig> + <ConfigName>DummyRoleConfigName.xml</ConfigName> </Configuration> </RoleInstance> </RoleInstanceList> diff --git a/tests/distro/test_resourceDisk.py b/tests/distro/test_resourceDisk.py index 198fd49..1bd79fe 100644 --- a/tests/distro/test_resourceDisk.py +++ b/tests/distro/test_resourceDisk.py @@ -18,8 +18,10 @@ # http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx # http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx -from tests.tools import * +from azurelinuxagent.common.utils import shellutil from azurelinuxagent.daemon.resourcedisk import get_resourcedisk_handler +from tests.tools import * + class TestResourceDisk(AgentTestCase): def test_mkfile(self): @@ -38,5 +40,70 @@ class TestResourceDisk(AgentTestCase): # cleanup os.remove(test_file) + def test_mkfile_dd_fallback(self): + with patch.object(shellutil, "run") as run_patch: + # setup + run_patch.return_value = 1 + test_file = os.path.join(self.tmp_dir, 'test_file') + file_size = 1024 * 128 + + # execute + if sys.version_info >= (3,3): + with patch("os.posix_fallocate", + side_effect=Exception('failure')): + get_resourcedisk_handler().mkfile(test_file, file_size) + else: + get_resourcedisk_handler().mkfile(test_file, file_size) + + # assert + assert run_patch.call_count > 1 + assert "fallocate" in run_patch.call_args_list[0][0][0] + assert "dd if" in run_patch.call_args_list[-1][0][0] + + def test_mkfile_xfs_fs(self): + # setup + test_file = os.path.join(self.tmp_dir, 'test_file') + file_size = 1024 * 128 + if os.path.exists(test_file): + os.remove(test_file) + + # execute + resource_disk_handler = get_resourcedisk_handler() + resource_disk_handler.fs = 'xfs' + + with patch.object(shellutil, "run") as run_patch: + resource_disk_handler.mkfile(test_file, file_size) + + # assert + if sys.version_info >= (3,3): + with patch("os.posix_fallocate") as posix_fallocate: + assert posix_fallocate.assert_not_called() + + assert run_patch.call_count == 1 + assert "dd if" in run_patch.call_args_list[0][0][0] + + + def test_change_partition_type(self): + resource_handler = get_resourcedisk_handler() + # test when sfdisk --part-type does not exist + with patch.object(shellutil, "run_get_output", + side_effect=[[1, ''], [0, '']]) as run_patch: + resource_handler.change_partition_type(suppress_message=True, option_str='') + + # assert + assert run_patch.call_count == 2 + assert "sfdisk --part-type" in run_patch.call_args_list[0][0][0] + assert "sfdisk -c" in run_patch.call_args_list[1][0][0] + + # test when sfdisk --part-type exists + with patch.object(shellutil, "run_get_output", + side_effect=[[0, '']]) as run_patch: + resource_handler.change_partition_type(suppress_message=True, option_str='') + + # assert + assert run_patch.call_count == 1 + assert "sfdisk --part-type" in run_patch.call_args_list[0][0][0] + + if __name__ == '__main__': unittest.main() diff --git a/tests/ga/test_extension.py b/tests/ga/test_extension.py index 71c219c..1df0a04 100644 --- a/tests/ga/test_extension.py +++ b/tests/ga/test_extension.py @@ -15,12 +15,141 @@ # Requires Python 2.4+ and Openssl 1.0+ # +import azurelinuxagent.common.utils.fileutil as fileutil + from tests.protocol.mockwiredata import * + from azurelinuxagent.common.exception import * from azurelinuxagent.common.protocol import get_protocol_util +from azurelinuxagent.common.protocol.restapi import ExtHandlerStatus, \ + ExtensionStatus, \ + ExtensionSubStatus, \ + Extension, \ + VMStatus, ExtHandler, \ + get_properties from azurelinuxagent.ga.exthandlers import * from azurelinuxagent.common.protocol.wire import WireProtocol +class TestHandlerStateMigration(AgentTestCase): + def setUp(self): + AgentTestCase.setUp(self) + + handler_name = "Not.A.Real.Extension" + handler_version = "1.2.3" + + self.ext_handler = ExtHandler(handler_name) + self.ext_handler.properties.version = handler_version + self.ext_handler_i = ExtHandlerInstance(self.ext_handler, "dummy protocol") + + self.handler_state = "Enabled" + self.handler_status = ExtHandlerStatus( + name=handler_name, + version=handler_version, + status="Ready", + message="Uninteresting message") + return + + def _prepare_handler_state(self): + handler_state_path = os.path.join( + self.tmp_dir, + "handler_state", + self.ext_handler_i.get_full_name()) + os.makedirs(handler_state_path) + fileutil.write_file( + os.path.join(handler_state_path, "state"), + self.handler_state) + fileutil.write_file( + os.path.join(handler_state_path, "status"), + json.dumps(get_properties(self.handler_status))) + return + + def _prepare_handler_config(self): + handler_config_path = os.path.join( + self.tmp_dir, + self.ext_handler_i.get_full_name(), + "config") + os.makedirs(handler_config_path) + return + + def test_migration_migrates(self): + self._prepare_handler_state() + self._prepare_handler_config() + + migrate_handler_state() + + self.assertEquals(self.ext_handler_i.get_handler_state(), self.handler_state) + self.assertEquals( + self.ext_handler_i.get_handler_status().status, + self.handler_status.status) + return + + def test_migration_skips_if_empty(self): + self._prepare_handler_config() + + migrate_handler_state() + + self.assertFalse( + os.path.isfile(os.path.join(self.ext_handler_i.get_conf_dir(), "HandlerState"))) + self.assertFalse( + os.path.isfile(os.path.join(self.ext_handler_i.get_conf_dir(), "HandlerStatus"))) + return + + def test_migration_cleans_up(self): + self._prepare_handler_state() + self._prepare_handler_config() + + migrate_handler_state() + + self.assertFalse(os.path.isdir(os.path.join(conf.get_lib_dir(), "handler_state"))) + return + + def test_migration_does_not_overwrite(self): + self._prepare_handler_state() + self._prepare_handler_config() + + state = "Installed" + status = "NotReady" + code = 1 + message = "A message" + self.assertNotEquals(state, self.handler_state) + self.assertNotEquals(status, self.handler_status.status) + self.assertNotEquals(code, self.handler_status.code) + self.assertNotEquals(message, self.handler_status.message) + + self.ext_handler_i.set_handler_state(state) + self.ext_handler_i.set_handler_status(status=status, code=code, message=message) + + migrate_handler_state() + + self.assertEquals(self.ext_handler_i.get_handler_state(), state) + handler_status = self.ext_handler_i.get_handler_status() + self.assertEquals(handler_status.status, status) + self.assertEquals(handler_status.code, code) + self.assertEquals(handler_status.message, message) + return + + @patch("shutil.move", side_effect=Exception) + def test_migration_ignores_move_errors(self, shutil_mock): + self._prepare_handler_state() + self._prepare_handler_config() + + try: + migrate_handler_state() + except Exception as e: + self.assertTrue(False, "Unexpected exception: {0}".format(str(e))) + return + + @patch("shutil.rmtree", side_effect=Exception) + def test_migration_ignores_tree_remove_errors(self, shutil_mock): + self._prepare_handler_state() + self._prepare_handler_config() + + try: + migrate_handler_state() + except Exception as e: + self.assertTrue(False, "Unexpected exception: {0}".format(str(e))) + return + @patch("azurelinuxagent.common.protocol.wire.CryptUtil") @patch("azurelinuxagent.common.utils.restutil.http_get") class TestExtension(AgentTestCase): @@ -33,7 +162,7 @@ class TestExtension(AgentTestCase): self.assertNotEquals(0, len(vm_status.vmAgent.extensionHandlers)) handler_status = vm_status.vmAgent.extensionHandlers[0] self.assertEquals(expected_status, handler_status.status) - self.assertEquals("OSTCExtensions.ExampleHandlerLinux", + self.assertEquals("OSTCExtensions.ExampleHandlerLinux", handler_status.name) self.assertEquals(version, handler_status.version) self.assertEquals(expected_ext_count, len(handler_status.extensions)) @@ -157,7 +286,51 @@ class TestExtension(AgentTestCase): mock_fileutil.write_file.return_value = IOError("Mock IO Error") exthandlers_handler.run() - def _assert_ext_status(self, report_ext_status, expected_status, + def test_handle_ext_handlers_on_hold_true(self, *args): + test_data = WireProtocolData(DATA_FILE) + exthandlers_handler, protocol = self._create_mock(test_data, *args) + exthandlers_handler.ext_handlers, exthandlers_handler.last_etag = protocol.get_ext_handlers() + protocol.get_artifacts_profile = MagicMock() + exthandlers_handler.protocol = protocol + + # Disable extension handling blocking + conf.get_enable_overprovisioning = Mock(return_value=False) + with patch.object(ExtHandlersHandler, 'handle_ext_handler') as patch_handle_ext_handler: + exthandlers_handler.handle_ext_handlers() + patch_handle_ext_handler.assert_called() + + # enable extension handling blocking + conf.get_enable_overprovisioning = Mock(return_value=True) + with patch.object(ExtHandlersHandler, 'handle_ext_handler') as patch_handle_ext_handler: + exthandlers_handler.handle_ext_handlers() + patch_handle_ext_handler.assert_not_called() + + + def test_handle_ext_handlers_on_hold_false(self, *args): + test_data = WireProtocolData(DATA_FILE) + exthandlers_handler, protocol = self._create_mock(test_data, *args) + exthandlers_handler.ext_handlers, exthandlers_handler.last_etag = protocol.get_ext_handlers() + exthandlers_handler.protocol = protocol + + # enable extension handling blocking + conf.get_enable_overprovisioning = Mock(return_value=True) + + #Test when is_on_hold returns False + from azurelinuxagent.common.protocol.wire import InVMArtifactsProfile + mock_in_vm_artifacts_profile = InVMArtifactsProfile(MagicMock()) + mock_in_vm_artifacts_profile.is_on_hold = Mock(return_value=False) + protocol.get_artifacts_profile = Mock(return_value=mock_in_vm_artifacts_profile) + with patch.object(ExtHandlersHandler, 'handle_ext_handler') as patch_handle_ext_handler: + exthandlers_handler.handle_ext_handlers() + patch_handle_ext_handler.assert_called_once() + + #Test when in_vm_artifacts_profile is not available + protocol.get_artifacts_profile = Mock(return_value=None) + with patch.object(ExtHandlersHandler, 'handle_ext_handler') as patch_handle_ext_handler: + exthandlers_handler.handle_ext_handlers() + patch_handle_ext_handler.assert_called_once() + + def _assert_ext_status(self, report_ext_status, expected_status, expected_seq_no): self.assertTrue(report_ext_status.called) args, kw = report_ext_status.call_args diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py index 74804fb..a431a9b 100644 --- a/tests/ga/test_update.py +++ b/tests/ga/test_update.py @@ -23,6 +23,7 @@ import json import os import platform import random +import re import subprocess import sys import tempfile @@ -109,8 +110,13 @@ class UpdateTestCase(AgentTestCase): AgentTestCase.setUp(self) return - def agent_bin(self, version): - return "bin/{0}-{1}.egg".format(AGENT_NAME, version) + def agent_bin(self, version, suffix): + return "bin/{0}-{1}{2}.egg".format(AGENT_NAME, version, suffix) + + def rename_agent_bin(self, path, src_v, dst_v): + src_bin = glob.glob(os.path.join(path, self.agent_bin(src_v, '*')))[0] + dst_bin = os.path.join(path, self.agent_bin(dst_v, '')) + shutil.move(src_bin, dst_bin) def agent_count(self): return len(self.agent_dirs()) @@ -158,8 +164,29 @@ class UpdateTestCase(AgentTestCase): fileutil.trim_ext(agent, "zip"))) return - def prepare_agents(self, base_version=AGENT_VERSION, count=5, is_available=True): - base_v = FlexibleVersion(base_version) + def prepare_agent(self, version): + """ + Create a download for the current agent version, copied from test data + """ + self.copy_agents(get_agent_pkgs()[0]) + self.expand_agents() + + versions = self.agent_versions() + src_v = FlexibleVersion(str(versions[0])) + + from_path = self.agent_dir(src_v) + dst_v = FlexibleVersion(str(version)) + to_path = self.agent_dir(dst_v) + + if from_path != to_path: + shutil.move(from_path + ".zip", to_path + ".zip") + shutil.move(from_path, to_path) + self.rename_agent_bin(to_path, src_v, dst_v) + return + + def prepare_agents(self, + count=5, + is_available=True): # Ensure the test data is copied over agent_count = self.agent_count() @@ -172,10 +199,6 @@ class UpdateTestCase(AgentTestCase): versions = self.agent_versions() src_v = FlexibleVersion(str(versions[0])) - # If the most recent agent is newer the minimum requested, use the agent version - if base_v < src_v: - base_v = src_v - # Create agent packages and directories return self.replicate_agents( src_v=src_v, @@ -193,25 +216,21 @@ class UpdateTestCase(AgentTestCase): pass return - def replicate_agents( - self, - count=5, - src_v=AGENT_VERSION, - is_available=True, - increment=1): + def replicate_agents(self, + count=5, + src_v=AGENT_VERSION, + is_available=True, + increment=1): from_path = self.agent_dir(src_v) dst_v = FlexibleVersion(str(src_v)) - for i in range(0,count): + for i in range(0, count): dst_v += increment to_path = self.agent_dir(dst_v) shutil.copyfile(from_path + ".zip", to_path + ".zip") shutil.copytree(from_path, to_path) - shutil.move( - os.path.join(to_path, self.agent_bin(src_v)), - os.path.join(to_path, self.agent_bin(dst_v))) + self.rename_agent_bin(to_path, src_v, dst_v) if not is_available: GuestAgent(to_path).mark_failure(is_fatal=True) - return dst_v @@ -437,7 +456,8 @@ class TestGuestAgent(UpdateTestCase): agent = GuestAgent(path=self.agent_path) agent._unpack() agent._load_manifest() - self.assertEqual(agent.manifest.get_enable_command(), agent.get_agent_cmd()) + self.assertEqual(agent.manifest.get_enable_command(), + agent.get_agent_cmd()) return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @@ -515,6 +535,34 @@ class TestGuestAgent(UpdateTestCase): self.assertFalse(agent.is_downloaded) return + @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") + @patch("azurelinuxagent.ga.update.restutil.http_get") + def test_download_fallback(self, mock_http_get, mock_ensure): + self.remove_agents() + self.assertFalse(os.path.isdir(self.agent_path)) + + mock_http_get.return_value = ResponseMock( + status=restutil.httpclient.SERVICE_UNAVAILABLE) + + ext_uri = 'ext_uri' + host_uri = 'host_uri' + mock_host = HostPluginProtocol(host_uri, + 'container_id', + 'role_config') + + pkg = ExtHandlerPackage(version=str(get_agent_version())) + pkg.uris.append(ExtHandlerPackageUri(uri=ext_uri)) + agent = GuestAgent(pkg=pkg) + agent.host = mock_host + + with patch.object(HostPluginProtocol, + "get_artifact_request", + return_value=[host_uri, {}]): + self.assertRaises(UpdateError, agent._download) + self.assertEqual(mock_http_get.call_count, 2) + self.assertEqual(mock_http_get.call_args_list[0][0][0], ext_uri) + self.assertEqual(mock_http_get.call_args_list[1][0][0], host_uri) + @patch("azurelinuxagent.ga.update.restutil.http_get") def test_ensure_downloaded(self, mock_http_get): self.remove_agents() @@ -598,13 +646,13 @@ class TestGuestAgent(UpdateTestCase): class TestUpdate(UpdateTestCase): def setUp(self): UpdateTestCase.setUp(self) + self.event_patch = patch('azurelinuxagent.common.event.add_event') self.update_handler = get_update_handler() return def test_creation(self): self.assertTrue(self.update_handler.running) - self.assertEqual(None, self.update_handler.last_etag) self.assertEqual(None, self.update_handler.last_attempt_time) self.assertEqual(0, len(self.update_handler.agents)) @@ -617,82 +665,113 @@ class TestUpdate(UpdateTestCase): self.assertEqual(None, self.update_handler.signal_handler) return - def _test_ensure_latest_agent( + def test_emit_restart_event_writes_sentinal_file(self): + self.assertFalse(os.path.isfile(self.update_handler._sentinal_file_path())) + self.update_handler._emit_restart_event() + self.assertTrue(os.path.isfile(self.update_handler._sentinal_file_path())) + return + + def test_emit_restart_event_emits_event_if_not_clean_start(self): + try: + mock_event = self.event_patch.start() + self.update_handler._set_sentinal() + self.update_handler._emit_restart_event() + self.assertEqual(1, mock_event.call_count) + except Exception as e: + pass + self.event_patch.stop() + return + + def _test_upgrade_available( self, base_version=FlexibleVersion(AGENT_VERSION), protocol=None, - versions=None): + versions=None, + count=5): - latest_version = self.prepare_agents() + latest_version = self.prepare_agents(count=count) if versions is None or len(versions) <= 0: versions = [latest_version] - etag = self.update_handler.last_etag if self.update_handler.last_etag is not None else 42 if protocol is None: - protocol = ProtocolMock(etag=etag, versions=versions) + protocol = ProtocolMock(versions=versions) self.update_handler.protocol_util = protocol conf.get_autoupdate_gafamily = Mock(return_value=protocol.family) - return self.update_handler._ensure_latest_agent(base_version=base_version) + return self.update_handler._upgrade_available(base_version=base_version) + + def test_upgrade_available_returns_true_on_first_use(self): + self.assertTrue(self._test_upgrade_available()) + return + + def test_get_latest_agent_excluded(self): + self.prepare_agent(AGENT_VERSION) + self.assertFalse(self._test_upgrade_available( + versions=self.agent_versions(), + count=1)) + self.assertEqual(None, self.update_handler.get_latest_agent()) + return - def test_ensure_latest_agent_returns_true_on_first_use(self): - self.assertEqual(None, self.update_handler.last_etag) - self.assertTrue(self._test_ensure_latest_agent()) + def test_upgrade_available_handles_missing_family(self): + extensions_config = ExtensionsConfig(load_data("wire/ext_conf_missing_family.xml")) + protocol = ProtocolMock() + protocol.family = "Prod" + protocol.agent_manifests = extensions_config.vmagent_manifests + self.update_handler.protocol_util = protocol + with patch('azurelinuxagent.common.logger.warn') as mock_logger: + with patch('tests.ga.test_update.ProtocolMock.get_vmagent_pkgs', side_effect=ProtocolError): + self.assertFalse(self.update_handler._upgrade_available(base_version=CURRENT_VERSION)) + self.assertEqual(0, mock_logger.call_count) return - def test_ensure_latest_agent_includes_old_agents(self): + def test_upgrade_available_includes_old_agents(self): self.prepare_agents() - old_count = FlexibleVersion(AGENT_VERSION).version[-1] old_version = self.agent_versions()[-1] + old_count = old_version.version[-1] self.replicate_agents(src_v=old_version, count=old_count, increment=-1) all_count = len(self.agent_versions()) - self.assertTrue(self._test_ensure_latest_agent(versions=self.agent_versions())) + self.assertTrue(self._test_upgrade_available(versions=self.agent_versions())) self.assertEqual(all_count, len(self.update_handler.agents)) return - def test_ensure_lastest_agent_purges_old_agents(self): + def test_upgrade_available_purges_old_agents(self): self.prepare_agents() agent_count = self.agent_count() self.assertEqual(5, agent_count) agent_versions = self.agent_versions()[:3] - self.assertTrue(self._test_ensure_latest_agent(versions=agent_versions)) + self.assertTrue(self._test_upgrade_available(versions=agent_versions)) self.assertEqual(len(agent_versions), len(self.update_handler.agents)) self.assertEqual(agent_versions, self.agent_versions()) return - def test_ensure_latest_agent_skips_if_too_frequent(self): + def test_upgrade_available_skips_if_too_frequent(self): conf.get_autoupdate_frequency = Mock(return_value=10000) self.update_handler.last_attempt_time = time.time() - self.assertFalse(self._test_ensure_latest_agent()) + self.assertFalse(self._test_upgrade_available()) return - def test_ensure_latest_agent_skips_when_etag_matches(self): - self.update_handler.last_etag = 42 - self.assertFalse(self._test_ensure_latest_agent()) - return - - def test_ensure_latest_agent_skips_if_when_no_new_versions(self): + def test_upgrade_available_skips_if_when_no_new_versions(self): self.prepare_agents() base_version = self.agent_versions()[0] + 1 - self.assertFalse(self._test_ensure_latest_agent(base_version=base_version)) + self.assertFalse(self._test_upgrade_available(base_version=base_version)) return - def test_ensure_latest_agent_skips_when_no_versions(self): - self.assertFalse(self._test_ensure_latest_agent(protocol=ProtocolMock())) + def test_upgrade_available_skips_when_no_versions(self): + self.assertFalse(self._test_upgrade_available(protocol=ProtocolMock())) return - def test_ensure_latest_agent_skips_when_updates_are_disabled(self): + def test_upgrade_available_skips_when_updates_are_disabled(self): conf.get_autoupdate_enabled = Mock(return_value=False) - self.assertFalse(self._test_ensure_latest_agent()) + self.assertFalse(self._test_upgrade_available()) return - def test_ensure_latest_agent_sorts(self): + def test_upgrade_available_sorts(self): self.prepare_agents() - self._test_ensure_latest_agent() + self._test_upgrade_available() v = FlexibleVersion("100000") for a in self.update_handler.agents: @@ -700,6 +779,58 @@ class TestUpdate(UpdateTestCase): v = a.version return + def _test_ensure_no_orphans(self, invocations=3, interval=ORPHAN_WAIT_INTERVAL): + with patch.object(self.update_handler, 'osutil') as mock_util: + # Note: + # - Python only allows mutations of objects to which a function has + # a reference. Incrementing an integer directly changes the + # reference. Incrementing an item of a list changes an item to + # which the code has a reference. + # See http://stackoverflow.com/questions/26408941/python-nested-functions-and-variable-scope + iterations = [0] + def iterator(*args, **kwargs): + iterations[0] += 1 + return iterations[0] < invocations + + mock_util.check_pid_alive = Mock(side_effect=iterator) + + with patch('os.getpid', return_value=42): + with patch('time.sleep', return_value=None) as mock_sleep: + self.update_handler._ensure_no_orphans(orphan_wait_interval=interval) + return mock_util.check_pid_alive.call_count, mock_sleep.call_count + return + + def test_ensure_no_orphans(self): + fileutil.write_file(os.path.join(self.tmp_dir, "0_waagent.pid"), ustr(41)) + calls, sleeps = self._test_ensure_no_orphans(invocations=3) + self.assertEqual(3, calls) + self.assertEqual(2, sleeps) + return + + def test_ensure_no_orphans_skips_if_no_orphans(self): + calls, sleeps = self._test_ensure_no_orphans(invocations=3) + self.assertEqual(0, calls) + self.assertEqual(0, sleeps) + return + + def test_ensure_no_orphans_ignores_exceptions(self): + with patch('azurelinuxagent.common.utils.fileutil.read_file', side_effect=Exception): + calls, sleeps = self._test_ensure_no_orphans(invocations=3) + self.assertEqual(0, calls) + self.assertEqual(0, sleeps) + return + + def test_ensure_no_orphans_kills_after_interval(self): + fileutil.write_file(os.path.join(self.tmp_dir, "0_waagent.pid"), ustr(41)) + with patch('os.kill') as mock_kill: + calls, sleeps = self._test_ensure_no_orphans( + invocations=4, + interval=3*GOAL_STATE_INTERVAL) + self.assertEqual(3, calls) + self.assertEqual(2, sleeps) + self.assertEqual(1, mock_kill.call_count) + return + def _test_evaluate_agent_health(self, child_agent_index=0): self.prepare_agents() @@ -789,6 +920,59 @@ class TestUpdate(UpdateTestCase): self.assertEqual(latest_agent.version, prior_agent.version) return + def test_get_pid_files(self): + previous_pid_file, pid_file, = self.update_handler._get_pid_files() + self.assertEqual(None, previous_pid_file) + self.assertEqual("0_waagent.pid", os.path.basename(pid_file)) + return + + def test_get_pid_files_returns_previous(self): + for n in range(1250): + fileutil.write_file(os.path.join(self.tmp_dir, str(n)+"_waagent.pid"), ustr(n+1)) + previous_pid_file, pid_file, = self.update_handler._get_pid_files() + self.assertEqual("1249_waagent.pid", os.path.basename(previous_pid_file)) + self.assertEqual("1250_waagent.pid", os.path.basename(pid_file)) + return + + def test_is_clean_start_returns_true_when_no_sentinal(self): + self.assertFalse(os.path.isfile(self.update_handler._sentinal_file_path())) + self.assertTrue(self.update_handler._is_clean_start) + return + + def test_is_clean_start_returns_true_sentinal_agent_is_not_current(self): + self.update_handler._set_sentinal(agent="Not the Current Agent") + self.assertTrue(os.path.isfile(self.update_handler._sentinal_file_path())) + self.assertTrue(self.update_handler._is_clean_start) + return + + def test_is_clean_start_returns_false_for_current_agent(self): + self.update_handler._set_sentinal(agent=CURRENT_AGENT) + self.assertFalse(self.update_handler._is_clean_start) + return + + def test_is_clean_start_returns_false_for_exceptions(self): + self.update_handler._set_sentinal() + with patch("azurelinuxagent.common.utils.fileutil.read_file", side_effect=Exception): + self.assertFalse(self.update_handler._is_clean_start) + return + + def test_is_orphaned_returns_false_if_parent_exists(self): + fileutil.write_file(conf.get_agent_pid_file_path(), ustr(42)) + with patch('os.getppid', return_value=42): + self.assertFalse(self.update_handler._is_orphaned) + return + + def test_is_orphaned_returns_true_if_parent_is_init(self): + with patch('os.getppid', return_value=1): + self.assertTrue(self.update_handler._is_orphaned) + return + + def test_is_orphaned_returns_true_if_parent_does_not_exist(self): + fileutil.write_file(conf.get_agent_pid_file_path(), ustr(24)) + with patch('os.getppid', return_value=42): + self.assertTrue(self.update_handler._is_orphaned) + return + def test_load_agents(self): self.prepare_agents() @@ -797,14 +981,14 @@ class TestUpdate(UpdateTestCase): self.assertEqual(len(get_agents(self.tmp_dir)), len(self.update_handler.agents)) return - def test_load_agents_does_not_reload(self): + def test_load_agents_does_reload(self): self.prepare_agents() self.update_handler._load_agents() agents = self.update_handler.agents self.update_handler._load_agents() - self.assertEqual(agents, self.update_handler.agents) + self.assertNotEqual(agents, self.update_handler.agents) return def test_load_agents_sorts(self): @@ -868,13 +1052,14 @@ class TestUpdate(UpdateTestCase): agent = self.update_handler.get_latest_agent() args, kwargs = self._test_run_latest() - cmds = shlex.split(agent.get_agent_cmd()) + cmds = textutil.safe_shlex_split(agent.get_agent_cmd()) if cmds[0].lower() == "python": cmds[0] = get_python_cmd() self.assertEqual(args[0], cmds) self.assertEqual(True, 'cwd' in kwargs) self.assertEqual(agent.get_agent_dir(), kwargs['cwd']) + self.assertEqual(False, '\x00' in cmds[0]) return def test_run_latest_polls_and_waits_for_success(self): @@ -945,7 +1130,8 @@ class TestUpdate(UpdateTestCase): self.assertEqual(0.0, latest_agent.error.last_failure) self.assertEqual(0, latest_agent.error.failure_count) - self._test_run_latest(mock_child=ChildMock(return_value=1)) + with patch('azurelinuxagent.ga.update.UpdateHandler.get_latest_agent', return_value=latest_agent): + self._test_run_latest(mock_child=ChildMock(return_value=1)) self.assertTrue(latest_agent.is_available) self.assertNotEqual(0.0, latest_agent.error.last_failure) @@ -961,7 +1147,8 @@ class TestUpdate(UpdateTestCase): self.assertEqual(0.0, latest_agent.error.last_failure) self.assertEqual(0, latest_agent.error.failure_count) - self._test_run_latest(mock_child=ChildMock(side_effect=Exception("Force blacklisting"))) + with patch('azurelinuxagent.ga.update.UpdateHandler.get_latest_agent', return_value=latest_agent): + self._test_run_latest(mock_child=ChildMock(side_effect=Exception("Force blacklisting"))) self.assertFalse(latest_agent.is_available) self.assertTrue(latest_agent.error.is_blacklisted) @@ -991,7 +1178,7 @@ class TestUpdate(UpdateTestCase): # reference. Incrementing an item of a list changes an item to # which the code has a reference. # See http://stackoverflow.com/questions/26408941/python-nested-functions-and-variable-scope - iterations = [0] + iterations = [0] def iterator(*args, **kwargs): iterations[0] += 1 if iterations[0] >= invocations: @@ -999,14 +1186,19 @@ class TestUpdate(UpdateTestCase): return calls = calls * invocations - + + fileutil.write_file(conf.get_agent_pid_file_path(), ustr(42)) + with patch('azurelinuxagent.ga.exthandlers.get_exthandlers_handler') as mock_handler: with patch('azurelinuxagent.ga.monitor.get_monitor_handler') as mock_monitor: with patch('azurelinuxagent.ga.env.get_env_handler') as mock_env: with patch('time.sleep', side_effect=iterator) as mock_sleep: with patch('sys.exit') as mock_exit: - - self.update_handler.run() + if isinstance(os.getppid, MagicMock): + self.update_handler.run() + else: + with patch('os.getppid', return_value=42): + self.update_handler.run() self.assertEqual(1, mock_handler.call_count) self.assertEqual(mock_handler.return_value.method_calls, calls) @@ -1014,6 +1206,7 @@ class TestUpdate(UpdateTestCase): self.assertEqual(1, mock_monitor.call_count) self.assertEqual(1, mock_env.call_count) self.assertEqual(1, mock_exit.call_count) + return def test_run(self): @@ -1025,8 +1218,30 @@ class TestUpdate(UpdateTestCase): return def test_run_stops_if_update_available(self): - self.update_handler._ensure_latest_agent = Mock(return_value=True) + self.update_handler._upgrade_available = Mock(return_value=True) + self._test_run(invocations=0, calls=[], enable_updates=True) + return + + def test_run_stops_if_orphaned(self): + with patch('os.getppid', return_value=1): + self._test_run(invocations=0, calls=[], enable_updates=True) + return + + def test_run_clears_sentinal_on_successful_exit(self): + self._test_run() + self.assertFalse(os.path.isfile(self.update_handler._sentinal_file_path())) + return + + def test_run_leaves_sentinal_on_unsuccessful_exit(self): + self.update_handler._upgrade_available = Mock(side_effect=Exception) self._test_run(invocations=0, calls=[], enable_updates=True) + self.assertTrue(os.path.isfile(self.update_handler._sentinal_file_path())) + return + + def test_run_emits_restart_event(self): + self.update_handler._emit_restart_event = Mock() + self._test_run() + self.assertEqual(1, self.update_handler._emit_restart_event.call_count) return def test_set_agents_sets_agents(self): @@ -1048,6 +1263,59 @@ class TestUpdate(UpdateTestCase): v = a.version return + def test_set_sentinal(self): + self.assertFalse(os.path.isfile(self.update_handler._sentinal_file_path())) + self.update_handler._set_sentinal() + self.assertTrue(os.path.isfile(self.update_handler._sentinal_file_path())) + return + + def test_set_sentinal_writes_current_agent(self): + self.update_handler._set_sentinal() + self.assertTrue( + fileutil.read_file(self.update_handler._sentinal_file_path()), + CURRENT_AGENT) + return + + def test_shutdown(self): + self.update_handler._set_sentinal() + self.update_handler._shutdown() + self.assertFalse(os.path.isfile(self.update_handler._sentinal_file_path())) + return + + def test_shutdown_ignores_missing_sentinal_file(self): + self.assertFalse(os.path.isfile(self.update_handler._sentinal_file_path())) + self.update_handler._shutdown() + self.assertFalse(os.path.isfile(self.update_handler._sentinal_file_path())) + return + + def test_shutdown_ignores_exceptions(self): + self.update_handler._set_sentinal() + + try: + with patch("os.remove", side_effect=Exception): + self.update_handler._shutdown() + except Exception as e: + self.assertTrue(False, "Unexpected exception") + return + + def test_write_pid_file(self): + for n in range(1112): + fileutil.write_file(os.path.join(self.tmp_dir, str(n)+"_waagent.pid"), ustr(n+1)) + with patch('os.getpid', return_value=1112): + previous_pid_file, pid_file = self.update_handler._write_pid_file() + self.assertEqual("1111_waagent.pid", os.path.basename(previous_pid_file)) + self.assertEqual("1112_waagent.pid", os.path.basename(pid_file)) + self.assertEqual(fileutil.read_file(pid_file), ustr(1112)) + return + + def test_write_pid_file_ignores_exceptions(self): + with patch('azurelinuxagent.common.utils.fileutil.write_file', side_effect=Exception): + with patch('os.getpid', return_value=42): + previous_pid_file, pid_file = self.update_handler._write_pid_file() + self.assertEqual(None, previous_pid_file) + self.assertEqual(None, pid_file) + return + class ChildMock(Mock): def __init__(self, return_value=0, side_effect=None): @@ -1059,8 +1327,9 @@ class ChildMock(Mock): class ProtocolMock(object): - def __init__(self, family="TestAgent", etag=42, versions=None): + def __init__(self, family="TestAgent", etag=42, versions=None, client=None): self.family = family + self.client = client self.etag = etag self.versions = versions if versions is not None else [] self.create_manifests() diff --git a/tests/pa/test_deprovision.py b/tests/pa/test_deprovision.py new file mode 100644 index 0000000..be34915 --- /dev/null +++ b/tests/pa/test_deprovision.py @@ -0,0 +1,48 @@ +# Copyright 2016 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# + +from tests.tools import * +from azurelinuxagent.pa.deprovision import get_deprovision_handler + + +class TestDeprovision(AgentTestCase): + @distros("redhat") + def test_deprovision(self, + distro_name, + distro_version, + distro_full_name): + deprovision_handler = get_deprovision_handler(distro_name, + distro_version, + distro_full_name) + warnings, actions = deprovision_handler.setup(deluser=False) + assert any("/etc/resolv.conf" in w for w in warnings) + + @distros("ubuntu") + def test_deprovision_ubuntu(self, + distro_name, + distro_version, + distro_full_name): + deprovision_handler = get_deprovision_handler(distro_name, + distro_version, + distro_full_name) + + with patch("os.path.realpath", return_value="/run/resolvconf/resolv.conf"): + warnings, actions = deprovision_handler.setup(deluser=False) + assert any("/etc/resolvconf/resolv.conf.d/tail" in w for w in warnings) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/protocol/mockmetadata.py b/tests/protocol/mockmetadata.py index dce3367..d41ce88 100644 --- a/tests/protocol/mockmetadata.py +++ b/tests/protocol/mockmetadata.py @@ -22,8 +22,11 @@ from azurelinuxagent.common.utils.cryptutil import CryptUtil DATA_FILE = { "identity": "metadata/identity.json", "certificates": "metadata/certificates.json", + "certificates_data": "metadata/certificates_data.json", "ext_handlers": "metadata/ext_handlers.json", "ext_handler_pkgs": "metadata/ext_handler_pkgs.json", + "trans_prv": "metadata/trans_prv", + "trans_cert": "metadata/trans_cert", } DATA_FILE_NO_EXT = DATA_FILE.copy() @@ -33,8 +36,11 @@ class MetadataProtocolData(object): def __init__(self, data_files): self.identity = load_data(data_files.get("identity")) self.certificates = load_data(data_files.get("certificates")) + self.certificates_data = load_data(data_files.get("certificates_data")) self.ext_handlers = load_data(data_files.get("ext_handlers")) self.ext_handler_pkgs = load_data(data_files.get("ext_handler_pkgs")) + self.trans_prv = load_data(data_files.get("trans_prv")) + self.trans_cert = load_data(data_files.get("trans_cert")) def mock_http_get(self, url, *args, **kwargs): content = None @@ -42,6 +48,8 @@ class MetadataProtocolData(object): content = self.identity elif url.count(u"certificates") > 0: content = self.certificates + elif url.count(u"certificates_data") > 0: + content = self.certificates_data elif url.count(u"extensionHandlers") > 0: content = self.ext_handlers elif url.count(u"versionUri") > 0: diff --git a/tests/protocol/test_hostplugin.py b/tests/protocol/test_hostplugin.py index 65c8465..3b99050 100644 --- a/tests/protocol/test_hostplugin.py +++ b/tests/protocol/test_hostplugin.py @@ -15,23 +15,32 @@ # Requires Python 2.4+ and Openssl 1.0+ # -from tests.tools import * import unittest -import azurelinuxagent.common.protocol.wire as wire + import azurelinuxagent.common.protocol.restapi as restapi +import azurelinuxagent.common.protocol.wire as wire +import azurelinuxagent.common.protocol.hostplugin as hostplugin +from tests.protocol.mockwiredata import WireProtocolData, DATA_FILE +from tests.tools import * wireserver_url = "168.63.129.16" sas_url = "http://sas_url" +testtype = 'BlockBlob' api_versions = '["2015-09-01"]' class TestHostPlugin(AgentTestCase): def test_fallback(self): - with patch.object(wire.HostPluginProtocol, - "put_vm_status") as patch_put: - with patch.object(wire.StatusBlob, "upload") as patch_upload: - patch_upload.return_value = False + """ + Validate fallback to upload status using HostGAPlugin is happening when status reporting via + default method is unsuccessful + """ + test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state) + + with patch.object(wire.HostPluginProtocol, "put_vm_status") as patch_put: + with patch.object(wire.StatusBlob, "upload", return_value=False) as patch_upload: wire_protocol_client = wire.WireProtocol(wireserver_url).client + wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state) wire_protocol_client.ext_conf = wire.ExtensionsConfig(None) wire_protocol_client.ext_conf.status_upload_blob = sas_url wire_protocol_client.upload_status_blob() @@ -39,7 +48,43 @@ class TestHostPlugin(AgentTestCase): "Fallback was not engaged") self.assertTrue(patch_put.call_args[0][1] == sas_url) + def test_validate_http_request(self): + """Validate correct set of data is sent to HostGAPlugin when reporting VM status""" + from azurelinuxagent.common.protocol.hostplugin import API_VERSION + from azurelinuxagent.common.utils import restutil + exp_method = 'PUT' + exp_url = 'http://{0}:32526/status'.format(wireserver_url) + exp_data = '{"content": "eyJkdW1teSI6ICJkYXRhIn0=", "headers": [{"headerName": ' \ + '"x-ms-version", "headerValue": "2014-02-14"}, ' \ + '{"headerName": "x-ms-blob-type", "headerValue": "BlockBlob"}], ' \ + '"requestUri": "http://sas_url"}' + test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state) + + with patch.object(restutil, "http_request") as patch_http: + wire_protocol_client = wire.WireProtocol(wireserver_url).client + wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state) + plugin = wire_protocol_client.get_host_plugin() + blob = wire_protocol_client.status_blob + blob.vm_status = restapi.VMStatus() + blob.data = '{"dummy": "data"}' + with patch.object(plugin, 'get_api_versions') as patch_api: + patch_api.return_value = API_VERSION + plugin.put_vm_status(blob, sas_url, testtype) + self.assertTrue(patch_http.call_count == 1) + self.assertTrue(patch_http.call_args[0][0] == exp_method) + self.assertTrue(patch_http.call_args[0][1] == exp_url) + self.assertTrue(patch_http.call_args[0][2] == exp_data) + + # Assert headers + headers = patch_http.call_args[1]['headers'] + self.assertEqual(headers['x-ms-containerid'], test_goal_state.container_id) + self.assertEqual(headers['x-ms-host-config-name'], test_goal_state.role_config_name) + def test_no_fallback(self): + """ + Validate fallback to upload status using HostGAPlugin is not happening when status reporting via + default method is successful + """ with patch.object(wire.HostPluginProtocol, "put_vm_status") as patch_put: with patch.object(wire.StatusBlob, "upload") as patch_upload: @@ -51,21 +96,29 @@ class TestHostPlugin(AgentTestCase): self.assertTrue(patch_put.call_count == 0, "Fallback was engaged") - def test_init_put(self): + def test_validate_http_put(self): + """Validate correct set of data is sent to HostGAPlugin when reporting VM status""" + test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state) expected_url = "http://168.63.129.16:32526/status" - expected_headers = {'x-ms-version': '2015-09-01'} - expected_content = '{"content": "b2s=", ' \ + expected_headers = {'x-ms-version': '2015-09-01', + "Content-type": "application/json", + "x-ms-containerid": test_goal_state.container_id, + "x-ms-host-config-name": test_goal_state.role_config_name} + expected_content = '{"content": "eyJkdW1teSI6ICJkYXRhIn0=", ' \ '"headers": [{"headerName": "x-ms-version", ' \ '"headerValue": "2014-02-14"}, ' \ '{"headerName": "x-ms-blob-type", "headerValue": ' \ '"BlockBlob"}], ' \ '"requestUri": "http://sas_url"}' - host_client = wire.HostPluginProtocol(wireserver_url) + host_client = wire.HostPluginProtocol(wireserver_url, + test_goal_state.container_id, + test_goal_state.role_config_name) self.assertFalse(host_client.is_initialized) self.assertTrue(host_client.api_versions is None) status_blob = wire.StatusBlob(None) - status_blob.vm_status = "ok" + status_blob.vm_status = restapi.VMStatus() + status_blob.data = '{"dummy": "data"}' status_blob.type = "BlockBlob" with patch.object(wire.HostPluginProtocol, "get_api_versions") as patch_get: @@ -77,9 +130,39 @@ class TestHostPlugin(AgentTestCase): self.assertFalse(host_client.api_versions is None) self.assertTrue(patch_put.call_count == 1) self.assertTrue(patch_put.call_args[0][0] == expected_url) - self.assertTrue(patch_put.call_args[0][1] == expected_content) - self.assertTrue(patch_put.call_args[0][2] == expected_headers) + self.assertTrue(patch_put.call_args[1]['data'] == expected_content) + self.assertTrue(patch_put.call_args[1]['headers'] == expected_headers) + + def test_validate_get_extension_artifacts(self): + test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state) + expected_url = hostplugin.URI_FORMAT_GET_EXTENSION_ARTIFACT.format(wireserver_url, hostplugin.HOST_PLUGIN_PORT) + expected_headers = {'x-ms-version': '2015-09-01', + "x-ms-containerid": test_goal_state.container_id, + "x-ms-host-config-name": test_goal_state.role_config_name, + "x-ms-artifact-location": sas_url} + + host_client = wire.HostPluginProtocol(wireserver_url, + test_goal_state.container_id, + test_goal_state.role_config_name) + self.assertFalse(host_client.is_initialized) + self.assertTrue(host_client.api_versions is None) + + with patch.object(wire.HostPluginProtocol, "get_api_versions", return_value=api_versions) as patch_get: + actual_url, actual_headers = host_client.get_artifact_request(sas_url) + self.assertTrue(host_client.is_initialized) + self.assertFalse(host_client.api_versions is None) + self.assertEqual(expected_url, actual_url) + for k in expected_headers: + self.assertTrue(k in actual_headers) + self.assertEqual(expected_headers[k], actual_headers[k]) + +class MockResponse: + def __init__(self, body, status_code): + self.body = body + self.status = status_code + def read(self): + return self.body if __name__ == '__main__': unittest.main() diff --git a/tests/protocol/test_metadata.py b/tests/protocol/test_metadata.py index e2ef57a..f390f7a 100644 --- a/tests/protocol/test_metadata.py +++ b/tests/protocol/test_metadata.py @@ -22,7 +22,7 @@ from azurelinuxagent.common.protocol.metadata import MetadataProtocol @patch("time.sleep") @patch("azurelinuxagent.common.protocol.metadata.restutil") -class TestWireProtocolGetters(AgentTestCase): +class TestMetadataProtocolGetters(AgentTestCase): def _test_getters(self, test_data, mock_restutil ,_): mock_restutil.http_get.side_effect = test_data.mock_http_get diff --git a/tests/protocol/test_wire.py b/tests/protocol/test_wire.py index bd3acaf..8c9cc02 100644 --- a/tests/protocol/test_wire.py +++ b/tests/protocol/test_wire.py @@ -15,31 +15,23 @@ # Requires Python 2.4+ and Openssl 1.0+ # -from tests.tools import * +from azurelinuxagent.common.protocol.wire import * from tests.protocol.mockwiredata import * -import uuid -import unittest -import os -import time -from azurelinuxagent.common.utils.restutil import httpclient -from azurelinuxagent.common.utils.cryptutil import CryptUtil -from azurelinuxagent.common.protocol.restapi import * -from azurelinuxagent.common.protocol.wire import WireClient, WireProtocol, \ - TRANSPORT_PRV_FILE_NAME, \ - TRANSPORT_CERT_FILE_NAME data_with_bom = b'\xef\xbb\xbfhehe' +testurl = 'http://foo' +testtype = 'BlockBlob' +wireserver_url = '168.63.129.16' @patch("time.sleep") @patch("azurelinuxagent.common.protocol.wire.CryptUtil") @patch("azurelinuxagent.common.protocol.wire.restutil") class TestWireProtocolGetters(AgentTestCase): - def _test_getters(self, test_data, mock_restutil, MockCryptUtil, _): mock_restutil.http_get.side_effect = test_data.mock_http_get MockCryptUtil.side_effect = test_data.mock_crypt_util - protocol = WireProtocol("foo.bar") + protocol = WireProtocol(wireserver_url) protocol.detect() protocol.get_vminfo() protocol.get_certs() @@ -47,9 +39,9 @@ class TestWireProtocolGetters(AgentTestCase): for ext_handler in ext_handlers.extHandlers: protocol.get_ext_handler_pkgs(ext_handler) - crt1 = os.path.join(self.tmp_dir, - '33B0ABCE4673538650971C10F7D7397E71561F35.crt') - crt2 = os.path.join(self.tmp_dir, + crt1 = os.path.join(self.tmp_dir, + '33B0ABCE4673538650971C10F7D7397E71561F35.crt') + crt2 = os.path.join(self.tmp_dir, '4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3.crt') prv2 = os.path.join(self.tmp_dir, '4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3.prv') @@ -57,7 +49,7 @@ class TestWireProtocolGetters(AgentTestCase): self.assertTrue(os.path.isfile(crt1)) self.assertTrue(os.path.isfile(crt2)) self.assertTrue(os.path.isfile(prv2)) - + def test_getters(self, *args): """Normal case""" test_data = WireProtocolData(DATA_FILE) @@ -72,11 +64,220 @@ class TestWireProtocolGetters(AgentTestCase): """Extensions without any settings""" test_data = WireProtocolData(DATA_FILE_EXT_NO_SETTINGS) self._test_getters(test_data, *args) - + def test_getters_ext_no_public(self, *args): """Extensions without any public settings""" test_data = WireProtocolData(DATA_FILE_EXT_NO_PUBLIC) self._test_getters(test_data, *args) + def test_call_storage_kwargs(self, + mock_restutil, + mock_cryptutil, + mock_sleep): + from azurelinuxagent.common.utils import restutil + with patch.object(restutil, 'http_get') as http_patch: + http_req = restutil.http_get + url = testurl + headers = {} + + # no kwargs + WireClient.call_storage_service(http_req) + # kwargs, no chk_proxy + WireClient.call_storage_service(http_req, + url, + headers) + # kwargs, chk_proxy False + WireClient.call_storage_service(http_req, + url, + headers, + chk_proxy=False) + # kwargs, chk_proxy True + WireClient.call_storage_service(http_req, + url, + headers, + chk_proxy=True) + # assert + self.assertTrue(http_patch.call_count == 4) + for c in http_patch.call_args_list: + self.assertTrue(c[-1]['chk_proxy'] == True) + + def test_status_blob_parsing(self, *args): + wire_protocol_client = WireProtocol(wireserver_url).client + wire_protocol_client.ext_conf = ExtensionsConfig(WireProtocolData(DATA_FILE).ext_conf) + self.assertEqual(wire_protocol_client.ext_conf.status_upload_blob, + u'https://yuezhatest.blob.core.windows.net/vhds/test' + u'-cs12.test-cs12.test-cs12.status?sr=b&sp=rw&se' + u'=9999-01-01&sk=key1&sv=2014-02-14&sig' + u'=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D') + self.assertEqual(wire_protocol_client.ext_conf.status_upload_blob_type, + u'BlockBlob') + pass + + def test_get_host_ga_plugin(self, *args): + wire_protocol_client = WireProtocol(wireserver_url).client + goal_state = GoalState(WireProtocolData(DATA_FILE).goal_state) + + with patch.object(WireClient, "get_goal_state", return_value = goal_state) as patch_get_goal_state: + host_plugin = wire_protocol_client.get_host_plugin() + self.assertEqual(goal_state.container_id, host_plugin.container_id) + self.assertEqual(goal_state.role_config_name, host_plugin.role_config_name) + patch_get_goal_state.assert_called_once() + + def test_download_ext_handler_pkg_fallback(self, *args): + ext_uri = 'extension_uri' + host_uri = 'host_uri' + mock_host = HostPluginProtocol(host_uri, 'container_id', 'role_config') + with patch.object(restutil, + "http_request", + side_effect=IOError) as patch_http: + with patch.object(WireClient, + "get_host_plugin", + return_value=mock_host): + with patch.object(HostPluginProtocol, + "get_artifact_request", + return_value=[host_uri, {}]) as patch_request: + + WireProtocol(wireserver_url).download_ext_handler_pkg(ext_uri) + + self.assertEqual(patch_http.call_count, 2) + self.assertEqual(patch_request.call_count, 1) + + self.assertEqual(patch_http.call_args_list[0][0][1], + ext_uri) + self.assertEqual(patch_http.call_args_list[1][0][1], + host_uri) + + def test_upload_status_blob_default(self, *args): + wire_protocol_client = WireProtocol(wireserver_url).client + wire_protocol_client.ext_conf = ExtensionsConfig(None) + wire_protocol_client.ext_conf.status_upload_blob = testurl + + with patch.object(WireClient, "get_goal_state") as patch_get_goal_state: + with patch.object(HostPluginProtocol, "put_vm_status") as patch_host_ga_plugin_upload: + with patch.object(StatusBlob, "upload", return_value = True) as patch_default_upload: + wire_protocol_client.upload_status_blob() + + patch_default_upload.assert_called_once_with(testurl) + patch_get_goal_state.assert_not_called() + patch_host_ga_plugin_upload.assert_not_called() + + def test_upload_status_blob_host_ga_plugin(self, *args): + wire_protocol_client = WireProtocol(wireserver_url).client + wire_protocol_client.ext_conf = ExtensionsConfig(None) + wire_protocol_client.ext_conf.status_upload_blob = testurl + wire_protocol_client.ext_conf.status_upload_blob_type = testtype + goal_state = GoalState(WireProtocolData(DATA_FILE).goal_state) + + with patch.object(HostPluginProtocol, "put_vm_status") as patch_host_ga_plugin_upload: + with patch.object(StatusBlob, "upload", return_value=False) as patch_default_upload: + wire_protocol_client.get_goal_state = Mock(return_value = goal_state) + wire_protocol_client.upload_status_blob() + + patch_default_upload.assert_called_once_with(testurl) + wire_protocol_client.get_goal_state.assert_called_once() + patch_host_ga_plugin_upload.assert_called_once_with(wire_protocol_client.status_blob, testurl, testtype) + + def test_get_in_vm_artifacts_profile_blob_not_available(self, *args): + wire_protocol_client = WireProtocol(wireserver_url).client + wire_protocol_client.ext_conf = ExtensionsConfig(None) + + # Test when artifacts_profile_blob is null/None + self.assertEqual(None, wire_protocol_client.get_artifacts_profile()) + + #Test when artifacts_profile_blob is whitespace + wire_protocol_client.ext_conf.artifacts_profile_blob = " " + self.assertEqual(None, wire_protocol_client.get_artifacts_profile()) + + def test_get_in_vm_artifacts_profile_response_body_not_valid(self, *args): + wire_protocol_client = WireProtocol(wireserver_url).client + wire_protocol_client.ext_conf = ExtensionsConfig(None) + wire_protocol_client.ext_conf.artifacts_profile_blob = testurl + goal_state = GoalState(WireProtocolData(DATA_FILE).goal_state) + wire_protocol_client.get_goal_state = Mock(return_value=goal_state) + + with patch.object(HostPluginProtocol, "get_artifact_request", + return_value = ['dummy_url', {}]) as host_plugin_get_artifact_url_and_headers: + #Test when response body is None + wire_protocol_client.call_storage_service = Mock(return_value=MockResponse(None, 200)) + in_vm_artifacts_profile = wire_protocol_client.get_artifacts_profile() + self.assertTrue(in_vm_artifacts_profile is None) + + #Test when response body is None + wire_protocol_client.call_storage_service = Mock(return_value=MockResponse(' '.encode('utf-8'), 200)) + in_vm_artifacts_profile = wire_protocol_client.get_artifacts_profile() + self.assertTrue(in_vm_artifacts_profile is None) + + #Test when response body is None + wire_protocol_client.call_storage_service = Mock(return_value=MockResponse('{ }'.encode('utf-8'), 200)) + in_vm_artifacts_profile = wire_protocol_client.get_artifacts_profile() + self.assertEqual(dict(), in_vm_artifacts_profile.__dict__, + 'If artifacts_profile_blob has empty json dictionary, in_vm_artifacts_profile ' + 'should contain nothing') + + host_plugin_get_artifact_url_and_headers.assert_called_with(testurl) + + + def test_get_in_vm_artifacts_profile_default(self, *args): + wire_protocol_client = WireProtocol(wireserver_url).client + wire_protocol_client.ext_conf = ExtensionsConfig(None) + wire_protocol_client.ext_conf.artifacts_profile_blob = testurl + goal_state = GoalState(WireProtocolData(DATA_FILE).goal_state) + wire_protocol_client.get_goal_state = Mock(return_value=goal_state) + + wire_protocol_client.call_storage_service = Mock(return_value=MockResponse('{"onHold": "true"}'.encode('utf-8'), 200)) + in_vm_artifacts_profile = wire_protocol_client.get_artifacts_profile() + self.assertEqual(dict(onHold='true'), in_vm_artifacts_profile.__dict__) + self.assertTrue(in_vm_artifacts_profile.is_on_hold()) + + @patch("time.sleep") + def test_fetch_manifest_fallback(self, patch_sleep, *args): + uri1 = ExtHandlerVersionUri() + uri1.uri = 'ext_uri' + uris = DataContractList(ExtHandlerVersionUri) + uris.append(uri1) + host_uri = 'host_uri' + mock_host = HostPluginProtocol(host_uri, + 'container_id', + 'role_config') + client = WireProtocol(wireserver_url).client + with patch.object(WireClient, + "fetch", + return_value=None) as patch_fetch: + with patch.object(WireClient, + "get_host_plugin", + return_value=mock_host): + with patch.object(HostPluginProtocol, + "get_artifact_request", + return_value=[host_uri, {}]): + self.assertRaises(ProtocolError, client.fetch_manifest, uris) + self.assertEqual(patch_fetch.call_count, 2) + self.assertEqual(patch_fetch.call_args_list[0][0][0], uri1.uri) + self.assertEqual(patch_fetch.call_args_list[1][0][0], host_uri) + + def test_get_in_vm_artifacts_profile_host_ga_plugin(self, *args): + wire_protocol_client = WireProtocol(wireserver_url).client + wire_protocol_client.ext_conf = ExtensionsConfig(None) + wire_protocol_client.ext_conf.artifacts_profile_blob = testurl + goal_state = GoalState(WireProtocolData(DATA_FILE).goal_state) + wire_protocol_client.get_goal_state = Mock(return_value=goal_state) + wire_protocol_client.fetch = Mock(side_effect=[None, '{"onHold": "true"}'.encode('utf-8')]) + with patch.object(HostPluginProtocol, + "get_artifact_request", + return_value=['dummy_url', {}]) as artifact_request: + in_vm_artifacts_profile = wire_protocol_client.get_artifacts_profile() + self.assertTrue(in_vm_artifacts_profile is not None) + self.assertEqual(dict(onHold='true'), in_vm_artifacts_profile.__dict__) + self.assertTrue(in_vm_artifacts_profile.is_on_hold()) + artifact_request.assert_called_once_with(testurl) + + +class MockResponse: + def __init__(self, body, status_code): + self.body = body + self.status = status_code + + def read(self): + return self.body + if __name__ == '__main__': unittest.main() diff --git a/tests/tools.py b/tests/tools.py index 8bf23ed..8801a0c 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -56,10 +56,12 @@ class AgentTestCase(unittest.TestCase): def setUp(self): prefix = "{0}_".format(self.__class__.__name__) self.tmp_dir = tempfile.mkdtemp(prefix=prefix) + self.test_file = 'test_file' conf.get_autoupdate_enabled = Mock(return_value=True) conf.get_lib_dir = Mock(return_value=self.tmp_dir) ext_log_dir = os.path.join(self.tmp_dir, "azure") conf.get_ext_log_dir = Mock(return_value=ext_log_dir) + conf.get_agent_pid_file_path = Mock(return_value=os.path.join(self.tmp_dir, "waagent.pid")) def tearDown(self): if not debug and self.tmp_dir is not None: diff --git a/tests/utils/test_file_util.py b/tests/utils/test_file_util.py index 9a5479e..f16f409 100644 --- a/tests/utils/test_file_util.py +++ b/tests/utils/test_file_util.py @@ -24,8 +24,9 @@ from azurelinuxagent.common.future import ustr import azurelinuxagent.common.utils.fileutil as fileutil class TestFileOperations(AgentTestCase): + def test_read_write_file(self): - test_file=os.path.join(self.tmp_dir, 'test_file') + test_file=os.path.join(self.tmp_dir, self.test_file) content = ustr(uuid.uuid4()) fileutil.write_file(test_file, content) @@ -34,7 +35,7 @@ class TestFileOperations(AgentTestCase): os.remove(test_file) def test_rw_utf8_file(self): - test_file=os.path.join(self.tmp_dir, 'test_file') + test_file=os.path.join(self.tmp_dir, self.test_file) content = u"\u6211" fileutil.write_file(test_file, content, encoding="utf-8") @@ -43,14 +44,14 @@ class TestFileOperations(AgentTestCase): os.remove(test_file) def test_remove_bom(self): - test_file=os.path.join(self.tmp_dir, 'test_file') + test_file=os.path.join(self.tmp_dir, self.test_file) data = b'\xef\xbb\xbfhehe' fileutil.write_file(test_file, data, asbin=True) data = fileutil.read_file(test_file, remove_bom=True) self.assertNotEquals(0xbb, ord(data[0])) def test_append_file(self): - test_file=os.path.join(self.tmp_dir, 'test_file') + test_file=os.path.join(self.tmp_dir, self.test_file) content = ustr(uuid.uuid4()) fileutil.append_file(test_file, content) @@ -68,5 +69,53 @@ class TestFileOperations(AgentTestCase): filename = fileutil.base_name(filepath) self.assertEquals('abc', filename) + def test_remove_files(self): + import random + import string + import glob + random_word = lambda : ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(5)) + + #Create 10 test files + test_file = os.path.join(self.tmp_dir, self.test_file) + test_file2 = os.path.join(self.tmp_dir, 'another_file') + test_files = [test_file + random_word() for _ in range(5)] + \ + [test_file2 + random_word() for _ in range(5)] + for file in test_files: + open(file, 'a').close() + + #Remove files using fileutil.rm_files + test_file_pattern = test_file + '*' + test_file_pattern2 = test_file2 + '*' + fileutil.rm_files(test_file_pattern, test_file_pattern2) + + self.assertEqual(0, len(glob.glob(os.path.join(self.tmp_dir, test_file_pattern)))) + self.assertEqual(0, len(glob.glob(os.path.join(self.tmp_dir, test_file_pattern2)))) + + def test_get_all_files(self): + import random + import string + random_word = lambda: ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(5)) + + # Create 10 test files at the root dir and 10 other in the sub dir + test_file = os.path.join(self.tmp_dir, self.test_file) + test_file2 = os.path.join(self.tmp_dir, 'another_file') + expected_files = [test_file + random_word() for _ in range(5)] + \ + [test_file2 + random_word() for _ in range(5)] + + test_subdir = os.path.join(self.tmp_dir, 'test_dir') + os.mkdir(test_subdir) + test_file_in_subdir = os.path.join(test_subdir, self.test_file) + test_file_in_subdir2 = os.path.join(test_subdir, 'another_file') + expected_files.extend([test_file_in_subdir + random_word() for _ in range(5)] + \ + [test_file_in_subdir2 + random_word() for _ in range(5)]) + + for file in expected_files: + open(file, 'a').close() + + # Get All files using fileutil.get_all_files + actual_files = fileutil.get_all_files(self.tmp_dir) + + self.assertEqual(set(expected_files), set(actual_files)) + if __name__ == '__main__': unittest.main() diff --git a/tests/utils/test_rest_util.py b/tests/utils/test_rest_util.py index 874e527..5f084a6 100644 --- a/tests/utils/test_rest_util.py +++ b/tests/utils/test_rest_util.py @@ -15,16 +15,13 @@ # Requires Python 2.4+ and Openssl 1.0+ # -from tests.tools import AgentTestCase, patch, Mock, MagicMock -import uuid import unittest -import os import azurelinuxagent.common.utils.restutil as restutil -from azurelinuxagent.common.future import ustr, httpclient -import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.future import httpclient +from tests.tools import AgentTestCase, patch, Mock, MagicMock -class TestHttpOperations(AgentTestCase): +class TestHttpOperations(AgentTestCase): def test_parse_url(self): test_uri = "http://abc.def/ghi#hash?jkl=mn" host, port, secure, rel_uri = restutil._parse_url(test_uri) @@ -36,11 +33,11 @@ class TestHttpOperations(AgentTestCase): self.assertEquals("abc.def", host) self.assertEquals("/", rel_uri) self.assertEquals(False, secure) - + test_uri = "https://abc.def/ghi?jkl=mn" host, port, secure, rel_uri = restutil._parse_url(test_uri) self.assertEquals(True, secure) - + test_uri = "http://abc.def:80/" host, port, secure, rel_uri = restutil._parse_url(test_uri) self.assertEquals("abc.def", host) @@ -53,71 +50,71 @@ class TestHttpOperations(AgentTestCase): self.assertEquals(None, host) self.assertEquals(rel_uri, "None") - @patch("azurelinuxagent.common.future.httpclient.HTTPSConnection") @patch("azurelinuxagent.common.future.httpclient.HTTPConnection") def test_http_request(self, HTTPConnection, HTTPSConnection): - mock_httpconn = MagicMock() - mock_httpresp = MagicMock() - mock_httpconn.getresponse = Mock(return_value=mock_httpresp) - HTTPConnection.return_value = mock_httpconn - HTTPSConnection.return_value = mock_httpconn - - mock_httpresp.read = Mock(return_value="_(:3| <)_") - - #Test http get + mock_http_conn = MagicMock() + mock_http_resp = MagicMock() + mock_http_conn.getresponse = Mock(return_value=mock_http_resp) + HTTPConnection.return_value = mock_http_conn + HTTPSConnection.return_value = mock_http_conn + + mock_http_resp.read = Mock(return_value="_(:3| <)_") + + # Test http get resp = restutil._http_request("GET", "foo", "bar") self.assertNotEquals(None, resp) self.assertEquals("_(:3| <)_", resp.read()) - - #Test https get + + # Test https get resp = restutil._http_request("GET", "foo", "bar", secure=True) self.assertNotEquals(None, resp) self.assertEquals("_(:3| <)_", resp.read()) - - #Test http get with proxy - mock_httpresp.read = Mock(return_value="_(:3| <)_") + + # Test http get with proxy + mock_http_resp.read = Mock(return_value="_(:3| <)_") resp = restutil._http_request("GET", "foo", "bar", proxy_host="foo.bar", proxy_port=23333) self.assertNotEquals(None, resp) self.assertEquals("_(:3| <)_", resp.read()) - - #Test https get + + # Test https get resp = restutil._http_request("GET", "foo", "bar", secure=True) self.assertNotEquals(None, resp) self.assertEquals("_(:3| <)_", resp.read()) - - #Test https get with proxy - mock_httpresp.read = Mock(return_value="_(:3| <)_") + + # Test https get with proxy + mock_http_resp.read = Mock(return_value="_(:3| <)_") resp = restutil._http_request("GET", "foo", "bar", proxy_host="foo.bar", proxy_port=23333, secure=True) self.assertNotEquals(None, resp) self.assertEquals("_(:3| <)_", resp.read()) - + @patch("time.sleep") @patch("azurelinuxagent.common.utils.restutil._http_request") def test_http_request_with_retry(self, _http_request, sleep): - mock_httpresp = MagicMock() - mock_httpresp.read = Mock(return_value="hehe") - _http_request.return_value = mock_httpresp + mock_http_resp = MagicMock() + mock_http_resp.read = Mock(return_value="hehe") + _http_request.return_value = mock_http_resp - #Test http get - resp = restutil.http_get("http://foo.bar") + # Test http get + resp = restutil.http_get("http://foo.bar") self.assertEquals("hehe", resp.read()) - #Test https get - resp = restutil.http_get("https://foo.bar") + # Test https get + resp = restutil.http_get("https://foo.bar") self.assertEquals("hehe", resp.read()) - - #Test http failure + + # Test http failure _http_request.side_effect = httpclient.HTTPException("Http failure") - self.assertRaises(restutil.HttpError, restutil.http_get, "http://foo.bar") + self.assertRaises(restutil.HttpError, restutil.http_get, + "http://foo.bar") - #Test http failure + # Test http failure _http_request.side_effect = IOError("IO failure") - self.assertRaises(restutil.HttpError, restutil.http_get, "http://foo.bar") - -if __name__ == '__main__': - unittest.main() + self.assertRaises(restutil.HttpError, restutil.http_get, + "http://foo.bar") + + if __name__ == '__main__': unittest.main() diff --git a/tests/utils/test_text_util.py b/tests/utils/test_text_util.py index 9ac0707..dc3de85 100644 --- a/tests/utils/test_text_util.py +++ b/tests/utils/test_text_util.py @@ -35,12 +35,31 @@ class TestTextUtil(AgentTestCase): data = ustr(b'\xef\xbb\xbfhehe', encoding='utf-8') data = textutil.remove_bom(data) self.assertNotEquals(0xbb, data[0]) - + + #bom is comprised of a sequence of three bytes and ff length of the input is shorter + # than three bytes, remove_bom should not do anything + data = u"\xa7" + data = textutil.remove_bom(data) + self.assertEquals(data, data[0]) + + data = u"\xa7\xef" + data = textutil.remove_bom(data) + self.assertEquals(u"\xa7", data[0]) + self.assertEquals(u"\xef", data[1]) + #Test string without BOM is not affected data = u"hehe" data = textutil.remove_bom(data) self.assertEquals(u"h", data[0]) + data = u"" + data = textutil.remove_bom(data) + self.assertEquals(u"", data) + + data = u" " + data = textutil.remove_bom(data) + self.assertEquals(u" ", data) + def test_version_compare(self): self.assertTrue(Version("1.0") < Version("1.1")) self.assertTrue(Version("1.9") < Version("1.10")) |