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.
1321 lines
54 KiB
HTML
1321 lines
54 KiB
HTML
<!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 = ""] -> 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] -> 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
|
|
] -> 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 [] -> 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<String>,
|
|
#[serde(flatten)]
|
|
payload: serde_json::Value,
|
|
}
|
|
|
|
async fn handle_versioned_request(
|
|
headers: HeaderMap,
|
|
req: ApiRequest,
|
|
) -> Result<ApiResponse, ApiError> {
|
|
let api_version = headers
|
|
.get("API-Version")
|
|
.and_then(|v| v.to_str().ok())
|
|
.unwrap_or("v1");
|
|
|
|
match api_version {
|
|
"v1" => handle_v1_request(req.payload).await,
|
|
"v2" => handle_v2_request(req.payload).await,
|
|
"v3" => handle_v3_request(req.payload).await,
|
|
_ => Err(ApiError::UnsupportedVersion(api_version.to_string())),
|
|
}
|
|
}
|
|
|
|
// V1 compatibility endpoint
|
|
async fn handle_v1_request(payload: serde_json::Value) -> Result<ApiResponse, ApiError> {
|
|
// 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) > 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 > 0, "Timeout must be positive"
|
|
retry_count >= 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
|
|
] -> record {
|
|
let endpoint = match $api_version {
|
|
"v1" => "/servers",
|
|
"v2" => "/workflows/servers/create",
|
|
"v3" => "/workflows/batch/submit",
|
|
_ => (error make {msg: $"Unsupported API version: ($api_version)"})
|
|
}
|
|
|
|
let request_body = match $api_version {
|
|
"v1" => {name: $name, plan: $plan},
|
|
"v2" => {name: $name, plan: $plan, orchestrated: $orchestrated},
|
|
"v3" => {
|
|
operations: [{
|
|
id: "create_server",
|
|
type: "server_create",
|
|
config: {name: $name, plan: $plan}
|
|
}]
|
|
},
|
|
_ => (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
|
|
] -> 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"] => migrate_filesystem_to_sqlite,
|
|
["filesystem", "surrealdb"] => migrate_filesystem_to_surrealdb,
|
|
["sqlite", "surrealdb"] => migrate_sqlite_to_surrealdb,
|
|
_ => (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 [] -> 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] -> 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
|
|
] -> 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 [] -> 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 [] -> 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 [] -> 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 [] -> 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 [] -> 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<string>
|
|
] -> list<string> {
|
|
match $legacy_command {
|
|
["provisioning", "server", "create", $name, $plan, ...$args] => {
|
|
let new_args = ($args | each { |arg|
|
|
match $arg {
|
|
"--dry-run" => "--dry-run",
|
|
"--wait" => "--wait",
|
|
$zone if ($zone | str starts-with "--zone=") => $zone,
|
|
_ => $arg
|
|
}
|
|
})
|
|
|
|
["provisioning", "server", "create", $name, $plan] ++ $new_args ++ ["--orchestrated"]
|
|
},
|
|
_ => $legacy_command # Pass through unchanged
|
|
}
|
|
}
|
|
|
|
# Data format translation
|
|
export def translate-response [
|
|
legacy_response: record,
|
|
target_format: string = "v2"
|
|
] -> record {
|
|
match $target_format {
|
|
"v2" => {
|
|
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"
|
|
},
|
|
_ => $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) <= $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
|
|
] -> 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 [] -> 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] -> 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 [] -> 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 [] -> 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>
|