diff --git a/README.md b/README.md index 690040c..4d476dc 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,8 @@ with DiodeDryRunClient(app_name="my_app", output_dir="/tmp") as client: ``` The produced file can later be ingested by a real Diode instance using -`load_dryrun_entities` with a standard `DiodeClient`: +`load_dryrun_entities` with a standard `DiodeClient` or via the bundled +`diode-replay-dryrun` helper: ```python from netboxlabs.diode.sdk import DiodeClient, load_dryrun_entities @@ -104,6 +105,17 @@ with DiodeClient( client.ingest(entities=entities) ``` +Alternatively, the same file can be ingested using the `diode-replay-dryrun` +command shipped with the SDK: + +```bash +diode-replay-dryrun \ + --target grpc://localhost:8080/diode \ + --app-name my-test-app \ + --app-version 0.0.1 \ + my_app_92722156890707.json +``` + ## Supported entities (object types) * ASN diff --git a/netboxlabs/diode/scripts/__init__.py b/netboxlabs/diode/scripts/__init__.py new file mode 100644 index 0000000..194d30d --- /dev/null +++ b/netboxlabs/diode/scripts/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# Copyright 2025 NetBox Labs Inc +"""NetBox Labs, Diode - Scripts.""" diff --git a/netboxlabs/diode/scripts/dryrun_replay.py b/netboxlabs/diode/scripts/dryrun_replay.py new file mode 100644 index 0000000..0c2670f --- /dev/null +++ b/netboxlabs/diode/scripts/dryrun_replay.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# Copyright 2025 NetBox Labs Inc +"""CLI helper to ingest dry-run JSON messages into Diode.""" + +import argparse +import sys + +from netboxlabs.diode.sdk import DiodeClient, load_dryrun_entities + + +def main() -> None: + """Ingest JSON files generated by ``DiodeDryRunClient``.""" + parser = argparse.ArgumentParser( + description="Load dry-run JSON messages and ingest them into Diode" + ) + parser.add_argument( + "-t", + "--target", + required=True, + help="gRPC target of the Diode server, e.g. grpc://localhost:8080/diode", + ) + parser.add_argument( + "-a", + "--app-name", + required=True, + help="Application name used when ingesting the dry-run messages", + ) + parser.add_argument( + "-v", + "--app-version", + required=True, + help="Application version used when ingesting the dry-run messages", + ) + parser.add_argument( + "-c", + "--client-id", + help="OAuth2 client ID. Defaults to the DIODE_CLIENT_ID environment variable if not provided", + ) + parser.add_argument( + "-k", + "--client-secret", + help="OAuth2 client secret. Defaults to the DIODE_CLIENT_SECRET environment variable if not provided", + ) + parser.add_argument( + "files", + nargs="+", + metavar="FILE", + help="Dry-run JSON files to ingest", + ) + + args = parser.parse_args() + + with DiodeClient( + target=args.target, + app_name=args.app_name, + app_version=args.app_version, + client_id=args.client_id, + client_secret=args.client_secret, + ) as client: + has_errors = False + for file_path in args.files: + entities = list(load_dryrun_entities(file_path)) + if entities: + response = client.ingest(entities=entities) + if response.errors: + print(f"Errors while ingesting {file_path}: {response.errors}", file=sys.stderr) + has_errors = True + else: + print(f"Ingested {len(entities)} entities from {file_path}") + if has_errors: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 249b6ed..a6ac5a0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ test = ["coverage", "pytest", "pytest-cov==6.0.0"] [tool.coverage.run] omit = [ + "*/netboxlabs/diode/scripts/*", "*/netboxlabs/diode/sdk/ingester.py", "*/netboxlabs/diode/sdk/diode/*", "*/netboxlabs/diode/sdk/validate/*", @@ -45,10 +46,12 @@ omit = [ [project.urls] # Optional "Homepage" = "https://netboxlabs.com/" -[project.scripts] # Optional +[project.scripts] +diode-replay-dryrun = "netboxlabs.diode.scripts.dryrun_replay:main" [tool.setuptools] packages = [ + "netboxlabs.diode.scripts", "netboxlabs.diode.sdk", "netboxlabs.diode.sdk.diode", "netboxlabs.diode.sdk.diode.v1",