Repository: Azure/WALinuxAgent
Pull request #1756 (2020-01-14 23:06:58)

Fix singleton per thread
<!-- DO NOT DELETE THIS TEMPLATE -->

## Description
Currently the ProtocolUtil for the MonitorThread and the EnvThread is being initialized in the ExtHandlerThread (MainThread). Due to the SingletonPerThread approach, this would face a problem as all the different threads would still have the same ProtocolUtil object (and subsequently the same Protocol object) which would cause concurrency issues.

This PR addresses this issue and ensures that each thread has its own protocol util object.

Issue # <!-- if any -->
<!--
Please add an informative description that covers that changes made by the pull request. 
This checklist is used to make sure that common issues in a pull request are addressed.
This will expedite the process of getting your pull request merged and avoid extra work on your part to fix issues discovered during the review process.
-->

---

### PR information
- [ ] The title of the PR is clear and informative.
- [ ] There are a small number of commits, each of which has an informative message. This means that previously merged commits do not appear in the history of the PR. For information on cleaning up the commits in your pull request, [see this page](https://github.com/Azure/azure-powershell/blob/master/documentation/development-docs/cleaning-up-commits.md).
- [ ] Except for special cases involving multiple contributors, the PR is started from a fork of the main repository, not a branch.
- [ ] If applicable, the PR references the bug/issue that it fixes in the description.
- [ ] New Unit tests were added for the changes made and Travis.CI is passing.

### Quality of Code and Contribution Guidelines
- [ ] I have read the [contribution guidelines](https://github.com/Azure/WALinuxAgent/blob/master/.github/CONTRIBUTING.md).

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/azure/walinuxagent/1756)
<!-- Reviewable:end -->


codecov[bot]: # [Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/1756?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`develop@fbcbab7`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/Azure/WALinuxAgent/pull/1756/graphs/tree.svg?width=650&token=ae90E2KYCw&height=150&src=pr)](https://codecov.io/gh/Azure/WALinuxAgent/pull/1756?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##             develop    #1756   +/-   ##
==========================================
  Coverage           ?   69.06%           
==========================================
  Files              ?       82           
  Lines              ?    11744           
  Branches           ?     1647           
==========================================
  Hits               ?     8111           
  Misses             ?     3287           
  Partials           ?      346
```


| [Impacted Files](https://codecov.io/gh/Azure/WALinuxAgent/pull/1756?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [azurelinuxagent/ga/monitor.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/1756/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL21vbml0b3IucHk=) | `94.02% <100%> (ø)` | |
| [azurelinuxagent/ga/env.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/1756/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL2Vudi5weQ==) | `72.89% <100%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/1756?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/1756?src=pr&el=footer). Last update [fbcbab7...9deeeec](https://codecov.io/gh/Azure/WALinuxAgent/pull/1756?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/azurelinuxagent/common/protocol/hostplugin.py b/azurelinuxagent/common/protocol/hostplugin.py
index ca1742c2..84e5ee1d 100644
--- a/azurelinuxagent/common/protocol/hostplugin.py
+++ b/azurelinuxagent/common/protocol/hostplugin.py
@@ -77,6 +77,15 @@ class HostPluginProtocol(object):
     def set_default_channel(is_default):
         HostPluginProtocol._is_default_channel = is_default
 
+    def update_container_id(self, new_container_id):
+        self.container_id = new_container_id
+
+    def update_role_config_name(self, new_role_config_name):
+        self.role_config_name = new_role_config_name
+
+    def update_manifest_uri(self, new_manifest_uri):
+        self.manifest_uri = new_manifest_uri
+
     def ensure_initialized(self):
         if not self.is_initialized:
             self.api_versions = self.get_api_versions()
diff --git a/azurelinuxagent/common/protocol/util.py b/azurelinuxagent/common/protocol/util.py
index dceb60ec..803caa5f 100644
--- a/azurelinuxagent/common/protocol/util.py
+++ b/azurelinuxagent/common/protocol/util.py
@@ -175,7 +175,7 @@ class ProtocolUtil(SingletonPerThread):
                     logger.info("WireServer endpoint {0} read from file", self.endpoint)
                     return self.endpoint
 
-                logger.error("[GetWireserverEndpoint] Unexpected empty file {0}: {1}", file_path, str(e))
+                logger.error("[GetWireserverEndpoint] Unexpected empty file {0}", file_path)
             except (IOError, OSError) as e:
                 logger.error("[GetWireserverEndpoint] Error reading file {0}: {1}", file_path, str(e))
         else:
diff --git a/azurelinuxagent/common/protocol/wire.py b/azurelinuxagent/common/protocol/wire.py
index b65746e2..a84a09a2 100644
--- a/azurelinuxagent/common/protocol/wire.py
+++ b/azurelinuxagent/common/protocol/wire.py
@@ -61,7 +61,6 @@ P7M_FILE_NAME = "Certificates.p7m"
 PEM_FILE_NAME = "Certificates.pem"
 EXT_CONF_FILE_NAME = "ExtensionsConfig.{0}.xml"
 MANIFEST_FILE_NAME = "{0}.{1}.manifest.xml"
-AGENTS_MANIFEST_FILE_NAME = "{0}.{1}.agentsManifest"
 TRANSPORT_CERT_FILE_NAME = "TransportCert.pem"
 TRANSPORT_PRV_FILE_NAME = "TransportPrivate.pem"
 # Store the last retrieved container id as an environment variable to be shared between threads for telemetry purposes
@@ -104,7 +103,7 @@ class WireProtocol(Protocol):
         self.client.update_goal_state()
 
     def get_endpoint(self):
-        return self.client.endpoint
+        return self.client.get_endpoint()
 
     def get_vminfo(self):
         goal_state = self.client.get_goal_state()
@@ -123,11 +122,7 @@ class WireProtocol(Protocol):
         return certificates.cert_list
 
     def get_incarnation(self):
-        path = os.path.join(conf.get_lib_dir(), INCARNATION_FILE_NAME)
-        if os.path.exists(path):
-            return fileutil.read_file(path)
-        else:
-            return 0
+        return self.client.get_goal_state().incarnation
 
     def get_vmagent_manifests(self):
         goal_state = self.client.get_goal_state()
@@ -137,7 +132,7 @@ class WireProtocol(Protocol):
     def get_vmagent_pkgs(self, vmagent_manifest):
         goal_state = self.client.get_goal_state()
         ga_manifest = self.client.get_gafamily_manifest(vmagent_manifest, goal_state)
-        valid_pkg_list = self.client.filter_package_list(vmagent_manifest.family, ga_manifest, goal_state)
+        valid_pkg_list = ga_manifest.pkg_list
         return valid_pkg_list
 
     def get_ext_handlers(self):
@@ -528,17 +523,20 @@ class WireClient(object):
 
     def __init__(self, endpoint):
         logger.info("Wire server endpoint:{0}", endpoint)
-        self.endpoint = endpoint
-        self.goal_state = None
-        self.hosting_env = None
-        self.shared_conf = None
-        self.remote_access = None
-        self.certs = None
-        self.ext_conf = None
-        self.host_plugin = None
+        self._endpoint = endpoint
+        self._goal_state = None
+        self._hosting_env = None
+        self._shared_conf = None
+        self._remote_access = None
+        self._certs = None
+        self._ext_conf = None
+        self._host_plugin = None
         self.status_blob = StatusBlob(self)
         self.goal_state_flusher = StateFlusher(conf.get_lib_dir())
 
+    def get_endpoint(self):
+        return self._endpoint
+
     def call_wireserver(self, http_req, *args, **kwargs):
         try:
             # Never use the HTTP proxy for wireserver
@@ -625,7 +623,7 @@ class WireClient(object):
 
                 if response:
                     host = self.get_host_plugin()
-                    host.manifest_uri = version.uri
+                    host.update_manifest_uri(version.uri)
                     return response
             except Exception as e:
                 logger.warn("Exception when fetching manifest. Error: {0}".format(ustr(e)))
@@ -671,6 +669,7 @@ class WireClient(object):
                 use_proxy=use_proxy)
 
             host_plugin = self.get_host_plugin()
+
             if restutil.request_failed(resp):
                 error_response = restutil.read_response_error(resp)
                 msg = "Fetch failed from [{0}]: {1}".format(uri, error_response)
@@ -678,9 +677,9 @@ class WireClient(object):
 
                 if host_plugin is not None:
                     host_plugin.report_fetch_health(uri,
-                                                     is_healthy=not restutil.request_failed_at_hostplugin(resp),
-                                                     source='WireClient',
-                                                     response=error_response)
+                                                    is_healthy=not restutil.request_failed_at_hostplugin(resp),
+                                                    source='WireClient',
+                                                    response=error_response)
                 raise ProtocolError(msg)
             else:
                 if host_plugin is not None:
@@ -699,7 +698,7 @@ class WireClient(object):
         xml_text = self.fetch_config(goal_state.hosting_env_uri,
                                      self.get_header())
         self.save_cache(local_file, xml_text)
-        self.hosting_env = HostingEnv(xml_text)
+        self._set_hosting_env(HostingEnv(xml_text))
 
     def update_shared_conf(self, goal_state):
         if goal_state.shared_conf_uri is None:
@@ -708,7 +707,7 @@ class WireClient(object):
         xml_text = self.fetch_config(goal_state.shared_conf_uri,
                                      self.get_header())
         self.save_cache(local_file, xml_text)
-        self.shared_conf = SharedConfig(xml_text)
+        self._set_shared_conf(SharedConfig(xml_text))
 
     def update_certs(self, goal_state):
         if goal_state.certs_uri is None:
@@ -717,7 +716,7 @@ class WireClient(object):
         xml_text = self.fetch_config(goal_state.certs_uri,
                                      self.get_header_for_cert())
         self.save_cache(local_file, xml_text)
-        self.certs = Certificates(self, xml_text)
+        self._set_certs(Certificates(self, xml_text))
 
     def update_remote_access_conf(self, goal_state):
         if goal_state.remote_access_uri is None:
@@ -725,34 +724,26 @@ class WireClient(object):
             return
         xml_text = self.fetch_config(goal_state.remote_access_uri,
                                      self.get_header_for_cert())
-        self.remote_access = RemoteAccess(xml_text)
-        local_file = os.path.join(conf.get_lib_dir(), REMOTE_ACCESS_FILE_NAME.format(self.remote_access.incarnation))
+        remote_access = RemoteAccess(xml_text)
+        local_file = os.path.join(conf.get_lib_dir(), REMOTE_ACCESS_FILE_NAME.format(remote_access.incarnation))
         self.save_cache(local_file, xml_text)
+        self._set_remote_access(remote_access)
 
     def get_remote_access(self):
-        incarnation_file = os.path.join(conf.get_lib_dir(),
-                                        INCARNATION_FILE_NAME)
-        incarnation = self.fetch_cache(incarnation_file)
-        file_name = REMOTE_ACCESS_FILE_NAME.format(incarnation)
-        remote_access_file = os.path.join(conf.get_lib_dir(), file_name)
-        if not os.path.isfile(remote_access_file):
-            # no remote access data.
-            return None
-        xml_text = self.fetch_cache(remote_access_file)
-        remote_access = RemoteAccess(xml_text)
-        return remote_access
+        # This property can be None in the GoalState, so not raising a ProtocolError here
+        return self._remote_access
 
     def update_ext_conf(self, goal_state):
         if goal_state.ext_uri is None:
             logger.info("ExtensionsConfig.xml uri is empty")
