gravi-cli 0.2.2__tar.gz → 0.2.4__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.
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/.bumpversion.toml +1 -1
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/PKG-INFO +1 -1
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/gravi_cli/__init__.py +1 -1
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/gravi_cli/cli.py +394 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/gravi_cli/client.py +203 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/pyproject.toml +1 -1
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/.gitignore +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/LICENSE +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/README.md +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/gravi_cli/api.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/gravi_cli/auth.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/gravi_cli/config.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/gravi_cli/exceptions.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/gravi_cli/tunnel.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/release.sh +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/tests/__init__.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/tests/conftest.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/tests/test_api.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/tests/test_auth.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/tests/test_cli.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/tests/test_client.py +0 -0
- {gravi_cli-0.2.2 → gravi_cli-0.2.4}/tests/test_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gravi-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: CLI tool for Gravitate infrastructure management
|
|
5
5
|
Project-URL: Homepage, https://github.com/gravitate/mom
|
|
6
6
|
Project-URL: Documentation, https://github.com/gravitate/mom/tree/main/cli
|
|
@@ -32,6 +32,7 @@ from .exceptions import (
|
|
|
32
32
|
APIError,
|
|
33
33
|
RateLimitError,
|
|
34
34
|
ConfigError,
|
|
35
|
+
PermissionDeniedError,
|
|
35
36
|
)
|
|
36
37
|
|
|
37
38
|
|
|
@@ -393,6 +394,39 @@ def config(instance_key, format):
|
|
|
393
394
|
sys.exit(1)
|
|
394
395
|
|
|
395
396
|
|
|
397
|
+
@main.command()
|
|
398
|
+
@click.argument("instance_key")
|
|
399
|
+
@click.option(
|
|
400
|
+
"--json",
|
|
401
|
+
"output_json",
|
|
402
|
+
is_flag=True,
|
|
403
|
+
help="Output as JSON for scripting",
|
|
404
|
+
)
|
|
405
|
+
def token(instance_key, output_json):
|
|
406
|
+
"""Get access and refresh tokens for an instance."""
|
|
407
|
+
try:
|
|
408
|
+
token_data = get_instance_token(instance_key)
|
|
409
|
+
|
|
410
|
+
if output_json:
|
|
411
|
+
click.echo(json.dumps({
|
|
412
|
+
"access_token": token_data.get("access_token", ""),
|
|
413
|
+
"refresh_token": token_data.get("refresh_token", ""),
|
|
414
|
+
}))
|
|
415
|
+
else:
|
|
416
|
+
click.echo(f"AccessToken: {token_data.get('access_token', 'N/A')}")
|
|
417
|
+
click.echo(f"RefreshToken: {token_data.get('refresh_token', 'N/A')}")
|
|
418
|
+
|
|
419
|
+
except NotAuthenticatedError as e:
|
|
420
|
+
click.echo(f"Error: {e}", err=True)
|
|
421
|
+
sys.exit(1)
|
|
422
|
+
except PermissionDeniedError as e:
|
|
423
|
+
click.echo(f"Error: {e}", err=True)
|
|
424
|
+
sys.exit(1)
|
|
425
|
+
except Exception as e:
|
|
426
|
+
click.echo(f"Error: {e}", err=True)
|
|
427
|
+
sys.exit(1)
|
|
428
|
+
|
|
429
|
+
|
|
396
430
|
# Add missing import for timedelta
|
|
397
431
|
from datetime import timedelta
|
|
398
432
|
|
|
@@ -448,5 +482,365 @@ def dbtun(burner_id):
|
|
|
448
482
|
sys.exit(1)
|
|
449
483
|
|
|
450
484
|
|
|
485
|
+
# ============================================================================
|
|
486
|
+
# Burner Commands
|
|
487
|
+
# ============================================================================
|
|
488
|
+
|
|
489
|
+
@main.group()
|
|
490
|
+
def burner():
|
|
491
|
+
"""Manage ephemeral burner instances."""
|
|
492
|
+
pass
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def poll_operation_until_complete(
|
|
496
|
+
client: MomClient,
|
|
497
|
+
token: str,
|
|
498
|
+
operation_id: str,
|
|
499
|
+
show_progress: bool = True,
|
|
500
|
+
poll_interval: float = 2.0,
|
|
501
|
+
timeout: float = 600.0,
|
|
502
|
+
) -> dict:
|
|
503
|
+
"""
|
|
504
|
+
Poll an operation until it completes or fails.
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
client: MomClient instance
|
|
508
|
+
token: Access token
|
|
509
|
+
operation_id: Operation ID to poll
|
|
510
|
+
show_progress: Whether to print progress
|
|
511
|
+
poll_interval: Seconds between polls
|
|
512
|
+
timeout: Maximum time to wait
|
|
513
|
+
|
|
514
|
+
Returns:
|
|
515
|
+
Final operation status dict
|
|
516
|
+
"""
|
|
517
|
+
start_time = time.time()
|
|
518
|
+
last_step = None
|
|
519
|
+
|
|
520
|
+
while True:
|
|
521
|
+
if time.time() - start_time > timeout:
|
|
522
|
+
return {"status": "failed", "error_message": "Timeout waiting for operation"}
|
|
523
|
+
|
|
524
|
+
status = client.get_operation_status(operation_id, token)
|
|
525
|
+
|
|
526
|
+
if show_progress:
|
|
527
|
+
current_step = status.get("current_step")
|
|
528
|
+
progress = status.get("progress_percent", 0)
|
|
529
|
+
if current_step and current_step != last_step:
|
|
530
|
+
click.echo(f"\n {current_step}...", nl=False)
|
|
531
|
+
last_step = current_step
|
|
532
|
+
else:
|
|
533
|
+
click.echo(".", nl=False)
|
|
534
|
+
|
|
535
|
+
if status["status"] in ("completed", "failed"):
|
|
536
|
+
if show_progress:
|
|
537
|
+
click.echo()
|
|
538
|
+
return status
|
|
539
|
+
|
|
540
|
+
time.sleep(poll_interval)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
@burner.command("start")
|
|
544
|
+
@click.option("--ttl", "ttl_hours", default=2, type=int, help="Time-to-live in hours (1-168)")
|
|
545
|
+
@click.option("--sheet-id", default=None, help="Google Sheet ID for full seeding")
|
|
546
|
+
@click.option("--source-env", default=None, help="Source environment to copy from")
|
|
547
|
+
@click.option("--simple-demand", is_flag=True, help="Use even demand distribution")
|
|
548
|
+
@click.option("--sync", is_flag=True, help="Wait for burner to be ready")
|
|
549
|
+
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
|
|
550
|
+
def burner_start(ttl_hours, sheet_id, source_env, simple_demand, sync, output_json):
|
|
551
|
+
"""Create a new burner instance.
|
|
552
|
+
|
|
553
|
+
Examples:
|
|
554
|
+
|
|
555
|
+
gravi burner start # Quick barebone burner
|
|
556
|
+
|
|
557
|
+
gravi burner start --ttl 8 # 8 hour TTL
|
|
558
|
+
|
|
559
|
+
gravi burner start --sync # Wait until ready
|
|
560
|
+
|
|
561
|
+
gravi burner start --sheet-id ABC123 # Full seed from sheet
|
|
562
|
+
"""
|
|
563
|
+
try:
|
|
564
|
+
mom_token = get_mom_token()
|
|
565
|
+
mom_url = get_mom_url()
|
|
566
|
+
client = MomClient(mom_url)
|
|
567
|
+
|
|
568
|
+
# Determine seed level based on options
|
|
569
|
+
seed_level = "barebone"
|
|
570
|
+
if sheet_id:
|
|
571
|
+
seed_level = "full"
|
|
572
|
+
elif source_env:
|
|
573
|
+
seed_level = "copy"
|
|
574
|
+
|
|
575
|
+
# Initiate creation
|
|
576
|
+
response = client.create_burner(
|
|
577
|
+
access_token=mom_token,
|
|
578
|
+
ttl_hours=ttl_hours,
|
|
579
|
+
sheet_id=sheet_id,
|
|
580
|
+
source_env=source_env,
|
|
581
|
+
seed_level=seed_level,
|
|
582
|
+
simple_demand=simple_demand,
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
burner_id = response["burner_id"]
|
|
586
|
+
operation_id = response["operation_id"]
|
|
587
|
+
|
|
588
|
+
if not sync:
|
|
589
|
+
# Async mode: just output the operation info and exit
|
|
590
|
+
if output_json:
|
|
591
|
+
click.echo(json.dumps(response))
|
|
592
|
+
else:
|
|
593
|
+
click.echo(f"Burner '{burner_id}' creation started")
|
|
594
|
+
click.echo(f" Operation ID: {operation_id}")
|
|
595
|
+
click.echo(f"\nUse 'gravi burner status {burner_id}' to check status")
|
|
596
|
+
return
|
|
597
|
+
|
|
598
|
+
# Sync mode: poll until complete
|
|
599
|
+
if not output_json:
|
|
600
|
+
click.echo(f"Creating burner '{burner_id}'", nl=False)
|
|
601
|
+
|
|
602
|
+
final_result = poll_operation_until_complete(
|
|
603
|
+
client, mom_token, operation_id,
|
|
604
|
+
show_progress=(not output_json)
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
if final_result["status"] == "completed":
|
|
608
|
+
# Fetch the burner details
|
|
609
|
+
burner_info = client.get_burner(burner_id, mom_token)
|
|
610
|
+
|
|
611
|
+
if output_json:
|
|
612
|
+
click.echo(json.dumps(burner_info))
|
|
613
|
+
else:
|
|
614
|
+
click.echo(f"\nBurner ready:")
|
|
615
|
+
click.echo(f" ID: {burner_info['burner_id']}")
|
|
616
|
+
click.echo(f" URL: https://{burner_info['url']}")
|
|
617
|
+
click.echo(f" Expires: {burner_info['expires_at']}")
|
|
618
|
+
else:
|
|
619
|
+
error_msg = final_result.get("error_message", "Unknown error")
|
|
620
|
+
if output_json:
|
|
621
|
+
click.echo(json.dumps({"error": error_msg, "burner_id": burner_id}))
|
|
622
|
+
else:
|
|
623
|
+
click.echo(f"\nError: {error_msg}", err=True)
|
|
624
|
+
sys.exit(1)
|
|
625
|
+
|
|
626
|
+
except NotAuthenticatedError:
|
|
627
|
+
click.echo("Error: Please run 'gravi login' first", err=True)
|
|
628
|
+
sys.exit(1)
|
|
629
|
+
except RateLimitError as e:
|
|
630
|
+
click.echo(f"Error: {e}", err=True)
|
|
631
|
+
sys.exit(1)
|
|
632
|
+
except APIError as e:
|
|
633
|
+
click.echo(f"Error: {e}", err=True)
|
|
634
|
+
sys.exit(1)
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
@burner.command("delete")
|
|
638
|
+
@click.argument("burner_id")
|
|
639
|
+
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
|
|
640
|
+
def burner_delete(burner_id, output_json):
|
|
641
|
+
"""Delete a burner instance.
|
|
642
|
+
|
|
643
|
+
Examples:
|
|
644
|
+
|
|
645
|
+
gravi burner delete tank
|
|
646
|
+
|
|
647
|
+
gravi burner delete pump --json
|
|
648
|
+
"""
|
|
649
|
+
try:
|
|
650
|
+
mom_token = get_mom_token()
|
|
651
|
+
mom_url = get_mom_url()
|
|
652
|
+
client = MomClient(mom_url)
|
|
653
|
+
|
|
654
|
+
response = client.delete_burner(burner_id, mom_token)
|
|
655
|
+
|
|
656
|
+
if output_json:
|
|
657
|
+
click.echo(json.dumps(response))
|
|
658
|
+
else:
|
|
659
|
+
click.echo(f"Burner '{burner_id}' deletion initiated")
|
|
660
|
+
click.echo(f" Operation ID: {response['operation_id']}")
|
|
661
|
+
|
|
662
|
+
except NotAuthenticatedError:
|
|
663
|
+
click.echo("Error: Please run 'gravi login' first", err=True)
|
|
664
|
+
sys.exit(1)
|
|
665
|
+
except APIError as e:
|
|
666
|
+
click.echo(f"Error: {e}", err=True)
|
|
667
|
+
sys.exit(1)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
@burner.command("status")
|
|
671
|
+
@click.argument("burner_id")
|
|
672
|
+
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
|
|
673
|
+
def burner_status(burner_id, output_json):
|
|
674
|
+
"""Get detailed status of a burner.
|
|
675
|
+
|
|
676
|
+
Examples:
|
|
677
|
+
|
|
678
|
+
gravi burner status tank
|
|
679
|
+
|
|
680
|
+
gravi burner status pump --json
|
|
681
|
+
"""
|
|
682
|
+
try:
|
|
683
|
+
mom_token = get_mom_token()
|
|
684
|
+
mom_url = get_mom_url()
|
|
685
|
+
client = MomClient(mom_url)
|
|
686
|
+
|
|
687
|
+
burner_info = client.get_burner(burner_id, mom_token)
|
|
688
|
+
|
|
689
|
+
if output_json:
|
|
690
|
+
click.echo(json.dumps(burner_info))
|
|
691
|
+
else:
|
|
692
|
+
click.echo(f"Burner: {burner_info['burner_id']}")
|
|
693
|
+
click.echo("=" * 40)
|
|
694
|
+
click.echo(f"Status: {burner_info['status']}")
|
|
695
|
+
click.echo(f"URL: https://{burner_info['url']}")
|
|
696
|
+
click.echo(f"Created by: {burner_info['created_by']}")
|
|
697
|
+
click.echo(f"Created: {burner_info['created_at']}")
|
|
698
|
+
click.echo(f"Expires: {burner_info['expires_at']}")
|
|
699
|
+
click.echo(f"TTL: {burner_info['ttl_hours']} hours")
|
|
700
|
+
click.echo(f"Build: {burner_info['build']}")
|
|
701
|
+
click.echo(f"Seed level: {burner_info['seed_level']}")
|
|
702
|
+
if burner_info.get('custom_name'):
|
|
703
|
+
click.echo(f"Name: {burner_info['custom_name']}")
|
|
704
|
+
if burner_info.get('sheet_id'):
|
|
705
|
+
click.echo(f"Sheet ID: {burner_info['sheet_id']}")
|
|
706
|
+
|
|
707
|
+
except NotAuthenticatedError:
|
|
708
|
+
click.echo("Error: Please run 'gravi login' first", err=True)
|
|
709
|
+
sys.exit(1)
|
|
710
|
+
except APIError as e:
|
|
711
|
+
click.echo(f"Error: {e}", err=True)
|
|
712
|
+
sys.exit(1)
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
@burner.command("pods")
|
|
716
|
+
@click.argument("burner_id")
|
|
717
|
+
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
|
|
718
|
+
def burner_pods(burner_id, output_json):
|
|
719
|
+
"""List pods in a burner instance.
|
|
720
|
+
|
|
721
|
+
Shows all pods grouped by deployment with their status.
|
|
722
|
+
|
|
723
|
+
Examples:
|
|
724
|
+
|
|
725
|
+
gravi burner pods tank
|
|
726
|
+
|
|
727
|
+
gravi burner pods pump --json
|
|
728
|
+
"""
|
|
729
|
+
try:
|
|
730
|
+
mom_token = get_mom_token()
|
|
731
|
+
mom_url = get_mom_url()
|
|
732
|
+
client = MomClient(mom_url)
|
|
733
|
+
|
|
734
|
+
data = client.list_burner_pods(burner_id, mom_token)
|
|
735
|
+
|
|
736
|
+
if output_json:
|
|
737
|
+
click.echo(json.dumps(data))
|
|
738
|
+
else:
|
|
739
|
+
click.echo(f"Pods in burner '{burner_id}' ({data['namespace']})")
|
|
740
|
+
click.echo("=" * 60)
|
|
741
|
+
|
|
742
|
+
for deployment in data.get("deployments", []):
|
|
743
|
+
ready = deployment["ready_replicas"]
|
|
744
|
+
desired = deployment["desired_replicas"]
|
|
745
|
+
status_icon = "✓" if ready == desired else "○"
|
|
746
|
+
click.echo(f"\n{status_icon} {deployment['name']} ({ready}/{desired} ready)")
|
|
747
|
+
|
|
748
|
+
for pod in deployment.get("pods", []):
|
|
749
|
+
pod_status = pod.get("status", "Unknown")
|
|
750
|
+
restarts = pod.get("restart_count", 0)
|
|
751
|
+
restart_str = f" (restarts: {restarts})" if restarts > 0 else ""
|
|
752
|
+
status_color = "green" if pod_status == "Running" else "yellow"
|
|
753
|
+
click.echo(f" {pod['name']}: {pod_status}{restart_str}")
|
|
754
|
+
|
|
755
|
+
except NotAuthenticatedError:
|
|
756
|
+
click.echo("Error: Please run 'gravi login' first", err=True)
|
|
757
|
+
sys.exit(1)
|
|
758
|
+
except APIError as e:
|
|
759
|
+
click.echo(f"Error: {e}", err=True)
|
|
760
|
+
sys.exit(1)
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
@burner.command("logs")
|
|
764
|
+
@click.argument("burner_id")
|
|
765
|
+
@click.argument("pod_name")
|
|
766
|
+
@click.option("--tail", "-n", "tail_lines", default=100, type=int, help="Number of lines (default: 100)")
|
|
767
|
+
@click.option("--container", "-c", default=None, help="Container name (optional)")
|
|
768
|
+
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
|
|
769
|
+
def burner_logs(burner_id, pod_name, tail_lines, container, output_json):
|
|
770
|
+
"""Fetch logs from a pod in a burner.
|
|
771
|
+
|
|
772
|
+
Examples:
|
|
773
|
+
|
|
774
|
+
gravi burner logs tank backend-xyz
|
|
775
|
+
|
|
776
|
+
gravi burner logs tank backend-xyz --tail 500
|
|
777
|
+
|
|
778
|
+
gravi burner logs tank backend-xyz -c backend
|
|
779
|
+
"""
|
|
780
|
+
try:
|
|
781
|
+
mom_token = get_mom_token()
|
|
782
|
+
mom_url = get_mom_url()
|
|
783
|
+
client = MomClient(mom_url)
|
|
784
|
+
|
|
785
|
+
data = client.get_pod_logs(
|
|
786
|
+
burner_id=burner_id,
|
|
787
|
+
pod_name=pod_name,
|
|
788
|
+
access_token=mom_token,
|
|
789
|
+
container=container,
|
|
790
|
+
tail_lines=tail_lines,
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
if output_json:
|
|
794
|
+
click.echo(json.dumps(data))
|
|
795
|
+
else:
|
|
796
|
+
if data.get("truncated"):
|
|
797
|
+
click.echo(f"[Showing last {tail_lines} lines, logs truncated]", err=True)
|
|
798
|
+
# API returns 'lines' as array or 'logs' as string
|
|
799
|
+
logs = data.get("logs") or "\n".join(data.get("lines", []))
|
|
800
|
+
click.echo(logs)
|
|
801
|
+
|
|
802
|
+
except NotAuthenticatedError:
|
|
803
|
+
click.echo("Error: Please run 'gravi login' first", err=True)
|
|
804
|
+
sys.exit(1)
|
|
805
|
+
except APIError as e:
|
|
806
|
+
click.echo(f"Error: {e}", err=True)
|
|
807
|
+
sys.exit(1)
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
@burner.command("restart")
|
|
811
|
+
@click.argument("burner_id")
|
|
812
|
+
@click.argument("deployment_name")
|
|
813
|
+
@click.option("--json", "output_json", is_flag=True, help="Output as JSON")
|
|
814
|
+
def burner_restart(burner_id, deployment_name, output_json):
|
|
815
|
+
"""Restart a deployment in a burner.
|
|
816
|
+
|
|
817
|
+
Triggers a rolling restart of all pods in the deployment.
|
|
818
|
+
|
|
819
|
+
Examples:
|
|
820
|
+
|
|
821
|
+
gravi burner restart tank backend
|
|
822
|
+
|
|
823
|
+
gravi burner restart pump forecast
|
|
824
|
+
"""
|
|
825
|
+
try:
|
|
826
|
+
mom_token = get_mom_token()
|
|
827
|
+
mom_url = get_mom_url()
|
|
828
|
+
client = MomClient(mom_url)
|
|
829
|
+
|
|
830
|
+
response = client.restart_deployment(burner_id, deployment_name, mom_token)
|
|
831
|
+
|
|
832
|
+
if output_json:
|
|
833
|
+
click.echo(json.dumps(response))
|
|
834
|
+
else:
|
|
835
|
+
click.echo(f"Restart initiated for '{deployment_name}' in burner '{burner_id}'")
|
|
836
|
+
|
|
837
|
+
except NotAuthenticatedError:
|
|
838
|
+
click.echo("Error: Please run 'gravi login' first", err=True)
|
|
839
|
+
sys.exit(1)
|
|
840
|
+
except APIError as e:
|
|
841
|
+
click.echo(f"Error: {e}", err=True)
|
|
842
|
+
sys.exit(1)
|
|
843
|
+
|
|
844
|
+
|
|
451
845
|
if __name__ == "__main__":
|
|
452
846
|
main()
|
|
@@ -359,3 +359,206 @@ class MomClient:
|
|
|
359
359
|
json_data=json_data,
|
|
360
360
|
auth_token=access_token
|
|
361
361
|
)
|
|
362
|
+
|
|
363
|
+
def create_burner(
|
|
364
|
+
self,
|
|
365
|
+
access_token: str,
|
|
366
|
+
ttl_hours: int = 2,
|
|
367
|
+
sheet_id: str | None = None,
|
|
368
|
+
source_env: str | None = None,
|
|
369
|
+
seed_level: str = "barebone",
|
|
370
|
+
custom_name: str | None = None,
|
|
371
|
+
build: str = "dev",
|
|
372
|
+
simple_demand: bool = False,
|
|
373
|
+
) -> dict:
|
|
374
|
+
"""
|
|
375
|
+
Create a new burner instance.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
access_token: Mom access token
|
|
379
|
+
ttl_hours: Time-to-live in hours (1-168)
|
|
380
|
+
sheet_id: Google Sheet ID for full seeding
|
|
381
|
+
source_env: Source environment to copy from
|
|
382
|
+
seed_level: Seeding level (none, barebone, full, copy)
|
|
383
|
+
custom_name: Custom name for the burner
|
|
384
|
+
build: Build to deploy (dev, test, rc, or SHA)
|
|
385
|
+
simple_demand: Use even demand distribution
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
{
|
|
389
|
+
"operation_id": "...",
|
|
390
|
+
"burner_id": "tank",
|
|
391
|
+
"stream_url": "/burners/operations/stream?operation_id=...",
|
|
392
|
+
"message": "Burner creation initiated"
|
|
393
|
+
}
|
|
394
|
+
"""
|
|
395
|
+
json_data = {
|
|
396
|
+
"ttl_hours": ttl_hours,
|
|
397
|
+
"seed_level": seed_level,
|
|
398
|
+
"build": build,
|
|
399
|
+
"simple_demand": simple_demand,
|
|
400
|
+
}
|
|
401
|
+
if sheet_id:
|
|
402
|
+
json_data["sheet_id"] = sheet_id
|
|
403
|
+
if source_env:
|
|
404
|
+
json_data["source_env"] = source_env
|
|
405
|
+
if custom_name:
|
|
406
|
+
json_data["custom_name"] = custom_name
|
|
407
|
+
|
|
408
|
+
return self._make_request(
|
|
409
|
+
"POST",
|
|
410
|
+
"burners/create",
|
|
411
|
+
json_data=json_data,
|
|
412
|
+
auth_token=access_token,
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
def delete_burner(self, burner_id: str, access_token: str) -> dict:
|
|
416
|
+
"""
|
|
417
|
+
Delete a burner instance.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
burner_id: Burner ID to delete (e.g., "tank")
|
|
421
|
+
access_token: Mom access token
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
{
|
|
425
|
+
"operation_id": "...",
|
|
426
|
+
"burner_id": "tank",
|
|
427
|
+
"stream_url": "/burners/operations/stream?operation_id=...",
|
|
428
|
+
"message": "Burner deletion initiated"
|
|
429
|
+
}
|
|
430
|
+
"""
|
|
431
|
+
return self._make_request(
|
|
432
|
+
"POST",
|
|
433
|
+
"burners/delete",
|
|
434
|
+
json_data={"burner_id": burner_id},
|
|
435
|
+
auth_token=access_token,
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
def get_burner(self, burner_id: str, access_token: str) -> dict:
|
|
439
|
+
"""
|
|
440
|
+
Get details of a specific burner.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
burner_id: Burner ID (e.g., "tank")
|
|
444
|
+
access_token: Mom access token
|
|
445
|
+
|
|
446
|
+
Returns:
|
|
447
|
+
Burner details including status, url, created_by, expires_at, etc.
|
|
448
|
+
"""
|
|
449
|
+
return self._make_request(
|
|
450
|
+
"POST",
|
|
451
|
+
"burners/get",
|
|
452
|
+
json_data={"burner_id": burner_id},
|
|
453
|
+
auth_token=access_token,
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
def get_operation_status(self, operation_id: str, access_token: str) -> dict:
|
|
457
|
+
"""
|
|
458
|
+
Get status of a burner operation.
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
operation_id: Operation ID
|
|
462
|
+
access_token: Mom access token
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
{
|
|
466
|
+
"operation_id": "...",
|
|
467
|
+
"status": "pending|running|completed|failed",
|
|
468
|
+
"progress_percent": 0-100,
|
|
469
|
+
"current_step": "...",
|
|
470
|
+
"initiated_at": "...",
|
|
471
|
+
"completed_at": "...",
|
|
472
|
+
"error_message": "..."
|
|
473
|
+
}
|
|
474
|
+
"""
|
|
475
|
+
return self._make_request(
|
|
476
|
+
"POST",
|
|
477
|
+
"burners/operations/get",
|
|
478
|
+
json_data={"operation_id": operation_id},
|
|
479
|
+
auth_token=access_token,
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
def list_burner_pods(self, burner_id: str, access_token: str) -> dict:
|
|
483
|
+
"""
|
|
484
|
+
List all pods in a burner namespace, grouped by deployment.
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
burner_id: Burner ID (e.g., "tank")
|
|
488
|
+
access_token: Mom access token
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
{
|
|
492
|
+
"pods": [...],
|
|
493
|
+
"deployments": [
|
|
494
|
+
{"name": "backend", "desired_replicas": 1, "ready_replicas": 1, "pods": [...]},
|
|
495
|
+
...
|
|
496
|
+
],
|
|
497
|
+
"namespace": "burner-tank"
|
|
498
|
+
}
|
|
499
|
+
"""
|
|
500
|
+
return self._make_request(
|
|
501
|
+
"GET",
|
|
502
|
+
f"burners/{burner_id}/pods",
|
|
503
|
+
auth_token=access_token,
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
def get_pod_logs(
|
|
507
|
+
self,
|
|
508
|
+
burner_id: str,
|
|
509
|
+
pod_name: str,
|
|
510
|
+
access_token: str,
|
|
511
|
+
container: str | None = None,
|
|
512
|
+
tail_lines: int = 100,
|
|
513
|
+
since_seconds: int | None = None,
|
|
514
|
+
) -> dict:
|
|
515
|
+
"""
|
|
516
|
+
Fetch logs from a pod.
|
|
517
|
+
|
|
518
|
+
Args:
|
|
519
|
+
burner_id: Burner ID (e.g., "tank")
|
|
520
|
+
pod_name: Name of the pod
|
|
521
|
+
access_token: Mom access token
|
|
522
|
+
container: Container name (optional)
|
|
523
|
+
tail_lines: Number of lines from end (default 100)
|
|
524
|
+
since_seconds: Only fetch logs newer than this many seconds
|
|
525
|
+
|
|
526
|
+
Returns:
|
|
527
|
+
{
|
|
528
|
+
"pod_name": "...",
|
|
529
|
+
"container": "...",
|
|
530
|
+
"logs": "...",
|
|
531
|
+
"truncated": false
|
|
532
|
+
}
|
|
533
|
+
"""
|
|
534
|
+
# Build query params
|
|
535
|
+
params = [f"tail_lines={tail_lines}"]
|
|
536
|
+
if container:
|
|
537
|
+
params.append(f"container={container}")
|
|
538
|
+
if since_seconds:
|
|
539
|
+
params.append(f"since_seconds={since_seconds}")
|
|
540
|
+
|
|
541
|
+
query_string = "&".join(params)
|
|
542
|
+
return self._make_request(
|
|
543
|
+
"GET",
|
|
544
|
+
f"burners/{burner_id}/pods/{pod_name}/logs?{query_string}",
|
|
545
|
+
auth_token=access_token,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
def restart_deployment(self, burner_id: str, deployment_name: str, access_token: str) -> dict:
|
|
549
|
+
"""
|
|
550
|
+
Restart all pods in a deployment.
|
|
551
|
+
|
|
552
|
+
Args:
|
|
553
|
+
burner_id: Burner ID (e.g., "tank")
|
|
554
|
+
deployment_name: Name of the deployment to restart
|
|
555
|
+
access_token: Mom access token
|
|
556
|
+
|
|
557
|
+
Returns:
|
|
558
|
+
{"message": "Deployment backend restart initiated"}
|
|
559
|
+
"""
|
|
560
|
+
return self._make_request(
|
|
561
|
+
"POST",
|
|
562
|
+
f"burners/{burner_id}/deployments/{deployment_name}/restart",
|
|
563
|
+
auth_token=access_token,
|
|
564
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|