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

1258 lines
45 KiB
HTML

<!DOCTYPE HTML>
<html lang="en" class="ayu sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>SDKs - 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/api/sdks.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="sdk-documentation"><a class="header" href="#sdk-documentation">SDK Documentation</a></h1>
<p>This document provides comprehensive documentation for the official SDKs and client libraries available for provisioning.</p>
<h2 id="available-sdks"><a class="header" href="#available-sdks">Available SDKs</a></h2>
<p>Provisioning provides SDKs in multiple languages to facilitate integration:</p>
<h3 id="official-sdks"><a class="header" href="#official-sdks">Official SDKs</a></h3>
<ul>
<li><strong>Python SDK</strong> (<code>provisioning-client</code>) - Full-featured Python client</li>
<li><strong>JavaScript/TypeScript SDK</strong> (<code>@provisioning/client</code>) - Node.js and browser support</li>
<li><strong>Go SDK</strong> (<code>go-provisioning-client</code>) - Go client library</li>
<li><strong>Rust SDK</strong> (<code>provisioning-rs</code>) - Native Rust integration</li>
</ul>
<h3 id="community-sdks"><a class="header" href="#community-sdks">Community SDKs</a></h3>
<ul>
<li><strong>Java SDK</strong> - Community-maintained Java client</li>
<li><strong>C# SDK</strong> - .NET client library</li>
<li><strong>PHP SDK</strong> - PHP client library</li>
</ul>
<h2 id="python-sdk"><a class="header" href="#python-sdk">Python SDK</a></h2>
<h3 id="installation"><a class="header" href="#installation">Installation</a></h3>
<pre><code class="language-bash"># Install from PyPI
pip install provisioning-client
# Or install development version
pip install git+https://github.com/provisioning-systems/python-client.git
</code></pre>
<h3 id="quick-start"><a class="header" href="#quick-start">Quick Start</a></h3>
<pre><code class="language-python">from provisioning_client import ProvisioningClient
import asyncio
async def main():
# Initialize client
client = ProvisioningClient(
base_url="http://localhost:9090",
auth_url="http://localhost:8081",
username="admin",
password="your-password"
)
try:
# Authenticate
token = await client.authenticate()
print(f"Authenticated with token: {token[:20]}...")
# Create a server workflow
task_id = client.create_server_workflow(
infra="production",
settings="prod-settings.k",
wait=False
)
print(f"Server workflow created: {task_id}")
# Wait for completion
task = client.wait_for_task_completion(task_id, timeout=600)
print(f"Task completed with status: {task.status}")
if task.status == "Completed":
print(f"Output: {task.output}")
elif task.status == "Failed":
print(f"Error: {task.error}")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
asyncio.run(main())
</code></pre>
<h3 id="advanced-usage"><a class="header" href="#advanced-usage">Advanced Usage</a></h3>
<h4 id="websocket-integration"><a class="header" href="#websocket-integration">WebSocket Integration</a></h4>
<pre><code class="language-python">async def monitor_workflows():
client = ProvisioningClient()
await client.authenticate()
# Set up event handlers
async def on_task_update(event):
print(f"Task {event['data']['task_id']} status: {event['data']['status']}")
async def on_progress_update(event):
print(f"Progress: {event['data']['progress']}% - {event['data']['current_step']}")
client.on_event('TaskStatusChanged', on_task_update)
client.on_event('WorkflowProgressUpdate', on_progress_update)
# Connect to WebSocket
await client.connect_websocket(['TaskStatusChanged', 'WorkflowProgressUpdate'])
# Keep connection alive
await asyncio.sleep(3600) # Monitor for 1 hour
</code></pre>
<h4 id="batch-operations"><a class="header" href="#batch-operations">Batch Operations</a></h4>
<pre><code class="language-python">async def execute_batch_deployment():
client = ProvisioningClient()
await client.authenticate()
batch_config = {
"name": "production_deployment",
"version": "1.0.0",
"storage_backend": "surrealdb",
"parallel_limit": 5,
"rollback_enabled": True,
"operations": [
{
"id": "servers",
"type": "server_batch",
"provider": "upcloud",
"dependencies": [],
"config": {
"server_configs": [
{"name": "web-01", "plan": "2xCPU-4GB", "zone": "de-fra1"},
{"name": "web-02", "plan": "2xCPU-4GB", "zone": "de-fra1"}
]
}
},
{
"id": "kubernetes",
"type": "taskserv_batch",
"provider": "upcloud",
"dependencies": ["servers"],
"config": {
"taskservs": ["kubernetes", "cilium", "containerd"]
}
}
]
}
# Execute batch operation
batch_result = await client.execute_batch_operation(batch_config)
print(f"Batch operation started: {batch_result['batch_id']}")
# Monitor progress
while True:
status = await client.get_batch_status(batch_result['batch_id'])
print(f"Batch status: {status['status']} - {status.get('progress', 0)}%")
if status['status'] in ['Completed', 'Failed', 'Cancelled']:
break
await asyncio.sleep(10)
print(f"Batch operation finished: {status['status']}")
</code></pre>
<h4 id="error-handling-with-retries"><a class="header" href="#error-handling-with-retries">Error Handling with Retries</a></h4>
<pre><code class="language-python">from provisioning_client.exceptions import (
ProvisioningAPIError,
AuthenticationError,
ValidationError,
RateLimitError
)
from tenacity import retry, stop_after_attempt, wait_exponential
class RobustProvisioningClient(ProvisioningClient):
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=4, max=10)
)
async def create_server_workflow_with_retry(self, **kwargs):
try:
return await self.create_server_workflow(**kwargs)
except RateLimitError as e:
print(f"Rate limited, retrying in {e.retry_after} seconds...")
await asyncio.sleep(e.retry_after)
raise
except AuthenticationError:
print("Authentication failed, re-authenticating...")
await self.authenticate()
raise
except ValidationError as e:
print(f"Validation error: {e}")
# Don't retry validation errors
raise
except ProvisioningAPIError as e:
print(f"API error: {e}")
raise
# Usage
async def robust_workflow():
client = RobustProvisioningClient()
try:
task_id = await client.create_server_workflow_with_retry(
infra="production",
settings="config.k"
)
print(f"Workflow created successfully: {task_id}")
except Exception as e:
print(f"Failed after retries: {e}")
</code></pre>
<h3 id="api-reference"><a class="header" href="#api-reference">API Reference</a></h3>
<h4 id="provisioningclient-class"><a class="header" href="#provisioningclient-class">ProvisioningClient Class</a></h4>
<pre><code class="language-python">class ProvisioningClient:
def __init__(self,
base_url: str = "http://localhost:9090",
auth_url: str = "http://localhost:8081",
username: str = None,
password: str = None,
token: str = None):
"""Initialize the provisioning client"""
async def authenticate(self) -&gt; str:
"""Authenticate and get JWT token"""
def create_server_workflow(self,
infra: str,
settings: str = "config.k",
check_mode: bool = False,
wait: bool = False) -&gt; str:
"""Create a server provisioning workflow"""
def create_taskserv_workflow(self,
operation: str,
taskserv: str,
infra: str,
settings: str = "config.k",
check_mode: bool = False,
wait: bool = False) -&gt; str:
"""Create a task service workflow"""
def get_task_status(self, task_id: str) -&gt; WorkflowTask:
"""Get the status of a specific task"""
def wait_for_task_completion(self,
task_id: str,
timeout: int = 300,
poll_interval: int = 5) -&gt; WorkflowTask:
"""Wait for a task to complete"""
async def connect_websocket(self, event_types: List[str] = None):
"""Connect to WebSocket for real-time updates"""
def on_event(self, event_type: str, handler: Callable):
"""Register an event handler"""
</code></pre>
<h2 id="javascripttypescript-sdk"><a class="header" href="#javascripttypescript-sdk">JavaScript/TypeScript SDK</a></h2>
<h3 id="installation-1"><a class="header" href="#installation-1">Installation</a></h3>
<pre><code class="language-bash"># npm
npm install @provisioning/client
# yarn
yarn add @provisioning/client
# pnpm
pnpm add @provisioning/client
</code></pre>
<h3 id="quick-start-1"><a class="header" href="#quick-start-1">Quick Start</a></h3>
<pre><code class="language-typescript">import { ProvisioningClient } from '@provisioning/client';
async function main() {
const client = new ProvisioningClient({
baseUrl: 'http://localhost:9090',
authUrl: 'http://localhost:8081',
username: 'admin',
password: 'your-password'
});
try {
// Authenticate
await client.authenticate();
console.log('Authentication successful');
// Create server workflow
const taskId = await client.createServerWorkflow({
infra: 'production',
settings: 'prod-settings.k'
});
console.log(`Server workflow created: ${taskId}`);
// Wait for completion
const task = await client.waitForTaskCompletion(taskId);
console.log(`Task completed with status: ${task.status}`);
} catch (error) {
console.error('Error:', error.message);
}
}
main();
</code></pre>
<h3 id="react-integration"><a class="header" href="#react-integration">React Integration</a></h3>
<pre><code class="language-tsx">import React, { useState, useEffect } from 'react';
import { ProvisioningClient } from '@provisioning/client';
interface Task {
id: string;
name: string;
status: string;
progress?: number;
}
const WorkflowDashboard: React.FC = () =&gt; {
const [client] = useState(() =&gt; new ProvisioningClient({
baseUrl: process.env.REACT_APP_API_URL,
username: process.env.REACT_APP_USERNAME,
password: process.env.REACT_APP_PASSWORD
}));
const [tasks, setTasks] = useState&lt;Task[]&gt;([]);
const [connected, setConnected] = useState(false);
useEffect(() =&gt; {
const initClient = async () =&gt; {
try {
await client.authenticate();
// Set up WebSocket event handlers
client.on('TaskStatusChanged', (event: any) =&gt; {
setTasks(prev =&gt; prev.map(task =&gt;
task.id === event.data.task_id
? { ...task, status: event.data.status, progress: event.data.progress }
: task
));
});
client.on('websocketConnected', () =&gt; {
setConnected(true);
});
client.on('websocketDisconnected', () =&gt; {
setConnected(false);
});
// Connect WebSocket
await client.connectWebSocket(['TaskStatusChanged', 'WorkflowProgressUpdate']);
// Load initial tasks
const initialTasks = await client.listTasks();
setTasks(initialTasks);
} catch (error) {
console.error('Failed to initialize client:', error);
}
};
initClient();
return () =&gt; {
client.disconnectWebSocket();
};
}, [client]);
const createServerWorkflow = async () =&gt; {
try {
const taskId = await client.createServerWorkflow({
infra: 'production',
settings: 'config.k'
});
// Add to tasks list
setTasks(prev =&gt; [...prev, {
id: taskId,
name: 'Server Creation',
status: 'Pending'
}]);
} catch (error) {
console.error('Failed to create workflow:', error);
}
};
return (
&lt;div className="workflow-dashboard"&gt;
&lt;div className="header"&gt;
&lt;h1&gt;Workflow Dashboard&lt;/h1&gt;
&lt;div className={`connection-status ${connected ? 'connected' : 'disconnected'}`}&gt;
{connected ? '🟢 Connected' : '🔴 Disconnected'}
&lt;/div&gt;
&lt;/div&gt;
&lt;div className="controls"&gt;
&lt;button onClick={createServerWorkflow}&gt;
Create Server Workflow
&lt;/button&gt;
&lt;/div&gt;
&lt;div className="tasks"&gt;
{tasks.map(task =&gt; (
&lt;div key={task.id} className="task-card"&gt;
&lt;h3&gt;{task.name}&lt;/h3&gt;
&lt;div className="task-status"&gt;
&lt;span className={`status ${task.status.toLowerCase()}`}&gt;
{task.status}
&lt;/span&gt;
{task.progress &amp;&amp; (
&lt;div className="progress-bar"&gt;
&lt;div
className="progress-fill"
style={{ width: `${task.progress}%` }}
/&gt;
&lt;span className="progress-text"&gt;{task.progress}%&lt;/span&gt;
&lt;/div&gt;
)}
&lt;/div&gt;
&lt;/div&gt;
))}
&lt;/div&gt;
&lt;/div&gt;
);
};
export default WorkflowDashboard;
</code></pre>
<h3 id="nodejs-cli-tool"><a class="header" href="#nodejs-cli-tool">Node.js CLI Tool</a></h3>
<pre><code class="language-typescript">#!/usr/bin/env node
import { Command } from 'commander';
import { ProvisioningClient } from '@provisioning/client';
import chalk from 'chalk';
import ora from 'ora';
const program = new Command();
program
.name('provisioning-cli')
.description('CLI tool for provisioning')
.version('1.0.0');
program
.command('create-server')
.description('Create a server workflow')
.requiredOption('-i, --infra &lt;infra&gt;', 'Infrastructure target')
.option('-s, --settings &lt;settings&gt;', 'Settings file', 'config.k')
.option('-c, --check', 'Check mode only')
.option('-w, --wait', 'Wait for completion')
.action(async (options) =&gt; {
const client = new ProvisioningClient({
baseUrl: process.env.PROVISIONING_API_URL,
username: process.env.PROVISIONING_USERNAME,
password: process.env.PROVISIONING_PASSWORD
});
const spinner = ora('Authenticating...').start();
try {
await client.authenticate();
spinner.text = 'Creating server workflow...';
const taskId = await client.createServerWorkflow({
infra: options.infra,
settings: options.settings,
check_mode: options.check,
wait: false
});
spinner.succeed(`Server workflow created: ${chalk.green(taskId)}`);
if (options.wait) {
spinner.start('Waiting for completion...');
// Set up progress updates
client.on('TaskStatusChanged', (event: any) =&gt; {
if (event.data.task_id === taskId) {
spinner.text = `Status: ${event.data.status}`;
}
});
client.on('WorkflowProgressUpdate', (event: any) =&gt; {
if (event.data.workflow_id === taskId) {
spinner.text = `${event.data.progress}% - ${event.data.current_step}`;
}
});
await client.connectWebSocket(['TaskStatusChanged', 'WorkflowProgressUpdate']);
const task = await client.waitForTaskCompletion(taskId);
if (task.status === 'Completed') {
spinner.succeed(chalk.green('Workflow completed successfully!'));
if (task.output) {
console.log(chalk.gray('Output:'), task.output);
}
} else {
spinner.fail(chalk.red(`Workflow failed: ${task.error}`));
process.exit(1);
}
}
} catch (error) {
spinner.fail(chalk.red(`Error: ${error.message}`));
process.exit(1);
}
});
program
.command('list-tasks')
.description('List all tasks')
.option('-s, --status &lt;status&gt;', 'Filter by status')
.action(async (options) =&gt; {
const client = new ProvisioningClient();
try {
await client.authenticate();
const tasks = await client.listTasks(options.status);
console.log(chalk.bold('Tasks:'));
tasks.forEach(task =&gt; {
const statusColor = task.status === 'Completed' ? 'green' :
task.status === 'Failed' ? 'red' :
task.status === 'Running' ? 'yellow' : 'gray';
console.log(` ${task.id} - ${task.name} [${chalk[statusColor](task.status)}]`);
});
} catch (error) {
console.error(chalk.red(`Error: ${error.message}`));
process.exit(1);
}
});
program
.command('monitor')
.description('Monitor workflows in real-time')
.action(async () =&gt; {
const client = new ProvisioningClient();
try {
await client.authenticate();
console.log(chalk.bold('🔍 Monitoring workflows...'));
console.log(chalk.gray('Press Ctrl+C to stop'));
client.on('TaskStatusChanged', (event: any) =&gt; {
const timestamp = new Date().toLocaleTimeString();
const statusColor = event.data.status === 'Completed' ? 'green' :
event.data.status === 'Failed' ? 'red' :
event.data.status === 'Running' ? 'yellow' : 'gray';
console.log(`[${chalk.gray(timestamp)}] Task ${event.data.task_id} → ${chalk[statusColor](event.data.status)}`);
});
client.on('WorkflowProgressUpdate', (event: any) =&gt; {
const timestamp = new Date().toLocaleTimeString();
console.log(`[${chalk.gray(timestamp)}] ${event.data.workflow_id}: ${event.data.progress}% - ${event.data.current_step}`);
});
await client.connectWebSocket(['TaskStatusChanged', 'WorkflowProgressUpdate']);
// Keep the process running
process.on('SIGINT', () =&gt; {
console.log(chalk.yellow('\nStopping monitor...'));
client.disconnectWebSocket();
process.exit(0);
});
// Keep alive
setInterval(() =&gt; {}, 1000);
} catch (error) {
console.error(chalk.red(`Error: ${error.message}`));
process.exit(1);
}
});
program.parse();
</code></pre>
<h3 id="api-reference-1"><a class="header" href="#api-reference-1">API Reference</a></h3>
<pre><code class="language-typescript">interface ProvisioningClientOptions {
baseUrl?: string;
authUrl?: string;
username?: string;
password?: string;
token?: string;
}
class ProvisioningClient extends EventEmitter {
constructor(options: ProvisioningClientOptions);
async authenticate(): Promise&lt;string&gt;;
async createServerWorkflow(config: {
infra: string;
settings?: string;
check_mode?: boolean;
wait?: boolean;
}): Promise&lt;string&gt;;
async createTaskservWorkflow(config: {
operation: string;
taskserv: string;
infra: string;
settings?: string;
check_mode?: boolean;
wait?: boolean;
}): Promise&lt;string&gt;;
async getTaskStatus(taskId: string): Promise&lt;Task&gt;;
async listTasks(statusFilter?: string): Promise&lt;Task[]&gt;;
async waitForTaskCompletion(
taskId: string,
timeout?: number,
pollInterval?: number
): Promise&lt;Task&gt;;
async connectWebSocket(eventTypes?: string[]): Promise&lt;void&gt;;
disconnectWebSocket(): void;
async executeBatchOperation(batchConfig: BatchConfig): Promise&lt;any&gt;;
async getBatchStatus(batchId: string): Promise&lt;any&gt;;
}
</code></pre>
<h2 id="go-sdk"><a class="header" href="#go-sdk">Go SDK</a></h2>
<h3 id="installation-2"><a class="header" href="#installation-2">Installation</a></h3>
<pre><code class="language-bash">go get github.com/provisioning-systems/go-client
</code></pre>
<h3 id="quick-start-2"><a class="header" href="#quick-start-2">Quick Start</a></h3>
<pre><code class="language-go">package main
import (
"context"
"fmt"
"log"
"time"
"github.com/provisioning-systems/go-client"
)
func main() {
// Initialize client
client, err := provisioning.NewClient(&amp;provisioning.Config{
BaseURL: "http://localhost:9090",
AuthURL: "http://localhost:8081",
Username: "admin",
Password: "your-password",
})
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
ctx := context.Background()
// Authenticate
token, err := client.Authenticate(ctx)
if err != nil {
log.Fatalf("Authentication failed: %v", err)
}
fmt.Printf("Authenticated with token: %.20s...\n", token)
// Create server workflow
taskID, err := client.CreateServerWorkflow(ctx, &amp;provisioning.CreateServerRequest{
Infra: "production",
Settings: "prod-settings.k",
Wait: false,
})
if err != nil {
log.Fatalf("Failed to create workflow: %v", err)
}
fmt.Printf("Server workflow created: %s\n", taskID)
// Wait for completion
task, err := client.WaitForTaskCompletion(ctx, taskID, 10*time.Minute)
if err != nil {
log.Fatalf("Failed to wait for completion: %v", err)
}
fmt.Printf("Task completed with status: %s\n", task.Status)
if task.Status == "Completed" {
fmt.Printf("Output: %s\n", task.Output)
} else if task.Status == "Failed" {
fmt.Printf("Error: %s\n", task.Error)
}
}
</code></pre>
<h3 id="websocket-integration-1"><a class="header" href="#websocket-integration-1">WebSocket Integration</a></h3>
<pre><code class="language-go">package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"github.com/provisioning-systems/go-client"
)
func main() {
client, err := provisioning.NewClient(&amp;provisioning.Config{
BaseURL: "http://localhost:9090",
Username: "admin",
Password: "password",
})
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
ctx := context.Background()
// Authenticate
_, err = client.Authenticate(ctx)
if err != nil {
log.Fatalf("Authentication failed: %v", err)
}
// Set up WebSocket connection
ws, err := client.ConnectWebSocket(ctx, []string{
"TaskStatusChanged",
"WorkflowProgressUpdate",
})
if err != nil {
log.Fatalf("Failed to connect WebSocket: %v", err)
}
defer ws.Close()
// Handle events
go func() {
for event := range ws.Events() {
switch event.Type {
case "TaskStatusChanged":
fmt.Printf("Task %s status changed to: %s\n",
event.Data["task_id"], event.Data["status"])
case "WorkflowProgressUpdate":
fmt.Printf("Workflow progress: %v%% - %s\n",
event.Data["progress"], event.Data["current_step"])
}
}
}()
// Wait for interrupt
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
&lt;-c
fmt.Println("Shutting down...")
}
</code></pre>
<h3 id="http-client-with-retry-logic"><a class="header" href="#http-client-with-retry-logic">HTTP Client with Retry Logic</a></h3>
<pre><code class="language-go">package main
import (
"context"
"fmt"
"time"
"github.com/provisioning-systems/go-client"
"github.com/cenkalti/backoff/v4"
)
type ResilientClient struct {
*provisioning.Client
}
func NewResilientClient(config *provisioning.Config) (*ResilientClient, error) {
client, err := provisioning.NewClient(config)
if err != nil {
return nil, err
}
return &amp;ResilientClient{Client: client}, nil
}
func (c *ResilientClient) CreateServerWorkflowWithRetry(
ctx context.Context,
req *provisioning.CreateServerRequest,
) (string, error) {
var taskID string
operation := func() error {
var err error
taskID, err = c.CreateServerWorkflow(ctx, req)
// Don't retry validation errors
if provisioning.IsValidationError(err) {
return backoff.Permanent(err)
}
return err
}
exponentialBackoff := backoff.NewExponentialBackOff()
exponentialBackoff.MaxElapsedTime = 5 * time.Minute
err := backoff.Retry(operation, exponentialBackoff)
if err != nil {
return "", fmt.Errorf("failed after retries: %w", err)
}
return taskID, nil
}
func main() {
client, err := NewResilientClient(&amp;provisioning.Config{
BaseURL: "http://localhost:9090",
Username: "admin",
Password: "password",
})
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
ctx := context.Background()
// Authenticate with retry
_, err = client.Authenticate(ctx)
if err != nil {
log.Fatalf("Authentication failed: %v", err)
}
// Create workflow with retry
taskID, err := client.CreateServerWorkflowWithRetry(ctx, &amp;provisioning.CreateServerRequest{
Infra: "production",
Settings: "config.k",
})
if err != nil {
log.Fatalf("Failed to create workflow: %v", err)
}
fmt.Printf("Workflow created successfully: %s\n", taskID)
}
</code></pre>
<h2 id="rust-sdk"><a class="header" href="#rust-sdk">Rust SDK</a></h2>
<h3 id="installation-3"><a class="header" href="#installation-3">Installation</a></h3>
<p>Add to your <code>Cargo.toml</code>:</p>
<pre><code class="language-toml">[dependencies]
provisioning-rs = "2.0.0"
tokio = { version = "1.0", features = ["full"] }
</code></pre>
<h3 id="quick-start-3"><a class="header" href="#quick-start-3">Quick Start</a></h3>
<pre><code class="language-rust">use provisioning_rs::{ProvisioningClient, Config, CreateServerRequest};
use tokio;
#[tokio::main]
async fn main() -&gt; Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt; {
// Initialize client
let config = Config {
base_url: "http://localhost:9090".to_string(),
auth_url: Some("http://localhost:8081".to_string()),
username: Some("admin".to_string()),
password: Some("your-password".to_string()),
token: None,
};
let mut client = ProvisioningClient::new(config);
// Authenticate
let token = client.authenticate().await?;
println!("Authenticated with token: {}...", &amp;token[..20]);
// Create server workflow
let request = CreateServerRequest {
infra: "production".to_string(),
settings: Some("prod-settings.k".to_string()),
check_mode: false,
wait: false,
};
let task_id = client.create_server_workflow(request).await?;
println!("Server workflow created: {}", task_id);
// Wait for completion
let task = client.wait_for_task_completion(&amp;task_id, std::time::Duration::from_secs(600)).await?;
println!("Task completed with status: {:?}", task.status);
match task.status {
TaskStatus::Completed =&gt; {
if let Some(output) = task.output {
println!("Output: {}", output);
}
},
TaskStatus::Failed =&gt; {
if let Some(error) = task.error {
println!("Error: {}", error);
}
},
_ =&gt; {}
}
Ok(())
}</code></pre>
<h3 id="websocket-integration-2"><a class="header" href="#websocket-integration-2">WebSocket Integration</a></h3>
<pre><code class="language-rust">use provisioning_rs::{ProvisioningClient, Config, WebSocketEvent};
use futures_util::StreamExt;
use tokio;
#[tokio::main]
async fn main() -&gt; Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt; {
let config = Config {
base_url: "http://localhost:9090".to_string(),
username: Some("admin".to_string()),
password: Some("password".to_string()),
..Default::default()
};
let mut client = ProvisioningClient::new(config);
// Authenticate
client.authenticate().await?;
// Connect WebSocket
let mut ws = client.connect_websocket(vec![
"TaskStatusChanged".to_string(),
"WorkflowProgressUpdate".to_string(),
]).await?;
// Handle events
tokio::spawn(async move {
while let Some(event) = ws.next().await {
match event {
Ok(WebSocketEvent::TaskStatusChanged { data }) =&gt; {
println!("Task {} status changed to: {}", data.task_id, data.status);
},
Ok(WebSocketEvent::WorkflowProgressUpdate { data }) =&gt; {
println!("Workflow progress: {}% - {}", data.progress, data.current_step);
},
Ok(WebSocketEvent::SystemHealthUpdate { data }) =&gt; {
println!("System health: {}", data.overall_status);
},
Err(e) =&gt; {
eprintln!("WebSocket error: {}", e);
break;
}
}
}
});
// Keep the main thread alive
tokio::signal::ctrl_c().await?;
println!("Shutting down...");
Ok(())
}</code></pre>
<h3 id="batch-operations-1"><a class="header" href="#batch-operations-1">Batch Operations</a></h3>
<pre><code class="language-rust">use provisioning_rs::{BatchOperationRequest, BatchOperation};
#[tokio::main]
async fn main() -&gt; Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt; {
let mut client = ProvisioningClient::new(config);
client.authenticate().await?;
// Define batch operation
let batch_request = BatchOperationRequest {
name: "production_deployment".to_string(),
version: "1.0.0".to_string(),
storage_backend: "surrealdb".to_string(),
parallel_limit: 5,
rollback_enabled: true,
operations: vec![
BatchOperation {
id: "servers".to_string(),
operation_type: "server_batch".to_string(),
provider: "upcloud".to_string(),
dependencies: vec![],
config: serde_json::json!({
"server_configs": [
{"name": "web-01", "plan": "2xCPU-4GB", "zone": "de-fra1"},
{"name": "web-02", "plan": "2xCPU-4GB", "zone": "de-fra1"}
]
}),
},
BatchOperation {
id: "kubernetes".to_string(),
operation_type: "taskserv_batch".to_string(),
provider: "upcloud".to_string(),
dependencies: vec!["servers".to_string()],
config: serde_json::json!({
"taskservs": ["kubernetes", "cilium", "containerd"]
}),
},
],
};
// Execute batch operation
let batch_result = client.execute_batch_operation(batch_request).await?;
println!("Batch operation started: {}", batch_result.batch_id);
// Monitor progress
loop {
let status = client.get_batch_status(&amp;batch_result.batch_id).await?;
println!("Batch status: {} - {}%", status.status, status.progress.unwrap_or(0.0));
match status.status.as_str() {
"Completed" | "Failed" | "Cancelled" =&gt; break,
_ =&gt; tokio::time::sleep(std::time::Duration::from_secs(10)).await,
}
}
Ok(())
}</code></pre>
<h2 id="best-practices"><a class="header" href="#best-practices">Best Practices</a></h2>
<h3 id="authentication-and-security"><a class="header" href="#authentication-and-security">Authentication and Security</a></h3>
<ol>
<li><strong>Token Management</strong>: Store tokens securely and implement automatic refresh</li>
<li><strong>Environment Variables</strong>: Use environment variables for credentials</li>
<li><strong>HTTPS</strong>: Always use HTTPS in production environments</li>
<li><strong>Token Expiration</strong>: Handle token expiration gracefully</li>
</ol>
<h3 id="error-handling"><a class="header" href="#error-handling">Error Handling</a></h3>
<ol>
<li><strong>Specific Exceptions</strong>: Handle specific error types appropriately</li>
<li><strong>Retry Logic</strong>: Implement exponential backoff for transient failures</li>
<li><strong>Circuit Breakers</strong>: Use circuit breakers for resilient integrations</li>
<li><strong>Logging</strong>: Log errors with appropriate context</li>
</ol>
<h3 id="performance-optimization"><a class="header" href="#performance-optimization">Performance Optimization</a></h3>
<ol>
<li><strong>Connection Pooling</strong>: Reuse HTTP connections</li>
<li><strong>Async Operations</strong>: Use asynchronous operations where possible</li>
<li><strong>Batch Operations</strong>: Group related operations for efficiency</li>
<li><strong>Caching</strong>: Cache frequently accessed data appropriately</li>
</ol>
<h3 id="websocket-connections"><a class="header" href="#websocket-connections">WebSocket Connections</a></h3>
<ol>
<li><strong>Reconnection</strong>: Implement automatic reconnection with backoff</li>
<li><strong>Event Filtering</strong>: Subscribe only to needed event types</li>
<li><strong>Error Handling</strong>: Handle WebSocket errors gracefully</li>
<li><strong>Resource Cleanup</strong>: Properly close WebSocket connections</li>
</ol>
<h3 id="testing"><a class="header" href="#testing">Testing</a></h3>
<ol>
<li><strong>Unit Tests</strong>: Test SDK functionality with mocked responses</li>
<li><strong>Integration Tests</strong>: Test against real API endpoints</li>
<li><strong>Error Scenarios</strong>: Test error handling paths</li>
<li><strong>Load Testing</strong>: Validate performance under load</li>
</ol>
<p>This comprehensive SDK documentation provides developers with everything needed to integrate with provisioning using their preferred programming language, complete with examples, best practices, and detailed API references.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../api/extensions.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="../api/integration-examples.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="../api/extensions.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="../api/integration-examples.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>