-            self.ext_conf = ExtensionsConfig(None)
+            self._set_ext_conf(ExtensionsConfig(None))
             return
         incarnation = goal_state.incarnation
         local_file = os.path.join(conf.get_lib_dir(),
                                   EXT_CONF_FILE_NAME.format(incarnation))
         xml_text = self.fetch_config(goal_state.ext_uri, self.get_header())
         self.save_cache(local_file, xml_text)
-        self.ext_conf = ExtensionsConfig(xml_text)
+        self._set_ext_conf(ExtensionsConfig(xml_text))
 
     # Type of update performed by _update_from_goal_state()
     class _UpdateType(object):
@@ -773,14 +764,14 @@ class WireClient(object):
         """
         Updates the goal state if the incarnation changed or if 'forced' is True
         """
-        self._update_from_goal_state(WireClient._UpdateType.GoalStateForced if forced else WireClient._UpdateType.GoalState)
+        self._update_from_goal_state(
+            WireClient._UpdateType.GoalStateForced if forced else WireClient._UpdateType.GoalState)
 
     def _update_from_goal_state(self, refresh_type):
         """
         Fetches a new goal state and updates the internal state of the WireClient according to the requested 'refresh_type'
         """
-        incarnation_file = os.path.join(conf.get_lib_dir(), INCARNATION_FILE_NAME)
-        uri = GOAL_STATE_URI.format(self.endpoint)
+        uri = GOAL_STATE_URI.format(self.get_endpoint())
 
         max_retry = 3
 
@@ -790,35 +781,30 @@ class WireClient(object):
                 new_goal_state = GoalState(new_goal_state_xml)
 
                 def update_host_plugin():
-                    if self.host_plugin is not None:
-                        self.host_plugin.container_id = new_goal_state.container_id
-                        self.host_plugin.role_config_name = new_goal_state.role_config_name
+                    if self._host_plugin is not None:
+                        self._host_plugin.update_container_id(new_goal_state.container_id)
+                        self._host_plugin.update_role_config_name(new_goal_state.role_config_name)
 
                 if refresh_type == WireClient._UpdateType.HostPlugin:
                     update_host_plugin()
                     return
 
-                def incarnation_changed():
-                    last_incarnation = None
-                    if os.path.isfile(incarnation_file):
-                        last_incarnation = fileutil.read_file(incarnation_file)
-                    return last_incarnation is None or last_incarnation != new_goal_state.incarnation
-
-                if refresh_type == WireClient._UpdateType.GoalStateForced or incarnation_changed() or self.goal_state is None:
+                if self._goal_state is None or refresh_type == WireClient._UpdateType.GoalStateForced or \
+                        new_goal_state.incarnation != self._goal_state.incarnation:
                     def save_goal_state(incarnation, xml_text):
                         file_name = GOAL_STATE_FILE_NAME.format(incarnation)
                         goal_state_file = os.path.join(conf.get_lib_dir(), file_name)
                         self.save_cache(goal_state_file, xml_text)
 
                     self.goal_state_flusher.flush(datetime.utcnow())
-                    self.goal_state = new_goal_state
+                    self._set_goal_state(new_goal_state)
                     save_goal_state(new_goal_state.incarnation, new_goal_state_xml)
                     self.update_hosting_env(new_goal_state)
                     self.update_shared_conf(new_goal_state)
                     self.update_certs(new_goal_state)
                     self.update_ext_conf(new_goal_state)
                     self.update_remote_access_conf(new_goal_state)
-                    self.save_cache(incarnation_file, new_goal_state.incarnation)
+                    self.save_cache(os.path.join(conf.get_lib_dir(), INCARNATION_FILE_NAME), new_goal_state.incarnation)
 
                     update_host_plugin()
                 return
@@ -837,70 +823,67 @@ class WireClient(object):
 
         raise ProtocolError("Exceeded max retry updating goal state")
 
-    def get_goal_state(self):
-        if self.goal_state is None:
-            incarnation_file = os.path.join(conf.get_lib_dir(),
-                                            INCARNATION_FILE_NAME)
-            incarnation = self.fetch_cache(incarnation_file)
+    def _set_goal_state(self, new_goal_state):
+        if new_goal_state is None:
+            logger.warn("Setting empty Goal State object!")
+        self._goal_state = new_goal_state
+
+    def _set_hosting_env(self, new_hosting_env):
+        if new_hosting_env is None:
+            logger.warn("Setting empty Hosting Environment object!")
+        self._hosting_env = new_hosting_env
+
+    def _set_shared_conf(self, new_shared_config):
+        if new_shared_config is None:
+            logger.warn("Setting empty Shared Config object!")
+        self._shared_conf = new_shared_config
+
+    def _set_certs(self, new_certs):
+        if new_certs is None:
+            logger.warn("Setting empty Certs object!")
+        self._certs = new_certs
+
+    def _set_ext_conf(self, new_ext_conf):
+        if new_ext_conf is None:
+            logger.warn("Setting empty Extension Config object!")
+        self._ext_conf = new_ext_conf
+
+    def _set_remote_access(self, new_remote_access):
+        if new_remote_access is None:
+            logger.warn("Setting empty Remote Access object!")
+        self._remote_access = new_remote_access
+
+    def _set_host_plugin(self, new_host_plugin):
+        if new_host_plugin is None:
+            logger.warn("Setting empty Host Plugin object!")
+        self._host_plugin = new_host_plugin
 
-            file_name = GOAL_STATE_FILE_NAME.format(incarnation)
-            goal_state_file = os.path.join(conf.get_lib_dir(), file_name)
-            xml_text = self.fetch_cache(goal_state_file)
-            self.goal_state = GoalState(xml_text)
-        return self.goal_state
+    def get_goal_state(self):
+        if self._goal_state is None:
+            raise ProtocolError("Trying to fetch goal state before initialization!")
+        return self._goal_state
 
     def get_hosting_env(self):
-        if self.hosting_env is None:
-            local_file = os.path.join(conf.get_lib_dir(),
-                                      HOSTING_ENV_FILE_NAME)
-            xml_text = self.fetch_cache(local_file)
-            self.hosting_env = HostingEnv(xml_text)
-        return self.hosting_env
+        if self._hosting_env is None:
+            raise ProtocolError("Trying to fetch Hosting Environment before initialization!")
+        return self._hosting_env
 
     def get_shared_conf(self):
-        if self.shared_conf is None:
-            local_file = os.path.join(conf.get_lib_dir(),
-                                      SHARED_CONF_FILE_NAME)
-            xml_text = self.fetch_cache(local_file)
-            self.shared_conf = SharedConfig(xml_text)
-        return self.shared_conf
+        if self._shared_conf is None:
+            raise ProtocolError("Trying to fetch Shared Conf before initialization!")
+        return self._shared_conf
 
     def get_certs(self):
-        if self.certs is None:
-            local_file = os.path.join(conf.get_lib_dir(), CERTS_FILE_NAME)
-            xml_text = self.fetch_cache(local_file)
-            self.certs = Certificates(self, xml_text)
-        if self.certs is None:
-            return None
-        return self.certs
-
-    def get_current_handlers(self):
-        handler_list = list()
-        try:
-            incarnation = self.fetch_cache(os.path.join(conf.get_lib_dir(),
-                                                        INCARNATION_FILE_NAME))
-            ext_conf = ExtensionsConfig(self.fetch_cache(os.path.join(conf.get_lib_dir(),
-                                                                      EXT_CONF_FILE_NAME.format(incarnation))))
-            handler_list = ext_conf.ext_handlers.extHandlers
-        except ProtocolError as pe:
-            # cache file is missing, nothing to do
-            logger.verbose(ustr(pe))
-        except Exception as e:
-            logger.error("Could not obtain current handlers: {0}", ustr(e))
-
-        return handler_list
+        # This property can be None in the GoalState, so not returning a ProtocolError here
+        return self._certs
 
     def get_ext_conf(self):
-        if self.ext_conf is None:
-            local_goal_state = self.get_goal_state()
-            if local_goal_state.ext_uri is None:
-                self.ext_conf = ExtensionsConfig(None)
+        if self._ext_conf is None:
+            if self.get_goal_state().ext_uri is None:
+                self._set_ext_conf(ExtensionsConfig(None))
             else:
-                local_file = EXT_CONF_FILE_NAME.format(local_goal_state.incarnation)
-                local_file = os.path.join(conf.get_lib_dir(), local_file)
-                xml_text = self.fetch_cache(local_file)
-                self.ext_conf = ExtensionsConfig(xml_text)
-        return self.ext_conf
+                raise ProtocolError("Trying to fetch Extension Conf before initialization!")
+        return self._ext_conf
 
     def get_ext_manifest(self, ext_handler, goal_state):
         local_file = MANIFEST_FILE_NAME.format(ext_handler.name, goal_state.incarnation)
@@ -913,44 +896,7 @@ class WireClient(object):
         except Exception as e:
             raise ExtensionDownloadError("Failed to retrieve extension manifest. Error: {0}".format(ustr(e)))
 
-    def filter_package_list(self, family, ga_manifest, goal_state):
-        complete_list = ga_manifest.pkg_list
-        agent_manifest = os.path.join(conf.get_lib_dir(),
-                                      AGENTS_MANIFEST_FILE_NAME.format(
-                                          family,
-                                          goal_state.incarnation))
-
-        if not os.path.exists(agent_manifest):
-            # clear memory cache
-            ga_manifest.allowed_versions = None
-
-            # create disk cache
-            with open(agent_manifest, mode='w') as manifest_fh:
-                for version in complete_list.versions:
-                    manifest_fh.write('{0}\n'.format(version.version))
-            fileutil.chmod(agent_manifest, 0o644)
-
-            return complete_list
-
-        else:
-            # use allowed versions from cache, otherwise from disk
-            if ga_manifest.allowed_versions is None:
-                with open(agent_manifest, mode='r') as manifest_fh:
-                    ga_manifest.allowed_versions = [v.strip('\n') for v
-                                                    in manifest_fh.readlines()]
-
-            # use the updated manifest urls for allowed versions
-            allowed_list = ExtHandlerPackageList()
-            allowed_list.versions = [version for version
-                                     in complete_list.versions
-                                     if version.version
-                                     in ga_manifest.allowed_versions]
-
-            return allowed_list
-
     def get_gafamily_manifest(self, vmagent_manifest, goal_state):
-        self._remove_stale_agent_manifest(vmagent_manifest.family, goal_state.incarnation)
-
         local_file = MANIFEST_FILE_NAME.format(vmagent_manifest.family, goal_state.incarnation)
         local_file = os.path.join(conf.get_lib_dir(), local_file)
 
@@ -961,27 +907,8 @@ class WireClient(object):
         except Exception as e:
             raise ProtocolError("Failed to retrieve GAFamily manifest. Error: {0}".format(ustr(e)))
 
-    def _remove_stale_agent_manifest(self, family, incarnation):
-        """
-        The incarnation number can reset at any time, which means there
-        could be a stale agentsManifest on disk.  Stale files are cleaned
-        on demand as new goal states arrive from WireServer. If the stale
-        file is not removed agent upgrade may be delayed.
-
-        :param family: GA family, e.g. Prod or Test
-        :param incarnation: incarnation of the current goal state
-        """
-        fn = AGENTS_MANIFEST_FILE_NAME.format(
-            family,
-            incarnation)
-
-        agent_manifest = os.path.join(conf.get_lib_dir(), fn)
-
-        if os.path.exists(agent_manifest):
-            os.unlink(agent_manifest)
-
     def check_wire_protocol_version(self):
-        uri = VERSION_INFO_URI.format(self.endpoint)
+        uri = VERSION_INFO_URI.format(self.get_endpoint())
         version_info_xml = self.fetch_config(uri, None)
         version_info = VersionInfo(version_info_xml)
 
@@ -1145,7 +1072,7 @@ class WireClient(object):
                                            goal_state.role_instance_id,
                                            thumbprint)
         role_prop = role_prop.encode("utf-8")
-        role_prop_uri = ROLE_PROP_URI.format(self.endpoint)
+        role_prop_uri = ROLE_PROP_URI.format(self.get_endpoint())
         headers = self.get_header_for_xml_content()
         try:
             resp = self.call_wireserver(restutil.http_post,
@@ -1169,7 +1096,7 @@ class WireClient(object):
                                              substatus,
                                              description)
         health_report = health_report.encode("utf-8")
-        health_report_uri = HEALTH_REPORT_URI.format(self.endpoint)
+        health_report_uri = HEALTH_REPORT_URI.format(self.get_endpoint())
         headers = self.get_header_for_xml_content()
         try:
             # 30 retries with 10s sleep gives ~5min for wireserver updates;
@@ -1190,7 +1117,7 @@ class WireClient(object):
                                                       resp.read()))
 
     def send_event(self, provider_id, event_str):
-        uri = TELEMETRY_URI.format(self.endpoint)
+        uri = TELEMETRY_URI.format(self.get_endpoint())
         data_format = ('<?xml version="1.0"?>'
                        '<TelemetryData version="1.0">'
                        '<Provider id="{0}">{1}'
@@ -1269,16 +1196,17 @@ class WireClient(object):
         }
 
     def get_host_plugin(self):
-        if self.host_plugin is None:
+        if self._host_plugin is None:
             goal_state = self.get_goal_state()
-            self.host_plugin = HostPluginProtocol(self.endpoint,
-                                                  goal_state.container_id,
-                                                  goal_state.role_config_name)
-        return self.host_plugin
+            self._set_host_plugin(HostPluginProtocol(self.get_endpoint(),
+                                                     goal_state.container_id,
+                                                     goal_state.role_config_name))
+        return self._host_plugin
 
     def has_artifacts_profile_blob(self):
-        return self.ext_conf and not \
-            textutil.is_str_none_or_whitespace(self.ext_conf.artifacts_profile_blob)
+        ext_conf = self.get_ext_conf()
+        return ext_conf and not \
+            textutil.is_str_none_or_whitespace(ext_conf.artifacts_profile_blob)
 
     def get_artifacts_profile_through_host(self, blob):
         host = self.get_host_plugin()
@@ -1290,7 +1218,7 @@ class WireClient(object):
         artifacts_profile = None
 
         if self.has_artifacts_profile_blob():
-            blob = self.ext_conf.artifacts_profile_blob
+            blob = self.get_ext_conf().artifacts_profile_blob
             direct_func = lambda: self.fetch(blob)
             # NOTE: the host_func may be called after refreshing the goal state, be careful about any goal state data
             # in the lambda.
@@ -1719,7 +1647,6 @@ class ExtensionManifest(object):
             raise ValueError("ExtensionManifest is None")
         logger.verbose("Load ExtensionManifest.xml")
         self.pkg_list = ExtHandlerPackageList()
-        self.allowed_versions = None
         self._parse(xml_text)
 
     def _parse(self, xml_text):
diff --git a/azurelinuxagent/common/rdma.py b/azurelinuxagent/common/rdma.py
index 49d8c289..72e6ea3f 100644
--- a/azurelinuxagent/common/rdma.py
+++ b/azurelinuxagent/common/rdma.py
@@ -29,19 +29,16 @@ import azurelinuxagent.common.utils.fileutil as fileutil
 import azurelinuxagent.common.utils.shellutil as shellutil
 from azurelinuxagent.common.utils.textutil import parse_doc, find, getattrib
 
-
-from azurelinuxagent.common.protocol.wire import SHARED_CONF_FILE_NAME
-
 dapl_config_paths = [
     '/etc/dat.conf',
     '/etc/rdma/dat.conf',
     '/usr/local/etc/dat.conf'
 ]
 
-def setup_rdma_device(nd_version):
+
+def setup_rdma_device(nd_version, shared_conf):
     logger.verbose("Parsing SharedConfig XML contents for RDMA details")
-    xml_doc = parse_doc(
-        fileutil.read_file(os.path.join(conf.get_lib_dir(), SHARED_CONF_FILE_NAME)))
+    xml_doc = parse_doc(shared_conf.xml_text)
     if xml_doc is None:
         logger.error("Could not parse SharedConfig XML document")
         return
@@ -64,7 +61,7 @@ def setup_rdma_device(nd_version):
 
     # add colons to the MAC address (e.g. 00155D33FF1D ->
     # 00:15:5D:33:FF:1D)
