diff --git a/projects/control-service/projects/versions-of-external-dependencies.gradle b/projects/control-service/projects/versions-of-external-dependencies.gradle index 6332e2a104..f834a4c8ee 100644 --- a/projects/control-service/projects/versions-of-external-dependencies.gradle +++ b/projects/control-service/projects/versions-of-external-dependencies.gradle @@ -2,7 +2,7 @@ project.ext { versions = [ // include only libraries which are NOT in the Spring Boot BOM: org.springframework:spring-boot-dependencies 'com.google.guava:guava' : 'com.google.guava:guava:31.1-jre', - 'com.nimbusds:nimbus-jose-jwt' : 'com.nimbusds:nimbus-jose-jwt:9.25.6', + 'com.nimbusds:nimbus-jose-jwt' : 'com.nimbusds:nimbus-jose-jwt:9.28', // Update the ph-client to latest. 'io.micrometer:micrometer-registry-prometheus' : 'io.micrometer:micrometer-registry-prometheus:1.10.2', 'org.openapitools:jackson-databind-nullable' : 'org.openapitools:jackson-databind-nullable:0.2.4', @@ -15,8 +15,8 @@ project.ext { 'org.junit.jupiter:junit-jupiter-engine' : 'org.junit.jupiter:junit-jupiter-engine:5.9.1', 'org.junit.platform:junit-platform-suite-api' : 'org.junit.platform:junit-platform-suite-api:1.9.1', 'com.mmnaseri.utils:spring-data-mock' : 'com.mmnaseri.utils:spring-data-mock:2.2.0', - 'org.mockito:mockito-core' : 'org.mockito:mockito-core:4.10.0', - 'net.bytebuddy:byte-buddy' : 'net.bytebuddy:byte-buddy:1.12.19', + 'org.mockito:mockito-core' : 'org.mockito:mockito-core:4.11.0', + 'net.bytebuddy:byte-buddy' : 'net.bytebuddy:byte-buddy:1.12.20', 'com.fasterxml.jackson.core:jackson-databind' : 'com.fasterxml.jackson.core:jackson-databind:2.14.1', 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' : 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.1', 'org.json:json' : 'org.json:json:20220924', @@ -40,7 +40,7 @@ project.ext { 'org.apache.commons:commons-lang3' : 'org.apache.commons:commons-lang3:3.12.0', 'org.apache.commons:commons-text' : 'org.apache.commons:commons-text:1.10.0', 'com.github.tomakehurst:wiremock' : 'com.github.tomakehurst:wiremock:2.27.2', - 'com.graphql-java:graphql-java-extended-scalars' : 'com.graphql-java:graphql-java-extended-scalars:19.1', + 'com.graphql-java:graphql-java-extended-scalars' : 'com.graphql-java:graphql-java-extended-scalars:20.0', 'org.springframework.retry:spring-retry' : 'org.springframework.retry:spring-retry:2.0.0', 'org.apache.tika:tika-core' : 'org.apache.tika:tika-core:2.6.0', diff --git a/projects/vdk-plugins/vdk-ipython-ext/.plugin-ci.yml b/projects/vdk-plugins/vdk-ipython-ext/.plugin-ci.yml new file mode 100644 index 0000000000..211ab1e968 --- /dev/null +++ b/projects/vdk-plugins/vdk-ipython-ext/.plugin-ci.yml @@ -0,0 +1,35 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 + +image: "python:3.7" + +.build-vdk-ipython-ext: + variables: + PLUGIN_NAME: vdk-ipython-ext + extends: .build-plugin + +build-py37-vdk-ipython-ext: + extends: .build-vdk-ipython-ext + image: "python:3.7" + + +build-py38-vdk-ipython-ext: + extends: .build-vdk-ipython-ext + image: "python:3.8" + +build-py39-vdk-ipython-ext: + extends: .build-vdk-ipython-ext + image: "python:3.9" + +build-py310-vdk-ipython-ext: + extends: .build-vdk-ipython-ext + image: "python:3.10" + +build-py311-vdk-ipython-ext: + extends: .build-vdk-ipython-ext + image: "python:3.11" + +release-vdk-ipython-ext: + variables: + PLUGIN_NAME: vdk-ipython-ext + extends: .release-plugin diff --git a/projects/vdk-plugins/vdk-ipython-ext/README.md b/projects/vdk-plugins/vdk-ipython-ext/README.md new file mode 100644 index 0000000000..4f1646ec52 --- /dev/null +++ b/projects/vdk-plugins/vdk-ipython-ext/README.md @@ -0,0 +1,53 @@ +# ipython-ext + +Ipython extension for VDK + +This extension introduces a magic command for Jupyter. +The command enables the user to load job_input for his current data job and use it freely while working with Jupyter. + +See more about magic commands: https://ipython.readthedocs.io/en/stable/interactive/magics.html + + +## Usage +To use the extension it must be firstly installed with pip as a python package. +Then to load the extension in Jupyter the user should use: +``` +%reload_ext vdk_ipython_ext +``` +And to load the job_input: +``` +%reload_job_input +``` +The %reload_job_input magic can be used with arguments such as passing the job's path with --path +or giving the job a new with --name, etc. + +### Example +The output of this example is "myjob" +``` +%reload_ext vdk_ipython_ext + +%reload_job_input --name=myjob + +job_input.get_name() +``` + +### Build and testing + +``` +pip install -r requirements.txt +pip install -e . +pytest +``` + +In VDK repo [../build-plugin.sh](https://github.com/vmware/versatile-data-kit/tree/main/projects/vdk-plugins/build-plugin.sh) script can be used also. + + +#### Note about the CICD: + +.plugin-ci.yaml is needed only for plugins part of [Versatile Data Kit Plugin repo](https://github.com/vmware/versatile-data-kit/tree/main/projects/vdk-plugins). + +The CI/CD is separated in two stages, a build stage and a release stage. +The build stage is made up of a few jobs, all which inherit from the same +job configuration and only differ in the Python version they use (3.7, 3.8, 3.9, 3.10 and 3.11). +They run according to rules, which are ordered in a way such that changes to a +plugin's directory trigger the plugin CI, but changes to a different plugin does not. diff --git a/projects/vdk-plugins/vdk-ipython-ext/requirements.txt b/projects/vdk-plugins/vdk-ipython-ext/requirements.txt new file mode 100644 index 0000000000..2ffb45f8f0 --- /dev/null +++ b/projects/vdk-plugins/vdk-ipython-ext/requirements.txt @@ -0,0 +1,8 @@ +# this file is used to provide testing requirements +# for requirements (dependencies) needed during and after installation of the plugin see (and update) setup.py install_requires section + +IPython + +pytest +vdk-core +vdk-test-utils diff --git a/projects/vdk-plugins/vdk-ipython-ext/setup.py b/projects/vdk-plugins/vdk-ipython-ext/setup.py new file mode 100644 index 0000000000..a64b22beb1 --- /dev/null +++ b/projects/vdk-plugins/vdk-ipython-ext/setup.py @@ -0,0 +1,31 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +import pathlib + +import setuptools + + +__version__ = "0.1.0" + +setuptools.setup( + name="vdk-ipython-ext", + version=__version__, + url="https://github.com/vmware/versatile-data-kit", + description="Ipython extension for VDK", + long_description=pathlib.Path("README.md").read_text(), + long_description_content_type="text/markdown", + install_requires=["vdk-core"], + package_dir={"": "src"}, + packages=setuptools.find_namespace_packages(where="src"), + entry_points={"vdk.plugin.run": ["ipython-ext = vdk_ipython_ext"]}, + classifiers=[ + "Development Status :: 2 - Pre-Alpha", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Framework :: IPython", + ], +) diff --git a/projects/vdk-plugins/vdk-ipython-ext/src/vdk_ipython_ext.py b/projects/vdk-plugins/vdk-ipython-ext/src/vdk_ipython_ext.py new file mode 100644 index 0000000000..f39d748985 --- /dev/null +++ b/projects/vdk-plugins/vdk-ipython-ext/src/vdk_ipython_ext.py @@ -0,0 +1,45 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +import os +import pathlib + +from IPython import get_ipython +from IPython.core.magic_arguments import argument +from IPython.core.magic_arguments import magic_arguments +from IPython.core.magic_arguments import parse_argstring +from vdk.internal.builtin_plugins.run.standalone_data_job import ( + StandaloneDataJobFactory, +) + + +def load_ipython_extension(ipython): + """ + IPython will look for this function specifically. + See https://ipython.readthedocs.io/en/stable/config/extensions/index.html + """ + ipython.register_magic_function(magic_load_job, magic_name="reload_job_input") + + +@magic_arguments() +@argument("-p", "--path", type=str, default=None) +@argument("-n", "--name", type=str, default=None) +@argument("-a", "--arguments", type=str, default=None) +@argument("-t", "--template", type=str, default=None) +def magic_load_job(line: str): + """ + You can use %initialize_vdk_job line magic within your Notebook to reload the job_input variable + for your current job + See more for line magic: https://ipython.readthedocs.io/en/stable/interactive/magics.html + """ + args = parse_argstring(magic_load_job, line) + load_job(args.path, args.name, args.arguments, args.template) + + +def load_job( + path: str = None, name: str = None, arguments: str = None, template: str = None +): + path = pathlib.Path(path) if path else pathlib.Path(os.getcwd()) + with StandaloneDataJobFactory.create( + data_job_directory=path, name=name, job_args=arguments, template_name=template + ) as job_input: + get_ipython().push(variables={"job_input": job_input}) diff --git a/projects/vdk-plugins/vdk-ipython-ext/tests/test_plugin.py b/projects/vdk-plugins/vdk-ipython-ext/tests/test_plugin.py new file mode 100644 index 0000000000..961262e9b7 --- /dev/null +++ b/projects/vdk-plugins/vdk-ipython-ext/tests/test_plugin.py @@ -0,0 +1,38 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +import pytest +from IPython.core.error import UsageError +from IPython.testing.globalipapp import start_ipython +from vdk.api.job_input import IJobInput + + +@pytest.fixture(scope="session") +def session_ip(): + yield start_ipython() + + +@pytest.fixture(scope="function") +def ip(session_ip): + session_ip.run_line_magic(magic_name="load_ext", line="vdk_ipython_ext") + yield session_ip + session_ip.run_line_magic(magic_name="reset", line="-f") + + +def test_load_job_input_with_no_arguments(ip): + ip.run_line_magic(magic_name="reload_job_input", line="") + assert ip.user_global_ns["job_input"] is not None + assert isinstance(ip.user_global_ns["job_input"], IJobInput) + + +def test_load_job_input_with_valid_argument(ip): + ip.run_line_magic(magic_name="reload_job_input", line="--name=test") + assert ip.user_global_ns["job_input"] is not None + assert isinstance(ip.user_global_ns["job_input"], IJobInput) + assert ip.user_global_ns["job_input"].get_name() == "test" + + +def test_load_job_input_with_invalid_argument(ip): + with pytest.raises( + UsageError, match=r"unrecognized arguments: --invalid_arg=dummy" + ): + ip.run_line_magic(magic_name="reload_job_input", line="--invalid_arg=dummy") diff --git a/projects/vdk-plugins/vdk-jobs-troubleshooting/.plugin-ci.yml b/projects/vdk-plugins/vdk-jobs-troubleshooting/.plugin-ci.yml index b0518a1512..aac889fee9 100644 --- a/projects/vdk-plugins/vdk-jobs-troubleshooting/.plugin-ci.yml +++ b/projects/vdk-plugins/vdk-jobs-troubleshooting/.plugin-ci.yml @@ -30,13 +30,13 @@ build-py311-vdk-jobs-troubleshooting: extends: .build-vdk-jobs-troubleshooting image: "python:3.11" -#build-vdk-jobs-troubleshooting-on-vdk-core-release: -# variables: -# PLUGIN_NAME: vdk-jobs-troubleshooting -# extends: .build-plugin-on-vdk-core-release -# image: "python:3.9" - -#release-vdk-jobs-troubleshooting: -# variables: -# PLUGIN_NAME: vdk-jobs-troubleshooting -# extends: .release-plugin +build-vdk-jobs-troubleshooting-on-vdk-core-release: + variables: + PLUGIN_NAME: vdk-jobs-troubleshooting + extends: .build-plugin-on-vdk-core-release + image: "python:3.9" + +release-vdk-jobs-troubleshooting: + variables: + PLUGIN_NAME: vdk-jobs-troubleshooting + extends: .release-plugin diff --git a/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/jobs_troubleshoot_plugin.py b/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/jobs_troubleshoot_plugin.py index 4f22725246..6dd981effa 100644 --- a/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/jobs_troubleshoot_plugin.py +++ b/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/jobs_troubleshoot_plugin.py @@ -8,18 +8,65 @@ from vdk.api.plugin.hook_markers import hookimpl from vdk.api.plugin.plugin_registry import IPluginRegistry +from vdk.internal.builtin_plugins.run.job_context import JobContext from vdk.internal.core.config import ConfigurationBuilder +from vdk.plugin.jobs_troubleshoot.api.troubleshoot_utility import ITroubleshootUtility from vdk.plugin.jobs_troubleshoot.troubleshoot_configuration import add_definitions +from vdk.plugin.jobs_troubleshoot.troubleshoot_utilities.utilities_registry import ( + get_utilities_to_use, +) log = logging.getLogger(__name__) class JobTroubleshootingPlugin: + """ + Entrypoint for the Data Jobs Troubleshooting plugin - it provides the means to initialize and configure + troubleshooting utilities, based on the configured environment variables. + + Example: + To start the thread dump utility, configure the following environment variables: + VDK_TROUBLESHOOT_UTILITIES_TO_USE="thread-dump" + VDK_PORT_TO_USE=8783 + """ + + def __init__(self): + self.troubleshooting_utils: List[ITroubleshootUtility] = [] + @staticmethod @hookimpl def vdk_configure(config_builder: ConfigurationBuilder) -> None: add_definitions(config_builder=config_builder) + @hookimpl + def initialize_job(self, context: JobContext) -> None: + self.troubleshooting_utils = get_utilities_to_use( + job_config=context.core_context.configuration + ) + try: + for util in self.troubleshooting_utils: + util.start() + except Exception as e: + log.info( + f""" + An exception occurred while processing a troubleshooting + utility. The error was: {e} + """ + ) + + @hookimpl + def finalize_job(self, context: JobContext) -> None: + try: + for util in self.troubleshooting_utils: + util.stop() + except Exception as e: + log.info( + f""" + An exception occurred while processing a troubleshooting + utility. The error was: {e} + """ + ) + @hookimpl def vdk_start(plugin_registry: IPluginRegistry, command_line_args: List) -> None: diff --git a/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/troubleshoot_utilities/healthcheck_server.py b/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/troubleshoot_utilities/healthcheck_server.py new file mode 100644 index 0000000000..b793273ea3 --- /dev/null +++ b/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/troubleshoot_utilities/healthcheck_server.py @@ -0,0 +1,45 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +import logging +from http.server import HTTPServer +from threading import Thread +from typing import Any + +log = logging.getLogger(__name__) + + +class HealthCheckServer: + """ + A class for creating an HTTP server for serving requests. + """ + + def __init__(self, port: int, handler: Any = None): + """ + Initializes a new instance of the HealthCheckServer class. + + Parameters: + port (int): The port number on which the server will listen for requests. + handler (Any, optional): The request handler class. Defaults to SimpleHTTPRequestHandler. + """ + if handler: + self._server = HTTPServer(("", port), handler) + self._thread = Thread(target=self._server.serve_forever) + log.error(f"Troubleshooting utility server started on port {port}.") + else: + log.error( + "Troubleshooting utility handler not specified. Will not start the server." + ) + + def start(self): + """ + Starts the server. + """ + self._thread.start() + + def stop(self): + """ + Stops the server. + """ + self._server.shutdown() + self._server.server_close() + self._thread.join() diff --git a/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/troubleshoot_utilities/thread_dump.py b/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/troubleshoot_utilities/thread_dump.py new file mode 100644 index 0000000000..1140a4aa11 --- /dev/null +++ b/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/troubleshoot_utilities/thread_dump.py @@ -0,0 +1,80 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +import logging +import sys +import threading +import traceback +from http.server import BaseHTTPRequestHandler + +from vdk.internal.core.config import Configuration +from vdk.plugin.jobs_troubleshoot.api.troubleshoot_utility import ITroubleshootUtility +from vdk.plugin.jobs_troubleshoot.troubleshoot_configuration import ( + TROUBLESHOOT_PORT_TO_USE, +) +from vdk.plugin.jobs_troubleshoot.troubleshoot_utilities.healthcheck_server import ( + HealthCheckServer, +) + +log = logging.getLogger(__name__) + + +class ThreadDumpHandler(BaseHTTPRequestHandler): + """ + A class for handling HTTP requests and generating thread dumps used by the thread-dump troubleshooting utility. + """ + + def do_GET(self): + """ + Handles GET requests. If the request path is "/threads", a thread dump is logged and sent as the response. + Otherwise, a 404 error is sent as the response. + """ + if self.path == "/threads": + log.info("Dumping threads") + self.send_response(200) + self.send_header("Content-type", "text/plain]") + self.end_headers() + self._log_thread_dump() + else: + self.send_error(404) + + def _log_thread_dump(self): + """ + Logs a thread dump and writes it to the response body. + """ + try: + log.info("------- Dumping threads stacks -------") + for t in threading.enumerate(): + log.info( + f"Thread:{t.getName()} alive:{t.is_alive()} daemon:{t.isDaemon()}" + ) + self.wfile.write( + str.encode( + f"\nThread:{t.getName()} alive:{t.is_alive()} daemon:{t.isDaemon()}" + ) + ) + code = [] + for threadId, stack in sys._current_frames().items(): + code.append("# ThreadID: %s" % threadId) + for filename, lineno, name, line in traceback.extract_stack(stack): + code.append( + "%s::%d::%s::%s" + % (filename, lineno, name, (line.strip() if line else "")) + ) + for line in code: + log.info(line) + self.wfile.write(str.encode(f"\n {line}")) + log.info("------- End of thread dump -------") + except Exception as e: + log.exception(f"Error while dumping threads:{e}", exc_info=True) + + +class ThreadDumpUtility(ITroubleshootUtility): + def __init__(self, job_configuration: Configuration): + self.port_to_use = job_configuration.get_value(TROUBLESHOOT_PORT_TO_USE) + self.server = HealthCheckServer(self.port_to_use, ThreadDumpHandler) + + def start(self): + self.server.start() + + def stop(self): + self.server.stop() diff --git a/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/troubleshoot_utilities/utilities_registry.py b/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/troubleshoot_utilities/utilities_registry.py new file mode 100644 index 0000000000..7e5decfc90 --- /dev/null +++ b/projects/vdk-plugins/vdk-jobs-troubleshooting/src/vdk/plugin/jobs_troubleshoot/troubleshoot_utilities/utilities_registry.py @@ -0,0 +1,64 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +import logging +from typing import Any +from typing import Dict +from typing import List + +from vdk.internal.core.config import Configuration +from vdk.plugin.jobs_troubleshoot.api.troubleshoot_utility import ITroubleshootUtility +from vdk.plugin.jobs_troubleshoot.troubleshoot_configuration import ( + TROUBLESHOOT_UTILITIES_TO_USE, +) +from vdk.plugin.jobs_troubleshoot.troubleshoot_utilities.thread_dump import ( + ThreadDumpUtility, +) + + +log = logging.getLogger(__name__) + + +def utilities_registry(job_config: Configuration) -> Dict[str, Any]: + """ + The troubleshooting utilities registry is where all utility objects are to + be initialized. + TODO: Come up with a more elegant approach to register utilities. + + :param job_config: The data job configuration. + :return: A dictionary with all available troubleshooting utilities. + """ + registered_utilities: Dict[str, Any] = {} + registered_utilities["thread-dump"] = ThreadDumpUtility( + job_configuration=job_config + ) + + return registered_utilities + + +def get_utilities_to_use( + job_config: Configuration, +) -> List[ITroubleshootUtility]: + """ + Get a list of the initialized troubleshooting utilities that are specified + by the VDK_TROUBLESHOOT_UTILITIES_TO_USE configuration variable. + :param job_config: Data Job configuration + :return: A list of utility objects that are to be used. + """ + utilities: List[ITroubleshootUtility] = [] + selected_utilities: str = job_config.get_value(TROUBLESHOOT_UTILITIES_TO_USE) + registered_utilities: Dict = utilities_registry(job_config=job_config) + + for util in selected_utilities.split(","): + registered_util = registered_utilities.get(util) + if registered_util: + utilities.append(registered_util) + else: + log.info( + f""" + Utility {util} is not in the list of available troubleshooting + utilities. + Available utilities: {registered_utilities.keys()} + """ + ) + + return utilities diff --git a/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/functional/jobs/request-thread-dump/request-thread-dump.py b/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/functional/jobs/request-thread-dump/request-thread-dump.py new file mode 100644 index 0000000000..013aecee36 --- /dev/null +++ b/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/functional/jobs/request-thread-dump/request-thread-dump.py @@ -0,0 +1,18 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +import requests +from vdk.api.job_input import IJobInput + + +def run(job_input: IJobInput): + response = requests.get("http://localhost:8783/threads") + print(response.status_code) + print(response.text) + + if response.status_code != 200: + raise Exception("unexpected response code from server") + + if "Thread:MainThread" not in response.text: + raise Exception("unexpected output from server") diff --git a/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/functional/test_thread_dump.py b/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/functional/test_thread_dump.py new file mode 100644 index 0000000000..669750bb38 --- /dev/null +++ b/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/functional/test_thread_dump.py @@ -0,0 +1,35 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +import os +import pathlib +from unittest import mock + +from click.testing import Result +from vdk.plugin.jobs_troubleshoot import jobs_troubleshoot_plugin +from vdk.plugin.test_utils.util_funcs import cli_assert_equal +from vdk.plugin.test_utils.util_funcs import CliEntryBasedTestRunner +from vdk.plugin.test_utils.util_funcs import get_test_job_path + + +def job_path(job_name: str): + return get_test_job_path( + pathlib.Path(os.path.dirname(os.path.abspath(__file__))), job_name + ) + + +def test_http_ingestion(): + with mock.patch.dict( + os.environ, + { + "VDK_TROUBLESHOOT_UTILITIES_TO_USE": "thread-dump", + "VDK_PORT_TO_USE": "8783", + }, + ): + # create table first, as the ingestion fails otherwise + runner = CliEntryBasedTestRunner(jobs_troubleshoot_plugin) + + result: Result = runner.invoke(["run", job_path("request-thread-dump")]) + cli_assert_equal(0, result) + assert "Dumping threads stacks" in result.stdout diff --git a/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/test_jobs_troubleshoot.py b/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/test_jobs_troubleshoot.py deleted file mode 100644 index dac242d392..0000000000 --- a/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/test_jobs_troubleshoot.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright 2021 VMware, Inc. -# SPDX-License-Identifier: Apache-2.0 - - -def test_job_troubleshoot(): - pass diff --git a/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/test_utilities_registry.py b/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/test_utilities_registry.py new file mode 100644 index 0000000000..dfab41de01 --- /dev/null +++ b/projects/vdk-plugins/vdk-jobs-troubleshooting/tests/test_utilities_registry.py @@ -0,0 +1,39 @@ +# Copyright 2021 VMware, Inc. +# SPDX-License-Identifier: Apache-2.0 +from typing import List + +from vdk.internal.core.config import ConfigurationBuilder +from vdk.plugin.jobs_troubleshoot.troubleshoot_configuration import ( + TROUBLESHOOT_PORT_TO_USE, +) +from vdk.plugin.jobs_troubleshoot.troubleshoot_configuration import ( + TROUBLESHOOT_UTILITIES_TO_USE, +) +from vdk.plugin.jobs_troubleshoot.troubleshoot_utilities.utilities_registry import ( + get_utilities_to_use, +) + + +def test_get_utilities_to_use(): + config_builder = ConfigurationBuilder() + config_builder.set_value(key=TROUBLESHOOT_UTILITIES_TO_USE, value="thread-dump") + config_builder.set_value(key=TROUBLESHOOT_PORT_TO_USE, value=8783) + configuration = config_builder.build() + + utility = get_utilities_to_use(configuration) + + assert isinstance(utility, List) + assert len(utility) == 1 + + +def test_get_utilities_to_use__nonexistent_utility(): + config_builder = ConfigurationBuilder() + config_builder.set_value( + key=TROUBLESHOOT_UTILITIES_TO_USE, value="non-existent-utility" + ) + config_builder.set_value(key=TROUBLESHOOT_PORT_TO_USE, value=8783) + configuration = config_builder.build() + + utility = get_utilities_to_use(configuration) + + assert len(utility) == 0