Skip to content

Commit 7be13ad

Browse files
authored
Cloud discovery (#50)
* Azure discovery improvements and auto firewall exception adding * Azure firewall detection on existing connections * Improve package installation flow * Improve driver failed to load proccess * Fix default region * Improve azure discovery * Ux improvements * Cloud discovery refacotirng * Add redshift and bigquery adapters. Add AWS and GCP discovery * Connection picker polish * Mock * Fix MySQL dependency: use PyMySQL instead of mysql-connector-python * Fix test failures: Windows auth check, Docker tab navigation, password visibility tests --------- Co-authored-by: Peter Adams <18162810+Maxteabag@users.noreply.github.com>
1 parent 24a5e59 commit 7be13ad

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+6539
-449
lines changed

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ dynamic = ["version"]
3939
all = [
4040
"psycopg2-binary>=2.9.0",
4141
"mssql-python>=1.1.0",
42-
"mysql-connector-python>=9.1.0", # min avoids known CVEs
42+
"PyMySQL>=1.1.0",
4343
"mariadb>=1.1.0",
4444
"oracledb>=2.0.0",
4545
"duckdb>=1.1.0", # min avoids known CVEs
@@ -55,7 +55,7 @@ all = [
5555
postgres = ["psycopg2-binary>=2.9.0"]
5656
cockroachdb = ["psycopg2-binary>=2.9.0"]
5757
mssql = ["mssql-python>=1.1.0"]
58-
mysql = ["mysql-connector-python>=9.1.0"] # min avoids known CVEs
58+
mysql = ["PyMySQL>=1.1.0"]
5959
mariadb = ["mariadb>=1.1.0"]
6060
oracle = ["oracledb>=2.0.0"]
6161
duckdb = ["duckdb>=1.1.0"] # min avoids known CVEs

sqlit/cli.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ def main() -> int:
151151
metavar="COUNT",
152152
help="Maximum rows to fetch and render (default: 10000). Use for performance testing.",
153153
)
154+
parser.add_argument(
155+
"--mock-cloud",
156+
action="store_true",
157+
help="Use mock cloud provider data (Azure, AWS, GCP) for demos/screenshots.",
158+
)
154159
parser.add_argument(
155160
"--profile-startup",
156161
action="store_true",
@@ -255,6 +260,11 @@ def main() -> int:
255260
help="Maximum rows to fetch (default: 1000, use 0 for unlimited)",
256261
)
257262

263+
# Docker discovery command
264+
docker_parser = subparsers.add_parser("docker", help="Docker container discovery")
265+
docker_subparsers = docker_parser.add_subparsers(dest="docker_command", help="Docker commands")
266+
docker_subparsers.add_parser("list", help="List detected database containers")
267+
258268
startup_mark = time.perf_counter()
259269
args = parser.parse_args(filtered_argv[1:]) # Skip program name
260270
if args.settings:
@@ -281,6 +291,10 @@ def main() -> int:
281291
os.environ["SQLIT_MAX_ROWS"] = str(args.max_rows)
282292
else:
283293
os.environ.pop("SQLIT_MAX_ROWS", None)
294+
if args.mock_cloud:
295+
os.environ["SQLIT_MOCK_CLOUD"] = "1"
296+
else:
297+
os.environ.pop("SQLIT_MOCK_CLOUD", None)
284298
if args.profile_startup:
285299
os.environ["SQLIT_PROFILE_STARTUP"] = "1"
286300
else:
@@ -388,6 +402,15 @@ def main() -> int:
388402
if args.command == "query":
389403
return cmd_query(args)
390404

405+
if args.command == "docker":
406+
from .commands import cmd_docker_list
407+
408+
if args.docker_command == "list":
409+
return cmd_docker_list(args)
410+
else:
411+
docker_parser.print_help()
412+
return 1
413+
391414
parser.print_help()
392415
return 1
393416

sqlit/commands.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,54 @@ def _output_table(columns: list[str], rows: list[tuple], truncated: bool) -> Non
345345
print(f"\n({len(rows)} row(s) returned)")
346346

347347

348+
def cmd_docker_list(args: Any) -> int:
349+
"""List detected Docker database containers."""
350+
from .services.docker_detector import (
351+
ContainerStatus,
352+
DockerStatus,
353+
detect_database_containers,
354+
)
355+
356+
status, containers = detect_database_containers()
357+
358+
if status == DockerStatus.NOT_INSTALLED:
359+
print("Error: Docker Python library not installed.")
360+
print("Install it with: pip install docker")
361+
return 1
362+
elif status == DockerStatus.NOT_RUNNING:
363+
print("Error: Docker is not running.")
364+
return 1
365+
elif status == DockerStatus.NOT_ACCESSIBLE:
366+
print("Error: Docker is not accessible (permission denied).")
367+
print("Try adding your user to the docker group or running with sudo.")
368+
return 1
369+
370+
if not containers:
371+
print("No database containers found.")
372+
return 0
373+
374+
running = [c for c in containers if c.status == ContainerStatus.RUNNING]
375+
exited = [c for c in containers if c.status == ContainerStatus.EXITED]
376+
377+
print(f"{'Container':<25} {'Type':<12} {'Port':<8} {'Database':<15} {'Status':<10}")
378+
print("-" * 75)
379+
380+
for c in running:
381+
port_str = str(c.port) if c.port else "-"
382+
db_str = c.database[:13] + ".." if c.database and len(c.database) > 15 else (c.database or "-")
383+
name_str = c.container_name[:23] + ".." if len(c.container_name) > 25 else c.container_name
384+
print(f"{name_str:<25} {c.db_type:<12} {port_str:<8} {db_str:<15} {'running':<10}")
385+
386+
for c in exited:
387+
port_str = "-"
388+
db_str = c.database[:13] + ".." if c.database and len(c.database) > 15 else (c.database or "-")
389+
name_str = c.container_name[:23] + ".." if len(c.container_name) > 25 else c.container_name
390+
print(f"{name_str:<25} {c.db_type:<12} {port_str:<8} {db_str:<15} {'exited':<10}")
391+
392+
print(f"\nFound {len(running)} running, {len(exited)} exited database container(s).")
393+
return 0
394+
395+
348396
def cmd_query(
349397
args: Any,
350398
*,

sqlit/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ class AuthType(Enum):
133133
AD_PASSWORD = "ad_password"
134134
AD_INTERACTIVE = "ad_interactive"
135135
AD_INTEGRATED = "ad_integrated"
136+
AD_DEFAULT = "ad_default" # Uses Azure CLI / environment credentials
136137

137138

138139
AUTH_TYPE_LABELS = {
@@ -141,6 +142,7 @@ class AuthType(Enum):
141142
AuthType.AD_PASSWORD: "Microsoft Entra Password",
142143
AuthType.AD_INTERACTIVE: "Microsoft Entra MFA",
143144
AuthType.AD_INTEGRATED: "Microsoft Entra Integrated",
145+
AuthType.AD_DEFAULT: "Microsoft Entra Default (CLI)",
144146
}
145147

146148

@@ -229,6 +231,7 @@ def get_source_emoji(self) -> str:
229231
# Source emoji mapping
230232
SOURCE_EMOJIS: dict[str, str] = {
231233
"docker": "🐳 ",
234+
"azure": "",
232235
}
233236

234237

sqlit/db/adapters/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
"DatabaseAdapter",
2020
"TableInfo",
2121
# Adapter classes (lazy via __getattr__)
22+
"AthenaAdapter",
23+
"BigQueryAdapter",
2224
"ClickHouseAdapter",
2325
"CockroachDBAdapter",
2426
"DuckDBAdapter",
@@ -27,17 +29,19 @@
2729
"MySQLAdapter",
2830
"OracleAdapter",
2931
"PostgreSQLAdapter",
32+
"RedshiftAdapter",
3033
"SQLiteAdapter",
3134
"SQLServerAdapter",
3235
"SupabaseAdapter",
3336
"TursoAdapter",
34-
"AthenaAdapter",
3537
# Factory helpers
3638
"get_adapter",
3739
"get_supported_adapter_db_types",
3840
]
3941

4042
if TYPE_CHECKING:
43+
from .athena import AthenaAdapter
44+
from .bigquery import BigQueryAdapter
4145
from .clickhouse import ClickHouseAdapter
4246
from .cockroachdb import CockroachDBAdapter
4347
from .duckdb import DuckDBAdapter
@@ -47,10 +51,10 @@
4751
from .mysql import MySQLAdapter
4852
from .oracle import OracleAdapter
4953
from .postgresql import PostgreSQLAdapter
54+
from .redshift import RedshiftAdapter
5055
from .sqlite import SQLiteAdapter
5156
from .supabase import SupabaseAdapter
5257
from .turso import TursoAdapter
53-
from .athena import AthenaAdapter
5458

5559

5660
def get_adapter(db_type: str) -> DatabaseAdapter:

0 commit comments

Comments
 (0)