-
Notifications
You must be signed in to change notification settings - Fork 372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Log collector should not fetch full goal state with extensions #2713
Changes from 20 commits
da72c37
59dbd22
633a826
14a743f
55e261a
908985e
8fc6471
3d370d0
d5bb016
2633959
b8d98a5
e578129
c642900
041dee3
cbfb6ad
05f4b79
dc2fe69
a8c5a7f
011fbef
b833735
d762a03
11f9c3a
212a72b
7805dc1
b1d21ea
c61a067
dd9d8da
7711ae2
3926b35
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,6 +48,19 @@ | |
_GET_GOAL_STATE_MAX_ATTEMPTS = 6 | ||
|
||
|
||
class GoalStateProperties(object): | ||
""" | ||
Enum for defining the properties that we fetch in the goal state | ||
""" | ||
RoleConfig = 0x1 | ||
HostingEnv = 0x2 | ||
SharedConfig = 0x4 | ||
ExtensionsConfig = 0x8 | ||
Certificates = 0x10 | ||
RemoteAccessInfo = 0x20 | ||
All = RoleConfig | HostingEnv | SharedConfig | ExtensionsConfig | Certificates | RemoteAccessInfo | ||
|
||
|
||
class GoalStateInconsistentError(ProtocolError): | ||
""" | ||
Indicates an inconsistency in the goal state (e.g. missing tenant certificate) | ||
|
@@ -57,7 +70,7 @@ def __init__(self, msg, inner=None): | |
|
||
|
||
class GoalState(object): | ||
def __init__(self, wire_client, silent=False): | ||
def __init__(self, wire_client, goal_state_properties=GoalStateProperties.All, silent=False): | ||
""" | ||
Fetches the goal state using the given wire client. | ||
|
||
|
@@ -72,6 +85,7 @@ def __init__(self, wire_client, silent=False): | |
self._wire_client = wire_client | ||
self._history = None | ||
self._extensions_goal_state = None # populated from vmSettings or extensionsConfig | ||
self._goal_state_properties = goal_state_properties | ||
self.logger = logger.Logger(logger.DEFAULT_LOGGER) | ||
self.logger.silent = silent | ||
|
||
|
@@ -99,35 +113,59 @@ def incarnation(self): | |
|
||
@property | ||
def container_id(self): | ||
return self._container_id | ||
if not self._goal_state_properties & GoalStateProperties.RoleConfig: | ||
raise ProtocolError("RoleConfig is not in goal state properties") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. container id |
||
else: | ||
return self._container_id | ||
|
||
@property | ||
def role_instance_id(self): | ||
return self._role_instance_id | ||
if not self._goal_state_properties & GoalStateProperties.RoleConfig: | ||
raise ProtocolError("RoleConfig is not in goal state properties") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. role instance id |
||
else: | ||
return self._role_instance_id | ||
|
||
@property | ||
def role_config_name(self): | ||
return self._role_config_name | ||
if not self._goal_state_properties & GoalStateProperties.RoleConfig: | ||
raise ProtocolError("RoleConfig is not in goal state properties") | ||
else: | ||
return self._role_config_name | ||
|
||
@property | ||
def extensions_goal_state(self): | ||
return self._extensions_goal_state | ||
if not self._goal_state_properties & GoalStateProperties.ExtensionsConfig: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, i did not notice this before. the extensions goal state may come from vmSettings or ExtensionsConfig. The latter is for fabric and the former for fast track. Let's rename GoalStateProperties.ExtensionsConfig to GoalStateProperties.ExtensionsGoalState There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, in _update(), we need to retrieve the vmSettings only when requesting GoalStateProperties.ExtensionsGoalState There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to add this check |
||
raise ProtocolError("ExtensionsConfig is not in goal state properties") | ||
else: | ||
return self._extensions_goal_state | ||
|
||
@property | ||
def certs(self): | ||
return self._certs | ||
if not self._goal_state_properties & GoalStateProperties.Certificates: | ||
raise ProtocolError("Certificates is not in goal state properties") | ||
else: | ||
return self._certs | ||
|
||
@property | ||
def hosting_env(self): | ||
return self._hosting_env | ||
if not self._goal_state_properties & GoalStateProperties.HostingEnv: | ||
raise ProtocolError("HostingEnvironment is not in goal state properties") | ||
else: | ||
return self._hosting_env | ||
|
||
@property | ||
def shared_conf(self): | ||
return self._shared_conf | ||
if not self._goal_state_properties & GoalStateProperties.SharedConfig: | ||
raise ProtocolError("SharedConfig is not in goal state properties") | ||
else: | ||
return self._shared_conf | ||
|
||
@property | ||
def remote_access(self): | ||
return self._remote_access | ||
if not self._goal_state_properties & GoalStateProperties.RemoteAccessInfo: | ||
raise ProtocolError("RemoteAccessInfo is not in goal state properties") | ||
else: | ||
return self._remote_access | ||
|
||
def fetch_agent_manifest(self, family_name, uris): | ||
""" | ||
|
@@ -356,40 +394,48 @@ def _fetch_full_wire_server_goal_state(self, incarnation, xml_doc): | |
self.logger.info(message) | ||
add_event(op=WALAEventOperation.GoalState, message=message) | ||
|
||
role_instance = find(xml_doc, "RoleInstance") | ||
role_instance_id = findtext(role_instance, "InstanceId") | ||
role_config = find(role_instance, "Configuration") | ||
role_config_name = findtext(role_config, "ConfigName") | ||
container = find(xml_doc, "Container") | ||
container_id = findtext(container, "ContainerId") | ||
role_instance_id = None | ||
role_config_name = None | ||
container_id = None | ||
if GoalStateProperties.RoleConfig & self._goal_state_properties: | ||
role_instance = find(xml_doc, "RoleInstance") | ||
role_instance_id = findtext(role_instance, "InstanceId") | ||
role_config = find(role_instance, "Configuration") | ||
role_config_name = findtext(role_config, "ConfigName") | ||
container = find(xml_doc, "Container") | ||
container_id = findtext(container, "ContainerId") | ||
|
||
extensions_config_uri = findtext(xml_doc, "ExtensionsConfig") | ||
if extensions_config_uri is None: | ||
if not (GoalStateProperties.ExtensionsConfig & self._goal_state_properties) or extensions_config_uri is None: | ||
extensions_config = ExtensionsGoalStateFactory.create_empty(incarnation) | ||
else: | ||
xml_text = self._wire_client.fetch_config(extensions_config_uri, self._wire_client.get_header()) | ||
extensions_config = ExtensionsGoalStateFactory.create_from_extensions_config(incarnation, xml_text, self._wire_client) | ||
self._history.save_extensions_config(extensions_config.get_redacted_text()) | ||
|
||
hosting_env_uri = findtext(xml_doc, "HostingEnvironmentConfig") | ||
xml_text = self._wire_client.fetch_config(hosting_env_uri, self._wire_client.get_header()) | ||
hosting_env = HostingEnv(xml_text) | ||
self._history.save_hosting_env(xml_text) | ||
|
||
shared_conf_uri = findtext(xml_doc, "SharedConfig") | ||
xml_text = self._wire_client.fetch_config(shared_conf_uri, self._wire_client.get_header()) | ||
shared_config = SharedConfig(xml_text) | ||
self._history.save_shared_conf(xml_text) | ||
# SharedConfig.xml is used by other components (Azsec and Singularity/HPC Infiniband), so save it to the agent's root directory as well | ||
shared_config_file = os.path.join(conf.get_lib_dir(), SHARED_CONF_FILE_NAME) | ||
try: | ||
fileutil.write_file(shared_config_file, xml_text) | ||
except Exception as e: | ||
logger.warn("Failed to save {0}: {1}".format(shared_config, e)) | ||
hosting_env = None | ||
if GoalStateProperties.HostingEnv & self._goal_state_properties: | ||
hosting_env_uri = findtext(xml_doc, "HostingEnvironmentConfig") | ||
xml_text = self._wire_client.fetch_config(hosting_env_uri, self._wire_client.get_header()) | ||
hosting_env = HostingEnv(xml_text) | ||
self._history.save_hosting_env(xml_text) | ||
|
||
shared_config = None | ||
if GoalStateProperties.SharedConfig & self._goal_state_properties: | ||
shared_conf_uri = findtext(xml_doc, "SharedConfig") | ||
xml_text = self._wire_client.fetch_config(shared_conf_uri, self._wire_client.get_header()) | ||
shared_config = SharedConfig(xml_text) | ||
self._history.save_shared_conf(xml_text) | ||
# SharedConfig.xml is used by other components (Azsec and Singularity/HPC Infiniband), so save it to the agent's root directory as well | ||
shared_config_file = os.path.join(conf.get_lib_dir(), SHARED_CONF_FILE_NAME) | ||
try: | ||
fileutil.write_file(shared_config_file, xml_text) | ||
except Exception as e: | ||
logger.warn("Failed to save {0}: {1}".format(shared_config, e)) | ||
|
||
certs = EmptyCertificates() | ||
certs_uri = findtext(xml_doc, "Certificates") | ||
if certs_uri is not None: | ||
if (GoalStateProperties.Certificates & self._goal_state_properties) and certs_uri is not None: | ||
xml_text = self._wire_client.fetch_config(certs_uri, self._wire_client.get_header_for_cert()) | ||
certs = Certificates(xml_text, self.logger) | ||
# Log and save the certificates summary (i.e. the thumbprint but not the certificate itself) to the goal state history | ||
|
@@ -403,11 +449,12 @@ def _fetch_full_wire_server_goal_state(self, incarnation, xml_doc): | |
self._history.save_certificates(json.dumps(certs.summary)) | ||
|
||
remote_access = None | ||
remote_access_uri = findtext(container, "RemoteAccessInfo") | ||
if remote_access_uri is not None: | ||
xml_text = self._wire_client.fetch_config(remote_access_uri, self._wire_client.get_header_for_cert()) | ||
remote_access = RemoteAccess(xml_text) | ||
self._history.save_remote_access(xml_text) | ||
if GoalStateProperties.RemoteAccessInfo & self._goal_state_properties: | ||
remote_access_uri = findtext(container, "RemoteAccessInfo") | ||
if remote_access_uri is not None: | ||
xml_text = self._wire_client.fetch_config(remote_access_uri, self._wire_client.get_header_for_cert()) | ||
remote_access = RemoteAccess(xml_text) | ||
self._history.save_remote_access(xml_text) | ||
|
||
self._incarnation = incarnation | ||
self._role_instance_id = role_instance_id | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,7 +37,8 @@ | |
from azurelinuxagent.common.exception import ProtocolNotFoundError, \ | ||
ResourceGoneError, ExtensionDownloadError, InvalidContainerError, ProtocolError, HttpError, ExtensionErrorCodes | ||
from azurelinuxagent.common.future import httpclient, bytebuffer, ustr | ||
from azurelinuxagent.common.protocol.goal_state import GoalState, TRANSPORT_CERT_FILE_NAME, TRANSPORT_PRV_FILE_NAME | ||
from azurelinuxagent.common.protocol.goal_state import GoalState, TRANSPORT_CERT_FILE_NAME, TRANSPORT_PRV_FILE_NAME, \ | ||
GoalStateProperties | ||
from azurelinuxagent.common.protocol.hostplugin import HostPluginProtocol | ||
from azurelinuxagent.common.protocol.restapi import DataContract, ProvisionStatus, VMInfo, VMStatus | ||
from azurelinuxagent.common.telemetryevent import GuestAgentExtensionEventsSchema | ||
|
@@ -84,10 +85,7 @@ def detect(self): | |
|
||
# Initialize the goal state, including all the inner properties | ||
logger.info('Initializing goal state during protocol detection') | ||
self.client.update_goal_state(force_update=True) | ||
|
||
def update_goal_state(self, silent=False): | ||
self.client.update_goal_state(silent=silent) | ||
self.client.reset_goal_state() | ||
|
||
def update_host_plugin_from_goal_state(self): | ||
self.client.update_host_plugin_from_goal_state() | ||
|
@@ -778,18 +776,30 @@ def update_host_plugin(self, container_id, role_config_name): | |
self._host_plugin.update_container_id(container_id) | ||
self._host_plugin.update_role_config_name(role_config_name) | ||
|
||
def update_goal_state(self, force_update=False, silent=False): | ||
def update_goal_state(self, silent=False): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't quite get the update_goal_state vs reset_goal_state. When do we use each of these? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're removing the force_update parameter and using reset_goal_state in all cases where force_update was True. Reset will initialize the goal state instead of updating |
||
""" | ||
Updates the goal state if the incarnation or etag changed or if 'force_update' is True | ||
Updates the goal state if the incarnation or etag changed | ||
""" | ||
try: | ||
if force_update and not silent: | ||
if not silent: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can remove this message since the force flag was removed |
||
logger.info("Forcing an update of the goal state.") | ||
|
||
if self._goal_state is None or force_update: | ||
self._goal_state = GoalState(self, silent=silent) | ||
else: | ||
self._goal_state.update(silent=silent) | ||
self._goal_state.update(silent=silent) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to keep a call to reset here (minus the check for the force flag). This is needed by the initialization of the goal state on line 430 that you pointed out
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added the None check back |
||
|
||
except ProtocolError: | ||
raise | ||
except Exception as exception: | ||
raise ProtocolError("Error fetching goal state: {0}".format(ustr(exception))) | ||
|
||
def reset_goal_state(self, goal_state_properties=GoalStateProperties.All, silent=False): | ||
""" | ||
Resets the goal state | ||
""" | ||
try: | ||
if not silent: | ||
logger.info("Forcing an update of the goal state.") | ||
|
||
self._goal_state = GoalState(self, goal_state_properties=goal_state_properties, silent=silent) | ||
|
||
except ProtocolError: | ||
raise | ||
|
@@ -925,7 +935,7 @@ def upload_status_blob(self): | |
|
||
if extensions_goal_state.status_upload_blob is None: | ||
# the status upload blob is in ExtensionsConfig so force a full goal state refresh | ||
self.update_goal_state(force_update=True, silent=True) | ||
self.reset_goal_state(silent=True) | ||
extensions_goal_state = self.get_goal_state().extensions_goal_state | ||
|
||
if extensions_goal_state.status_upload_blob is None: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -485,7 +485,7 @@ def _try_update_goal_state(self, protocol): | |
try: | ||
max_errors_to_log = 3 | ||
|
||
protocol.update_goal_state(silent=self._update_goal_state_error_count >= max_errors_to_log) | ||
protocol.client.update_goal_state(silent=self._update_goal_state_error_count >= max_errors_to_log) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @narrieta On line 430 in update.py we discussed changing It looks like we sleep until try_update_goal_state returns True for success.. Should we implement similar logic for reset_goal_state? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point. i added a comment on _try_update_goal_state |
||
|
||
self._goal_state = protocol.get_goal_state() | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like get_protocal() still initializing goal state with all properties?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 - we probably need to add a flag to get_protocol to not initialize the shared goal state (then the call to reset in the next line would initialize it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added flag to get_protocol()