-    rdma_mac_addr = ':'.join([rdma_mac_addr[i:i+2]
+    rdma_mac_addr = ':'.join([rdma_mac_addr[i:i + 2]
                               for i in range(0, len(rdma_mac_addr), 2)])
     logger.info("Found RDMA details. IPv4={0} MAC={1}".format(
         rdma_ipv4_addr, rdma_mac_addr))
diff --git a/azurelinuxagent/daemon/main.py b/azurelinuxagent/daemon/main.py
index 91f39fea..d328daf9 100644
--- a/azurelinuxagent/daemon/main.py
+++ b/azurelinuxagent/daemon/main.py
@@ -160,7 +160,7 @@ class DaemonHandler(object):
                     raise Exception("Attempt to setup RDMA without Wireserver")
                 protocol.client.update_goal_state(forced=True)
 
-                setup_rdma_device(nd_version)
+                setup_rdma_device(nd_version, protocol.client.get_shared_conf())
             except Exception as e:
                 logger.error("Error setting up rdma device: %s" % e)
         else:
diff --git a/azurelinuxagent/ga/env.py b/azurelinuxagent/ga/env.py
index bb8c486c..371fa38b 100644
--- a/azurelinuxagent/ga/env.py
+++ b/azurelinuxagent/ga/env.py
@@ -61,7 +61,7 @@ class EnvHandler(object):
     def __init__(self):
         self.osutil = get_osutil()
         self.dhcp_handler = get_dhcp_handler()
-        self.protocol_util = get_protocol_util()
+        self.protocol_util = None
         self.stopped = True
         self.hostname = None
         self.dhcp_id_list = []
@@ -98,6 +98,11 @@ class EnvHandler(object):
         If dhcp client process re-start has occurred, reset routes.
         Purge unnecessary files from disk cache.
         """
+
+        # The initialization of ProtocolUtil for the Environment thread should be done within the thread itself rather
+        # than initializing it in the ExtHandler thread. This is done to avoid any concurrency issues as each
+        # thread would now have its own ProtocolUtil object as per the SingletonPerThread model.
+        self.protocol_util = get_protocol_util()
         protocol = self.protocol_util.get_protocol()
         reset_firewall_fules = False
         while not self.stopped:
diff --git a/azurelinuxagent/ga/monitor.py b/azurelinuxagent/ga/monitor.py
index 77ed176e..bf828d0e 100644
--- a/azurelinuxagent/ga/monitor.py
+++ b/azurelinuxagent/ga/monitor.py
@@ -118,7 +118,6 @@ class MonitorHandler(object):
 
     def __init__(self):
         self.osutil = get_osutil()
-        self.protocol_util = get_protocol_util()
         self.imds_client = get_imds_client()
 
         self.event_thread = None
@@ -130,6 +129,7 @@ class MonitorHandler(object):
         self.last_host_plugin_heartbeat = None
         self.last_imds_heartbeat = None
         self.protocol = None
+        self.protocol_util = None
         self.health_service = None
         self.last_route_table_hash = b''
         self.last_nic_state = {}
@@ -142,9 +142,7 @@ class MonitorHandler(object):
         self.imds_errorstate = ErrorState(min_timedelta=MonitorHandler.IMDS_HEALTH_PERIOD)
 
     def run(self):
-        self.init_protocols()
-        self.init_sysinfo()
-        self.start()
+        self.start(init_data=True)
 
     def stop(self):
         self.should_run = False
@@ -152,14 +150,20 @@ class MonitorHandler(object):
             self.event_thread.join()
 
     def init_protocols(self):
+        # The initialization of ProtocolUtil for the Monitor thread should be done within the thread itself rather
+        # than initializing it in the ExtHandler thread. This is done to avoid any concurrency issues as each
+        # thread would now have its own ProtocolUtil object as per the SingletonPerThread model.
+        self.protocol_util = get_protocol_util()
         self.protocol = self.protocol_util.get_protocol()
+        # Update the GoalState first time to instantiate all required objects for the monitor thread
+        self.protocol.update_goal_state()
         self.health_service = HealthService(self.protocol.get_endpoint())
 
     def is_alive(self):
         return self.event_thread is not None and self.event_thread.is_alive()
 
-    def start(self):
-        self.event_thread = threading.Thread(target=self.daemon)
+    def start(self, init_data=False):
+        self.event_thread = threading.Thread(target=self.daemon, args=(init_data,))
         self.event_thread.setDaemon(True)
         self.event_thread.setName("MonitorHandler")
         self.event_thread.start()
@@ -269,7 +273,12 @@ class MonitorHandler(object):
 
             self.last_event_collection = datetime.datetime.utcnow()
 
-    def daemon(self):
+    def daemon(self, init_data=False):
+
+        if init_data:
+            self.init_protocols()
+            self.init_sysinfo()
+
         min_delta = min(MonitorHandler.TELEMETRY_HEARTBEAT_PERIOD,
                         MonitorHandler.CGROUP_TELEMETRY_POLLING_PERIOD,
                         MonitorHandler.CGROUP_TELEMETRY_REPORTING_PERIOD,
diff --git a/azurelinuxagent/ga/remoteaccess.py b/azurelinuxagent/ga/remoteaccess.py
index 429f11fb..94b76646 100644
--- a/azurelinuxagent/ga/remoteaccess.py
+++ b/azurelinuxagent/ga/remoteaccess.py
@@ -28,7 +28,7 @@ import azurelinuxagent.common.logger as logger
 from datetime import datetime, timedelta
 
 from azurelinuxagent.common.event import add_event, WALAEventOperation
-from azurelinuxagent.common.exception import RemoteAccessError
+from azurelinuxagent.common.exception import RemoteAccessError, ProtocolError
 from azurelinuxagent.common.future import ustr
 from azurelinuxagent.common.utils.cryptutil import CryptUtil
 from azurelinuxagent.common.version import AGENT_NAME, CURRENT_VERSION


Test patch:
diff --git a/tests/ga/test_extension.py b/tests/ga/test_extension.py
index c5719d27..58e636a5 100644
--- a/tests/ga/test_extension.py
+++ b/tests/ga/test_extension.py
@@ -21,6 +21,8 @@ import unittest
 
 from datetime import timedelta
 
+from azurelinuxagent.common.protocol.util import get_protocol_util
+
 from azurelinuxagent.ga.monitor import get_monitor_handler
 from nose.plugins.attrib import attr
 from tests.protocol import mockwiredata
@@ -2228,7 +2230,9 @@ class TestExtensionWithCGroupsEnabled(AgentTestCase):
 
         ext_handler = get_exthandlers_handler(protocol)
 
-        monitor_handler.protocol_util.get_protocol = Mock(return_value=protocol)
+        protocol_util = get_protocol_util()
+        protocol_util.get_protocol = Mock(return_value=protocol)
+        monitor_handler.protocol_util = Mock(return_value=protocol_util)
         return ext_handler, monitor_handler, protocol
 
     @attr('requires_sudo')
diff --git a/tests/ga/test_monitor.py b/tests/ga/test_monitor.py
index dcd3f4b2..ed9202f0 100644
--- a/tests/ga/test_monitor.py
+++ b/tests/ga/test_monitor.py
@@ -26,7 +26,7 @@ import tempfile
 import time
 from datetime import timedelta
 
-from azurelinuxagent.common.protocol.util import ProtocolUtil
+from azurelinuxagent.common.protocol.util import ProtocolUtil, get_protocol_util
 from nose.plugins.attrib import attr
 
 import azurelinuxagent.common.conf as conf
@@ -540,7 +540,9 @@ class TestEventMonitoring(AgentTestCase):
         protocol.report_ext_status = MagicMock()
         protocol.report_vm_status = MagicMock()
 
-        monitor_handler.protocol_util.get_protocol = Mock(return_value=protocol)
+        protocol_util = get_protocol_util()
+        protocol_util.get_protocol = Mock(return_value=protocol)
+        monitor_handler.protocol_util = Mock(return_value=protocol_util)
         return monitor_handler, protocol
 
     @patch("azurelinuxagent.common.protocol.imds.ImdsClient.get_compute",
@@ -621,7 +623,7 @@ class TestEventMonitoring(AgentTestCase):
                 # list.
 
                 # The container id value is obtained from the goal state and must be present in all telemetry events.
-                container_id_param = TelemetryEventParam("ContainerId", protocol.client.goal_state.container_id)
+                container_id_param = TelemetryEventParam("ContainerId", protocol.client.get_goal_state().container_id)
                 self.assertTrue(container_id_param in event.parameters)
 
                 # Same for GAVersion
@@ -850,7 +852,6 @@ class TestEventMonitoring(AgentTestCase):
 @patch('azurelinuxagent.common.osutil.get_osutil')
 @patch('azurelinuxagent.common.protocol.get_protocol_util')
 @patch("azurelinuxagent.common.protocol.healthservice.HealthService._report")
-@patch('azurelinuxagent.common.protocol.util.ProtocolUtil.get_protocol', return_value=WireProtocol('endpoint'))
 @patch("azurelinuxagent.common.utils.restutil.http_get")
 class TestExtensionMetricsDataTelemetry(AgentTestCase):
 
@@ -858,10 +859,16 @@ class TestExtensionMetricsDataTelemetry(AgentTestCase):
         AgentTestCase.setUp(self)
         event.init_event_logger(os.path.join(self.tmp_dir, "events"))
         CGroupsTelemetry.reset()
+        clear_singleton_instances(ProtocolUtil)
+        protocol = WireProtocol('endpoint')
+        protocol.update_goal_state = MagicMock()
+        self.get_protocol = patch('azurelinuxagent.common.protocol.util.ProtocolUtil.get_protocol', return_value=protocol)
+        self.get_protocol.start()
 
     def tearDown(self):
         AgentTestCase.tearDown(self)
         CGroupsTelemetry.reset()
+        self.get_protocol.stop()
 
     @patch('azurelinuxagent.common.event.EventLogger.add_metric')
     @patch('azurelinuxagent.common.event.EventLogger.add_event')
@@ -1238,32 +1245,32 @@ for i in range(3):
         self.assertEqual(message_json, None)
 
 
-@patch('azurelinuxagent.common.event.EventLogger.add_event')
 @patch("azurelinuxagent.common.utils.restutil.http_post")
-@patch("azurelinuxagent.common.utils.restutil.http_get")
 @patch('azurelinuxagent.common.protocol.wire.WireClient.get_goal_state')
-@patch('azurelinuxagent.common.protocol.util.ProtocolUtil.get_protocol', return_value=WireProtocol('endpoint'))
+@patch('azurelinuxagent.common.event.EventLogger.add_event')
+@patch("azurelinuxagent.common.utils.restutil.http_get")
 class TestMonitorFailure(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.healthservice.HealthService.report_host_plugin_heartbeat")
-    def test_error_heartbeat_creates_no_signal(self, patch_report_heartbeat, *args):
-        patch_http_get = args[2]
-        patch_add_event = args[4]
+    def test_error_heartbeat_creates_no_signal(self, patch_report_heartbeat, patch_http_get, patch_add_event, *args):
 
         monitor_handler = get_monitor_handler()
-        monitor_handler.init_protocols()
-        monitor_handler.last_host_plugin_heartbeat = datetime.datetime.utcnow() - timedelta(hours=1)
+        protocol = WireProtocol('endpoint')
+        protocol.update_goal_state = MagicMock()
+        with patch('azurelinuxagent.common.protocol.util.ProtocolUtil.get_protocol', return_value=protocol):
+            monitor_handler.init_protocols()
+            monitor_handler.last_host_plugin_heartbeat = datetime.datetime.utcnow() - timedelta(hours=1)
 
-        patch_http_get.side_effect = IOError('client error')
-        monitor_handler.send_host_plugin_heartbeat()
+            patch_http_get.side_effect = IOError('client error')
+            monitor_handler.send_host_plugin_heartbeat()
 
-        # health report should not be made
-        self.assertEqual(0, patch_report_heartbeat.call_count)
+            # health report should not be made
+            self.assertEqual(0, patch_report_heartbeat.call_count)
 
-        # telemetry with failure details is sent
-        self.assertEqual(1, patch_add_event.call_count)
-        self.assertEqual('HostPluginHeartbeat', patch_add_event.call_args[1]['op'])
-        self.assertTrue('client error' in patch_add_event.call_args[1]['message'])
+            # telemetry with failure details is sent
+            self.assertEqual(1, patch_add_event.call_count)
+            self.assertEqual('HostPluginHeartbeat', patch_add_event.call_args[1]['op'])
+            self.assertTrue('client error' in patch_add_event.call_args[1]['message'])
 
-        self.assertEqual(False, patch_add_event.call_args[1]['is_success'])
-        monitor_handler.stop()
+            self.assertEqual(False, patch_add_event.call_args[1]['is_success'])
+            monitor_handler.stop()
diff --git a/tests/ga/test_remoteaccess.py b/tests/ga/test_remoteaccess.py
index c0d0cfc6..05ee25f6 100644
--- a/tests/ga/test_remoteaccess.py
+++ b/tests/ga/test_remoteaccess.py
@@ -84,10 +84,10 @@ class TestRemoteAccess(AgentTestCase):
         protocol = WireProtocol('12.34.56.78')
         goal_state = protocol.client.get_goal_state()
         protocol.client.update_remote_access_conf(goal_state)
-        self.assertNotEquals(None, protocol.client.remote_access)
-        self.assertEquals(1, len(protocol.client.remote_access.user_list.users))
-        self.assertEquals('testAccount', protocol.client.remote_access.user_list.users[0].name)
-        self.assertEquals('encryptedPasswordString', protocol.client.remote_access.user_list.users[0].encrypted_password)
+        self.assertNotEquals(None, protocol.client.get_remote_access())
+        self.assertEquals(1, len(protocol.client.get_remote_access().user_list.users))
+        self.assertEquals('testAccount', protocol.client.get_remote_access().user_list.users[0].name)
+        self.assertEquals('encryptedPasswordString', protocol.client.get_remote_access().user_list.users[0].encrypted_password)
 
     def test_parse_bad_remote_access_data(self):
         data = "foobar"
diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py
index 65b84d99..3a145e9f 100644
--- a/tests/ga/test_update.py
+++ b/tests/ga/test_update.py
@@ -5,13 +5,20 @@ from __future__ import print_function
 
 import tempfile
 import unittest
+import uuid
+from threading import currentThread
+
+from azurelinuxagent.ga.env import EnvHandler
+
+from azurelinuxagent.common.protocol.util import ProtocolUtil
 
 from azurelinuxagent.common.protocol.hostplugin import *
 from azurelinuxagent.common.protocol.metadata import *
 from azurelinuxagent.common.protocol.wire import *
+from azurelinuxagent.ga.monitor import MonitorHandler
 from azurelinuxagent.ga.update import *
-from tests.tools import AgentTestCase, call, data_dir, DEFAULT, patch, load_bin_data, load_data, Mock, MagicMock
-
+from tests.tools import AgentTestCase, call, data_dir, DEFAULT, patch, load_bin_data, load_data, Mock, MagicMock, \
+    mock_sleep, clear_singleton_instances
 
 NO_ERROR = {
     "last_failure" : 0.0,
@@ -1426,64 +1433,6 @@ class TestUpdate(UpdateTestCase):
                 self.assertEqual(0, len(pid_files))
                 self.assertEqual(None, pid_file)
 
-    @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')
-
     @patch('azurelinuxagent.common.conf.get_extensions_enabled', return_value=False)
     def test_update_happens_when_extensions_disabled(self, _):
         """
@@ -1507,8 +1456,10 @@ class MonitorThreadTest(AgentTestCase):
     def setUp(self):
         AgentTestCase.setUp(self)
         self.event_patch = patch('azurelinuxagent.common.event.add_event')
+        currentThread().setName("ExtHandler")
         self.update_handler = get_update_handler()
         self.update_handler.protocol_util = Mock()
+        clear_singleton_instances(ProtocolUtil)
 
     def _test_run(self, invocations=1):
         iterations = [0]
@@ -1648,6 +1599,76 @@ class MonitorThreadTest(AgentTestCase):
         self.assertEqual(True, mock_env_thread.is_alive.called)
         self.assertEqual(True, mock_env_thread.start.called)
 
+    @patch("time.sleep", lambda *_: mock_sleep(0.01))
+    @patch("azurelinuxagent.common.protocol.util.ProtocolUtil.get_protocol")
+    @patch('azurelinuxagent.ga.monitor.get_monitor_handler')
+    @patch('azurelinuxagent.ga.env.get_env_handler')
+    def test_each_thread_should_have_separate_protocol_util(self, mock_env, mock_monitor, *args):
+
+        self.assertTrue(self.update_handler.running)
+        env_handler = EnvHandler()
+        monitor_handler = MonitorHandler()
+        mock_env.return_value = env_handler
+        mock_monitor.return_value = monitor_handler
+
+        self._test_run(invocations=0)
+        env_handler.stop()
+        monitor_handler.stop()
+
+        singleton_instances = {}
+        for inst in ProtocolUtil._instances:
+            if ProtocolUtil.__name__ in inst:
+                singleton_instances[inst] = ProtocolUtil._instances[inst]
+
+        self.assertEqual(3, len(singleton_instances))
+        self.assertIn("ProtocolUtil__MonitorHandler", singleton_instances)
+        self.assertIn("ProtocolUtil__EnvHandler", singleton_instances)
+        self.assertIn("ProtocolUtil__ExtHandler", singleton_instances)
+
+    @patch("azurelinuxagent.common.protocol.util.ProtocolUtil.get_protocol")
+    @patch('azurelinuxagent.ga.env.get_env_handler')
+    @patch('azurelinuxagent.ga.monitor.MonitorHandler.send_telemetry_heartbeat',
+           side_effect=Exception("Fail Monitor Thread"))
+    @patch('azurelinuxagent.ga.monitor.get_monitor_handler')
+    def test_monitor_handler_should_only_run_init_sysinfo_once(self, mock_monitor, patch_fail, *args):
+        self.assertTrue(self.update_handler.running)
+        os_version = "OSVersion"
+        execution_mode_value = {'value': None}
+
+        def get_telemetry_event_value(sysinfo, event_name):
+            for event in sysinfo:
+                if event.name == event_name:
+                    return event.value
+            return None
+
+        def generate_new_platform_system():
+            value = str(uuid.uuid4())
+            if execution_mode_value['value'] is None:
+                # Save the first value, this is the one that should always be there
+                execution_mode_value['value'] = value
+            return value
+
+        monitor_handler = MonitorHandler()
+        mock_monitor.return_value = monitor_handler
+
+        # There is some timing issues in the threading module in Py2 where the threads take time to join which was
+        # causing race conditions. To ensure that doesnt happen, I'm mocking the ensure_no_orphans function to force
+        # the monitor thread to join as that will fail due to the Exception being thrown above.
+        with patch("azurelinuxagent.ga.update.UpdateHandler._ensure_no_orphans",
+                   side_effect=lambda *_: monitor_handler.event_thread.join()):
+            with patch.object(platform, "system", side_effect=generate_new_platform_system):
+                self._test_run(invocations=3)
+                # Ensure that the monitor_handler thread is dead before proceeding with the test
+                while monitor_handler.is_alive():
+                    continue
+                self.assertFalse(monitor_handler.is_alive())
+
+                # This is ensuring the testing conditions were met by checking the monitor thread was called multiple times
+                self.assertTrue(patch_fail.call_count > 1)
+
+                # Check that the OSVersion value is the same as the first time by ensuring that init_sysinfo was not called again
+                self.assertIn(execution_mode_value['value'], get_telemetry_event_value(monitor_handler.sysinfo, os_version))
+
 
 class ChildMock(Mock):
     def __init__(self, return_value=0, side_effect=None):
diff --git a/tests/protocol/test_hostplugin.py b/tests/protocol/test_hostplugin.py
index 4cdc5281..f0fac933 100644
--- a/tests/protocol/test_hostplugin.py
+++ b/tests/protocol/test_hostplugin.py
@@ -149,6 +149,21 @@ class TestHostPlugin(AgentTestCase):
         self.assertEqual(headers['x-ms-containerid'], goal_state.container_id)
         self.assertEqual(headers['x-ms-host-config-name'], goal_state.role_config_name)
 
+    def _get_test_wire_protocol(self):
+        test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state)
+        status = restapi.VMStatus(status="Ready", message="Guest Agent is running")
+
+        wire_protocol = wire.WireProtocol(wireserver_url)
+        wire_protocol_client = wire_protocol.client
+        wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state)
+        ext_conf = wire.ExtensionsConfig(None)
+        ext_conf.status_upload_blob = sas_url
+        ext_conf.status_upload_blob_type = page_blob_type
+        wire_protocol_client._set_ext_conf(ext_conf)
+        wire_protocol_client.status_blob.set_vm_status(status)
+
+        return wire_protocol
+
     @patch("azurelinuxagent.common.protocol.healthservice.HealthService.report_host_plugin_versions")
     @patch("azurelinuxagent.ga.update.restutil.http_get")
     @patch("azurelinuxagent.common.event.report_event")
