-
Notifications
You must be signed in to change notification settings - Fork 11
Adding status file for non enable commands and catching early errors #144
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
Changes from all commits
ad2e576
52ae945
76ef9d4
4aceeac
bb46e3c
b6453c0
054e02c
4b2ced2
b45aa59
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,7 @@ def __init__(self, logger, env_layer, telemetry_writer, utility, runtime_context | |
self.stdout_file_mirror = None | ||
self.file_logger = None | ||
self.operation_id_substitute_for_all_actions_in_telemetry = str((datetime.datetime.utcnow()).strftime(Constants.UTC_DATETIME_FORMAT)) | ||
self.seq_no = self.ext_config_settings_handler.get_seq_no_from_env_var() | ||
|
||
def determine_operation(self, command): | ||
switcher = { | ||
|
@@ -64,32 +65,49 @@ def determine_operation(self, command): | |
raise e | ||
|
||
def setup(self, action, log_message): | ||
self.setup_file_logger(action) | ||
self.setup_telemetry() | ||
self.logger.log(log_message) | ||
if action == Constants.ENABLE: | ||
self.write_basic_status() | ||
try: | ||
self.setup_file_logger(action) | ||
self.validate_sudo() # Validating sudo as early as possible and before it's use in setup_telemetry | ||
self.setup_telemetry() | ||
self.logger.log(log_message) | ||
except Exception: | ||
raise | ||
finally: | ||
self.write_basic_status(action) | ||
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. curiosity: If lines 69 through 72 throw, can the exception be written to the basic status file and then rethrown? It looks like you'll have to write basic status on line 73 at the end of the try and within the except. In that case a finally will no longer be necessary. 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. With basic status, we set the top level status to "transitioning". Now incase of exceptions, and all these are currently terminal, the status is re-written to "error" The way the code is in all setup() callers right now, we can have write_basic_status() on line 72, just before except, so a basic status (with status=Transitioning) is written in a happy path, and incase of exceptions, there is only 1 status write with status=error (instead of 2 writes). But thought of keeping write_basic_status() in finally, in case something changes in callers later |
||
|
||
def validate_sudo(self): | ||
# Disable tty for sudo access, if required | ||
self.env_health_manager.ensure_tty_not_required() | ||
|
||
# Ensure sudo works in the environment | ||
sudo_check_result = self.env_health_manager.check_sudo_status() | ||
self.logger.log_debug("Sudo status check: " + str(sudo_check_result) + "\n") | ||
|
||
def write_basic_status(self): | ||
def write_basic_status(self, action): | ||
""" Writes a basic status file if one for the same sequence number does not exist """ | ||
try: | ||
# read seq no, if not found, log error and return, as this code opportunistically tries to write status file as early as possible | ||
seq_no = self.ext_config_settings_handler.get_seq_no_from_env_var() | ||
if seq_no is None: | ||
if self.seq_no is None: | ||
self.logger.log_error("Since sequence number for current operation was not found, handler could not write an initial/basic status file") | ||
return | ||
|
||
# check if a status file for this sequence exists, if yes, do nothing | ||
if not os.path.exists(os.path.join(self.ext_env_handler.status_folder, str(seq_no) + Constants.STATUS_FILE_EXTENSION)): | ||
config_settings = self.ext_config_settings_handler.read_file(seq_no) | ||
if os.path.exists(os.path.join(self.ext_env_handler.status_folder, str(self.seq_no) + Constants.STATUS_FILE_EXTENSION)): | ||
return | ||
|
||
# if status file for this sequence does not exist and enable is executed, read and use values from config setting file, else do not set those in status file | ||
rane-rajasi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
operation = "" | ||
if action == Constants.ENABLE: | ||
config_settings = self.ext_config_settings_handler.read_file(self.seq_no) | ||
|
||
# set activity_id in telemetry | ||
if self.telemetry_writer is not None: | ||
self.telemetry_writer.set_operation_id(config_settings.__getattribute__(Constants.ConfigPublicSettingsFields.activity_id)) | ||
|
||
operation = config_settings.__getattribute__(Constants.ConfigPublicSettingsFields.operation) | ||
# create status file with basic status | ||
self.ext_output_status_handler.write_status_file(operation, seq_no) | ||
|
||
# create status file with basic status | ||
self.ext_output_status_handler.write_status_file(operation, self.seq_no) | ||
|
||
except Exception as error: | ||
self.logger.log_error("Exception occurred while writing basic status. [Exception={0}]".format(repr(error))) | ||
|
@@ -159,10 +177,16 @@ def install(self): | |
try: | ||
self.setup(action=Constants.INSTALL, log_message="Extension installation started") | ||
install_command_handler = InstallCommandHandler(self.logger, self.ext_env_handler) | ||
return install_command_handler.execute_handler_action() | ||
exit_code_from_executing_install = install_command_handler.execute_handler_action() | ||
rane-rajasi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if exit_code_from_executing_install == Constants.ExitCode.Okay or exit_code_from_executing_install is None: | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Success.lower()) | ||
else: | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message="Error occurred during extension install", code=exit_code_from_executing_install) | ||
return exit_code_from_executing_install | ||
|
||
except Exception as error: | ||
self.logger.log_error("Error occurred during extension install. [Error={0}]".format(repr(error))) | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message="Error occurred during extension install", code=Constants.ExitCode.HandlerFailed) | ||
return Constants.ExitCode.HandlerFailed | ||
|
||
finally: | ||
|
@@ -191,27 +215,34 @@ def update(self): | |
# Extension Update action called when | ||
# a) artifacts for the preceding version do not exist on the machine, or | ||
# b) after all artifacts from the preceding versions have been deleted | ||
self.logger.log_error("No earlier versions for the extension found on the machine. So, could not copy any references to the current version.") | ||
error_msg = "No earlier versions for the extension found on the machine. So, could not copy any references to the current version." | ||
self.logger.log_error(error_msg) | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message=error_msg, code=Constants.ExitCode.HandlerFailed) | ||
return Constants.ExitCode.HandlerFailed | ||
|
||
# identify the version preceding current | ||
self.logger.log("Fetching the extension version preceding current from all available versions...") | ||
paths_to_all_versions.sort(reverse=True, key=LooseVersion) | ||
preceding_version_path = paths_to_all_versions[1] | ||
if preceding_version_path is None or preceding_version_path == "" or not os.path.exists(preceding_version_path): | ||
self.logger.log_error("Could not find path where preceding extension version artifacts are stored. Hence, cannot copy the required artifacts to the latest version. " | ||
"[Preceding extension version path={0}]".format(str(preceding_version_path))) | ||
error_msg = "Could not find path where preceding extension version artifacts are stored. Hence, cannot copy the required artifacts to the latest version. "\ | ||
"[Preceding extension version path={0}]".format(str(preceding_version_path)) | ||
self.logger.log_error(error_msg) | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message=error_msg, code=Constants.ExitCode.HandlerFailed) | ||
return Constants.ExitCode.HandlerFailed | ||
|
||
self.logger.log("Preceding version path. [Path={0}]".format(str(preceding_version_path))) | ||
|
||
# copy all required files from preceding version to current | ||
self.copy_config_files(preceding_version_path, new_version_config_folder) | ||
|
||
self.logger.log("All update actions from extension handler completed.") | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Success.lower()) | ||
return Constants.ExitCode.Okay | ||
|
||
except Exception as error: | ||
self.logger.log_error("Error occurred during extension update. [Error={0}]".format(repr(error))) | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message="Error occurred during extension update", code=Constants.ExitCode.HandlerFailed) | ||
return Constants.ExitCode.HandlerFailed | ||
|
||
finally: | ||
|
@@ -260,10 +291,12 @@ def copy_config_files(self, src, dst, raise_if_not_copied=False): | |
def uninstall(self): | ||
try: | ||
self.setup(action=Constants.UNINSTALL, log_message="Extension uninstalled") | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Success.lower()) | ||
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. Please check with Jemal about the cases where the Windows extension writes a status file to ensure alignment between the extensions. 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. Done, windows extension also writes status only for enable. Windows agent may write status file for enable, this is considered by our extension while writing status. As for other handler actions, we aren't sure if agent does write status and the extension does not write status |
||
return Constants.ExitCode.Okay | ||
|
||
except Exception as error: | ||
self.logger.log_error("Error occurred during extension uninstall. [Error={0}]".format(repr(error))) | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message="Error occurred during extension uninstall", code=Constants.ExitCode.HandlerFailed) | ||
return Constants.ExitCode.HandlerFailed | ||
|
||
finally: | ||
|
@@ -273,10 +306,14 @@ def enable(self): | |
try: | ||
self.setup(action=Constants.ENABLE, log_message="Enable triggered on extension") | ||
enable_command_handler = EnableCommandHandler(self.logger, self.telemetry_writer, self.utility, self.env_health_manager, self.runtime_context_handler, self.ext_env_handler, self.ext_config_settings_handler, self.core_state_handler, self.ext_state_handler, self.ext_output_status_handler, self.process_handler, self.cmd_exec_start_time) | ||
return enable_command_handler.execute_handler_action() | ||
exit_code_returned_from_executing_enable = enable_command_handler.execute_handler_action() | ||
if exit_code_returned_from_executing_enable is not None and exit_code_returned_from_executing_enable != Constants.ExitCode.Okay: | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message="Error occurred during extension enable", code=exit_code_returned_from_executing_enable) | ||
return Constants.ExitCode.Okay if exit_code_returned_from_executing_enable is None else exit_code_returned_from_executing_enable | ||
|
||
except Exception as error: | ||
self.logger.log_error("Error occurred during extension enable. [Error={0}]".format(repr(error))) | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message="Error occurred during extension enable", code=Constants.ExitCode.HandlerFailed) | ||
rane-rajasi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return Constants.ExitCode.HandlerFailed | ||
finally: | ||
self.tear_down() | ||
|
@@ -306,10 +343,12 @@ def disable(self): | |
# End of temporary auto-assessment disablement | ||
|
||
self.logger.log("Extension disabled successfully") | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Success.lower()) | ||
return Constants.ExitCode.Okay | ||
|
||
except Exception as error: | ||
self.logger.log_error("Error occurred during extension disable. [Error={0}]".format(repr(error))) | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message="Error occurred during extension disable", code=Constants.ExitCode.HandlerFailed) | ||
return Constants.ExitCode.HandlerFailed | ||
finally: | ||
self.tear_down() | ||
|
@@ -319,10 +358,12 @@ def reset(self): | |
self.setup(action=Constants.RESET, log_message="Reset triggered on extension, deleting CoreState and ExtState files") | ||
self.utility.delete_file(self.core_state_handler.dir_path, self.core_state_handler.file, raise_if_not_found=False) | ||
self.utility.delete_file(self.ext_state_handler.dir_path, self.ext_state_handler.file, raise_if_not_found=False) | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Success.lower()) | ||
return Constants.ExitCode.Okay | ||
|
||
except Exception as error: | ||
self.logger.log_error("Error occurred during extension reset. [Error={0}]".format(repr(error))) | ||
self.ext_output_status_handler.write_status_file("", self.seq_no, status=Constants.Status.Error.lower(), message="Error occurred during extension reset", code=Constants.ExitCode.HandlerFailed) | ||
return Constants.ExitCode.HandlerFailed | ||
finally: | ||
self.tear_down() | ||
|
Uh oh!
There was an error while loading. Please reload this page.