Skip to content
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

Adding EULA acceptance based on an inVM approval mechanism #215

Merged
merged 12 commits into from
Sep 14, 2023
Merged
Prev Previous commit
Next Next commit
Adding EULA acceptance: Addressing PR feedback #1
  • Loading branch information
rane-rajasi committed Sep 11, 2023

Verified

This commit was signed with the committer’s verified signature.
commit a27112669c8e9672aabddd855e22e408b7dd4d39
60 changes: 57 additions & 3 deletions src/core/src/bootstrap/ConfigurationFactory.py
Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@

""" Configure factory. This module populates configuration based on package manager and environment, e.g. TEST/DEV/PROD"""
from __future__ import print_function

import json
import os
import time
from core.src.bootstrap.Constants import Constants
@@ -91,16 +93,16 @@ def get_bootstrap_configuration(self, env):
configuration_key = str.lower('{0}_config'.format(str(env)))
return self.bootstrap_configurations[configuration_key]

@staticmethod
def get_arguments_configuration(argv):
def get_arguments_configuration(self, argv):
""" Composes the configuration with the passed in arguments. """
arguments_config = {
'execution_arguments': str(argv),
'execution_config': {
'component': ExecutionConfig,
'component_args': ['env_layer', 'composite_logger'],
'component_kwargs': {
'execution_parameters': str(argv)
'execution_parameters': str(argv),
'accept_package_eula': self.accept_package_eula(),
}
}
}
@@ -298,4 +300,56 @@ def get_vm_cloud_type():
else:
print("Failed to connect IMDS end point after 5 retries. This is expected in Arc VMs. VMCloudType is set to Arc.\n")
return Constants.VMCloudType.ARC

def accept_package_eula(self):
""" Reads customer provided config on EULA acceptance from disk and returns a boolean.
NOTE: This is a temporary solution and will be deprecated no later than TBD date"""
if not os.path.exists(Constants.Paths.EULA_SETTINGS):
print("NOT accepting EULA for any patch as no corresponding EULA Settings found on the VM")
return False

try:
eula_settings = json.loads(self.read_with_retry(Constants.Paths.EULA_SETTINGS) or 'null')
rane-rajasi marked this conversation as resolved.
Show resolved Hide resolved
if eula_settings is not None \
and Constants.EulaSettings.ACCEPT_EULA_FOR_ALL_PATCHES in eula_settings \
and eula_settings[Constants.EulaSettings.ACCEPT_EULA_FOR_ALL_PATCHES] is True:
print("Accept EULA set to True in customer config")
return True
else:
print("Accept EULA not found to be set to True in customer config")
return False
except Exception as error:
print("Error occurred while reading and parsing EULA settings. Not accepting EULA for any patch. Error=[{0}]".format(repr(error)))
return False

@staticmethod
def open(file_path, mode):
""" Provides a file handle to the file_path requested using implicit redirection where required """
for i in range(0, Constants.MAX_FILE_OPERATION_RETRY_COUNT):
try:
return open(file_path, mode)
except Exception as error:
if i < Constants.MAX_FILE_OPERATION_RETRY_COUNT - 1:
time.sleep(i + 1)
else:
raise Exception("Unable to open {0} (retries exhausted). Error: {1}.".format(str(file_path), repr(error)))

def __obtain_file_handle(self, file_path_or_handle, mode='a+'):
""" Pass-through for handle. For path, resolution and handle open with retry. """
is_path = False
if isinstance(file_path_or_handle, str) or not (hasattr(file_path_or_handle, 'read') and hasattr(file_path_or_handle, 'write')):
is_path = True
file_path_or_handle = self.open(file_path_or_handle, mode)
file_handle = file_path_or_handle
return file_handle, is_path

def read_with_retry(self, file_path_or_handle):
""" Reads all content from a given file path in a single operation """
if isinstance(file_path_or_handle, str):
file_handle, was_path = self.__obtain_file_handle(file_path_or_handle, 'r')
value = file_handle.read()
if was_path: # what was passed in was not a file handle, so close the handle that was init here
file_handle.close()
return value
return None
# endregion
5 changes: 4 additions & 1 deletion src/core/src/core_logic/ExecutionConfig.py
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@


class ExecutionConfig(object):
def __init__(self, env_layer, composite_logger, execution_parameters):
def __init__(self, env_layer, composite_logger, execution_parameters, accept_package_eula):
self.env_layer = env_layer
self.composite_logger = composite_logger
self.execution_parameters = eval(execution_parameters)
@@ -86,6 +86,9 @@ def __init__(self, env_layer, composite_logger, execution_parameters):
else:
self.composite_logger.log_debug("Not executing in auto-assessment mode.")

# EULA config
self.accept_package_eula = accept_package_eula

