Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ omit =
splunk_add_on_ucc_framework/templates/input.module-template

[report]
fail_under = 84.64
fail_under = 84.70
1 change: 1 addition & 0 deletions docs/generated_files.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ The following table describes the files generated by UCC framework.
| inputs.xml | output/<YOUR_ADDON_NAME>/default/data/ui/views | Generates inputs.xml based on inputs configuration present in globalConfig, in `default/data/ui/views/inputs.xml` folder |
| _redirect.xml | output/<YOUR_ADDON_NAME>/default/data/ui/views | Generates ta_name_redirect.xml file, if oauth is mentioned in globalConfig, in `default/data/ui/views/` folder. |
| _.html | output/<YOUR_ADDON_NAME>/default/data/ui/alerts | Generates `alert_name.html` file based on alerts configuration present in globalConfig, in `default/data/ui/alerts` folder. |
| globalConfig.json | <source_dir> | Generates globalConfig.json file in the source code if globalConfig is not present in source directory at build time. |

224 changes: 105 additions & 119 deletions splunk_add_on_ucc_framework/commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,137 +442,123 @@ def generate(
generated_files = []

gc_path = _get_and_check_global_config_path(source, config_path)
if gc_path:
logger.info(f"Using globalConfig file located @ {gc_path}")
global_config = global_config_lib.GlobalConfig.from_file(gc_path)
global_config.cleanup_unwanted_params()
# handle the update of globalConfig before validating
global_config_update.handle_global_config_update(global_config, gc_path)
try:
validator = global_config_validator.GlobalConfigValidator(
internal_root_dir, global_config
)
validator.validate()
logger.info("globalConfig file is valid")
except exceptions.GlobalConfigValidatorException as e:
logger.error(f"globalConfig file is not valid. Error: {e}")
sys.exit(1)
global_config.update_addon_version(addon_version)
global_config.dump(gc_path)
logger.info(
f"Updated and saved add-on version in the globalConfig file to {addon_version}"
if not gc_path:
# create one in source directory if it doesn't exist
gc_path = os.path.sep.join(
[os.path.abspath(os.path.dirname(source)), "globalConfig.json"]
)
global_config.add_ucc_version(__version__)
global_config.expand()
if ta_name != global_config.product:
logger.error(
"Add-on name mentioned in globalConfig meta tag and that app.manifest are not same,"
"please unify them to build the add-on."
)
sys.exit(1)
global_config.parse_user_defined_handlers()
scheme = global_config_builder_schema.GlobalConfigBuilderSchema(global_config)
if global_config.has_pages():
utils.recursive_overwrite(
os.path.join(internal_root_dir, "package"),
os.path.join(output_directory, ta_name),
ui_source_map,
)
global_config_file = (
"globalConfig.yaml" if gc_path.endswith(".yaml") else "globalConfig.json"
)
output_build_path = os.path.join(
output_directory, ta_name, "appserver", "static", "js", "build"
)
if not os.path.isdir(output_build_path):
# this path may not exist for the .conf-only add-ons
os.makedirs(output_build_path)
global_config.dump(os.path.join(output_build_path, global_config_file))
logger.info("Copied globalConfig to output")
ucc_lib_target = os.path.join(output_directory, ta_name, "lib")
try:
install_python_libraries(
source,
ucc_lib_target,
python_binary_name,
includes_ui=True,
os_libraries=global_config.os_libraries,
pip_version=pip_version,
pip_legacy_resolver=pip_legacy_resolver,
pip_custom_flag=pip_custom_flag,
includes_oauth=global_config.has_oauth(),
)
except SplunktaucclibNotFound as e:
logger.error(str(e))
sys.exit(1)
logger.info(
f"Installed add-on requirements into {ucc_lib_target} from {source}"
global_config_lib.GlobalConfig.from_app_manifest(app_manifest).dump(gc_path)

# no need of the condition as globalConfig would always exist
logger.info(f"Using globalConfig file located @ {gc_path}")
global_config = global_config_lib.GlobalConfig.from_file(gc_path)
global_config.cleanup_unwanted_params()
# handle the update of globalConfig before validating
global_config_update.handle_global_config_update(global_config, gc_path)
try:
validator = global_config_validator.GlobalConfigValidator(
internal_root_dir, global_config
)
generated_files.extend(
begin(
global_config=global_config,
input_dir=source,
output_dir=output_directory,
ucc_dir=internal_root_dir,
addon_name=ta_name,
app_manifest=app_manifest,
addon_version=addon_version,
has_ui=global_config.meta.get("isVisible", True),
)
validator.validate()
logger.info("globalConfig file is valid")
except exceptions.GlobalConfigValidatorException as e:
logger.error(f"globalConfig file is not valid. Error: {e}")
sys.exit(1)
global_config.update_addon_version(addon_version)
global_config.dump(gc_path)
logger.info(
f"Updated and saved add-on version in the globalConfig file to {addon_version}"
)
global_config.add_ucc_version(__version__)
global_config.expand()
if ta_name != global_config.product:
logger.error(
"Add-on name mentioned in globalConfig meta tag and that app.manifest are not same,"
"please unify them to build the add-on."
)
# TODO: all FILES GENERATED object: generated_files, use it for comparison
if global_config.has_pages():
builder_obj = RestBuilder(scheme, os.path.join(output_directory, ta_name))
builder_obj.build()
_modify_and_replace_token_for_oauth_templates(
ta_name,
global_config,
output_directory,
)
if global_config.has_inputs():
logger.info("Generating inputs code")
_add_modular_input(ta_name, global_config, output_directory)
if global_config.has_alerts():
logger.info("Generating alerts code")
alert_builder.generate_alerts(global_config, ta_name, output_directory)

conf_file_names = []
conf_file_names.extend(list(scheme.settings_conf_file_names))
conf_file_names.extend(list(scheme.configs_conf_file_names))
conf_file_names.extend(list(scheme.oauth_conf_file_names))

if global_config.has_dashboard():
logger.info("Including dashboard")
dashboard_definition_json_path = os.path.join(
output_directory,
ta_name,
"appserver",
"static",
"js",
"build",
"custom",
)
dashboard.generate_dashboard(
global_config, gc_path, ta_name, dashboard_definition_json_path
)

else:
global_config = None
conf_file_names = []
logger.warning(
"Skipped generating UI components as globalConfig file does not exist"
sys.exit(1)
global_config.parse_user_defined_handlers()
scheme = global_config_builder_schema.GlobalConfigBuilderSchema(global_config)
if global_config.has_pages():
utils.recursive_overwrite(
os.path.join(internal_root_dir, "package"),
os.path.join(output_directory, ta_name),
ui_source_map,
)
ucc_lib_target = os.path.join(output_directory, ta_name, "lib")
global_config_file = (
"globalConfig.yaml" if gc_path.endswith(".yaml") else "globalConfig.json"
)
output_build_path = os.path.join(
output_directory, ta_name, "appserver", "static", "js", "build"
)
if not os.path.isdir(output_build_path):
# this path may not exist for the .conf-only add-ons
os.makedirs(output_build_path)
global_config.dump(os.path.join(output_build_path, global_config_file))
logger.info("Copied globalConfig to output")
ucc_lib_target = os.path.join(output_directory, ta_name, "lib")
try:
install_python_libraries(
source,
ucc_lib_target,
python_binary_name,
includes_ui=True,
os_libraries=global_config.os_libraries,
pip_version=pip_version,
pip_legacy_resolver=pip_legacy_resolver,
pip_custom_flag=pip_custom_flag,
includes_oauth=global_config.has_oauth(),
)
logger.info(
f"Installed add-on requirements into {ucc_lib_target} from {source}"
except SplunktaucclibNotFound as e:
logger.error(str(e))
sys.exit(1)
logger.info(f"Installed add-on requirements into {ucc_lib_target} from {source}")
generated_files.extend(
begin(
global_config=global_config,
input_dir=source,
output_dir=output_directory,
ucc_dir=internal_root_dir,
addon_name=ta_name,
app_manifest=app_manifest,
addon_version=addon_version,
has_ui=global_config.meta.get("isVisible", True),
)
)
# TODO: all FILES GENERATED object: generated_files, use it for comparison
if global_config.has_pages():
builder_obj = RestBuilder(scheme, os.path.join(output_directory, ta_name))
builder_obj.build()
_modify_and_replace_token_for_oauth_templates(
ta_name,
global_config,
output_directory,
)
if global_config.has_inputs():
logger.info("Generating inputs code")
_add_modular_input(ta_name, global_config, output_directory)
if global_config.has_alerts():
logger.info("Generating alerts code")
alert_builder.generate_alerts(global_config, ta_name, output_directory)

conf_file_names = []
conf_file_names.extend(list(scheme.settings_conf_file_names))
conf_file_names.extend(list(scheme.configs_conf_file_names))
conf_file_names.extend(list(scheme.oauth_conf_file_names))

if global_config.has_dashboard():
logger.info("Including dashboard")
dashboard_definition_json_path = os.path.join(
output_directory,
ta_name,
"appserver",
"static",
"js",
"build",
"custom",
)
dashboard.generate_dashboard(
global_config, gc_path, ta_name, dashboard_definition_json_path
)

ignore_list = _get_ignore_list(
Expand Down
6 changes: 6 additions & 0 deletions splunk_add_on_ucc_framework/generators/doc_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ def generate_docs() -> None:
f"| {f_tup.file_name} | output/<YOUR_ADDON_NAME>/{'/'.join(f_tup.file_path)} "
f"| {f_tup.file_description} |"
)
# statically appending this line as we are generating it now
doc_content.append(
"| globalConfig.json | <source_dir> | "
"Generates globalConfig.json file in the source code if globalConfig "
"is not present in source directory at build time. |"
)
doc_content.append("\n")
with open(
join(
Expand Down
21 changes: 20 additions & 1 deletion tests/smoke/test_ucc_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,12 @@ def test_ucc_generate_with_configuration_files_only():
"package_no_global_config",
"package",
)
global_config_path = path.join(
package_folder,
path.pardir,
"globalConfig.json",
)
assert not path.exists(global_config_path)
build.generate(source=package_folder, output_directory=temp_dir)

expected_folder = path.join(
Expand Down Expand Up @@ -474,6 +480,10 @@ def test_ucc_generate_with_configuration_files_only():
expected_folder,
actual_folder,
)
# the globalConfig would now always exist
assert path.exists(global_config_path)
# clean-up for tests
os.remove(global_config_path)


def test_ucc_generate_openapi_with_configuration_files_only():
Expand All @@ -491,7 +501,16 @@ def test_ucc_generate_openapi_with_configuration_files_only():
actual_file_path = path.join(
temp_dir, "Splunk_TA_UCCExample", "appserver", "static", "openapi.json"
)
assert not path.exists(actual_file_path)
# the openapi.json would now exist as globalConfig.json would always exist
assert path.exists(actual_file_path)
# clean-up for tests
os.remove(
path.join(
package_folder,
path.pardir,
"globalConfig.json",
)
)


def test_ucc_build_verbose_mode(caplog):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"meta": {
"name": "Splunk_TA_UCCExample",
"restRoot": "Splunk_TA_UCCExample",
"displayName": "Splunk Add-on for UCC Example",
"version": "5.59.0+728642d3c",
"checkForUpdates": true,
"schemaVersion": "0.0.9",
"_uccVersion": "5.59.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"openapi": "3.0.0",
"info": {
"title": "Splunk_TA_UCCExample",
"version": "5.59.0+728642d3c",
"description": "Splunk Add-on for UCC Example",
"contact": {
"name": "Splunk Inc."
}
},
"servers": [
{
"url": "https://{domain}:{port}/servicesNS/-/Splunk_TA_UCCExample",
"variables": {
"domain": {
"default": "localhost"
},
"port": {
"default": "8089"
}
},
"description": "Access via management interface"
}
],
"components": {
"schemas": {},
"securitySchemes": {
"BasicAuth": {
"type": "http",
"scheme": "basic"
}
}
},
"paths": {},
"security": [
{
"BasicAuth": []
}
]
}
8 changes: 7 additions & 1 deletion tests/unit/generators/test_doc_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def test_generate_docs(mock_open, mock_dirname, mock_realpath):
"| ------------ | ------------ | ----------------- |",
"| file1.conf | output/<YOUR_ADDON_NAME>/some/path | Conf file |",
"| file2.xml | output/<YOUR_ADDON_NAME>/xml/path | XML file |",
"| globalConfig.json | <source_dir> | "
"Generates globalConfig.json file in the source code if globalConfig "
"is not present in source directory at build time. |",
"\n",
]
)
Expand Down Expand Up @@ -75,7 +78,10 @@ def test_generate_docs_empty_list(mock_open_file, mock_dirname, mock_realpath):
"",
"| File Name | File Location | File Description |",
"| ------------ | ------------ | ----------------- |",
"\n",
"| globalConfig.json | <source_dir> | "
"Generates globalConfig.json file in the source code if globalConfig "
"is not present in source directory at build time. |"
"\n\n",
]
)

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/test_install_python_libraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ def test_install_libraries_when_no_splunktaucclib_is_present_but_has_ui(tmp_path

with pytest.raises(SplunktaucclibNotFound) as exc:
install_python_libraries(
str(tmp_path),
str(tmp_ucc_lib_target),
source_path=str(tmp_path),
ucc_lib_target=str(tmp_ucc_lib_target),
python_binary_name="python3",
includes_ui=True,
)
Expand Down
Loading