def run_safe_validations(handler): """This function runs all the validations that can safely be done without running user code. It returns the common parent directory of the archive. """ logger.info(">> Running safe validations") # check archive format namelist = handler.get_namelist() common_dir = validate_archive_files(namelist) logger.debug("Common directory is %s" % common_dir) # check for and then read configuration file config_file = common_dir + os.sep + DJANGO_CFG_FILENAME if not handler.is_file_in_archive(config_file): raise FileMissingError("Archive missing configuration file %s" % config_file) fileobj = handler.open_member(config_file) data = fileobj.read() django_config = django_config_from_json(data, COMPATIBLE_PACKAGER_VERSION) # check for additional_components file and parse if present components_file = common_dir + os.sep + COMPONENTS_FILENAME if handler.is_file_in_archive(components_file): fileobj = handler.open_member(components_file) read_components_file(fileobj, components_file, django_config.components) else: if len(django_config.components)>0: raise ValidationError("No additional component file at %s, but application with packaged with an additional component file" % filepath) # check for other required files settings_module_file = django_config.get_settings_module_file() if not handler.is_file_in_archive(settings_module_file): raise FileMissingError("Archive missing django settings file %s" % settings_module_file) logger.debug("Has required files") return (common_dir, django_config)
def simulated_install(archive_file_or_directory, install_path, django_settings_module, package_cache_dir=None): """Simulate the install process. install_path is the directory under which we extract the archive. We also create a python virtualenv there and install all the required dependencies. """ if not os.path.isdir(install_path): raise Exception( "simulated_install: install_path %s does not exist" % install_path) if os.path.isdir(archive_file_or_directory): app_dir_path = os.path.join( install_path, os.path.basename(archive_file_or_directory)) shutil.copytree(archive_file_or_directory, app_dir_path) else: # we have an archive with create_handler(archive_file_or_directory) as h: common_dir = validate_archive_files(h.get_namelist()) h.extract(install_path) app_dir_path = os.path.join(install_path, common_dir) components_file = os.path.join(app_dir_path, COMPONENTS_FILENAME) if os.path.exists(components_file): with open(components_file, "rb") as cf: components_list = read_components_file(cf, components_file, None) else: components_list = [] undo_ops = generate_settings_file(app_dir_path, django_settings_module, components_list) virtualenv_path = os.path.join(install_path, "python") create_virtualenv(virtualenv_path, package_cache_dir=package_cache_dir) platform_reqs = write_platform_requirements_file(install_path, components_list) logger.info(">> Installing platform requirements into virtualenv") install_requirements( virtualenv_path, platform_reqs, package_cache_dir=package_cache_dir) app_reqs = os.path.join(app_dir_path, "requirements.txt") if os.path.exists(app_reqs): logger.info(">> Installing application requirements into virtualenv") install_requirements( virtualenv_path, app_reqs, package_cache_dir=package_cache_dir) else: logger.debug("No install requirements file at %s" % app_reqs) return (app_dir_path, undo_ops, os.path.join(virtualenv_path, "bin/python"))
def validate_settings(app_dir_path, django_settings_module, django_config=None, prev_version_component_list=None): """This is the main settings validation function. It takes the following arguments: app_dir_path - path to the top level directory of the extracted application django_settings_module - fully qualified name of the django settings module django_config - if provided, this is the django_config data generated during the original packaging of the app. We validate that it is still consistent with the current app. This function returns an intance of SettingValidationResults. Note that validate_settings() must be run after generate_settings(). We import the deployed_settings module rather than the user's django_settings_module so we can see whether they've overridden any of the settings. """ # normalize the target install path app_dir_path = os.path.abspath(os.path.expanduser(app_dir_path)) results = SettingValidationResults(VERSION, logger) python_path_dir = find_python_module(django_settings_module, app_dir_path) if not python_path_dir: raise ValidationError("Unable to find django settings module %s under %s" % (django_settings_module, app_dir_path)) # we store only the subdirectory part of the python path, since the rest depends # on where we install the app. if os.path.dirname(app_dir_path)==python_path_dir: results.python_path_subdirectory = "" else: results.python_path_subdirectory = _get_subdir_component(os.path.dirname(app_dir_path), python_path_dir) # get the settings file directory settings_file_directory = get_settings_file_directory(python_path_dir, django_settings_module) # do the import of app's settings sys.path = [python_path_dir] + sys.path deployed_settings_module = get_deployed_settings_module(django_settings_module) logger.debug("Importing settings module %s" % deployed_settings_module) try: settings_module = import_module(deployed_settings_module) except: (exc_type, exc_value, exc_traceback) = sys.exc_info() logger.exception("Exception in settings file import: %s(%s)" % (exc_type.__name__, str(exc_value))) raise SettingsImportError("Error in settings import: %s(%s)" % (exc_type.__name__, str(exc_value))) # Check that the settings controlled by engage weren't overridden by app. # If any are overridden, we treat them as warnings. check_if_setting_overridden('TIME_ZONE', settings_module, results) check_if_setting_overridden('SECRET_KEY', settings_module, results) check_if_setting_overridden('ADMINS', settings_module, results) check_if_setting_overridden('DATABASES', settings_module, results) check_if_setting_overridden('LOGGING_CONFIG', settings_module, results) # Check that settings which point to a directory are either not set or # point to a valid directory if hasattr(settings_module, "MEDIA_ROOT"): check_directory_setting("MEDIA_ROOT", settings_module.MEDIA_ROOT, '', app_dir_path, results) if hasattr(settings_module, "TEMPLATE_DIRS"): check_directory_tuple_setting("TEMPLATE_DIRS", settings_module.TEMPLATE_DIRS, app_dir_path, results) # Get the packages in requirements.txt. We use this in validating # the django apps. We defer the validation of the actual packages # until we have parsed and validated the engage_components.json file. user_required_packages = get_user_required_packages(app_dir_path) # check that all INSTALLED_APPS are pointing to apps accessible in the target system if hasattr(settings_module, "INSTALLED_APPS"): installed_apps = [] packages = PREINSTALLED_PACKAGES + user_required_packages known_apps = set(get_apps_for_packages(packages)) for app_name in settings_module.INSTALLED_APPS: validate_installed_app(app_name, python_path_dir, known_apps, app_dir_path, django_settings_module, results) installed_apps.append(app_name) else: installed_apps = [] results.installed_apps = installed_apps if hasattr(settings_module, "FIXTURE_DIRS"): fixture_dirs = _tuple_setting_to_list(settings_module.FIXTURE_DIRS) check_directory_tuple_setting("FIXTURE_DIRS", fixture_dirs, app_dir_path, results) else: fixture_dirs = [] # check that ENGAGE_APP_DB_FIXTURES points to valid fixture files if hasattr(settings_module, "ENGAGE_APP_DB_FIXTURES"): results.fixtures = _tuple_setting_to_list(settings_module.ENGAGE_APP_DB_FIXTURES) for fixture in results.fixtures: validate_fixture_file(fixture, results.installed_apps, fixture_dirs, python_path_dir, settings_file_directory, known_apps, results) else: results.fixtures = [] # check ENGAGE_MIGRATION_APPS, if present if hasattr(settings_module, "ENGAGE_MIGRATION_APPS"): results.migration_apps = _tuple_setting_to_list(settings_module.ENGAGE_MIGRATION_APPS) if len(results.migration_apps)>0 and not ("south" in results.installed_apps): results.error("Django apps to upgraded specified in ENGAGE_MIGRATION_APPS, but south not included in INSTALLED_APPS") validate_migration_apps(results.migration_apps, results.installed_apps, results) else: results.migration_apps = [] # check the static files directories, if present. Each entry could be a source # directory, or a tuple of (target_subdir, source_path) if hasattr(settings_module, "STATICFILES_DIRS"): staticfiles_dirs = _tuple_setting_to_list(settings_module.STATICFILES_DIRS) for dirpath in staticfiles_dirs: if isinstance(dirpath, tuple): dirpath = dirpath[1] if not os.path.isdir(dirpath): results.error("Setting STATICFILES_DIRS references '%s', which does not exist" % dirpath) elif string.find(os.path.realpath(dirpath), os.path.realpath(app_dir_path)) != 0: results.error("Setting STATICFILES_DIRS references '%s', which is not a subdirectory of '%s'" % (dirpath, app_dir_path)) check_directory_tuple_setting("STATICFILES_DIRS", staticfiles_dirs, app_dir_path, results) # gather the values of static files related settings for use during # installation. extract_static_files_settings(settings_module, app_dir_path, results) # check each command in ENGAGE_DJANGO_POSTINSTALL_COMMANDS is actually present in manager if hasattr(settings_module, "ENGAGE_DJANGO_POSTINSTALL_COMMANDS"): results.post_install_commands = list(settings_module.ENGAGE_DJANGO_POSTINSTALL_COMMANDS) validate_post_install_commands(app_name, settings_module, results) else: results.post_install_commands = [] # read the additional components file and put the data into the results additional_comp_file = os.path.join(app_dir_path, COMPONENTS_FILENAME) if os.path.exists(additional_comp_file): with open(additional_comp_file, "rb") as cf: results.components = read_components_file(cf, additional_comp_file, None) else: results.components = [] # validate the user required packages, taking into account the components requested # by the user. validate_package_list(user_required_packages, results.components, results) # extract the product name and version, if present if hasattr(settings_module, "ENGAGE_PRODUCT_NAME"): results.product = settings_module.ENGAGE_PRODUCT_NAME if hasattr(settings_module, "ENGAGE_PRODUCT_VERSION"): results.product_version = settings_module.ENGAGE_PRODUCT_VERSION # if provided, check that the django_config matches the settings values if django_config: django_config_ok = True if installed_apps != django_config.installed_apps: results.error("INSTALLED_APPS in configuration file (%s) does not match INSTALLED_APPS in settings file (%s). Your configuration file is likely out of date. Try re-running prepare." % (django_config.installed_apps.__repr__(), installed_apps.__repr__())) django_config_ok = False if results.fixtures != django_config.fixtures: # TODO: this was originally an error, which caused some issues. # See ticket #166. results.warning("ENGAGE_APP_DB_FIXTURES in configuration file (%s) does not match value in settings file (%s). If this is not what you expect, your configuration file is likely out of date: try re-running prepare." % (django_config.fixtures.__repr__(), results.fixtures.__repr__())) django_config_ok = False if results.migration_apps != django_config.migration_apps: results.error("ENGAGE_MIGRATION_APPS in configuration file (%s) does not match value in settings file (%s). Your configuration file is likely out of date. Try re-running prepare." % (django_config.migration_apps.__repr__(), results.migration_apps.__repr__())) django_config_ok = False if results.product and results.product != django_config.product: results.error("ENGAGE_PRODUCT_NAME in configuration file (%s) does not match value in settings file (%s). Your configuration file is likely out of date. Try re-running prepare." % (django_config.product, results.product)) django_config_ok = False if results.product_version and results.product_version != django_config.product_version: results.error("ENGAGE_PRODUCT_VERSION in configuration file (%s) does not match value in settings file (%s). Your configuration file is likely out of date. Try re-running prepare." % (django_config.product_version, results.product_version)) django_config_ok = False if django_config_ok: logger.debug("Verified config file is consistent with settings file") return results # all done