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

feat: add checks for the appropriate splunktaucclib version #1363

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
3 changes: 1 addition & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jsonschema = "^4.4.0"
PyYAML = "^6.0"
defusedxml = "^0.7.1"
colorama = "^0.4.6"
packaging = "24.0"

[tool.poetry.group.dev.dependencies]
mkdocs = "^1.4.2"
Expand Down
88 changes: 70 additions & 18 deletions splunk_add_on_ucc_framework/install_python_libraries.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import subprocess
import sys
from pathlib import Path

from packaging.version import Version
artemrys marked this conversation as resolved.
Show resolved Hide resolved
from typing import List, Optional, Set, Iterable, Dict
from splunk_add_on_ucc_framework.global_config import OSDependentLibraryConfig

Expand All @@ -30,26 +32,37 @@ class SplunktaucclibNotFound(Exception):
pass


class WrongSplunktaucclibVersion(Exception):
pass


class CouldNotInstallRequirements(Exception):
pass


def _subprocess_call(
class InvalidArguments(Exception):
pass


def _subprocess_run(
command: str,
command_desc: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
) -> int:
) -> "subprocess.CompletedProcess[bytes]":
command_desc = command_desc or command
try:
logger.info(f"Executing: {command}")
return_code = subprocess.call(command, shell=True, env=env)
process_result = subprocess.run(
command, shell=True, env=env, capture_output=True
)
return_code = process_result.returncode
if return_code < 0:
logger.error(
f"Child ({command_desc}) was terminated by signal {-return_code}"
)
if return_code > 0:
logger.error(f"Command ({command_desc}) returned {return_code} status code")
return return_code
return process_result
except OSError as e:
logger.error(f"Execution ({command_desc}) failed due to {e}")
raise e
Expand All @@ -58,26 +71,46 @@ def _subprocess_call(
def _pip_install(installer: str, command: str, command_desc: str) -> None:
cmd = f"{installer} -m pip install {command}"
try:
return_code = _subprocess_call(command=cmd, command_desc=command_desc)
return_code = _subprocess_run(command=cmd, command_desc=command_desc).returncode
if return_code != 0:
raise CouldNotInstallRequirements
except OSError as e:
raise CouldNotInstallRequirements from e


def _pip_is_lib_installed(
installer: str, target: str, libname: str, version: Optional[str] = None
installer: str,
target: str,
libname: str,
version: Optional[str] = None,
allow_higher_version: bool = False,
) -> bool:
if not version and allow_higher_version:
raise InvalidArguments(
"Parameter 'allow_higher_version' can not be set to True if 'version' parameter is not provided"
)

lib_installed_cmd = f"{installer} -m pip show --version {libname}"
lib_version_match_cmd = f'{lib_installed_cmd} | grep "Version: {version}"'

cmd = lib_version_match_cmd if version else lib_installed_cmd
if version and allow_higher_version:
cmd = f'{lib_installed_cmd} | grep "Version"'
elif version and not allow_higher_version:
cmd = f'{lib_installed_cmd} | grep "Version: {version}"'
else:
cmd = lib_installed_cmd

try:
my_env = os.environ.copy()
my_env["PYTHONPATH"] = target
return_code = _subprocess_call(command=cmd, env=my_env)
return return_code == 0
if allow_higher_version:
result = _subprocess_run(command=cmd, env=my_env)
if result.returncode != 0:
return False
result_version = result.stdout.decode("utf-8").split("Version:")[1].strip()
return Version(result_version) >= Version(version)
else:
return_code = _subprocess_run(command=cmd, env=my_env).returncode
return return_code == 0
except OSError as e:
raise CouldNotInstallRequirements from e

Expand All @@ -91,6 +124,30 @@ def _check_ucc_library_in_requirements_file(path_to_requirements: str) -> bool:
return False


def _check_libraries_required_for_ui(
python_binary_name: str, ucc_lib_target: str, path_to_requirements_file: str
) -> None:
if not _pip_is_lib_installed(
installer=python_binary_name,
target=ucc_lib_target,
libname="splunktaucclib",
):
raise SplunktaucclibNotFound(
f"This add-on has an UI, so the splunktaucclib is required but not found in "
f"{path_to_requirements_file}. Please add it there and make sure it is at least version 6.4."
)
if not _pip_is_lib_installed(
installer=python_binary_name,
target=ucc_lib_target,
libname="splunktaucclib",
version="6.4",
allow_higher_version=True,
):
raise WrongSplunktaucclibVersion(
"Splunktaucclib found but has the wrong version. Please make sure it is at least version 6.4."
)


def install_python_libraries(
source_path: str,
ucc_lib_target: str,
Expand All @@ -112,14 +169,9 @@ def install_python_libraries(
pip_version=pip_version,
pip_legacy_resolver=pip_legacy_resolver,
)
if includes_ui and not _pip_is_lib_installed(
installer=python_binary_name,
target=ucc_lib_target,
libname="splunktaucclib",
):
raise SplunktaucclibNotFound(
f"splunktaucclib is not found in {path_to_requirements_file}. "
f"Please add it there because this add-on has UI."
if includes_ui:
_check_libraries_required_for_ui(
python_binary_name, ucc_lib_target, path_to_requirements_file
)

cleanup_libraries = install_os_dependent_libraries(
Expand Down
Loading
Loading