@@ -229,21 +244,13 @@ class TestHostPlugin(AgentTestCase):
         channel are ignored.  Validate that the default channel is never changed
         as part of status upload.
         """
-        test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state)
-        status = restapi.VMStatus(status="Ready", message="Guest Agent is running")
 
-        wire_protocol = wire.WireProtocol(wireserver_url)
-        wire_protocol_client = wire_protocol.client
-        wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state)
-        wire_protocol_client.ext_conf = wire.ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.status_upload_blob = sas_url
-        wire_protocol_client.ext_conf.status_upload_blob_type = page_blob_type
-        wire_protocol_client.status_blob.set_vm_status(status)
+        wire_protocol = self._get_test_wire_protocol()
 
         wire_protocol.update_goal_state()
 
         # act
-        wire_protocol_client.upload_status_blob()
+        wire_protocol.client.upload_status_blob()
 
         # assert direct route is not called
         self.assertEqual(0, patch_upload.call_count, "Direct channel was used")
@@ -272,21 +279,12 @@ class TestHostPlugin(AgentTestCase):
         """
         When host plugin returns a 503, we should fall back to the direct channel
         """
-        test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state)
-        status = restapi.VMStatus(status="Ready", message="Guest Agent is running")
-
-        wire_protocol = wire.WireProtocol(wireserver_url)
-        wire_protocol_client = wire_protocol.client
-        wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state)
-        wire_protocol_client.ext_conf = wire.ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.status_upload_blob = sas_url
-        wire_protocol_client.ext_conf.status_upload_blob_type = page_blob_type
-        wire_protocol_client.status_blob.set_vm_status(status)
+        wire_protocol = self._get_test_wire_protocol()
 
         wire_protocol.update_goal_state()
 
         # act
-        wire_protocol_client.upload_status_blob()
+        wire_protocol.client.upload_status_blob()
 
         # assert direct route is called
         self.assertEqual(1, patch_upload.call_count, "Direct channel was not used")
@@ -316,21 +314,12 @@ class TestHostPlugin(AgentTestCase):
         """
         When host plugin returns a 410, we should force the goal state update and return
         """
-        test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state)
-        status = restapi.VMStatus(status="Ready", message="Guest Agent is running")
-
-        wire_protocol = wire.WireProtocol(wireserver_url)
-        wire_protocol_client = wire_protocol.client
-        wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state)
-        wire_protocol_client.ext_conf = wire.ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.status_upload_blob = sas_url
-        wire_protocol_client.ext_conf.status_upload_blob_type = page_blob_type
-        wire_protocol_client.status_blob.set_vm_status(status)
+        wire_protocol = self._get_test_wire_protocol()
 
         wire_protocol.update_goal_state()
 
         # act
-        wire_protocol_client.upload_status_blob()
+        wire_protocol.client.upload_status_blob()
 
         # assert direct route is not called
         self.assertEqual(0, patch_upload.call_count, "Direct channel was used")
@@ -360,21 +349,12 @@ class TestHostPlugin(AgentTestCase):
         """
         When host plugin returns a 500, and direct fails, we should raise a ProtocolError
         """
-        test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state)
-        status = restapi.VMStatus(status="Ready", message="Guest Agent is running")
-
-        wire_protocol = wire.WireProtocol(wireserver_url)
-        wire_protocol_client = wire_protocol.client
-        wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state)
-        wire_protocol_client.ext_conf = wire.ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.status_upload_blob = sas_url
-        wire_protocol_client.ext_conf.status_upload_blob_type = page_blob_type
-        wire_protocol_client.status_blob.set_vm_status(status)
+        wire_protocol = self._get_test_wire_protocol()
 
         wire_protocol.update_goal_state()
 
         # act
-        self.assertRaises(wire.ProtocolError, wire_protocol_client.upload_status_blob)
+        self.assertRaises(wire.ProtocolError, wire_protocol.client.upload_status_blob)
 
         # assert direct route is not called
         self.assertEqual(1, patch_upload.call_count, "Direct channel was not used")
@@ -398,22 +378,14 @@ class TestHostPlugin(AgentTestCase):
         """
         Validate the telemetry when uploading status fails
         """
-        test_goal_state = wire.GoalState(WireProtocolData(DATA_FILE).goal_state)
-        status = restapi.VMStatus(status="Ready",
-                                  message="Guest Agent is running")
         wire.HostPluginProtocol.set_default_channel(False)
-        with patch.object(wire.StatusBlob,
-                          "upload",
-                          return_value=False):
-            wire_protocol_client = wire.WireProtocol(wireserver_url).client
-            wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state)
-            wire_protocol_client.ext_conf = wire.ExtensionsConfig(None)
-            wire_protocol_client.ext_conf.status_upload_blob = sas_url
-            wire_protocol_client.status_blob.set_vm_status(status)
+        with patch.object(wire.StatusBlob, "upload", return_value=False):
+
+            wire_protocol = self._get_test_wire_protocol()
+            wire_protocol_client = wire_protocol.client
+
             put_error = wire.HttpError("put status http error")
-            with patch.object(restutil,
-                              "http_put",
-                              side_effect=put_error) as patch_http_put:
+            with patch.object(restutil, "http_put", side_effect=put_error):
                 with patch.object(wire.HostPluginProtocol,
                                   "ensure_initialized", return_value=True):
                     self.assertRaises(wire.ProtocolError, wire_protocol_client.upload_status_blob)
@@ -480,10 +452,12 @@ class TestHostPlugin(AgentTestCase):
             with patch.object(wire.StatusBlob, "upload") as patch_upload:
                 patch_upload.return_value = True
                 wire_protocol_client = wire.WireProtocol(wireserver_url).client
-                wire_protocol_client.ext_conf = wire.ExtensionsConfig(None)
-                wire_protocol_client.ext_conf.status_upload_blob = sas_url
+                ext_config = wire.ExtensionsConfig(None)
+                ext_config.status_upload_blob = sas_url
+                wire_protocol_client._set_ext_conf(ext_config)
                 wire_protocol_client.status_blob.vm_status = vmstatus
                 wire_protocol_client.upload_status_blob()
+
                 self.assertTrue(patch_put.call_count == 0, "Fallback was engaged")
 
     def test_validate_block_blob(self):
diff --git a/tests/protocol/test_wire.py b/tests/protocol/test_wire.py
index 1f634b39..072569dd 100644
--- a/tests/protocol/test_wire.py
+++ b/tests/protocol/test_wire.py
@@ -30,8 +30,7 @@ from azurelinuxagent.common.exception import InvalidContainerError, ResourceGone
 from azurelinuxagent.common.future import httpclient
 from azurelinuxagent.common.protocol.hostplugin import HostPluginProtocol
 from azurelinuxagent.common.protocol.wire import WireProtocol, WireClient, GoalState, ExtensionsConfig, \
-    InVMArtifactsProfile, VMAgentManifestUri, StatusBlob, VMStatus, ExtHandlerVersionUri, \
-    DataContractList, socket
+    InVMArtifactsProfile, VMAgentManifestUri, StatusBlob, VMStatus, ExtHandlerVersionUri, DataContractList, socket
 from azurelinuxagent.common.telemetryevent import TelemetryEvent, TelemetryEventParam, TelemetryEventList
 from azurelinuxagent.common.utils import fileutil, restutil
 from azurelinuxagent.common.utils.shellutil import run_get_output
@@ -61,6 +60,16 @@ def get_event(message, duration=30000, evt_type="", is_internal=False, is_succes
     return event
 
 
+def set_up_wireclient_ext_conf(wire_protocol_client, artifacts_profile_blob=None,
+                               status_upload_blob=None,
+                               status_upload_blob_type=None):
+    ext_conf = ExtensionsConfig(None)
+    ext_conf.artifacts_profile_blob = artifacts_profile_blob
+    ext_conf.status_upload_blob = status_upload_blob
+    ext_conf.status_upload_blob_type = status_upload_blob_type
+    wire_protocol_client._set_ext_conf(ext_conf)
+
+
 @patch("time.sleep")
 @patch("azurelinuxagent.common.protocol.wire.CryptUtil")
 @patch("azurelinuxagent.common.protocol.healthservice.HealthService._report")
@@ -184,12 +193,13 @@ class TestWireProtocol(AgentTestCase):
 
     def test_status_blob_parsing(self, *args):
         wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.ext_conf = ExtensionsConfig(mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).ext_conf)
-        self.assertEqual(wire_protocol_client.ext_conf.status_upload_blob,
+        wire_protocol_client._set_ext_conf(
+            ExtensionsConfig(mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).ext_conf))
+        self.assertEqual(wire_protocol_client.get_ext_conf().status_upload_blob,
                          'https://test.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')
-        self.assertEqual(wire_protocol_client.ext_conf.status_upload_blob_type,
+        self.assertEqual(wire_protocol_client.get_ext_conf().status_upload_blob_type,
                          u'BlockBlob')
         pass
 
@@ -255,9 +265,7 @@ class TestWireProtocol(AgentTestCase):
         """
         vmstatus = VMStatus(message="Ready", status="Ready")
         wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.ext_conf = ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.status_upload_blob = testurl
