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.
- phlo_clickstack/__init__.py +1 -0
- phlo_clickstack/cli.py +113 -0
- phlo_clickstack/cli_plugin.py +26 -0
- phlo_clickstack/plugin.py +31 -0
- phlo_clickstack/service.yaml +60 -0
- phlo_clickstack-0.1.0.dist-info/METADATA +15 -0
- phlo_clickstack-0.1.0.dist-info/RECORD +10 -0
- phlo_clickstack-0.1.0.dist-info/WHEEL +5 -0
- phlo_clickstack-0.1.0.dist-info/entry_points.txt +5 -0
- phlo_clickstack-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -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 @@
|
|
|
1
|
+
phlo_clickstack
|