Skip to content

Commit 33448ac

Browse files
committed
bootstrap py
1 parent 2e922f3 commit 33448ac

File tree

4 files changed

+317
-36
lines changed

4 files changed

+317
-36
lines changed

queries/config/spine.gql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ query spine_config($device: String!) {
145145
interface_services{
146146
edges{
147147
node{
148+
__typename
148149
... on ServiceOSPF{
149150
area { node {area {value}}}
150151
ospf_interface_profile{node{

scripts/bootstrap.py

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Bootstrap Infrahub with schemas, data, and configurations.
4+
5+
This is a Python equivalent of scripts/bootstrap.sh with enhanced Rich UI formatting.
6+
7+
This script loads all necessary data into Infrahub including:
8+
- Schemas
9+
- Menu definitions
10+
- Bootstrap data (locations, platforms, roles)
11+
- Security data
12+
- Demo repository
13+
- Event actions
14+
15+
Advantages over bash version:
16+
- Beautiful progress bars with time elapsed
17+
- Better error handling and reporting
18+
- More structured code with type hints
19+
- Rich-formatted panels and status messages
20+
- Visual progress indicators for long-running operations
21+
22+
Usage:
23+
python scripts/bootstrap.py # Use main branch
24+
python scripts/bootstrap.py --branch dev # Use specific branch
25+
uv run invoke bootstrap-py # Via invoke (Python version)
26+
uv run invoke bootstrap # Via invoke (Bash version)
27+
28+
Performance comparison:
29+
To compare performance between bash and Python versions:
30+
time ./scripts/bootstrap.sh
31+
time uv run python scripts/bootstrap.py
32+
"""
33+
34+
import argparse
35+
import subprocess
36+
import sys
37+
import time
38+
39+
import requests
40+
from rich.console import Console
41+
from rich.panel import Panel
42+
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
43+
from rich import box
44+
45+
console = Console()
46+
47+
INFRAHUB_ADDRESS = "http://localhost:8000"
48+
49+
50+
def check_infrahub_ready(max_retries: int = 30, sleep_time: int = 2) -> bool:
51+
"""Check if Infrahub is ready to accept requests."""
52+
console.print("\n[cyan]→[/cyan] Checking if Infrahub is ready...")
53+
54+
with Progress(
55+
SpinnerColumn(),
56+
TextColumn("[progress.description]{task.description}"),
57+
BarColumn(),
58+
TimeElapsedColumn(),
59+
console=console,
60+
) as progress:
61+
task = progress.add_task("[cyan]Waiting for Infrahub...", total=max_retries)
62+
63+
for attempt in range(max_retries):
64+
try:
65+
response = requests.get(f"{INFRAHUB_ADDRESS}/api/schema", timeout=2)
66+
if response.status_code == 200:
67+
progress.update(task, completed=max_retries)
68+
console.print("[green]✓[/green] Infrahub is ready!\n")
69+
return True
70+
except requests.exceptions.RequestException:
71+
pass
72+
73+
progress.update(task, advance=1)
74+
time.sleep(sleep_time)
75+
76+
console.print()
77+
console.print(Panel(
78+
"[red]✗ ERROR: Infrahub is not responding[/red]\n\n"
79+
"[dim]Please ensure Infrahub is running with:[/dim]\n"
80+
" [bold]uv run invoke start[/bold]\n\n"
81+
"[dim]Check container status with:[/dim]\n"
82+
" [bold]docker ps[/bold]",
83+
title="Connection Error",
84+
border_style="red",
85+
box=box.ROUNDED
86+
))
87+
return False
88+
89+
90+
def run_command(command: str, description: str, step: str) -> bool:
91+
"""Run a shell command and display output."""
92+
console.print(f"\n[cyan]{step}[/cyan] {description}")
93+
94+
try:
95+
subprocess.run(
96+
command,
97+
shell=True,
98+
check=True,
99+
capture_output=False,
100+
text=True
101+
)
102+
console.print(f"[green]✓[/green] {description} completed")
103+
return True
104+
except subprocess.CalledProcessError as e:
105+
console.print(f"[red]✗[/red] Failed: {description}")
106+
console.print(f"[dim]Error: {e}[/dim]")
107+
return False
108+
109+
110+
def wait_for_repository_sync(seconds: int = 120) -> None:
111+
"""Wait for repository synchronization with progress bar."""
112+
console.print(f"\n[cyan]→[/cyan] Waiting for repository sync ({seconds} seconds)...")
113+
114+
with Progress(
115+
SpinnerColumn(),
116+
TextColumn("[progress.description]{task.description}"),
117+
BarColumn(),
118+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
119+
TimeElapsedColumn(),
120+
console=console,
121+
) as progress:
122+
task = progress.add_task("[cyan]Syncing repository...", total=seconds)
123+
124+
for _ in range(seconds):
125+
time.sleep(1)
126+
progress.update(task, advance=1)
127+
128+
console.print("[green]✓[/green] Repository sync complete\n")
129+
130+
131+
def main(branch: str = "main") -> int:
132+
"""Main bootstrap function."""
133+
console.print()
134+
console.print(Panel(
135+
f"[bold blue]Infrahub Demo Bootstrap[/bold blue]\n"
136+
f"[dim]Branch:[/dim] [bold]{branch}[/bold]\n\n"
137+
"[dim]This will load schemas, menu, bootstrap data, security, and repository[/dim]",
138+
border_style="blue",
139+
box=box.ROUNDED,
140+
title="Bootstrap Process"
141+
))
142+
143+
# Check if Infrahub is ready
144+
if not check_infrahub_ready():
145+
return 1
146+
147+
steps = [
148+
{
149+
"step": "[1/7]",
150+
"description": "Loading schemas",
151+
"command": f"uv run infrahubctl schema load schemas --branch {branch}"
152+
},
153+
{
154+
"step": "[2/7]",
155+
"description": "Loading menu definitions",
156+
"command": f"uv run infrahubctl menu load menu --branch {branch}"
157+
},
158+
{
159+
"step": "[3/7]",
160+
"description": "Loading bootstrap data (locations, platforms, roles, etc.)",
161+
"command": f"uv run infrahubctl object load objects/bootstrap/ --branch {branch}"
162+
},
163+
{
164+
"step": "[4/7]",
165+
"description": "Loading security data (zones, policies, rules)",
166+
"command": f"uv run infrahubctl object load objects/security/ --branch {branch}"
167+
},
168+
{
169+
"step": "[5/7]",
170+
"description": "Populating security relationships",
171+
"command": "uv run python scripts/populate_security_relationships.py"
172+
},
173+
]
174+
175+
# Execute all steps
176+
for step_info in steps:
177+
if not run_command(
178+
step_info["command"],
179+
step_info["description"],
180+
step_info["step"]
181+
):
182+
console.print("\n[red]Bootstrap failed![/red]")
183+
return 1
184+
185+
# Add repository (may already exist)
186+
console.print("\n[cyan][6/7][/cyan] Adding demo repository")
187+
result = subprocess.run(
188+
"uv run infrahubctl repository add DEMO https://github.com/opsmill/infrahub-demo.git --ref main --read-only --ref main",
189+
shell=True,
190+
capture_output=True,
191+
text=True
192+
)
193+
194+
if result.returncode == 0:
195+
console.print("[green]✓[/green] Repository added")
196+
else:
197+
if "already exists" in result.stderr.lower() or "already exists" in result.stdout.lower():
198+
console.print("[yellow]⚠[/yellow] Repository already exists, skipping...")
199+
else:
200+
console.print("[red]✗[/red] Failed to add repository")
201+
console.print(f"[dim]{result.stderr}[/dim]")
202+
203+
# Wait for repository sync
204+
console.print("\n[cyan][7/7][/cyan] Waiting for repository sync")
205+
wait_for_repository_sync(120)
206+
207+
# Load event actions
208+
console.print("\n[cyan]→[/cyan] Loading event actions")
209+
if run_command(
210+
f"uv run infrahubctl object load objects/events/ --branch {branch}",
211+
"Event actions loading",
212+
""
213+
):
214+
console.print("[green]✓[/green] Event actions loaded successfully")
215+
216+
# Display completion message
217+
console.print()
218+
console.print(Panel(
219+
f"[bold green]Bootstrap Complete![/bold green]\n\n"
220+
f"[dim]All data has been loaded into Infrahub[/dim]\n"
221+
f"[dim]Branch:[/dim] [bold]{branch}[/bold]\n\n"
222+
"[cyan]Next steps:[/cyan]\n"
223+
" • Demo a DC design: [bold]uv run invoke demo-dc-arista[/bold]\n"
224+
" • Create a Proposed Change",
225+
title="Success",
226+
border_style="green",
227+
box=box.ROUNDED
228+
))
229+
230+
return 0
231+
232+
233+
if __name__ == "__main__":
234+
parser = argparse.ArgumentParser(
235+
description="Bootstrap Infrahub with schemas, data, and configurations"
236+
)
237+
parser.add_argument(
238+
"--branch",
239+
"-b",
240+
type=str,
241+
default="main",
242+
help="Branch to load data into (default: main)"
243+
)
244+
args = parser.parse_args()
245+
246+
sys.exit(main(branch=args.branch))

0 commit comments

Comments
 (0)