provisioning/docs/book/development/integration.html

1321 lines
54 KiB
HTML
Raw Normal View History

<!DOCTYPE HTML>
<html lang="en" class="ayu sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Integration - 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/development/integration.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="integration-guide"><a class="header" href="#integration-guide">Integration Guide</a></h1>
<p>This document explains how the new project structure integrates with existing systems, API compatibility and versioning, database migration strategies, deployment considerations, and monitoring and observability.</p>
<h2 id="table-of-contents"><a class="header" href="#table-of-contents">Table of Contents</a></h2>
<ol>
<li><a href="#overview">Overview</a></li>
<li><a href="#existing-system-integration">Existing System Integration</a></li>
<li><a href="#api-compatibility-and-versioning">API Compatibility and Versioning</a></li>
<li><a href="#database-migration-strategies">Database Migration Strategies</a></li>
<li><a href="#deployment-considerations">Deployment Considerations</a></li>
<li><a href="#monitoring-and-observability">Monitoring and Observability</a></li>
<li><a href="#legacy-system-bridge">Legacy System Bridge</a></li>
<li><a href="#migration-pathways">Migration Pathways</a></li>
<li><a href="#troubleshooting-integration-issues">Troubleshooting Integration Issues</a></li>
</ol>
<h2 id="overview"><a class="header" href="#overview">Overview</a></h2>
<p>Provisioning has been designed with integration as a core principle, ensuring seamless compatibility between new development-focused components and existing production systems while providing clear migration pathways.</p>
<p><strong>Integration Principles</strong>:</p>
<ul>
<li><strong>Backward Compatibility</strong>: All existing APIs and interfaces remain functional</li>
<li><strong>Gradual Migration</strong>: Systems can be migrated incrementally without disruption</li>
<li><strong>Dual Operation</strong>: New and legacy systems operate side-by-side during transition</li>
<li><strong>Zero Downtime</strong>: Migrations occur without service interruption</li>
<li><strong>Data Integrity</strong>: All data migrations are atomic and reversible</li>
</ul>
<p><strong>Integration Architecture</strong>:</p>
<pre><code>Integration Ecosystem
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Legacy Core │ ←→ │ Bridge Layer │ ←→ │ New Systems │
│ │ │ │ │ │
│ - ENV config │ │ - Compatibility │ │ - TOML config │
│ - Direct calls │ │ - Translation │ │ - Orchestrator │
│ - File-based │ │ - Monitoring │ │ - Workflows │
│ - Simple logging│ │ - Validation │ │ - REST APIs │
└─────────────────┘ └─────────────────┘ └─────────────────┘
</code></pre>
<h2 id="existing-system-integration"><a class="header" href="#existing-system-integration">Existing System Integration</a></h2>
<h3 id="command-line-interface-integration"><a class="header" href="#command-line-interface-integration">Command-Line Interface Integration</a></h3>
<p><strong>Seamless CLI Compatibility</strong>:</p>
<pre><code class="language-bash"># All existing commands continue to work unchanged
./core/nulib/provisioning server create web-01 2xCPU-4GB
./core/nulib/provisioning taskserv install kubernetes
./core/nulib/provisioning cluster create buildkit
# New commands available alongside existing ones
./src/core/nulib/provisioning server create web-01 2xCPU-4GB --orchestrated
nu workspace/tools/workspace.nu health --detailed
</code></pre>
<p><strong>Path Resolution Integration</strong>:</p>
<pre><code class="language-nushell"># Automatic path resolution between systems
use workspace/lib/path-resolver.nu
# Resolves to workspace path if available, falls back to core
let config_path = (path-resolver resolve_path "config" "user" --fallback-to-core)
# Seamless extension discovery
let provider_path = (path-resolver resolve_extension "providers" "upcloud")
</code></pre>
<h3 id="configuration-system-bridge"><a class="header" href="#configuration-system-bridge">Configuration System Bridge</a></h3>
<p><strong>Dual Configuration Support</strong>:</p>
<pre><code class="language-nushell"># Configuration bridge supports both ENV and TOML
def get-config-value-bridge [key: string, default: string = ""] -&gt; string {
# Try new TOML configuration first
let toml_value = try {
get-config-value $key
} catch { null }
if $toml_value != null {
return $toml_value
}
# Fall back to ENV variable (legacy support)
let env_key = ($key | str replace "." "_" | str upcase | $"PROVISIONING_($in)")
let env_value = ($env | get $env_key | default null)
if $env_value != null {
return $env_value
}
# Use default if provided
if $default != "" {
return $default
}
# Error with helpful migration message
error make {
msg: $"Configuration not found: ($key)",
help: $"Migrate from ($env_key) environment variable to ($key) in config file"
}
}
</code></pre>
<h3 id="data-integration"><a class="header" href="#data-integration">Data Integration</a></h3>
<p><strong>Shared Data Access</strong>:</p>
<pre><code class="language-nushell"># Unified data access across old and new systems
def get-server-info [server_name: string] -&gt; record {
# Try new orchestrator data store first
let orchestrator_data = try {
get-orchestrator-server-data $server_name
} catch { null }
if $orchestrator_data != null {
return $orchestrator_data
}
# Fall back to legacy file-based storage
let legacy_data = try {
get-legacy-server-data $server_name
} catch { null }
if $legacy_data != null {
return ($legacy_data | migrate-to-new-format)
}
error make {msg: $"Server not found: ($server_name)"}
}
</code></pre>
<h3 id="process-integration"><a class="header" href="#process-integration">Process Integration</a></h3>
<p><strong>Hybrid Process Management</strong>:</p>
<pre><code class="language-nushell"># Orchestrator-aware process management
def create-server-integrated [
name: string,
plan: string,
--orchestrated: bool = false
] -&gt; record {
if $orchestrated and (check-orchestrator-available) {
# Use new orchestrator workflow
return (create-server-workflow $name $plan)
} else {
# Use legacy direct creation
return (create-server-direct $name $plan)
}
}
def check-orchestrator-available [] -&gt; bool {
try {
http get "http://localhost:9090/health" | get status == "ok"
} catch {
false
}
}
</code></pre>
<h2 id="api-compatibility-and-versioning"><a class="header" href="#api-compatibility-and-versioning">API Compatibility and Versioning</a></h2>
<h3 id="rest-api-versioning"><a class="header" href="#rest-api-versioning">REST API Versioning</a></h3>
<p><strong>API Version Strategy</strong>:</p>
<ul>
<li><strong>v1</strong>: Legacy compatibility API (existing functionality)</li>
<li><strong>v2</strong>: Enhanced API with orchestrator features</li>
<li><strong>v3</strong>: Full workflow and batch operation support</li>
</ul>
<p><strong>Version Header Support</strong>:</p>
<pre><code class="language-bash"># API calls with version specification
curl -H "API-Version: v1" http://localhost:9090/servers
curl -H "API-Version: v2" http://localhost:9090/workflows/servers/create
curl -H "API-Version: v3" http://localhost:9090/workflows/batch/submit
</code></pre>
<h3 id="api-compatibility-layer"><a class="header" href="#api-compatibility-layer">API Compatibility Layer</a></h3>
<p><strong>Backward Compatible Endpoints</strong>:</p>
<pre><code class="language-rust">// Rust API compatibility layer
#[derive(Debug, Serialize, Deserialize)]
struct ApiRequest {
version: Option&lt;String&gt;,
#[serde(flatten)]
payload: serde_json::Value,
}
async fn handle_versioned_request(
headers: HeaderMap,
req: ApiRequest,
) -&gt; Result&lt;ApiResponse, ApiError&gt; {
let api_version = headers
.get("API-Version")
.and_then(|v| v.to_str().ok())
.unwrap_or("v1");
match api_version {
"v1" =&gt; handle_v1_request(req.payload).await,
"v2" =&gt; handle_v2_request(req.payload).await,
"v3" =&gt; handle_v3_request(req.payload).await,
_ =&gt; Err(ApiError::UnsupportedVersion(api_version.to_string())),
}
}
// V1 compatibility endpoint
async fn handle_v1_request(payload: serde_json::Value) -&gt; Result&lt;ApiResponse, ApiError&gt; {
// Transform request to legacy format
let legacy_request = transform_to_legacy_format(payload)?;
// Execute using legacy system
let result = execute_legacy_operation(legacy_request).await?;
// Transform response to v1 format
Ok(transform_to_v1_response(result))
}</code></pre>
<h3 id="schema-evolution"><a class="header" href="#schema-evolution">Schema Evolution</a></h3>
<p><strong>Backward Compatible Schema Changes</strong>:</p>
<pre><code class="language-kcl"># API schema with version support
schema ServerCreateRequest {
# V1 fields (always supported)
name: str
plan: str
zone?: str = "auto"
# V2 additions (optional for backward compatibility)
orchestrated?: bool = false
workflow_options?: WorkflowOptions
# V3 additions
batch_options?: BatchOptions
dependencies?: [str] = []
# Version constraints
api_version?: str = "v1"
check:
len(name) &gt; 0, "Name cannot be empty"
plan in ["1xCPU-2GB", "2xCPU-4GB", "4xCPU-8GB", "8xCPU-16GB"], "Invalid plan"
}
# Conditional validation based on API version
schema WorkflowOptions:
wait_for_completion?: bool = true
timeout_seconds?: int = 300
retry_count?: int = 3
check:
timeout_seconds &gt; 0, "Timeout must be positive"
retry_count &gt;= 0, "Retry count must be non-negative"
</code></pre>
<h3 id="client-sdk-compatibility"><a class="header" href="#client-sdk-compatibility">Client SDK Compatibility</a></h3>
<p><strong>Multi-Version Client Support</strong>:</p>
<pre><code class="language-nushell"># Nushell client with version support
def "client create-server" [
name: string,
plan: string,
--api-version: string = "v1",
--orchestrated: bool = false
] -&gt; record {
let endpoint = match $api_version {
"v1" =&gt; "/servers",
"v2" =&gt; "/workflows/servers/create",
"v3" =&gt; "/workflows/batch/submit",
_ =&gt; (error make {msg: $"Unsupported API version: ($api_version)"})
}
let request_body = match $api_version {
"v1" =&gt; {name: $name, plan: $plan},
"v2" =&gt; {name: $name, plan: $plan, orchestrated: $orchestrated},
"v3" =&gt; {
operations: [{
id: "create_server",
type: "server_create",
config: {name: $name, plan: $plan}
}]
},
_ =&gt; (error make {msg: $"Unsupported API version: ($api_version)"})
}
http post $"http://localhost:9090($endpoint)" $request_body
--headers {
"Content-Type": "application/json",
"API-Version": $api_version
}
}
</code></pre>
<h2 id="database-migration-strategies"><a class="header" href="#database-migration-strategies">Database Migration Strategies</a></h2>
<h3 id="database-architecture-evolution"><a class="header" href="#database-architecture-evolution">Database Architecture Evolution</a></h3>
<p><strong>Migration Strategy</strong>:</p>
<pre><code>Database Evolution Path
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ File-based │ → │ SQLite │ → │ SurrealDB │
│ Storage │ │ Migration │ │ Full Schema │
│ │ │ │ │ │
│ - JSON files │ │ - Structured │ │ - Graph DB │
│ - Text logs │ │ - Transactions │ │ - Real-time │
│ - Simple state │ │ - Backup/restore│ │ - Clustering │
└─────────────────┘ └─────────────────┘ └─────────────────┘
</code></pre>
<h3 id="migration-scripts"><a class="header" href="#migration-scripts">Migration Scripts</a></h3>
<p><strong>Automated Database Migration</strong>:</p>
<pre><code class="language-nushell"># Database migration orchestration
def migrate-database [
--from: string = "filesystem",
--to: string = "surrealdb",
--backup-first: bool = true,
--verify: bool = true
] -&gt; record {
if $backup_first {
print "Creating backup before migration..."
let backup_result = (create-database-backup $from)
print $"Backup created: ($backup_result.path)"
}
print $"Migrating from ($from) to ($to)..."
match [$from, $to] {
["filesystem", "sqlite"] =&gt; migrate_filesystem_to_sqlite,
["filesystem", "surrealdb"] =&gt; migrate_filesystem_to_surrealdb,
["sqlite", "surrealdb"] =&gt; migrate_sqlite_to_surrealdb,
_ =&gt; (error make {msg: $"Unsupported migration path: ($from) → ($to)"})
}
if $verify {
print "Verifying migration integrity..."
let verification = (verify-migration $from $to)
if not $verification.success {
error make {
msg: $"Migration verification failed: ($verification.errors)",
help: "Restore from backup and retry migration"
}
}
}
print $"Migration from ($from) to ($to) completed successfully"
{from: $from, to: $to, status: "completed", migrated_at: (date now)}
}
</code></pre>
<p><strong>File System to SurrealDB Migration</strong>:</p>
<pre><code class="language-nushell">def migrate_filesystem_to_surrealdb [] -&gt; record {
# Initialize SurrealDB connection
let db = (connect-surrealdb)
# Migrate server data
let server_files = (ls data/servers/*.json)
let migrated_servers = []
for server_file in $server_files {
let server_data = (open $server_file.name | from json)
# Transform to new schema
let server_record = {
id: $server_data.id,
name: $server_data.name,
plan: $server_data.plan,
zone: ($server_data.zone? | default "unknown"),
status: $server_data.status,
ip_address: $server_data.ip_address?,
created_at: $server_data.created_at,
updated_at: (date now),
metadata: ($server_data.metadata? | default {}),
tags: ($server_data.tags? | default [])
}
# Insert into SurrealDB
let insert_result = try {
query-surrealdb $"CREATE servers:($server_record.id) CONTENT ($server_record | to json)"
} catch { |e|
print $"Warning: Failed to migrate server ($server_data.name): ($e.msg)"
}
$migrated_servers = ($migrated_servers | append $server_record.id)
}
# Migrate workflow data
migrate_workflows_to_surrealdb $db
# Migrate state data
migrate_state_to_surrealdb $db
{
migrated_servers: ($migrated_servers | length),
migrated_workflows: (migrate_workflows_to_surrealdb $db).count,
status: "completed"
}
}
</code></pre>
<h3 id="data-integrity-verification"><a class="header" href="#data-integrity-verification">Data Integrity Verification</a></h3>
<p><strong>Migration Verification</strong>:</p>
<pre><code class="language-nushell">def verify-migration [from: string, to: string] -&gt; record {
print "Verifying data integrity..."
let source_data = (read-source-data $from)
let target_data = (read-target-data $to)
let errors = []
# Verify record counts
if $source_data.servers.count != $target_data.servers.count {
$errors = ($errors | append "Server count mismatch")
}
# Verify key records
for server in $source_data.servers {
let target_server = ($target_data.servers | where id == $server.id | first)
if ($target_server | is-empty) {
$errors = ($errors | append $"Missing server: ($server.id)")
} else {
# Verify critical fields
if $target_server.name != $server.name {
$errors = ($errors | append $"Name mismatch for server ($server.id)")
}
if $target_server.status != $server.status {
$errors = ($errors | append $"Status mismatch for server ($server.id)")
}
}
}
{
success: ($errors | length) == 0,
errors: $errors,
verified_at: (date now)
}
}
</code></pre>
<h2 id="deployment-considerations"><a class="header" href="#deployment-considerations">Deployment Considerations</a></h2>
<h3 id="deployment-architecture"><a class="header" href="#deployment-architecture">Deployment Architecture</a></h3>
<p><strong>Hybrid Deployment Model</strong>:</p>
<pre><code>Deployment Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Load Balancer / Reverse Proxy │
└─────────────────────┬───────────────────────────────────────────┘
┌─────────────────┼─────────────────┐
│ │ │
┌───▼────┐ ┌─────▼─────┐ ┌───▼────┐
│Legacy │ │Orchestrator│ │New │
│System │ ←→ │Bridge │ ←→ │Systems │
│ │ │ │ │ │
│- CLI │ │- API Gate │ │- REST │
│- Files │ │- Compat │ │- DB │
│- Logs │ │- Monitor │ │- Queue │
└────────┘ └────────────┘ └────────┘
</code></pre>
<h3 id="deployment-strategies"><a class="header" href="#deployment-strategies">Deployment Strategies</a></h3>
<p><strong>Blue-Green Deployment</strong>:</p>
<pre><code class="language-bash"># Blue-Green deployment with integration bridge
# Phase 1: Deploy new system alongside existing (Green environment)
cd src/tools
make all
make create-installers
# Install new system without disrupting existing
./packages/installers/install-provisioning-2.0.0.sh \
--install-path /opt/provisioning-v2 \
--no-replace-existing \
--enable-bridge-mode
# Phase 2: Start orchestrator and validate integration
/opt/provisioning-v2/bin/orchestrator start --bridge-mode --legacy-path /opt/provisioning-v1
# Phase 3: Gradual traffic shift
# Route 10% traffic to new system
nginx-traffic-split --new-backend 10%
# Validate metrics and gradually increase
nginx-traffic-split --new-backend 50%
nginx-traffic-split --new-backend 90%
# Phase 4: Complete cutover
nginx-traffic-split --new-backend 100%
/opt/provisioning-v1/bin/orchestrator stop
</code></pre>
<p><strong>Rolling Update</strong>:</p>
<pre><code class="language-nushell">def rolling-deployment [
--target-version: string,
--batch-size: int = 3,
--health-check-interval: duration = 30sec
] -&gt; record {
let nodes = (get-deployment-nodes)
let batches = ($nodes | group_by --chunk-size $batch_size)
let deployment_results = []
for batch in $batches {
print $"Deploying to batch: ($batch | get name | str join ', ')"
# Deploy to batch
for node in $batch {
deploy-to-node $node $target_version
}
# Wait for health checks
sleep $health_check_interval
# Verify batch health
let batch_health = ($batch | each { |node| check-node-health $node })
let healthy_nodes = ($batch_health | where healthy == true | length)
if $healthy_nodes != ($batch | length) {
# Rollback batch on failure
print $"Health check failed, rolling back batch"
for node in $batch {
rollback-node $node
}
error make {msg: "Rolling deployment failed at batch"}
}
print $"Batch deployed successfully"
$deployment_results = ($deployment_results | append {
batch: $batch,
status: "success",
deployed_at: (date now)
})
}
{
strategy: "rolling",
target_version: $target_version,
batches: ($deployment_results | length),
status: "completed",
completed_at: (date now)
}
}
</code></pre>
<h3 id="configuration-deployment"><a class="header" href="#configuration-deployment">Configuration Deployment</a></h3>
<p><strong>Environment-Specific Deployment</strong>:</p>
<pre><code class="language-bash"># Development deployment
PROVISIONING_ENV=dev ./deploy.sh \
--config-source config.dev.toml \
--enable-debug \
--enable-hot-reload
# Staging deployment
PROVISIONING_ENV=staging ./deploy.sh \
--config-source config.staging.toml \
--enable-monitoring \
--backup-before-deploy
# Production deployment
PROVISIONING_ENV=prod ./deploy.sh \
--config-source config.prod.toml \
--zero-downtime \
--enable-all-monitoring \
--backup-before-deploy \
--health-check-timeout 5m
</code></pre>
<h3 id="container-integration"><a class="header" href="#container-integration">Container Integration</a></h3>
<p><strong>Docker Deployment with Bridge</strong>:</p>
<pre><code class="language-dockerfile"># Multi-stage Docker build supporting both systems
FROM rust:1.70 as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM ubuntu:22.04 as runtime
WORKDIR /app
# Install both legacy and new systems
COPY --from=builder /app/target/release/orchestrator /app/bin/
COPY legacy-provisioning/ /app/legacy/
COPY config/ /app/config/
# Bridge script for dual operation
COPY bridge-start.sh /app/bin/
ENV PROVISIONING_BRIDGE_MODE=true
ENV PROVISIONING_LEGACY_PATH=/app/legacy
ENV PROVISIONING_NEW_PATH=/app/bin
EXPOSE 8080
CMD ["/app/bin/bridge-start.sh"]
</code></pre>
<p><strong>Kubernetes Integration</strong>:</p>
<pre><code class="language-yaml"># Kubernetes deployment with bridge sidecar
apiVersion: apps/v1
kind: Deployment
metadata:
name: provisioning-system
spec:
replicas: 3
template:
spec:
containers:
- name: orchestrator
image: provisioning-system:2.0.0
ports:
- containerPort: 8080
env:
- name: PROVISIONING_BRIDGE_MODE
value: "true"
volumeMounts:
- name: config
mountPath: /app/config
- name: legacy-data
mountPath: /app/legacy/data
- name: legacy-bridge
image: provisioning-legacy:1.0.0
env:
- name: BRIDGE_ORCHESTRATOR_URL
value: "http://localhost:9090"
volumeMounts:
- name: legacy-data
mountPath: /data
volumes:
- name: config
configMap:
name: provisioning-config
- name: legacy-data
persistentVolumeClaim:
claimName: provisioning-data
</code></pre>
<h2 id="monitoring-and-observability"><a class="header" href="#monitoring-and-observability">Monitoring and Observability</a></h2>
<h3 id="integrated-monitoring-architecture"><a class="header" href="#integrated-monitoring-architecture">Integrated Monitoring Architecture</a></h3>
<p><strong>Monitoring Stack Integration</strong>:</p>
<pre><code>Observability Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Monitoring Dashboard │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Grafana │ │ Jaeger │ │ AlertMgr │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────┬───────────────┬───────────────┬─────────────────┘
│ │ │
┌──────────▼──────────┐ │ ┌───────────▼───────────┐
│ Prometheus │ │ │ Jaeger │
│ (Metrics) │ │ │ (Tracing) │
└──────────┬──────────┘ │ └───────────┬───────────┘
│ │ │
┌─────────────▼─────────────┐ │ ┌─────────────▼─────────────┐
│ Legacy │ │ │ New System │
│ Monitoring │ │ │ Monitoring │
│ │ │ │ │
│ - File-based logs │ │ │ - Structured logs │
│ - Simple metrics │ │ │ - Prometheus metrics │
│ - Basic health checks │ │ │ - Distributed tracing │
└───────────────────────────┘ │ └───────────────────────────┘
┌─────────▼─────────┐
│ Bridge Monitor │
│ │
│ - Integration │
│ - Compatibility │
│ - Migration │
└───────────────────┘
</code></pre>
<h3 id="metrics-integration"><a class="header" href="#metrics-integration">Metrics Integration</a></h3>
<p><strong>Unified Metrics Collection</strong>:</p>
<pre><code class="language-nushell"># Metrics bridge for legacy and new systems
def collect-system-metrics [] -&gt; record {
let legacy_metrics = collect-legacy-metrics
let new_metrics = collect-new-metrics
let bridge_metrics = collect-bridge-metrics
{
timestamp: (date now),
legacy: $legacy_metrics,
new: $new_metrics,
bridge: $bridge_metrics,
integration: {
compatibility_rate: (calculate-compatibility-rate $bridge_metrics),
migration_progress: (calculate-migration-progress),
system_health: (assess-overall-health $legacy_metrics $new_metrics)
}
}
}
def collect-legacy-metrics [] -&gt; record {
let log_files = (ls logs/*.log)
let process_stats = (get-process-stats "legacy-provisioning")
{
active_processes: $process_stats.count,
log_file_sizes: ($log_files | get size | math sum),
last_activity: (get-last-log-timestamp),
error_count: (count-log-errors "last 1h"),
performance: {
avg_response_time: (calculate-avg-response-time),
throughput: (calculate-throughput)
}
}
}
def collect-new-metrics [] -&gt; record {
let orchestrator_stats = try {
http get "http://localhost:9090/metrics"
} catch {
{status: "unavailable"}
}
{
orchestrator: $orchestrator_stats,
workflow_stats: (get-workflow-metrics),
api_stats: (get-api-metrics),
database_stats: (get-database-metrics)
}
}
</code></pre>
<h3 id="logging-integration"><a class="header" href="#logging-integration">Logging Integration</a></h3>
<p><strong>Unified Logging Strategy</strong>:</p>
<pre><code class="language-nushell"># Structured logging bridge
def log-integrated [
level: string,
message: string,
--component: string = "bridge",
--legacy-compat: bool = true
] {
let log_entry = {
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S%.3f"),
level: $level,
component: $component,
message: $message,
system: "integrated",
correlation_id: (generate-correlation-id)
}
# Write to structured log (new system)
$log_entry | to json | save --append logs/integrated.jsonl
if $legacy_compat {
# Write to legacy log format
let legacy_entry = $"[($log_entry.timestamp)] [($level)] ($component): ($message)"
$legacy_entry | save --append logs/legacy.log
}
# Send to monitoring system
send-to-monitoring $log_entry
}
</code></pre>
<h3 id="health-check-integration"><a class="header" href="#health-check-integration">Health Check Integration</a></h3>
<p><strong>Comprehensive Health Monitoring</strong>:</p>
<pre><code class="language-nushell">def health-check-integrated [] -&gt; record {
let health_checks = [
{name: "legacy-system", check: (check-legacy-health)},
{name: "orchestrator", check: (check-orchestrator-health)},
{name: "database", check: (check-database-health)},
{name: "bridge-compatibility", check: (check-bridge-health)},
{name: "configuration", check: (check-config-health)}
]
let results = ($health_checks | each { |check|
let result = try {
do $check.check
} catch { |e|
{status: "unhealthy", error: $e.msg}
}
{name: $check.name, result: $result}
})
let healthy_count = ($results | where result.status == "healthy" | length)
let total_count = ($results | length)
{
overall_status: (if $healthy_count == $total_count { "healthy" } else { "degraded" }),
healthy_services: $healthy_count,
total_services: $total_count,
services: $results,
checked_at: (date now)
}
}
</code></pre>
<h2 id="legacy-system-bridge"><a class="header" href="#legacy-system-bridge">Legacy System Bridge</a></h2>
<h3 id="bridge-architecture"><a class="header" href="#bridge-architecture">Bridge Architecture</a></h3>
<p><strong>Bridge Component Design</strong>:</p>
<pre><code class="language-nushell"># Legacy system bridge module
export module bridge {
# Bridge state management
export def init-bridge [] -&gt; record {
let bridge_config = get-config-section "bridge"
{
legacy_path: ($bridge_config.legacy_path? | default "/opt/provisioning-v1"),
new_path: ($bridge_config.new_path? | default "/opt/provisioning-v2"),
mode: ($bridge_config.mode? | default "compatibility"),
monitoring_enabled: ($bridge_config.monitoring? | default true),
initialized_at: (date now)
}
}
# Command translation layer
export def translate-command [
legacy_command: list&lt;string&gt;
] -&gt; list&lt;string&gt; {
match $legacy_command {
["provisioning", "server", "create", $name, $plan, ...$args] =&gt; {
let new_args = ($args | each { |arg|
match $arg {
"--dry-run" =&gt; "--dry-run",
"--wait" =&gt; "--wait",
$zone if ($zone | str starts-with "--zone=") =&gt; $zone,
_ =&gt; $arg
}
})
["provisioning", "server", "create", $name, $plan] ++ $new_args ++ ["--orchestrated"]
},
_ =&gt; $legacy_command # Pass through unchanged
}
}
# Data format translation
export def translate-response [
legacy_response: record,
target_format: string = "v2"
] -&gt; record {
match $target_format {
"v2" =&gt; {
id: ($legacy_response.id? | default (generate-uuid)),
name: $legacy_response.name,
status: $legacy_response.status,
created_at: ($legacy_response.created_at? | default (date now)),
metadata: ($legacy_response | reject name status created_at),
version: "v2-compat"
},
_ =&gt; $legacy_response
}
}
}
</code></pre>
<h3 id="bridge-operation-modes"><a class="header" href="#bridge-operation-modes">Bridge Operation Modes</a></h3>
<p><strong>Compatibility Mode</strong>:</p>
<pre><code class="language-nushell"># Full compatibility with legacy system
def run-compatibility-mode [] {
print "Starting bridge in compatibility mode..."
# Intercept legacy commands
let legacy_commands = monitor-legacy-commands
for command in $legacy_commands {
let translated = (bridge translate-command $command)
try {
let result = (execute-new-system $translated)
let legacy_result = (bridge translate-response $result "v1")
respond-to-legacy $legacy_result
} catch { |e|
# Fall back to legacy system on error
let fallback_result = (execute-legacy-system $command)
respond-to-legacy $fallback_result
}
}
}
</code></pre>
<p><strong>Migration Mode</strong>:</p>
<pre><code class="language-nushell"># Gradual migration with traffic splitting
def run-migration-mode [
--new-system-percentage: int = 50
] {
print $"Starting bridge in migration mode (($new_system_percentage)% new system)"
let commands = monitor-all-commands
for command in $commands {
let route_to_new = ((random integer 1..100) &lt;= $new_system_percentage)
if $route_to_new {
try {
execute-new-system $command
} catch {
# Fall back to legacy on failure
execute-legacy-system $command
}
} else {
execute-legacy-system $command
}
}
}
</code></pre>
<h2 id="migration-pathways"><a class="header" href="#migration-pathways">Migration Pathways</a></h2>
<h3 id="migration-phases"><a class="header" href="#migration-phases">Migration Phases</a></h3>
<p><strong>Phase 1: Parallel Deployment</strong></p>
<ul>
<li>Deploy new system alongside existing</li>
<li>Enable bridge for compatibility</li>
<li>Begin data synchronization</li>
<li>Monitor integration health</li>
</ul>
<p><strong>Phase 2: Gradual Migration</strong></p>
<ul>
<li>Route increasing traffic to new system</li>
<li>Migrate data in background</li>
<li>Validate consistency</li>
<li>Address integration issues</li>
</ul>
<p><strong>Phase 3: Full Migration</strong></p>
<ul>
<li>Complete traffic cutover</li>
<li>Decommission legacy system</li>
<li>Clean up bridge components</li>
<li>Finalize data migration</li>
</ul>
<h3 id="migration-automation"><a class="header" href="#migration-automation">Migration Automation</a></h3>
<p><strong>Automated Migration Orchestration</strong>:</p>
<pre><code class="language-nushell">def execute-migration-plan [
migration_plan: string,
--dry-run: bool = false,
--skip-backup: bool = false
] -&gt; record {
let plan = (open $migration_plan | from yaml)
if not $skip_backup {
create-pre-migration-backup
}
let migration_results = []
for phase in $plan.phases {
print $"Executing migration phase: ($phase.name)"
if $dry_run {
print $"[DRY RUN] Would execute phase: ($phase)"
continue
}
let phase_result = try {
execute-migration-phase $phase
} catch { |e|
print $"Migration phase failed: ($e.msg)"
if $phase.rollback_on_failure? | default false {
print "Rolling back migration phase..."
rollback-migration-phase $phase
}
error make {msg: $"Migration failed at phase ($phase.name): ($e.msg)"}
}
$migration_results = ($migration_results | append $phase_result)
# Wait between phases if specified
if "wait_seconds" in $phase {
sleep ($phase.wait_seconds * 1sec)
}
}
{
migration_plan: $migration_plan,
phases_completed: ($migration_results | length),
status: "completed",
completed_at: (date now),
results: $migration_results
}
}
</code></pre>
<p><strong>Migration Validation</strong>:</p>
<pre><code class="language-nushell">def validate-migration-readiness [] -&gt; record {
let checks = [
{name: "backup-available", check: (check-backup-exists)},
{name: "new-system-healthy", check: (check-new-system-health)},
{name: "database-accessible", check: (check-database-connectivity)},
{name: "configuration-valid", check: (validate-migration-config)},
{name: "resources-available", check: (check-system-resources)},
{name: "network-connectivity", check: (check-network-health)}
]
let results = ($checks | each { |check|
{
name: $check.name,
result: (do $check.check),
timestamp: (date now)
}
})
let failed_checks = ($results | where result.status != "ready")
{
ready_for_migration: ($failed_checks | length) == 0,
checks: $results,
failed_checks: $failed_checks,
validated_at: (date now)
}
}
</code></pre>
<h2 id="troubleshooting-integration-issues"><a class="header" href="#troubleshooting-integration-issues">Troubleshooting Integration Issues</a></h2>
<h3 id="common-integration-problems"><a class="header" href="#common-integration-problems">Common Integration Problems</a></h3>
<h4 id="api-compatibility-issues"><a class="header" href="#api-compatibility-issues">API Compatibility Issues</a></h4>
<p><strong>Problem</strong>: Version mismatch between client and server</p>
<pre><code class="language-bash"># Diagnosis
curl -H "API-Version: v1" http://localhost:9090/health
curl -H "API-Version: v2" http://localhost:9090/health
# Solution: Check supported versions
curl http://localhost:9090/api/versions
# Update client API version
export PROVISIONING_API_VERSION=v2
</code></pre>
<h4 id="configuration-bridge-issues"><a class="header" href="#configuration-bridge-issues">Configuration Bridge Issues</a></h4>
<p><strong>Problem</strong>: Configuration not found in either system</p>
<pre><code class="language-nushell"># Diagnosis
def diagnose-config-issue [key: string] -&gt; record {
let toml_result = try {
get-config-value $key
} catch { |e| {status: "failed", error: $e.msg} }
let env_key = ($key | str replace "." "_" | str upcase | $"PROVISIONING_($in)")
let env_result = try {
$env | get $env_key
} catch { |e| {status: "failed", error: $e.msg} }
{
key: $key,
toml_config: $toml_result,
env_config: $env_result,
migration_needed: ($toml_result.status == "failed" and $env_result.status != "failed")
}
}
# Solution: Migrate configuration
def migrate-single-config [key: string] {
let diagnosis = (diagnose-config-issue $key)
if $diagnosis.migration_needed {
let env_value = $diagnosis.env_config
set-config-value $key $env_value
print $"Migrated ($key) from environment variable"
}
}
</code></pre>
<h4 id="database-integration-issues"><a class="header" href="#database-integration-issues">Database Integration Issues</a></h4>
<p><strong>Problem</strong>: Data inconsistency between systems</p>
<pre><code class="language-nushell"># Diagnosis and repair
def repair-data-consistency [] -&gt; record {
let legacy_data = (read-legacy-data)
let new_data = (read-new-data)
let inconsistencies = []
# Check server records
for server in $legacy_data.servers {
let new_server = ($new_data.servers | where id == $server.id | first)
if ($new_server | is-empty) {
print $"Missing server in new system: ($server.id)"
create-server-record $server
$inconsistencies = ($inconsistencies | append {type: "missing", id: $server.id})
} else if $new_server != $server {
print $"Inconsistent server data: ($server.id)"
update-server-record $server
$inconsistencies = ($inconsistencies | append {type: "inconsistent", id: $server.id})
}
}
{
inconsistencies_found: ($inconsistencies | length),
repairs_applied: ($inconsistencies | length),
repaired_at: (date now)
}
}
</code></pre>
<h3 id="debug-tools"><a class="header" href="#debug-tools">Debug Tools</a></h3>
<p><strong>Integration Debug Mode</strong>:</p>
<pre><code class="language-bash"># Enable comprehensive debugging
export PROVISIONING_DEBUG=true
export PROVISIONING_LOG_LEVEL=debug
export PROVISIONING_BRIDGE_DEBUG=true
export PROVISIONING_INTEGRATION_TRACE=true
# Run with integration debugging
provisioning server create test-server 2xCPU-4GB --debug-integration
</code></pre>
<p><strong>Health Check Debugging</strong>:</p>
<pre><code class="language-nushell">def debug-integration-health [] -&gt; record {
print "=== Integration Health Debug ==="
# Check all integration points
let legacy_health = try {
check-legacy-system
} catch { |e| {status: "error", error: $e.msg} }
let orchestrator_health = try {
http get "http://localhost:9090/health"
} catch { |e| {status: "error", error: $e.msg} }
let bridge_health = try {
check-bridge-status
} catch { |e| {status: "error", error: $e.msg} }
let config_health = try {
validate-config-integration
} catch { |e| {status: "error", error: $e.msg} }
print $"Legacy System: ($legacy_health.status)"
print $"Orchestrator: ($orchestrator_health.status)"
print $"Bridge: ($bridge_health.status)"
print $"Configuration: ($config_health.status)"
{
legacy: $legacy_health,
orchestrator: $orchestrator_health,
bridge: $bridge_health,
configuration: $config_health,
debug_timestamp: (date now)
}
}
</code></pre>
<p>This integration guide provides a comprehensive framework for seamlessly integrating new development components with existing production systems while maintaining reliability, compatibility, and clear migration pathways.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../development/workflow.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="../development/implementation-guide.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="../development/workflow.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="../development/implementation-guide.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>