def __transform_execution_config_for_auto_assessment(self):
self.activity_id = str(uuid.uuid4())
self.included_classifications_list = self.included_package_name_mask_list = self.excluded_package_name_mask_list = []
35 changes: 7 additions & 28 deletions src/core/src/package_managers/AptitudePackageManager.py
Original file line number Diff line number Diff line change
@@ -34,6 +34,10 @@ def __init__(self, env_layer, execution_config, composite_logger, telemetry_writ
super(AptitudePackageManager, self).__init__(env_layer, execution_config, composite_logger, telemetry_writer, status_handler)

security_list_guid = str(uuid.uuid4())

# Accept EULA (End User License Agreement) as per the EULA settings set by user
optional_accept_eula_in_cmd = "ACCEPT_EULA=Y" if execution_config.accept_package_eula else ""

# Repo refresh
self.repo_refresh = 'sudo apt-get -q update'

@@ -44,15 +48,12 @@ def __init__(self, env_layer, execution_config, composite_logger, telemetry_writ
self.single_package_check_versions = 'apt-cache madison <PACKAGE-NAME>'
self.single_package_find_installed_dpkg = 'sudo dpkg -s <PACKAGE-NAME>'
self.single_package_find_installed_apt = 'sudo apt list --installed <PACKAGE-NAME>'
self.single_package_upgrade_simulation_cmd = '''DEBIAN_FRONTEND=noninteractive apt-get -y --only-upgrade true -s install '''
self.single_package_dependency_resolution_template = 'DEBIAN_FRONTEND=noninteractive LANG=en_US.UTF8 apt-get -y --only-upgrade true -s install <PACKAGE-NAME> '
self.single_package_upgrade_simulation_cmd = '''DEBIAN_FRONTEND=noninteractive ''' + optional_accept_eula_in_cmd + ''' apt-get -y --only-upgrade true -s install '''
self.single_package_dependency_resolution_template = 'DEBIAN_FRONTEND=noninteractive ' + optional_accept_eula_in_cmd + ' LANG=en_US.UTF8 apt-get -y --only-upgrade true -s install <PACKAGE-NAME> '

# Install update
# --only-upgrade: upgrade only single package (only if it is installed)
self.single_package_upgrade_cmd = '''sudo DEBIAN_FRONTEND=noninteractive apt-get -y --only-upgrade true install '''

# Accept EULA (End User License Agreement) as per the EULA settings set by user
self.accept_eula_for_patches()
self.single_package_upgrade_cmd = '''sudo DEBIAN_FRONTEND=noninteractive ''' + optional_accept_eula_in_cmd + ''' apt-get -y --only-upgrade true install '''

# Package manager exit code(s)
self.apt_exitcode_ok = 0
@@ -292,28 +293,6 @@ def get_composite_package_identifier(self, package, package_version):
def install_updates_fail_safe(self, excluded_packages):
return

def accept_eula_for_patches(self):
""" Accepts eula for patches based on the config provided by customers """
if not os.path.exists(Constants.Paths.EULA_SETTINGS):
self.composite_logger.log_warning("NOT accepting EULA for any patch as no corresponding EULA Settings found on the VM")
return

try:
eula_settings = json.loads(self.env_layer.file_system.read_with_retry(Constants.Paths.EULA_SETTINGS, raise_if_not_found=False) or 'null')

if eula_settings is not None \
and Constants.EulaSettings.ACCEPT_EULA_FOR_ALL_PATCHES in eula_settings \
and eula_settings[Constants.EulaSettings.ACCEPT_EULA_FOR_ALL_PATCHES] is True:
self.composite_logger.log_debug("Accepting EULA for all patches as per the EULA setting found on VM")
self.single_package_upgrade_simulation_cmd += " ACCEPT_EULA=Y"
self.single_package_dependency_resolution_template += " ACCEPT_EULA=Y"
self.single_package_upgrade_cmd += " ACCEPT_EULA=Y"
else:
self.composite_logger.log_warning("NOT accepting EULA for any patch as no corresponding acceptance for EULA found on the VM")
return
except Exception as error:
self.composite_logger.log_warning("Error occurred while reading and parsing EULA settings. Not accepting EULA for any patch. Error=[{0}]".format(repr(error)))

# endregion

# region Package Information
4 changes: 0 additions & 4 deletions src/core/src/package_managers/PackageManager.py
Original file line number Diff line number Diff line change
@@ -329,10 +329,6 @@ def install_update_and_dependencies_and_get_status(self, package_and_dependencie
install_result = self.get_installation_status(code, out, exec_cmd, package_and_dependencies[0], package_and_dependency_versions[0], simulate)
return install_result

def accept_eula_for_patches(self):
""" Accepts eula for patches based on the config provided by customers """
pass

# endregion

# region Package Information
4 changes: 0 additions & 4 deletions src/core/src/package_managers/YumPackageManager.py
Original file line number Diff line number Diff line change
@@ -245,10 +245,6 @@ def install_updates_fail_safe(self, excluded_packages):

self.composite_logger.log_debug("[FAIL SAFE MODE] UPDATING PACKAGES USING COMMAND: " + cmd)
self.invoke_package_manager(cmd)

def accept_eula_for_patches(self):
""" Accepts eula for patches based on the config provided by customers """
pass
# endregion

# region Package Information
4 changes: 0 additions & 4 deletions src/core/src/package_managers/ZypperPackageManager.py
Original file line number Diff line number Diff line change
@@ -415,10 +415,6 @@ def get_composite_package_identifier(self, package, package_version):

def install_updates_fail_safe(self, excluded_packages):
return

def accept_eula_for_patches(self):
""" Accepts eula for patches based on the config provided by customers """
pass
# endregion

# region Package Information
26 changes: 17 additions & 9 deletions src/core/tests/Test_AptitudePackageManager.py
Original file line number Diff line number Diff line change
@@ -26,7 +26,8 @@

class TestAptitudePackageManager(unittest.TestCase):
def setUp(self):
self.runtime = RuntimeCompositor(ArgumentComposer().get_composed_arguments(), True, Constants.APT)
self.argument_composer = ArgumentComposer().get_composed_arguments()
self.runtime = RuntimeCompositor(self.argument_composer, True, Constants.APT)
self.container = self.runtime.container

def tearDown(self):
@@ -575,10 +576,13 @@ def test_eula_acceptance_for_patches(self):
"LastModified": "2023-08-29"
}
self.runtime.env_layer.file_system.write_with_retry(Constants.Paths.EULA_SETTINGS, '{0}'.format(json.dumps(eula_settings)), mode='w+')
package_manager.accept_eula_for_patches()
self.assertTrue("ACCEPT_EULA=Y" in package_manager.single_package_upgrade_simulation_cmd)
self.assertTrue("ACCEPT_EULA=Y" in package_manager.single_package_dependency_resolution_template)
self.assertTrue("ACCEPT_EULA=Y" in package_manager.single_package_upgrade_cmd)
self.runtime_for_test = RuntimeCompositor(self.argument_composer, True, Constants.APT)
self.container_for_test = self.runtime_for_test.container
package_manager_for_test = self.container_for_test.get('package_manager')

self.assertTrue("ACCEPT_EULA=Y" in package_manager_for_test.single_package_upgrade_simulation_cmd)
self.assertTrue("ACCEPT_EULA=Y" in package_manager_for_test.single_package_dependency_resolution_template)
self.assertTrue("ACCEPT_EULA=Y" in package_manager_for_test.single_package_upgrade_cmd)

def test_eula_not_accepted_for_patches(self):
package_manager = self.container.get('package_manager')
@@ -594,7 +598,8 @@ def test_eula_not_accepted_for_patches(self):
"LastModified": "2023-08-29"
}
self.runtime.env_layer.file_system.write_with_retry(Constants.Paths.EULA_SETTINGS, '{0}'.format(json.dumps(eula_settings)), mode='w+')
package_manager.accept_eula_for_patches()
self.runtime_for_test = RuntimeCompositor(ArgumentComposer().get_composed_arguments(), True, Constants.APT)
self.container_for_test = self.runtime_for_test.container
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_upgrade_simulation_cmd)
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_dependency_resolution_template)
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_upgrade_cmd)
@@ -606,7 +611,8 @@ def test_eula_not_accepted_for_patches(self):
"LastModified": "2023-08-29"
}
self.runtime.env_layer.file_system.write_with_retry(Constants.Paths.EULA_SETTINGS, '{0}'.format(json.dumps(eula_settings)), mode='w+')
package_manager.accept_eula_for_patches()
self.runtime_for_test = RuntimeCompositor(ArgumentComposer().get_composed_arguments(), True, Constants.APT)
self.container_for_test = self.runtime_for_test.container
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_upgrade_simulation_cmd)
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_dependency_resolution_template)
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_upgrade_cmd)
@@ -621,15 +627,17 @@ def test_empty_eula_settings(self):
# Empty eula settings file
eula_settings = ''
self.runtime.env_layer.file_system.write_with_retry(Constants.Paths.EULA_SETTINGS, '{0}'.format(json.dumps(eula_settings)), mode='w+')
package_manager.accept_eula_for_patches()
self.runtime_for_test = RuntimeCompositor(ArgumentComposer().get_composed_arguments(), True, Constants.APT)
self.container_for_test = self.runtime_for_test.container
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_upgrade_simulation_cmd)
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_dependency_resolution_template)
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_upgrade_cmd)

# Empty eula settings file
eula_settings = None
self.runtime.env_layer.file_system.write_with_retry(Constants.Paths.EULA_SETTINGS, '{0}'.format(json.dumps(eula_settings)), mode='w+')
package_manager.accept_eula_for_patches()
self.runtime_for_test = RuntimeCompositor(ArgumentComposer().get_composed_arguments(), True, Constants.APT)
self.container_for_test = self.runtime_for_test.container
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_upgrade_simulation_cmd)
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_dependency_resolution_template)
self.assertTrue("ACCEPT_EULA=Y" not in package_manager.single_package_upgrade_cmd)