diff options
Diffstat (limited to 'tests')
31 files changed, 1218 insertions, 437 deletions
diff --git a/tests/common/osutil/test_default.py b/tests/common/osutil/test_default.py index 08125ae..c9fa1de 100644 --- a/tests/common/osutil/test_default.py +++ b/tests/common/osutil/test_default.py @@ -489,6 +489,62 @@ Match host 192.168.1.2\n\ print("WRITING TO {0}".format(waagent_sudoers)) self.assertEqual(1, count) + def test_get_firewall_dropped_packets_returns_zero_if_firewall_disabled(self): + osutil._enable_firewall = False + util = osutil.DefaultOSUtil() + + self.assertEqual(0, util.get_firewall_dropped_packets("not used")) + + @patch('azurelinuxagent.common.utils.shellutil.run_get_output') + def test_get_firewall_dropped_packets_returns_negative_if_error(self, mock_output): + osutil._enable_firewall = True + util = osutil.DefaultOSUtil() + + mock_output.side_effect = [ + (0, "iptables v{0}".format(osutil.IPTABLES_LOCKING_VERSION)), + (1, "not used")] + self.assertEqual(-1, util.get_firewall_dropped_packets("not used")) + + @patch('azurelinuxagent.common.utils.shellutil.run_get_output') + def test_get_firewall_dropped_packets_returns_negative_if_exception(self, mock_output): + osutil._enable_firewall = True + util = osutil.DefaultOSUtil() + + mock_output.side_effect = [ + (0, "iptables v{0}".format(osutil.IPTABLES_LOCKING_VERSION)), + (1, Exception)] + self.assertEqual(-1, util.get_firewall_dropped_packets("not used")) + + @patch('azurelinuxagent.common.utils.shellutil.run_get_output') + def test_get_firewall_dropped_packets_transient_error_ignored(self, mock_output): + osutil._enable_firewall = True + util = osutil.DefaultOSUtil() + + mock_output.side_effect = [ + (0, "iptables v{0}".format(osutil.IPTABLES_LOCKING_VERSION)), + (3, "can't initialize iptables table `security': iptables who? (do you need to insmod?)")] + self.assertEqual(0, util.get_firewall_dropped_packets("not used")) + + @patch('azurelinuxagent.common.utils.shellutil.run_get_output') + def test_get_firewall_dropped_packets(self, mock_output): + osutil._enable_firewall = True + util = osutil.DefaultOSUtil() + + mock_output.side_effect = [ + (0, "iptables v{0}".format(osutil.IPTABLES_LOCKING_VERSION)), + (0, +''' + +Chain OUTPUT (policy ACCEPT 104 packets, 43628 bytes) + pkts bytes target prot opt in out source destination + 0 0 ACCEPT tcp -- any any anywhere 168.63.129.16 owner UID match daemon + 32 1920 DROP tcp -- any any anywhere 168.63.129.16 + +''')] + dst = '168.63.129.16' + + self.assertEqual(32, util.get_firewall_dropped_packets(dst)) + @patch('os.getuid', return_value=42) @patch('azurelinuxagent.common.utils.shellutil.run_get_output') @patch('azurelinuxagent.common.utils.shellutil.run') @@ -592,6 +648,34 @@ Match host 192.168.1.2\n\ ]) self.assertFalse(osutil._enable_firewall) + @patch('azurelinuxagent.common.utils.shellutil.run_get_output') + @patch('azurelinuxagent.common.utils.shellutil.run') + def test_enable_firewall_checks_for_invalid_iptables_options(self, mock_run, mock_output): + osutil._enable_firewall = True + util = osutil.DefaultOSUtil() + + dst = '1.2.3.4' + version = "iptables v{0}".format(osutil.IPTABLES_LOCKING_VERSION) + wait = "-w" + + # iptables uses the following exit codes + # 0 - correct function + # 1 - other errors + # 2 - errors which appear to be caused by invalid or abused command + # line parameters + mock_run.side_effect = [2] + mock_output.return_value = (0, version) + + self.assertFalse(util.enable_firewall(dst_ip='1.2.3.4', uid=42)) + self.assertFalse(osutil._enable_firewall) + + mock_run.assert_has_calls([ + call(osutil.FIREWALL_DROP.format(wait, "C", dst), chk_err=False), + ]) + mock_output.assert_has_calls([ + call(osutil.IPTABLES_VERSION) + ]) + @patch('os.getuid', return_value=42) @patch('azurelinuxagent.common.utils.shellutil.run_get_output') @patch('azurelinuxagent.common.utils.shellutil.run') @@ -624,17 +708,54 @@ Match host 192.168.1.2\n\ version = "iptables v{0}".format(osutil.IPTABLES_LOCKING_VERSION) wait = "-w" - mock_run.side_effect = [0, 0] + mock_run.side_effect = [0, 1, 0, 1] mock_output.side_effect = [(0, version), (0, "Output")] - self.assertTrue(util.remove_firewall()) + self.assertTrue(util.remove_firewall(dst, uid)) mock_run.assert_has_calls([ - call(osutil.FIREWALL_FLUSH.format(wait), chk_err=False) + call(osutil.FIREWALL_DELETE_CONNTRACK.format(wait, dst), chk_err=False), + call(osutil.FIREWALL_DELETE_CONNTRACK.format(wait, dst), chk_err=False), + call(osutil.FIREWALL_DELETE_OWNER.format(wait, dst, uid), chk_err=False), + call(osutil.FIREWALL_DELETE_OWNER.format(wait, dst, uid), chk_err=False), ]) mock_output.assert_has_calls([ call(osutil.IPTABLES_VERSION) ]) self.assertTrue(osutil._enable_firewall) + @patch('os.getuid', return_value=42) + @patch('azurelinuxagent.common.utils.shellutil.run_get_output') + @patch('azurelinuxagent.common.utils.shellutil.run') + def test_remove_firewall_does_not_repeat(self, mock_run, mock_output, _): + osutil._enable_firewall = True + util = osutil.DefaultOSUtil() + + dst_ip='1.2.3.4' + uid=42 + version = "iptables v{0}".format(osutil.IPTABLES_LOCKING_VERSION) + wait = "-w" + + mock_run.side_effect = [2] + mock_output.side_effect = [(0, version), (1, "Output")] + self.assertFalse(util.remove_firewall(dst_ip, uid)) + + mock_run.assert_has_calls([ + call(osutil.FIREWALL_DELETE_CONNTRACK.format(wait, dst_ip), chk_err=False), + ]) + mock_output.assert_has_calls([ + call(osutil.IPTABLES_VERSION) + ]) + self.assertFalse(osutil._enable_firewall) + + self.assertTrue(mock_run.call_count == 1) + self.assertTrue(mock_output.call_count == 1) + + self.assertFalse(util.remove_firewall()) + self.assertFalse(util.remove_firewall()) + + self.assertTrue(mock_run.call_count == 1) + self.assertTrue(mock_output.call_count == 1) + + if __name__ == '__main__': unittest.main() diff --git a/tests/common/test_conf.py b/tests/common/test_conf.py index 93759de..057c83b 100644 --- a/tests/common/test_conf.py +++ b/tests/common/test_conf.py @@ -48,6 +48,7 @@ class TestConf(AgentTestCase): "OS.EnableFIPS" : True, "OS.RootDeviceScsiTimeout" : '300', "OS.OpensslPath" : '/usr/bin/openssl', + "OS.SshClientAliveInterval" : 42, "OS.SshDir" : "/notareal/path", "HttpProxy.Host" : None, "HttpProxy.Port" : None, @@ -64,7 +65,7 @@ class TestConf(AgentTestCase): "AutoUpdate.GAFamily" : "Prod", "EnableOverProvisioning" : False, "OS.AllowHTTP" : False, - "OS.EnableFirewall" : True + "OS.EnableFirewall" : False } def setUp(self): @@ -77,6 +78,7 @@ class TestConf(AgentTestCase): def test_key_value_handling(self): self.assertEqual("Value1", self.conf.get("FauxKey1", "Bad")) self.assertEqual("Value2 Value2", self.conf.get("FauxKey2", "Bad")) + self.assertEqual("delalloc,rw,noatime,nobarrier,users,mode=777", self.conf.get("FauxKey3", "Bad")) def test_get_ssh_dir(self): self.assertTrue(get_ssh_dir(self.conf).startswith("/notareal/path")) @@ -109,4 +111,5 @@ class TestConf(AgentTestCase): for k in TestConf.EXPECTED_CONFIGURATION.keys(): self.assertEqual( TestConf.EXPECTED_CONFIGURATION[k], - configuration[k]) + configuration[k], + k) diff --git a/tests/common/test_errorstate.py b/tests/common/test_errorstate.py new file mode 100644 index 0000000..7513fe5 --- /dev/null +++ b/tests/common/test_errorstate.py @@ -0,0 +1,69 @@ +from datetime import timedelta + +from azurelinuxagent.common.errorstate import * +from tests.tools import * + + +class TestErrorState(unittest.TestCase): + def test_errorstate00(self): + """ + If ErrorState is never incremented, it will never trigger. + """ + test_subject = ErrorState(timedelta(seconds=10000)) + self.assertFalse(test_subject.is_triggered()) + self.assertEqual(0, test_subject.count) + + def test_errorstate01(self): + """ + If ErrorState is never incremented, and the timedelta is zero it will + not trigger. + """ + test_subject = ErrorState(timedelta(seconds=0)) + self.assertFalse(test_subject.is_triggered()) + self.assertEqual(0, test_subject.count) + + def test_errorstate02(self): + """ + If ErrorState is triggered, and the current time is within timedelta + of now it will trigger. + """ + test_subject = ErrorState(timedelta(seconds=0)) + test_subject.incr() + + + self.assertTrue(test_subject.is_triggered()) + self.assertEqual(1, test_subject.count) + + @patch('azurelinuxagent.common.errorstate.datetime') + def test_errorstate03(self, mock_time): + """ + ErrorState will not trigger until + 1. ErrorState has been incr() at least once. + 2. The timedelta from the first incr() has elapsed. + """ + test_subject = ErrorState(timedelta(minutes=15)) + + for x in range(1, 10): + mock_time.utcnow = Mock(return_value=datetime.utcnow() + timedelta(minutes=x)) + + test_subject.incr() + self.assertFalse(test_subject.is_triggered()) + + mock_time.utcnow = Mock(return_value=datetime.utcnow() + timedelta(minutes=30)) + test_subject.incr() + self.assertTrue(test_subject.is_triggered()) + + def test_errorstate04(self): + """ + If ErrorState is reset the timestamp of the last incr() is reset to + None. + """ + + test_subject = ErrorState(timedelta(minutes=15)) + self.assertTrue(test_subject.timestamp is None) + + test_subject.incr() + self.assertTrue(test_subject.timestamp is not None) + + test_subject.reset() + self.assertTrue(test_subject.timestamp is None) diff --git a/tests/common/test_event.py b/tests/common/test_event.py index 55a99c4..01bcd7b 100644 --- a/tests/common/test_event.py +++ b/tests/common/test_event.py @@ -23,7 +23,8 @@ import azurelinuxagent.common.event as event import azurelinuxagent.common.logger as logger from azurelinuxagent.common.event import add_event, \ - mark_event_status, should_emit_event + mark_event_status, should_emit_event, \ + WALAEventOperation from azurelinuxagent.common.future import ustr from azurelinuxagent.common.version import CURRENT_VERSION @@ -48,8 +49,7 @@ class TestEvent(AgentTestCase): self.assertTrue(es.event_succeeded("Foo", "1.2", "FauxOperation")) def test_event_status_records_status(self): - d = tempfile.mkdtemp() - es = event.EventStatus(tempfile.mkdtemp()) + es = event.EventStatus() es.mark_event_status("Foo", "1.2", "FauxOperation", True) self.assertTrue(es.event_succeeded("Foo", "1.2", "FauxOperation")) @@ -69,7 +69,7 @@ class TestEvent(AgentTestCase): self.assertFalse(es.event_succeeded("Foo", "1.2", "FauxOperation")) def test_should_emit_event_ignores_unknown_operations(self): - event.__event_status__ = event.EventStatus(tempfile.mkdtemp()) + event.__event_status__ = event.EventStatus() self.assertTrue(event.should_emit_event("Foo", "1.2", "FauxOperation", True)) self.assertTrue(event.should_emit_event("Foo", "1.2", "FauxOperation", False)) @@ -82,7 +82,7 @@ class TestEvent(AgentTestCase): def test_should_emit_event_handles_known_operations(self): - event.__event_status__ = event.EventStatus(tempfile.mkdtemp()) + event.__event_status__ = event.EventStatus() # Known operations always initially "fire" for op in event.__event_status_operations__: @@ -144,7 +144,7 @@ class TestEvent(AgentTestCase): event.add_periodic(logger.EVERY_DAY, "FauxEvent") self.assertEqual(1, mock_event.call_count) - h = hash("FauxEvent"+""+ustr(True)+"") + h = hash("FauxEvent"+WALAEventOperation.Unknown+ustr(True)) event.__event_logger__.periodic_events[h] = \ datetime.now() - logger.EVERY_DAY - logger.EVERY_HOUR event.add_periodic(logger.EVERY_DAY, "FauxEvent") @@ -158,7 +158,8 @@ class TestEvent(AgentTestCase): mock_event.assert_called_once_with( "FauxEvent", duration=0, evt_type='', is_internal=False, is_success=True, - log_event=True, message='', op='', version=str(CURRENT_VERSION)) + log_event=True, message='', op=WALAEventOperation.Unknown, + version=str(CURRENT_VERSION)) def test_save_event(self): add_event('test', message='test event') diff --git a/tests/common/test_logger.py b/tests/common/test_logger.py index 9e298b3..005c429 100644 --- a/tests/common/test_logger.py +++ b/tests/common/test_logger.py @@ -15,17 +15,20 @@ # Requires Python 2.4+ and Openssl 1.0+ # +import json from datetime import datetime import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.event import add_log_event +from azurelinuxagent.common.version import CURRENT_AGENT, CURRENT_VERSION from tests.tools import * _MSG = "This is our test logging message {0} {1}" _DATA = ["arg1", "arg2"] -class TestLogger(AgentTestCase): +class TestLogger(AgentTestCase): @patch('azurelinuxagent.common.logger.Logger.info') def test_periodic_emits_if_not_previously_sent(self, mock_info): logger.reset_periodic() @@ -64,3 +67,38 @@ class TestLogger(AgentTestCase): logger.periodic(logger.EVERY_DAY, _MSG, *_DATA) mock_info.assert_called_once_with(_MSG, *_DATA) + + def test_telemetry_logger(self): + mock = MagicMock() + appender = logger.TelemetryAppender(logger.LogLevel.WARNING, mock) + appender.write(logger.LogLevel.WARNING, "--unit-test--") + + mock.assert_called_once_with(logger.LogLevel.WARNING, "--unit-test--") + + @patch('azurelinuxagent.common.event.EventLogger.save_event') + def test_telemetry_logger1(self, mock_save): + appender = logger.TelemetryAppender(logger.LogLevel.WARNING, add_log_event) + appender.write(logger.LogLevel.WARNING, "--unit-test--") + + self.assertEqual(1, mock_save.call_count) + telemetry_json = json.loads(mock_save.call_args[0][0]) + + self.assertEqual('FFF0196F-EE4C-4EAF-9AA5-776F622DEB4F', telemetry_json['providerId']) + self.assertEqual(7, telemetry_json['eventId']) + + self.assertEqual(5, len(telemetry_json['parameters'])) + for x in telemetry_json['parameters']: + if x['name'] == 'EventName': + self.assertEqual(x['value'], 'Log') + + elif x['name'] == 'CapabilityUsed': + self.assertEqual(x['value'], 'WARNING') + + elif x['name'] == 'Context1': + self.assertEqual(x['value'], '--unit-test--') + + elif x['name'] == 'Context2': + self.assertEqual(x['value'], '') + + elif x['name'] == 'Context3': + self.assertEqual(x['value'], '') diff --git a/tests/data/ext/sample_ext-1.2.0.zip b/tests/data/ext/sample_ext-1.3.0.zip Binary files differindex 08cfaf7..08cfaf7 100644 --- a/tests/data/ext/sample_ext-1.2.0.zip +++ b/tests/data/ext/sample_ext-1.3.0.zip diff --git a/tests/data/ext/sample_ext-1.2.0/HandlerManifest.json b/tests/data/ext/sample_ext-1.3.0/HandlerManifest.json index 9890d0c..9890d0c 100644 --- a/tests/data/ext/sample_ext-1.2.0/HandlerManifest.json +++ b/tests/data/ext/sample_ext-1.3.0/HandlerManifest.json diff --git a/tests/data/ext/sample_ext-1.2.0/sample.py b/tests/data/ext/sample_ext-1.3.0/sample.py index 74bd839..74bd839 100755 --- a/tests/data/ext/sample_ext-1.2.0/sample.py +++ b/tests/data/ext/sample_ext-1.3.0/sample.py diff --git a/tests/data/ga/WALinuxAgent-2.2.14.zip b/tests/data/ga/WALinuxAgent-2.2.14.zip Binary files differdeleted file mode 100644 index a978207..0000000 --- a/tests/data/ga/WALinuxAgent-2.2.14.zip +++ /dev/null diff --git a/tests/data/ga/WALinuxAgent-2.2.19.zip b/tests/data/ga/WALinuxAgent-2.2.19.zip Binary files differnew file mode 100644 index 0000000..dcb8a5a --- /dev/null +++ b/tests/data/ga/WALinuxAgent-2.2.19.zip diff --git a/tests/data/ga/supported.json b/tests/data/ga/supported.json deleted file mode 100644 index 2ae3753..0000000 --- a/tests/data/ga/supported.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "ubuntu.16.10-x64": { - "versions": [ - "^Ubuntu,16.10,yakkety$" - ], - "slice": 10 - } -} diff --git a/tests/data/safe_deploy.json b/tests/data/safe_deploy.json new file mode 100644 index 0000000..9d0e3eb --- /dev/null +++ b/tests/data/safe_deploy.json @@ -0,0 +1,23 @@ +{ + "blacklisted" : [ + "^1.2.3$", + "^1.3(?:\\.\\d+)*$" + ], + + "families" : { + "ubuntu-x64": { + "versions": [ + "^Ubuntu,(1[4-9]|2[0-9])\\.\\d+,.*$" + ], + "require_64bit": true, + "partition": 85 + }, + "fedora-x64": { + "versions": [ + "^Oracle[^,]*,([7-9]|[1-9][0-9])\\.\\d+,.*$", + "^Red\\sHat[^,]*,([7-9]|[1-9][0-9])\\.\\d+,.*$" + ], + "partition": 20 + } + } +}
\ No newline at end of file diff --git a/tests/data/test_waagent.conf b/tests/data/test_waagent.conf index edc3676..be0596a 100644 --- a/tests/data/test_waagent.conf +++ b/tests/data/test_waagent.conf @@ -6,6 +6,7 @@ =Value0 FauxKey1= Value1 FauxKey2=Value2 Value2 +FauxKey3=delalloc,rw,noatime,nobarrier,users,mode=777 # Enable instance creation Provisioning.Enabled=y @@ -19,14 +20,15 @@ Provisioning.DeleteRootPassword=y # Generate fresh host key pair. Provisioning.RegenerateSshHostKeyPair=y -# Supported values are "rsa", "dsa" and "ecdsa". -Provisioning.SshHostKeyPairType=rsa +# Supported values are "rsa", "dsa", "ecdsa", "ed25519", and "auto". +# The "auto" option is supported on OpenSSH 5.9 (2011) and later. +Provisioning.SshHostKeyPairType=rsa # An EOL comment that should be ignored # Monitor host name changes and publish changes via DHCP requests. Provisioning.MonitorHostName=y # Decode CustomData from Base64. -Provisioning.DecodeCustomData=n +Provisioning.DecodeCustomData=n#Another EOL comment that should be ignored # Execute CustomData after provisioning. Provisioning.ExecuteCustomData=n @@ -63,7 +65,7 @@ ResourceDisk.MountOptions=None Logs.Verbose=n # Is FIPS enabled -OS.EnableFIPS=y +OS.EnableFIPS=y#Another EOL comment that should be ignored # Root device timeout in seconds. OS.RootDeviceScsiTimeout=300 @@ -71,6 +73,9 @@ OS.RootDeviceScsiTimeout=300 # If "None", the system default version is used. OS.OpensslPath=None +# Set the SSH ClientAliveInterval +OS.SshClientAliveInterval=42#Yet another EOL comment with a '#' that should be ignored + # Set the path to SSH keys and configuration files OS.SshDir=/notareal/path @@ -119,5 +124,5 @@ OS.SshDir=/notareal/path # Add firewall rules to protect access to Azure host node services # Note: -# - The default is false to protect the state of exising VMs -OS.EnableFirewall=y +# - The default is false to protect the state of existing VMs +OS.EnableFirewall=n diff --git a/tests/data/wire/ext_conf_autoupgrade_internalversion.xml b/tests/data/wire/ext_conf_autoupgrade_internalversion.xml index 1e613ea..9b61556 100644 --- a/tests/data/wire/ext_conf_autoupgrade_internalversion.xml +++ b/tests/data/wire/ext_conf_autoupgrade_internalversion.xml @@ -17,10 +17,10 @@ </GAFamilies> </GuestAgentExtension> <Plugins> - <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.2.0" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" /> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.3.0" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" /> </Plugins> <PluginSettings> - <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.2.0"> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.3.0"> <RuntimeSettings seqNo="0">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK","publicSettings":{"foo":"bar"}}}]}</RuntimeSettings> </Plugin> </PluginSettings> diff --git a/tests/data/wire/ext_conf_internalversion.xml b/tests/data/wire/ext_conf_internalversion.xml index 1e613ea..9b61556 100644 --- a/tests/data/wire/ext_conf_internalversion.xml +++ b/tests/data/wire/ext_conf_internalversion.xml @@ -17,10 +17,10 @@ </GAFamilies> </GuestAgentExtension> <Plugins> - <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.2.0" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" /> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.3.0" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" /> </Plugins> <PluginSettings> - <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.2.0"> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.3.0"> <RuntimeSettings seqNo="0">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK","publicSettings":{"foo":"bar"}}}]}</RuntimeSettings> </Plugin> </PluginSettings> diff --git a/tests/data/wire/ext_conf_upgradeguid.xml b/tests/data/wire/ext_conf_upgradeguid.xml new file mode 100644 index 0000000..1526a93 --- /dev/null +++ b/tests/data/wire/ext_conf_upgradeguid.xml @@ -0,0 +1,26 @@ +<Extensions version="1.0.0.0" goalStateIncarnation="9"><GuestAgentExtension xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> + <GAFamilies> + <GAFamily> + <Name>Prod</Name> + <Uris> + <Uri>http://manifest_of_ga.xml</Uri> + </Uris> + </GAFamily> + <GAFamily> + <Name>Test</Name> + <Uris> + <Uri>http://manifest_of_ga.xml</Uri> + </Uris> + </GAFamily> + </GAFamilies> +</GuestAgentExtension> +<Plugins> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.0.0" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="false" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" upgradeGuid="12345678-09AB-ABCD-CDEF-FE0987654321" /> +</Plugins> +<PluginSettings> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.0.0"> + <RuntimeSettings seqNo="0">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK","publicSettings":{"foo":"bar"}}}]}</RuntimeSettings> + </Plugin> +</PluginSettings> +<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/ga_manifest.xml b/tests/data/wire/ga_manifest.xml index f43daf5..fb7238a 100644 --- a/tests/data/wire/ga_manifest.xml +++ b/tests/data/wire/ga_manifest.xml @@ -14,6 +14,18 @@ </Uris> </Plugin> <Plugin> + <Version>1.1.1</Version> + <Uris> + <Uri>http://foo.bar/zar/OSTCExtensions.WALinuxAgent__1.1.1</Uri> + </Uris> + </Plugin> + <Plugin> + <Version>1.2.0</Version> + <Uris> + <Uri>http://foo.bar/zar/OSTCExtensions.WALinuxAgent__1.2.0</Uri> + </Uris> + </Plugin> + <Plugin> <Version>2.0.0</Version><Uris><Uri>http://host/OSTCExtensions.WALinuxAgent__2.0.0</Uri></Uris> </Plugin> <Plugin> diff --git a/tests/data/wire/ga_manifest_1.xml b/tests/data/wire/ga_manifest_1.xml new file mode 100644 index 0000000..f2b1839 --- /dev/null +++ b/tests/data/wire/ga_manifest_1.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<PluginVersionManifest xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> + <Plugins/> + <InternalPlugins> + <Plugin> + <Version>2.2.13</Version> + <Uris> + <Uri>url1_13</Uri> + </Uris> + </Plugin> + <Plugin> + <Version>2.2.14</Version> + <Uris> + <Uri>url1_14</Uri> + </Uris> + </Plugin> + </InternalPlugins> +</PluginVersionManifest> diff --git a/tests/data/wire/ga_manifest_2.xml b/tests/data/wire/ga_manifest_2.xml new file mode 100644 index 0000000..7636f8c --- /dev/null +++ b/tests/data/wire/ga_manifest_2.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<PluginVersionManifest xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> + <Plugins/> + <InternalPlugins> + <Plugin> + <Version>2.2.13</Version> + <Uris> + <Uri>url2_13</Uri> + </Uris> + </Plugin> + <Plugin> + <Version>2.2.14</Version> + <Uris> + <Uri>url2_14</Uri> + </Uris> + </Plugin> + <Plugin> + <Version>2.2.15</Version> + <Uris> + <Uri>url1_15</Uri> + </Uris> + </Plugin> + </InternalPlugins> +</PluginVersionManifest> diff --git a/tests/data/wire/manifest.xml b/tests/data/wire/manifest.xml index ff42b9d..eef8ade 100644 --- a/tests/data/wire/manifest.xml +++ b/tests/data/wire/manifest.xml @@ -14,6 +14,18 @@ </Uris> </Plugin> <Plugin> + <Version>1.1.1</Version> + <Uris> + <Uri>http://foo.bar/zar/OSTCExtensions.ExampleHandlerLinux__1.1.1</Uri> + </Uris> + </Plugin> + <Plugin> + <Version>1.2.0</Version> + <Uris> + <Uri>http://foo.bar/zar/OSTCExtensions.ExampleHandlerLinux__1.2.0</Uri> + </Uris> + </Plugin> + <Plugin> <Version>2.0.0</Version><Uris><Uri>http://host/OSTCExtensions.ExampleHandlerLinux__2.0.0</Uri></Uris> </Plugin> <Plugin> @@ -44,9 +56,9 @@ </Plugins> <InternalPlugins> <Plugin> - <Version>1.2.0</Version> + <Version>1.3.0</Version> <Uris> - <Uri>http://foo.bar/zar/OSTCExtensions.ExampleHandlerLinux__1.2.0</Uri> + <Uri>http://foo.bar/zar/OSTCExtensions.ExampleHandlerLinux__1.3.0</Uri> </Uris> </Plugin> <Plugin> diff --git a/tests/ga/test_env.py b/tests/ga/test_env.py new file mode 100644 index 0000000..06e9a64 --- /dev/null +++ b/tests/ga/test_env.py @@ -0,0 +1,86 @@ +# 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+ +# +import glob +import tempfile + +import os +from mock import patch + +from azurelinuxagent.common.utils import fileutil +from azurelinuxagent.ga.env import MAXIMUM_CACHED_FILES, EnvHandler +from tests.tools import AgentTestCase + + +class TestEnv(AgentTestCase): + @patch("azurelinuxagent.common.conf.get_lib_dir") + def test_purge_disk_cache(self, mock_conf, *args): + names = [ + ("Prod", "agentsManifest"), + ("Test", "agentsManifest"), + ("FauxExtension1", "manifest.xml"), + ("FauxExtension2", "manifest.xml"), + ("GoalState", "xml"), + ("ExtensionsConfig", "xml") + ] + + env = EnvHandler() + + tmp_dir = tempfile.mkdtemp() + mock_conf.return_value = tmp_dir + + # write incarnations 1-100 + for t in names: + self._create_files(tmp_dir, + t[0], + t[1], + 2 * MAXIMUM_CACHED_FILES, + with_sleep=0.001) + + # update incarnation 1 with the latest timestamp + for t in names: + f = os.path.join(tmp_dir, '.'.join((t[0], '1', t[1]))) + fileutil.write_file(f, "faux content") + + # ensure the expected number of files are created + for t in names: + p = os.path.join(tmp_dir, '{0}.*.{1}'.format(*t)) + self.assertEqual(2 * MAXIMUM_CACHED_FILES, len(glob.glob(p))) + + env.purge_disk_cache() + + # ensure the expected number of files remain + for t in names: + p = os.path.join(tmp_dir, '{0}.*.{1}'.format(*t)) + incarnation1 = os.path.join(tmp_dir, '{0}.1.{1}'.format(t[0], t[1])) + incarnation2 = os.path.join(tmp_dir, '{0}.2.{1}'.format(t[0], t[1])) + self.assertEqual(MAXIMUM_CACHED_FILES, len(glob.glob(p))) + self.assertTrue(os.path.exists(incarnation1)) + self.assertFalse(os.path.exists(incarnation2)) + + # write incarnation 101 + for t in names: + f = os.path.join(tmp_dir, '.'.join((t[0], '101', t[1]))) + fileutil.write_file(f, "faux content") + + # call to purge should be ignored, since interval has not elapsed + env.purge_disk_cache() + + for t in names: + p = os.path.join(tmp_dir, '{0}.*.{1}'.format(*t)) + incarnation1 = os.path.join(tmp_dir, '{0}.1.{1}'.format(t[0], t[1])) + self.assertEqual(MAXIMUM_CACHED_FILES + 1, len(glob.glob(p))) + self.assertTrue(os.path.exists(incarnation1)) diff --git a/tests/ga/test_extension.py b/tests/ga/test_extension.py index 2a60ea3..9a72989 100644 --- a/tests/ga/test_extension.py +++ b/tests/ga/test_extension.py @@ -46,8 +46,8 @@ class TestExtensionCleanup(AgentTestCase): def _install_handlers(self, start=0, count=1, handler_state=ExtHandlerState.Installed): - src = os.path.join(data_dir, "ext", "sample_ext-1.2.0.zip") - version = FlexibleVersion("1.2.0") + src = os.path.join(data_dir, "ext", "sample_ext-1.3.0.zip") + version = FlexibleVersion("1.3.0") version += start - version.patch for i in range(start, start+count): @@ -318,34 +318,44 @@ class TestExtension(AgentTestCase): self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") self._assert_ext_status(protocol.report_ext_status, "success", 1) - #Test upgrade + #Test hotfix test_data.goal_state = test_data.goal_state.replace("<Incarnation>2<", "<Incarnation>3<") test_data.ext_conf = test_data.ext_conf.replace("1.0.0", "1.1.0") test_data.ext_conf = test_data.ext_conf.replace("seqNo=\"1\"", "seqNo=\"2\"") exthandlers_handler.run() - self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.1.0") + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.1.1") self._assert_ext_status(protocol.report_ext_status, "success", 2) - #Test disable + #Test upgrade test_data.goal_state = test_data.goal_state.replace("<Incarnation>3<", "<Incarnation>4<") + test_data.ext_conf = test_data.ext_conf.replace("1.1.0", "1.2.0") + test_data.ext_conf = test_data.ext_conf.replace("seqNo=\"2\"", + "seqNo=\"3\"") + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.2.0") + self._assert_ext_status(protocol.report_ext_status, "success", 3) + + #Test disable + test_data.goal_state = test_data.goal_state.replace("<Incarnation>4<", + "<Incarnation>5<") test_data.ext_conf = test_data.ext_conf.replace("enabled", "disabled") exthandlers_handler.run() self._assert_handler_status(protocol.report_vm_status, "NotReady", - 1, "1.1.0") + 1, "1.2.0") #Test uninstall - test_data.goal_state = test_data.goal_state.replace("<Incarnation>4<", - "<Incarnation>5<") + test_data.goal_state = test_data.goal_state.replace("<Incarnation>5<", + "<Incarnation>6<") test_data.ext_conf = test_data.ext_conf.replace("disabled", "uninstall") exthandlers_handler.run() self._assert_no_handler_status(protocol.report_vm_status) #Test uninstall again! - test_data.goal_state = test_data.goal_state.replace("<Incarnation>5<", - "<Incarnation>6<") + test_data.goal_state = test_data.goal_state.replace("<Incarnation>6<", + "<Incarnation>7<") exthandlers_handler.run() self._assert_no_handler_status(protocol.report_vm_status) @@ -371,6 +381,95 @@ class TestExtension(AgentTestCase): exthandlers_handler.run() self._assert_no_handler_status(protocol.report_vm_status) + def test_ext_handler_rollingupgrade(self, *args): + test_data = WireProtocolData(DATA_FILE_EXT_ROLLINGUPGRADE) + exthandlers_handler, protocol = self._create_mock(test_data, *args) + + #Test enable scenario. + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") + self._assert_ext_status(protocol.report_ext_status, "success", 0) + + #Test goal state not changed + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") + + #Test goal state changed without new GUID + test_data.goal_state = test_data.goal_state.replace("<Incarnation>1<", + "<Incarnation>2<") + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") + self._assert_ext_status(protocol.report_ext_status, "success", 0) + + #Test GUID change without new version available + test_data.goal_state = test_data.goal_state.replace("<Incarnation>2<", + "<Incarnation>3<") + test_data.ext_conf = test_data.ext_conf.replace("FE0987654321", "FE0987654322") + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") + self._assert_ext_status(protocol.report_ext_status, "success", 0) + + #Test hotfix available without GUID change + test_data.goal_state = test_data.goal_state.replace("<Incarnation>3<", + "<Incarnation>4<") + test_data.ext_conf = test_data.ext_conf.replace("1.0.0", "1.1.0") + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.0.0") + self._assert_ext_status(protocol.report_ext_status, "success", 0) + + #Test GUID change with hotfix + test_data.goal_state = test_data.goal_state.replace("<Incarnation>4<", + "<Incarnation>5<") + test_data.ext_conf = test_data.ext_conf.replace("FE0987654322", "FE0987654323") + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.1.1") + self._assert_ext_status(protocol.report_ext_status, "success", 0) + + #Test disable + test_data.goal_state = test_data.goal_state.replace("<Incarnation>5<", + "<Incarnation>6<") + test_data.ext_conf = test_data.ext_conf.replace("enabled", "disabled") + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "NotReady", + 1, "1.1.1") + + #Test uninstall + test_data.goal_state = test_data.goal_state.replace("<Incarnation>6<", + "<Incarnation>7<") + test_data.ext_conf = test_data.ext_conf.replace("disabled", "uninstall") + exthandlers_handler.run() + self._assert_no_handler_status(protocol.report_vm_status) + + #Test uninstall again! + test_data.goal_state = test_data.goal_state.replace("<Incarnation>7<", + "<Incarnation>8<") + exthandlers_handler.run() + self._assert_no_handler_status(protocol.report_vm_status) + + #Test re-install + test_data.goal_state = test_data.goal_state.replace("<Incarnation>8<", + "<Incarnation>9<") + test_data.ext_conf = test_data.ext_conf.replace("uninstall", "enabled") + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.1.1") + self._assert_ext_status(protocol.report_ext_status, "success", 0) + + #Test upgrade available without GUID change + test_data.goal_state = test_data.goal_state.replace("<Incarnation>9<", + "<Incarnation>10<") + test_data.ext_conf = test_data.ext_conf.replace("1.1.0", "1.2.0") + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.1.1") + self._assert_ext_status(protocol.report_ext_status, "success", 0) + + #Test GUID change with upgrade available + test_data.goal_state = test_data.goal_state.replace("<Incarnation>10<", + "<Incarnation>11<") + test_data.ext_conf = test_data.ext_conf.replace("FE0987654323", "FE0987654324") + exthandlers_handler.run() + self._assert_handler_status(protocol.report_vm_status, "Ready", 1, "1.2.0") + self._assert_ext_status(protocol.report_ext_status, "success", 0) + @patch('azurelinuxagent.ga.exthandlers.add_event') def test_ext_handler_download_failure(self, mock_add_event, *args): test_data = WireProtocolData(DATA_FILE) @@ -464,8 +563,8 @@ class TestExtension(AgentTestCase): for internal in [False, True]: for autoupgrade in [False, True]: if internal: - config_version = '1.2.0' - decision_version = '1.2.0' + config_version = '1.3.0' + decision_version = '1.3.0' if autoupgrade: datafile = DATA_FILE_EXT_AUTOUPGRADE_INTERNALVERSION else: @@ -474,18 +573,18 @@ class TestExtension(AgentTestCase): config_version = '1.0.0' if autoupgrade: datafile = DATA_FILE_EXT_AUTOUPGRADE - decision_version = '1.1.0' + decision_version = '1.2.0' else: datafile = DATA_FILE decision_version = '1.0.0' _, protocol = self._create_mock(WireProtocolData(datafile), *args) - ext_handlers, _ = protocol.get_ext_handlers() + ext_handlers, etag = protocol.get_ext_handlers() self.assertEqual(1, len(ext_handlers.extHandlers)) ext_handler = ext_handlers.extHandlers[0] self.assertEqual('OSTCExtensions.ExampleHandlerLinux', ext_handler.name) self.assertEqual(config_version, ext_handler.properties.version, "config version.") - ExtHandlerInstance(ext_handler, protocol).decide_version() + ExtHandlerInstance(ext_handler, protocol).decide_version(etag) self.assertEqual(decision_version, ext_handler.properties.version, "decision version.") def test_ext_handler_version_decide_between_minor_versions(self, *args): @@ -500,7 +599,7 @@ class TestExtension(AgentTestCase): cases = [ (None, '2.0', '2.0.0', '2.2.0'), (None, '2.0.0', '2.0.0', '2.2.0'), - ('1.0', '1.0.0', '1.0.0', '1.1.0'), + ('1.0', '1.0.0', '1.0.0', '1.2.0'), (None, '2.1.0', '2.1.1', '2.2.0'), (None, '2.2.0', '2.2.0', '2.2.0'), (None, '2.3.0', '2.3.0', '2.3.0'), @@ -512,6 +611,7 @@ class TestExtension(AgentTestCase): _, protocol = self._create_mock(WireProtocolData(DATA_FILE), *args) version_uri = Mock() version_uri.uri = 'http://some/Microsoft.OSTCExtensions_ExampleHandlerLinux_asiaeast_manifest.xml' + incarnation = 1 for (installed_version, config_version, expected_version, autoupgrade_expected_version) in cases: ext_handler = Mock() @@ -523,8 +623,9 @@ class TestExtension(AgentTestCase): ext_handler_instance = ExtHandlerInstance(ext_handler, protocol) ext_handler_instance.get_installed_version = Mock(return_value=installed_version) - ext_handler_instance.decide_version() + ext_handler_instance.decide_version(incarnation) self.assertEqual(expected_version, ext_handler.properties.version) + incarnation += 1 ext_handler.properties.version = config_version ext_handler.properties.upgradePolicy = 'auto' @@ -532,8 +633,9 @@ class TestExtension(AgentTestCase): ext_handler_instance = ExtHandlerInstance(ext_handler, protocol) ext_handler_instance.get_installed_version = Mock(return_value=installed_version) - ext_handler_instance.decide_version() + ext_handler_instance.decide_version(incarnation) self.assertEqual(autoupgrade_expected_version, ext_handler.properties.version) + incarnation += 1 if __name__ == '__main__': diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py index 59251cb..21c81e9 100644 --- a/tests/ga/test_update.py +++ b/tests/ga/test_update.py @@ -17,16 +17,12 @@ from __future__ import print_function -from datetime import datetime - -import json -import shutil - from azurelinuxagent.common.event import * from azurelinuxagent.common.protocol.hostplugin import * from azurelinuxagent.common.protocol.metadata import * from azurelinuxagent.common.protocol.wire import * from azurelinuxagent.common.utils.fileutil import * +from azurelinuxagent.common.version import AGENT_PKG_GLOB, AGENT_DIR_GLOB from azurelinuxagent.ga.update import * from tests.tools import * @@ -43,12 +39,6 @@ FATAL_ERROR = { "was_fatal" : True } -SENTINEL_ERROR = { - "last_failure" : 0.0, - "failure_count" : 0, - "was_fatal" : True -} - WITH_ERROR = { "last_failure" : 42.42, "failure_count" : 2, @@ -107,9 +97,6 @@ def faux_logger(): class UpdateTestCase(AgentTestCase): - def setUp(self): - AgentTestCase.setUp(self) - return def agent_bin(self, version, suffix): return "bin/{0}-{1}{2}.egg".format(AGENT_NAME, version, suffix) @@ -118,6 +105,9 @@ class UpdateTestCase(AgentTestCase): 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 agents(self): + return [GuestAgent(path=path) for path in self.agent_dirs()] def agent_count(self): return len(self.agent_dirs()) @@ -160,25 +150,17 @@ class UpdateTestCase(AgentTestCase): fileutil.copy_file(agent, to_dir=self.tmp_dir) return - def expand_agents(self, mark_optional=False): + def expand_agents(self): for agent in self.agent_pkgs(): path = os.path.join(self.tmp_dir, fileutil.trim_ext(agent, "zip")) zipfile.ZipFile(agent).extractall(path) - if mark_optional: - src = os.path.join(data_dir, 'ga', 'supported.json') - dst = os.path.join(path, 'supported.json') - shutil.copy(src, dst) - - dst = os.path.join(path, 'error.json') - fileutil.write_file(dst, json.dumps(SENTINEL_ERROR)) - return - def prepare_agent(self, version, mark_optional=False): + 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(mark_optional=mark_optional) + self.expand_agents() versions = self.agent_versions() src_v = FlexibleVersion(str(versions[0])) @@ -243,64 +225,6 @@ class UpdateTestCase(AgentTestCase): return dst_v -class TestSupportedDistribution(UpdateTestCase): - def setUp(self): - UpdateTestCase.setUp(self) - self.sd = SupportedDistribution({ - 'slice':10, - 'versions': ['^Ubuntu,16.10,yakkety$']}) - - def test_creation(self): - self.assertRaises(TypeError, SupportedDistribution) - self.assertRaises(UpdateError, SupportedDistribution, None) - - self.assertEqual(self.sd.slice, 10) - self.assertEqual(self.sd.versions, ['^Ubuntu,16.10,yakkety$']) - - @patch('platform.linux_distribution') - def test_is_supported(self, mock_dist): - mock_dist.return_value = ['Ubuntu', '16.10', 'yakkety'] - self.assertTrue(self.sd.is_supported) - - mock_dist.return_value = ['something', 'else', 'entirely'] - self.assertFalse(self.sd.is_supported) - - @patch('azurelinuxagent.ga.update.datetime') - def test_in_slice(self, mock_dt): - mock_dt.utcnow = Mock(return_value=datetime(2017, 1, 1, 0, 0, 5)) - self.assertTrue(self.sd.in_slice) - - mock_dt.utcnow = Mock(return_value=datetime(2017, 1, 1, 0, 0, 42)) - self.assertFalse(self.sd.in_slice) - - -class TestSupported(UpdateTestCase): - def setUp(self): - UpdateTestCase.setUp(self) - self.sp = Supported(os.path.join(data_dir, 'ga', 'supported.json')) - self.sp.load() - - def test_creation(self): - self.assertRaises(TypeError, Supported) - self.assertRaises(UpdateError, Supported, None) - - @patch('platform.linux_distribution') - def test_is_supported(self, mock_dist): - mock_dist.return_value = ['Ubuntu', '16.10', 'yakkety'] - self.assertTrue(self.sp.is_supported) - - mock_dist.return_value = ['something', 'else', 'entirely'] - self.assertFalse(self.sp.is_supported) - - @patch('platform.linux_distribution', return_value=['Ubuntu', '16.10', 'yakkety']) - @patch('azurelinuxagent.ga.update.datetime') - def test_in_slice(self, mock_dt, mock_dist): - mock_dt.utcnow = Mock(return_value=datetime(2017, 1, 1, 0, 0, 5)) - self.assertTrue(self.sp.in_slice) - - mock_dt.utcnow = Mock(return_value=datetime(2017, 1, 1, 0, 0, 42)) - self.assertFalse(self.sp.in_slice) - class TestGuestAgentError(UpdateTestCase): def test_creation(self): self.assertRaises(TypeError, GuestAgentError) @@ -330,19 +254,6 @@ class TestGuestAgentError(UpdateTestCase): self.assertEqual(NO_ERROR["was_fatal"], err.was_fatal) return - def test_is_sentinel(self): - with self.get_error_file(error_data=SENTINEL_ERROR) as path: - err = GuestAgentError(path.name) - err.load() - self.assertTrue(err.is_blacklisted) - self.assertTrue(err.is_sentinel) - - with self.get_error_file(error_data=FATAL_ERROR) as path: - err = GuestAgentError(path.name) - err.load() - self.assertTrue(err.is_blacklisted) - self.assertFalse(err.is_sentinel) - def test_save(self): err1 = self.create_error() err1.mark_failure() @@ -398,7 +309,6 @@ class TestGuestAgent(UpdateTestCase): UpdateTestCase.setUp(self) self.copy_agents(get_agent_file_path()) self.agent_path = os.path.join(self.tmp_dir, get_agent_name()) - return def test_creation(self): self.assertRaises(UpdateError, GuestAgent, "A very bad file name") @@ -412,9 +322,6 @@ class TestGuestAgent(UpdateTestCase): self.assertEqual(get_agent_name(), agent.name) self.assertEqual(get_agent_version(), agent.version) - self.assertFalse(agent._is_optional) - self.assertFalse(agent._in_slice) - self.assertEqual(self.agent_path, agent.get_agent_dir()) path = os.path.join(self.agent_path, AGENT_MANIFEST_FILE) @@ -430,7 +337,6 @@ class TestGuestAgent(UpdateTestCase): self.assertTrue(agent.is_downloaded) self.assertFalse(agent.is_blacklisted) self.assertTrue(agent.is_available) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") def test_clear_error(self, mock_downloaded): @@ -449,7 +355,6 @@ class TestGuestAgent(UpdateTestCase): self.assertEqual(0, agent.error.failure_count) self.assertFalse(agent.is_blacklisted) self.assertEqual(agent.is_blacklisted, agent.error.is_blacklisted) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -462,7 +367,6 @@ class TestGuestAgent(UpdateTestCase): agent.mark_failure(is_fatal=True) self.assertFalse(agent.is_available) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -477,7 +381,30 @@ class TestGuestAgent(UpdateTestCase): agent.mark_failure(is_fatal=True) self.assertTrue(agent.is_blacklisted) self.assertEqual(agent.is_blacklisted, agent.error.is_blacklisted) - return + + @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") + @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") + def test_resource_gone_error_not_blacklisted(self, mock_loaded, mock_downloaded): + try: + mock_downloaded.side_effect = ResourceGoneError() + agent = GuestAgent(path=self.agent_path) + self.assertFalse(agent.is_blacklisted) + except ResourceGoneError: + pass + except: + self.fail("Exception was not expected!") + + @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") + @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") + def test_ioerror_not_blacklisted(self, mock_loaded, mock_downloaded): + try: + mock_downloaded.side_effect = IOError() + agent = GuestAgent(path=self.agent_path) + self.assertFalse(agent.is_blacklisted) + except IOError: + pass + except: + self.fail("Exception was not expected!") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -486,44 +413,6 @@ class TestGuestAgent(UpdateTestCase): self.assertFalse(agent.is_downloaded) agent._unpack() self.assertTrue(agent.is_downloaded) - return - - @patch('platform.linux_distribution', return_value=['Ubuntu', '16.10', 'yakkety']) - @patch('azurelinuxagent.ga.update.GuestAgent._enable') - def test_is_optional(self, mock_enable, mock_dist): - self.expand_agents(mark_optional=True) - agent = GuestAgent(path=self.agent_path) - - self.assertTrue(agent.is_blacklisted) - self.assertTrue(agent._is_optional) - - @patch('platform.linux_distribution', return_value=['Ubuntu', '16.10', 'yakkety']) - @patch('azurelinuxagent.ga.update.datetime') - def test_in_slice(self, mock_dt, mock_dist): - self.expand_agents(mark_optional=True) - agent = GuestAgent(path=self.agent_path) - - mock_dt.utcnow = Mock(return_value=datetime(2017, 1, 1, 0, 0, 5)) - self.assertTrue(agent._in_slice) - - mock_dt.utcnow = Mock(return_value=datetime(2017, 1, 1, 0, 0, 42)) - self.assertFalse(agent._in_slice) - - @patch('platform.linux_distribution', return_value=['Ubuntu', '16.10', 'yakkety']) - @patch('azurelinuxagent.ga.update.datetime') - def test_enable(self, mock_dt, mock_dist): - mock_dt.utcnow = Mock(return_value=datetime(2017, 1, 1, 0, 0, 5)) - - self.expand_agents(mark_optional=True) - agent = GuestAgent(path=self.agent_path) - - self.assertFalse(agent.is_blacklisted) - self.assertFalse(agent._is_optional) - - # Ensure the new state is preserved to disk - agent = GuestAgent(path=self.agent_path) - self.assertFalse(agent.is_blacklisted) - self.assertFalse(agent._is_optional) @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -536,7 +425,6 @@ class TestGuestAgent(UpdateTestCase): agent.mark_failure(is_fatal=True) self.assertEqual(2, agent.error.failure_count) self.assertTrue(agent.is_blacklisted) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -546,7 +434,6 @@ class TestGuestAgent(UpdateTestCase): agent._unpack() self.assertTrue(os.path.isdir(agent.get_agent_dir())) self.assertTrue(os.path.isfile(agent.get_agent_manifest_path())) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -555,7 +442,6 @@ class TestGuestAgent(UpdateTestCase): self.assertFalse(os.path.isdir(agent.get_agent_dir())) os.remove(agent.get_agent_pkg_path()) self.assertRaises(UpdateError, agent._unpack) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -565,7 +451,6 @@ class TestGuestAgent(UpdateTestCase): agent._load_manifest() self.assertEqual(agent.manifest.get_enable_command(), agent.get_agent_cmd()) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -575,7 +460,6 @@ class TestGuestAgent(UpdateTestCase): agent._unpack() os.remove(agent.get_agent_manifest_path()) self.assertRaises(UpdateError, agent._load_manifest) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -588,7 +472,6 @@ class TestGuestAgent(UpdateTestCase): with open(agent.get_agent_manifest_path(), "w") as file: json.dump(EMPTY_MANIFEST, file) self.assertRaises(UpdateError, agent._load_manifest) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -601,7 +484,6 @@ class TestGuestAgent(UpdateTestCase): with open(agent.get_agent_manifest_path(), "w") as file: file.write("This is not JSON data") self.assertRaises(UpdateError, agent._load_manifest) - return def test_load_error(self): agent = GuestAgent(path=self.agent_path) @@ -609,7 +491,6 @@ class TestGuestAgent(UpdateTestCase): agent._load_error() self.assertTrue(agent.error is not None) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -627,7 +508,6 @@ class TestGuestAgent(UpdateTestCase): agent._download() self.assertTrue(os.path.isfile(agent.get_agent_pkg_path())) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -645,7 +525,6 @@ class TestGuestAgent(UpdateTestCase): self.assertRaises(UpdateError, agent._download) self.assertFalse(os.path.isfile(agent.get_agent_pkg_path())) self.assertFalse(agent.is_downloaded) - return @patch("azurelinuxagent.ga.update.GuestAgent._ensure_downloaded") @patch("azurelinuxagent.ga.update.GuestAgent._ensure_loaded") @@ -721,7 +600,6 @@ class TestGuestAgent(UpdateTestCase): self.assertTrue(os.path.isfile(agent.get_agent_manifest_path())) self.assertTrue(agent.is_downloaded) - return @patch("azurelinuxagent.ga.update.GuestAgent._download", side_effect=UpdateError) def test_ensure_downloaded_download_fails(self, mock_download): @@ -735,7 +613,6 @@ class TestGuestAgent(UpdateTestCase): self.assertEqual(1, agent.error.failure_count) self.assertFalse(agent.error.was_fatal) self.assertFalse(agent.is_blacklisted) - return @patch("azurelinuxagent.ga.update.GuestAgent._download") @patch("azurelinuxagent.ga.update.GuestAgent._unpack", side_effect=UpdateError) @@ -749,7 +626,6 @@ class TestGuestAgent(UpdateTestCase): self.assertEqual(1, agent.error.failure_count) self.assertTrue(agent.error.was_fatal) self.assertTrue(agent.is_blacklisted) - return @patch("azurelinuxagent.ga.update.GuestAgent._download") @patch("azurelinuxagent.ga.update.GuestAgent._unpack") @@ -764,7 +640,6 @@ class TestGuestAgent(UpdateTestCase): self.assertEqual(1, agent.error.failure_count) self.assertTrue(agent.error.was_fatal) self.assertTrue(agent.is_blacklisted) - return @patch("azurelinuxagent.ga.update.GuestAgent._download") @patch("azurelinuxagent.ga.update.GuestAgent._unpack") @@ -786,7 +661,6 @@ class TestGuestAgent(UpdateTestCase): self.assertTrue(agent.is_blacklisted) self.assertEqual(0, mock_download.call_count) self.assertEqual(0, mock_unpack.call_count) - return class TestUpdate(UpdateTestCase): @@ -794,7 +668,7 @@ class TestUpdate(UpdateTestCase): UpdateTestCase.setUp(self) self.event_patch = patch('azurelinuxagent.common.event.add_event') self.update_handler = get_update_handler() - return + self.update_handler.protocol_util = Mock() def test_creation(self): self.assertTrue(self.update_handler.running) @@ -809,13 +683,11 @@ class TestUpdate(UpdateTestCase): self.assertEqual(None, self.update_handler.child_process) self.assertEqual(None, self.update_handler.signal_handler) - return 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: @@ -826,7 +698,6 @@ class TestUpdate(UpdateTestCase): except Exception as e: pass self.event_patch.stop() - return def _create_protocol(self, count=5, versions=None): latest_version = self.prepare_agents(count=count) @@ -834,110 +705,6 @@ class TestUpdate(UpdateTestCase): versions = [latest_version] return ProtocolMock(versions=versions) - def _test_upgrade_available( - self, - base_version=FlexibleVersion(AGENT_VERSION), - protocol=None, - versions=None, - count=5): - - if protocol is None: - protocol = self._create_protocol(count=count, versions=versions) - - self.update_handler.protocol_util = protocol - conf.get_autoupdate_gafamily = Mock(return_value=protocol.family) - - 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_upgrade_available_will_refresh_goal_state(self): - protocol = self._create_protocol() - protocol.emulate_stale_goal_state() - self.assertTrue(self._test_upgrade_available(protocol=protocol)) - self.assertEqual(2, protocol.call_counts["get_vmagent_manifests"]) - self.assertEqual(1, protocol.call_counts["get_vmagent_pkgs"]) - self.assertEqual(1, protocol.call_counts["update_goal_state"]) - self.assertTrue(protocol.goal_state_forced) - 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_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_upgrade_available_includes_old_agents(self): - self.prepare_agents() - - 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_upgrade_available(versions=self.agent_versions())) - self.assertEqual(all_count, len(self.update_handler.agents)) - return - - 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_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_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_upgrade_available()) - return - - 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_upgrade_available(base_version=base_version)) - return - - def test_upgrade_available_skips_when_no_versions(self): - self.assertFalse(self._test_upgrade_available(protocol=ProtocolMock())) - return - - def test_upgrade_available_skips_when_updates_are_disabled(self): - conf.get_autoupdate_enabled = Mock(return_value=False) - self.assertFalse(self._test_upgrade_available()) - return - - def test_upgrade_available_sorts(self): - self.prepare_agents() - self._test_upgrade_available() - - v = FlexibleVersion("100000") - for a in self.update_handler.agents: - self.assertTrue(v > a.version) - v = a.version - return - def _test_ensure_no_orphans(self, invocations=3, interval=ORPHAN_WAIT_INTERVAL, pid_count=0): with patch.object(self.update_handler, 'osutil') as mock_util: # Note: @@ -962,27 +729,23 @@ class TestUpdate(UpdateTestCase): for pid_file in pid_files: self.assertFalse(os.path.exists(pid_file)) 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, pid_count=1) 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)) @@ -994,7 +757,64 @@ class TestUpdate(UpdateTestCase): self.assertEqual(3, calls) self.assertEqual(2, sleeps) self.assertEqual(1, mock_kill.call_count) - return + + @patch('azurelinuxagent.ga.update.datetime') + def test_ensure_partition_assigned(self, mock_time): + path = os.path.join(conf.get_lib_dir(), AGENT_PARTITION_FILE) + mock_time.utcnow = Mock() + + self.assertFalse(os.path.exists(path)) + + for n in range(0,99): + mock_time.utcnow.return_value = Mock(microsecond=n* 10000) + + self.update_handler._ensure_partition_assigned() + + self.assertTrue(os.path.exists(path)) + s = fileutil.read_file(path) + self.assertEqual(n, int(s)) + os.remove(path) + + def test_ensure_readonly_sets_readonly(self): + test_files = [ + os.path.join(conf.get_lib_dir(), "faux_certificate.crt"), + os.path.join(conf.get_lib_dir(), "faux_certificate.p7m"), + os.path.join(conf.get_lib_dir(), "faux_certificate.pem"), + os.path.join(conf.get_lib_dir(), "faux_certificate.prv"), + os.path.join(conf.get_lib_dir(), "ovf-env.xml") + ] + for path in test_files: + fileutil.write_file(path, "Faux content") + os.chmod(path, + stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) + + self.update_handler._ensure_readonly_files() + + for path in test_files: + mode = os.stat(path).st_mode + mode &= (stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + self.assertEqual(0, mode ^ stat.S_IRUSR) + + def test_ensure_readonly_leaves_unmodified(self): + test_files = [ + os.path.join(conf.get_lib_dir(), "faux.xml"), + os.path.join(conf.get_lib_dir(), "faux.json"), + os.path.join(conf.get_lib_dir(), "faux.txt"), + os.path.join(conf.get_lib_dir(), "faux") + ] + for path in test_files: + fileutil.write_file(path, "Faux content") + os.chmod(path, + stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) + + self.update_handler._ensure_readonly_files() + + for path in test_files: + mode = os.stat(path).st_mode + mode &= (stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + self.assertEqual( + stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH, + mode) def _test_evaluate_agent_health(self, child_agent_index=0): self.prepare_agents() @@ -1010,36 +830,30 @@ class TestUpdate(UpdateTestCase): self.update_handler.child_agent = child_agent self.update_handler._evaluate_agent_health(latest_agent) - return def test_evaluate_agent_health_ignores_installed_agent(self): self.update_handler._evaluate_agent_health(None) - return def test_evaluate_agent_health_raises_exception_for_restarting_agent(self): self.update_handler.child_launch_time = time.time() - (4 * 60) self.update_handler.child_launch_attempts = CHILD_LAUNCH_RESTART_MAX - 1 self.assertRaises(Exception, self._test_evaluate_agent_health) - return def test_evaluate_agent_health_will_not_raise_exception_for_long_restarts(self): self.update_handler.child_launch_time = time.time() - 24 * 60 self.update_handler.child_launch_attempts = CHILD_LAUNCH_RESTART_MAX self._test_evaluate_agent_health() - return def test_evaluate_agent_health_will_not_raise_exception_too_few_restarts(self): self.update_handler.child_launch_time = time.time() self.update_handler.child_launch_attempts = CHILD_LAUNCH_RESTART_MAX - 2 self._test_evaluate_agent_health() - return def test_evaluate_agent_health_resets_with_new_agent(self): self.update_handler.child_launch_time = time.time() - (4 * 60) self.update_handler.child_launch_attempts = CHILD_LAUNCH_RESTART_MAX - 1 self._test_evaluate_agent_health(child_agent_index=1) self.assertEqual(1, self.update_handler.child_launch_attempts) - return def test_filter_blacklisted_agents(self): self.prepare_agents() @@ -1053,7 +867,31 @@ class TestUpdate(UpdateTestCase): agent.mark_failure(is_fatal=True) self.update_handler._filter_blacklisted_agents() self.assertEqual(kept_agents, self.update_handler.agents) - return + + def test_find_agents(self): + self.prepare_agents() + + self.assertTrue(0 <= len(self.update_handler.agents)) + self.update_handler._find_agents() + self.assertEqual(len(get_agents(self.tmp_dir)), len(self.update_handler.agents)) + + def test_find_agents_does_reload(self): + self.prepare_agents() + + self.update_handler._find_agents() + agents = self.update_handler.agents + + self.update_handler._find_agents() + self.assertNotEqual(agents, self.update_handler.agents) + + def test_find_agents_sorts(self): + self.prepare_agents() + self.update_handler._find_agents() + + v = FlexibleVersion("100000") + for a in self.update_handler.agents: + self.assertTrue(v > a.version) + v = a.version @patch('azurelinuxagent.common.protocol.wire.WireClient.get_host_plugin') def test_get_host_plugin_returns_host_for_wireserver(self, mock_get_host): @@ -1076,16 +914,20 @@ class TestUpdate(UpdateTestCase): latest_agent = self.update_handler.get_latest_agent() self.assertEqual(len(get_agents(self.tmp_dir)), len(self.update_handler.agents)) self.assertEqual(latest_version, latest_agent.version) - 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()) def test_get_latest_agent_no_updates(self): self.assertEqual(None, self.update_handler.get_latest_agent()) - return def test_get_latest_agent_skip_updates(self): conf.get_autoupdate_enabled = Mock(return_value=False) self.assertEqual(None, self.update_handler.get_latest_agent()) - return def test_get_latest_agent_skips_unavailable(self): self.prepare_agents() @@ -1098,12 +940,10 @@ class TestUpdate(UpdateTestCase): latest_agent = self.update_handler.get_latest_agent() self.assertTrue(latest_agent.version < latest_version) self.assertEqual(latest_agent.version, prior_agent.version) - return def test_get_pid_files(self): pid_files = self.update_handler._get_pid_files() self.assertEqual(0, len(pid_files)) - return def test_get_pid_files_returns_previous(self): for n in range(1250): @@ -1114,74 +954,61 @@ class TestUpdate(UpdateTestCase): pid_dir, pid_name, pid_re = self.update_handler._get_pid_parts() for p in pid_files: self.assertTrue(pid_re.match(os.path.basename(p))) - 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): + def test_is_clean_start_returns_false_when_sentinal_exists(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_find_agents(self): - self.prepare_agents() + def test_is_version_available(self): + self.prepare_agents(is_available=True) + self.update_handler.agents = self.agents() - self.assertTrue(0 <= len(self.update_handler.agents)) - self.update_handler._find_agents() - self.assertEqual(len(get_agents(self.tmp_dir)), len(self.update_handler.agents)) - return + for agent in self.agents(): + self.assertTrue(self.update_handler._is_version_eligible(agent.version)) - def test_find_agents_does_reload(self): - self.prepare_agents() + @patch("azurelinuxagent.ga.update.is_current_agent_installed", return_value=False) + def test_is_version_available_rejects(self, mock_current): + self.prepare_agents(is_available=True) + self.update_handler.agents = self.agents() - self.update_handler._find_agents() - agents = self.update_handler.agents + self.update_handler.agents[0].mark_failure(is_fatal=True) + self.assertFalse(self.update_handler._is_version_eligible(self.agents()[0].version)) - self.update_handler._find_agents() - self.assertNotEqual(agents, self.update_handler.agents) - return + @patch("azurelinuxagent.ga.update.is_current_agent_installed", return_value=True) + def test_is_version_available_accepts_current(self, mock_current): + self.update_handler.agents = [] + self.assertTrue(self.update_handler._is_version_eligible(CURRENT_VERSION)) - def test_find_agents_sorts(self): + @patch("azurelinuxagent.ga.update.is_current_agent_installed", return_value=False) + def test_is_version_available_rejects_by_default(self, mock_current): self.prepare_agents() - self.update_handler._find_agents() + self.update_handler.agents = [] - v = FlexibleVersion("100000") - for a in self.update_handler.agents: - self.assertTrue(v > a.version) - v = a.version - return + v = self.agents()[0].version + self.assertFalse(self.update_handler._is_version_eligible(v)) def test_purge_agents(self): self.prepare_agents() @@ -1191,8 +1018,8 @@ class TestUpdate(UpdateTestCase): self.assertTrue(2 < len(self.update_handler.agents)) # Purge every other agent - kept_agents = self.update_handler.agents[1::2] - purged_agents = self.update_handler.agents[::2] + purged_agents = self.update_handler.agents[1::2] + kept_agents = self.update_handler.agents[::2] # Reload and assert only the kept agents remain on disk self.update_handler.agents = kept_agents @@ -1213,7 +1040,6 @@ class TestUpdate(UpdateTestCase): agent_path = os.path.join(self.tmp_dir, "{0}-{1}".format(AGENT_NAME, agent.version)) self.assertTrue(os.path.exists(agent_path)) self.assertTrue(os.path.exists(agent_path + ".zip")) - return def _test_run_latest(self, mock_child=None, mock_time=None, child_args=None): if mock_child is None: @@ -1246,7 +1072,6 @@ class TestUpdate(UpdateTestCase): 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_passes_child_args(self): self.prepare_agents() @@ -1258,7 +1083,6 @@ class TestUpdate(UpdateTestCase): self.assertTrue(len(args) > 1) self.assertTrue(args[0].startswith("python")) self.assertEqual("AnArgument", args[len(args)-1]) - return def test_run_latest_polls_and_waits_for_success(self): mock_child = ChildMock(return_value=None) @@ -1266,7 +1090,6 @@ class TestUpdate(UpdateTestCase): self._test_run_latest(mock_child=mock_child, mock_time=mock_time) self.assertEqual(2, mock_child.poll.call_count) self.assertEqual(1, mock_child.wait.call_count) - return def test_run_latest_polling_stops_at_success(self): mock_child = ChildMock(return_value=0) @@ -1274,7 +1097,6 @@ class TestUpdate(UpdateTestCase): self._test_run_latest(mock_child=mock_child, mock_time=mock_time) self.assertEqual(1, mock_child.poll.call_count) self.assertEqual(0, mock_child.wait.call_count) - return def test_run_latest_polling_stops_at_failure(self): mock_child = ChildMock(return_value=42) @@ -1282,14 +1104,12 @@ class TestUpdate(UpdateTestCase): self._test_run_latest(mock_child=mock_child, mock_time=mock_time) self.assertEqual(1, mock_child.poll.call_count) self.assertEqual(0, mock_child.wait.call_count) - return def test_run_latest_polls_frequently_if_installed_is_latest(self): mock_child = ChildMock(return_value=0) mock_time = TimeMock(time_increment=CHILD_HEALTH_INTERVAL/2) self._test_run_latest(mock_time=mock_time) self.assertEqual(1, mock_time.sleep_interval) - return def test_run_latest_polls_moderately_if_installed_not_latest(self): self.prepare_agents() @@ -1298,7 +1118,6 @@ class TestUpdate(UpdateTestCase): mock_time = TimeMock(time_increment=CHILD_HEALTH_INTERVAL/2) self._test_run_latest(mock_time=mock_time) self.assertNotEqual(1, mock_time.sleep_interval) - return def test_run_latest_defaults_to_current(self): self.assertEqual(None, self.update_handler.get_latest_agent()) @@ -1308,7 +1127,6 @@ class TestUpdate(UpdateTestCase): self.assertEqual(args[0], [get_python_cmd(), "-u", sys.argv[0], "-run-exthandlers"]) self.assertEqual(True, 'cwd' in kwargs) self.assertEqual(os.getcwd(), kwargs['cwd']) - return def test_run_latest_forwards_output(self): try: @@ -1332,7 +1150,6 @@ class TestUpdate(UpdateTestCase): self.assertEqual(1, len(stderr.readlines())) finally: shutil.rmtree(tempdir, True) - return def test_run_latest_nonzero_code_marks_failures(self): # logger.add_logger_appender(logger.AppenderType.STDOUT) @@ -1350,7 +1167,6 @@ class TestUpdate(UpdateTestCase): self.assertFalse(latest_agent.is_available) self.assertNotEqual(0.0, latest_agent.error.last_failure) self.assertEqual(1, latest_agent.error.failure_count) - return def test_run_latest_exception_blacklists(self): self.prepare_agents() @@ -1367,7 +1183,6 @@ class TestUpdate(UpdateTestCase): self.assertTrue(latest_agent.error.is_blacklisted) self.assertNotEqual(0.0, latest_agent.error.last_failure) self.assertEqual(1, latest_agent.error.failure_count) - return def test_run_latest_exception_does_not_blacklist_if_terminating(self): self.prepare_agents() @@ -1385,20 +1200,17 @@ class TestUpdate(UpdateTestCase): self.assertFalse(latest_agent.error.is_blacklisted) self.assertEqual(0.0, latest_agent.error.last_failure) self.assertEqual(0, latest_agent.error.failure_count) - return @patch('signal.signal') def test_run_latest_captures_signals(self, mock_signal): self._test_run_latest() self.assertEqual(1, mock_signal.call_count) - return @patch('signal.signal') def test_run_latest_creates_only_one_signal_handler(self, mock_signal): self.update_handler.signal_handler = "Not None" self._test_run_latest() self.assertEqual(0, mock_signal.call_count) - return def _test_run(self, invocations=1, calls=[call.run()], enable_updates=False): conf.get_autoupdate_enabled = Mock(return_value=enable_updates) @@ -1436,42 +1248,34 @@ class TestUpdate(UpdateTestCase): self.assertEqual(1, mock_env.call_count) self.assertEqual(1, mock_exit.call_count) - return def test_run(self): self._test_run() - return def test_run_keeps_running(self): self._test_run(invocations=15, calls=[call.run()]*15) - return def test_run_stops_if_update_available(self): 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): self.prepare_agents() @@ -1479,7 +1283,6 @@ class TestUpdate(UpdateTestCase): self.update_handler._set_agents([GuestAgent(path=path) for path in self.agent_dirs()]) self.assertTrue(len(self.update_handler.agents) > 0) self.assertEqual(len(self.agent_dirs()), len(self.update_handler.agents)) - return def test_set_agents_sorts_agents(self): self.prepare_agents() @@ -1490,34 +1293,29 @@ class TestUpdate(UpdateTestCase): for a in self.update_handler.agents: self.assertTrue(v > a.version) 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(self.update_handler.running) 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(self.update_handler.running) self.assertFalse(os.path.isfile(self.update_handler._sentinal_file_path())) - return def test_shutdown_ignores_exceptions(self): self.update_handler._set_sentinal() @@ -1527,7 +1325,101 @@ class TestUpdate(UpdateTestCase): self.update_handler._shutdown() except Exception as e: self.assertTrue(False, "Unexpected exception") - return + + def _test_upgrade_available( + self, + base_version=FlexibleVersion(AGENT_VERSION), + protocol=None, + versions=None, + count=5): + + if protocol is None: + protocol = self._create_protocol(count=count, versions=versions) + + self.update_handler.protocol_util = protocol + conf.get_autoupdate_gafamily = Mock(return_value=protocol.family) + + 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()) + + def test_upgrade_available_will_refresh_goal_state(self): + protocol = self._create_protocol() + protocol.emulate_stale_goal_state() + self.assertTrue(self._test_upgrade_available(protocol=protocol)) + self.assertEqual(2, protocol.call_counts["get_vmagent_manifests"]) + self.assertEqual(1, protocol.call_counts["get_vmagent_pkgs"]) + self.assertEqual(1, protocol.call_counts["update_goal_state"]) + self.assertTrue(protocol.goal_state_forced) + + 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) + + def test_upgrade_available_includes_old_agents(self): + self.prepare_agents() + + 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_upgrade_available(versions=self.agent_versions())) + self.assertEqual(all_count, len(self.update_handler.agents)) + + 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_upgrade_available(versions=agent_versions)) + self.assertEqual(len(agent_versions), len(self.update_handler.agents)) + + # Purging always keeps the running agent + if CURRENT_VERSION not in agent_versions: + agent_versions.append(CURRENT_VERSION) + self.assertEqual(agent_versions, self.agent_versions()) + + def test_update_available_returns_true_if_current_gets_blacklisted(self): + self.update_handler._is_version_eligible = Mock(return_value=False) + self.assertTrue(self._test_upgrade_available()) + + 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_upgrade_available()) + + def test_upgrade_available_skips_if_when_no_new_versions(self): + self.prepare_agents() + base_version = self.agent_versions()[0] + 1 + self.update_handler._is_version_eligible = lambda x: x == base_version + self.assertFalse(self._test_upgrade_available(base_version=base_version)) + + def test_upgrade_available_skips_when_no_versions(self): + self.assertFalse(self._test_upgrade_available(protocol=ProtocolMock())) + + def test_upgrade_available_skips_when_updates_are_disabled(self): + conf.get_autoupdate_enabled = Mock(return_value=False) + self.assertFalse(self._test_upgrade_available()) + + def test_upgrade_available_sorts(self): + self.prepare_agents() + self._test_upgrade_available() + + v = FlexibleVersion("100000") + for a in self.update_handler.agents: + self.assertTrue(v > a.version) + v = a.version def test_write_pid_file(self): for n in range(1112): @@ -1538,7 +1430,6 @@ class TestUpdate(UpdateTestCase): self.assertEqual("1111_waagent.pid", os.path.basename(pid_files[-1])) 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): @@ -1546,7 +1437,64 @@ class TestUpdate(UpdateTestCase): pid_files, pid_file = self.update_handler._write_pid_file() self.assertEqual(0, len(pid_files)) self.assertEqual(None, pid_file) - return + + @patch('azurelinuxagent.common.protocol.wire.WireClient.get_goal_state', + return_value=GoalState(load_data('wire/goal_state.xml'))) + def test_package_filter_for_agent_manifest(self, _): + + protocol = WireProtocol('12.34.56.78') + extension_config = ExtensionsConfig(load_data('wire/ext_conf.xml')) + agent_manifest = extension_config.vmagent_manifests.vmAgentManifests[0] + + # has agent versions 13, 14 + ga_manifest_1 = ExtensionManifest(load_data('wire/ga_manifest_1.xml')) + + # has agent versions 13, 14, 15 + ga_manifest_2 = ExtensionManifest(load_data('wire/ga_manifest_2.xml')) + + goal_state = protocol.client.get_goal_state() + disk_cache = os.path.join(conf.get_lib_dir(), + AGENTS_MANIFEST_FILE_NAME.format( + agent_manifest.family, + goal_state.incarnation)) + + self.assertFalse(os.path.exists(disk_cache)) + self.assertTrue(ga_manifest_1.allowed_versions is None) + + with patch( + 'azurelinuxagent.common.protocol.wire.WireClient' + '.get_gafamily_manifest', + return_value=ga_manifest_1): + + pkg_list_1 = protocol.get_vmagent_pkgs(agent_manifest) + self.assertTrue(pkg_list_1 is not None) + self.assertTrue(len(pkg_list_1.versions) == 2) + self.assertTrue(pkg_list_1.versions[0].version == '2.2.13') + self.assertTrue(pkg_list_1.versions[0].uris[0].uri == 'url1_13') + self.assertTrue(pkg_list_1.versions[1].version == '2.2.14') + self.assertTrue(pkg_list_1.versions[1].uris[0].uri == 'url1_14') + + self.assertTrue(os.path.exists(disk_cache)) + + with patch( + 'azurelinuxagent.common.protocol.wire.WireClient' + '.get_gafamily_manifest', + return_value=ga_manifest_2): + + pkg_list_2 = protocol.get_vmagent_pkgs(agent_manifest) + self.assertTrue(pkg_list_2 is not None) + self.assertTrue(len(pkg_list_2.versions) == 2) + self.assertTrue(pkg_list_2.versions[0].version == '2.2.13') + self.assertTrue(pkg_list_2.versions[0].uris[0].uri == 'url2_13') + self.assertTrue(pkg_list_2.versions[1].version == '2.2.14') + self.assertTrue(pkg_list_2.versions[1].uris[0].uri == 'url2_14') + # does not contain 2.2.15 + + self.assertTrue(os.path.exists(disk_cache)) + self.assertTrue(ga_manifest_2.allowed_versions is not None) + self.assertTrue(len(ga_manifest_2.allowed_versions) == 2) + self.assertTrue(ga_manifest_2.allowed_versions[0] == '2.2.13') + self.assertTrue(ga_manifest_2.allowed_versions[1] == '2.2.14') class ChildMock(Mock): @@ -1555,7 +1503,6 @@ class ChildMock(Mock): self.poll = Mock(return_value=return_value, side_effect=side_effect) self.wait = Mock(return_value=return_value, side_effect=side_effect) - return class ProtocolMock(object): @@ -1573,7 +1520,6 @@ class ProtocolMock(object): self.versions = versions if versions is not None else [] self.create_manifests() self.create_packages() - return def emulate_stale_goal_state(self): self.goal_state_is_stale = True @@ -1589,7 +1535,6 @@ class ProtocolMock(object): manifest_uri = "https://nowhere.msft/agent/{0}".format(i) manifest.versionsManifestUris.append(VMAgentManifestUri(uri=manifest_uri)) self.agent_manifests.vmAgentManifests.append(manifest) - return def create_packages(self): self.agent_packages = ExtHandlerPackageList() @@ -1602,7 +1547,6 @@ class ProtocolMock(object): package_uri = "https://nowhere.msft/agent_pkg/{0}".format(i) package.uris.append(ExtHandlerPackageUri(uri=package_uri)) self.agent_packages.versions.append(package) - return def get_protocol(self): return self @@ -1624,7 +1568,7 @@ class ProtocolMock(object): def update_goal_state(self, forced=False, max_retry=3): self.call_counts["update_goal_state"] += 1 self.goal_state_forced = self.goal_state_forced or forced - return + class ResponseMock(Mock): def __init__(self, status=restutil.httpclient.OK, response=None, reason=None): @@ -1632,7 +1576,6 @@ class ResponseMock(Mock): self.status = status self.reason = reason self.response = response - return def read(self): return self.response @@ -1646,11 +1589,9 @@ class TimeMock(Mock): self.time_increment = time_increment self.sleep_interval = None - return def sleep(self, n): self.sleep_interval = n - return def time(self): self.time_call_count += 1 @@ -1658,5 +1599,6 @@ class TimeMock(Mock): self.next_time += self.time_increment return current_time + if __name__ == '__main__': unittest.main() diff --git a/tests/pa/test_provision.py b/tests/pa/test_provision.py index 7045fcc..2c2d2c9 100644 --- a/tests/pa/test_provision.py +++ b/tests/pa/test_provision.py @@ -15,9 +15,13 @@ # Requires Python 2.4+ and Openssl 1.0+ # +import json +import socket + import azurelinuxagent.common.utils.fileutil as fileutil -from azurelinuxagent.common.exception import ProtocolError +from azurelinuxagent.common.event import WALAEventOperation +from azurelinuxagent.common.exception import ProvisionError from azurelinuxagent.common.osutil.default import DefaultOSUtil from azurelinuxagent.common.protocol import OVF_FILE_NAME from azurelinuxagent.pa.provision import get_provision_handler @@ -115,6 +119,85 @@ class TestProvision(AgentTestCase): ph.osutil.is_current_instance_id.assert_called_once() deprovision_handler.run_changed_unique_id.assert_called_once() + @distros() + @patch('azurelinuxagent.common.osutil.default.DefaultOSUtil.get_instance_id', + return_value='B9F3C233-9913-9F42-8EB3-BA656DF32502') + def test_provision_telemetry_success(self, mock_util, distro_name, distro_version, + distro_full_name): + """ + Assert that the agent issues two telemetry messages as part of a + successful provisioning. + + 1. Provision + 2. GuestState + """ + ph = get_provision_handler(distro_name, distro_version, + distro_full_name) + ph.report_event = MagicMock() + ph.reg_ssh_host_key = MagicMock(return_value='--thumprint--') + + mock_osutil = MagicMock() + mock_osutil.decode_customdata = Mock(return_value="") + + ph.osutil = mock_osutil + ph.protocol_util.osutil = mock_osutil + ph.protocol_util.get_protocol_by_file = MagicMock() + ph.protocol_util.get_protocol = MagicMock() + + conf.get_dvd_mount_point = Mock(return_value=self.tmp_dir) + ovfenv_file = os.path.join(self.tmp_dir, OVF_FILE_NAME) + ovfenv_data = load_data("ovf-env.xml") + fileutil.write_file(ovfenv_file, ovfenv_data) + + ph.run() + + call1 = call("Provisioning succeeded", duration=ANY, is_success=True) + call2 = call(ANY, is_success=True, operation=WALAEventOperation.GuestState) + ph.report_event.assert_has_calls([call1, call2]) + + args, kwargs = ph.report_event.call_args_list[1] + guest_state_json = json.loads(args[0]) + self.assertTrue(1 <= guest_state_json['cpu']) + self.assertTrue(1 <= guest_state_json['mem']) + self.assertEqual(socket.gethostname(), guest_state_json['hostname']) + + @distros() + @patch( + 'azurelinuxagent.common.osutil.default.DefaultOSUtil.get_instance_id', + return_value='B9F3C233-9913-9F42-8EB3-BA656DF32502') + def test_provision_telemetry_fail(self, mock_util, distro_name, + distro_version, + distro_full_name): + """ + Assert that the agent issues one telemetry message as part of a + failed provisioning. + + 1. Provision + """ + ph = get_provision_handler(distro_name, distro_version, + distro_full_name) + ph.report_event = MagicMock() + ph.reg_ssh_host_key = MagicMock(side_effect=ProvisionError( + "--unit-test--")) + + mock_osutil = MagicMock() + mock_osutil.decode_customdata = Mock(return_value="") + + ph.osutil = mock_osutil + ph.protocol_util.osutil = mock_osutil + ph.protocol_util.get_protocol_by_file = MagicMock() + ph.protocol_util.get_protocol = MagicMock() + + conf.get_dvd_mount_point = Mock(return_value=self.tmp_dir) + ovfenv_file = os.path.join(self.tmp_dir, OVF_FILE_NAME) + ovfenv_data = load_data("ovf-env.xml") + fileutil.write_file(ovfenv_file, ovfenv_data) + + ph.run() + ph.report_event.assert_called_once_with( + "[ProvisionError] --unit-test--") + + if __name__ == '__main__': unittest.main() diff --git a/tests/protocol/mockwiredata.py b/tests/protocol/mockwiredata.py index 5924719..db59ece 100644 --- a/tests/protocol/mockwiredata.py +++ b/tests/protocol/mockwiredata.py @@ -31,7 +31,7 @@ DATA_FILE = { "ga_manifest" : "wire/ga_manifest.xml", "trans_prv": "wire/trans_prv", "trans_cert": "wire/trans_cert", - "test_ext": "ext/sample_ext-1.2.0.zip" + "test_ext": "ext/sample_ext-1.3.0.zip" } DATA_FILE_NO_EXT = DATA_FILE.copy() @@ -52,6 +52,9 @@ DATA_FILE_EXT_INTERNALVERSION["ext_conf"] = "wire/ext_conf_internalversion.xml" DATA_FILE_EXT_AUTOUPGRADE_INTERNALVERSION = DATA_FILE.copy() DATA_FILE_EXT_AUTOUPGRADE_INTERNALVERSION["ext_conf"] = "wire/ext_conf_autoupgrade_internalversion.xml" +DATA_FILE_EXT_ROLLINGUPGRADE = DATA_FILE.copy() +DATA_FILE_EXT_ROLLINGUPGRADE["ext_conf"] = "wire/ext_conf_upgradeguid.xml" + class WireProtocolData(object): def __init__(self, data_files=DATA_FILE): self.emulate_stale_goal_state = False diff --git a/tests/protocol/test_metadata.py b/tests/protocol/test_metadata.py index 5047b86..5f90f12 100644 --- a/tests/protocol/test_metadata.py +++ b/tests/protocol/test_metadata.py @@ -39,7 +39,7 @@ class TestMetadataProtocolGetters(AgentTestCase): protocol.get_certs() ext_handlers, etag = protocol.get_ext_handlers() for ext_handler in ext_handlers.extHandlers: - protocol.get_ext_handler_pkgs(ext_handler) + protocol.get_ext_handler_pkgs(ext_handler, etag) def test_getters(self, *args): test_data = MetadataProtocolData(DATA_FILE) diff --git a/tests/protocol/test_wire.py b/tests/protocol/test_wire.py index d19bab1..9e475ec 100644 --- a/tests/protocol/test_wire.py +++ b/tests/protocol/test_wire.py @@ -14,6 +14,9 @@ # # Requires Python 2.4+ and Openssl 1.0+ # + +import glob + from azurelinuxagent.common import event from azurelinuxagent.common.protocol.wire import * from tests.protocol.mockwiredata import * @@ -25,10 +28,10 @@ wireserver_url = '168.63.129.16' @patch("time.sleep") @patch("azurelinuxagent.common.protocol.wire.CryptUtil") -class TestWireProtocolGetters(AgentTestCase): +class TestWireProtocol(AgentTestCase): def setUp(self): - super(TestWireProtocolGetters, self).setUp() + super(TestWireProtocol, self).setUp() HostPluginProtocol.set_default_channel(False) def _test_getters(self, test_data, MockCryptUtil, _): @@ -40,8 +43,9 @@ class TestWireProtocolGetters(AgentTestCase): protocol.get_vminfo() protocol.get_certs() ext_handlers, etag = protocol.get_ext_handlers() + self.assertEqual("1", etag) for ext_handler in ext_handlers.extHandlers: - protocol.get_ext_handler_pkgs(ext_handler) + protocol.get_ext_handler_pkgs(ext_handler, etag) crt1 = os.path.join(self.tmp_dir, '33B0ABCE4673538650971C10F7D7397E71561F35.crt') @@ -54,6 +58,8 @@ class TestWireProtocolGetters(AgentTestCase): self.assertTrue(os.path.isfile(crt2)) self.assertTrue(os.path.isfile(prv2)) + self.assertEqual("1", protocol.get_incarnation()) + def test_getters(self, *args): """Normal case""" test_data = WireProtocolData(DATA_FILE) @@ -88,6 +94,7 @@ class TestWireProtocolGetters(AgentTestCase): # HostingEnvironmentConfig, will be retrieved the expected number self.assertEqual(2, test_data.call_counts["hostingenvuri"]) + def test_call_storage_kwargs(self, mock_cryptutil, mock_sleep): @@ -365,11 +372,14 @@ class TestWireProtocolGetters(AgentTestCase): v1_ga_status = { 'version': str(CURRENT_VERSION), 'status': status, - 'osversion': DISTRO_VERSION, - 'osname': DISTRO_NAME, - 'hostname': socket.gethostname(), 'formattedMessage': formatted_msg } + v1_ga_guest_info = { + 'computerName': socket.gethostname(), + 'osName': DISTRO_NAME, + 'osVersion': DISTRO_VERSION, + 'version': str(CURRENT_VERSION), + } v1_agg_status = { 'guestAgentStatus': v1_ga_status, 'handlerAggregateStatus': [] @@ -377,7 +387,8 @@ class TestWireProtocolGetters(AgentTestCase): v1_vm_status = { 'version': '1.1', 'timestampUTC': timestamp, - 'aggregateStatus': v1_agg_status + 'aggregateStatus': v1_agg_status, + 'guestOSInfo' : v1_ga_guest_info } self.assertEqual(json.dumps(v1_vm_status), actual.to_json()) @@ -390,5 +401,6 @@ class MockResponse: def read(self): return self.body + if __name__ == '__main__': unittest.main() diff --git a/tests/test_agent.py b/tests/test_agent.py index 77be07a..2095db0 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -15,9 +15,7 @@ # Requires Python 2.4+ and Openssl 1.0+ # -import mock import os.path -import sys from azurelinuxagent.agent import * from azurelinuxagent.common.conf import * @@ -39,12 +37,13 @@ Logs.Verbose = False OS.AllowHTTP = False OS.CheckRdmaDriver = False OS.EnableFIPS = True -OS.EnableFirewall = True +OS.EnableFirewall = False OS.EnableRDMA = False OS.HomeDir = /home OS.OpensslPath = /usr/bin/openssl OS.PasswordPath = /etc/shadow OS.RootDeviceScsiTimeout = 300 +OS.SshClientAliveInterval = 42 OS.SshDir = /notareal/path OS.SudoersDir = /etc/sudoers.d OS.UpdateRdmaDriver = False @@ -65,8 +64,7 @@ ResourceDisk.Filesystem = ext4 ResourceDisk.Format = True ResourceDisk.MountOptions = None ResourceDisk.MountPoint = /mnt/resource -ResourceDisk.SwapSizeMB = 0 -""".split('\n') +ResourceDisk.SwapSizeMB = 0""".split('\n') class TestAgent(AgentTestCase): @@ -160,10 +158,30 @@ class TestAgent(AgentTestCase): self.assertFalse(os.path.isdir(ext_log_dir)) mock_log.assert_called_once() - def test_agent_show_configuration(self): - if not hasattr(sys.stdout, 'getvalue'): - self.fail('Test requires at least Python 2.7 with buffered output') - agent = Agent(False, - conf_file_path=os.path.join(data_dir, "test_waagent.conf")) - agent.show_configuration() - self.assertEqual(EXPECTED_CONFIGURATION, sys.stdout.getvalue().split('\n')) + def test_agent_get_configuration(self): + Agent(False, conf_file_path=os.path.join(data_dir, "test_waagent.conf")) + + actual_configuration = [] + configuration = conf.get_configuration() + for k in sorted(configuration.keys()): + actual_configuration.append("{0} = {1}".format(k, configuration[k])) + self.assertEqual(EXPECTED_CONFIGURATION, actual_configuration) + + def test_agent_usage_message(self): + message = usage() + + # Python 2.6 does not have assertIn() + self.assertTrue("-verbose" in message) + self.assertTrue("-force" in message) + self.assertTrue("-help" in message) + self.assertTrue("-configuration-path" in message) + self.assertTrue("-deprovision" in message) + self.assertTrue("-register-service" in message) + self.assertTrue("-version" in message) + self.assertTrue("-daemon" in message) + self.assertTrue("-start" in message) + self.assertTrue("-run-exthandlers" in message) + self.assertTrue("-show-configuration" in message) + + # sanity check + self.assertFalse("-not-a-valid-option" in message) diff --git a/tests/tools.py b/tests/tools.py index 94fab7f..5c65847 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -26,17 +26,20 @@ import tempfile import unittest from functools import wraps +import time + import azurelinuxagent.common.event as event import azurelinuxagent.common.conf as conf import azurelinuxagent.common.logger as logger +from azurelinuxagent.common.utils import fileutil from azurelinuxagent.common.version import PY_VERSION_MAJOR -#Import mock module for Python2 and Python3 +# Import mock module for Python2 and Python3 try: - from unittest.mock import Mock, patch, MagicMock, DEFAULT, call + from unittest.mock import Mock, patch, MagicMock, DEFAULT, ANY, call except ImportError: - from mock import Mock, patch, MagicMock, DEFAULT, call + from mock import Mock, patch, MagicMock, DEFAULT, ANY, call test_dir = os.path.dirname(os.path.abspath(__file__)) data_dir = os.path.join(test_dir, "data") @@ -45,11 +48,12 @@ debug = False if os.environ.get('DEBUG') == '1': debug = True -#Enable verbose logger to stdout +# Enable verbose logger to stdout if debug: logger.add_logger_appender(logger.AppenderType.STDOUT, logger.LogLevel.VERBOSE) + class AgentTestCase(unittest.TestCase): def setUp(self): prefix = "{0}_".format(self.__class__.__name__) @@ -72,12 +76,20 @@ class AgentTestCase(unittest.TestCase): if not debug and self.tmp_dir is not None: shutil.rmtree(self.tmp_dir) + def _create_files(self, tmp_dir, prefix, suffix, count, with_sleep=0): + for i in range(count): + f = os.path.join(tmp_dir, '.'.join((prefix, str(i), suffix))) + fileutil.write_file(f, "faux content") + time.sleep(with_sleep) + + def load_data(name): """Load test data""" path = os.path.join(data_dir, name) with open(path, "r") as data_file: return data_file.read() + def load_bin_data(name): """Load test bin data""" path = os.path.join(data_dir, name) @@ -106,12 +118,14 @@ supported_distro = [ ] + def open_patch(): open_name = '__builtin__.open' if PY_VERSION_MAJOR == 3: open_name = 'builtins.open' return open_name + def distros(distro_name=".*", distro_version=".*", distro_full_name=".*"): """Run test on multiple distros""" def decorator(test_method): @@ -128,8 +142,8 @@ def distros(distro_name=".*", distro_version=".*", distro_full_name=".*"): new_args.extend(args) new_args.extend(distro) test_method(self, *new_args, **kwargs) - #Call tearDown and setUp to create seprated environment for - #distro testing + # Call tearDown and setUp to create separated environment + # for distro testing self.tearDown() self.setUp() return wrapper diff --git a/tests/utils/test_flexible_version.py b/tests/utils/test_flexible_version.py index 1162022..89a7dbf 100644 --- a/tests/utils/test_flexible_version.py +++ b/tests/utils/test_flexible_version.py @@ -405,6 +405,60 @@ class TestFlexibleVersion(unittest.TestCase): self.assertEqual(test, str(FlexibleVersion(test))) return + def test_creation_from_flexible_version(self): + tests = [ + '1', + '1.2', + '1.2.3', + '1.2.3.4', + '1.2.3.4.5', + + '1alpha', + '1.alpha', + '1-alpha', + '1alpha0', + '1.alpha0', + '1-alpha0', + '1.2alpha', + '1.2.alpha', + '1.2-alpha', + '1.2alpha0', + '1.2.alpha0', + '1.2-alpha0', + + '1beta', + '1.beta', + '1-beta', + '1beta0', + '1.beta0', + '1-beta0', + '1.2beta', + '1.2.beta', + '1.2-beta', + '1.2beta0', + '1.2.beta0', + '1.2-beta0', + + '1rc', + '1.rc', + '1-rc', + '1rc0', + '1.rc0', + '1-rc0', + '1.2rc', + '1.2.rc', + '1.2-rc', + '1.2rc0', + '1.2.rc0', + '1.2-rc0', + + '1.2.3.4alpha5', + ] + for test in tests: + v = FlexibleVersion(test) + self.assertEqual(test, str(FlexibleVersion(v))) + return + def test_repr(self): v = FlexibleVersion('1,2,3rc4', ',', ['lol', 'rc']) expected = "FlexibleVersion ('1,2,3rc4', ',', ('lol', 'rc'))" diff --git a/tests/utils/test_rest_util.py b/tests/utils/test_rest_util.py index 52674da..bde0c3d 100644 --- a/tests/utils/test_rest_util.py +++ b/tests/utils/test_rest_util.py @@ -24,9 +24,70 @@ from azurelinuxagent.common.exception import HttpError, \ import azurelinuxagent.common.utils.restutil as restutil from azurelinuxagent.common.future import httpclient, ustr + from tests.tools import * +class TestIOErrorCounter(AgentTestCase): + def test_increment_hostplugin(self): + restutil.IOErrorCounter.reset() + restutil.IOErrorCounter.set_protocol_endpoint() + + restutil.IOErrorCounter.increment( + restutil.DEFAULT_PROTOCOL_ENDPOINT, restutil.HOST_PLUGIN_PORT) + + counts = restutil.IOErrorCounter.get_and_reset() + self.assertEqual(1, counts["hostplugin"]) + self.assertEqual(0, counts["protocol"]) + self.assertEqual(0, counts["other"]) + + def test_increment_protocol(self): + restutil.IOErrorCounter.reset() + restutil.IOErrorCounter.set_protocol_endpoint() + + restutil.IOErrorCounter.increment( + restutil.DEFAULT_PROTOCOL_ENDPOINT, 80) + + counts = restutil.IOErrorCounter.get_and_reset() + self.assertEqual(0, counts["hostplugin"]) + self.assertEqual(1, counts["protocol"]) + self.assertEqual(0, counts["other"]) + + def test_increment_other(self): + restutil.IOErrorCounter.reset() + restutil.IOErrorCounter.set_protocol_endpoint() + + restutil.IOErrorCounter.increment( + '169.254.169.254', 80) + + counts = restutil.IOErrorCounter.get_and_reset() + self.assertEqual(0, counts["hostplugin"]) + self.assertEqual(0, counts["protocol"]) + self.assertEqual(1, counts["other"]) + + def test_get_and_reset(self): + restutil.IOErrorCounter.reset() + restutil.IOErrorCounter.set_protocol_endpoint() + + restutil.IOErrorCounter.increment( + restutil.DEFAULT_PROTOCOL_ENDPOINT, restutil.HOST_PLUGIN_PORT) + restutil.IOErrorCounter.increment( + restutil.DEFAULT_PROTOCOL_ENDPOINT, restutil.HOST_PLUGIN_PORT) + restutil.IOErrorCounter.increment( + restutil.DEFAULT_PROTOCOL_ENDPOINT, 80) + restutil.IOErrorCounter.increment( + '169.254.169.254', 80) + restutil.IOErrorCounter.increment( + '169.254.169.254', 80) + + counts = restutil.IOErrorCounter.get_and_reset() + self.assertEqual(2, counts.get("hostplugin")) + self.assertEqual(1, counts.get("protocol")) + self.assertEqual(2, counts.get("other")) + self.assertEqual( + {"hostplugin":0, "protocol":0, "other":0}, + restutil.IOErrorCounter._counts) + class TestHttpOperations(AgentTestCase): def test_parse_url(self): test_uri = "http://abc.def/ghi#hash?jkl=mn" @@ -268,6 +329,68 @@ class TestHttpOperations(AgentTestCase): @patch("time.sleep") @patch("azurelinuxagent.common.utils.restutil._http_request") + def test_http_request_retries_with_fibonacci_delay(self, _http_request, _sleep): + # Ensure the code is not a throttle code + self.assertFalse(httpclient.BAD_GATEWAY in restutil.THROTTLE_CODES) + + _http_request.side_effect = [ + Mock(status=httpclient.BAD_GATEWAY) + for i in range(restutil.DEFAULT_RETRIES) + ] + [Mock(status=httpclient.OK)] + + restutil.http_get("https://foo.bar", + max_retry=restutil.DEFAULT_RETRIES+1) + + self.assertEqual(restutil.DEFAULT_RETRIES+1, _http_request.call_count) + self.assertEqual(restutil.DEFAULT_RETRIES, _sleep.call_count) + self.assertEqual( + [ + call(restutil._compute_delay(i+1, restutil.DELAY_IN_SECONDS)) + for i in range(restutil.DEFAULT_RETRIES)], + _sleep.call_args_list) + + @patch("time.sleep") + @patch("azurelinuxagent.common.utils.restutil._http_request") + def test_http_request_retries_with_constant_delay_when_throttled(self, _http_request, _sleep): + # Ensure the code is a throttle code + self.assertTrue(httpclient.SERVICE_UNAVAILABLE in restutil.THROTTLE_CODES) + + _http_request.side_effect = [ + Mock(status=httpclient.SERVICE_UNAVAILABLE) + for i in range(restutil.DEFAULT_RETRIES) + ] + [Mock(status=httpclient.OK)] + + restutil.http_get("https://foo.bar", + max_retry=restutil.DEFAULT_RETRIES+1) + + self.assertEqual(restutil.DEFAULT_RETRIES+1, _http_request.call_count) + self.assertEqual(restutil.DEFAULT_RETRIES, _sleep.call_count) + self.assertEqual( + [call(1) for i in range(restutil.DEFAULT_RETRIES)], + _sleep.call_args_list) + + @patch("time.sleep") + @patch("azurelinuxagent.common.utils.restutil._http_request") + def test_http_request_retries_for_safe_minimum_number_when_throttled(self, _http_request, _sleep): + # Ensure the code is a throttle code + self.assertTrue(httpclient.SERVICE_UNAVAILABLE in restutil.THROTTLE_CODES) + + _http_request.side_effect = [ + Mock(status=httpclient.SERVICE_UNAVAILABLE) + for i in range(restutil.THROTTLE_RETRIES-1) + ] + [Mock(status=httpclient.OK)] + + restutil.http_get("https://foo.bar", + max_retry=1) + + self.assertEqual(restutil.THROTTLE_RETRIES, _http_request.call_count) + self.assertEqual(restutil.THROTTLE_RETRIES-1, _sleep.call_count) + self.assertEqual( + [call(1) for i in range(restutil.THROTTLE_RETRIES-1)], + _sleep.call_args_list) + + @patch("time.sleep") + @patch("azurelinuxagent.common.utils.restutil._http_request") def test_http_request_raises_for_bad_request(self, _http_request, _sleep): _http_request.side_effect = [ Mock(status=httpclient.BAD_REQUEST) |