summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/common/osutil/test_bigip.py352
-rw-r--r--tests/common/osutil/test_default.py22
-rw-r--r--tests/common/test_version.py109
-rw-r--r--tests/daemon/test_resourcedisk.py43
-rw-r--r--tests/data/ga/WALinuxAgent-2.1.5.zipbin244184 -> 0 bytes
-rw-r--r--tests/data/ga/WALinuxAgent-2.2.0.zipbin0 -> 357277 bytes
-rw-r--r--tests/data/metadata/certificates.json2
-rw-r--r--tests/data/metadata/certificates_data.json74
-rw-r--r--tests/data/metadata/trans_cert19
-rw-r--r--tests/data/metadata/trans_prv28
-rw-r--r--tests/data/wire/ext_conf.xml2
-rw-r--r--tests/data/wire/ext_conf_missing_family.xml33
-rw-r--r--tests/data/wire/goal_state.xml1
-rw-r--r--tests/distro/test_resourceDisk.py69
-rw-r--r--tests/ga/test_extension.py177
-rw-r--r--tests/ga/test_update.py381
-rw-r--r--tests/pa/test_deprovision.py48
-rw-r--r--tests/protocol/mockmetadata.py8
-rw-r--r--tests/protocol/test_hostplugin.py109
-rw-r--r--tests/protocol/test_metadata.py2
-rw-r--r--tests/protocol/test_wire.py237
-rw-r--r--tests/tools.py2
-rw-r--r--tests/utils/test_file_util.py57
-rw-r--r--tests/utils/test_rest_util.py85
-rw-r--r--tests/utils/test_text_util.py21
25 files changed, 1738 insertions, 143 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
deleted file mode 100644
index b1f2684..0000000
--- a/tests/data/ga/WALinuxAgent-2.1.5.zip
+++ /dev/null
Binary files differ
diff --git a/tests/data/ga/WALinuxAgent-2.2.0.zip b/tests/data/ga/WALinuxAgent-2.2.0.zip
new file mode 100644
index 0000000..4f28a83
--- /dev/null
+++ b/tests/data/ga/WALinuxAgent-2.2.0.zip
Binary files differ
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&amp;sp=rw&amp;se=9999-01-01&amp;sk=key1&amp;sv=2014-02-14&amp;sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob></Extensions>
+<StatusUploadBlob statusBlobType="BlockBlob">https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&amp;sp=rw&amp;se=9999-01-01&amp;sk=key1&amp;sv=2014-02-14&amp;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&amp;sp=rw&amp;se=9999-01-01&amp;sk=key1&amp;sv=2014-02-14&amp;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 89fe95d..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()
@@ -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):
@@ -993,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:
@@ -1001,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)
@@ -1016,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):
@@ -1027,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):
@@ -1050,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):
@@ -1061,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"))