Jesús Pérez 6a59d34bb1
chore: update provisioning configuration and documentation
Update configuration files, templates, and internal documentation
for the provisioning repository system.

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

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

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

1118 lines
42 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE HTML>
<html lang="en" class="ayu sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Workflow - 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/workflow.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="development-workflow-guide"><a class="header" href="#development-workflow-guide">Development Workflow Guide</a></h1>
<p>This document outlines the recommended development workflows, coding practices, testing strategies, and debugging techniques for the provisioning project.</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="#development-setup">Development Setup</a></li>
<li><a href="#daily-development-workflow">Daily Development Workflow</a></li>
<li><a href="#code-organization">Code Organization</a></li>
<li><a href="#testing-strategies">Testing Strategies</a></li>
<li><a href="#debugging-techniques">Debugging Techniques</a></li>
<li><a href="#integration-workflows">Integration Workflows</a></li>
<li><a href="#collaboration-guidelines">Collaboration Guidelines</a></li>
<li><a href="#quality-assurance">Quality Assurance</a></li>
<li><a href="#best-practices">Best Practices</a></li>
</ol>
<h2 id="overview"><a class="header" href="#overview">Overview</a></h2>
<p>The provisioning project employs a multi-language, multi-component architecture requiring specific development workflows to maintain consistency, quality, and efficiency.</p>
<p><strong>Key Technologies</strong>:</p>
<ul>
<li><strong>Nushell</strong>: Primary scripting and automation language</li>
<li><strong>Rust</strong>: High-performance system components</li>
<li><strong>KCL</strong>: Configuration language and schemas</li>
<li><strong>TOML</strong>: Configuration files</li>
<li><strong>Jinja2</strong>: Template engine</li>
</ul>
<p><strong>Development Principles</strong>:</p>
<ul>
<li><strong>Configuration-Driven</strong>: Never hardcode, always configure</li>
<li><strong>Hybrid Architecture</strong>: Rust for performance, Nushell for flexibility</li>
<li><strong>Test-First</strong>: Comprehensive testing at all levels</li>
<li><strong>Documentation-Driven</strong>: Code and APIs are self-documenting</li>
</ul>
<h2 id="development-setup"><a class="header" href="#development-setup">Development Setup</a></h2>
<h3 id="initial-environment-setup"><a class="header" href="#initial-environment-setup">Initial Environment Setup</a></h3>
<p><strong>1. Clone and Navigate</strong>:</p>
<pre><code class="language-bash"># Clone repository
git clone https://github.com/company/provisioning-system.git
cd provisioning-system
# Navigate to workspace
cd workspace/tools
</code></pre>
<p><strong>2. Initialize Workspace</strong>:</p>
<pre><code class="language-bash"># Initialize development workspace
nu workspace.nu init --user-name $USER --infra-name dev-env
# Check workspace health
nu workspace.nu health --detailed --fix-issues
</code></pre>
<p><strong>3. Configure Development Environment</strong>:</p>
<pre><code class="language-bash"># Create user configuration
cp workspace/config/local-overrides.toml.example workspace/config/$USER.toml
# Edit configuration for development
$EDITOR workspace/config/$USER.toml
</code></pre>
<p><strong>4. Set Up Build System</strong>:</p>
<pre><code class="language-bash"># Navigate to build tools
cd src/tools
# Check build prerequisites
make info
# Perform initial build
make dev-build
</code></pre>
<h3 id="tool-installation"><a class="header" href="#tool-installation">Tool Installation</a></h3>
<p><strong>Required Tools</strong>:</p>
<pre><code class="language-bash"># Install Nushell
cargo install nu
# Install KCL
cargo install kcl-cli
# Install additional tools
cargo install cross # Cross-compilation
cargo install cargo-audit # Security auditing
cargo install cargo-watch # File watching
</code></pre>
<p><strong>Optional Development Tools</strong>:</p>
<pre><code class="language-bash"># Install development enhancers
cargo install nu_plugin_tera # Template plugin
cargo install sops # Secrets management
brew install k9s # Kubernetes management
</code></pre>
<h3 id="ide-configuration"><a class="header" href="#ide-configuration">IDE Configuration</a></h3>
<p><strong>VS Code Setup</strong> (<code>.vscode/settings.json</code>):</p>
<pre><code class="language-json">{
"files.associations": {
"*.nu": "shellscript",
"*.k": "kcl",
"*.toml": "toml"
},
"nushell.shellPath": "/usr/local/bin/nu",
"rust-analyzer.cargo.features": "all",
"editor.formatOnSave": true,
"editor.rulers": [100],
"files.trimTrailingWhitespace": true
}
</code></pre>
<p><strong>Recommended Extensions</strong>:</p>
<ul>
<li>Nushell Language Support</li>
<li>Rust Analyzer</li>
<li>KCL Language Support</li>
<li>TOML Language Support</li>
<li>Better TOML</li>
</ul>
<h2 id="daily-development-workflow"><a class="header" href="#daily-development-workflow">Daily Development Workflow</a></h2>
<h3 id="morning-routine"><a class="header" href="#morning-routine">Morning Routine</a></h3>
<p><strong>1. Sync and Update</strong>:</p>
<pre><code class="language-bash"># Sync with upstream
git pull origin main
# Update workspace
cd workspace/tools
nu workspace.nu health --fix-issues
# Check for updates
nu workspace.nu status --detailed
</code></pre>
<p><strong>2. Review Current State</strong>:</p>
<pre><code class="language-bash"># Check current infrastructure
provisioning show servers
provisioning show settings
# Review workspace status
nu workspace.nu status
</code></pre>
<h3 id="development-cycle"><a class="header" href="#development-cycle">Development Cycle</a></h3>
<p><strong>1. Feature Development</strong>:</p>
<pre><code class="language-bash"># Create feature branch
git checkout -b feature/new-provider-support
# Start development environment
cd workspace/tools
nu workspace.nu init --workspace-type development
# Begin development
$EDITOR workspace/extensions/providers/new-provider/nulib/provider.nu
</code></pre>
<p><strong>2. Incremental Testing</strong>:</p>
<pre><code class="language-bash"># Test syntax during development
nu --check workspace/extensions/providers/new-provider/nulib/provider.nu
# Run unit tests
nu workspace/extensions/providers/new-provider/tests/unit/basic-test.nu
# Integration testing
nu workspace.nu tools test-extension providers/new-provider
</code></pre>
<p><strong>3. Build and Validate</strong>:</p>
<pre><code class="language-bash"># Quick development build
cd src/tools
make dev-build
# Validate changes
make validate-all
# Test distribution
make test-dist
</code></pre>
<h3 id="testing-during-development"><a class="header" href="#testing-during-development">Testing During Development</a></h3>
<p><strong>Unit Testing</strong>:</p>
<pre><code class="language-nushell"># Add test examples to functions
def create-server [name: string] -&gt; record {
# @test: "test-server" -&gt; {name: "test-server", status: "created"}
# Implementation here
}
</code></pre>
<p><strong>Integration Testing</strong>:</p>
<pre><code class="language-bash"># Test with real infrastructure
nu workspace/extensions/providers/new-provider/nulib/provider.nu \
create-server test-server --dry-run
# Test with workspace isolation
PROVISIONING_WORKSPACE_USER=$USER provisioning server create test-server --check
</code></pre>
<h3 id="end-of-day-routine"><a class="header" href="#end-of-day-routine">End-of-Day Routine</a></h3>
<p><strong>1. Commit Progress</strong>:</p>
<pre><code class="language-bash"># Stage changes
git add .
# Commit with descriptive message
git commit -m "feat(provider): add new cloud provider support
- Implement basic server creation
- Add configuration schema
- Include unit tests
- Update documentation"
# Push to feature branch
git push origin feature/new-provider-support
</code></pre>
<p><strong>2. Workspace Maintenance</strong>:</p>
<pre><code class="language-bash"># Clean up development data
nu workspace.nu cleanup --type cache --age 1d
# Backup current state
nu workspace.nu backup --auto-name --components config,extensions
# Check workspace health
nu workspace.nu health
</code></pre>
<h2 id="code-organization"><a class="header" href="#code-organization">Code Organization</a></h2>
<h3 id="nushell-code-structure"><a class="header" href="#nushell-code-structure">Nushell Code Structure</a></h3>
<p><strong>File Organization</strong>:</p>
<pre><code>Extension Structure:
├── nulib/
│ ├── main.nu # Main entry point
│ ├── core/ # Core functionality
│ │ ├── api.nu # API interactions
│ │ ├── config.nu # Configuration handling
│ │ └── utils.nu # Utility functions
│ ├── commands/ # User commands
│ │ ├── create.nu # Create operations
│ │ ├── delete.nu # Delete operations
│ │ └── list.nu # List operations
│ └── tests/ # Test files
│ ├── unit/ # Unit tests
│ └── integration/ # Integration tests
└── templates/ # Template files
├── config.j2 # Configuration templates
└── manifest.j2 # Manifest templates
</code></pre>
<p><strong>Function Naming Conventions</strong>:</p>
<pre><code class="language-nushell"># Use kebab-case for commands
def create-server [name: string] -&gt; record { ... }
def validate-config [config: record] -&gt; bool { ... }
# Use snake_case for internal functions
def get_api_client [] -&gt; record { ... }
def parse_config_file [path: string] -&gt; record { ... }
# Use descriptive prefixes
def check-server-status [server: string] -&gt; string { ... }
def get-server-info [server: string] -&gt; record { ... }
def list-available-zones [] -&gt; list&lt;string&gt; { ... }
</code></pre>
<p><strong>Error Handling Pattern</strong>:</p>
<pre><code class="language-nushell">def create-server [
name: string
--dry-run: bool = false
] -&gt; record {
# 1. Validate inputs
if ($name | str length) == 0 {
error make {
msg: "Server name cannot be empty"
label: {
text: "empty name provided"
span: (metadata $name).span
}
}
}
# 2. Check prerequisites
let config = try {
get-provider-config
} catch {
error make {msg: "Failed to load provider configuration"}
}
# 3. Perform operation
if $dry_run {
return {action: "create", server: $name, status: "dry-run"}
}
# 4. Return result
{server: $name, status: "created", id: (generate-id)}
}
</code></pre>
<h3 id="rust-code-structure"><a class="header" href="#rust-code-structure">Rust Code Structure</a></h3>
<p><strong>Project Organization</strong>:</p>
<pre><code>src/
├── lib.rs # Library root
├── main.rs # Binary entry point
├── config/ # Configuration handling
│ ├── mod.rs
│ ├── loader.rs # Config loading
│ └── validation.rs # Config validation
├── api/ # HTTP API
│ ├── mod.rs
│ ├── handlers.rs # Request handlers
│ └── middleware.rs # Middleware components
└── orchestrator/ # Orchestration logic
├── mod.rs
├── workflow.rs # Workflow management
└── task_queue.rs # Task queue management
</code></pre>
<p><strong>Error Handling</strong>:</p>
<pre><code class="language-rust">use anyhow::{Context, Result};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ProvisioningError {
#[error("Configuration error: {message}")]
Config { message: String },
#[error("Network error: {source}")]
Network {
#[from]
source: reqwest::Error,
},
#[error("Validation failed: {field}")]
Validation { field: String },
}
pub fn create_server(name: &amp;str) -&gt; Result&lt;ServerInfo&gt; {
let config = load_config()
.context("Failed to load configuration")?;
validate_server_name(name)
.context("Server name validation failed")?;
let server = provision_server(name, &amp;config)
.context("Failed to provision server")?;
Ok(server)
}</code></pre>
<h3 id="kcl-schema-organization"><a class="header" href="#kcl-schema-organization">KCL Schema Organization</a></h3>
<p><strong>Schema Structure</strong>:</p>
<pre><code class="language-kcl"># Base schema definitions
schema ServerConfig:
name: str
plan: str
zone: str
tags?: {str: str} = {}
check:
len(name) &gt; 0, "Server name cannot be empty"
plan in ["1xCPU-2GB", "2xCPU-4GB", "4xCPU-8GB"], "Invalid plan"
# Provider-specific extensions
schema UpCloudServerConfig(ServerConfig):
template?: str = "Ubuntu Server 22.04 LTS (Jammy Jellyfish)"
storage?: int = 25
check:
storage &gt;= 10, "Minimum storage is 10GB"
storage &lt;= 2048, "Maximum storage is 2TB"
# Composition schemas
schema InfrastructureConfig:
servers: [ServerConfig]
networks?: [NetworkConfig] = []
load_balancers?: [LoadBalancerConfig] = []
check:
len(servers) &gt; 0, "At least one server required"
</code></pre>
<h2 id="testing-strategies"><a class="header" href="#testing-strategies">Testing Strategies</a></h2>
<h3 id="test-driven-development"><a class="header" href="#test-driven-development">Test-Driven Development</a></h3>
<p><strong>TDD Workflow</strong>:</p>
<ol>
<li><strong>Write Test First</strong>: Define expected behavior</li>
<li><strong>Run Test (Fail)</strong>: Confirm test fails as expected</li>
<li><strong>Write Code</strong>: Implement minimal code to pass</li>
<li><strong>Run Test (Pass)</strong>: Confirm test now passes</li>
<li><strong>Refactor</strong>: Improve code while keeping tests green</li>
</ol>
<h3 id="nushell-testing"><a class="header" href="#nushell-testing">Nushell Testing</a></h3>
<p><strong>Unit Test Pattern</strong>:</p>
<pre><code class="language-nushell"># Function with embedded test
def validate-server-name [name: string] -&gt; bool {
# @test: "valid-name" -&gt; true
# @test: "" -&gt; false
# @test: "name-with-spaces" -&gt; false
if ($name | str length) == 0 {
return false
}
if ($name | str contains " ") {
return false
}
true
}
# Separate test file
# tests/unit/server-validation-test.nu
def test_validate_server_name [] {
# Valid cases
assert (validate-server-name "valid-name")
assert (validate-server-name "server123")
# Invalid cases
assert not (validate-server-name "")
assert not (validate-server-name "name with spaces")
assert not (validate-server-name "name@with!special")
print "✅ validate-server-name tests passed"
}
</code></pre>
<p><strong>Integration Test Pattern</strong>:</p>
<pre><code class="language-nushell"># tests/integration/server-lifecycle-test.nu
def test_complete_server_lifecycle [] {
# Setup
let test_server = "test-server-" + (date now | format date "%Y%m%d%H%M%S")
try {
# Test creation
let create_result = (create-server $test_server --dry-run)
assert ($create_result.status == "dry-run")
# Test validation
let validate_result = (validate-server-config $test_server)
assert $validate_result
print $"✅ Server lifecycle test passed for ($test_server)"
} catch { |e|
print $"❌ Server lifecycle test failed: ($e.msg)"
exit 1
}
}
</code></pre>
<h3 id="rust-testing"><a class="header" href="#rust-testing">Rust Testing</a></h3>
<p><strong>Unit Testing</strong>:</p>
<pre><code class="language-rust">#[cfg(test)]
mod tests {
use super::*;
use tokio_test;
#[test]
fn test_validate_server_name() {
assert!(validate_server_name("valid-name"));
assert!(validate_server_name("server123"));
assert!(!validate_server_name(""));
assert!(!validate_server_name("name with spaces"));
assert!(!validate_server_name("name@special"));
}
#[tokio::test]
async fn test_server_creation() {
let config = test_config();
let result = create_server("test-server", &amp;config).await;
assert!(result.is_ok());
let server = result.unwrap();
assert_eq!(server.name, "test-server");
assert_eq!(server.status, "created");
}
}</code></pre>
<p><strong>Integration Testing</strong>:</p>
<pre><code class="language-rust">#[cfg(test)]
mod integration_tests {
use super::*;
use testcontainers::*;
#[tokio::test]
async fn test_full_workflow() {
// Setup test environment
let docker = clients::Cli::default();
let postgres = docker.run(images::postgres::Postgres::default());
let config = TestConfig {
database_url: format!("postgresql://localhost:{}/test",
postgres.get_host_port_ipv4(5432))
};
// Test complete workflow
let workflow = create_workflow(&amp;config).await.unwrap();
let result = execute_workflow(workflow).await.unwrap();
assert_eq!(result.status, WorkflowStatus::Completed);
}
}</code></pre>
<h3 id="kcl-testing"><a class="header" href="#kcl-testing">KCL Testing</a></h3>
<p><strong>Schema Validation Testing</strong>:</p>
<pre><code class="language-bash"># Test KCL schemas
kcl test kcl/
# Validate specific schemas
kcl check kcl/server.k --data test-data.yaml
# Test with examples
kcl run kcl/server.k -D name="test-server" -D plan="2xCPU-4GB"
</code></pre>
<h3 id="test-automation"><a class="header" href="#test-automation">Test Automation</a></h3>
<p><strong>Continuous Testing</strong>:</p>
<pre><code class="language-bash"># Watch for changes and run tests
cargo watch -x test -x check
# Watch Nushell files
find . -name "*.nu" | entr -r nu tests/run-all-tests.nu
# Automated testing in workspace
nu workspace.nu tools test-all --watch
</code></pre>
<h2 id="debugging-techniques"><a class="header" href="#debugging-techniques">Debugging Techniques</a></h2>
<h3 id="debug-configuration"><a class="header" href="#debug-configuration">Debug Configuration</a></h3>
<p><strong>Enable Debug Mode</strong>:</p>
<pre><code class="language-bash"># Environment variables
export PROVISIONING_DEBUG=true
export PROVISIONING_LOG_LEVEL=debug
export RUST_LOG=debug
export RUST_BACKTRACE=1
# Workspace debug
export PROVISIONING_WORKSPACE_USER=$USER
</code></pre>
<h3 id="nushell-debugging"><a class="header" href="#nushell-debugging">Nushell Debugging</a></h3>
<p><strong>Debug Techniques</strong>:</p>
<pre><code class="language-nushell"># Debug prints
def debug-server-creation [name: string] {
print $"🐛 Creating server: ($name)"
let config = get-provider-config
print $"🐛 Config loaded: ($config | to json)"
let result = try {
create-server-api $name $config
} catch { |e|
print $"🐛 API call failed: ($e.msg)"
$e
}
print $"🐛 Result: ($result | to json)"
$result
}
# Conditional debugging
def create-server [name: string] {
if $env.PROVISIONING_DEBUG? == "true" {
print $"Debug: Creating server ($name)"
}
# Implementation
}
# Interactive debugging
def debug-interactive [] {
print "🐛 Entering debug mode..."
print "Available commands: $env.PATH"
print "Current config: " (get-config | to json)
# Drop into interactive shell
nu --interactive
}
</code></pre>
<p><strong>Error Investigation</strong>:</p>
<pre><code class="language-nushell"># Comprehensive error handling
def safe-server-creation [name: string] {
try {
create-server $name
} catch { |e|
# Log error details
{
timestamp: (date now | format date "%Y-%m-%d %H:%M:%S"),
operation: "create-server",
input: $name,
error: $e.msg,
debug: $e.debug?,
env: {
user: $env.USER,
workspace: $env.PROVISIONING_WORKSPACE_USER?,
debug: $env.PROVISIONING_DEBUG?
}
} | save --append logs/error-debug.json
# Re-throw with context
error make {
msg: $"Server creation failed: ($e.msg)",
label: {text: "failed here", span: $e.span?}
}
}
}
</code></pre>
<h3 id="rust-debugging"><a class="header" href="#rust-debugging">Rust Debugging</a></h3>
<p><strong>Debug Logging</strong>:</p>
<pre><code class="language-rust">use tracing::{debug, info, warn, error, instrument};
#[instrument]
pub async fn create_server(name: &amp;str) -&gt; Result&lt;ServerInfo&gt; {
debug!("Starting server creation for: {}", name);
let config = load_config()
.map_err(|e| {
error!("Failed to load config: {:?}", e);
e
})?;
info!("Configuration loaded successfully");
debug!("Config details: {:?}", config);
let server = provision_server(name, &amp;config).await
.map_err(|e| {
error!("Provisioning failed for {}: {:?}", name, e);
e
})?;
info!("Server {} created successfully", name);
Ok(server)
}</code></pre>
<p><strong>Interactive Debugging</strong>:</p>
<pre><code class="language-rust">// Use debugger breakpoints
#[cfg(debug_assertions)]
{
println!("Debug: server creation starting");
dbg!(&amp;config);
// Add breakpoint here in IDE
}</code></pre>
<h3 id="log-analysis"><a class="header" href="#log-analysis">Log Analysis</a></h3>
<p><strong>Log Monitoring</strong>:</p>
<pre><code class="language-bash"># Follow all logs
tail -f workspace/runtime/logs/$USER/*.log
# Filter for errors
grep -i error workspace/runtime/logs/$USER/*.log
# Monitor specific component
tail -f workspace/runtime/logs/$USER/orchestrator.log | grep -i workflow
# Structured log analysis
jq '.level == "ERROR"' workspace/runtime/logs/$USER/structured.jsonl
</code></pre>
<p><strong>Debug Log Levels</strong>:</p>
<pre><code class="language-bash"># Different verbosity levels
PROVISIONING_LOG_LEVEL=trace provisioning server create test
PROVISIONING_LOG_LEVEL=debug provisioning server create test
PROVISIONING_LOG_LEVEL=info provisioning server create test
</code></pre>
<h2 id="integration-workflows"><a class="header" href="#integration-workflows">Integration Workflows</a></h2>
<h3 id="existing-system-integration"><a class="header" href="#existing-system-integration">Existing System Integration</a></h3>
<p><strong>Working with Legacy Components</strong>:</p>
<pre><code class="language-bash"># Test integration with existing system
provisioning --version # Legacy system
src/core/nulib/provisioning --version # New system
# Test workspace integration
PROVISIONING_WORKSPACE_USER=$USER provisioning server list
# Validate configuration compatibility
provisioning validate config
nu workspace.nu config validate
</code></pre>
<h3 id="api-integration-testing"><a class="header" href="#api-integration-testing">API Integration Testing</a></h3>
<p><strong>REST API Testing</strong>:</p>
<pre><code class="language-bash"># Test orchestrator API
curl -X GET http://localhost:9090/health
curl -X GET http://localhost:9090/tasks
# Test workflow creation
curl -X POST http://localhost:9090/workflows/servers/create \
-H "Content-Type: application/json" \
-d '{"name": "test-server", "plan": "2xCPU-4GB"}'
# Monitor workflow
curl -X GET http://localhost:9090/workflows/batch/status/workflow-id
</code></pre>
<h3 id="database-integration"><a class="header" href="#database-integration">Database Integration</a></h3>
<p><strong>SurrealDB Integration</strong>:</p>
<pre><code class="language-nushell"># Test database connectivity
use core/nulib/lib_provisioning/database/surreal.nu
let db = (connect-database)
(test-connection $db)
# Workflow state testing
let workflow_id = (create-workflow-record "test-workflow")
let status = (get-workflow-status $workflow_id)
assert ($status.status == "pending")
</code></pre>
<h3 id="external-tool-integration"><a class="header" href="#external-tool-integration">External Tool Integration</a></h3>
<p><strong>Container Integration</strong>:</p>
<pre><code class="language-bash"># Test with Docker
docker run --rm -v $(pwd):/work provisioning:dev provisioning --version
# Test with Kubernetes
kubectl apply -f manifests/test-pod.yaml
kubectl logs test-pod
# Validate in different environments
make test-dist PLATFORM=docker
make test-dist PLATFORM=kubernetes
</code></pre>
<h2 id="collaboration-guidelines"><a class="header" href="#collaboration-guidelines">Collaboration Guidelines</a></h2>
<h3 id="branch-strategy"><a class="header" href="#branch-strategy">Branch Strategy</a></h3>
<p><strong>Branch Naming</strong>:</p>
<ul>
<li><code>feature/description</code> - New features</li>
<li><code>fix/description</code> - Bug fixes</li>
<li><code>docs/description</code> - Documentation updates</li>
<li><code>refactor/description</code> - Code refactoring</li>
<li><code>test/description</code> - Test improvements</li>
</ul>
<p><strong>Workflow</strong>:</p>
<pre><code class="language-bash"># Start new feature
git checkout main
git pull origin main
git checkout -b feature/new-provider-support
# Regular commits
git add .
git commit -m "feat(provider): implement server creation API"
# Push and create PR
git push origin feature/new-provider-support
gh pr create --title "Add new provider support" --body "..."
</code></pre>
<h3 id="code-review-process"><a class="header" href="#code-review-process">Code Review Process</a></h3>
<p><strong>Review Checklist</strong>:</p>
<ul>
<li><input disabled="" type="checkbox"/>
Code follows project conventions</li>
<li><input disabled="" type="checkbox"/>
Tests are included and passing</li>
<li><input disabled="" type="checkbox"/>
Documentation is updated</li>
<li><input disabled="" type="checkbox"/>
No hardcoded values</li>
<li><input disabled="" type="checkbox"/>
Error handling is comprehensive</li>
<li><input disabled="" type="checkbox"/>
Performance considerations addressed</li>
</ul>
<p><strong>Review Commands</strong>:</p>
<pre><code class="language-bash"># Test PR locally
gh pr checkout 123
cd src/tools &amp;&amp; make ci-test
# Run specific tests
nu workspace/extensions/providers/new-provider/tests/run-all.nu
# Check code quality
cargo clippy -- -D warnings
nu --check $(find . -name "*.nu")
</code></pre>
<h3 id="documentation-requirements"><a class="header" href="#documentation-requirements">Documentation Requirements</a></h3>
<p><strong>Code Documentation</strong>:</p>
<pre><code class="language-nushell"># Function documentation
def create-server [
name: string # Server name (must be unique)
plan: string # Server plan (e.g., "2xCPU-4GB")
--dry-run: bool # Show what would be created without doing it
] -&gt; record { # Returns server creation result
# Creates a new server with the specified configuration
#
# Examples:
# create-server "web-01" "2xCPU-4GB"
# create-server "test" "1xCPU-2GB" --dry-run
# Implementation
}
</code></pre>
<h3 id="communication"><a class="header" href="#communication">Communication</a></h3>
<p><strong>Progress Updates</strong>:</p>
<ul>
<li>Daily standup participation</li>
<li>Weekly architecture reviews</li>
<li>PR descriptions with context</li>
<li>Issue tracking with details</li>
</ul>
<p><strong>Knowledge Sharing</strong>:</p>
<ul>
<li>Technical blog posts</li>
<li>Architecture decision records</li>
<li>Code review discussions</li>
<li>Team documentation updates</li>
</ul>
<h2 id="quality-assurance"><a class="header" href="#quality-assurance">Quality Assurance</a></h2>
<h3 id="code-quality-checks"><a class="header" href="#code-quality-checks">Code Quality Checks</a></h3>
<p><strong>Automated Quality Gates</strong>:</p>
<pre><code class="language-bash"># Pre-commit hooks
pre-commit install
# Manual quality check
cd src/tools
make validate-all
# Security audit
cargo audit
</code></pre>
<p><strong>Quality Metrics</strong>:</p>
<ul>
<li>Code coverage &gt; 80%</li>
<li>No critical security vulnerabilities</li>
<li>All tests passing</li>
<li>Documentation coverage complete</li>
<li>Performance benchmarks met</li>
</ul>
<h3 id="performance-monitoring"><a class="header" href="#performance-monitoring">Performance Monitoring</a></h3>
<p><strong>Performance Testing</strong>:</p>
<pre><code class="language-bash"># Benchmark builds
make benchmark
# Performance profiling
cargo flamegraph --bin provisioning-orchestrator
# Load testing
ab -n 1000 -c 10 http://localhost:9090/health
</code></pre>
<p><strong>Resource Monitoring</strong>:</p>
<pre><code class="language-bash"># Monitor during development
nu workspace/tools/runtime-manager.nu monitor --duration 5m
# Check resource usage
du -sh workspace/runtime/
df -h
</code></pre>
<h2 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h2>
<h3 id="configuration-management"><a class="header" href="#configuration-management">Configuration Management</a></h3>
<p><strong>Never Hardcode</strong>:</p>
<pre><code class="language-nushell"># Bad
def get-api-url [] { "https://api.upcloud.com" }
# Good
def get-api-url [] {
get-config-value "providers.upcloud.api_url" "https://api.upcloud.com"
}
</code></pre>
<h3 id="error-handling"><a class="header" href="#error-handling">Error Handling</a></h3>
<p><strong>Comprehensive Error Context</strong>:</p>
<pre><code class="language-nushell">def create-server [name: string] {
try {
validate-server-name $name
} catch { |e|
error make {
msg: $"Invalid server name '($name)': ($e.msg)",
label: {text: "server name validation failed", span: $e.span?}
}
}
try {
provision-server $name
} catch { |e|
error make {
msg: $"Server provisioning failed for '($name)': ($e.msg)",
help: "Check provider credentials and quota limits"
}
}
}
</code></pre>
<h3 id="resource-management"><a class="header" href="#resource-management">Resource Management</a></h3>
<p><strong>Clean Up Resources</strong>:</p>
<pre><code class="language-nushell">def with-temporary-server [name: string, action: closure] {
let server = (create-server $name)
try {
do $action $server
} catch { |e|
# Clean up on error
delete-server $name
$e
}
# Clean up on success
delete-server $name
}
</code></pre>
<h3 id="testing-best-practices"><a class="header" href="#testing-best-practices">Testing Best Practices</a></h3>
<p><strong>Test Isolation</strong>:</p>
<pre><code class="language-nushell">def test-with-isolation [test_name: string, test_action: closure] {
let test_workspace = $"test-($test_name)-(date now | format date '%Y%m%d%H%M%S')"
try {
# Set up isolated environment
$env.PROVISIONING_WORKSPACE_USER = $test_workspace
nu workspace.nu init --user-name $test_workspace
# Run test
do $test_action
print $"✅ Test ($test_name) passed"
} catch { |e|
print $"❌ Test ($test_name) failed: ($e.msg)"
exit 1
} finally {
# Clean up test environment
nu workspace.nu cleanup --user-name $test_workspace --type all --force
}
}
</code></pre>
<p>This development workflow provides a comprehensive framework for efficient, quality-focused development while maintaining the projects architectural principles and ensuring smooth collaboration across the team.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../development/project-structure.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/integration.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/project-structure.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/integration.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>