From 5908819fb4169c60f12dac7aeffff8c84de1b3aa Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Fri, 6 Dec 2024 15:15:32 -0300 Subject: [PATCH 1/6] cmds: rename 'results' cmd to 'maestro-results' The results command is accessing the Maestro API directly for fetching results. We want to build an origin agnostic infra, so let's rename this one to 'maestro-results' for clarity. Signed-off-by: Gustavo Padovan --- docs/{results.md => maestro-results.md} | 12 +++++++----- kcidev/main.py | 6 +++--- .../subcommands/{results.py => maestro_results.py} | 4 ++-- tests/test_kcidev.py | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) rename docs/{results.md => maestro-results.md} (91%) rename kcidev/subcommands/{results.py => maestro_results.py} (95%) diff --git a/docs/results.md b/docs/maestro-results.md similarity index 91% rename from docs/results.md rename to docs/maestro-results.md index 660831e..37927ce 100644 --- a/docs/results.md +++ b/docs/maestro-results.md @@ -1,21 +1,23 @@ +++ -title = 'results' +title = 'maestro-results' date = 2024-01-14T07:07:07+01:00 -description = 'Command for show test results.' +description = 'Command for show Maestro test results.' +++ -This command will show the test result by node id. +This command is Maestro-specific and will show the test result by node id. + +Do not use it, unless you are requesting test on Maestro through `kci-dev`. Example: ```sh -kci-dev results --nodeid +kci-dev maestro-results --nodeid ``` This command will show the results of tests by page nodes limit and page offset. Example: ```sh -kci-dev results --nodes --limit --offset +kci-dev maestro-results --nodes --limit --offset ``` Result sample: diff --git a/kcidev/main.py b/kcidev/main.py index b62f666..2d264bc 100755 --- a/kcidev/main.py +++ b/kcidev/main.py @@ -4,11 +4,11 @@ import click from kcidev.libs.common import * -from kcidev.subcommands import checkout, commit, patch, results, testretry +from kcidev.subcommands import checkout, commit, patch, maestro_results, testretry @click.group( - help="Stand alone tool for Linux Kernel developers and maintainers to interact with KernelCI" + help="Stand alone tool for Linux Kernel developers and maintainers to interact with KernelCI." ) @click.version_option("0.1.0", prog_name="kci-dev") @click.option( @@ -38,7 +38,7 @@ def run(): cli.add_command(checkout.checkout) cli.add_command(commit.commit) cli.add_command(patch.patch) - cli.add_command(results.results) + cli.add_command(maestro_results.maestro_results) cli.add_command(testretry.testretry) cli() diff --git a/kcidev/subcommands/results.py b/kcidev/subcommands/maestro_results.py similarity index 95% rename from kcidev/subcommands/results.py rename to kcidev/subcommands/maestro_results.py index a6a2da0..97d138b 100644 --- a/kcidev/subcommands/results.py +++ b/kcidev/subcommands/maestro_results.py @@ -72,7 +72,7 @@ def get_nodes(url, limit, offset, filter, field): print_nodes(nodes, field) -@click.command(help="Get results") +@click.command(help="Get results directly from KernelCI's Maestro") @click.option( "--nodeid", required=False, @@ -109,7 +109,7 @@ def get_nodes(url, limit, offset, filter, field): help="Print only particular field(s) from node data", ) @click.pass_context -def results(ctx, nodeid, nodes, limit, offset, filter, field): +def maestro_results(ctx, nodeid, nodes, limit, offset, filter, field): config = ctx.obj.get("CFG") instance = ctx.obj.get("INSTANCE") url = api_connection(config[instance]["api"]) diff --git a/tests/test_kcidev.py b/tests/test_kcidev.py index 989e24f..69bff16 100644 --- a/tests/test_kcidev.py +++ b/tests/test_kcidev.py @@ -46,7 +46,7 @@ def test_kcidev_patch_help(): def test_kcidev_results_help(): - command = ["poetry", "run", "kci-dev", "results", "--help"] + command = ["poetry", "run", "kci-dev", "maestro-results", "--help"] result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True) print("returncode: " + str(result.returncode)) print("#### stdout ####") @@ -85,7 +85,7 @@ def test_kcidev_results_tests(): "kci-dev", "--instance", "staging", - "results", + "maestro-results", "--nodeid", "65a1355ee98651d0fe81e41d", ] From 64a1cfab3b0553042f8c1f8dbc8d9cf97524fff0 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Tue, 10 Dec 2024 19:13:33 -0300 Subject: [PATCH 2/6] common: add click.echo wrappers Create base wrappers for normal print and error messages. Signed-off-by: Gustavo Padovan --- kcidev/libs/common.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kcidev/libs/common.py b/kcidev/libs/common.py index cd97cef..68b5336 100644 --- a/kcidev/libs/common.py +++ b/kcidev/libs/common.py @@ -34,3 +34,11 @@ def load_toml(settings): raise click.Abort() return config + + +def kci_print(content): + click.echo(content) + + +def kci_err(content): + click.secho(content, fg="red", err=True) From 721542cae3577dc1df2cdaf9c4b02d7ddaff0dd7 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Mon, 9 Dec 2024 15:56:45 -0300 Subject: [PATCH 3/6] cmds: add experimental results fetch from Dashboard API It introduces `kci-dev results` to supporting pulling results data in various forms from the Dashboard API. For now it has two actions (through the --action param) 1. `--action=summary`: to get a basic numeric summary of pass/fail 2. `--action=failed-builds`: to get the list of fail builds Addionally it has the `--download-logs` option to download the build logs with errors. Signed-off-by: Gustavo Padovan --- kcidev/main.py | 37 +++++++---- kcidev/subcommands/results.py | 116 ++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 kcidev/subcommands/results.py diff --git a/kcidev/main.py b/kcidev/main.py index 2d264bc..544b2a5 100755 --- a/kcidev/main.py +++ b/kcidev/main.py @@ -4,7 +4,14 @@ import click from kcidev.libs.common import * -from kcidev.subcommands import checkout, commit, patch, maestro_results, testretry +from kcidev.subcommands import ( + checkout, + commit, + maestro_results, + patch, + results, + testretry, +) @click.group( @@ -20,18 +27,21 @@ @click.option("--instance", help="API instance to use", required=False) @click.pass_context def cli(ctx, settings, instance): - ctx.obj = {"CFG": load_toml(settings)} - if instance: - ctx.obj["INSTANCE"] = instance - else: - ctx.obj["INSTANCE"] = ctx.obj["CFG"].get("default_instance") - if not ctx.obj["INSTANCE"]: - click.secho("No instance defined in settings or as argument", fg="red") - raise click.Abort() - if ctx.obj["INSTANCE"] not in ctx.obj["CFG"]: - click.secho(f"Instance {ctx.obj['INSTANCE']} not found in {settings}", fg="red") - raise click.Abort() - pass + if ctx.invoked_subcommand != "results": + ctx.obj = {"CFG": load_toml(settings)} + if instance: + ctx.obj["INSTANCE"] = instance + else: + ctx.obj["INSTANCE"] = ctx.obj["CFG"].get("default_instance") + if not ctx.obj["INSTANCE"]: + click.secho("No instance defined in settings or as argument", fg="red") + raise click.Abort() + if ctx.obj["INSTANCE"] not in ctx.obj["CFG"]: + click.secho( + f"Instance {ctx.obj['INSTANCE']} not found in {settings}", fg="red" + ) + raise click.Abort() + pass def run(): @@ -40,6 +50,7 @@ def run(): cli.add_command(patch.patch) cli.add_command(maestro_results.maestro_results) cli.add_command(testretry.testretry) + cli.add_command(results.results) cli() diff --git a/kcidev/subcommands/results.py b/kcidev/subcommands/results.py new file mode 100644 index 0000000..1f0bdae --- /dev/null +++ b/kcidev/subcommands/results.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import gzip +import json +import urllib + +import click +import requests + +from kcidev.libs.common import * + +DASHBOARD_API = "https://dashboard.kernelci.org/api/" + + +def fetch_from_api(endpoint, params): + base_url = urllib.parse.urljoin(DASHBOARD_API, endpoint) + try: + url = "{}?{}".format(base_url, urllib.parse.urlencode(params)) + r = requests.get(url) + except: + click.secho(f"Failed to fetch from {DASHBOARD_API}.") + raise click.Abort() + + return r.json() + + +def cmd_summary(data): + kci_print(f"Builds: {data['buildsSummary']['builds']}") + kci_print(f"Boots: {data['bootStatusSummary']}") + kci_print(f"Tests: {data['testStatusSummary']}") + + +def cmd_failed_builds(data, download_logs): + kci_print("Failed builds:") + for build in data["builds"]: + if not build["valid"]: + log_path = build["log_url"] + if download_logs: + try: + log_gz = requests.get(build["log_url"]) + log = gzip.decompress(log_gz.content) + log_file = f"{build['config_name']}-{build['architecture']}-{build['compiler']}.log" + with open(log_file, mode="wb") as file: + file.write(log) + log_path = os.path.join(os.getcwd(), log_file) + except: + kci_err(f"Failed to fetch log {log_file}).") + pass + + kci_print( + f"- config: {build['config_name']}; arch: {build['architecture']}" + ) + kci_print(f" compiler: {build['compiler']}") + kci_print(f" config_url: {build['config_url']}") + kci_print(f" log: {log_path}") + kci_print(f" id: {build['id']}") + + +def process_action(action, data, download_logs): + if action == None or action == "summary": + cmd_summary(data) + elif action == "failed-builds": + cmd_failed_builds(data, download_logs) + + +def fetch_full_results(origin, giturl, branch, commit): + endpoint = f"tree/{commit}/full" + params = { + "origin": origin, + "git_url": giturl, + "git_branch": branch, + "commit": commit, + } + + return fetch_from_api(endpoint, params) + + +@click.command(help=" [Experimental] Get results from the dashboard") +@click.option( + "--origin", + help="Select KCIDB origin", + default="maestro", +) +@click.option( + "--giturl", + help="Git URL of kernel tree ", + required=True, +) +@click.option( + "--branch", + help="Branch to get results for", + required=True, +) +@click.option( + "--commit", + help="Commit or tag to get results for", + required=True, +) +@click.option( + "--action", + help="Select desired results action", +) +@click.option( + "--download-logs", + is_flag=True, + help="Select desired results action", +) +@click.pass_context +def results(ctx, origin, giturl, branch, commit, action, download_logs): + data = fetch_full_results(origin, giturl, branch, commit) + process_action(action, data, download_logs) + + +if __name__ == "__main__": + main_kcidev() From 2cc540ad597bcc5cfd2f0ead118cde4979733616 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Tue, 10 Dec 2024 19:41:53 -0300 Subject: [PATCH 4/6] docs: refactor to place Maestro commands in their corner kci-dev is evolving beyond just the Maestro service, so we have to re-organize the house to accomadate our new usecases. Signed-off-by: Gustavo Padovan --- docs/_index.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/_index.md b/docs/_index.md index 448deb4..4ddaa79 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -27,6 +27,8 @@ poetry run kci-dev ## Configuration +> Configuration is only necessary if you are using any of the Maestro Commands listed in the Maestro section. + kci-dev searches for and loads a configuration file in the following order of priority: 1) The global configuration file located at /etc/kci-dev.toml. 2) The user-specific configuration file at ~/.config/kci-dev/kci-dev.toml @@ -61,9 +63,9 @@ pipeline is the URL of the KernelCI Pipeline API endpoint, api is the URL of the If you are using KernelCI Pipeline instance, you can get the token from the project maintainers. If it is a local instance, you can generate your token using [kernelci-pipeline/tools/jwt_generator.py](https://github.com/kernelci/kernelci-pipeline/blob/main/tools/jwt_generator.py) script. -## Options +### Configuration options -### instance +#### --instance You can provide the instance name to use for the command. Example: @@ -71,7 +73,7 @@ Example: kci-dev --instance staging ``` -### settings +#### --settings You can provide the configuration file path to use for the command. @@ -80,17 +82,17 @@ Example: kci-dev --settings /path/to/.kci-dev.toml ``` -## Commands +### Maestro Commands -### checkout +#### checkout - [checkout](checkout) -### testretry +#### testretry - [testretry](testretry) -### results +#### maestro-results -- [results](results) +- [maestro-results](maestro-results) From ee1df43e61afbf57256f6239b856d1046fbda5b1 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Tue, 10 Dec 2024 19:57:53 -0300 Subject: [PATCH 5/6] docs: Clarify install and usage for a wider audience kci-dev is growing to meet the demand of the kernel community, so our documenation has to follow that movement. This commit is nothing more than just a first stab to document better. Signed-off-by: Gustavo Padovan --- README.md | 2 +- docs/_index.md | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 10a8cda..41f3ff4 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ > *cmdline tool for interact with KernelCI* -kci-dev is a cmdline tool for interact with a enabled KernelCI server +Stand alone tool for Linux Kernel developers and maintainers to interact with KernelCI services. ## Quickstart diff --git a/docs/_index.md b/docs/_index.md index 4ddaa79..2debbb7 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -4,25 +4,45 @@ date = 2024-01-14T07:07:07+01:00 description = 'Tool for interact programmatically with KernelCI instances.' +++ -kci-dev is a cmdline tool for interact with a enabled KernelCI server. -Purpose of this tool to provide a easy way to use features of KernelCI Pipeline instance. +Stand alone tool for Linux Kernel developers and maintainers to interact with KernelCI. + +Purpose of this tool to provide an easy-to-use command line tool for developers and maintainers request test from KernelCI, view results, download logs, integrate with scripts, etc. ## Installation -### Using PyPI and virtualenv +You may want to use python virtual environment. +If you are not familiar with it, check [this](https://docs.python.org/3/library/venv.html). + +To quickly setup it: + ```sh virtualenv .venv source .venv/bin/activate +``` + +### Using package from PyPI + +Simply install it using `pip`: + +```sh pip install kci-dev ``` -### Using poetry and virtualenv +### Development snapshot through poetry + +Clone the `kci-dev` repo you want, select the desired branch and run: + ```sh virtualenv .venv source .venv/bin/activate pip install poetry poetry install -poetry run kci-dev +``` + +Then, to execute kci-dev: + +```sh +poetry run kci-dev ``` ## Configuration From 84668d993742d0471a6e24551b104491e3cf7ef0 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Tue, 10 Dec 2024 20:20:44 -0300 Subject: [PATCH 6/6] docs: add results cmd documentation. Help our users understand how to use the experimental commands. Signed-off-by: Gustavo Padovan --- docs/_index.md | 6 +++++ docs/results.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 docs/results.md diff --git a/docs/_index.md b/docs/_index.md index 2debbb7..ee8d342 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -102,6 +102,12 @@ Example: kci-dev --settings /path/to/.kci-dev.toml ``` +### General Commands + +#### results + +Pull results from the Dashboard. See detailed [documentation](results). + ### Maestro Commands #### checkout diff --git a/docs/results.md b/docs/results.md new file mode 100644 index 0000000..8d93ebf --- /dev/null +++ b/docs/results.md @@ -0,0 +1,66 @@ ++++ +title = 'results' +date = 2024-12-10T07:07:07+01:00 +description = 'Fetch results from the KernelCI ecosystem.' ++++ + +`kci-dev` pulls from our Dashboard API. As of now, it is an EXPERIMENTAL tooling under development with close collaboration from Linux kernel maintainers. + +> KNOWN ISSUE: The Dashboard endpoint we are using returns a file of a few megabytes in size, so download may take +a few seconds, slowing down your usage of `kci-dev results`. We are working on [it](https://github.com/kernelci/dashboard/issues/661). + +## Base parameters + +### --origin + +Set the KCIDB origin desired. 'maestro' is the default. + +### --giturl + +The url of the tree to fetch results + +### --branch + +The branch to get results for + +### --commit + +The tip of tree commit being tested. It needs to be the full commit hash. + +Unfortunately the Dashboard API doesn't support git tags as parameters yet. + +## Results actions + +### --action=summary + +Shows a numeric summary of the build, boot and test results. +If `--action` is omitted, it will show the summary by default. + +Example: + +```sh +kci-dev results --giturl 'https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git' --branch master --commit d1486dca38afd08ca279ae94eb3a397f10737824 --action=summary +``` + +### --action=failed-builds + +List failed builds. + +Example: + +```sh +kci-dev results --giturl 'https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git' --branch master --commit d1486dca38afd08ca279ae94eb3a397f10737824 --action failed-builds +``` + +## Downloading logs + +`--download-logs` Download failed logs when used with `--action=failed-*` commands. + +Example: +```sh +kci-dev results --giturl 'https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git' --branch master --commit d1486dca38afd08ca279ae94eb3a397f10737824 --action failed-builds --download-logs +``` + + + +