provisioning/docs/book/development/QUICK_PROVIDER_GUIDE.html
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

509 lines
24 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>Quick Provider Guide - 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/QUICK_PROVIDER_GUIDE.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="quick-developer-guide-adding-new-providers"><a class="header" href="#quick-developer-guide-adding-new-providers">Quick Developer Guide: Adding New Providers</a></h1>
<p>This guide shows how to quickly add a new provider to the provider-agnostic infrastructure system.</p>
<h2 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h2>
<ul>
<li>Understand the <a href="PROVIDER_AGNOSTIC_ARCHITECTURE.html">Provider-Agnostic Architecture</a></li>
<li>Have the providers SDK or API available</li>
<li>Know the providers authentication requirements</li>
</ul>
<h2 id="5-minute-provider-addition"><a class="header" href="#5-minute-provider-addition">5-Minute Provider Addition</a></h2>
<h3 id="step-1-create-provider-directory"><a class="header" href="#step-1-create-provider-directory">Step 1: Create Provider Directory</a></h3>
<pre><code class="language-bash">mkdir -p provisioning/extensions/providers/{provider_name}
mkdir -p provisioning/extensions/providers/{provider_name}/nulib/{provider_name}
</code></pre>
<h3 id="step-2-copy-template-and-customize"><a class="header" href="#step-2-copy-template-and-customize">Step 2: Copy Template and Customize</a></h3>
<pre><code class="language-bash"># Copy the local provider as a template
cp provisioning/extensions/providers/local/provider.nu \
provisioning/extensions/providers/{provider_name}/provider.nu
</code></pre>
<h3 id="step-3-update-provider-metadata"><a class="header" href="#step-3-update-provider-metadata">Step 3: Update Provider Metadata</a></h3>
<p>Edit <code>provisioning/extensions/providers/{provider_name}/provider.nu</code>:</p>
<pre><code class="language-nushell">export def get-provider-metadata []: nothing -&gt; record {
{
name: "your_provider_name"
version: "1.0.0"
description: "Your Provider Description"
capabilities: {
server_management: true
network_management: true # Set based on provider features
auto_scaling: false # Set based on provider features
multi_region: true # Set based on provider features
serverless: false # Set based on provider features
# ... customize other capabilities
}
}
}
</code></pre>
<h3 id="step-4-implement-core-functions"><a class="header" href="#step-4-implement-core-functions">Step 4: Implement Core Functions</a></h3>
<p>The provider interface requires these essential functions:</p>
<pre><code class="language-nushell"># Required: Server operations
export def query_servers [find?: string, cols?: string]: nothing -&gt; list {
# Call your provider's server listing API
your_provider_query_servers $find $cols
}
export def create_server [settings: record, server: record, check: bool, wait: bool]: nothing -&gt; bool {
# Call your provider's server creation API
your_provider_create_server $settings $server $check $wait
}
export def server_exists [server: record, error_exit: bool]: nothing -&gt; bool {
# Check if server exists in your provider
your_provider_server_exists $server $error_exit
}
export def get_ip [settings: record, server: record, ip_type: string, error_exit: bool]: nothing -&gt; string {
# Get server IP from your provider
your_provider_get_ip $settings $server $ip_type $error_exit
}
# Required: Infrastructure operations
export def delete_server [settings: record, server: record, keep_storage: bool, error_exit: bool]: nothing -&gt; bool {
your_provider_delete_server $settings $server $keep_storage $error_exit
}
export def server_state [server: record, new_state: string, error_exit: bool, wait: bool, settings: record]: nothing -&gt; bool {
your_provider_server_state $server $new_state $error_exit $wait $settings
}
</code></pre>
<h3 id="step-5-create-provider-specific-functions"><a class="header" href="#step-5-create-provider-specific-functions">Step 5: Create Provider-Specific Functions</a></h3>
<p>Create <code>provisioning/extensions/providers/{provider_name}/nulib/{provider_name}/servers.nu</code>:</p>
<pre><code class="language-nushell"># Example: DigitalOcean provider functions
export def digitalocean_query_servers [find?: string, cols?: string]: nothing -&gt; list {
# Use DigitalOcean API to list droplets
let droplets = (http get "https://api.digitalocean.com/v2/droplets"
--headers { Authorization: $"Bearer ($env.DO_TOKEN)" })
$droplets.droplets | select name status memory disk region.name networks.v4
}
export def digitalocean_create_server [settings: record, server: record, check: bool, wait: bool]: nothing -&gt; bool {
# Use DigitalOcean API to create droplet
let payload = {
name: $server.hostname
region: $server.zone
size: $server.plan
image: ($server.image? | default "ubuntu-20-04-x64")
}
if $check {
print $"Would create DigitalOcean droplet: ($payload)"
return true
}
let result = (http post "https://api.digitalocean.com/v2/droplets"
--headers { Authorization: $"Bearer ($env.DO_TOKEN)" }
--content-type application/json
$payload)
$result.droplet.id != null
}
</code></pre>
<h3 id="step-6-test-your-provider"><a class="header" href="#step-6-test-your-provider">Step 6: Test Your Provider</a></h3>
<pre><code class="language-bash"># Test provider discovery
nu -c "use provisioning/core/nulib/lib_provisioning/providers/registry.nu *; init-provider-registry; list-providers"
# Test provider loading
nu -c "use provisioning/core/nulib/lib_provisioning/providers/loader.nu *; load-provider 'your_provider_name'"
# Test provider functions
nu -c "use provisioning/extensions/providers/your_provider_name/provider.nu *; query_servers"
</code></pre>
<h3 id="step-7-add-provider-to-infrastructure"><a class="header" href="#step-7-add-provider-to-infrastructure">Step 7: Add Provider to Infrastructure</a></h3>
<p>Add to your KCL configuration:</p>
<pre><code class="language-kcl"># workspace/infra/example/servers.k
servers = [
{
hostname = "test-server"
provider = "your_provider_name"
zone = "your-region-1"
plan = "your-instance-type"
}
]
</code></pre>
<h2 id="provider-templates"><a class="header" href="#provider-templates">Provider Templates</a></h2>
<h3 id="cloud-provider-template"><a class="header" href="#cloud-provider-template">Cloud Provider Template</a></h3>
<p>For cloud providers (AWS, GCP, Azure, etc.):</p>
<pre><code class="language-nushell"># Use HTTP calls to cloud APIs
export def cloud_query_servers [find?: string, cols?: string]: nothing -&gt; list {
let auth_header = { Authorization: $"Bearer ($env.PROVIDER_TOKEN)" }
let servers = (http get $"($env.PROVIDER_API_URL)/servers" --headers $auth_header)
$servers | select name status region instance_type public_ip
}
</code></pre>
<h3 id="container-platform-template"><a class="header" href="#container-platform-template">Container Platform Template</a></h3>
<p>For container platforms (Docker, Podman, etc.):</p>
<pre><code class="language-nushell"># Use CLI commands for container platforms
export def container_query_servers [find?: string, cols?: string]: nothing -&gt; list {
let containers = (docker ps --format json | from json)
$containers | select Names State Status Image
}
</code></pre>
<h3 id="bare-metal-provider-template"><a class="header" href="#bare-metal-provider-template">Bare Metal Provider Template</a></h3>
<p>For bare metal or existing servers:</p>
<pre><code class="language-nushell"># Use SSH or local commands
export def baremetal_query_servers [find?: string, cols?: string]: nothing -&gt; list {
# Read from inventory file or ping servers
let inventory = (open inventory.yaml | from yaml)
$inventory.servers | select hostname ip_address status
}
</code></pre>
<h2 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h2>
<h3 id="1-error-handling"><a class="header" href="#1-error-handling">1. Error Handling</a></h3>
<pre><code class="language-nushell">export def provider_operation []: nothing -&gt; any {
try {
# Your provider operation
provider_api_call
} catch {|err|
log-error $"Provider operation failed: ($err.msg)" "provider"
if $error_exit { exit 1 }
null
}
}
</code></pre>
<h3 id="2-authentication"><a class="header" href="#2-authentication">2. Authentication</a></h3>
<pre><code class="language-nushell"># Check for required environment variables
def check_auth []: nothing -&gt; bool {
if ($env | get -o PROVIDER_TOKEN) == null {
log-error "PROVIDER_TOKEN environment variable required" "auth"
return false
}
true
}
</code></pre>
<h3 id="3-rate-limiting"><a class="header" href="#3-rate-limiting">3. Rate Limiting</a></h3>
<pre><code class="language-nushell"># Add delays for API rate limits
def api_call_with_retry [url: string]: nothing -&gt; any {
mut attempts = 0
mut max_attempts = 3
while $attempts &lt; $max_attempts {
try {
return (http get $url)
} catch {
$attempts += 1
sleep 1sec
}
}
error make { msg: "API call failed after retries" }
}
</code></pre>
<h3 id="4-provider-capabilities"><a class="header" href="#4-provider-capabilities">4. Provider Capabilities</a></h3>
<p>Set capabilities accurately:</p>
<pre><code class="language-nushell">capabilities: {
server_management: true # Can create/delete servers
network_management: true # Can manage networks/VPCs
storage_management: true # Can manage block storage
load_balancer: false # No load balancer support
dns_management: false # No DNS support
auto_scaling: true # Supports auto-scaling
spot_instances: false # No spot instance support
multi_region: true # Supports multiple regions
containers: false # No container support
serverless: false # No serverless support
encryption_at_rest: true # Supports encryption
compliance_certifications: ["SOC2"] # Available certifications
}
</code></pre>
<h2 id="testing-checklist"><a class="header" href="#testing-checklist">Testing Checklist</a></h2>
<ul>
<li><input disabled="" type="checkbox"/>
Provider discovered by registry</li>
<li><input disabled="" type="checkbox"/>
Provider loads without errors</li>
<li><input disabled="" type="checkbox"/>
All required interface functions implemented</li>
<li><input disabled="" type="checkbox"/>
Provider metadata correct</li>
<li><input disabled="" type="checkbox"/>
Authentication working</li>
<li><input disabled="" type="checkbox"/>
Can query existing resources</li>
<li><input disabled="" type="checkbox"/>
Can create new resources (in test mode)</li>
<li><input disabled="" type="checkbox"/>
Error handling working</li>
<li><input disabled="" type="checkbox"/>
Compatible with existing infrastructure configs</li>
</ul>
<h2 id="common-issues"><a class="header" href="#common-issues">Common Issues</a></h2>
<h3 id="provider-not-found"><a class="header" href="#provider-not-found">Provider Not Found</a></h3>
<pre><code class="language-bash"># Check provider directory structure
ls -la provisioning/extensions/providers/your_provider_name/
# Ensure provider.nu exists and has get-provider-metadata function
grep "get-provider-metadata" provisioning/extensions/providers/your_provider_name/provider.nu
</code></pre>
<h3 id="interface-validation-failed"><a class="header" href="#interface-validation-failed">Interface Validation Failed</a></h3>
<pre><code class="language-bash"># Check which functions are missing
nu -c "use provisioning/core/nulib/lib_provisioning/providers/interface.nu *; validate-provider-interface 'your_provider_name'"
</code></pre>
<h3 id="authentication-errors"><a class="header" href="#authentication-errors">Authentication Errors</a></h3>
<pre><code class="language-bash"># Check environment variables
env | grep PROVIDER
# Test API access manually
curl -H "Authorization: Bearer $PROVIDER_TOKEN" https://api.provider.com/test
</code></pre>
<h2 id="next-steps"><a class="header" href="#next-steps">Next Steps</a></h2>
<ol>
<li><strong>Documentation</strong>: Add provider-specific documentation to <code>docs/providers/</code></li>
<li><strong>Examples</strong>: Create example infrastructure using your provider</li>
<li><strong>Testing</strong>: Add integration tests for your provider</li>
<li><strong>Optimization</strong>: Implement caching and performance optimizations</li>
<li><strong>Features</strong>: Add provider-specific advanced features</li>
</ol>
<h2 id="getting-help"><a class="header" href="#getting-help">Getting Help</a></h2>
<ul>
<li>Check existing providers for implementation patterns</li>
<li>Review the <a href="PROVIDER_AGNOSTIC_ARCHITECTURE.html#provider-interface">Provider Interface Documentation</a></li>
<li>Test with the provider test suite: <code>./provisioning/tools/test-provider-agnostic.nu</code></li>
<li>Run migration checks: <code>./provisioning/tools/migrate-to-provider-agnostic.nu status</code></li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../development/PROVIDER_AGNOSTIC_ARCHITECTURE.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/TASKSERV_DEVELOPER_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/PROVIDER_AGNOSTIC_ARCHITECTURE.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/TASKSERV_DEVELOPER_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>