diff --git a/poetry.lock b/poetry.lock index 3a52f339..d245f611 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,32 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. + +[[package]] +name = "about-time" +version = "4.2.1" +description = "Easily measure timing and throughput of code blocks, with beautiful human friendly representations." +category = "main" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "about-time-4.2.1.tar.gz", hash = "sha256:6a538862d33ce67d997429d14998310e1dbfda6cb7d9bbfbf799c4709847fece"}, + {file = "about_time-4.2.1-py3-none-any.whl", hash = "sha256:8bbf4c75fe13cbd3d72f49a03b02c5c7dca32169b6d49117c257e7eb3eaee341"}, +] + +[[package]] +name = "alive-progress" +version = "3.1.2" +description = "A new kind of Progress Bar, with real-time throughput, ETA, and very cool animations!" +category = "main" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "alive-progress-3.1.2.tar.gz", hash = "sha256:b22ba960151f582cdd6d489c56462d2f9adca821608075d0d8d2cd15d1b6845b"}, + {file = "alive_progress-3.1.2-py3-none-any.whl", hash = "sha256:d2b89b60fee2f112668117a9f361ceea44a685a37cafd009f396b87b9816efa3"}, +] + +[package.dependencies] +about-time = "4.2.1" +grapheme = "0.6.0" [[package]] name = "altgraph" @@ -72,6 +100,8 @@ mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -353,6 +383,21 @@ calendars = ["convertdate", "hijri-converter"] fasttext = ["fasttext"] langdetect = ["langdetect"] +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "flake8" version = "6.0.0" @@ -421,6 +466,20 @@ pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0dev)"] +[[package]] +name = "grapheme" +version = "0.6.0" +description = "Unicode grapheme helpers" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "grapheme-0.6.0.tar.gz", hash = "sha256:44c2b9f21bbe77cfb05835fec230bd435954275267fea1858013b102f8603cca"}, +] + +[package.extras] +test = ["pytest", "sphinx", "sphinx-autobuild", "twine", "wheel"] + [[package]] name = "httmock" version = "1.4.0" @@ -448,6 +507,25 @@ files = [ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] +[[package]] +name = "importlib-resources" +version = "5.12.0" +description = "Read resources from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, + {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -653,6 +731,7 @@ files = [ contourpy = ">=1.0.1" cycler = ">=0.10" fonttools = ">=4.22.0" +importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} kiwisolver = ">=1.0.1" numpy = ">=1.20" packaging = ">=20.0" @@ -710,6 +789,7 @@ files = [ [package.dependencies] mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing-extensions = ">=3.10" [package.extras] @@ -836,6 +916,7 @@ files = [ [package.dependencies] numpy = [ + {version = ">=1.20.3", markers = "python_version < \"3.10\""}, {version = ">=1.21.0", markers = "python_version >= \"3.10\""}, {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, ] @@ -877,6 +958,20 @@ category = "main" optional = false python-versions = ">=3.7" files = [ + {file = "Pillow-9.4.0-1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1"}, + {file = "Pillow-9.4.0-1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12"}, + {file = "Pillow-9.4.0-1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd"}, + {file = "Pillow-9.4.0-1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9"}, + {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"}, + {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"}, + {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"}, + {file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"}, + {file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"}, + {file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"}, + {file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"}, + {file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"}, + {file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"}, + {file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"}, {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"}, {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"}, {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"}, @@ -1193,9 +1288,11 @@ files = [ [package.dependencies] attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] @@ -1518,6 +1615,18 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + [[package]] name = "typer" version = "0.7.0" @@ -1671,7 +1780,23 @@ docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] optional = ["python-socks", "wsaccel"] test = ["websockets"] +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [metadata] lock-version = "2.0" -python-versions = ">=3.11,<3.12" -content-hash = "817cf47c5901d1820c7e13ee7edddce945ed4f8ebe076cd39020397ac8d5b62b" +python-versions = ">=3.9,<3.12" +content-hash = "c9745b8f30ad68c862f3d34fb7856096f2fa178700aea8fdf0540915f19bc8a9" diff --git a/pyproject.toml b/pyproject.toml index f3bea420..fe9317c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ pydantic = "1.10.7" kubernetes = "^26.1.0" prometheus-api-client = "^0.5.3" numpy = "^1.24.2" +alive-progress = "^3.1.2" [tool.poetry.group.dev.dependencies] diff --git a/requirements.txt b/requirements.txt index 3cab1f9d..b16a2ffe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +about-time==4.2.1 ; python_version >= "3.9" and python_version < "3.12" +alive-progress==3.1.2 ; python_version >= "3.9" and python_version < "3.12" cachetools==5.3.0 ; python_version >= "3.9" and python_version < "3.12" certifi==2022.12.7 ; python_version >= "3.9" and python_version < "3.12" charset-normalizer==3.0.1 ; python_version >= "3.9" and python_version < "3.12" @@ -9,8 +11,10 @@ cycler==0.11.0 ; python_version >= "3.9" and python_version < "3.12" dateparser==1.1.7 ; python_version >= "3.9" and python_version < "3.12" fonttools==4.39.0 ; python_version >= "3.9" and python_version < "3.12" google-auth==2.16.2 ; python_version >= "3.9" and python_version < "3.12" +grapheme==0.6.0 ; python_version >= "3.9" and python_version < "3.12" httmock==1.4.0 ; python_version >= "3.9" and python_version < "3.12" idna==3.4 ; python_version >= "3.9" and python_version < "3.12" +importlib-resources==5.12.0 ; python_version >= "3.9" and python_version < "3.10" kiwisolver==1.4.4 ; python_version >= "3.9" and python_version < "3.12" kubernetes==26.1.0 ; python_version >= "3.9" and python_version < "3.12" matplotlib==3.7.1 ; python_version >= "3.9" and python_version < "3.12" @@ -43,3 +47,4 @@ tzdata==2022.7 ; python_version >= "3.9" and python_version < "3.12" tzlocal==4.2 ; python_version >= "3.9" and python_version < "3.12" urllib3==1.26.14 ; python_version >= "3.9" and python_version < "3.12" websocket-client==1.5.1 ; python_version >= "3.9" and python_version < "3.12" +zipp==3.15.0 ; python_version >= "3.9" and python_version < "3.10" diff --git a/robusta_krr/core/runner.py b/robusta_krr/core/runner.py index 51642fee..40a7b8e3 100644 --- a/robusta_krr/core/runner.py +++ b/robusta_krr/core/runner.py @@ -11,7 +11,7 @@ from robusta_krr.utils.configurable import Configurable from robusta_krr.utils.logo import ASCII_LOGO from robusta_krr.utils.version import get_version - +from robusta_krr.utils.progress_bar import ProgressBar class Runner(Configurable): EXPECTED_EXCEPTIONS = (KeyboardInterrupt, PrometheusNotFound) @@ -111,6 +111,8 @@ async def _calculate_object_recommendations(self, object: K8sObjectData) -> tupl data = dict(zip(ResourceType, data_tuple)) queries = {resource: data[resource].query for resource in ResourceType} + self.__progressbar.progress() + # NOTE: We run this in a threadpool as the strategy calculation might be CPU intensive # But keep in mind that numpy calcluations will not block the GIL result = await asyncio.to_thread(self._strategy.run, data, object) @@ -143,7 +145,8 @@ async def _collect_result(self) -> Result: self.warning("Note that you are using the '*' namespace filter, which by default excludes kube-system.") return Result(scans=[]) - resource_recommendations = await self._gather_objects_recommendations(objects) + with ProgressBar(self.config, total=len(objects), title="Calculating Recommendation") as self.__progressbar: + resource_recommendations = await self._gather_objects_recommendations(objects) return Result( scans=[ diff --git a/robusta_krr/utils/progress_bar.py b/robusta_krr/utils/progress_bar.py new file mode 100644 index 00000000..68bfb8ac --- /dev/null +++ b/robusta_krr/utils/progress_bar.py @@ -0,0 +1,23 @@ +from robusta_krr.utils.configurable import Configurable +from alive_progress import alive_bar +from robusta_krr.core.models.config import Config + +class ProgressBar(Configurable): + def __init__(self, config: Config, **kwargs) -> None: + super().__init__(config) + self.show_bar = self.echo_active + if self.show_bar: + self.alive_bar = alive_bar(**kwargs) + + def __enter__(self): + if self.show_bar: + self.bar = self.alive_bar.__enter__() + return self + + def progress(self): + if self.show_bar: + self.bar() + + def __exit__(self, *args): + if self.show_bar: + self.alive_bar.__exit__(*args) \ No newline at end of file