terraui 0.1.0__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.
- terraui-0.1.0/.gitignore +38 -0
- terraui-0.1.0/BACKEND_SPEC.md +388 -0
- terraui-0.1.0/LICENSE +21 -0
- terraui-0.1.0/PKG-INFO +156 -0
- terraui-0.1.0/README.md +112 -0
- terraui-0.1.0/pyproject.toml +66 -0
- terraui-0.1.0/src/terraui/__init__.py +3 -0
- terraui-0.1.0/src/terraui/ai/__init__.py +0 -0
- terraui-0.1.0/src/terraui/ai/chat.py +98 -0
- terraui-0.1.0/src/terraui/cli.py +242 -0
- terraui-0.1.0/src/terraui/clouds/__init__.py +0 -0
- terraui-0.1.0/src/terraui/clouds/auth.py +76 -0
- terraui-0.1.0/src/terraui/clouds/preflight.py +192 -0
- terraui-0.1.0/src/terraui/demo.py +106 -0
- terraui-0.1.0/src/terraui/discovery/__init__.py +1 -0
- terraui-0.1.0/src/terraui/discovery/hcl.py +106 -0
- terraui-0.1.0/src/terraui/discovery/scanner.py +115 -0
- terraui-0.1.0/src/terraui/execution/__init__.py +0 -0
- terraui-0.1.0/src/terraui/execution/command.py +51 -0
- terraui-0.1.0/src/terraui/execution/executor.py +176 -0
- terraui-0.1.0/src/terraui/models.py +116 -0
- terraui-0.1.0/src/terraui/server/__init__.py +1 -0
- terraui-0.1.0/src/terraui/server/app.py +364 -0
- terraui-0.1.0/src/terraui/shell/__init__.py +0 -0
- terraui-0.1.0/src/terraui/shell/pty.py +113 -0
- terraui-0.1.0/src/terraui/state/__init__.py +0 -0
- terraui-0.1.0/src/terraui/state/inspect.py +53 -0
- terraui-0.1.0/src/terraui/store/__init__.py +1 -0
- terraui-0.1.0/src/terraui/store/db.py +85 -0
- terraui-0.1.0/src/terraui/web/index.html +829 -0
- terraui-0.1.0/tests/test_api.py +93 -0
- terraui-0.1.0/tests/test_command.py +64 -0
- terraui-0.1.0/tests/test_discovery.py +51 -0
- terraui-0.1.0/tests/test_preflight.py +85 -0
terraui-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Virtual envs
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Tooling / caches
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.coverage
|
|
20
|
+
htmlcov/
|
|
21
|
+
|
|
22
|
+
# Local runtime / scratch
|
|
23
|
+
*.out
|
|
24
|
+
pytest.out
|
|
25
|
+
terraui.db
|
|
26
|
+
*.sqlite
|
|
27
|
+
*.sqlite3
|
|
28
|
+
|
|
29
|
+
# Editors / OS
|
|
30
|
+
.vscode/
|
|
31
|
+
.idea/
|
|
32
|
+
.DS_Store
|
|
33
|
+
Thumbs.db
|
|
34
|
+
|
|
35
|
+
# Secrets / env
|
|
36
|
+
.env
|
|
37
|
+
.env.*
|
|
38
|
+
!.env.example
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# TerraUi — Backend Build Prompt / Engineering Spec
|
|
2
|
+
|
|
3
|
+
> Hand this document to an engineer (or to Claude Code) as the build brief for the
|
|
4
|
+
> TerraUi backend. The frontend (`TerraUi.dc.html`) is the source of truth for the
|
|
5
|
+
> product surface; this spec defines the engine that makes it real.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. Mission
|
|
10
|
+
|
|
11
|
+
TerraUi is a **local-first control plane for Terraform and Terragrunt**. An org points it at
|
|
12
|
+
the directory that holds all their IaC repos; TerraUi discovers every unit (standalone
|
|
13
|
+
Terraform *and* Terragrunt), and gives a single web UI to:
|
|
14
|
+
|
|
15
|
+
- inventory every stack across clouds (AWS, GCP, Azure) and environments,
|
|
16
|
+
- run `plan` / `apply` / `destroy` with a flag picker, streamed live,
|
|
17
|
+
- detect and reconcile **drift**,
|
|
18
|
+
- view **diffs** and full plan output,
|
|
19
|
+
- see **who holds the state lock** and acquire/release it,
|
|
20
|
+
- read **history / audit** (who changed what, from which PR/commit),
|
|
21
|
+
- orchestrate **Terragrunt dependency-ordered** `run-all`,
|
|
22
|
+
- generate plans from **GitHub & GitLab PRs**,
|
|
23
|
+
- expose a **Cloud Shell** (real terminal) wired to the user's local shell + cloud SDKs,
|
|
24
|
+
- answer questions via an **AI assistant**.
|
|
25
|
+
|
|
26
|
+
Install path the user expects:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install terraui
|
|
30
|
+
cd ~/acme/infra # the folder that contains all IaC repos
|
|
31
|
+
terraui start # discovers everything, opens http://localhost:8787
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Core principle:** TerraUi runs commands on the *user's own machine*, using the *user's own
|
|
35
|
+
shell* (PowerShell on Windows, zsh/bash on macOS & Linux) and the *user's existing cloud
|
|
36
|
+
credentials*. It never stores secrets and never invents its own auth — it shells out exactly
|
|
37
|
+
as the engineer would by hand.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. Tech stack
|
|
42
|
+
|
|
43
|
+
| Concern | Choice | Notes |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| Language | **Python 3.10+** | Matches `pip install` expectation. |
|
|
46
|
+
| Web framework | **FastAPI** + **Uvicorn** | REST + WebSocket in one app. |
|
|
47
|
+
| Async | `asyncio` + `anyio` | Subprocess streaming, concurrency. |
|
|
48
|
+
| CLI | **Typer** (or Click) | `terraui start|server|agent|scan`. |
|
|
49
|
+
| Terminal | **ptyprocess** / `pywinpty` (Windows) + **xterm.js** on the front | Real PTY. |
|
|
50
|
+
| HCL parsing | **python-hcl2** + targeted regex fallbacks | Parse providers, backends, deps. |
|
|
51
|
+
| Local store | **SQLite** (local mode) → **Postgres** (server mode) via SQLAlchemy | Runs, audit, drift snapshots. |
|
|
52
|
+
| Realtime | WebSockets | Run output, terminal, drift events. |
|
|
53
|
+
| Packaging | **pyproject.toml** (hatchling/poetry), console_scripts entry point | Ships the built frontend as package data. |
|
|
54
|
+
|
|
55
|
+
The compiled frontend (a single self-contained HTML bundle of `TerraUi.dc.html`) is shipped
|
|
56
|
+
inside the wheel and served at `/`. `terraui start` opens the browser to it.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 2. CLI surface
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
terraui start [PATH] # local mode: scan PATH (default cwd), serve UI + executor
|
|
64
|
+
--port 8787
|
|
65
|
+
--scan ./live ./modules # extra roots / globs
|
|
66
|
+
--no-open # don't auto-open browser
|
|
67
|
+
--shell powershell|zsh|bash # override detected shell
|
|
68
|
+
--drift-interval 30m # background drift scan cadence (0 = off)
|
|
69
|
+
|
|
70
|
+
terraui scan [PATH] --json # print discovered units as JSON, no server (CI/debug)
|
|
71
|
+
|
|
72
|
+
terraui server --config terraui.yaml # team mode: central control plane (SSO, RBAC, Postgres)
|
|
73
|
+
terraui agent --server URL --label prod-runner # remote executor that registers with a server
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Local mode = single binary, single user, zero infra. Server mode = shared deployment;
|
|
77
|
+
**agents** hold the cloud creds and do the execution, the server never does (see §11).
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 3. Architecture
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
┌──────────────────────────────────────────────┐
|
|
85
|
+
Browser ──► │ FastAPI app │
|
|
86
|
+
(TerraUi UI) │ ├─ REST /api/... (inventory, runs) │
|
|
87
|
+
▲ WS │ ├─ WS /ws/run/{id} (live plan output) │
|
|
88
|
+
└─────────┤ ├─ WS /ws/term/{id} (PTY terminal) │
|
|
89
|
+
│ ├─ Discovery service (HCL walk + parse) │
|
|
90
|
+
│ ├─ Execution engine (subprocess/PTY) │
|
|
91
|
+
│ ├─ Auth probe (aws/gcloud/az) │
|
|
92
|
+
│ ├─ State+lock inspector (per backend) │
|
|
93
|
+
│ ├─ Drift scheduler │
|
|
94
|
+
│ ├─ VCS connectors (GitHub/GitLab) │
|
|
95
|
+
│ └─ AI service (LLM proxy + repo RAG) │
|
|
96
|
+
└──────────────────────────────────────────────┘
|
|
97
|
+
│ subprocess (user's shell)
|
|
98
|
+
▼
|
|
99
|
+
terraform / terragrunt / aws / gcloud / az on the local machine
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 4. Discovery service
|
|
105
|
+
|
|
106
|
+
On `start`, walk the scan roots and build the stack inventory.
|
|
107
|
+
|
|
108
|
+
**Detection rules**
|
|
109
|
+
- A directory with `*.tf` files → a **Terraform unit**.
|
|
110
|
+
- A directory with `terragrunt.hcl` → a **Terragrunt unit** (takes precedence; the surrounding
|
|
111
|
+
`live/<env>/<stack>` convention defines env + stack name).
|
|
112
|
+
- Read the `terraform { backend "<type>" { ... } }` block to find the **state location**
|
|
113
|
+
(s3/gcs/azurerm/local/remote) and any **lock table/lease**.
|
|
114
|
+
- Parse `provider "<name>" { ... }` and `required_providers` to infer the **cloud**
|
|
115
|
+
(`aws`→AWS, `google`/`google-beta`→GCP, `azurerm`→Azure). A unit may be multi-cloud.
|
|
116
|
+
- For Terragrunt, parse `dependency "<x>" { config_path = ... }` and `dependencies { paths = [...] }`
|
|
117
|
+
to build the **DAG** used for `run-all` ordering.
|
|
118
|
+
- Derive **environment** from path (`live/prod/...`, `*-prod`, workspace) — make this
|
|
119
|
+
configurable via `terraui.yaml` overrides.
|
|
120
|
+
|
|
121
|
+
**Output:** a normalized model the frontend consumes (one row per stack):
|
|
122
|
+
|
|
123
|
+
```jsonc
|
|
124
|
+
{
|
|
125
|
+
"id": "eks-prod",
|
|
126
|
+
"name": "eks-cluster",
|
|
127
|
+
"env": "prod",
|
|
128
|
+
"tool": "terragrunt", // or "terraform"
|
|
129
|
+
"clouds": ["aws"],
|
|
130
|
+
"path": "live/prod/eks-cluster",
|
|
131
|
+
"vcs": { "host": "github", "repo": "acme/infra-live", "subdir": "live/prod/eks-cluster" },
|
|
132
|
+
"backend": { "type": "s3", "bucket": "acme-tfstate", "key": "prod/eks.tfstate",
|
|
133
|
+
"lock": { "type": "dynamodb", "table": "acme-tf-locks" } },
|
|
134
|
+
"deps": ["net-prod"],
|
|
135
|
+
"resourceCount": 88,
|
|
136
|
+
"status": "drift|healthy|running|error|plan_pending",
|
|
137
|
+
"drift": 3,
|
|
138
|
+
"lock": null
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Discovery must be **incremental & watchable** — re-scan on file change (watchdog) so the UI
|
|
143
|
+
stays live as repos change. Cache parse results keyed by file mtime.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 5. Execution engine
|
|
148
|
+
|
|
149
|
+
The heart of the product. Runs `terraform`/`terragrunt` as a child process **in the unit's
|
|
150
|
+
directory**, using the user's shell, streaming output to the browser.
|
|
151
|
+
|
|
152
|
+
**Requirements**
|
|
153
|
+
- Build the command from a structured request (see flag model below) — never string-concat
|
|
154
|
+
untrusted input; pass args as a list, validate the action against an allow-list
|
|
155
|
+
(`plan|apply|destroy|init|validate|output|state list|force-unlock`).
|
|
156
|
+
- Spawn via the detected shell so the user's PATH, profile, and cloud CLI shims are present:
|
|
157
|
+
- Windows: `pwsh -NoLogo -Command <cmd>` (fallback `powershell.exe`).
|
|
158
|
+
- macOS/Linux: `$SHELL -lc <cmd>` (login shell to load creds/profile).
|
|
159
|
+
- Stream **stdout+stderr** line-buffered over `WS /ws/run/{run_id}`. Emit structured events:
|
|
160
|
+
`{type:"line", stream:"stdout", text}`, `{type:"status", status}`, `{type:"summary", add, change, destroy}`.
|
|
161
|
+
- Parse `terraform plan -json` / `-detailed-exitcode` to populate the **diff** and the
|
|
162
|
+
`+add ~change -destroy` summary the UI shows. Prefer `-json` for machine parsing; keep the
|
|
163
|
+
raw text for the log pane.
|
|
164
|
+
- Persist every run: id, stack, action, full command, actor, trigger (manual/PR/merge/drift/schedule),
|
|
165
|
+
exit code, duration, summary, and the complete log. This is the **audit trail**.
|
|
166
|
+
- Concurrency: serialize runs per stack (one lock per state); allow parallel across independent
|
|
167
|
+
stacks. For Terragrunt `run-all`, resolve the DAG and execute level-by-level honoring `--terragrunt-parallelism`.
|
|
168
|
+
|
|
169
|
+
**Flag model** (mirrors the UI's run picker — `cmdVM` in the frontend):
|
|
170
|
+
|
|
171
|
+
```jsonc
|
|
172
|
+
{
|
|
173
|
+
"stackId": "rds-prod",
|
|
174
|
+
"action": "plan", // plan | apply | destroy
|
|
175
|
+
"runAll": false, // terragrunt run-all
|
|
176
|
+
"flags": {
|
|
177
|
+
"refresh": true, // -refresh=false when false
|
|
178
|
+
"lock": true, // -lock=false when false
|
|
179
|
+
"autoApprove": false, // -auto-approve (apply/destroy only)
|
|
180
|
+
"noColor": false, // -no-color
|
|
181
|
+
"nonInteractive": true, // --terragrunt-non-interactive
|
|
182
|
+
"parallelism": 10, // -parallelism=N
|
|
183
|
+
"target": "", // -target=ADDR (repeatable)
|
|
184
|
+
"varFile": "env/prod.tfvars" // -var-file=FILE (repeatable)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`build_command()` must produce exactly what the UI previews, e.g.
|
|
190
|
+
`terragrunt run-all plan -var-file=env/prod.tfvars --terragrunt-non-interactive`.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 6. Cloud Shell (PTY terminal)
|
|
195
|
+
|
|
196
|
+
A genuine interactive terminal, not a fake. `WS /ws/term/{session}` bridges xterm.js to a PTY
|
|
197
|
+
running the user's shell in the IaC root. Supports `aws`, `gcloud`, `az`, `terraform`,
|
|
198
|
+
`terragrunt`, `git`, etc. — anything on the user's PATH.
|
|
199
|
+
|
|
200
|
+
- Use `ptyprocess` (POSIX) / `pywinpty` (Windows). Forward keystrokes, stream output, handle
|
|
201
|
+
resize (`SIGWINCH` / pty set-size) from the client's `cols/rows`.
|
|
202
|
+
- The "Run on shell" button in the UI's flag modal sends a composed command into this session
|
|
203
|
+
(or a dedicated run session) so plan/apply output streams in the same place.
|
|
204
|
+
- Reflect the active shell (PowerShell/zsh/bash) in the prompt; allow switching the executor shell.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 7. Cloud SDK auth detection
|
|
209
|
+
|
|
210
|
+
Probe each cloud's CLI and surface status in the top bar + Cloud Shell. Run these read-only
|
|
211
|
+
checks (cache results ~60s, re-probe on demand and after a login):
|
|
212
|
+
|
|
213
|
+
| Cloud | Probe command | Authenticated when |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| **AWS** | `aws sts get-caller-identity` | exit 0; show Account + Arn + region |
|
|
216
|
+
| **GCP** | `gcloud auth application-default print-access-token` **and** `gcloud auth list` | ADC token prints **and** an active account exists |
|
|
217
|
+
| **Azure** | `az account show` | exit 0; show subscription name/id |
|
|
218
|
+
|
|
219
|
+
**GCP needs both logins** — document and surface this clearly:
|
|
220
|
+
- `gcloud auth login` → user identity for the `gcloud` CLI.
|
|
221
|
+
- `gcloud auth application-default login` → **ADC**, which the Terraform `google` provider uses.
|
|
222
|
+
|
|
223
|
+
If a probe fails / token expired → mark the cloud **re-auth** (amber), gate affected stacks,
|
|
224
|
+
and offer the exact remediation command (`az login`, `aws sso login --profile …`,
|
|
225
|
+
`gcloud auth application-default login`) runnable straight from the Cloud Shell. Detect SDK
|
|
226
|
+
presence + versions (`aws --version`, `gcloud --version`, `az version`) and warn if missing,
|
|
227
|
+
with install links.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 8. State & lock inspection
|
|
232
|
+
|
|
233
|
+
Per backend type, without mutating state:
|
|
234
|
+
|
|
235
|
+
- **Read state / resource list:** `terraform state list` + `terraform show -json` (run in unit dir).
|
|
236
|
+
- **Locks:**
|
|
237
|
+
- S3 + **DynamoDB**: read the lock item from the lock table → who/when/lock-id.
|
|
238
|
+
- **GCS**: object generation/metadata lock.
|
|
239
|
+
- **azurerm**: blob **lease** state.
|
|
240
|
+
- Surface lock holder, age, and lock-id in the UI. Support **acquire/release**:
|
|
241
|
+
release maps to `terraform force-unlock <LOCK_ID>` (guarded — require confirmation + RBAC in
|
|
242
|
+
server mode).
|
|
243
|
+
- Optional **state versioning/history** (bucket versioning, or TerraUi-side snapshots) for the
|
|
244
|
+
"who changed what" timeline.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 9. Drift detection
|
|
249
|
+
|
|
250
|
+
- Scheduled background scan (default every 30m, `--drift-interval`): per stack run
|
|
251
|
+
`terraform plan -detailed-exitcode -refresh-only` (exit code 2 = drift) or parse a normal
|
|
252
|
+
`-json` plan for changes not originating from config.
|
|
253
|
+
- Store drift snapshots: per-resource, attribute-level `state` vs `actual` (exactly what the
|
|
254
|
+
Drift tab renders). Emit a WS event so the sidebar badge and dashboard metric update live.
|
|
255
|
+
- "Reconcile" action → opens the run picker pre-set to `plan` for that stack.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 10. VCS integration (GitHub & GitLab)
|
|
260
|
+
|
|
261
|
+
- OAuth/App install or PAT. Register **webhooks**: on PR/MR open & push, identify affected
|
|
262
|
+
stacks (changed paths ∩ discovered units) and run a **speculative plan**; post the
|
|
263
|
+
`+add ~change -destroy` summary + a link back to TerraUi as a PR/MR comment + status check.
|
|
264
|
+
- On merge to the default branch → optionally trigger `apply` (policy-gated).
|
|
265
|
+
- Record the PR/MR number, branch, commit SHA, and author on the run so the Runs/audit view can
|
|
266
|
+
show "GH #482 / GL !113 — title" with the engineer behind it (matches the frontend's run rows).
|
|
267
|
+
- Abstract a `VCSProvider` interface; implement `GitHubProvider` and `GitLabProvider`.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 11. Team mode & scaling (future-ready from day one)
|
|
272
|
+
|
|
273
|
+
Design the local app so the same core promotes to a shared deployment without a rewrite:
|
|
274
|
+
|
|
275
|
+
- **Server mode** (`terraui server`): central FastAPI + Postgres, serves the UI to many users.
|
|
276
|
+
- **Agents** (`terraui agent`): remote executors that register with the server, hold the cloud
|
|
277
|
+
creds for their environment, pull run jobs from a queue, and stream output back. The server
|
|
278
|
+
**never** holds cloud secrets.
|
|
279
|
+
- **Job queue / workers:** Redis or Postgres-backed (e.g. `arq`/Celery) for fan-out and
|
|
280
|
+
run-all orchestration across agents.
|
|
281
|
+
- **AuthN/Z:** OIDC/SAML SSO; **RBAC** with per-environment apply approvals; optional policy
|
|
282
|
+
gate (**OPA / Sentinel**) evaluated on the plan before apply is allowed.
|
|
283
|
+
- **Multi-tenant:** org → projects → stacks; row-level scoping in Postgres.
|
|
284
|
+
- **Observability:** structured logs, Prometheus metrics, OpenTelemetry traces on runs.
|
|
285
|
+
|
|
286
|
+
Keep all execution behind an `Executor` interface with `LocalExecutor` (subprocess/PTY) and
|
|
287
|
+
`AgentExecutor` (remote) implementations — local and server modes differ only in which executor
|
|
288
|
+
and store are wired in.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 12. AI assistant
|
|
293
|
+
|
|
294
|
+
- Endpoint `POST /api/ai/chat` proxying an LLM. **Never** put model keys in the browser; the
|
|
295
|
+
server holds them (env/secret store).
|
|
296
|
+
- Ground answers in the user's repos: build a lightweight index (RAG) over discovered HCL,
|
|
297
|
+
backend configs, latest plan/drift snapshots, and run history, so questions like
|
|
298
|
+
"why is eks-cluster drifting?" use real context.
|
|
299
|
+
- Allow the assistant to call **read-only** tools (state list, plan summary, auth status, dep
|
|
300
|
+
graph) but require explicit human approval before any mutating action — surface that guarantee
|
|
301
|
+
in the UI ("never applies without approval").
|
|
302
|
+
- Stream tokens over SSE/WS for a responsive chat.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## 13. API surface (align with the frontend)
|
|
307
|
+
|
|
308
|
+
REST (JSON):
|
|
309
|
+
```
|
|
310
|
+
GET /api/stacks → inventory (see §4 model)
|
|
311
|
+
GET /api/stacks/{id} → detail + backend + deps + activity
|
|
312
|
+
GET /api/stacks/{id}/state → resource list
|
|
313
|
+
GET /api/stacks/{id}/drift → attribute-level drift snapshot
|
|
314
|
+
POST /api/stacks/{id}/lock → acquire | DELETE → release (force-unlock)
|
|
315
|
+
POST /api/runs → start a run (flag model §5) → {runId}
|
|
316
|
+
GET /api/runs → history (filter: pr|manual|schedule|drift)
|
|
317
|
+
GET /api/runs/{id} → run detail + full log
|
|
318
|
+
GET /api/graph?env=prod → Terragrunt DAG + run-all order
|
|
319
|
+
GET /api/clouds → auth status per cloud (§7)
|
|
320
|
+
POST /api/ai/chat → assistant
|
|
321
|
+
GET /api/health
|
|
322
|
+
```
|
|
323
|
+
WebSocket:
|
|
324
|
+
```
|
|
325
|
+
/ws/run/{runId} → live run events (line/status/summary)
|
|
326
|
+
/ws/term/{session} → PTY terminal I/O + resize
|
|
327
|
+
/ws/events → inventory/drift/lock change push (UI live updates)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## 14. Security
|
|
333
|
+
|
|
334
|
+
- No secret storage. Use the cloud SDK credential chains already on the machine/agent.
|
|
335
|
+
- Command allow-list + argument validation; never shell-interpolate user strings — pass argv.
|
|
336
|
+
- Bind local mode to `127.0.0.1` by default; CSRF/Origin checks on WS upgrades.
|
|
337
|
+
- Server mode: TLS, SSO, RBAC, signed agent registration, full audit log of every run & lock op.
|
|
338
|
+
- Redact secrets from streamed logs (provider env vars, tokens) before persistence.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## 15. Packaging & layout
|
|
343
|
+
|
|
344
|
+
```
|
|
345
|
+
terraui/
|
|
346
|
+
├─ pyproject.toml # [project.scripts] terraui = "terraui.cli:app"
|
|
347
|
+
├─ terraui/
|
|
348
|
+
│ ├─ cli.py # Typer app: start/server/agent/scan
|
|
349
|
+
│ ├─ server/app.py # FastAPI factory, routers, WS
|
|
350
|
+
│ ├─ discovery/ # walk + HCL parse + DAG
|
|
351
|
+
│ ├─ exec/ # Executor, LocalExecutor, AgentExecutor, PTY
|
|
352
|
+
│ ├─ clouds/ # aws/gcp/azure auth probes
|
|
353
|
+
│ ├─ state/ # backend-specific state + lock inspectors
|
|
354
|
+
│ ├─ drift/ # scheduler + snapshots
|
|
355
|
+
│ ├─ vcs/ # github.py, gitlab.py
|
|
356
|
+
│ ├─ ai/ # chat + RAG
|
|
357
|
+
│ ├─ store/ # SQLAlchemy models, sqlite/postgres
|
|
358
|
+
│ └─ web/ # bundled frontend (TerraUi.html) as package data
|
|
359
|
+
└─ tests/
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
> Implementation note: this repo uses a `src/terraui/` layout (PyPI best practice). The
|
|
363
|
+
> `exec/` module is named `execution/` (avoids shadowing the Python builtin); the bundled
|
|
364
|
+
> frontend lives at `src/terraui/web/index.html`.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## 16. Milestones
|
|
369
|
+
|
|
370
|
+
1. **M1 — Inventory + UI shell:** discovery, `terraui start`, serve frontend, stacks list, state read.
|
|
371
|
+
2. **M2 — Execution:** flag-model runs (plan/apply/destroy) streamed over WS; run history/audit; PTY Cloud Shell.
|
|
372
|
+
3. **M3 — Drift + locks:** scheduled drift scans, attribute diffs, lock inspect/acquire/release.
|
|
373
|
+
4. **M4 — Terragrunt orchestration:** DAG + dependency-ordered `run-all`.
|
|
374
|
+
5. **M5 — VCS:** GitHub/GitLab webhooks, speculative PR plans, status checks, apply-on-merge.
|
|
375
|
+
6. **M6 — AI assistant:** grounded chat with read-only tool access.
|
|
376
|
+
7. **M7 — Server mode:** agents, SSO, RBAC, Postgres, policy gates — the scale story.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## 17. Acceptance (local mode MVP)
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
pip install terraui && cd ~/acme/infra && terraui start
|
|
384
|
+
```
|
|
385
|
+
…must: discover every Terraform & Terragrunt unit; show them grouped by env/cloud; report
|
|
386
|
+
AWS/GCP/Azure auth status; let me open a stack, pick flags, run a streamed `plan`, see the diff,
|
|
387
|
+
view drift, see who holds the lock, read run history with the triggering PR — and open a Cloud
|
|
388
|
+
Shell that runs `az login` / `gcloud auth application-default login` against my real machine.
|
terraui-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Preet
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
terraui-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: terraui
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local-first control plane for Terraform & Terragrunt — inventory, plan/apply, drift, locks and a cloud shell, in one UI.
|
|
5
|
+
Project-URL: Homepage, https://gitlab.com/terraui/terraui
|
|
6
|
+
Project-URL: Repository, https://gitlab.com/terraui/terraui.git
|
|
7
|
+
Project-URL: Issues, https://gitlab.com/terraui/terraui/-/issues
|
|
8
|
+
Author: Preet
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: control-plane,devops,drift,iac,infrastructure,terraform,terragrunt
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Environment :: Web Environment
|
|
15
|
+
Classifier: Framework :: FastAPI
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: System Administrators
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
26
|
+
Classifier: Topic :: System :: Systems Administration
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Requires-Dist: fastapi>=0.110
|
|
29
|
+
Requires-Dist: httpx>=0.27
|
|
30
|
+
Requires-Dist: python-hcl2>=4.3; python_version < '4'
|
|
31
|
+
Requires-Dist: typer>=0.12
|
|
32
|
+
Requires-Dist: uvicorn[standard]>=0.29
|
|
33
|
+
Provides-Extra: ai
|
|
34
|
+
Requires-Dist: anthropic>=0.40; extra == 'ai'
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
39
|
+
Requires-Dist: twine>=5; extra == 'dev'
|
|
40
|
+
Provides-Extra: pty
|
|
41
|
+
Requires-Dist: ptyprocess>=0.7; (sys_platform != 'win32') and extra == 'pty'
|
|
42
|
+
Requires-Dist: pywinpty>=2.0; (sys_platform == 'win32') and extra == 'pty'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# TerraUi
|
|
46
|
+
|
|
47
|
+
A **local-first control plane for Terraform & Terragrunt**. Point it at the folder
|
|
48
|
+
that holds all your IaC repos; it discovers every unit, and gives one web UI to
|
|
49
|
+
inventory stacks, run `plan`/`apply`/`destroy` (streamed live), detect drift, read
|
|
50
|
+
state and locks, browse the Terragrunt dependency graph, open a cloud shell, and
|
|
51
|
+
ask an AI assistant.
|
|
52
|
+
|
|
53
|
+
This repo implements the `TerraUi.dc.html` design (the product surface) plus the
|
|
54
|
+
backend from `BACKEND_SPEC.md`. It ships as a Python package with the compiled
|
|
55
|
+
frontend bundled and served at `/`.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install -e . # from this repo (Python 3.10+)
|
|
59
|
+
cd ~/acme/infra # the folder containing your IaC repos
|
|
60
|
+
terraui start # discovers everything, opens http://localhost:8787
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
No IaC in the current folder? `terraui start` falls back to a fully-populated
|
|
64
|
+
**demo dataset** (the one from the design) so you can explore the whole UI.
|
|
65
|
+
|
|
66
|
+
## Cloud SDK setup (gcloud / aws / az)
|
|
67
|
+
|
|
68
|
+
TerraUi shells out to your existing cloud CLIs — it never stores credentials. On
|
|
69
|
+
first `terraui start` it runs a **read-only** check of the providers your stacks
|
|
70
|
+
actually use and, if anything is missing, points you at `terraui setup`:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
terraui setup # per provider: detect → offer install → offer login
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`setup` is **per-provider and skippable** — if you only use GCP, skip AWS and
|
|
77
|
+
Azure and configure them later. It shows the exact install/login command before
|
|
78
|
+
running it and asks for confirmation; nothing happens silently. GCP runs **both**
|
|
79
|
+
required logins (`gcloud auth login` for the CLI and
|
|
80
|
+
`gcloud auth application-default login` for the Terraform provider).
|
|
81
|
+
|
|
82
|
+
## CLI
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
terraui start [PATH] Local mode: scan PATH (default cwd), serve UI + executor
|
|
86
|
+
--port 8787 Port (default 8787)
|
|
87
|
+
--scan ./live ./mods Extra roots to scan
|
|
88
|
+
--no-open Don't auto-open the browser
|
|
89
|
+
--shell powershell|zsh|bash
|
|
90
|
+
--drift-interval 30m Background drift cadence (0 = off)
|
|
91
|
+
--demo Force the bundled demo dataset
|
|
92
|
+
--check Run interactive cloud-SDK setup before serving
|
|
93
|
+
--skip-checks Skip the cloud-SDK status check
|
|
94
|
+
|
|
95
|
+
terraui setup [PATH] Detect / install / authenticate cloud SDKs (per-provider, skippable)
|
|
96
|
+
--providers aws,gcp,azure Limit to specific providers
|
|
97
|
+
--yes Non-interactive (install only; skip browser logins)
|
|
98
|
+
|
|
99
|
+
terraui scan [PATH] --json Print discovered units as JSON (CI / debug)
|
|
100
|
+
terraui server --config … Team mode (scaffold — see BACKEND_SPEC §11)
|
|
101
|
+
terraui agent --server … Remote executor (scaffold)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## What's implemented
|
|
105
|
+
|
|
106
|
+
| Area | Status |
|
|
107
|
+
|---|---|
|
|
108
|
+
| Discovery (HCL walk, backend/provider/deps parse, Terragrunt DAG) | ✅ `discovery/` |
|
|
109
|
+
| Execution engine (flag model → `build_command`, streamed over WS, persisted) | ✅ `execution/`, `store/` |
|
|
110
|
+
| Cloud auth probes (AWS / GCP-with-ADC / Azure) | ✅ `clouds/` |
|
|
111
|
+
| State & lock read (`terraform state list`, acquire/release) | ✅ `state/` |
|
|
112
|
+
| Drift (per-stack snapshots; demo data, live scan scaffolded) | ◑ `drift` endpoint |
|
|
113
|
+
| Cloud Shell PTY (pywinpty / ptyprocess, subprocess fallback) | ✅ `shell/` |
|
|
114
|
+
| AI assistant (Claude `claude-haiku-4-5` proxy + offline fallback) | ✅ `ai/` |
|
|
115
|
+
| Frontend bundle (all views, drawer, flag modal, toast) | ✅ `web/index.html` |
|
|
116
|
+
| VCS webhooks, server/agent mode, RBAC, policy gates | ☐ scaffolded (§10–11) |
|
|
117
|
+
|
|
118
|
+
The command the UI previews is exactly the command the executor runs —
|
|
119
|
+
`buildCmdStr` (frontend) and `build_command` (backend) are kept identical and
|
|
120
|
+
unit-tested against the spec example
|
|
121
|
+
(`terragrunt run-all plan -var-file=env/prod.tfvars --terragrunt-non-interactive`).
|
|
122
|
+
|
|
123
|
+
## Architecture
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
Browser (web/index.html)
|
|
127
|
+
REST /api/* inventory, runs, drift, locks, graph, clouds, ai
|
|
128
|
+
WS /ws/run/{id} live plan/apply output
|
|
129
|
+
WS /ws/term/{sess} PTY terminal
|
|
130
|
+
│
|
|
131
|
+
FastAPI app (server/app.py)
|
|
132
|
+
discovery · execution · clouds · state · drift · ai · store
|
|
133
|
+
│ subprocess (user's shell)
|
|
134
|
+
▼
|
|
135
|
+
terraform / terragrunt / aws / gcloud / az on the local machine
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Develop
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
pip install -e ".[dev,pty]"
|
|
142
|
+
pytest # command builder, discovery, API smoke tests
|
|
143
|
+
terraui start --demo # run the UI against the demo dataset
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The AI assistant proxies to Claude when `ANTHROPIC_API_KEY` is set (model
|
|
147
|
+
`claude-haiku-4-5`); otherwise it returns grounded canned answers. The key never
|
|
148
|
+
reaches the browser.
|
|
149
|
+
|
|
150
|
+
## Security
|
|
151
|
+
|
|
152
|
+
Local mode binds to `127.0.0.1`. No secrets are stored — TerraUi shells out using
|
|
153
|
+
the cloud SDK credential chains already on your machine. Commands are built from a
|
|
154
|
+
structured flag model and passed as argv (never shell-interpolated); the action is
|
|
155
|
+
checked against an allow-list. Secrets are redacted from streamed logs before they
|
|
156
|
+
are persisted. See `BACKEND_SPEC.md` §14 for the full model.
|