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

1577 lines
61 KiB
HTML
Raw Permalink 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>Extensions - 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/extensions.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="extension-development-guide"><a class="header" href="#extension-development-guide">Extension Development Guide</a></h1>
<p>This document provides comprehensive guidance on creating providers, task services, and clusters for provisioning, including templates, testing frameworks, publishing, and best practices.</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="#extension-types">Extension Types</a></li>
<li><a href="#provider-development">Provider Development</a></li>
<li><a href="#task-service-development">Task Service Development</a></li>
<li><a href="#cluster-development">Cluster Development</a></li>
<li><a href="#testing-and-validation">Testing and Validation</a></li>
<li><a href="#publishing-and-distribution">Publishing and Distribution</a></li>
<li><a href="#best-practices">Best Practices</a></li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
</ol>
<h2 id="overview"><a class="header" href="#overview">Overview</a></h2>
<p>Provisioning supports three types of extensions that enable customization and expansion of functionality:</p>
<ul>
<li><strong>Providers</strong>: Cloud provider implementations for resource management</li>
<li><strong>Task Services</strong>: Infrastructure service components (databases, monitoring, etc.)</li>
<li><strong>Clusters</strong>: Complete deployment solutions combining multiple services</li>
</ul>
<p><strong>Key Features</strong>:</p>
<ul>
<li><strong>Template-Based Development</strong>: Comprehensive templates for all extension types</li>
<li><strong>Workspace Integration</strong>: Extensions developed in isolated workspace environments</li>
<li><strong>Configuration-Driven</strong>: KCL schemas for type-safe configuration</li>
<li><strong>Version Management</strong>: GitHub integration for version tracking</li>
<li><strong>Testing Framework</strong>: Comprehensive testing and validation tools</li>
<li><strong>Hot Reloading</strong>: Development-time hot reloading support</li>
</ul>
<p><strong>Location</strong>: <code>workspace/extensions/</code></p>
<h2 id="extension-types"><a class="header" href="#extension-types">Extension Types</a></h2>
<h3 id="extension-architecture"><a class="header" href="#extension-architecture">Extension Architecture</a></h3>
<pre><code>Extension Ecosystem
├── Providers # Cloud resource management
│ ├── AWS # Amazon Web Services
│ ├── UpCloud # UpCloud platform
│ ├── Local # Local development
│ └── Custom # User-defined providers
├── Task Services # Infrastructure components
│ ├── Kubernetes # Container orchestration
│ ├── Database Services # PostgreSQL, MongoDB, etc.
│ ├── Monitoring # Prometheus, Grafana, etc.
│ ├── Networking # Cilium, CoreDNS, etc.
│ └── Custom Services # User-defined services
└── Clusters # Complete solutions
├── Web Stack # Web application deployment
├── CI/CD Pipeline # Continuous integration/deployment
├── Data Platform # Data processing and analytics
└── Custom Clusters # User-defined clusters
</code></pre>
<h3 id="extension-discovery"><a class="header" href="#extension-discovery">Extension Discovery</a></h3>
<p><strong>Discovery Order</strong>:</p>
<ol>
<li><code>workspace/extensions/{type}/{user}/{name}</code> - User-specific extensions</li>
<li><code>workspace/extensions/{type}/{name}</code> - Workspace shared extensions</li>
<li><code>workspace/extensions/{type}/template</code> - Templates</li>
<li>Core system paths (fallback)</li>
</ol>
<p><strong>Path Resolution</strong>:</p>
<pre><code class="language-nushell"># Automatic extension discovery
use workspace/lib/path-resolver.nu
# Find provider extension
let provider_path = (path-resolver resolve_extension "providers" "my-aws-provider")
# List all available task services
let taskservs = (path-resolver list_extensions "taskservs" --include-core)
# Resolve cluster definition
let cluster_path = (path-resolver resolve_extension "clusters" "web-stack")
</code></pre>
<h2 id="provider-development"><a class="header" href="#provider-development">Provider Development</a></h2>
<h3 id="provider-architecture"><a class="header" href="#provider-architecture">Provider Architecture</a></h3>
<p>Providers implement cloud resource management through a standardized interface that supports multiple cloud platforms while maintaining consistent APIs.</p>
<p><strong>Core Responsibilities</strong>:</p>
<ul>
<li><strong>Authentication</strong>: Secure API authentication and credential management</li>
<li><strong>Resource Management</strong>: Server creation, deletion, and lifecycle management</li>
<li><strong>Configuration</strong>: Provider-specific settings and validation</li>
<li><strong>Error Handling</strong>: Comprehensive error handling and recovery</li>
<li><strong>Rate Limiting</strong>: API rate limiting and retry logic</li>
</ul>
<h3 id="creating-a-new-provider"><a class="header" href="#creating-a-new-provider">Creating a New Provider</a></h3>
<p><strong>1. Initialize from Template</strong>:</p>
<pre><code class="language-bash"># Copy provider template
cp -r workspace/extensions/providers/template workspace/extensions/providers/my-cloud
# Navigate to new provider
cd workspace/extensions/providers/my-cloud
</code></pre>
<p><strong>2. Update Configuration</strong>:</p>
<pre><code class="language-bash"># Initialize provider metadata
nu init-provider.nu \
--name "my-cloud" \
--display-name "MyCloud Provider" \
--author "$USER" \
--description "MyCloud platform integration"
</code></pre>
<h3 id="provider-structure"><a class="header" href="#provider-structure">Provider Structure</a></h3>
<pre><code>my-cloud/
├── README.md # Provider documentation
├── kcl/ # KCL configuration schemas
│ ├── settings.k # Provider settings schema
│ ├── servers.k # Server configuration schema
│ ├── networks.k # Network configuration schema
│ └── kcl.mod # KCL module dependencies
├── nulib/ # Nushell implementation
│ ├── provider.nu # Main provider interface
│ ├── servers/ # Server management
│ │ ├── create.nu # Server creation logic
│ │ ├── delete.nu # Server deletion logic
│ │ ├── list.nu # Server listing
│ │ ├── status.nu # Server status checking
│ │ └── utils.nu # Server utilities
│ ├── auth/ # Authentication
│ │ ├── client.nu # API client setup
│ │ ├── tokens.nu # Token management
│ │ └── validation.nu # Credential validation
│ └── utils/ # Provider utilities
│ ├── api.nu # API interaction helpers
│ ├── config.nu # Configuration helpers
│ └── validation.nu # Input validation
├── templates/ # Jinja2 templates
│ ├── server-config.j2 # Server configuration
│ ├── cloud-init.j2 # Cloud initialization
│ └── network-config.j2 # Network configuration
├── generate/ # Code generation
│ ├── server-configs.nu # Generate server configurations
│ └── infrastructure.nu # Generate infrastructure
└── tests/ # Testing framework
├── unit/ # Unit tests
│ ├── test-auth.nu # Authentication tests
│ ├── test-servers.nu # Server management tests
│ └── test-validation.nu # Validation tests
├── integration/ # Integration tests
│ ├── test-lifecycle.nu # Complete lifecycle tests
│ └── test-api.nu # API integration tests
└── mock/ # Mock data and services
├── api-responses.json # Mock API responses
└── test-configs.toml # Test configurations
</code></pre>
<h3 id="provider-implementation"><a class="header" href="#provider-implementation">Provider Implementation</a></h3>
<p><strong>Main Provider Interface</strong> (<code>nulib/provider.nu</code>):</p>
<pre><code class="language-nushell">#!/usr/bin/env nu
# MyCloud Provider Implementation
# Provider metadata
export const PROVIDER_NAME = "my-cloud"
export const PROVIDER_VERSION = "1.0.0"
export const API_VERSION = "v1"
# Main provider initialization
export def "provider init" [
--config-path: string = "" # Path to provider configuration
--validate: bool = true # Validate configuration on init
] -&gt; record {
let config = if $config_path == "" {
load_provider_config
} else {
open $config_path | from toml
}
if $validate {
validate_provider_config $config
}
# Initialize API client
let client = (setup_api_client $config)
# Return provider instance
{
name: $PROVIDER_NAME,
version: $PROVIDER_VERSION,
config: $config,
client: $client,
initialized: true
}
}
# Server management interface
export def "provider create-server" [
name: string # Server name
plan: string # Server plan/size
--zone: string = "auto" # Deployment zone
--template: string = "ubuntu22" # OS template
--dry-run: bool = false # Show what would be created
] -&gt; record {
let provider = (provider init)
# Validate inputs
if ($name | str length) == 0 {
error make {msg: "Server name cannot be empty"}
}
if not (is_valid_plan $plan) {
error make {msg: $"Invalid server plan: ($plan)"}
}
# Build server configuration
let server_config = {
name: $name,
plan: $plan,
zone: (resolve_zone $zone),
template: $template,
provider: $PROVIDER_NAME
}
if $dry_run {
return {action: "create", config: $server_config, status: "dry-run"}
}
# Create server via API
let result = try {
create_server_api $server_config $provider.client
} catch { |e|
error make {
msg: $"Server creation failed: ($e.msg)",
help: "Check provider credentials and quota limits"
}
}
{
server: $name,
status: "created",
id: $result.id,
ip_address: $result.ip_address,
created_at: (date now)
}
}
export def "provider delete-server" [
name: string # Server name or ID
--force: bool = false # Force deletion without confirmation
] -&gt; record {
let provider = (provider init)
# Find server
let server = try {
find_server $name $provider.client
} catch {
error make {msg: $"Server not found: ($name)"}
}
if not $force {
let confirm = (input $"Delete server '($name)' (y/N)? ")
if $confirm != "y" and $confirm != "yes" {
return {action: "delete", server: $name, status: "cancelled"}
}
}
# Delete server
let result = try {
delete_server_api $server.id $provider.client
} catch { |e|
error make {msg: $"Server deletion failed: ($e.msg)"}
}
{
server: $name,
status: "deleted",
deleted_at: (date now)
}
}
export def "provider list-servers" [
--zone: string = "" # Filter by zone
--status: string = "" # Filter by status
--format: string = "table" # Output format: table, json, yaml
] -&gt; list&lt;record&gt; {
let provider = (provider init)
let servers = try {
list_servers_api $provider.client
} catch { |e|
error make {msg: $"Failed to list servers: ($e.msg)"}
}
# Apply filters
let filtered = $servers
| if $zone != "" { filter {|s| $s.zone == $zone} } else { $in }
| if $status != "" { filter {|s| $s.status == $status} } else { $in }
match $format {
"json" =&gt; ($filtered | to json),
"yaml" =&gt; ($filtered | to yaml),
_ =&gt; $filtered
}
}
# Provider testing interface
export def "provider test" [
--test-type: string = "basic" # Test type: basic, full, integration
] -&gt; record {
match $test_type {
"basic" =&gt; test_basic_functionality,
"full" =&gt; test_full_functionality,
"integration" =&gt; test_integration,
_ =&gt; (error make {msg: $"Unknown test type: ($test_type)"})
}
}
</code></pre>
<p><strong>Authentication Module</strong> (<code>nulib/auth/client.nu</code>):</p>
<pre><code class="language-nushell"># API client setup and authentication
export def setup_api_client [config: record] -&gt; record {
# Validate credentials
if not ("api_key" in $config) {
error make {msg: "API key not found in configuration"}
}
if not ("api_secret" in $config) {
error make {msg: "API secret not found in configuration"}
}
# Setup HTTP client with authentication
let client = {
base_url: ($config.api_url? | default "https://api.my-cloud.com"),
api_key: $config.api_key,
api_secret: $config.api_secret,
timeout: ($config.timeout? | default 30),
retries: ($config.retries? | default 3)
}
# Test authentication
try {
test_auth_api $client
} catch { |e|
error make {
msg: $"Authentication failed: ($e.msg)",
help: "Check your API credentials and network connectivity"
}
}
$client
}
def test_auth_api [client: record] -&gt; bool {
let response = http get $"($client.base_url)/auth/test" --headers {
"Authorization": $"Bearer ($client.api_key)",
"Content-Type": "application/json"
}
$response.status == "success"
}
</code></pre>
<p><strong>KCL Configuration Schema</strong> (<code>kcl/settings.k</code>):</p>
<pre><code class="language-kcl"># MyCloud Provider Configuration Schema
schema MyCloudConfig:
"""MyCloud provider configuration"""
api_url?: str = "https://api.my-cloud.com"
api_key: str
api_secret: str
timeout?: int = 30
retries?: int = 3
# Rate limiting
rate_limit?: {
requests_per_minute?: int = 60
burst_size?: int = 10
} = {}
# Default settings
defaults?: {
zone?: str = "us-east-1"
template?: str = "ubuntu-22.04"
network?: str = "default"
} = {}
check:
len(api_key) &gt; 0, "API key cannot be empty"
len(api_secret) &gt; 0, "API secret cannot be empty"
timeout &gt; 0, "Timeout must be positive"
retries &gt;= 0, "Retries must be non-negative"
schema MyCloudServerConfig:
"""MyCloud server configuration"""
name: str
plan: str
zone?: str
template?: str = "ubuntu-22.04"
storage?: int = 25
tags?: {str: str} = {}
# Network configuration
network?: {
vpc_id?: str
subnet_id?: str
public_ip?: bool = true
firewall_rules?: [FirewallRule] = []
}
check:
len(name) &gt; 0, "Server name cannot be empty"
plan in ["small", "medium", "large", "xlarge"], "Invalid plan"
storage &gt;= 10, "Minimum storage is 10GB"
storage &lt;= 2048, "Maximum storage is 2TB"
schema FirewallRule:
"""Firewall rule configuration"""
port: int | str
protocol: str = "tcp"
source: str = "0.0.0.0/0"
description?: str
check:
protocol in ["tcp", "udp", "icmp"], "Invalid protocol"
</code></pre>
<h3 id="provider-testing"><a class="header" href="#provider-testing">Provider Testing</a></h3>
<p><strong>Unit Testing</strong> (<code>tests/unit/test-servers.nu</code>):</p>
<pre><code class="language-nushell"># Unit tests for server management
use ../../../nulib/provider.nu
def test_server_creation [] {
# Test valid server creation
let result = (provider create-server "test-server" "small" --dry-run)
assert ($result.action == "create")
assert ($result.config.name == "test-server")
assert ($result.config.plan == "small")
assert ($result.status == "dry-run")
print "✅ Server creation test passed"
}
def test_invalid_server_name [] {
# Test invalid server name
try {
provider create-server "" "small" --dry-run
assert false "Should have failed with empty name"
} catch { |e|
assert ($e.msg | str contains "Server name cannot be empty")
}
print "✅ Invalid server name test passed"
}
def test_invalid_plan [] {
# Test invalid server plan
try {
provider create-server "test" "invalid-plan" --dry-run
assert false "Should have failed with invalid plan"
} catch { |e|
assert ($e.msg | str contains "Invalid server plan")
}
print "✅ Invalid plan test passed"
}
def main [] {
print "Running server management unit tests..."
test_server_creation
test_invalid_server_name
test_invalid_plan
print "✅ All server management tests passed"
}
</code></pre>
<p><strong>Integration Testing</strong> (<code>tests/integration/test-lifecycle.nu</code>):</p>
<pre><code class="language-nushell"># Integration tests for complete server lifecycle
use ../../../nulib/provider.nu
def test_complete_lifecycle [] {
let test_server = $"test-server-(date now | format date '%Y%m%d%H%M%S')"
try {
# Test server creation (dry run)
let create_result = (provider create-server $test_server "small" --dry-run)
assert ($create_result.status == "dry-run")
# Test server listing
let servers = (provider list-servers --format json)
assert ($servers | length) &gt;= 0
# Test provider info
let provider_info = (provider init)
assert ($provider_info.name == "my-cloud")
assert $provider_info.initialized
print $"✅ Complete lifecycle test passed for ($test_server)"
} catch { |e|
print $"❌ Integration test failed: ($e.msg)"
exit 1
}
}
def main [] {
print "Running provider integration tests..."
test_complete_lifecycle
print "✅ All integration tests passed"
}
</code></pre>
<h2 id="task-service-development"><a class="header" href="#task-service-development">Task Service Development</a></h2>
<h3 id="task-service-architecture"><a class="header" href="#task-service-architecture">Task Service Architecture</a></h3>
<p>Task services are infrastructure components that can be deployed and managed across different environments. They provide standardized interfaces for installation, configuration, and lifecycle management.</p>
<p><strong>Core Responsibilities</strong>:</p>
<ul>
<li><strong>Installation</strong>: Service deployment and setup</li>
<li><strong>Configuration</strong>: Dynamic configuration management</li>
<li><strong>Health Checking</strong>: Service status monitoring</li>
<li><strong>Version Management</strong>: Automatic version updates from GitHub</li>
<li><strong>Integration</strong>: Integration with other services and clusters</li>
</ul>
<h3 id="creating-a-new-task-service"><a class="header" href="#creating-a-new-task-service">Creating a New Task Service</a></h3>
<p><strong>1. Initialize from Template</strong>:</p>
<pre><code class="language-bash"># Copy task service template
cp -r workspace/extensions/taskservs/template workspace/extensions/taskservs/my-service
# Navigate to new service
cd workspace/extensions/taskservs/my-service
</code></pre>
<p><strong>2. Initialize Service</strong>:</p>
<pre><code class="language-bash"># Initialize service metadata
nu init-service.nu \
--name "my-service" \
--display-name "My Custom Service" \
--type "database" \
--github-repo "myorg/my-service"
</code></pre>
<h3 id="task-service-structure"><a class="header" href="#task-service-structure">Task Service Structure</a></h3>
<pre><code>my-service/
├── README.md # Service documentation
├── kcl/ # KCL schemas
│ ├── version.k # Version and GitHub integration
│ ├── config.k # Service configuration schema
│ └── kcl.mod # Module dependencies
├── nushell/ # Nushell implementation
│ ├── taskserv.nu # Main service interface
│ ├── install.nu # Installation logic
│ ├── uninstall.nu # Removal logic
│ ├── config.nu # Configuration management
│ ├── status.nu # Status and health checking
│ ├── versions.nu # Version management
│ └── utils.nu # Service utilities
├── templates/ # Jinja2 templates
│ ├── deployment.yaml.j2 # Kubernetes deployment
│ ├── service.yaml.j2 # Kubernetes service
│ ├── configmap.yaml.j2 # Configuration
│ ├── install.sh.j2 # Installation script
│ └── systemd.service.j2 # Systemd service
├── manifests/ # Static manifests
│ ├── rbac.yaml # RBAC definitions
│ ├── pvc.yaml # Persistent volume claims
│ └── ingress.yaml # Ingress configuration
├── generate/ # Code generation
│ ├── manifests.nu # Generate Kubernetes manifests
│ ├── configs.nu # Generate configurations
│ └── docs.nu # Generate documentation
└── tests/ # Testing framework
├── unit/ # Unit tests
├── integration/ # Integration tests
└── fixtures/ # Test fixtures and data
</code></pre>
<h3 id="task-service-implementation"><a class="header" href="#task-service-implementation">Task Service Implementation</a></h3>
<p><strong>Main Service Interface</strong> (<code>nushell/taskserv.nu</code>):</p>
<pre><code class="language-nushell">#!/usr/bin/env nu
# My Custom Service Task Service Implementation
export const SERVICE_NAME = "my-service"
export const SERVICE_TYPE = "database"
export const SERVICE_VERSION = "1.0.0"
# Service installation
export def "taskserv install" [
target: string # Target server or cluster
--config: string = "" # Custom configuration file
--dry-run: bool = false # Show what would be installed
--wait: bool = true # Wait for installation to complete
] -&gt; record {
# Load service configuration
let service_config = if $config != "" {
open $config | from toml
} else {
load_default_config
}
# Validate target environment
let target_info = validate_target $target
if not $target_info.valid {
error make {msg: $"Invalid target: ($target_info.reason)"}
}
if $dry_run {
let install_plan = generate_install_plan $target $service_config
return {
action: "install",
service: $SERVICE_NAME,
target: $target,
plan: $install_plan,
status: "dry-run"
}
}
# Perform installation
print $"Installing ($SERVICE_NAME) on ($target)..."
let install_result = try {
install_service $target $service_config $wait
} catch { |e|
error make {
msg: $"Installation failed: ($e.msg)",
help: "Check target connectivity and permissions"
}
}
{
service: $SERVICE_NAME,
target: $target,
status: "installed",
version: $install_result.version,
endpoint: $install_result.endpoint?,
installed_at: (date now)
}
}
# Service removal
export def "taskserv uninstall" [
target: string # Target server or cluster
--force: bool = false # Force removal without confirmation
--cleanup-data: bool = false # Remove persistent data
] -&gt; record {
let target_info = validate_target $target
if not $target_info.valid {
error make {msg: $"Invalid target: ($target_info.reason)"}
}
# Check if service is installed
let status = get_service_status $target
if $status.status != "installed" {
error make {msg: $"Service ($SERVICE_NAME) is not installed on ($target)"}
}
if not $force {
let confirm = (input $"Remove ($SERVICE_NAME) from ($target)? (y/N) ")
if $confirm != "y" and $confirm != "yes" {
return {action: "uninstall", service: $SERVICE_NAME, status: "cancelled"}
}
}
print $"Removing ($SERVICE_NAME) from ($target)..."
let removal_result = try {
uninstall_service $target $cleanup_data
} catch { |e|
error make {msg: $"Removal failed: ($e.msg)"}
}
{
service: $SERVICE_NAME,
target: $target,
status: "uninstalled",
data_removed: $cleanup_data,
uninstalled_at: (date now)
}
}
# Service status checking
export def "taskserv status" [
target: string # Target server or cluster
--detailed: bool = false # Show detailed status information
] -&gt; record {
let target_info = validate_target $target
if not $target_info.valid {
error make {msg: $"Invalid target: ($target_info.reason)"}
}
let status = get_service_status $target
if $detailed {
let health = check_service_health $target
let metrics = get_service_metrics $target
$status | merge {
health: $health,
metrics: $metrics,
checked_at: (date now)
}
} else {
$status
}
}
# Version management
export def "taskserv check-updates" [
--target: string = "" # Check updates for specific target
] -&gt; record {
let current_version = get_current_version
let latest_version = get_latest_version_from_github
let update_available = $latest_version != $current_version
{
service: $SERVICE_NAME,
current_version: $current_version,
latest_version: $latest_version,
update_available: $update_available,
target: $target,
checked_at: (date now)
}
}
export def "taskserv update" [
target: string # Target to update
--version: string = "latest" # Specific version to update to
--dry-run: bool = false # Show what would be updated
] -&gt; record {
let current_status = (taskserv status $target)
if $current_status.status != "installed" {
error make {msg: $"Service not installed on ($target)"}
}
let target_version = if $version == "latest" {
get_latest_version_from_github
} else {
$version
}
if $dry_run {
return {
action: "update",
service: $SERVICE_NAME,
target: $target,
from_version: $current_status.version,
to_version: $target_version,
status: "dry-run"
}
}
print $"Updating ($SERVICE_NAME) on ($target) to version ($target_version)..."
let update_result = try {
update_service $target $target_version
} catch { |e|
error make {msg: $"Update failed: ($e.msg)"}
}
{
service: $SERVICE_NAME,
target: $target,
status: "updated",
from_version: $current_status.version,
to_version: $target_version,
updated_at: (date now)
}
}
# Service testing
export def "taskserv test" [
target: string = "local" # Target for testing
--test-type: string = "basic" # Test type: basic, integration, full
] -&gt; record {
match $test_type {
"basic" =&gt; test_basic_functionality $target,
"integration" =&gt; test_integration $target,
"full" =&gt; test_full_functionality $target,
_ =&gt; (error make {msg: $"Unknown test type: ($test_type)"})
}
}
</code></pre>
<p><strong>Version Configuration</strong> (<code>kcl/version.k</code>):</p>
<pre><code class="language-kcl"># Version management with GitHub integration
version_config: VersionConfig = {
service_name = "my-service"
# GitHub repository for version checking
github = {
owner = "myorg"
repo = "my-service"
# Release configuration
release = {
tag_prefix = "v"
prerelease = false
draft = false
}
# Asset patterns for different platforms
assets = {
linux_amd64 = "my-service-{version}-linux-amd64.tar.gz"
darwin_amd64 = "my-service-{version}-darwin-amd64.tar.gz"
windows_amd64 = "my-service-{version}-windows-amd64.zip"
}
}
# Version constraints and compatibility
compatibility = {
min_kubernetes_version = "1.20.0"
max_kubernetes_version = "1.28.*"
# Dependencies
requires = {
"cert-manager": "&gt;=1.8.0"
"ingress-nginx": "&gt;=1.0.0"
}
# Conflicts
conflicts = {
"old-my-service": "*"
}
}
# Installation configuration
installation = {
default_namespace = "my-service"
create_namespace = true
# Resource requirements
resources = {
requests = {
cpu = "100m"
memory = "128Mi"
}
limits = {
cpu = "500m"
memory = "512Mi"
}
}
# Persistence
persistence = {
enabled = true
storage_class = "default"
size = "10Gi"
}
}
# Health check configuration
health_check = {
initial_delay_seconds = 30
period_seconds = 10
timeout_seconds = 5
failure_threshold = 3
# Health endpoints
endpoints = {
liveness = "/health/live"
readiness = "/health/ready"
}
}
}
</code></pre>
<h2 id="cluster-development"><a class="header" href="#cluster-development">Cluster Development</a></h2>
<h3 id="cluster-architecture"><a class="header" href="#cluster-architecture">Cluster Architecture</a></h3>
<p>Clusters represent complete deployment solutions that combine multiple task services, providers, and configurations to create functional environments.</p>
<p><strong>Core Responsibilities</strong>:</p>
<ul>
<li><strong>Service Orchestration</strong>: Coordinate multiple task service deployments</li>
<li><strong>Dependency Management</strong>: Handle service dependencies and startup order</li>
<li><strong>Configuration Management</strong>: Manage cross-service configuration</li>
<li><strong>Health Monitoring</strong>: Monitor overall cluster health</li>
<li><strong>Scaling</strong>: Handle cluster scaling operations</li>
</ul>
<h3 id="creating-a-new-cluster"><a class="header" href="#creating-a-new-cluster">Creating a New Cluster</a></h3>
<p><strong>1. Initialize from Template</strong>:</p>
<pre><code class="language-bash"># Copy cluster template
cp -r workspace/extensions/clusters/template workspace/extensions/clusters/my-stack
# Navigate to new cluster
cd workspace/extensions/clusters/my-stack
</code></pre>
<p><strong>2. Initialize Cluster</strong>:</p>
<pre><code class="language-bash"># Initialize cluster metadata
nu init-cluster.nu \
--name "my-stack" \
--display-name "My Application Stack" \
--type "web-application"
</code></pre>
<h3 id="cluster-implementation"><a class="header" href="#cluster-implementation">Cluster Implementation</a></h3>
<p><strong>Main Cluster Interface</strong> (<code>nushell/cluster.nu</code>):</p>
<pre><code class="language-nushell">#!/usr/bin/env nu
# My Application Stack Cluster Implementation
export const CLUSTER_NAME = "my-stack"
export const CLUSTER_TYPE = "web-application"
export const CLUSTER_VERSION = "1.0.0"
# Cluster creation
export def "cluster create" [
target: string # Target infrastructure
--config: string = "" # Custom configuration file
--dry-run: bool = false # Show what would be created
--wait: bool = true # Wait for cluster to be ready
] -&gt; record {
let cluster_config = if $config != "" {
open $config | from toml
} else {
load_default_cluster_config
}
if $dry_run {
let deployment_plan = generate_deployment_plan $target $cluster_config
return {
action: "create",
cluster: $CLUSTER_NAME,
target: $target,
plan: $deployment_plan,
status: "dry-run"
}
}
print $"Creating cluster ($CLUSTER_NAME) on ($target)..."
# Deploy services in dependency order
let services = get_service_deployment_order $cluster_config.services
let deployment_results = []
for service in $services {
print $"Deploying service: ($service.name)"
let result = try {
deploy_service $service $target $wait
} catch { |e|
# Rollback on failure
rollback_cluster $target $deployment_results
error make {msg: $"Service deployment failed: ($e.msg)"}
}
$deployment_results = ($deployment_results | append $result)
}
# Configure inter-service communication
configure_service_mesh $target $deployment_results
{
cluster: $CLUSTER_NAME,
target: $target,
status: "created",
services: $deployment_results,
created_at: (date now)
}
}
# Cluster deletion
export def "cluster delete" [
target: string # Target infrastructure
--force: bool = false # Force deletion without confirmation
--cleanup-data: bool = false # Remove persistent data
] -&gt; record {
let cluster_status = get_cluster_status $target
if $cluster_status.status != "running" {
error make {msg: $"Cluster ($CLUSTER_NAME) is not running on ($target)"}
}
if not $force {
let confirm = (input $"Delete cluster ($CLUSTER_NAME) from ($target)? (y/N) ")
if $confirm != "y" and $confirm != "yes" {
return {action: "delete", cluster: $CLUSTER_NAME, status: "cancelled"}
}
}
print $"Deleting cluster ($CLUSTER_NAME) from ($target)..."
# Delete services in reverse dependency order
let services = get_service_deletion_order $cluster_status.services
let deletion_results = []
for service in $services {
print $"Removing service: ($service.name)"
let result = try {
remove_service $service $target $cleanup_data
} catch { |e|
print $"Warning: Failed to remove service ($service.name): ($e.msg)"
}
$deletion_results = ($deletion_results | append $result)
}
{
cluster: $CLUSTER_NAME,
target: $target,
status: "deleted",
services_removed: $deletion_results,
data_removed: $cleanup_data,
deleted_at: (date now)
}
}
</code></pre>
<h2 id="testing-and-validation"><a class="header" href="#testing-and-validation">Testing and Validation</a></h2>
<h3 id="testing-framework"><a class="header" href="#testing-framework">Testing Framework</a></h3>
<p><strong>Test Types</strong>:</p>
<ul>
<li><strong>Unit Tests</strong>: Individual function and module testing</li>
<li><strong>Integration Tests</strong>: Cross-component interaction testing</li>
<li><strong>End-to-End Tests</strong>: Complete workflow testing</li>
<li><strong>Performance Tests</strong>: Load and performance validation</li>
<li><strong>Security Tests</strong>: Security and vulnerability testing</li>
</ul>
<h3 id="extension-testing-commands"><a class="header" href="#extension-testing-commands">Extension Testing Commands</a></h3>
<p><strong>Workspace Testing Tools</strong>:</p>
<pre><code class="language-bash"># Validate extension syntax and structure
nu workspace.nu tools validate-extension providers/my-cloud
# Run extension unit tests
nu workspace.nu tools test-extension taskservs/my-service --test-type unit
# Integration testing with real infrastructure
nu workspace.nu tools test-extension clusters/my-stack --test-type integration --target test-env
# Performance testing
nu workspace.nu tools test-extension providers/my-cloud --test-type performance --duration 5m
</code></pre>
<h3 id="automated-testing"><a class="header" href="#automated-testing">Automated Testing</a></h3>
<p><strong>Test Runner</strong> (<code>tests/run-tests.nu</code>):</p>
<pre><code class="language-nushell">#!/usr/bin/env nu
# Automated test runner for extensions
def main [
extension_type: string # Extension type: providers, taskservs, clusters
extension_name: string # Extension name
--test-types: string = "all" # Test types to run: unit, integration, e2e, all
--target: string = "local" # Test target environment
--verbose: bool = false # Verbose test output
--parallel: bool = true # Run tests in parallel
] -&gt; record {
let extension_path = $"workspace/extensions/($extension_type)/($extension_name)"
if not ($extension_path | path exists) {
error make {msg: $"Extension not found: ($extension_path)"}
}
let test_types = if $test_types == "all" {
["unit", "integration", "e2e"]
} else {
$test_types | split row ","
}
print $"Running tests for ($extension_type)/($extension_name)..."
let test_results = []
for test_type in $test_types {
print $"Running ($test_type) tests..."
let result = try {
run_test_suite $extension_path $test_type $target $verbose
} catch { |e|
{
test_type: $test_type,
status: "failed",
error: $e.msg,
duration: 0
}
}
$test_results = ($test_results | append $result)
}
let total_tests = ($test_results | length)
let passed_tests = ($test_results | where status == "passed" | length)
let failed_tests = ($test_results | where status == "failed" | length)
{
extension: $"($extension_type)/($extension_name)",
test_results: $test_results,
summary: {
total: $total_tests,
passed: $passed_tests,
failed: $failed_tests,
success_rate: ($passed_tests / $total_tests * 100)
},
completed_at: (date now)
}
}
</code></pre>
<h2 id="publishing-and-distribution"><a class="header" href="#publishing-and-distribution">Publishing and Distribution</a></h2>
<h3 id="extension-publishing"><a class="header" href="#extension-publishing">Extension Publishing</a></h3>
<p><strong>Publishing Process</strong>:</p>
<ol>
<li><strong>Validation</strong>: Comprehensive testing and validation</li>
<li><strong>Documentation</strong>: Complete documentation and examples</li>
<li><strong>Packaging</strong>: Create distribution packages</li>
<li><strong>Registry</strong>: Publish to extension registry</li>
<li><strong>Versioning</strong>: Semantic version tagging</li>
</ol>
<h3 id="publishing-commands"><a class="header" href="#publishing-commands">Publishing Commands</a></h3>
<pre><code class="language-bash"># Validate extension for publishing
nu workspace.nu tools validate-for-publish providers/my-cloud
# Create distribution package
nu workspace.nu tools package-extension providers/my-cloud --version 1.0.0
# Publish to registry
nu workspace.nu tools publish-extension providers/my-cloud --registry official
# Tag version
nu workspace.nu tools tag-extension providers/my-cloud --version 1.0.0 --push
</code></pre>
<h3 id="extension-registry"><a class="header" href="#extension-registry">Extension Registry</a></h3>
<p><strong>Registry Structure</strong>:</p>
<pre><code>Extension Registry
├── providers/
│ ├── aws/ # Official AWS provider
│ ├── upcloud/ # Official UpCloud provider
│ └── community/ # Community providers
├── taskservs/
│ ├── kubernetes/ # Official Kubernetes service
│ ├── databases/ # Database services
│ └── monitoring/ # Monitoring services
└── clusters/
├── web-stacks/ # Web application stacks
├── data-platforms/ # Data processing platforms
└── ci-cd/ # CI/CD pipelines
</code></pre>
<h2 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h2>
<h3 id="code-quality"><a class="header" href="#code-quality">Code Quality</a></h3>
<p><strong>Function Design</strong>:</p>
<pre><code class="language-nushell"># Good: Single responsibility, clear parameters, comprehensive error handling
export def "provider create-server" [
name: string # Server name (must be unique in region)
plan: string # Server plan (see list-plans for options)
--zone: string = "auto" # Deployment zone (auto-selects optimal zone)
--dry-run: bool = false # Preview changes without creating resources
] -&gt; record { # Returns creation result with server details
# Validate inputs first
if ($name | str length) == 0 {
error make {
msg: "Server name cannot be empty"
help: "Provide a unique name for the server"
}
}
# Implementation with comprehensive error handling
# ...
}
# Bad: Unclear parameters, no error handling
def create [n, p] {
# Missing validation and error handling
api_call $n $p
}
</code></pre>
<p><strong>Configuration Management</strong>:</p>
<pre><code class="language-nushell"># Good: Configuration-driven with validation
def get_api_endpoint [provider: string] -&gt; string {
let config = get-config-value $"providers.($provider).api_url"
if ($config | is-empty) {
error make {
msg: $"API URL not configured for provider ($provider)",
help: $"Add 'api_url' to providers.($provider) configuration"
}
}
$config
}
# Bad: Hardcoded values
def get_api_endpoint [] {
"https://api.provider.com" # Never hardcode!
}
</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_with_context [name: string, config: record] -&gt; record {
try {
# Validate configuration
validate_server_config $config
} catch { |e|
error make {
msg: $"Invalid server configuration: ($e.msg)",
label: {text: "configuration error", span: $e.span?},
help: "Check configuration syntax and required fields"
}
}
try {
# Create server via API
let result = api_create_server $name $config
return $result
} catch { |e|
match $e.msg {
$msg if ($msg | str contains "quota") =&gt; {
error make {
msg: $"Server creation failed: quota limit exceeded",
help: "Contact support to increase quota or delete unused servers"
}
},
$msg if ($msg | str contains "auth") =&gt; {
error make {
msg: "Server creation failed: authentication error",
help: "Check API credentials and permissions"
}
},
_ =&gt; {
error make {
msg: $"Server creation failed: ($e.msg)",
help: "Check network connectivity and try again"
}
}
}
}
}
</code></pre>
<h3 id="testing-practices"><a class="header" href="#testing-practices">Testing Practices</a></h3>
<p><strong>Test Organization</strong>:</p>
<pre><code class="language-nushell"># Organize tests by functionality
# tests/unit/server-creation-test.nu
def test_valid_server_creation [] {
# Test valid cases with various inputs
let valid_configs = [
{name: "test-1", plan: "small"},
{name: "test-2", plan: "medium"},
{name: "test-3", plan: "large"}
]
for config in $valid_configs {
let result = create_server $config.name $config.plan --dry-run
assert ($result.status == "dry-run")
assert ($result.config.name == $config.name)
}
}
def test_invalid_inputs [] {
# Test error conditions
let invalid_cases = [
{name: "", plan: "small", error: "empty name"},
{name: "test", plan: "invalid", error: "invalid plan"},
{name: "test with spaces", plan: "small", error: "invalid characters"}
]
for case in $invalid_cases {
try {
create_server $case.name $case.plan --dry-run
assert false $"Should have failed: ($case.error)"
} catch { |e|
# Verify specific error message
assert ($e.msg | str contains $case.error)
}
}
}
</code></pre>
<h3 id="documentation-standards"><a class="header" href="#documentation-standards">Documentation Standards</a></h3>
<p><strong>Function Documentation</strong>:</p>
<pre><code class="language-nushell"># Comprehensive function documentation
def "provider create-server" [
name: string # Server name - must be unique within the provider
plan: string # Server size plan (run 'provider list-plans' for options)
--zone: string = "auto" # Target zone - 'auto' selects optimal zone based on load
--template: string = "ubuntu22" # OS template - see 'provider list-templates' for options
--storage: int = 25 # Storage size in GB (minimum 10, maximum 2048)
--dry-run: bool = false # Preview mode - shows what would be created without creating
] -&gt; record { # Returns server creation details including ID and IP
"""
Creates a new server instance with the specified configuration.
This function provisions a new server using the provider's API, configures
basic security settings, and returns the server details upon successful creation.
Examples:
# Create a small server with default settings
provider create-server "web-01" "small"
# Create with specific zone and storage
provider create-server "db-01" "large" --zone "us-west-2" --storage 100
# Preview what would be created
provider create-server "test" "medium" --dry-run
Error conditions:
- Invalid server name (empty, invalid characters)
- Invalid plan (not in supported plans list)
- Insufficient quota or permissions
- Network connectivity issues
Returns:
Record with keys: server, status, id, ip_address, created_at
"""
# Implementation...
}
</code></pre>
<h2 id="troubleshooting"><a class="header" href="#troubleshooting">Troubleshooting</a></h2>
<h3 id="common-development-issues"><a class="header" href="#common-development-issues">Common Development Issues</a></h3>
<h4 id="extension-not-found"><a class="header" href="#extension-not-found">Extension Not Found</a></h4>
<p><strong>Error</strong>: <code>Extension 'my-provider' not found</code></p>
<pre><code class="language-bash"># Solution: Check extension location and structure
ls -la workspace/extensions/providers/my-provider
nu workspace/lib/path-resolver.nu resolve_extension "providers" "my-provider"
# Validate extension structure
nu workspace.nu tools validate-extension providers/my-provider
</code></pre>
<h4 id="configuration-errors"><a class="header" href="#configuration-errors">Configuration Errors</a></h4>
<p><strong>Error</strong>: <code>Invalid KCL configuration</code></p>
<pre><code class="language-bash"># Solution: Validate KCL syntax
kcl check workspace/extensions/providers/my-provider/kcl/
# Format KCL files
kcl fmt workspace/extensions/providers/my-provider/kcl/
# Test with example data
kcl run workspace/extensions/providers/my-provider/kcl/settings.k -D api_key="test"
</code></pre>
<h4 id="api-integration-issues"><a class="header" href="#api-integration-issues">API Integration Issues</a></h4>
<p><strong>Error</strong>: <code>Authentication failed</code></p>
<pre><code class="language-bash"># Solution: Test credentials and connectivity
curl -H "Authorization: Bearer $API_KEY" https://api.provider.com/auth/test
# Debug API calls
export PROVISIONING_DEBUG=true
export PROVISIONING_LOG_LEVEL=debug
nu workspace/extensions/providers/my-provider/nulib/provider.nu test --test-type basic
</code></pre>
<h3 id="debug-mode"><a class="header" href="#debug-mode">Debug Mode</a></h3>
<p><strong>Enable Extension Debugging</strong>:</p>
<pre><code class="language-bash"># Set debug environment
export PROVISIONING_DEBUG=true
export PROVISIONING_LOG_LEVEL=debug
export PROVISIONING_WORKSPACE_USER=$USER
# Run extension with debug
nu workspace/extensions/providers/my-provider/nulib/provider.nu create-server test-server small --dry-run
</code></pre>
<h3 id="performance-optimization"><a class="header" href="#performance-optimization">Performance Optimization</a></h3>
<p><strong>Extension Performance</strong>:</p>
<pre><code class="language-bash"># Profile extension performance
time nu workspace/extensions/providers/my-provider/nulib/provider.nu list-servers
# Monitor resource usage
nu workspace/tools/runtime-manager.nu monitor --duration 1m --interval 5s
# Optimize API calls (use caching)
export PROVISIONING_CACHE_ENABLED=true
export PROVISIONING_CACHE_TTL=300 # 5 minutes
</code></pre>
<p>This extension development guide provides a comprehensive framework for creating high-quality, maintainable extensions that integrate seamlessly with provisionings architecture and workflows.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../development/distribution-process.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/PROVIDER_AGNOSTIC_ARCHITECTURE.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/distribution-process.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/PROVIDER_AGNOSTIC_ARCHITECTURE.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>