-        wire_protocol_client.ext_conf.status_upload_blob_type = testtype
+        set_up_wireclient_ext_conf(wire_protocol_client, status_upload_blob=testurl, status_upload_blob_type=testtype)
         wire_protocol_client.status_blob.vm_status = vmstatus
 
         with patch.object(WireClient, "get_goal_state") as patch_get_goal_state:
@@ -277,9 +285,7 @@ class TestWireProtocol(AgentTestCase):
     def test_upload_status_blob_host_ga_plugin(self, *args):
         vmstatus = VMStatus(message="Ready", status="Ready")
         wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.ext_conf = ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.status_upload_blob = testurl
-        wire_protocol_client.ext_conf.status_upload_blob_type = testtype
+        set_up_wireclient_ext_conf(wire_protocol_client, status_upload_blob=testurl, status_upload_blob_type=testtype)
         wire_protocol_client.status_blob.vm_status = vmstatus
         goal_state = GoalState(mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state)
 
@@ -304,9 +310,9 @@ class TestWireProtocol(AgentTestCase):
     def test_upload_status_blob_unknown_type_assumes_block(self, _, __, *args):
         vmstatus = VMStatus(message="Ready", status="Ready")
         wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.ext_conf = ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.status_upload_blob = testurl
-        wire_protocol_client.ext_conf.status_upload_blob_type = "NotALegalType"
+        set_up_wireclient_ext_conf(wire_protocol_client,
+                                   status_upload_blob=testurl,
+                                   status_upload_blob_type="NotALegalType")
         wire_protocol_client.status_blob.vm_status = vmstatus
 
         with patch.object(WireClient, "get_goal_state") as patch_get_goal_state:
@@ -323,9 +329,7 @@ class TestWireProtocol(AgentTestCase):
     def test_upload_status_blob_reports_prepare_error(self, *args):
         vmstatus = VMStatus(message="Ready", status="Ready")
         wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.ext_conf = ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.status_upload_blob = testurl
-        wire_protocol_client.ext_conf.status_upload_blob_type = testtype
+        set_up_wireclient_ext_conf(wire_protocol_client, status_upload_blob=testurl, status_upload_blob_type=testtype)
         wire_protocol_client.status_blob.vm_status = vmstatus
         goal_state = GoalState(mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state)
 
@@ -335,19 +339,18 @@ class TestWireProtocol(AgentTestCase):
 
     def test_get_in_vm_artifacts_profile_blob_not_available(self, *args):
         wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.ext_conf = ExtensionsConfig(None)
+        wire_protocol_client._set_ext_conf(ExtensionsConfig(None))
 
         # Test when artifacts_profile_blob is null/None
         self.assertEqual(None, wire_protocol_client.get_artifacts_profile())
 
         # Test when artifacts_profile_blob is whitespace
-        wire_protocol_client.ext_conf.artifacts_profile_blob = "  "
+        set_up_wireclient_ext_conf(wire_protocol_client, artifacts_profile_blob="  ")
         self.assertEqual(None, wire_protocol_client.get_artifacts_profile())
 
     def test_get_in_vm_artifacts_profile_response_body_not_valid(self, *args):
         wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.ext_conf = ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.artifacts_profile_blob = testurl
+        set_up_wireclient_ext_conf(wire_protocol_client, artifacts_profile_blob=testurl)
         goal_state = GoalState(mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state)
         wire_protocol_client.get_goal_state = Mock(return_value=goal_state)
 
@@ -375,8 +378,7 @@ class TestWireProtocol(AgentTestCase):
     @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
+        set_up_wireclient_ext_conf(wire_protocol_client, artifacts_profile_blob=testurl)
         goal_state = GoalState(mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state)
         wire_protocol_client.get_goal_state = Mock(return_value=goal_state)
 
@@ -395,8 +397,7 @@ class TestWireProtocol(AgentTestCase):
 
     def test_get_in_vm_artifacts_profile_default(self, *args):
         wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.ext_conf = ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.artifacts_profile_blob = testurl
+        set_up_wireclient_ext_conf(wire_protocol_client, artifacts_profile_blob=testurl)
         goal_state = GoalState(mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state)
         wire_protocol_client.get_goal_state = Mock(return_value=goal_state)
 
@@ -460,8 +461,7 @@ class TestWireProtocol(AgentTestCase):
 
     def test_get_in_vm_artifacts_profile_host_ga_plugin(self, *args):
         wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.ext_conf = ExtensionsConfig(None)
-        wire_protocol_client.ext_conf.artifacts_profile_blob = testurl
+        set_up_wireclient_ext_conf(wire_protocol_client, artifacts_profile_blob=testurl)
         goal_state = GoalState(mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state)
         wire_protocol_client.get_goal_state = Mock(return_value=goal_state)
         wire_protocol_client.fetch = Mock(side_effect=[None, '{"onHold": "true"}'])
@@ -596,24 +596,29 @@ class TestWireClient(AgentTestCase):
         self.assertIsNone(ext_conf.artifacts_profile_blob)
 
     def test_get_ext_conf_with_uri(self, *args):
