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.
930 lines
39 KiB
HTML
930 lines
39 KiB
HTML
<!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<Arc<AppState>>,
|
||
Json(request): Json<ServerCreateRequest>,
|
||
) -> Result<Json<WorkflowResponse>, ApiError> {
|
||
// Create task
|
||
let task = Task {
|
||
id: Uuid::new_v4(),
|
||
task_type: TaskType::ServerCreate,
|
||
payload: serde_json::to_value(&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 <id>
|
||
</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) -> Result<TaskResult> {
|
||
match task.task_type {
|
||
TaskType::ServerCreate => {
|
||
// 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(&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 <id>
|
||
</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) -> Result<TaskResult> {
|
||
let mut child = Command::new("nu")
|
||
.arg("-c")
|
||
.arg(&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(&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 Doesn’t 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 <id>
|
||
</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(&self, script: &str) -> Result<Output> {
|
||
Command::new(&self.nu_binary)
|
||
.env("NU_LIB_DIRS", &self.provisioning_lib)
|
||
.arg("-c")
|
||
.arg(script)
|
||
.output()
|
||
.await
|
||
}
|
||
|
||
pub async fn execute_module_function(
|
||
&self,
|
||
module: &str,
|
||
function: &str,
|
||
args: &[String],
|
||
) -> Result<Output> {
|
||
let script = format!(
|
||
"use {}/{}; {} {}",
|
||
self.provisioning_lib.display(),
|
||
module,
|
||
function,
|
||
args.join(" ")
|
||
);
|
||
|
||
self.execute_script(&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 it’s 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 Doesn’t 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>
|