phlo-clickstack 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ """ClickStack service package for Phlo."""
phlo_clickstack/cli.py ADDED
@@ -0,0 +1,113 @@
1
+ """CLI commands for querying ClickStack's bundled ClickHouse service."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from shutil import which
7
+ from subprocess import TimeoutExpired
8
+
9
+ import click
10
+
11
+ from phlo.cli.infrastructure.command import CommandError, run_command
12
+ from phlo.cli.infrastructure.compose import compose_base_cmd
13
+ from phlo.cli.infrastructure.utils import get_project_name
14
+ from phlo.logging import get_logger
15
+
16
+ logger = get_logger(__name__)
17
+
18
+
19
+ def _read_query(*, query: str | None, file: Path | None) -> str:
20
+ """Return SQL text from inline query or file input."""
21
+ if query and file:
22
+ raise click.ClickException("Use either an inline query or --file, not both.")
23
+ if file is not None:
24
+ try:
25
+ sql = file.read_text(encoding="utf-8")
26
+ except OSError as exc:
27
+ raise click.ClickException(f"Failed to read SQL file: {file}") from exc
28
+ if sql.strip():
29
+ return sql
30
+ raise click.ClickException(f"SQL file is empty: {file}")
31
+ if query and query.strip():
32
+ return query
33
+ raise click.ClickException("Provide a SQL query argument or --file.")
34
+
35
+
36
+ def _ensure_phlo_dir() -> Path:
37
+ """Return the local .phlo directory or exit with a clear error."""
38
+ phlo_dir = Path.cwd() / ".phlo"
39
+ if phlo_dir.exists():
40
+ return phlo_dir
41
+ raise click.ClickException(".phlo directory not found. Run 'phlo services init' first.")
42
+
43
+
44
+ def _require_docker() -> None:
45
+ """Validate that Docker is installed and responsive."""
46
+ if which("docker") is None:
47
+ raise click.ClickException("docker command not found.")
48
+ try:
49
+ result = run_command(
50
+ ["docker", "info"],
51
+ timeout_seconds=10,
52
+ capture_output=True,
53
+ check=False,
54
+ )
55
+ except TimeoutExpired as exc:
56
+ raise click.ClickException("docker info timed out.") from exc
57
+ if result.returncode == 0:
58
+ return
59
+ raise click.ClickException("Docker is not running.")
60
+
61
+
62
+ @click.group(name="clickstack")
63
+ def clickstack_group() -> None:
64
+ """Query and inspect the ClickStack service."""
65
+
66
+
67
+ @clickstack_group.command(name="query")
68
+ @click.argument("query", required=False)
69
+ @click.option("--file", "query_file", type=click.Path(exists=True, dir_okay=False, path_type=Path))
70
+ @click.option("--format", "output_format", default="TabSeparatedRaw", show_default=True)
71
+ @click.option("--timeout", "timeout_seconds", default=30, show_default=True, type=int)
72
+ def clickstack_query(
73
+ query: str | None,
74
+ query_file: Path | None,
75
+ output_format: str,
76
+ timeout_seconds: int,
77
+ ) -> None:
78
+ """Execute a ClickHouse query against the running ClickStack service."""
79
+ _require_docker()
80
+ phlo_dir = _ensure_phlo_dir()
81
+ project_name = get_project_name()
82
+ sql = _read_query(query=query, file=query_file)
83
+
84
+ cmd = compose_base_cmd(phlo_dir=phlo_dir, project_name=project_name)
85
+ cmd.extend(
86
+ [
87
+ "exec",
88
+ "-T",
89
+ "clickstack",
90
+ "clickhouse-client",
91
+ "--multiquery",
92
+ "--format",
93
+ output_format,
94
+ "--query",
95
+ sql,
96
+ ]
97
+ )
98
+
99
+ try:
100
+ result = run_command(
101
+ cmd,
102
+ timeout_seconds=timeout_seconds,
103
+ capture_output=True,
104
+ check=True,
105
+ )
106
+ except CommandError as exc:
107
+ stderr = exc.stderr.strip()
108
+ raise click.ClickException(stderr or str(exc)) from exc
109
+ except TimeoutExpired as exc:
110
+ raise click.ClickException(f"Query timed out after {timeout_seconds} seconds.") from exc
111
+
112
+ if result.stdout:
113
+ click.echo(result.stdout, nl=False)
@@ -0,0 +1,26 @@
1
+ """CLI plugin for ClickStack commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+ from phlo.plugins.base import CliCommandPlugin, PluginMetadata
8
+
9
+ from phlo_clickstack.cli import clickstack_group
10
+
11
+
12
+ class ClickStackCliPlugin(CliCommandPlugin):
13
+ """Register ClickStack CLI commands."""
14
+
15
+ @property
16
+ def metadata(self) -> PluginMetadata:
17
+ """Return plugin metadata for ClickStack CLI registration."""
18
+ return PluginMetadata(
19
+ name="clickstack",
20
+ version="0.1.0",
21
+ description="CLI commands for ClickStack query access",
22
+ )
23
+
24
+ def get_cli_commands(self) -> list[click.Command]:
25
+ """Return ClickStack CLI commands."""
26
+ return [clickstack_group]
@@ -0,0 +1,31 @@
1
+ """ClickStack service plugin."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import resources
6
+ from typing import Any
7
+
8
+ import yaml
9
+
10
+ from phlo.plugins import PluginMetadata, ServicePlugin
11
+
12
+
13
+ class ClickStackServicePlugin(ServicePlugin):
14
+ """Service plugin for ClickStack."""
15
+
16
+ @property
17
+ def metadata(self) -> PluginMetadata:
18
+ """Return plugin metadata for ClickStack service registration."""
19
+ return PluginMetadata(
20
+ name="clickstack",
21
+ version="0.1.0",
22
+ description="ClickStack all-in-one observability backend",
23
+ author="Phlo Team",
24
+ tags=["observability", "logs", "metrics", "traces"],
25
+ )
26
+
27
+ @property
28
+ def service_definition(self) -> dict[str, Any]:
29
+ """Load and return the ClickStack service definition."""
30
+ service_path = resources.files("phlo_clickstack").joinpath("service.yaml")
31
+ return yaml.safe_load(service_path.read_text(encoding="utf-8"))
@@ -0,0 +1,60 @@
1
+ name: clickstack
2
+ description: ClickStack all-in-one observability backend
3
+ category: observability
4
+ default: false
5
+ profile: observability
6
+
7
+ image: ${CLICKSTACK_IMAGE:-docker.hyperdx.io/hyperdx/hyperdx-all-in-one}
8
+
9
+ compose:
10
+ restart: unless-stopped
11
+ user: "0"
12
+ ports:
13
+ - "${CLICKSTACK_PORT:-8080}:8080"
14
+ - "${CLICKSTACK_OTLP_GRPC_PORT:-4317}:4317"
15
+ - "${CLICKSTACK_OTLP_HTTP_PORT:-4318}:4318"
16
+ - "${CLICKSTACK_NATIVE_PORT:-9000}:9000"
17
+ volumes:
18
+ - ./volumes/clickstack:/var/lib/clickhouse
19
+ healthcheck:
20
+ test:
21
+ [
22
+ "CMD",
23
+ "wget",
24
+ "--quiet",
25
+ "--tries=1",
26
+ "--spider",
27
+ "http://127.0.0.1:8080",
28
+ ]
29
+ interval: 10s
30
+ timeout: 5s
31
+ retries: 10
32
+
33
+ env_vars:
34
+ CLICKSTACK_IMAGE:
35
+ default: "docker.hyperdx.io/hyperdx/hyperdx-all-in-one"
36
+ description: Official ClickStack all-in-one image reference
37
+ CLICKSTACK_PORT:
38
+ default: 8080
39
+ description: ClickStack UI port
40
+ CLICKSTACK_OTLP_GRPC_PORT:
41
+ default: 4317
42
+ description: ClickStack OTLP gRPC ingest port
43
+ CLICKSTACK_OTLP_HTTP_PORT:
44
+ default: 4318
45
+ description: ClickStack OTLP HTTP ingest port
46
+ CLICKSTACK_NATIVE_PORT:
47
+ default: 9000
48
+ description: ClickHouse native port exposed by the all-in-one image
49
+ CLICKSTACK_PUBLIC_URL:
50
+ default: ""
51
+ description: Public ClickStack base URL used by observability links
52
+ CLICKSTACK_LOGS_PATH:
53
+ default: "/"
54
+ description: Path used to build ClickStack log links
55
+ CLICKSTACK_METRICS_PATH:
56
+ default: "/"
57
+ description: Path used to build ClickStack metric links
58
+ CLICKSTACK_DASHBOARDS_PATH:
59
+ default: "/"
60
+ description: Path used to build ClickStack dashboard links
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: phlo-clickstack
3
+ Version: 0.1.0
4
+ Summary: clickstack service plugin for Phlo
5
+ Author-email: Phlo Team <team@phlo.dev>
6
+ License: MIT
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/plain
9
+ Requires-Dist: phlo>=0.1.0
10
+ Requires-Dist: pyyaml>=6.0.1
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest>=7.0; extra == "dev"
13
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
14
+
15
+ clickstack service plugin for Phlo.
@@ -0,0 +1,10 @@
1
+ phlo_clickstack/__init__.py,sha256=APKW-ntAzXdaxXBzCUVg-uvjHcISeMRQtQs_w8jxfpc,43
2
+ phlo_clickstack/cli.py,sha256=y0gQL9EtZL-m0Ls0JHNjXQF4OBsW3dwWIy7j59BsLps,3665
3
+ phlo_clickstack/cli_plugin.py,sha256=arbOB891SJsJNM7eIOfzQWRQ0quc3aB-DfrIn4ZaMaI,729
4
+ phlo_clickstack/plugin.py,sha256=qgChkWVerUU6FJApMoN8ZivYOYCu1az_MZYbTBqXo1I,970
5
+ phlo_clickstack/service.yaml,sha256=mhB0l5ZiZost6U56osPRUMSvudb0FNBP0zyM0abMcVk,1682
6
+ phlo_clickstack-0.1.0.dist-info/METADATA,sha256=p4S1dFH2lpZocCAQa9RPDDvXG5brm4-JXz3HcgXXC4I,416
7
+ phlo_clickstack-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
8
+ phlo_clickstack-0.1.0.dist-info/entry_points.txt,sha256=FRjuN5Qxr1_6cKOKg-8qcVtvLMMQMkiujbxrjDhg12Q,164
9
+ phlo_clickstack-0.1.0.dist-info/top_level.txt,sha256=DP-XglbAvZvgxLt9MXVcx5x-6oM3zA5S8UiGwbZFGb4,16
10
+ phlo_clickstack-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,5 @@
1
+ [phlo.plugins.cli]
2
+ clickstack = phlo_clickstack.cli_plugin:ClickStackCliPlugin
3
+
4
+ [phlo.plugins.services]
5
+ clickstack = phlo_clickstack.plugin:ClickStackServicePlugin
@@ -0,0 +1 @@
1
+ phlo_clickstack