Exemplo n.º 1
0
def test_set_version(fx_copy_fn_main_mock_integration, fx_get_sub_parser, fx_cmd_line_args_dev_set_version):

    mock_integration_name = fx_copy_fn_main_mock_integration[0]
    path_fn_main_mock_integration = fx_copy_fn_main_mock_integration[1]

    # Replace cmd line arg "fn_main_mock_integration" with path to temp dir location
    sys.argv[sys.argv.index(mock_integration_name)] = path_fn_main_mock_integration

    # Parse the setup.py file
    path_setup_py_file = os.path.join(path_fn_main_mock_integration, package_helpers.BASE_NAME_SETUP_PY)
    setup_py_attributes = package_helpers.parse_setup_py(path_setup_py_file, package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

    # Get customize.py ImportDefinition
    path_customize_py = package_helpers.get_configuration_py_file_path("customize", setup_py_attributes)
    customize_py_import_definition = package_helpers.get_import_definition_from_customize_py(path_customize_py)

    # Get the old_version
    old_version = customize_py_import_definition["server_version"]["version"]
    assert old_version == "41.0.6783"

    # Run _set_version
    cmd_dev = CmdDev(fx_get_sub_parser)
    args = cmd_dev.parser.parse_known_args()[0]
    cmd_dev._set_version(args)

    # Get the new_version
    customize_py_import_definition = package_helpers.get_import_definition_from_customize_py(path_customize_py)
    new_version = customize_py_import_definition["server_version"]["version"]
    assert new_version == "35.0.0"
Exemplo n.º 2
0
def test_get_dependency_from_install_requires():
    setup_attributes = package_helpers.parse_setup_py(mock_paths.MOCK_SETUP_PY,
                                                      ["install_requires"])
    install_requires_str = setup_attributes.get("install_requires")
    res_circuits_dep_str = package_helpers.get_dependency_from_install_requires(
        install_requires_str, "resilient_circuits")

    assert res_circuits_dep_str == "resilient_circuits>=30.0.0"
Exemplo n.º 3
0
def test_parse_setup_py():
    attributes_wanted = ["name", "version", "author", "long_description"]
    setup_attributes = package_helpers.parse_setup_py(mock_paths.MOCK_SETUP_PY, attributes_wanted)

    assert setup_attributes.get("name") == "fn_main_mock_integration"
    assert setup_attributes.get("version") == "1.2.3"
    assert setup_attributes.get("author") == "John છ જ ઝ ઞ ટ ઠ Smith"
    assert setup_attributes.get("long_description") == """Lorem ipsum dolor sit amet, tortor volutpat scelerisque facilisis vivamus eget pretium. Vestibulum turpis. Sed donec, nisl dolor ut elementum, turpis nulla elementum, pellentesque at nostra in et eget praesent. Nulla numquam volutpat sit, class quisque ultricies mollit nec, ullamcorper urna, amet eu magnis a sit nec. Ut urna massa non, purus donec mauris libero quisque quis, ઘ ઙ ચ છ જ ઝ ઞ libero purus eget donec at lacus, pretium a sollicitudin convallis erat eros, tristique eu aliquam."""
Exemplo n.º 4
0
def test_parse_setup_py_with_globals():
    the_globals = {"__file__": "", "long_description": "mock long description"}
    sdk_proj_py_attributes = package_helpers.parse_setup_py(
        mock_paths.PATH_SDK_SETUP_PY,
        package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES,
        the_globals=the_globals)

    assert sdk_proj_py_attributes.get("name") == "resilient_sdk"
    assert sdk_proj_py_attributes.get(
        "long_description") == "mock long description"
Exemplo n.º 5
0
# resilient-sdk parser
sdk_parser = sdk_app.get_main_app_parser()
sdk_sub_parser = sdk_app.get_main_app_sub_parser(sdk_parser)

cmd_codegen = CmdCodegen(sdk_sub_parser)
cmd_docgen = CmdDocgen(sdk_sub_parser)
cmd_ext_package = CmdExtPackage(sdk_sub_parser)
cmd_clone = CmdClone(sdk_sub_parser)
cmd_extract = CmdExtract(sdk_sub_parser)
cmd_validate = CmdValidate(sdk_sub_parser)

# parse the setup.py files
the_globals = {"__file__": "", "long_description": ""}
resilient_setup_attributes = parse_setup_py(
    os.path.abspath("../resilient/setup.py"),
    SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES,
    the_globals=the_globals)
circuits_setup_attributes = parse_setup_py(
    os.path.abspath("../resilient-circuits/setup.py"),
    SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES,
    the_globals=the_globals)
lib_setup_attributes = parse_setup_py(
    os.path.abspath("../resilient-lib/setup.py"),
    SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES,
    the_globals=the_globals)
sdk_setup_attributes = parse_setup_py(
    os.path.abspath("../resilient-sdk/setup.py"),
    SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES,
    the_globals=the_globals)

# make variables available in .rst files
Exemplo n.º 6
0
    def _reload_package(args):

        old_params, path_customize_py_bak = [], ""

        # Get + validate package, customize.py and setup.py paths
        path_package = os.path.abspath(args.package)
        # Get basename of path_to_src (version information is stripped from the basename).
        path_package_basename = re.split(VERSION_REGEX, os.path.basename(path_package), 1)[0]
        sdk_helpers.validate_dir_paths(os.R_OK, path_package)

        path_customize_py = os.path.join(path_package, path_package_basename, package_helpers.PATH_CUSTOMIZE_PY)
        sdk_helpers.validate_file_paths(os.W_OK, path_customize_py)

        path_setup_py_file = os.path.join(path_package, package_helpers.PATH_SETUP_PY)
        sdk_helpers.validate_file_paths(os.R_OK, path_setup_py_file)

        # Set package + output args correctly (this handles if user runs 'codegen --reload -p .')
        args.package = os.path.basename(path_package)
        args.output = os.path.dirname(path_package)

        LOG.info("'codegen --reload' started for '%s'", args.package)

        # Load the customize.py module
        customize_py_module = package_helpers.load_customize_py_module(path_customize_py, warn=False)

        try:
            # Get the 'old_params' from customize.py
            old_params = customize_py_module.codegen_reload_data()
        except AttributeError:
            raise SDKException(u"Corrupt customize.py. No reload method found in {0}".format(path_customize_py))

        if not old_params:
            raise SDKException(u"No reload params found in {0}".format(path_customize_py))

        # Rename the old customize.py with .bak
        path_customize_py_bak = sdk_helpers.rename_to_bak_file(path_customize_py)

        # If local export file exists then save it to a .bak file.
        # (Older packages may not have the /util/data/export.res file)
        path_export_res = os.path.join(path_package, path_package_basename,
                                       package_helpers.PATH_UTIL_DATA_DIR,
                                       package_helpers.BASE_NAME_LOCAL_EXPORT_RES)
        if os.path.isfile(path_export_res):
            path_export_res_bak = sdk_helpers.rename_to_bak_file(path_export_res)
        else:
            path_export_res_bak = None

        try:
            # Map command line arg name to dict key returned by codegen_reload_data() in customize.py
            mapping_tuples = [
                ("messagedestination", "message_destinations"),
                ("function", "functions"),
                ("workflow", "workflows"),
                ("rule", "actions"),
                ("field", "incident_fields"),
                ("artifacttype", "incident_artifact_types"),
                ("datatable", "datatables"),
                ("task", "automatic_tasks"),
                ("script", "scripts")
            ]

            # Merge old_params with new params specified on command line
            args = CmdCodegen.merge_codegen_params(old_params, args, mapping_tuples)

            # Parse the setup.py file
            setup_py_attributes = package_helpers.parse_setup_py(path_setup_py_file, package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

            LOG.debug("Regenerating codegen '%s' package now", args.package)

            # Regenerate the package
            CmdCodegen._gen_package(args, setup_py_attributes=setup_py_attributes)

            LOG.info("\nNOTE: Ensure the MANIFEST.in file includes line:\nrecursive-include %s/util *\n", args.package)
            LOG.info("'codegen --reload' complete for '%s'", args.package)

        except Exception as err:
            LOG.error(u"Error running resilient-sdk codegen --reload\n\nERROR:%s", err)

        # This is required in finally block as user may kill using keyboard interrupt
        finally:
            # If an error occurred, customize.py does not exist, rename the backup file to original
            if not os.path.isfile(path_customize_py):
                LOG.info(u"An error occurred. Renaming customize.py.bak to customize.py")
                sdk_helpers.rename_file(path_customize_py_bak, package_helpers.BASE_NAME_CUSTOMIZE_PY)
            if not os.path.isfile(path_export_res) and path_export_res_bak:
                LOG.info(u"An error occurred. Renaming export.res.bak to export.res")
                sdk_helpers.rename_file(path_export_res_bak, package_helpers.BASE_NAME_LOCAL_EXPORT_RES)
Exemplo n.º 7
0
    def _set_version(args):

        new_version = args.set_version

        if not sdk_helpers.is_valid_version_syntax(new_version):
            raise SDKException(
                "{0} is not a valid version".format(new_version))

        new_version_int = list(map(int, (re.findall(r"\d+", new_version))))

        # Get absolute path_to_src
        path_to_src = os.path.abspath(args.package)

        # Get path to setup.py file
        path_setup_py_file = os.path.join(path_to_src,
                                          package_helpers.BASE_NAME_SETUP_PY)

        # Parse the setup.py file
        setup_py_attributes = package_helpers.parse_setup_py(
            path_setup_py_file,
            package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

        package_name = setup_py_attributes.get("name", "")

        LOG.info("Setting Resilient Platform version for %s to %s",
                 package_name, new_version)

        # Get the customize file location.
        path_customize_py = package_helpers.get_configuration_py_file_path(
            "customize", setup_py_attributes)

        # Get customize.py ImportDefinition
        customize_py_import_definition = package_helpers.get_import_definition_from_customize_py(
            path_customize_py)

        old_version = customize_py_import_definition["server_version"][
            "version"]

        LOG.info("Old Version: %s", old_version)
        LOG.info("New Version: %s", new_version)

        # Set the new version
        customize_py_import_definition["server_version"][
            "version"] = new_version
        customize_py_import_definition["server_version"][
            "major"] = new_version_int[0]
        customize_py_import_definition["server_version"][
            "minor"] = new_version_int[1]
        customize_py_import_definition["server_version"][
            "build_number"] = new_version_int[2]

        LOG.info("Loading old customize.py file")

        # Load the customize.py module
        customize_py_module = package_helpers.load_customize_py_module(
            path_customize_py, warn=False)

        # Get the 'old_params' from customize.py
        old_params = customize_py_module.codegen_reload_data()

        # Rename the old customize.py with .bak
        path_customize_py_bak = sdk_helpers.rename_to_bak_file(
            path_customize_py)

        # If local export file exists then save it to a .bak file.
        # (Older packages may not have the /util/data/export.res file)
        # Figure out the path of the files first
        dir_customize_py = os.path.dirname(path_customize_py)
        path_local_export_res = os.path.join(
            dir_customize_py, package_helpers.PATH_LOCAL_EXPORT_RES)

        path_local_export_res_bak = None
        if os.path.isfile(path_local_export_res):
            path_local_export_res_bak = sdk_helpers.rename_to_bak_file(
                path_local_export_res)

        try:

            jinja_data = sdk_helpers.get_from_export(
                customize_py_import_definition,
                message_destinations=old_params.get("message_destinations"),
                functions=old_params.get("functions"),
                workflows=old_params.get("workflows"),
                rules=old_params.get("actions"),
                fields=old_params.get("incident_fields"),
                artifact_types=old_params.get("incident_artifact_types"),
                datatables=old_params.get("datatables"),
                tasks=old_params.get("automatic_tasks"),
                scripts=old_params.get("scripts"))

            jinja_data["export_data"] = sdk_helpers.minify_export(
                customize_py_import_definition,
                message_destinations=sdk_helpers.get_object_api_names(
                    ResilientObjMap.MESSAGE_DESTINATIONS,
                    jinja_data.get("message_destinations")),
                functions=sdk_helpers.get_object_api_names(
                    ResilientObjMap.FUNCTIONS, jinja_data.get("functions")),
                workflows=sdk_helpers.get_object_api_names(
                    ResilientObjMap.WORKFLOWS, jinja_data.get("workflows")),
                rules=sdk_helpers.get_object_api_names(
                    ResilientObjMap.RULES, jinja_data.get("rules")),
                fields=jinja_data.get("all_fields"),
                artifact_types=sdk_helpers.get_object_api_names(
                    ResilientObjMap.INCIDENT_ARTIFACT_TYPES,
                    jinja_data.get("artifact_types")),
                datatables=sdk_helpers.get_object_api_names(
                    ResilientObjMap.DATATABLES, jinja_data.get("datatables")),
                tasks=sdk_helpers.get_object_api_names(
                    ResilientObjMap.TASKS, jinja_data.get("tasks")),
                phases=sdk_helpers.get_object_api_names(
                    ResilientObjMap.PHASES, jinja_data.get("phases")),
                scripts=sdk_helpers.get_object_api_names(
                    ResilientObjMap.SCRIPTS, jinja_data.get("scripts")))

            # Add package_name to jinja_data
            jinja_data["package_name"] = package_name

            # Add version
            jinja_data["version"] = setup_py_attributes.get(
                "version", package_helpers.MIN_SETUP_PY_VERSION)

            # Instansiate Jinja2 Environment with path to Jinja2 templates for customize.py
            jinja_env = sdk_helpers.setup_jinja_env(
                "data/codegen/templates/package_template/package/util")
            jinja_template = jinja_env.get_template("customize.py.jinja2")

            LOG.info("Writing new customize.py file")

            # Render & write jinja2 template
            jinja_rendered_text = jinja_template.render(jinja_data)
            sdk_helpers.write_file(path_customize_py, jinja_rendered_text)

            # Instansiate Jinja2 Environment with path to Jinja2 templates for /util/dat/export.res
            #jinja_env = sdk_helpers.setup_jinja_env("data/codegen/templates/package_template/package/util/data")
            jinja_template = jinja_env.get_template("/data/export.res.jinja2")

            LOG.debug("Writing new /util/data/export.res file")

            # Render jinja2 template
            jinja_rendered_text = jinja_template.render(jinja_data)

            # Make sure the /util/data directory is there if it is not
            dir_local_export_res = os.path.dirname(path_local_export_res)
            if not os.path.exists(dir_local_export_res):
                os.makedirs(dir_local_export_res)

            # Write the file
            sdk_helpers.write_file(path_local_export_res, jinja_rendered_text)

            LOG.info("'dev --set-version' complete for '%s'", package_name)

        except Exception as err:
            LOG.error(
                u"Error running resilient-sdk dev --set-version\n\nERROR:%s",
                err)

        # This is required in finally block as user may kill using keyboard interrupt
        finally:
            # If an error occurred, customize.py does not exist, rename the backup file to original
            if not os.path.isfile(path_customize_py):
                LOG.info(
                    u"An error occurred. Renaming customize.py.bak to customize.py"
                )
                sdk_helpers.rename_file(path_customize_py_bak,
                                        package_helpers.BASE_NAME_CUSTOMIZE_PY)
            if path_local_export_res_bak and not os.path.isfile(
                    path_local_export_res):
                LOG.info(
                    u"An error occurred. Renaming /util/data/export.res.bak to export.res"
                )
                sdk_helpers.rename_file(
                    path_local_export_res_bak,
                    package_helpers.BASE_NAME_LOCAL_EXPORT_RES)
Exemplo n.º 8
0
    def _reload_package(args):

        old_params, path_customize_py_bak = [], ""

        # Get absolute path to package
        path_package = os.path.abspath(args.package)

        LOG.debug("\nPath to project: %s", path_package)

        # Ensure the package directory exists and we have WRITE access
        sdk_helpers.validate_dir_paths(os.W_OK, path_package)

        path_setup_py_file = os.path.join(path_package, package_helpers.BASE_NAME_SETUP_PY)

        package_name = package_helpers.get_package_name(path_package)

        if not sdk_helpers.is_valid_package_name(package_name):
            raise SDKException(u"'{0}' is not a valid package name. 'name' attribute in setup.py file is not valid or not specified".format(package_name))

        LOG.debug("\nProject name: %s", package_name)

        # Generate path to customize.py file + validate we have permissions to read it
        path_customize_py = os.path.join(path_package, package_name, package_helpers.PATH_CUSTOMIZE_PY)
        sdk_helpers.validate_file_paths(os.W_OK, path_customize_py)

        # Set package + output args correctly (this handles if user runs 'codegen --reload -p .')
        args.package = package_name
        args.output = path_package

        LOG.info("'codegen --reload' started for '%s'", args.package)

        # Load the customize.py module
        customize_py_module = package_helpers.load_customize_py_module(path_customize_py, warn=False)

        try:
            # Get the 'old_params' from customize.py
            old_params = customize_py_module.codegen_reload_data()
        except AttributeError:
            raise SDKException(u"Corrupt customize.py. No reload method found in {0}".format(path_customize_py))

        if not old_params:
            raise SDKException(u"No reload params found in {0}".format(path_customize_py))

        # Rename the old customize.py with .bak
        path_customize_py_bak = sdk_helpers.rename_to_bak_file(path_customize_py)

        # If local export file exists then save it to a .bak file.
        # (Older packages may not have the /util/data/export.res file)
        path_export_res = os.path.join(path_package, package_name,
                                       package_helpers.PATH_UTIL_DATA_DIR,
                                       package_helpers.BASE_NAME_LOCAL_EXPORT_RES)
        if os.path.isfile(path_export_res):
            path_export_res_bak = sdk_helpers.rename_to_bak_file(path_export_res)
        else:
            path_export_res_bak = None

        try:
            # Map command line arg name to dict key returned by codegen_reload_data() in customize.py
            mapping_tuples = [
                ("messagedestination", "message_destinations"),
                ("function", "functions"),
                ("workflow", "workflows"),
                ("rule", "actions"),
                ("field", "incident_fields"),
                ("artifacttype", "incident_artifact_types"),
                ("incidenttype", "incident_types"),
                ("datatable", "datatables"),
                ("task", "automatic_tasks"),
                ("script", "scripts"),
                ("playbook", "playbooks")
            ]

            # Merge old_params with new params specified on command line
            args = CmdCodegen.merge_codegen_params(old_params, args, mapping_tuples)

            # Parse the setup.py file
            setup_py_attributes = package_helpers.parse_setup_py(path_setup_py_file, package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

            LOG.debug("Regenerating codegen '%s' package now", args.package)

            # Regenerate the package
            path_reloaded = CmdCodegen._gen_package(args, setup_py_attributes=setup_py_attributes)

            LOG.info("\nNOTE: Ensure the MANIFEST.in file includes line:\nrecursive-include %s/util *\n", args.package)
            LOG.info("'codegen --reload' complete for '%s'", args.package)

            return path_reloaded

        # This is required in finally block as user may kill using keyboard interrupt
        finally:
            # If an error occurred, customize.py does not exist, rename the backup file to original
            if not os.path.isfile(path_customize_py):
                LOG.info(u"An error occurred. Renaming customize.py.bak to customize.py")
                sdk_helpers.rename_file(path_customize_py_bak, package_helpers.BASE_NAME_CUSTOMIZE_PY)
            if not os.path.isfile(path_export_res) and path_export_res_bak:
                LOG.info(u"An error occurred. Renaming export.res.bak to export.res")
                sdk_helpers.rename_file(path_export_res_bak, package_helpers.BASE_NAME_LOCAL_EXPORT_RES)
Exemplo n.º 9
0
    def execute_command(self, args):
        LOG.debug("docgen called with %s", args)

        # Set docgen name for SDKException
        SDKException.command_ran = self.CMD_NAME

        # Get absolute path_to_src
        path_to_src = os.path.abspath(args.p)

        LOG.debug("Path to project: %s", path_to_src)

        # Instansiate Jinja2 Environment with path to Jinja2 templates
        jinja_env = sdk_helpers.setup_jinja_env("data/docgen/templates")

        # Load the Jinja2 Templates
        readme_template = jinja_env.get_template(README_TEMPLATE_NAME)

        # Generate path to setup.py file
        path_setup_py_file = os.path.join(path_to_src,
                                          package_helpers.BASE_NAME_SETUP_PY)

        try:
            # Ensure we have read permissions for setup.py
            sdk_helpers.validate_file_paths(os.R_OK, path_setup_py_file)
        except SDKException as err:
            err.message += "\nEnsure you are in the directory of the package you want to run docgen for"
            raise err

        # Parse the setup.py file
        setup_py_attributes = package_helpers.parse_setup_py(
            path_setup_py_file,
            package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

        package_name = setup_py_attributes.get("name", "")

        # Generate paths to other required directories + files
        path_customize_py_file = os.path.join(
            path_to_src, package_name, package_helpers.PATH_CUSTOMIZE_PY)
        path_config_py_file = os.path.join(path_to_src, package_name,
                                           package_helpers.PATH_CONFIG_PY)
        path_readme = os.path.join(path_to_src,
                                   package_helpers.BASE_NAME_README)
        path_screenshots_dir = os.path.join(path_to_src,
                                            package_helpers.PATH_SCREENSHOTS)
        path_payload_samples_dir = os.path.join(
            path_to_src, package_helpers.BASE_NAME_PAYLOAD_SAMPLES_DIR)

        # Ensure we have read permissions for each required file and the file exists
        sdk_helpers.validate_file_paths(os.R_OK, path_setup_py_file,
                                        path_customize_py_file,
                                        path_config_py_file)

        # Check doc/screenshots directory exists, if not, create it + copy default screenshot
        if not os.path.isdir(path_screenshots_dir):
            os.makedirs(path_screenshots_dir)
            shutil.copy(package_helpers.PATH_DEFAULT_SCREENSHOT,
                        path_screenshots_dir)

        # Get the resilient_circuits dependency string from setup.py file
        res_circuits_dep_str = package_helpers.get_dependency_from_install_requires(
            setup_py_attributes.get("install_requires"), "resilient_circuits")

        if not res_circuits_dep_str:
            res_circuits_dep_str = package_helpers.get_dependency_from_install_requires(
                setup_py_attributes.get("install_requires"),
                "resilient-circuits")

        # Get ImportDefinition from customize.py
        customize_py_import_def = package_helpers.get_import_definition_from_customize_py(
            path_customize_py_file)

        # Parse the app.configs from the config.py file
        jinja_app_configs = package_helpers.get_configs_from_config_py(
            path_config_py_file)

        # Get field names from ImportDefinition
        field_names = []
        for f in customize_py_import_def.get("fields", []):
            f_export_key = f.get("export_key")

            if "incident/" in f_export_key and f_export_key not in IGNORED_INCIDENT_FIELDS:
                field_names.append(f.get(ResilientObjMap.FIELDS, ""))

        # Get data from ImportDefinition
        import_def_data = sdk_helpers.get_from_export(
            customize_py_import_def,
            message_destinations=sdk_helpers.get_object_api_names(
                ResilientObjMap.MESSAGE_DESTINATIONS,
                customize_py_import_def.get("message_destinations")),
            functions=sdk_helpers.get_object_api_names(
                ResilientObjMap.FUNCTIONS,
                customize_py_import_def.get("functions")),
            workflows=sdk_helpers.get_object_api_names(
                ResilientObjMap.WORKFLOWS,
                customize_py_import_def.get("workflows")),
            rules=sdk_helpers.get_object_api_names(
                ResilientObjMap.RULES, customize_py_import_def.get("actions")),
            fields=field_names,
            artifact_types=sdk_helpers.get_object_api_names(
                ResilientObjMap.INCIDENT_ARTIFACT_TYPES,
                customize_py_import_def.get("incident_artifact_types")),
            datatables=sdk_helpers.get_object_api_names(
                ResilientObjMap.DATATABLES,
                customize_py_import_def.get("types")),
            tasks=sdk_helpers.get_object_api_names(
                ResilientObjMap.TASKS,
                customize_py_import_def.get("automatic_tasks")),
            scripts=sdk_helpers.get_object_api_names(
                ResilientObjMap.SCRIPTS,
                customize_py_import_def.get("scripts")),
            playbooks=sdk_helpers.get_object_api_names(
                ResilientObjMap.PLAYBOOKS,
                customize_py_import_def.get("playbooks", [])))

        # Lists we use in Jinja Templates
        jinja_functions = self._get_function_details(
            import_def_data.get("functions", []),
            import_def_data.get("workflows", []))
        jinja_scripts = self._get_script_details(
            import_def_data.get("scripts", []))
        jinja_rules = self._get_rule_details(import_def_data.get("rules", []))
        jinja_datatables = self._get_datatable_details(
            import_def_data.get("datatables", []))
        jinja_custom_fields = self._get_custom_fields_details(
            import_def_data.get("fields", []))
        jinja_custom_artifact_types = self._get_custom_artifact_details(
            import_def_data.get("artifact_types", []))
        jinja_playbooks = self._get_playbook_details(
            import_def_data.get("playbooks", []))

        # Other variables for Jinja Templates
        package_name_dash = package_name.replace("_", "-")
        server_version = customize_py_import_def.get("server_version", {})
        supported_app = sdk_helpers.does_url_contain(
            setup_py_attributes.get("url", ""), "ibm.com/mysupport")

        # See if a payload_samples dir exists and use the contents for function results
        try:
            sdk_helpers.validate_dir_paths(os.R_OK, path_payload_samples_dir)

            for f in jinja_functions:
                fn_name = f.get("x_api_name")
                path_payload_samples_fn_name = os.path.join(
                    path_payload_samples_dir, fn_name)
                path_output_json_example = os.path.join(
                    path_payload_samples_fn_name,
                    package_helpers.BASE_NAME_PAYLOAD_SAMPLES_EXAMPLE)

                try:
                    sdk_helpers.validate_file_paths(os.R_OK,
                                                    path_output_json_example)
                    f["results"] = sdk_helpers.read_json_file(
                        path_output_json_example)
                except SDKException as e:
                    sdk_helpers.handle_file_not_found_error(
                        e,
                        u"Error getting results. No '{0}' file found for '{1}'."
                        .format(
                            package_helpers.BASE_NAME_PAYLOAD_SAMPLES_EXAMPLE,
                            fn_name))

        except SDKException as e:
            sdk_helpers.handle_file_not_found_error(
                e, u"Error getting results. No '{0}' directory found.".format(
                    package_helpers.BASE_NAME_PAYLOAD_SAMPLES_EXAMPLE))

        LOG.info("Rendering README for %s", package_name_dash)

        # Render the README Jinja2 Templeate with parameters
        rendered_readme = readme_template.render({
            "name_underscore":
            package_name,
            "name_dash":
            package_name_dash,
            "display_name":
            setup_py_attributes.get("display_name", package_name),
            "short_description":
            setup_py_attributes.get("description"),
            "long_description":
            setup_py_attributes.get("long_description"),
            "version":
            setup_py_attributes.get("version"),
            "server_version":
            server_version.get("version"),
            "all_dependencies":
            setup_py_attributes.get("install_requires", []),
            "res_circuits_dependency_str":
            res_circuits_dep_str,
            "author":
            setup_py_attributes.get("author"),
            "support_url":
            setup_py_attributes.get("url"),
            "supported_app":
            supported_app,
            "app_configs":
            jinja_app_configs[1],
            "functions":
            jinja_functions,
            "scripts":
            jinja_scripts,
            "rules":
            jinja_rules,
            "datatables":
            jinja_datatables,
            "custom_fields":
            jinja_custom_fields,
            "custom_artifact_types":
            jinja_custom_artifact_types,
            "playbooks":
            jinja_playbooks,
            "placeholder_string":
            constants.DOCGEN_PLACEHOLDER_STRING
        })

        # Create a backup if needed of README
        sdk_helpers.rename_to_bak_file(path_readme,
                                       package_helpers.PATH_DEFAULT_README)

        LOG.info("Writing README to: %s", path_readme)

        # Write the new README
        sdk_helpers.write_file(path_readme, rendered_readme)
    def execute_command(self, args):
        """
        Function that creates The App.zip file from the give source path and returns
        the path to the new App.zip

        :param args: Arguments from command line:

            -  **args.package**: path to directory that must include a setup.py, customize.py and config.py file.
            -  **args.cmd**: `package` in this case
            -  **args.display_name**: will give the App that display name. Default: name from setup.py file
            -  **args.repository_name**: if defined, it will replace the default image repository name in app.json for
                                         container access.
            -  **args.keep_build_dir**: if defined, dist/build/ will not be removed.
            -  **args.no_samples**: if defined, set path_payload_samples to None.
            -  **args.validate**: if defined, run ``validate`` and save report in packaged app.
        :type args: argparse Namespace

        :return: Path to new app.zip
        :rtype: str
        """
        # Set name for SDKException
        SDKException.command_ran = self.CMD_NAME

        # Get absolute path_to_src
        path_to_src = os.path.abspath(args.package)

        LOG.debug("\nPath to project: %s", path_to_src)

        # Ensure the src directory exists and we have WRITE access
        sdk_helpers.validate_dir_paths(os.W_OK, path_to_src)

        # Generate path to setup.py file
        path_setup_py_file = os.path.join(path_to_src,
                                          package_helpers.BASE_NAME_SETUP_PY)

        # Ensure we have read permissions for setup.py
        sdk_helpers.validate_file_paths(os.R_OK, path_setup_py_file)

        # Parse the setup.py file
        setup_py_attributes = package_helpers.parse_setup_py(
            path_setup_py_file,
            package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

        LOG.debug("\nProject name: %s",
                  setup_py_attributes.get("name", "unknown"))

        # Generate paths to files required to create app
        path_docker_file = os.path.join(path_to_src,
                                        package_helpers.BASE_NAME_DOCKER_FILE)
        path_entry_point = os.path.join(path_to_src,
                                        package_helpers.BASE_NAME_ENTRY_POINT)
        path_apikey_permissions_file = os.path.join(
            path_to_src, package_helpers.BASE_NAME_APIKEY_PERMS_FILE)
        path_output_dir = os.path.join(path_to_src,
                                       package_helpers.BASE_NAME_DIST_DIR)
        path_extension_logo = os.path.join(
            path_to_src, package_helpers.PATH_ICON_EXTENSION_LOGO)
        path_company_logo = os.path.join(
            path_to_src, package_helpers.PATH_ICON_COMPANY_LOGO)
        path_payload_samples = os.path.join(
            path_to_src, package_helpers.BASE_NAME_PAYLOAD_SAMPLES_DIR)

        # if --no-samples flag, set path_payload_samples to None
        if args.no_samples:
            path_payload_samples = None

        # if --validate flag is set, run validate command
        # else set the path to the file if it exists or None if doesn't exist
        if args.validate:
            LOG.info(
                "Validation on {0} is starting. \nTo skip, run the 'package' command without the '--validate' flag.\nValidations can be executated separately by running: \n  'resilient-sdk validate -p {0}' \nto see more in-depth results.\n"
                .format(args.package))

            validate_args = self.cmd_validate.parser.parse_known_args()[0]

            path_validate_report = self.cmd_validate.execute_command(
                validate_args, output_suppressed=True, run_from_package=True)
        else:
            path_validate_report = package_helpers.check_validate_report_exists(
            )

        # Ensure the 'Dockerfile' and 'entrypoint.sh' files exist and we have READ access
        sdk_helpers.validate_file_paths(os.R_OK, path_docker_file,
                                        path_entry_point)

        LOG.info("\nBuild Distribution starting\n")

        # Create the build distribution
        use_setuptools.run_setup(setup_script=path_setup_py_file,
                                 args=["sdist", "--formats=gztar"])

        LOG.info("\nBuild Distribution finished. See: %s", path_output_dir)

        # Create the app
        path_the_extension_zip = package_helpers.create_extension(
            path_setup_py_file=path_setup_py_file,
            path_apikey_permissions_file=path_apikey_permissions_file,
            output_dir=path_output_dir,
            custom_display_name=args.display_name,
            repository_name=args.repository_name,
            keep_build_dir=args.keep_build_dir,
            path_extension_logo=path_extension_logo,
            path_company_logo=path_company_logo,
            path_payload_samples=path_payload_samples,
            path_validate_report=path_validate_report,
            image_hash=args.image_hash)

        LOG.info("App created at: %s", path_the_extension_zip)

        return path_the_extension_zip