provisioning/docs/book/architecture/orchestrator-integration-model.html
Jesús Pérez 6a59d34bb1
chore: update provisioning configuration and documentation
Update configuration files, templates, and internal documentation
for the provisioning repository system.

Configuration Updates:
- KMS configuration modernization
- Plugin system settings
- Service port mappings
- Test cluster topologies
- Installation configuration examples
- VM configuration defaults
- Cedar authorization policies

Documentation Updates:
- Library module documentation
- Extension API guides
- AI system documentation
- Service management guides
- Test environment setup
- Plugin usage guides
- Validator configuration documentation

All changes are backward compatible.
2025-12-11 21:50:42 +00:00

930 lines
39 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="ayu sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Orchestrator Integration Model - Provisioning Platform Documentation</title>
<!-- Custom HTML head -->
<meta name="description" content="Complete documentation for the Provisioning Platform - Infrastructure automation with Nushell, KCL, and Rust">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "ayu";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('ayu')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Provisioning Platform Documentation</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/provisioning/provisioning-platform" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/provisioning/provisioning-platform/edit/main/provisioning/docs/src/architecture/orchestrator-integration-model.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="orchestrator-integration-model---deep-dive"><a class="header" href="#orchestrator-integration-model---deep-dive">Orchestrator Integration Model - Deep Dive</a></h1>
<p><strong>Date:</strong> 2025-10-01
<strong>Status:</strong> Clarification Document
<strong>Related:</strong> <a href="multi-repo-strategy.html">Multi-Repo Strategy</a>, <a href="../user/hybrid-orchestrator.html">Hybrid Orchestrator v3.0</a></p>
<h2 id="executive-summary"><a class="header" href="#executive-summary">Executive Summary</a></h2>
<p>This document clarifies <strong>how the Rust orchestrator integrates with Nushell core</strong> in both monorepo and multi-repo architectures. The orchestrator is a <strong>critical performance layer</strong> that coordinates Nushell business logic execution, solving deep call stack limitations while preserving all existing functionality.</p>
<hr />
<h2 id="current-architecture-hybrid-orchestrator-v30"><a class="header" href="#current-architecture-hybrid-orchestrator-v30">Current Architecture (Hybrid Orchestrator v3.0)</a></h2>
<h3 id="the-problem-being-solved"><a class="header" href="#the-problem-being-solved">The Problem Being Solved</a></h3>
<p><strong>Original Issue:</strong></p>
<pre><code>Deep call stack in Nushell (template.nu:71)
→ "Type not supported" errors
→ Cannot handle complex nested workflows
→ Performance bottlenecks with recursive calls
</code></pre>
<p><strong>Solution:</strong> Rust orchestrator provides:</p>
<ol>
<li><strong>Task queue management</strong> (file-based, reliable)</li>
<li><strong>Priority scheduling</strong> (intelligent task ordering)</li>
<li><strong>Deep call stack elimination</strong> (Rust handles recursion)</li>
<li><strong>Performance optimization</strong> (async/await, parallel execution)</li>
<li><strong>State management</strong> (workflow checkpointing)</li>
</ol>
<h3 id="how-it-works-today-monorepo"><a class="header" href="#how-it-works-today-monorepo">How It Works Today (Monorepo)</a></h3>
<pre><code>┌─────────────────────────────────────────────────────────────┐
│ User │
└───────────────────────────┬─────────────────────────────────┘
│ calls
┌───────────────┐
│ provisioning │ (Nushell CLI)
│ CLI │
└───────┬───────┘
┌───────────────────┼───────────────────┐
│ │ │
↓ ↓ ↓
┌───────────────┐ ┌───────────────┐ ┌──────────────┐
│ Direct Mode │ │Orchestrated │ │ Workflow │
│ (Simple ops) │ │ Mode │ │ Mode │
└───────────────┘ └───────┬───────┘ └──────┬───────┘
│ │
↓ ↓
┌────────────────────────────────┐
│ Rust Orchestrator Service │
│ (Background daemon) │
│ │
│ • Task Queue (file-based) │
│ • Priority Scheduler │
│ • Workflow Engine │
│ • REST API Server │
└────────┬───────────────────────┘
│ spawns
┌────────────────┐
│ Nushell │
│ Business Logic │
│ │
│ • servers.nu │
│ • taskservs.nu │
│ • clusters.nu │
└────────────────┘
</code></pre>
<h3 id="three-execution-modes"><a class="header" href="#three-execution-modes">Three Execution Modes</a></h3>
<h4 id="mode-1-direct-mode-simple-operations"><a class="header" href="#mode-1-direct-mode-simple-operations">Mode 1: Direct Mode (Simple Operations)</a></h4>
<pre><code class="language-bash"># No orchestrator needed
provisioning server list
provisioning env
provisioning help
# Direct Nushell execution
provisioning (CLI) → Nushell scripts → Result
</code></pre>
<h4 id="mode-2-orchestrated-mode-complex-operations"><a class="header" href="#mode-2-orchestrated-mode-complex-operations">Mode 2: Orchestrated Mode (Complex Operations)</a></h4>
<pre><code class="language-bash"># Uses orchestrator for coordination
provisioning server create --orchestrated
# Flow:
provisioning CLI → Orchestrator API → Task Queue → Nushell executor
Result back to user
</code></pre>
<h4 id="mode-3-workflow-mode-batch-operations"><a class="header" href="#mode-3-workflow-mode-batch-operations">Mode 3: Workflow Mode (Batch Operations)</a></h4>
<pre><code class="language-bash"># Complex workflows with dependencies
provisioning workflow submit server-cluster.k
# Flow:
provisioning CLI → Orchestrator Workflow Engine → Dependency Graph
Parallel task execution
Nushell scripts for each task
Checkpoint state
</code></pre>
<hr />
<h2 id="integration-patterns"><a class="header" href="#integration-patterns">Integration Patterns</a></h2>
<h3 id="pattern-1-cli-submits-tasks-to-orchestrator"><a class="header" href="#pattern-1-cli-submits-tasks-to-orchestrator">Pattern 1: CLI Submits Tasks to Orchestrator</a></h3>
<p><strong>Current Implementation:</strong></p>
<p><strong>Nushell CLI (<code>core/nulib/workflows/server_create.nu</code>):</strong></p>
<pre><code class="language-nushell"># Submit server creation workflow to orchestrator
export def server_create_workflow [
infra_name: string
--orchestrated
] {
if $orchestrated {
# Submit task to orchestrator
let task = {
type: "server_create"
infra: $infra_name
params: { ... }
}
# POST to orchestrator REST API
http post http://localhost:9090/workflows/servers/create $task
} else {
# Direct execution (old way)
do-server-create $infra_name
}
}
</code></pre>
<p><strong>Rust Orchestrator (<code>platform/orchestrator/src/api/workflows.rs</code>):</strong></p>
<pre><code class="language-rust">// Receive workflow submission from Nushell CLI
#[axum::debug_handler]
async fn create_server_workflow(
State(state): State&lt;Arc&lt;AppState&gt;&gt;,
Json(request): Json&lt;ServerCreateRequest&gt;,
) -&gt; Result&lt;Json&lt;WorkflowResponse&gt;, ApiError&gt; {
// Create task
let task = Task {
id: Uuid::new_v4(),
task_type: TaskType::ServerCreate,
payload: serde_json::to_value(&amp;request)?,
priority: Priority::Normal,
status: TaskStatus::Pending,
created_at: Utc::now(),
};
// Queue task
state.task_queue.enqueue(task).await?;
// Return immediately (async execution)
Ok(Json(WorkflowResponse {
workflow_id: task.id,
status: "queued",
}))
}</code></pre>
<p><strong>Flow:</strong></p>
<pre><code>User → provisioning server create --orchestrated
Nushell CLI prepares task
HTTP POST to orchestrator (localhost:9090)
Orchestrator queues task
Returns workflow ID immediately
User can monitor: provisioning workflow monitor &lt;id&gt;
</code></pre>
<h3 id="pattern-2-orchestrator-executes-nushell-scripts"><a class="header" href="#pattern-2-orchestrator-executes-nushell-scripts">Pattern 2: Orchestrator Executes Nushell Scripts</a></h3>
<p><strong>Orchestrator Task Executor (<code>platform/orchestrator/src/executor.rs</code>):</strong></p>
<pre><code class="language-rust">// Orchestrator spawns Nushell to execute business logic
pub async fn execute_task(task: Task) -&gt; Result&lt;TaskResult&gt; {
match task.task_type {
TaskType::ServerCreate =&gt; {
// Orchestrator calls Nushell script via subprocess
let output = Command::new("nu")
.arg("-c")
.arg(format!(
"use {}/servers/create.nu; create-server '{}'",
PROVISIONING_LIB_PATH,
task.payload.infra_name
))
.output()
.await?;
// Parse Nushell output
let result = parse_nushell_output(&amp;output)?;
Ok(TaskResult {
task_id: task.id,
status: if result.success { "completed" } else { "failed" },
output: result.data,
})
}
// Other task types...
}
}</code></pre>
<p><strong>Flow:</strong></p>
<pre><code>Orchestrator task queue has pending task
Executor picks up task
Spawns Nushell subprocess: nu -c "use servers/create.nu; create-server 'wuji'"
Nushell executes business logic
Returns result to orchestrator
Orchestrator updates task status
User monitors via: provisioning workflow status &lt;id&gt;
</code></pre>
<h3 id="pattern-3-bidirectional-communication"><a class="header" href="#pattern-3-bidirectional-communication">Pattern 3: Bidirectional Communication</a></h3>
<p><strong>Nushell Calls Orchestrator API:</strong></p>
<pre><code class="language-nushell"># Nushell script checks orchestrator status during execution
export def check-orchestrator-health [] {
let response = (http get http://localhost:9090/health)
if $response.status != "healthy" {
error make { msg: "Orchestrator not available" }
}
$response
}
# Nushell script reports progress to orchestrator
export def report-progress [task_id: string, progress: int] {
http post http://localhost:9090/tasks/$task_id/progress {
progress: $progress
status: "in_progress"
}
}
</code></pre>
<p><strong>Orchestrator Monitors Nushell Execution:</strong></p>
<pre><code class="language-rust">// Orchestrator tracks Nushell subprocess
pub async fn execute_with_monitoring(task: Task) -&gt; Result&lt;TaskResult&gt; {
let mut child = Command::new("nu")
.arg("-c")
.arg(&amp;task.script)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
// Monitor stdout/stderr in real-time
let stdout = child.stdout.take().unwrap();
tokio::spawn(async move {
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
while let Some(line) = lines.next_line().await.unwrap() {
// Parse progress updates from Nushell
if line.contains("PROGRESS:") {
update_task_progress(&amp;line);
}
}
});
// Wait for completion with timeout
let result = tokio::time::timeout(
Duration::from_secs(3600),
child.wait()
).await??;
Ok(TaskResult::from_exit_status(result))
}</code></pre>
<hr />
<h2 id="multi-repo-architecture-impact"><a class="header" href="#multi-repo-architecture-impact">Multi-Repo Architecture Impact</a></h2>
<h3 id="repository-split-doesnt-change-integration-model"><a class="header" href="#repository-split-doesnt-change-integration-model">Repository Split Doesnt Change Integration Model</a></h3>
<p><strong>In Multi-Repo Setup:</strong></p>
<p><strong>Repository: <code>provisioning-core</code></strong></p>
<ul>
<li>Contains: Nushell business logic</li>
<li>Installs to: <code>/usr/local/lib/provisioning/</code></li>
<li>Package: <code>provisioning-core-3.2.1.tar.gz</code></li>
</ul>
<p><strong>Repository: <code>provisioning-platform</code></strong></p>
<ul>
<li>Contains: Rust orchestrator</li>
<li>Installs to: <code>/usr/local/bin/provisioning-orchestrator</code></li>
<li>Package: <code>provisioning-platform-2.5.3.tar.gz</code></li>
</ul>
<p><strong>Runtime Integration (Same as Monorepo):</strong></p>
<pre><code>User installs both packages:
provisioning-core-3.2.1 → /usr/local/lib/provisioning/
provisioning-platform-2.5.3 → /usr/local/bin/provisioning-orchestrator
Orchestrator expects core at: /usr/local/lib/provisioning/
Core expects orchestrator at: http://localhost:9090/
No code dependencies, just runtime coordination!
</code></pre>
<h3 id="configuration-based-integration"><a class="header" href="#configuration-based-integration">Configuration-Based Integration</a></h3>
<p><strong>Core Package (<code>provisioning-core</code>) config:</strong></p>
<pre><code class="language-toml"># /usr/local/share/provisioning/config/config.defaults.toml
[orchestrator]
enabled = true
endpoint = "http://localhost:9090"
timeout = 60
auto_start = true # Start orchestrator if not running
[execution]
default_mode = "orchestrated" # Use orchestrator by default
fallback_to_direct = true # Fall back if orchestrator down
</code></pre>
<p><strong>Platform Package (<code>provisioning-platform</code>) config:</strong></p>
<pre><code class="language-toml"># /usr/local/share/provisioning/platform/config.toml
[orchestrator]
host = "127.0.0.1"
port = 8080
data_dir = "/var/lib/provisioning/orchestrator"
[executor]
nushell_binary = "nu" # Expects nu in PATH
provisioning_lib = "/usr/local/lib/provisioning"
max_concurrent_tasks = 10
task_timeout_seconds = 3600
</code></pre>
<h3 id="version-compatibility"><a class="header" href="#version-compatibility">Version Compatibility</a></h3>
<p><strong>Compatibility Matrix (<code>provisioning-distribution/versions.toml</code>):</strong></p>
<pre><code class="language-toml">[compatibility.platform."2.5.3"]
core = "^3.2" # Platform 2.5.3 compatible with core 3.2.x
min-core = "3.2.0"
api-version = "v1"
[compatibility.core."3.2.1"]
platform = "^2.5" # Core 3.2.1 compatible with platform 2.5.x
min-platform = "2.5.0"
orchestrator-api = "v1"
</code></pre>
<hr />
<h2 id="execution-flow-examples"><a class="header" href="#execution-flow-examples">Execution Flow Examples</a></h2>
<h3 id="example-1-simple-server-creation-direct-mode"><a class="header" href="#example-1-simple-server-creation-direct-mode">Example 1: Simple Server Creation (Direct Mode)</a></h3>
<p><strong>No Orchestrator Needed:</strong></p>
<pre><code class="language-bash">provisioning server list
# Flow:
CLI → servers/list.nu → Query state → Return results
(Orchestrator not involved)
</code></pre>
<h3 id="example-2-server-creation-with-orchestrator"><a class="header" href="#example-2-server-creation-with-orchestrator">Example 2: Server Creation with Orchestrator</a></h3>
<p><strong>Using Orchestrator:</strong></p>
<pre><code class="language-bash">provisioning server create --orchestrated --infra wuji
# Detailed Flow:
1. User executes command
2. Nushell CLI (provisioning binary)
3. Reads config: orchestrator.enabled = true
4. Prepares task payload:
{
type: "server_create",
infra: "wuji",
params: { ... }
}
5. HTTP POST → http://localhost:9090/workflows/servers/create
6. Orchestrator receives request
7. Creates task with UUID
8. Enqueues to task queue (file-based: /var/lib/provisioning/queue/)
9. Returns immediately: { workflow_id: "abc-123", status: "queued" }
10. User sees: "Workflow submitted: abc-123"
11. Orchestrator executor picks up task
12. Spawns Nushell subprocess:
nu -c "use /usr/local/lib/provisioning/servers/create.nu; create-server 'wuji'"
13. Nushell executes business logic:
- Reads KCL config
- Calls provider API (UpCloud/AWS)
- Creates server
- Returns result
14. Orchestrator captures output
15. Updates task status: "completed"
16. User monitors: provisioning workflow status abc-123
→ Shows: "Server wuji created successfully"
</code></pre>
<h3 id="example-3-batch-workflow-with-dependencies"><a class="header" href="#example-3-batch-workflow-with-dependencies">Example 3: Batch Workflow with Dependencies</a></h3>
<p><strong>Complex Workflow:</strong></p>
<pre><code class="language-bash">provisioning batch submit multi-cloud-deployment.k
# Workflow contains:
- Create 5 servers (parallel)
- Install Kubernetes on servers (depends on server creation)
- Deploy applications (depends on Kubernetes)
# Detailed Flow:
1. CLI submits KCL workflow to orchestrator
2. Orchestrator parses workflow
3. Builds dependency graph using petgraph (Rust)
4. Topological sort determines execution order
5. Creates tasks for each operation
6. Executes in parallel where possible:
[Server 1] [Server 2] [Server 3] [Server 4] [Server 5]
↓ ↓ ↓ ↓ ↓
(All execute in parallel via Nushell subprocesses)
↓ ↓ ↓ ↓ ↓
└──────────┴──────────┴──────────┴──────────┘
[All servers ready]
[Install Kubernetes]
(Nushell subprocess)
[Kubernetes ready]
[Deploy applications]
(Nushell subprocess)
[Complete]
7. Orchestrator checkpoints state at each step
8. If failure occurs, can retry from checkpoint
9. User monitors real-time: provisioning batch monitor &lt;id&gt;
</code></pre>
<hr />
<h2 id="why-this-architecture"><a class="header" href="#why-this-architecture">Why This Architecture?</a></h2>
<h3 id="orchestrator-benefits"><a class="header" href="#orchestrator-benefits">Orchestrator Benefits</a></h3>
<ol>
<li>
<p><strong>Eliminates Deep Call Stack Issues</strong></p>
<pre><code>Without Orchestrator:
template.nu → calls → cluster.nu → calls → taskserv.nu → calls → provider.nu
(Deep nesting causes "Type not supported" errors)
With Orchestrator:
Orchestrator → spawns → Nushell subprocess (flat execution)
(No deep nesting, fresh Nushell context for each task)
</code></pre>
</li>
<li>
<p><strong>Performance Optimization</strong></p>
<pre><code class="language-rust">// Orchestrator executes tasks in parallel
let tasks = vec![task1, task2, task3, task4, task5];
let results = futures::future::join_all(
tasks.iter().map(|t| execute_task(t))
).await;
// 5 Nushell subprocesses run concurrently</code></pre>
</li>
<li>
<p><strong>Reliable State Management</strong></p>
<pre><code>Orchestrator maintains:
- Task queue (survives crashes)
- Workflow checkpoints (resume on failure)
- Progress tracking (real-time monitoring)
- Retry logic (automatic recovery)
</code></pre>
</li>
<li>
<p><strong>Clean Separation</strong></p>
<pre><code>Orchestrator (Rust): Performance, concurrency, state
Business Logic (Nushell): Providers, taskservs, workflows
Each does what it's best at!
</code></pre>
</li>
</ol>
<h3 id="why-not-pure-rust"><a class="header" href="#why-not-pure-rust">Why NOT Pure Rust?</a></h3>
<p><strong>Question:</strong> Why not implement everything in Rust?</p>
<p><strong>Answer:</strong></p>
<ol>
<li>
<p><strong>Nushell is perfect for infrastructure automation:</strong></p>
<ul>
<li>Shell-like scripting for system operations</li>
<li>Built-in structured data handling</li>
<li>Easy template rendering</li>
<li>Readable business logic</li>
</ul>
</li>
<li>
<p><strong>Rapid iteration:</strong></p>
<ul>
<li>Change Nushell scripts without recompiling</li>
<li>Community can contribute Nushell modules</li>
<li>Template-based configuration generation</li>
</ul>
</li>
<li>
<p><strong>Best of both worlds:</strong></p>
<ul>
<li>Rust: Performance, type safety, concurrency</li>
<li>Nushell: Flexibility, readability, ease of use</li>
</ul>
</li>
</ol>
<hr />
<h2 id="multi-repo-integration-example"><a class="header" href="#multi-repo-integration-example">Multi-Repo Integration Example</a></h2>
<h3 id="installation"><a class="header" href="#installation">Installation</a></h3>
<p><strong>User installs bundle:</strong></p>
<pre><code class="language-bash">curl -fsSL https://get.provisioning.io | sh
# Installs:
1. provisioning-core-3.2.1.tar.gz
→ /usr/local/bin/provisioning (Nushell CLI)
→ /usr/local/lib/provisioning/ (Nushell libraries)
→ /usr/local/share/provisioning/ (configs, templates)
2. provisioning-platform-2.5.3.tar.gz
→ /usr/local/bin/provisioning-orchestrator (Rust binary)
→ /usr/local/share/provisioning/platform/ (platform configs)
3. Sets up systemd/launchd service for orchestrator
</code></pre>
<h3 id="runtime-coordination"><a class="header" href="#runtime-coordination">Runtime Coordination</a></h3>
<p><strong>Core package expects orchestrator:</strong></p>
<pre><code class="language-nushell"># core/nulib/lib_provisioning/orchestrator/client.nu
# Check if orchestrator is running
export def orchestrator-available [] {
let config = (load-config)
let endpoint = $config.orchestrator.endpoint
try {
let response = (http get $"($endpoint)/health")
$response.status == "healthy"
} catch {
false
}
}
# Auto-start orchestrator if needed
export def ensure-orchestrator [] {
if not (orchestrator-available) {
if (load-config).orchestrator.auto_start {
print "Starting orchestrator..."
^provisioning-orchestrator --daemon
sleep 2sec
}
}
}
</code></pre>
<p><strong>Platform package executes core scripts:</strong></p>
<pre><code class="language-rust">// platform/orchestrator/src/executor/nushell.rs
pub struct NushellExecutor {
provisioning_lib: PathBuf, // /usr/local/lib/provisioning
nu_binary: PathBuf, // nu (from PATH)
}
impl NushellExecutor {
pub async fn execute_script(&amp;self, script: &amp;str) -&gt; Result&lt;Output&gt; {
Command::new(&amp;self.nu_binary)
.env("NU_LIB_DIRS", &amp;self.provisioning_lib)
.arg("-c")
.arg(script)
.output()
.await
}
pub async fn execute_module_function(
&amp;self,
module: &amp;str,
function: &amp;str,
args: &amp;[String],
) -&gt; Result&lt;Output&gt; {
let script = format!(
"use {}/{}; {} {}",
self.provisioning_lib.display(),
module,
function,
args.join(" ")
);
self.execute_script(&amp;script).await
}
}</code></pre>
<hr />
<h2 id="configuration-examples"><a class="header" href="#configuration-examples">Configuration Examples</a></h2>
<h3 id="core-package-config"><a class="header" href="#core-package-config">Core Package Config</a></h3>
<p><strong><code>/usr/local/share/provisioning/config/config.defaults.toml</code>:</strong></p>
<pre><code class="language-toml">[orchestrator]
enabled = true
endpoint = "http://localhost:9090"
timeout_seconds = 60
auto_start = true
fallback_to_direct = true
[execution]
# Modes: "direct", "orchestrated", "auto"
default_mode = "auto" # Auto-detect based on complexity
# Operations that always use orchestrator
force_orchestrated = [
"server.create",
"cluster.create",
"batch.*",
"workflow.*"
]
# Operations that always run direct
force_direct = [
"*.list",
"*.show",
"help",
"version"
]
</code></pre>
<h3 id="platform-package-config"><a class="header" href="#platform-package-config">Platform Package Config</a></h3>
<p><strong><code>/usr/local/share/provisioning/platform/config.toml</code>:</strong></p>
<pre><code class="language-toml">[server]
host = "127.0.0.1"
port = 8080
[storage]
backend = "filesystem" # or "surrealdb"
data_dir = "/var/lib/provisioning/orchestrator"
[executor]
max_concurrent_tasks = 10
task_timeout_seconds = 3600
checkpoint_interval_seconds = 30
[nushell]
binary = "nu" # Expects nu in PATH
provisioning_lib = "/usr/local/lib/provisioning"
env_vars = { NU_LIB_DIRS = "/usr/local/lib/provisioning" }
</code></pre>
<hr />
<h2 id="key-takeaways"><a class="header" href="#key-takeaways">Key Takeaways</a></h2>
<h3 id="1-orchestrator-is-essential"><a class="header" href="#1-orchestrator-is-essential">1. <strong>Orchestrator is Essential</strong></a></h3>
<ul>
<li>Solves deep call stack problems</li>
<li>Provides performance optimization</li>
<li>Enables complex workflows</li>
<li>NOT optional for production use</li>
</ul>
<h3 id="2-integration-is-loose-but-coordinated"><a class="header" href="#2-integration-is-loose-but-coordinated">2. <strong>Integration is Loose but Coordinated</strong></a></h3>
<ul>
<li>No code dependencies between repos</li>
<li>Runtime integration via CLI + REST API</li>
<li>Configuration-driven coordination</li>
<li>Works in both monorepo and multi-repo</li>
</ul>
<h3 id="3-best-of-both-worlds"><a class="header" href="#3-best-of-both-worlds">3. <strong>Best of Both Worlds</strong></a></h3>
<ul>
<li>Rust: High-performance coordination</li>
<li>Nushell: Flexible business logic</li>
<li>Clean separation of concerns</li>
<li>Each technology does what its best at</li>
</ul>
<h3 id="4-multi-repo-doesnt-change-integration"><a class="header" href="#4-multi-repo-doesnt-change-integration">4. <strong>Multi-Repo Doesnt Change Integration</strong></a></h3>
<ul>
<li>Same runtime model as monorepo</li>
<li>Package installation sets up paths</li>
<li>Configuration enables discovery</li>
<li>Versioning ensures compatibility</li>
</ul>
<hr />
<h2 id="conclusion"><a class="header" href="#conclusion">Conclusion</a></h2>
<p>The confusing example in the multi-repo doc was <strong>oversimplified</strong>. The real architecture is:</p>
<pre><code>✅ Orchestrator IS USED and IS ESSENTIAL
✅ Platform (Rust) coordinates Core (Nushell) execution
✅ Loose coupling via CLI + REST API (not code dependencies)
✅ Works identically in monorepo and multi-repo
✅ Configuration-based integration (no hardcoded paths)
</code></pre>
<p>The orchestrator provides:</p>
<ul>
<li>Performance layer (async, parallel execution)</li>
<li>Workflow engine (complex dependencies)</li>
<li>State management (checkpoints, recovery)</li>
<li>Task queue (reliable execution)</li>
</ul>
<p>While Nushell provides:</p>
<ul>
<li>Business logic (providers, taskservs, clusters)</li>
<li>Template rendering (Jinja2 via nu_plugin_tera)</li>
<li>Configuration management (KCL integration)</li>
<li>User-facing scripting</li>
</ul>
<p><strong>Multi-repo just splits WHERE the code lives, not HOW it works together.</strong></p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../architecture/multi-repo-strategy.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../architecture/orchestrator_info.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../architecture/multi-repo-strategy.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../architecture/orchestrator_info.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<!-- Livereload script (if served using the cli tool) -->
<script>
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
const socket = new WebSocket(wsAddress);
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload();
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>