-        test_goal_state = GoalState(mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state)
-        wire_protocol_client = WireProtocol(WIRESERVER_URL).client
-        wire_protocol_client.get_goal_state = Mock(return_value=test_goal_state)
-
-        with patch("azurelinuxagent.common.protocol.wire.WireClient.fetch_cache",
-                   return_value=mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).ext_conf):
-            ext_conf = wire_protocol_client.get_ext_conf()
-
-        self.assertEqual(1, len(ext_conf.ext_handlers.extHandlers))
-        self.assertEqual(2, len(ext_conf.vmagent_manifests.vmAgentManifests))
-        self.assertEqual("https://test.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", ext_conf.status_upload_blob)
-        self.assertEqual("BlockBlob", ext_conf.status_upload_blob_type)
-        self.assertEqual(None, ext_conf.artifacts_profile_blob)
+        mock_data = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE)
+
+        with patch("azurelinuxagent.common.utils.restutil.http_get", side_effect=mock_data.mock_http_get):
+            with patch("azurelinuxagent.common.protocol.wire.CryptUtil", side_effect=mock_data.mock_crypt_util):
+                protocol = WireProtocol(WIRESERVER_URL)
+                # This call would populate the ext_conf with the test data and also mock the required certs
+                protocol.detect()
+                wire_protocol_client = protocol.client
+                ext_conf = wire_protocol_client.get_ext_conf()
+
+                self.assertEqual(1, len(ext_conf.ext_handlers.extHandlers))
+                self.assertEqual(2, len(ext_conf.vmagent_manifests.vmAgentManifests))
+                self.assertEqual("https://test.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",
+                                 ext_conf.status_upload_blob)
+                self.assertEqual("BlockBlob", ext_conf.status_upload_blob_type)
+                self.assertEqual(None, ext_conf.artifacts_profile_blob)
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_download_ext_handler_pkg_should_not_invoke_host_channel_when_direct_channel_succeeds(self, mock_get_artifact_request, *args):
+    def test_download_ext_handler_pkg_should_not_invoke_host_channel_when_direct_channel_succeeds(self,
+                                                                                                  mock_get_artifact_request,
+                                                                                                  *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         protocol = WireProtocol("foo.bar")
         HostPluginProtocol.set_default_channel(False)
@@ -626,8 +631,9 @@ class TestWireClient(AgentTestCase):
             with patch("azurelinuxagent.common.protocol.wire.WireClient.update_goal_state") as mock_update_goal_state:
                 with patch("azurelinuxagent.common.protocol.wire.WireClient.stream", wraps=protocol.client.stream) \
                         as patch_direct:
-                    with patch("azurelinuxagent.common.protocol.wire.WireProtocol._download_ext_handler_pkg_through_host",
-                               wraps=protocol._download_ext_handler_pkg_through_host) as patch_host:
+                    with patch(
+                            "azurelinuxagent.common.protocol.wire.WireProtocol._download_ext_handler_pkg_through_host",
+                            wraps=protocol._download_ext_handler_pkg_through_host) as patch_host:
                         ret = protocol.download_ext_handler_pkg("uri", destination)
                         self.assertEquals(ret, True)
 
@@ -639,7 +645,8 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_download_ext_handler_pkg_should_use_host_channel_when_direct_channel_fails(self, mock_get_artifact_request, *args):
+    def test_download_ext_handler_pkg_should_use_host_channel_when_direct_channel_fails(self, mock_get_artifact_request,
+                                                                                        *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         protocol = WireProtocol("foo.bar")
         HostPluginProtocol.set_default_channel(False)
@@ -655,8 +662,9 @@ class TestWireClient(AgentTestCase):
             with patch("azurelinuxagent.common.protocol.wire.WireClient.update_goal_state") as mock_update_goal_state:
                 with patch("azurelinuxagent.common.protocol.wire.WireClient.stream", wraps=protocol.client.stream) \
                         as patch_direct:
-                    with patch("azurelinuxagent.common.protocol.wire.WireProtocol._download_ext_handler_pkg_through_host",
-                               wraps=protocol._download_ext_handler_pkg_through_host) as patch_host:
+                    with patch(
+                            "azurelinuxagent.common.protocol.wire.WireProtocol._download_ext_handler_pkg_through_host",
+                            wraps=protocol._download_ext_handler_pkg_through_host) as patch_host:
                         ret = protocol.download_ext_handler_pkg("uri", destination)
                         self.assertEquals(ret, True)
 
@@ -669,7 +677,9 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_download_ext_handler_pkg_should_retry_the_host_channel_after_refreshing_host_plugin(self, mock_get_artifact_request, *args):
+    def test_download_ext_handler_pkg_should_retry_the_host_channel_after_refreshing_host_plugin(self,
+                                                                                                 mock_get_artifact_request,
+                                                                                                 *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         protocol = WireProtocol("foo.bar")
         HostPluginProtocol.set_default_channel(False)
@@ -682,11 +692,13 @@ class TestWireClient(AgentTestCase):
         # As a consequence, goal state should have been updated and host channel should have been set as default.
         with patch("azurelinuxagent.common.utils.restutil._http_request",
                    side_effect=[mock_failed_response, mock_failed_response, mock_successful_response]):
-            with patch("azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
+            with patch(
+                    "azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
                 with patch("azurelinuxagent.common.protocol.wire.WireClient.stream", wraps=protocol.client.stream) \
                         as patch_direct:
-                    with patch("azurelinuxagent.common.protocol.wire.WireProtocol._download_ext_handler_pkg_through_host",
-                               wraps=protocol._download_ext_handler_pkg_through_host) as patch_host:
+                    with patch(
+                            "azurelinuxagent.common.protocol.wire.WireProtocol._download_ext_handler_pkg_through_host",
+                            wraps=protocol._download_ext_handler_pkg_through_host) as patch_host:
                         ret = protocol.download_ext_handler_pkg("uri", destination)
                         self.assertEquals(ret, True)
 
@@ -699,7 +711,8 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_download_ext_handler_pkg_should_not_change_default_channel_if_host_fails(self, mock_get_artifact_request, *args):
+    def test_download_ext_handler_pkg_should_not_change_default_channel_if_host_fails(self, mock_get_artifact_request,
+                                                                                      *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         protocol = WireProtocol("foo.bar")
         HostPluginProtocol.set_default_channel(False)
@@ -709,11 +722,13 @@ class TestWireClient(AgentTestCase):
 
         # Everything fails. Goal state should have been updated and host channel should not have been set as default.
         with patch("azurelinuxagent.common.utils.restutil._http_request", return_value=mock_failed_response):
-            with patch("azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
+            with patch(
+                    "azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
                 with patch("azurelinuxagent.common.protocol.wire.WireClient.stream", wraps=protocol.client.stream) \
                         as patch_direct:
-                    with patch("azurelinuxagent.common.protocol.wire.WireProtocol._download_ext_handler_pkg_through_host",
-                               wraps=protocol._download_ext_handler_pkg_through_host) as patch_host:
+                    with patch(
+                            "azurelinuxagent.common.protocol.wire.WireProtocol._download_ext_handler_pkg_through_host",
+                            wraps=protocol._download_ext_handler_pkg_through_host) as patch_host:
                         ret = protocol.download_ext_handler_pkg("uri", destination)
                         self.assertEquals(ret, False)
 
@@ -726,7 +741,8 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_fetch_manifest_should_not_invoke_host_channel_when_direct_channel_succeeds(self, mock_get_artifact_request, *args):
+    def test_fetch_manifest_should_not_invoke_host_channel_when_direct_channel_succeeds(self, mock_get_artifact_request,
+                                                                                        *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         client = WireClient("foo.bar")
 
@@ -783,7 +799,9 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_fetch_manifest_should_retry_the_host_channel_after_refreshing_the_host_plugin(self, mock_get_artifact_request, *args):
+    def test_fetch_manifest_should_retry_the_host_channel_after_refreshing_the_host_plugin(self,
+                                                                                           mock_get_artifact_request,
+                                                                                           *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         client = WireClient("foo.bar")
 
@@ -796,7 +814,8 @@ class TestWireClient(AgentTestCase):
         # As a consequence, goal state should have been updated and host channel should have been set as default.
         with patch("azurelinuxagent.common.utils.restutil._http_request",
                    side_effect=[mock_failed_response, mock_failed_response, mock_successful_response]):
-            with patch("azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
+            with patch(
+                    "azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
                 with patch("azurelinuxagent.common.protocol.wire.WireClient.fetch", wraps=client.fetch) as patch_direct:
                     with patch("azurelinuxagent.common.protocol.wire.WireClient.fetch_manifest_through_host",
                                wraps=client.fetch_manifest_through_host) as patch_host:
@@ -812,7 +831,9 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_fetch_manifest_should_update_goal_state_and_not_change_default_channel_if_host_fails(self, mock_get_artifact_request, *args):
+    def test_fetch_manifest_should_update_goal_state_and_not_change_default_channel_if_host_fails(self,
+                                                                                                  mock_get_artifact_request,
+                                                                                                  *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         client = WireClient("foo.bar")
 
@@ -837,11 +858,12 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_get_artifacts_profile_should_not_invoke_host_channel_when_direct_channel_succeeds(self, mock_get_artifact_request, *args):
+    def test_get_artifacts_profile_should_not_invoke_host_channel_when_direct_channel_succeeds(self,
+                                                                                               mock_get_artifact_request,
+                                                                                               *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         client = WireClient("foo.bar")
-        client.ext_conf = ExtensionsConfig(None)
-        client.ext_conf.artifacts_profile_blob = "testurl"
+        set_up_wireclient_ext_conf(client, artifacts_profile_blob="testurl")
         json_profile = b'{ "onHold": true }'
 
         HostPluginProtocol.set_default_channel(False)
@@ -864,11 +886,11 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_get_artifacts_profile_should_use_host_channel_when_direct_channel_fails(self, mock_get_artifact_request, *args):
+    def test_get_artifacts_profile_should_use_host_channel_when_direct_channel_fails(self, mock_get_artifact_request,
+                                                                                     *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         client = WireClient("foo.bar")
-        client.ext_conf = ExtensionsConfig(None)
-        client.ext_conf.artifacts_profile_blob = "testurl"
+        set_up_wireclient_ext_conf(client, artifacts_profile_blob="testurl")
         json_profile = b'{ "onHold": true }'
 
         HostPluginProtocol.set_default_channel(False)
@@ -896,11 +918,12 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_get_artifacts_profile_should_retry_the_host_channel_after_refreshing_the_host_plugin(self, mock_get_artifact_request, *args):
+    def test_get_artifacts_profile_should_retry_the_host_channel_after_refreshing_the_host_plugin(self,
+                                                                                                  mock_get_artifact_request,
+                                                                                                  *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         client = WireClient("foo.bar")
-        client.ext_conf = ExtensionsConfig(None)
-        client.ext_conf.artifacts_profile_blob = "testurl"
+        set_up_wireclient_ext_conf(client, artifacts_profile_blob="testurl")
         json_profile = b'{ "onHold": true }'
 
         HostPluginProtocol.set_default_channel(False)
@@ -912,7 +935,8 @@ class TestWireClient(AgentTestCase):
         # As a consequence, host plugin should have been updated and host channel should have been set as default.
         with patch("azurelinuxagent.common.utils.restutil._http_request",
                    side_effect=[mock_failed_response, mock_failed_response, mock_successful_response]):
-            with patch("azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
+            with patch(
+                    "azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
                 with patch("azurelinuxagent.common.protocol.wire.WireClient.fetch", wraps=client.fetch) as patch_direct:
                     with patch("azurelinuxagent.common.protocol.wire.WireClient.get_artifacts_profile_through_host",
                                wraps=client.get_artifacts_profile_through_host) as patch_host:
@@ -928,12 +952,11 @@ class TestWireClient(AgentTestCase):
 
     @patch("azurelinuxagent.common.protocol.wire.WireClient.get_goal_state")
     @patch("azurelinuxagent.common.protocol.hostplugin.HostPluginProtocol.get_artifact_request")
-    def test_get_artifacts_profile_should_refresh_the_host_plugin_and_not_change_default_channel_if_host_plugin_fails(self, mock_get_artifact_request, *args):
+    def test_get_artifacts_profile_should_refresh_the_host_plugin_and_not_change_default_channel_if_host_plugin_fails(
+            self, mock_get_artifact_request, *args):
         mock_get_artifact_request.return_value = "dummy_url", "dummy_header"
         client = WireClient("foo.bar")
-        client.ext_conf = ExtensionsConfig(None)
-        client.ext_conf.artifacts_profile_blob = "testurl"
-        json_profile = b'{ "onHold": true }'
+        set_up_wireclient_ext_conf(client, artifacts_profile_blob="testurl")
 
         HostPluginProtocol.set_default_channel(False)
 
@@ -941,7 +964,8 @@ class TestWireClient(AgentTestCase):
 
         # Everything fails. Goal state should have been updated and host channel should not have been set as default.
         with patch("azurelinuxagent.common.utils.restutil._http_request", return_value=mock_failed_response):
-            with patch("azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
+            with patch(
+                    "azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state") as mock_update_host_plugin_from_goal_state:
                 with patch("azurelinuxagent.common.protocol.wire.WireClient.fetch", wraps=client.fetch) as patch_direct:
                     with patch("azurelinuxagent.common.protocol.wire.WireClient.get_artifacts_profile_through_host",
                                wraps=client.get_artifacts_profile_through_host) as patch_host:
@@ -955,10 +979,11 @@ class TestWireClient(AgentTestCase):
 
                         self.assertEquals(HostPluginProtocol.is_default_channel(), False)
 
-    def test_send_request_using_appropriate_channel_should_not_invoke_host_channel_when_direct_channel_succeeds(self, *args):
+    def test_send_request_using_appropriate_channel_should_not_invoke_host_channel_when_direct_channel_succeeds(self,
+                                                                                                                *args):
         xml_text = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state
         client = WireClient(WIRESERVER_URL)
-        client.goal_state = GoalState(xml_text)
+        client._set_goal_state(GoalState(xml_text))
         client.get_host_plugin().set_default_channel(False)
 
         def direct_func(*args):
@@ -978,10 +1003,11 @@ class TestWireClient(AgentTestCase):
         self.assertEquals(1, direct_func.counter)
         self.assertEquals(0, host_func.counter)
 
-    def test_send_request_using_appropriate_channel_should_not_use_direct_channel_when_host_channel_is_default(self, *args):
+    def test_send_request_using_appropriate_channel_should_not_use_direct_channel_when_host_channel_is_default(self,
+                                                                                                               *args):
         xml_text = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state
         client = WireClient(WIRESERVER_URL)
-        client.goal_state = GoalState(xml_text)
+        client._set_goal_state(GoalState(xml_text))
         client.get_host_plugin().set_default_channel(True)
 
         def direct_func(*args):
@@ -1004,7 +1030,7 @@ class TestWireClient(AgentTestCase):
     def test_send_request_using_appropriate_channel_should_use_host_channel_when_direct_channel_fails(self, *args):
         xml_text = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state
         client = WireClient(WIRESERVER_URL)
-        client.goal_state = GoalState(xml_text)
+        client._set_goal_state(GoalState(xml_text))
         host = client.get_host_plugin()
         host.set_default_channel(False)
 
@@ -1027,10 +1053,11 @@ class TestWireClient(AgentTestCase):
         self.assertEquals(1, host_func.counter)
         self.assertEquals(True, host.is_default_channel())
 
-    def test_send_request_using_appropriate_channel_should_retry_the_host_channel_after_reloading_goal_state(self, *args):
+    def test_send_request_using_appropriate_channel_should_retry_the_host_channel_after_reloading_goal_state(self,
+                                                                                                             *args):
         xml_text = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE).goal_state
         client = WireClient(WIRESERVER_URL)
-        client.goal_state = GoalState(xml_text)
+        client._set_goal_state(GoalState(xml_text))
         client.get_host_plugin().set_default_channel(False)
 
         def direct_func(*args):
@@ -1048,7 +1075,8 @@ class TestWireClient(AgentTestCase):
 
         # Assert we've called both the direct channel function (once) and the host channel function (twice).
         # After the host channel succeeds, the host plugin should have been set as the default channel.
-        with patch('azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state') as mock_update_host_plugin_from_goal_state:
+        with patch(
+                'azurelinuxagent.common.protocol.wire.WireClient.update_host_plugin_from_goal_state') as mock_update_host_plugin_from_goal_state:
             ret = client.send_request_using_appropriate_channel(direct_func, host_func)
             self.assertEquals(42, ret)
             self.assertEquals(1, direct_func.counter)
@@ -1061,12 +1089,15 @@ class GoalStateTestCase(AgentTestCase):
     """
     Base class for UpdateGoalStateTestCase and UpdateHostPluginFromGoalStateTestCase
     """
+
     def setUp(self):
         AgentTestCase.setUp(self)
 
         self.mock_data = mockwiredata.WireProtocolData(mockwiredata.DATA_FILE)
-        self.mock_http_get = patch("azurelinuxagent.common.utils.restutil.http_get", side_effect=self.mock_data.mock_http_get)
-        self.mock_crypt_util = patch("azurelinuxagent.common.protocol.wire.CryptUtil", side_effect=self.mock_data.mock_crypt_util)
+        self.mock_http_get = patch("azurelinuxagent.common.utils.restutil.http_get",
+                                   side_effect=self.mock_data.mock_http_get)
+        self.mock_crypt_util = patch("azurelinuxagent.common.protocol.wire.CryptUtil",
+                                     side_effect=self.mock_data.mock_crypt_util)
         self.mock_http_get.start()
         self.mock_crypt_util.start()
 
@@ -1088,6 +1119,7 @@ class UpdateGoalStateTestCase(GoalStateTestCase):
     """
     Tests for WireClient.update_goal_state()
     """
+
     def test_it_should_update_the_goal_state_and_the_host_plugin_when_the_incarnation_changes(self):
         # if the incarnation changes the behavior is the same for forced and non-forced updates
         for forced in [True, False]:
@@ -1106,7 +1138,8 @@ class UpdateGoalStateTestCase(GoalStateTestCase):
             new_container_id = str(uuid.uuid4())
             new_role_config_name = str(uuid.uuid4())
             new_hosting_env_deployment_name = str(uuid.uuid4())
-            new_shared_conf = WireProtocolData.replace_xml_attribute_value(self.mock_data.shared_config, "Deployment", "name", str(uuid.uuid4()))
+            new_shared_conf = WireProtocolData.replace_xml_attribute_value(self.mock_data.shared_config, "Deployment",
+                                                                           "name", str(uuid.uuid4()))
             new_sequence_number = str(uuid.uuid4())
 
             if '<Format>Pkcs7BlobWithPfxContents</Format>' not in self.mock_data.certs:
@@ -1131,43 +1164,48 @@ class UpdateGoalStateTestCase(GoalStateTestCase):
             else:
                 self.wire_client.update_goal_state()
 
-            sequence_number = self.wire_client.ext_conf.ext_handlers.extHandlers[0].properties.extensions[0].sequenceNumber
+            sequence_number = self.wire_client.get_ext_conf().ext_handlers.extHandlers[0].properties.extensions[
+                0].sequenceNumber
 
-            self.assertEqual(self.wire_client.goal_state.incarnation, new_incarnation)
-            self.assertEqual(self.wire_client.hosting_env.deployment_name, new_hosting_env_deployment_name)
-            self.assertEqual(self.wire_client.shared_conf.xml_text, new_shared_conf)
+            self.assertEqual(self.wire_client.get_goal_state().incarnation, new_incarnation)
+            self.assertEqual(self.wire_client.get_hosting_env().deployment_name, new_hosting_env_deployment_name)
+            self.assertEqual(self.wire_client.get_shared_conf().xml_text, new_shared_conf)
             self.assertEqual(sequence_number, new_sequence_number)
-            self.assertEqual(len(self.wire_client.certs.cert_list.certificates), 0)
+            self.assertEqual(len(self.wire_client.get_certs().cert_list.certificates), 0)
 
-            self.assertEqual(self.wire_client.host_plugin.container_id, new_container_id)
-            self.assertEqual(self.wire_client.host_plugin.role_config_name, new_role_config_name)
+            self.assertEqual(self.wire_client.get_host_plugin().container_id, new_container_id)
+            self.assertEqual(self.wire_client.get_host_plugin().role_config_name, new_role_config_name)
 
-    def test_non_forced_update_should_not_update_the_goal_state_nor_the_host_plugin_when_the_incarnation_does_not_change(self):
+    def test_non_forced_update_should_not_update_the_goal_state_nor_the_host_plugin_when_the_incarnation_does_not_change(
+            self):
         # The container id, role config name and shared config can change without the incarnation changing; capture the initial
         # goal state and then change those fields.
-        goal_state = self.wire_client.goal_state.xml_text
-        shared_conf = self.wire_client.shared_conf.xml_text
-        container_id = self.wire_client.host_plugin.container_id
-        role_config_name = self.wire_client.host_plugin.role_config_name
+        goal_state = self.wire_client.get_goal_state().xml_text
+        shared_conf = self.wire_client.get_shared_conf().xml_text
+        container_id = self.wire_client.get_host_plugin().container_id
+        role_config_name = self.wire_client.get_host_plugin().role_config_name
 
         self.mock_data.set_container_id(str(uuid.uuid4()))
         self.mock_data.set_role_config_name(str(uuid.uuid4()))
-        self.mock_data.shared_config = WireProtocolData.replace_xml_attribute_value(self.mock_data.shared_config, "Deployment", "name", str(uuid.uuid4()))
+        self.mock_data.shared_config = WireProtocolData.replace_xml_attribute_value(self.mock_data.shared_config,
+                                                                                    "Deployment", "name",
+                                                                                    str(uuid.uuid4()))
 
         self.wire_client.update_goal_state()
 
-        self.assertEqual(self.wire_client.goal_state.xml_text, goal_state)
-        self.assertEqual(self.wire_client.shared_conf.xml_text, shared_conf)
+        self.assertEqual(self.wire_client.get_goal_state().xml_text, goal_state)
+        self.assertEqual(self.wire_client.get_shared_conf().xml_text, shared_conf)
 
-        self.assertEqual(self.wire_client.host_plugin.container_id, container_id)
-        self.assertEqual(self.wire_client.host_plugin.role_config_name, role_config_name)
+        self.assertEqual(self.wire_client.get_host_plugin().container_id, container_id)
+        self.assertEqual(self.wire_client.get_host_plugin().role_config_name, role_config_name)
 
     def test_forced_update_should_update_the_goal_state_and_the_host_plugin_when_the_incarnation_does_not_change(self):
         # The container id, role config name and shared config can change without the incarnation changing
-        incarnation = self.wire_client.goal_state.incarnation
+        incarnation = self.wire_client.get_goal_state().incarnation
         new_container_id = str(uuid.uuid4())
         new_role_config_name = str(uuid.uuid4())
-        new_shared_conf = WireProtocolData.replace_xml_attribute_value(self.mock_data.shared_config, "Deployment", "name", str(uuid.uuid4()))
+        new_shared_conf = WireProtocolData.replace_xml_attribute_value(self.mock_data.shared_config, "Deployment",
+                                                                       "name", str(uuid.uuid4()))
 
         self.mock_data.set_container_id(new_container_id)
         self.mock_data.set_role_config_name(new_role_config_name)
@@ -1175,17 +1213,18 @@ class UpdateGoalStateTestCase(GoalStateTestCase):
 
         self.wire_client.update_goal_state(forced=True)
 
-        self.assertEqual(self.wire_client.goal_state.incarnation, incarnation)
-        self.assertEqual(self.wire_client.shared_conf.xml_text, new_shared_conf)
+        self.assertEqual(self.wire_client.get_goal_state().incarnation, incarnation)
+        self.assertEqual(self.wire_client.get_shared_conf().xml_text, new_shared_conf)
 
-        self.assertEqual(self.wire_client.host_plugin.container_id, new_container_id)
-        self.assertEqual(self.wire_client.host_plugin.role_config_name, new_role_config_name)
+        self.assertEqual(self.wire_client.get_host_plugin().container_id, new_container_id)
+        self.assertEqual(self.wire_client.get_host_plugin().role_config_name, new_role_config_name)
 
 
 class UpdateHostPluginFromGoalStateTestCase(GoalStateTestCase):
     """
     Tests for WireClient.update_host_plugin_from_goal_state()
     """
+
     def test_it_should_update_the_host_plugin_with_or_without_incarnation_changes(self):
         # the behavior should be the same whether the incarnation changes or not
         for incarnation_change in [True, False]:
@@ -1200,16 +1239,18 @@ class UpdateHostPluginFromGoalStateTestCase(GoalStateTestCase):
                 self.mock_data.set_incarnation(str(uuid.uuid4()))
             self.mock_data.set_container_id(new_container_id)
             self.mock_data.set_role_config_name(new_role_config_name)
-            self.mock_data.shared_config = WireProtocolData.replace_xml_attribute_value(self.mock_data.shared_config, "Deployment", "name", str(uuid.uuid4()))
+            self.mock_data.shared_config = WireProtocolData.replace_xml_attribute_value(self.mock_data.shared_config,
+                                                                                        "Deployment", "name",
+                                                                                        str(uuid.uuid4()))
 
             self.wire_client.update_host_plugin_from_goal_state()
 
-            self.assertEqual(self.wire_client.host_plugin.container_id, new_container_id)
-            self.assertEqual(self.wire_client.host_plugin.role_config_name, new_role_config_name)
+            self.assertEqual(self.wire_client.get_host_plugin().container_id, new_container_id)
+            self.assertEqual(self.wire_client.get_host_plugin().role_config_name, new_role_config_name)
 
             # it should not update the goal state
-            self.assertEqual(self.wire_client.goal_state.xml_text, goal_state)
-            self.assertEqual(self.wire_client.shared_conf.xml_text, shared_conf)
+            self.assertEqual(self.wire_client.get_goal_state().xml_text, goal_state)
+            self.assertEqual(self.wire_client.get_shared_conf().xml_text, shared_conf)
 
 
 class MockResponse:
diff --git a/tests/tools.py b/tests/tools.py
index 71d85613..fee573ce 100644
--- a/tests/tools.py
+++ b/tests/tools.py
@@ -550,4 +550,3 @@ def clear_singleton_instances(cls):
         obj_name = "%s__%s" % (cls.__name__, currentThread().getName())  # Object Name = className__threadName
         if obj_name in cls._instances:
             del cls._instances[obj_name]
-