Repository: Azure/WALinuxAgent
Pull request #1217 (2018-06-19 20:30:47)

BVT cleanup
- catch json parsing exceptions for artifacts profile blob and send telemetry
- explicitly handle control flow when cert file does not exist
- remove vscode settings and minor cleanup


Patch:
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 26df38bf..00000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    "python.linting.enabled": false
-}
\ No newline at end of file
diff --git a/azurelinuxagent/common/event.py b/azurelinuxagent/common/event.py
index 381af578..91615a9e 100644
--- a/azurelinuxagent/common/event.py
+++ b/azurelinuxagent/common/event.py
@@ -43,6 +43,7 @@ class WALAEventOperation:
     ActivateResourceDisk = "ActivateResourceDisk"
     AgentBlacklisted = "AgentBlacklisted"
     AgentEnabled = "AgentEnabled"
+    ArtifactsProfileBlob = "ArtifactsProfileBlob"
     AutoUpdate = "AutoUpdate"
     CustomData = "CustomData"
     Deploy = "Deploy"
diff --git a/azurelinuxagent/common/protocol/wire.py b/azurelinuxagent/common/protocol/wire.py
index eb5e10dd..6e732f60 100644
--- a/azurelinuxagent/common/protocol/wire.py
+++ b/azurelinuxagent/common/protocol/wire.py
@@ -1112,8 +1112,8 @@ class WireClient(object):
         message = message.format(*args)
         logger.warn(message)
         report_event(op=WALAEventOperation.ReportStatus,
-                    is_success=False,
-                    message=message)
+                     is_success=False,
+                     message=message)
 
     def get_header(self):
         return {
@@ -1180,7 +1180,17 @@ class WireClient(object):
 
                     if not textutil.is_str_none_or_whitespace(profile):
                         logger.verbose("Artifacts profile downloaded")
-                        artifacts_profile = InVMArtifactsProfile(profile)
+                        try:
+                            artifacts_profile = InVMArtifactsProfile(profile)
+                        except Exception:
+                            logger.warn("Could not parse artifacts profile blob")
+                            msg = "Content: [{0}]".format(profile)
+                            logger.verbose(msg)
+
+                            from azurelinuxagent.common.event import report_event, WALAEventOperation
+                            report_event(op=WALAEventOperation.ArtifactsProfileBlob,
+                                         is_success=False,
+                                         message=msg)
 
                 return artifacts_profile
 
@@ -1189,9 +1199,7 @@ class WireClient(object):
                 continue
 
             except Exception as e:
-                logger.warn(
-                    "Exception retrieving artifacts profile: {0}".format(
-                        ustr(e)))
+                logger.warn("Exception retrieving artifacts profile: {0}".format(ustr(e)))
 
         return None
 
diff --git a/azurelinuxagent/common/utils/cryptutil.py b/azurelinuxagent/common/utils/cryptutil.py
index 8a19f76d..c3dd4d8c 100644
--- a/azurelinuxagent/common/utils/cryptutil.py
+++ b/azurelinuxagent/common/utils/cryptutil.py
@@ -18,6 +18,7 @@
 #
 
 import base64
+import errno
 import struct
 import sys
 import os.path
@@ -50,47 +51,46 @@ class CryptUtil(object):
 
     def get_pubkey_from_prv(self, file_name):
         if not os.path.exists(file_name):
-            raise IOError("File not found: {0}", file_name)
-
-        cmd = "{0} rsa -in {1} -pubout 2>/dev/null".format(self.openssl_cmd, 
-                                                           file_name)
-        pub = shellutil.run_get_output(cmd)[1]
-        return pub
+            raise IOError(errno.ENOENT, "File not found", file_name)
+        else:
+            cmd = "{0} rsa -in {1} -pubout 2>/dev/null".format(self.openssl_cmd,
+                                                               file_name)
+            pub = shellutil.run_get_output(cmd)[1]
+            return pub
 
     def get_pubkey_from_crt(self, file_name):
         if not os.path.exists(file_name):
-            raise IOError("File not found: {0}", file_name)
-
-        cmd = "{0} x509 -in {1} -pubkey -noout".format(self.openssl_cmd, 
-                                                       file_name)
-        pub = shellutil.run_get_output(cmd)[1]
-        return pub
+            raise IOError(errno.ENOENT, "File not found", file_name)
+        else:
+            cmd = "{0} x509 -in {1} -pubkey -noout".format(self.openssl_cmd,
+                                                           file_name)
+            pub = shellutil.run_get_output(cmd)[1]
+            return pub
 
     def get_thumbprint_from_crt(self, file_name):
         if not os.path.exists(file_name):
-            raise IOError("File not found: {0}", file_name)
-
-        cmd = "{0} x509 -in {1} -fingerprint -noout".format(self.openssl_cmd,
-                                                            file_name)
-        thumbprint = shellutil.run_get_output(cmd)[1]
-        thumbprint = thumbprint.rstrip().split('=')[1].replace(':', '').upper()
-        return thumbprint
+            raise IOError(errno.ENOENT, "File not found", file_name)
+        else:
+            cmd = "{0} x509 -in {1} -fingerprint -noout".format(self.openssl_cmd,
+                                                                file_name)
+            thumbprint = shellutil.run_get_output(cmd)[1]
+            thumbprint = thumbprint.rstrip().split('=')[1].replace(':', '').upper()
+            return thumbprint
 
     def decrypt_p7m(self, p7m_file, trans_prv_file, trans_cert_file, pem_file):
         if not os.path.exists(p7m_file):
-            raise IOError("File not found: {0}", p7m_file)
-
-        if not os.path.exists(trans_prv_file):
-            raise IOError("File not found: {0}", trans_prv_file)
-
-        cmd = ("{0} cms -decrypt -in {1} -inkey {2} -recip {3} "
-               "| {4} pkcs12 -nodes -password pass: -out {5}"
-               "").format(self.openssl_cmd, p7m_file, trans_prv_file, 
-                          trans_cert_file, self.openssl_cmd, pem_file)
-        shellutil.run(cmd)
-        rc = shellutil.run(cmd)
-        if rc != 0:
-            logger.error("Failed to decrypt {0}".format(p7m_file))
+            raise IOError(errno.ENOENT, "File not found", p7m_file)
+        elif not os.path.exists(trans_prv_file):
+            raise IOError(errno.ENOENT, "File not found", trans_prv_file)
+        else:
+            cmd = ("{0} cms -decrypt -in {1} -inkey {2} -recip {3} "
+                   "| {4} pkcs12 -nodes -password pass: -out {5}"
+                   "").format(self.openssl_cmd, p7m_file, trans_prv_file,
+                              trans_cert_file, self.openssl_cmd, pem_file)
+            shellutil.run(cmd)
+            rc = shellutil.run(cmd)
+            if rc != 0:
+                logger.error("Failed to decrypt {0}".format(p7m_file))
 
     def crt_to_ssh(self, input_file, output_file):
         shellutil.run("ssh-keygen -i -m PKCS8 -f {0} >> {1}".format(input_file,


Test patch:
diff --git a/tests/protocol/test_wire.py b/tests/protocol/test_wire.py
index 030f5418..64a6abed 100644
--- a/tests/protocol/test_wire.py
+++ b/tests/protocol/test_wire.py
@@ -299,6 +299,27 @@ class TestWireProtocol(AgentTestCase):
 
             host_plugin_get_artifact_url_and_headers.assert_called_with(testurl)
 
+    @patch("azurelinuxagent.common.event.add_event")
+    def test_artifacts_profile_json_parsing(self, patch_event, *args):
+        wire_protocol_client = WireProtocol(wireserver_url).client
+        wire_protocol_client.ext_conf = ExtensionsConfig(None)
+        wire_protocol_client.ext_conf.artifacts_profile_blob = testurl
+        goal_state = GoalState(WireProtocolData(DATA_FILE).goal_state)
+        wire_protocol_client.get_goal_state = Mock(return_value=goal_state)
+
+        # response is invalid json
+        wire_protocol_client.call_storage_service = Mock(return_value=MockResponse("invalid json".encode('utf-8'), 200))
+        in_vm_artifacts_profile = wire_protocol_client.get_artifacts_profile()
+
+        # ensure response is empty
+        self.assertEqual(None, in_vm_artifacts_profile)
+
+        # ensure event is logged
+        self.assertEqual(1, patch_event.call_count)
+        self.assertFalse(patch_event.call_args[1]['is_success'])
+        self.assertTrue('invalid json' in patch_event.call_args[1]['message'])
+        self.assertEqual('ArtifactsProfileBlob', patch_event.call_args[1]['op'])
+
     def test_get_in_vm_artifacts_profile_default(self, *args):
         wire_protocol_client = WireProtocol(wireserver_url).client
         wire_protocol_client.ext_conf = ExtensionsConfig(None)