Repository: Azure/WALinuxAgent
Pull request #1754 (2020-01-11 02:42:16)

Remove file dependency
<!-- DO NOT DELETE THIS TEMPLATE -->

## Description
Currently the Guest Agent relies on the filesystem to fetch cached data for the GoalState if the object is None. This behavior can lead to inconsistencies in the wire client objects as some thread might update the filesystems midway of execution of goal-state.

This PR removes the dependencies of the WireClient from the file system and keeps everything in memory. We still write to files because they help in debugging. 

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/1754)
<!-- Reviewable:end -->


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

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

```diff
@@            Coverage Diff             @@
##             develop    #1754   +/-   ##
==========================================
  Coverage           ?   68.75%           
==========================================
  Files              ?       82           
  Lines              ?    11729           
  Branches           ?     1648           
==========================================
  Hits               ?     8064           
  Misses             ?     3314           
  Partials           ?      351
```


| [Impacted Files](https://codecov.io/gh/Azure/WALinuxAgent/pull/1754?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [azurelinuxagent/common/protocol/wire.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/1754/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9wcm90b2NvbC93aXJlLnB5) | `78.65% <100%> (ø)` | |
| [azurelinuxagent/ga/monitor.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/1754/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL21vbml0b3IucHk=) | `89.27% <100%> (ø)` | |
| [azurelinuxagent/common/singletonperthread.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/1754/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9zaW5nbGV0b25wZXJ0aHJlYWQucHk=) | `100% <100%> (ø)` | |
| [azurelinuxagent/ga/update.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/1754/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL3VwZGF0ZS5weQ==) | `88.44% <100%> (ø)` | |
| [azurelinuxagent/ga/remoteaccess.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/1754/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2dhL3JlbW90ZWFjY2Vzcy5weQ==) | `88.88% <75%> (ø)` | |
| [azurelinuxagent/common/protocol/util.py](https://codecov.io/gh/Azure/WALinuxAgent/pull/1754/diff?src=pr&el=tree#diff-YXp1cmVsaW51eGFnZW50L2NvbW1vbi9wcm90b2NvbC91dGlsLnB5) | `83.41% <85.1%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/WALinuxAgent/pull/1754?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/1754?src=pr&el=footer). Last update [43ab21b...7923262](https://codecov.io/gh/Azure/WALinuxAgent/pull/1754?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 18ceee96..d328daf9 100644
--- a/azurelinuxagent/daemon/main.py
+++ b/azurelinuxagent/daemon/main.py
@@ -60,16 +60,19 @@ class DaemonHandler(object):
         self.osutil = get_osutil()
 
     def run(self, child_args=None):
+        #
+        # The Container ID in telemetry events is retrieved from the goal state. We can fetch the goal state
+        # only after protocol detection, which is done during provisioning.
+        #
+        # Be aware that telemetry events emitted before that will not include the Container ID.
+        #
         logger.info("{0} Version:{1}", AGENT_LONG_NAME, AGENT_VERSION)
         logger.info("OS: {0} {1}", DISTRO_NAME, DISTRO_VERSION)
-        logger.info("Python: {0}.{1}.{2}", PY_VERSION_MAJOR, PY_VERSION_MINOR,
-                    PY_VERSION_MICRO)
+        logger.info("Python: {0}.{1}.{2}", PY_VERSION_MAJOR, PY_VERSION_MINOR, PY_VERSION_MICRO)
 
         self.check_pid()
         self.initialize_environment()
 
-        CGroupConfigurator.get_instance().create_agent_cgroups(track_cgroups=False)
-
         # If FIPS is enabled, set the OpenSSL environment variable
         # Note:
         # -- Subprocesses inherit the current environment
@@ -138,6 +141,9 @@ class DaemonHandler(object):
 
         self.provision_handler.run()
 
+        # Initialize the agent cgroup
+        CGroupConfigurator.get_instance().create_agent_cgroups(track_cgroups=False)
+
         # Enable RDMA, continue in errors
         if conf.enable_rdma():
             nd_version = self.rdma_handler.get_rdma_version()
@@ -154,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/monitor.py b/azurelinuxagent/ga/monitor.py
index 77ed176e..12b0b992 100644
--- a/azurelinuxagent/ga/monitor.py
+++ b/azurelinuxagent/ga/monitor.py
@@ -153,6 +153,8 @@ class MonitorHandler(object):
 
     def init_protocols(self):
         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):
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
diff --git a/azurelinuxagent/ga/update.py b/azurelinuxagent/ga/update.py
index 7e52f8fd..9b80b303 100644
--- a/azurelinuxagent/ga/update.py
+++ b/azurelinuxagent/ga/update.py
@@ -248,33 +248,32 @@ class UpdateHandler(object):
         """
 
         try:
-            # NOTE: Do not add any telemetry events until after the monitoring handler has been started with the
-            # call to 'monitor_thread.run()'. That method call initializes the protocol, which is needed in order to
-            # load the goal state and update the container id in memory. Any telemetry events sent before this happens
-            # will result in an uninitialized container id value.
-
-            logger.info(u"Agent {0} is running as the goal state agent",
-                        CURRENT_AGENT)
-
-            # Log OS-specific info locally.
-            os_info_msg = u"Distro info: {0} {1}, osutil class being used: {2}, " \
-                          u"agent service name: {3}".format(DISTRO_NAME, DISTRO_VERSION,
-                                                            type(self.osutil).__name__, self.osutil.service_name)
+            logger.info(u"Agent {0} is running as the goal state agent", CURRENT_AGENT)
+
+            #
+            # Fetch the goal state one time; some components depend on information provided by the goal state and this
+            # call ensures the required info is initialized (e.g telemetry depends on the container ID.)
+            #
+            protocol = self.protocol_util.get_protocol()
+            protocol.update_goal_state()
+
+            # Log OS-specific info.
+            os_info_msg = u"Distro info: {0} {1}, osutil class being used: {2}, agent service name: {3}"\
+                .format(DISTRO_NAME, DISTRO_VERSION,type(self.osutil).__name__, self.osutil.service_name)
+
             logger.info(os_info_msg)
 
+            add_event(AGENT_NAME, op=WALAEventOperation.OSInfo, message=os_info_msg)
+
             # Launch monitoring threads
             from azurelinuxagent.ga.monitor import get_monitor_handler
             monitor_thread = get_monitor_handler()
             monitor_thread.run()
 
-            # NOTE: Any telemetry events added from this point on will be properly populated with the container id.
-
             from azurelinuxagent.ga.env import get_env_handler
             env_thread = get_env_handler()
             env_thread.run()
 
-            protocol = self.protocol_util.get_protocol()
-
             from azurelinuxagent.ga.exthandlers import get_exthandlers_handler, migrate_handler_state
             exthandlers_handler = get_exthandlers_handler(protocol)
             migrate_handler_state()
@@ -288,15 +287,7 @@ class UpdateHandler(object):
             self._ensure_readonly_files()
             self._ensure_cgroups_initialized()
 
-            # Send OS-specific info as a telemetry event after the monitoring thread has been initialized, and with
-            # it the container id too.
-            add_event(AGENT_NAME,
-                      op=WALAEventOperation.OSInfo,
-                      message=os_info_msg)
-
-            goal_state_interval = GOAL_STATE_INTERVAL \
-                if conf.get_extensions_enabled() \
-                else GOAL_STATE_INTERVAL_DISABLED
+            goal_state_interval = GOAL_STATE_INTERVAL if conf.get_extensions_enabled() else GOAL_STATE_INTERVAL_DISABLED
 
             while self.running:
                 #


Test patch:
diff --git a/tests/ga/test_monitor.py b/tests/ga/test_monitor.py
index dcd3f4b2..7c39a09d 100644
--- a/tests/ga/test_monitor.py
+++ b/tests/ga/test_monitor.py
@@ -621,7 +621,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 +850,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 +857,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 +1243,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..e4eea3a8 100644
--- a/tests/ga/test_update.py
+++ b/tests/ga/test_update.py
@@ -1426,64 +1426,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, _):
         """
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: