starrocks-br 0.4.0__tar.gz → 0.5.1__tar.gz
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.
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/PKG-INFO +3 -2
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/pyproject.toml +4 -3
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/cli.py +106 -57
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/concurrency.py +2 -9
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/config.py +11 -7
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/db.py +3 -3
- starrocks_br-0.5.1/src/starrocks_br/error_handler.py +307 -0
- starrocks_br-0.5.1/src/starrocks_br/exceptions.py +109 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/executor.py +4 -4
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/history.py +4 -6
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/labels.py +2 -2
- starrocks_br-0.5.1/src/starrocks_br/logger.py +66 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/planner.py +5 -10
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/restore.py +13 -16
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/timezone.py +1 -2
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br.egg-info/PKG-INFO +3 -2
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br.egg-info/SOURCES.txt +8 -1
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br.egg-info/requires.txt +1 -0
- starrocks_br-0.5.1/tests/test_cli_backup.py +205 -0
- starrocks_br-0.5.1/tests/test_cli_exceptions.py +1113 -0
- starrocks_br-0.5.1/tests/test_cli_general.py +43 -0
- starrocks_br-0.5.1/tests/test_cli_init.py +16 -0
- starrocks_br-0.5.1/tests/test_cli_restore.py +156 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_concurrency.py +22 -12
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_config.py +8 -6
- starrocks_br-0.5.1/tests/test_error_handler.py +182 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_executor.py +3 -3
- starrocks_br-0.5.1/tests/test_logger.py +78 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_planner.py +5 -3
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_restore.py +39 -11
- starrocks_br-0.4.0/src/starrocks_br/logger.py +0 -36
- starrocks_br-0.4.0/tests/test_cli.py +0 -1227
- starrocks_br-0.4.0/tests/test_logger.py +0 -276
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/README.md +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/setup.cfg +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/__init__.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/health.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/repository.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/schema.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br/utils.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br.egg-info/dependency_links.txt +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br.egg-info/entry_points.txt +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/src/starrocks_br.egg-info/top_level.txt +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_db.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_health_checks.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_history.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_labels.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_repository_sql.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_schema_setup.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_timezone.py +0 -0
- {starrocks_br-0.4.0 → starrocks_br-0.5.1}/tests/test_utils.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: starrocks-br
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: StarRocks Backup and Restore automation tool
|
|
5
|
-
Requires-Python: >=3.
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
Requires-Dist: click<9,>=8.1.7
|
|
8
8
|
Requires-Dist: PyYAML<7,>=6.0.1
|
|
@@ -12,6 +12,7 @@ Requires-Dist: pytest<9,>=8.3.2; extra == "dev"
|
|
|
12
12
|
Requires-Dist: pytest-mock<4,>=3.14.0; extra == "dev"
|
|
13
13
|
Requires-Dist: pytest-cov<6,>=5.0.0; extra == "dev"
|
|
14
14
|
Requires-Dist: ruff<1,>=0.8.0; extra == "dev"
|
|
15
|
+
Requires-Dist: pre-commit<5,>=4.0.0; extra == "dev"
|
|
15
16
|
|
|
16
17
|
# StarRocks Backup & Restore
|
|
17
18
|
|
|
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "starrocks-br"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.1"
|
|
8
8
|
description = "StarRocks Backup and Restore automation tool"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
11
|
dependencies = [
|
|
12
12
|
"click>=8.1.7,<9",
|
|
13
13
|
"PyYAML>=6.0.1,<7",
|
|
@@ -20,6 +20,7 @@ dev = [
|
|
|
20
20
|
"pytest-mock>=3.14.0,<4",
|
|
21
21
|
"pytest-cov>=5.0.0,<6",
|
|
22
22
|
"ruff>=0.8.0,<1",
|
|
23
|
+
"pre-commit>=4.0.0,<5",
|
|
23
24
|
]
|
|
24
25
|
|
|
25
26
|
[project.scripts]
|
|
@@ -47,7 +48,7 @@ line-length = 100
|
|
|
47
48
|
|
|
48
49
|
[tool.ruff]
|
|
49
50
|
line-length = 100
|
|
50
|
-
target-version = "
|
|
51
|
+
target-version = "py310"
|
|
51
52
|
|
|
52
53
|
[tool.ruff.lint]
|
|
53
54
|
select = [
|
|
@@ -6,6 +6,8 @@ import click
|
|
|
6
6
|
from . import (
|
|
7
7
|
concurrency,
|
|
8
8
|
db,
|
|
9
|
+
error_handler,
|
|
10
|
+
exceptions,
|
|
9
11
|
executor,
|
|
10
12
|
health,
|
|
11
13
|
labels,
|
|
@@ -63,9 +65,20 @@ def _handle_snapshot_exists_error(
|
|
|
63
65
|
|
|
64
66
|
|
|
65
67
|
@click.group()
|
|
66
|
-
|
|
68
|
+
@click.option("--verbose", is_flag=True, help="Enable verbose debug logging")
|
|
69
|
+
@click.pass_context
|
|
70
|
+
def cli(ctx, verbose):
|
|
67
71
|
"""StarRocks Backup & Restore automation tool."""
|
|
68
|
-
|
|
72
|
+
ctx.ensure_object(dict)
|
|
73
|
+
ctx.obj["verbose"] = verbose
|
|
74
|
+
|
|
75
|
+
if verbose:
|
|
76
|
+
import logging
|
|
77
|
+
|
|
78
|
+
logger.setup_logging(level=logging.DEBUG)
|
|
79
|
+
logger.debug("Verbose logging enabled")
|
|
80
|
+
else:
|
|
81
|
+
logger.setup_logging()
|
|
69
82
|
|
|
70
83
|
|
|
71
84
|
@cli.command("init")
|
|
@@ -112,11 +125,14 @@ def init(config):
|
|
|
112
125
|
" starrocks-br backup incremental --group my_daily_incremental --config config.yaml"
|
|
113
126
|
)
|
|
114
127
|
|
|
115
|
-
except
|
|
116
|
-
|
|
128
|
+
except exceptions.ConfigFileNotFoundError as e:
|
|
129
|
+
error_handler.handle_config_file_not_found_error(e)
|
|
117
130
|
sys.exit(1)
|
|
118
|
-
except
|
|
119
|
-
|
|
131
|
+
except exceptions.ConfigValidationError as e:
|
|
132
|
+
error_handler.handle_config_validation_error(e, config)
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
except FileNotFoundError as e:
|
|
135
|
+
error_handler.handle_config_file_not_found_error(exceptions.ConfigFileNotFoundError(str(e)))
|
|
120
136
|
sys.exit(1)
|
|
121
137
|
except Exception as e:
|
|
122
138
|
logger.error(f"Failed to initialize schema: {e}")
|
|
@@ -262,14 +278,26 @@ def backup_incremental(config, baseline_backup, group, name):
|
|
|
262
278
|
logger.error(f"{result['error_message']}")
|
|
263
279
|
sys.exit(1)
|
|
264
280
|
|
|
281
|
+
except exceptions.ConcurrencyConflictError as e:
|
|
282
|
+
error_handler.handle_concurrency_conflict_error(e, config)
|
|
283
|
+
sys.exit(1)
|
|
284
|
+
except exceptions.BackupLabelNotFoundError as e:
|
|
285
|
+
error_handler.handle_backup_label_not_found_error(e, config)
|
|
286
|
+
sys.exit(1)
|
|
287
|
+
except exceptions.NoFullBackupFoundError as e:
|
|
288
|
+
error_handler.handle_no_full_backup_found_error(e, config, group)
|
|
289
|
+
sys.exit(1)
|
|
290
|
+
except exceptions.ConfigFileNotFoundError as e:
|
|
291
|
+
error_handler.handle_config_file_not_found_error(e)
|
|
292
|
+
sys.exit(1)
|
|
293
|
+
except exceptions.ConfigValidationError as e:
|
|
294
|
+
error_handler.handle_config_validation_error(e, config)
|
|
295
|
+
sys.exit(1)
|
|
265
296
|
except FileNotFoundError as e:
|
|
266
|
-
|
|
297
|
+
error_handler.handle_config_file_not_found_error(exceptions.ConfigFileNotFoundError(str(e)))
|
|
267
298
|
sys.exit(1)
|
|
268
299
|
except ValueError as e:
|
|
269
|
-
logger.error(f"
|
|
270
|
-
sys.exit(1)
|
|
271
|
-
except RuntimeError as e:
|
|
272
|
-
logger.error(f"{e}")
|
|
300
|
+
logger.error(f"Error: {e}")
|
|
273
301
|
sys.exit(1)
|
|
274
302
|
except Exception as e:
|
|
275
303
|
logger.error(f"Unexpected error: {e}")
|
|
@@ -381,15 +409,23 @@ def backup_full(config, group, name):
|
|
|
381
409
|
logger.error(f"{result['error_message']}")
|
|
382
410
|
sys.exit(1)
|
|
383
411
|
|
|
384
|
-
except
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
412
|
+
except exceptions.ConcurrencyConflictError as e:
|
|
413
|
+
error_handler.handle_concurrency_conflict_error(e, config)
|
|
414
|
+
sys.exit(1)
|
|
415
|
+
except exceptions.ConfigFileNotFoundError as e:
|
|
416
|
+
error_handler.handle_config_file_not_found_error(e)
|
|
417
|
+
sys.exit(1)
|
|
418
|
+
except exceptions.ConfigValidationError as e:
|
|
419
|
+
error_handler.handle_config_validation_error(e, config)
|
|
420
|
+
sys.exit(1)
|
|
421
|
+
except FileNotFoundError as e:
|
|
422
|
+
error_handler.handle_config_file_not_found_error(exceptions.ConfigFileNotFoundError(str(e)))
|
|
423
|
+
sys.exit(1)
|
|
424
|
+
except ValueError as e:
|
|
425
|
+
logger.error(f"Error: {e}")
|
|
426
|
+
sys.exit(1)
|
|
427
|
+
except Exception as e:
|
|
428
|
+
logger.error(f"Unexpected error: {e}")
|
|
393
429
|
sys.exit(1)
|
|
394
430
|
|
|
395
431
|
|
|
@@ -429,14 +465,13 @@ def restore_command(config, target_label, group, table, rename_suffix, yes):
|
|
|
429
465
|
if table:
|
|
430
466
|
table = table.strip()
|
|
431
467
|
if not table:
|
|
432
|
-
|
|
433
|
-
sys.exit(1)
|
|
468
|
+
raise exceptions.InvalidTableNameError("", "Table name cannot be empty")
|
|
434
469
|
|
|
435
470
|
if "." in table:
|
|
436
|
-
|
|
437
|
-
|
|
471
|
+
raise exceptions.InvalidTableNameError(
|
|
472
|
+
table,
|
|
473
|
+
"Table name must not include database prefix. Use 'table_name' not 'database.table_name'",
|
|
438
474
|
)
|
|
439
|
-
sys.exit(1)
|
|
440
475
|
|
|
441
476
|
cfg = config_module.load_config(config)
|
|
442
477
|
config_module.validate_config(cfg)
|
|
@@ -472,39 +507,21 @@ def restore_command(config, target_label, group, table, rename_suffix, yes):
|
|
|
472
507
|
|
|
473
508
|
logger.info(f"Finding restore sequence for target backup: {target_label}")
|
|
474
509
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
logger.success(f"Found restore sequence: {' -> '.join(restore_pair)}")
|
|
478
|
-
except ValueError as e:
|
|
479
|
-
logger.error(f"Failed to find restore sequence: {e}")
|
|
480
|
-
sys.exit(1)
|
|
510
|
+
restore_pair = restore.find_restore_pair(database, target_label)
|
|
511
|
+
logger.success(f"Found restore sequence: {' -> '.join(restore_pair)}")
|
|
481
512
|
|
|
482
513
|
logger.info("Determining tables to restore from backup manifest...")
|
|
483
514
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
)
|
|
492
|
-
except ValueError as e:
|
|
493
|
-
logger.error(str(e))
|
|
494
|
-
sys.exit(1)
|
|
515
|
+
tables_to_restore = restore.get_tables_from_backup(
|
|
516
|
+
database,
|
|
517
|
+
target_label,
|
|
518
|
+
group=group,
|
|
519
|
+
table=table,
|
|
520
|
+
database=cfg["database"] if table else None,
|
|
521
|
+
)
|
|
495
522
|
|
|
496
523
|
if not tables_to_restore:
|
|
497
|
-
|
|
498
|
-
logger.warning(
|
|
499
|
-
f"No tables found in backup '{target_label}' for group '{group}'"
|
|
500
|
-
)
|
|
501
|
-
elif table:
|
|
502
|
-
logger.warning(
|
|
503
|
-
f"No tables found in backup '{target_label}' for table '{table}'"
|
|
504
|
-
)
|
|
505
|
-
else:
|
|
506
|
-
logger.warning(f"No tables found in backup '{target_label}'")
|
|
507
|
-
sys.exit(1)
|
|
524
|
+
raise exceptions.NoTablesFoundError(group=group, label=target_label)
|
|
508
525
|
|
|
509
526
|
logger.success(
|
|
510
527
|
f"Found {len(tables_to_restore)} table(s) to restore: {', '.join(tables_to_restore)}"
|
|
@@ -527,14 +544,46 @@ def restore_command(config, target_label, group, table, rename_suffix, yes):
|
|
|
527
544
|
logger.error(f"Restore failed: {result['error_message']}")
|
|
528
545
|
sys.exit(1)
|
|
529
546
|
|
|
547
|
+
except exceptions.InvalidTableNameError as e:
|
|
548
|
+
error_handler.handle_invalid_table_name_error(e)
|
|
549
|
+
sys.exit(1)
|
|
550
|
+
except exceptions.BackupLabelNotFoundError as e:
|
|
551
|
+
error_handler.handle_backup_label_not_found_error(e, config)
|
|
552
|
+
sys.exit(1)
|
|
553
|
+
except exceptions.NoSuccessfulFullBackupFoundError as e:
|
|
554
|
+
error_handler.handle_no_successful_full_backup_found_error(e, config)
|
|
555
|
+
sys.exit(1)
|
|
556
|
+
except exceptions.TableNotFoundInBackupError as e:
|
|
557
|
+
error_handler.handle_table_not_found_in_backup_error(e, config)
|
|
558
|
+
sys.exit(1)
|
|
559
|
+
except exceptions.NoTablesFoundError as e:
|
|
560
|
+
error_handler.handle_no_tables_found_error(e, config, target_label)
|
|
561
|
+
sys.exit(1)
|
|
562
|
+
except exceptions.SnapshotNotFoundError as e:
|
|
563
|
+
error_handler.handle_snapshot_not_found_error(e, config)
|
|
564
|
+
sys.exit(1)
|
|
565
|
+
except exceptions.RestoreOperationCancelledError:
|
|
566
|
+
error_handler.handle_restore_operation_cancelled_error()
|
|
567
|
+
sys.exit(1)
|
|
568
|
+
except exceptions.ConfigFileNotFoundError as e:
|
|
569
|
+
error_handler.handle_config_file_not_found_error(e)
|
|
570
|
+
sys.exit(1)
|
|
571
|
+
except exceptions.ConfigValidationError as e:
|
|
572
|
+
error_handler.handle_config_validation_error(e, config)
|
|
573
|
+
sys.exit(1)
|
|
574
|
+
except exceptions.ClusterHealthCheckFailedError as e:
|
|
575
|
+
error_handler.handle_cluster_health_check_failed_error(e, config)
|
|
576
|
+
sys.exit(1)
|
|
530
577
|
except FileNotFoundError as e:
|
|
531
|
-
|
|
578
|
+
error_handler.handle_config_file_not_found_error(exceptions.ConfigFileNotFoundError(str(e)))
|
|
532
579
|
sys.exit(1)
|
|
533
580
|
except ValueError as e:
|
|
534
|
-
|
|
581
|
+
error_handler.handle_config_validation_error(
|
|
582
|
+
exceptions.ConfigValidationError(str(e)), config
|
|
583
|
+
)
|
|
535
584
|
sys.exit(1)
|
|
536
|
-
except
|
|
537
|
-
|
|
585
|
+
except exceptions.ConcurrencyConflictError as e:
|
|
586
|
+
error_handler.handle_concurrency_conflict_error(e, config)
|
|
538
587
|
sys.exit(1)
|
|
539
588
|
except Exception as e:
|
|
540
589
|
logger.error(f"Unexpected error: {e}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Literal
|
|
2
2
|
|
|
3
|
-
from . import logger, utils
|
|
3
|
+
from . import exceptions, logger, utils
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def reserve_job_slot(db, scope: str, label: str) -> None:
|
|
@@ -46,14 +46,7 @@ def _can_heal_stale_job(scope: str, label: str, db) -> bool:
|
|
|
46
46
|
|
|
47
47
|
def _raise_concurrency_conflict(scope: str, active_jobs: list[tuple[str, str, str]]) -> None:
|
|
48
48
|
"""Raise a concurrency conflict error with helpful message."""
|
|
49
|
-
|
|
50
|
-
active_labels = [job[1] for job in active_jobs]
|
|
51
|
-
|
|
52
|
-
raise RuntimeError(
|
|
53
|
-
f"Concurrency conflict: Another '{scope}' job is already ACTIVE: {', '.join(active_job_strings)}. "
|
|
54
|
-
f"Wait for it to complete or cancel it via: UPDATE ops.run_status SET state='CANCELLED' "
|
|
55
|
-
f"WHERE label='{active_labels[0]}' AND state='ACTIVE'"
|
|
56
|
-
)
|
|
49
|
+
raise exceptions.ConcurrencyConflictError(scope, active_jobs)
|
|
57
50
|
|
|
58
51
|
|
|
59
52
|
def _insert_new_job(db, scope: str, label: str) -> None:
|
|
@@ -2,6 +2,8 @@ from typing import Any
|
|
|
2
2
|
|
|
3
3
|
import yaml
|
|
4
4
|
|
|
5
|
+
from . import exceptions
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
def load_config(config_path: str) -> dict[str, Any]:
|
|
7
9
|
"""Load and parse YAML configuration file.
|
|
@@ -20,7 +22,7 @@ def load_config(config_path: str) -> dict[str, Any]:
|
|
|
20
22
|
config = yaml.safe_load(f)
|
|
21
23
|
|
|
22
24
|
if not isinstance(config, dict):
|
|
23
|
-
raise
|
|
25
|
+
raise exceptions.ConfigValidationError("Config must be a dictionary")
|
|
24
26
|
|
|
25
27
|
return config
|
|
26
28
|
|
|
@@ -32,13 +34,13 @@ def validate_config(config: dict[str, Any]) -> None:
|
|
|
32
34
|
config: Configuration dictionary
|
|
33
35
|
|
|
34
36
|
Raises:
|
|
35
|
-
|
|
37
|
+
ConfigValidationError: If required fields are missing
|
|
36
38
|
"""
|
|
37
39
|
required_fields = ["host", "port", "user", "database", "repository"]
|
|
38
40
|
|
|
39
41
|
for field in required_fields:
|
|
40
42
|
if field not in config:
|
|
41
|
-
raise
|
|
43
|
+
raise exceptions.ConfigValidationError(f"Missing required config field: {field}")
|
|
42
44
|
|
|
43
45
|
_validate_tls_section(config.get("tls"))
|
|
44
46
|
|
|
@@ -48,17 +50,19 @@ def _validate_tls_section(tls_config) -> None:
|
|
|
48
50
|
return
|
|
49
51
|
|
|
50
52
|
if not isinstance(tls_config, dict):
|
|
51
|
-
raise
|
|
53
|
+
raise exceptions.ConfigValidationError("TLS configuration must be a dictionary")
|
|
52
54
|
|
|
53
55
|
enabled = bool(tls_config.get("enabled", False))
|
|
54
56
|
|
|
55
57
|
if enabled and not tls_config.get("ca_cert"):
|
|
56
|
-
raise
|
|
58
|
+
raise exceptions.ConfigValidationError(
|
|
59
|
+
"TLS configuration requires 'ca_cert' when 'enabled' is true"
|
|
60
|
+
)
|
|
57
61
|
|
|
58
62
|
if "verify_server_cert" in tls_config and not isinstance(
|
|
59
63
|
tls_config["verify_server_cert"], bool
|
|
60
64
|
):
|
|
61
|
-
raise
|
|
65
|
+
raise exceptions.ConfigValidationError(
|
|
62
66
|
"TLS configuration field 'verify_server_cert' must be a boolean if provided"
|
|
63
67
|
)
|
|
64
68
|
|
|
@@ -67,6 +71,6 @@ def _validate_tls_section(tls_config) -> None:
|
|
|
67
71
|
if not isinstance(tls_versions, list) or not all(
|
|
68
72
|
isinstance(version, str) for version in tls_versions
|
|
69
73
|
):
|
|
70
|
-
raise
|
|
74
|
+
raise exceptions.ConfigValidationError(
|
|
71
75
|
"TLS configuration field 'tls_versions' must be a list of strings if provided"
|
|
72
76
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
3
|
import mysql.connector
|
|
4
4
|
|
|
@@ -13,7 +13,7 @@ class StarRocksDB:
|
|
|
13
13
|
user: str,
|
|
14
14
|
password: str,
|
|
15
15
|
database: str,
|
|
16
|
-
tls_config:
|
|
16
|
+
tls_config: dict[str, Any] | None = None,
|
|
17
17
|
):
|
|
18
18
|
"""Initialize database connection.
|
|
19
19
|
|
|
@@ -31,7 +31,7 @@ class StarRocksDB:
|
|
|
31
31
|
self.database = database
|
|
32
32
|
self._connection = None
|
|
33
33
|
self.tls_config = tls_config or {}
|
|
34
|
-
self._timezone:
|
|
34
|
+
self._timezone: str | None = None
|
|
35
35
|
|
|
36
36
|
def connect(self) -> None:
|
|
37
37
|
"""Establish database connection."""
|