provisioning/tools/create-provider.nu

1174 lines
34 KiB
Plaintext
Raw Permalink Normal View History

2025-10-07 11:12:02 +01:00
# Provider Creation Tool
# Quickly create a new provider with proper structure and templates
use ../core/nulib/lib_provisioning/utils/logging.nu *
use ../core/nulib/lib_provisioning/providers/registry.nu *
# Create a new provider
export def main [
provider_name: string # Name of the new provider (e.g., "digitalocean")
--description: string = "" # Description of the provider
--api-base: string = "" # Base API URL for the provider
--auth-type: string = "token" # Authentication type (token, key, oauth)
--template: string = "cloud" # Template type (cloud, container, baremetal)
--dry-run # Show what would be created without executing
] {
log-section "Creating new provider: $provider_name" "create-provider"
# Validate provider name
if ($provider_name | str contains " ") {
log-error "Provider name cannot contain spaces" "validation"
return
}
if ($provider_name | str length) < 2 {
log-error "Provider name must be at least 2 characters" "validation"
return
}
# Check if provider already exists
let provider_path = $"../extensions/providers/($provider_name)"
if ($provider_path | path exists) {
log-error $"Provider ($provider_name) already exists at ($provider_path)" "validation"
return
}
# Create directory structure
let directories = [
$"../extensions/providers/($provider_name)"
$"../extensions/providers/($provider_name)/nulib"
$"../extensions/providers/($provider_name)/nulib/($provider_name)"
]
if $dry_run {
log-info "DRY RUN MODE - No files will be created" "create-provider"
print "Directories to create:"
for dir in $directories {
print $" 📁 ($dir)"
}
} else {
for dir in $directories {
mkdir $dir
log-success $"Created directory: ($dir)" "create-provider"
}
}
# Generate provider metadata
let metadata = generate_provider_metadata $provider_name $description $template
# Generate provider adapter
let adapter_content = generate_provider_adapter $provider_name $template $auth_type
# Generate provider implementation
let implementation_content = generate_provider_implementation $provider_name $template $api_base $auth_type
# Create files
let files = [
{
path: $"../extensions/providers/($provider_name)/provider.nu"
content: $adapter_content
description: "Provider adapter implementing standard interface"
}
{
path: $"../extensions/providers/($provider_name)/nulib/($provider_name)/servers.nu"
content: $implementation_content
description: "Provider-specific implementation functions"
}
{
path: $"../extensions/providers/($provider_name)/README.md"
content: (generate_provider_readme $provider_name $description $auth_type)
description: "Provider documentation"
}
]
if $dry_run {
print "\nFiles to create:"
for file in $files {
print $" 📄 ($file.path) - ($file.description)"
}
} else {
for file in $files {
$file.content | save $file.path
log-success $"Created file: ($file.path)" "create-provider"
}
}
# Generate usage examples
let usage_examples = generate_usage_examples $provider_name
if not $dry_run {
$usage_examples | save $"../extensions/providers/($provider_name)/USAGE.md"
log-success "Created usage examples" "create-provider"
}
# Show next steps
log-section "Next Steps" "create-provider"
let next_steps = [
$"1. Edit authentication in: ../extensions/providers/($provider_name)/nulib/($provider_name)/servers.nu"
$"2. Implement provider-specific API calls"
$"3. Test provider: nu -c \"use provisioning/core/nulib/lib_provisioning/providers/loader.nu *; load-provider '($provider_name)'\""
$"4. Validate interface: nu -c \"use provisioning/core/nulib/lib_provisioning/providers/interface.nu *; validate-provider-interface '($provider_name)'\""
$"5. Add provider to infrastructure configs"
$"6. Run tests: ./provisioning/tools/test-provider-agnostic.nu"
]
for step in $next_steps {
print $step
}
if not $dry_run {
log-success $"Provider ($provider_name) created successfully!" "create-provider"
} else {
log-info "Use --dry-run=false to create the provider" "create-provider"
}
}
# Generate provider metadata
def generate_provider_metadata [name: string, description: string, template: string]: nothing -> record {
let desc = if ($description | is-empty) {
match $template {
"cloud" => $"($name | str title-case) cloud provider"
"container" => $"($name | str title-case) container platform"
"baremetal" => $"($name | str title-case) bare metal provider"
_ => $"($name | str title-case) infrastructure provider"
}
} else {
$description
}
let capabilities = match $template {
"cloud" => {
server_management: true
network_management: true
storage_management: true
load_balancer: true
dns_management: false
cdn: false
backup_service: true
monitoring: false
logging: false
auto_scaling: true
spot_instances: false
containers: false
serverless: false
multi_region: true
encryption_at_rest: true
encryption_in_transit: true
compliance_certifications: []
}
"container" => {
server_management: false
network_management: true
storage_management: true
load_balancer: false
dns_management: false
cdn: false
backup_service: false
monitoring: false
logging: false
auto_scaling: true
spot_instances: false
containers: true
serverless: false
multi_region: false
encryption_at_rest: false
encryption_in_transit: true
compliance_certifications: []
}
"baremetal" => {
server_management: true
network_management: false
storage_management: true
load_balancer: false
dns_management: false
cdn: false
backup_service: false
monitoring: false
logging: false
auto_scaling: false
spot_instances: false
containers: true
serverless: false
multi_region: false
encryption_at_rest: false
encryption_in_transit: false
compliance_certifications: []
}
_ => {
server_management: true
network_management: false
storage_management: false
load_balancer: false
dns_management: false
cdn: false
backup_service: false
monitoring: false
logging: false
auto_scaling: false
spot_instances: false
containers: false
serverless: false
multi_region: false
encryption_at_rest: false
encryption_in_transit: false
compliance_certifications: []
}
}
{
name: $name
description: $desc
capabilities: $capabilities
}
}
# Generate provider adapter content
def generate_provider_adapter [name: string, template: string, auth_type: string]: nothing -> string {
let name_title = ($name | str title-case)
let metadata = generate_provider_metadata $name "" $template
$"# ($name_title) Provider Adapter
# Implements the standard provider interface for ($name_title)
use nulib/($name)/servers.nu *
# Provider metadata
export def get-provider-metadata []: nothing -> record {
{
name: \"($name)\"
version: \"1.0.0\"
description: \"($metadata.description)\"
capabilities: ($metadata.capabilities | to nuon)
}
}
# Server query operations
export def query_servers [
find?: string
cols?: string
]: nothing -> list {
let str_find = if \\$find != null { \\$find } else { \"\" }
let str_cols = if \\$cols != null { \\$cols } else { \"\" }
($name)_query_servers \\$str_find \\$str_cols
}
# Server information operations
export def server_info [
server: record
check: bool
find?: string
cols?: string
]: nothing -> record {
($name)_server_info \\$server \\$check
}
# Server existence and status operations
export def server_exists [
server: record
error_exit: bool
]: nothing -> bool {
($name)_server_exists \\$server \\$error_exit
}
export def server_is_running [
server: record
error_exit: bool
]: nothing -> bool {
($name)_server_is_running \\$server \\$error_exit
}
# Server lifecycle operations
export def check_server_requirements [
settings: record
server: record
check: bool
]: nothing -> bool {
($name)_check_server_requirements \\$settings \\$server \\$check
}
export def create_server [
settings: record
server: record
check: bool
wait: bool
]: nothing -> bool {
($name)_create_server \\$settings \\$server \\$check \\$wait
}
export def delete_server [
settings: record
server: record
keep_storage: bool
error_exit: bool
]: nothing -> bool {
($name)_delete_server \\$settings \\$server \\$keep_storage \\$error_exit
}
export def delete_server_storage [
settings: record
server: record
error_exit: bool
]: nothing -> bool {
($name)_delete_server_storage \\$settings \\$server \\$error_exit
}
export def post_create_server [
settings: record
server: record
check: bool
]: nothing -> bool {
($name)_post_create_server \\$settings \\$server \\$check
}
export def modify_server [
settings: record
server: record
new_values: list
error_exit: bool
]: nothing -> bool {
($name)_modify_server \\$settings \\$server \\$new_values \\$error_exit
}
# Server state management
export def server_state [
server: record
new_state: string
error_exit: bool
wait: bool
settings: record
]: nothing -> bool {
($name)_server_state \\$server \\$new_state \\$error_exit \\$wait \\$settings
}
# Network operations
export def get_ip [
settings: record
server: record
ip_type: string
error_exit: bool
]: nothing -> string {
let use_type = match \\$ip_type {
\"\\$network_public_ip\" => \"public\"
\"\\$network_private_ip\" => \"private\"
_ => \\$ip_type
}
let result = (($name)_get_ip \\$settings \\$server \\$use_type \\$error_exit)
\\$result | str trim
}
export def servers_ips [
settings: record
data: list
prov?: string
serverpos?: int
]: nothing -> list {
($name)_servers_ips \\$settings \\$data \\$prov \\$serverpos
}
# Infrastructure operations
export def load_infra_servers_info [
settings: record
server: record
error_exit: bool
]: nothing -> record {
($name)_load_infra_servers_info \\$settings \\$server \\$error_exit
}
export def load_infra_storages_info [
settings: record
server: record
error_exit: bool
]: nothing -> record {
($name)_load_infra_storages_info \\$settings \\$server \\$error_exit
}
export def get_infra_storage [
server: record
settings: record
cloud_data: record
error_exit: bool
]: nothing -> list {
($name)_get_infra_storage \\$server \\$settings \\$cloud_data \\$error_exit
}
export def get_infra_item [
server: record
settings: record
cloud_data: record
error_exit: bool
]: nothing -> record {
($name)_get_infra_item \\$server \\$settings \\$cloud_data \\$error_exit
}
export def get_infra_price [
server: record
data: record
key: string
error_exit: bool
price_col?: string
]: nothing -> float {
($name)_get_infra_price \\$server \\$data \\$key \\$error_exit \\$price_col
}
# Cache operations
export def start_cache_info [
settings: record
server: record
]: nothing -> nothing {
($name)_start_cache_info \\$settings \\$server
}
export def create_cache [
settings: record
server: record
error_exit: bool
]: nothing -> nothing {
($name)_create_cache \\$settings \\$server \\$error_exit
}
export def read_cache [
settings: record
server: record
error_exit: bool
]: nothing -> nothing {
($name)_read_cache \\$settings \\$server \\$error_exit
}
export def clean_cache [
settings: record
server: record
error_exit: bool
]: nothing -> nothing {
($name)_clean_cache \\$settings \\$server \\$error_exit
}
export def ip_from_cache [
settings: record
server: record
error_exit: bool
]: nothing -> string {
($name)_ip_from_cache \\$settings \\$server \\$error_exit
}
# Provider metadata operations
export def on_prov_server [
server: record
]: nothing -> string {
($name)_on_prov_server \\$server
}
# Provider validation
export def validate_provider []: nothing -> record {
{
provider: \"($name)\"
valid: true
interface_version: \"1.0.0\"
capabilities: (get-provider-metadata).capabilities
last_validated: (date now)
}
}
"
}
# Generate provider implementation content
def generate_provider_implementation [name: string, template: string, api_base: string, auth_type: string]: nothing -> string {
let name_title = ($name | str title-case)
let auth_var = match $auth_type {
"token" => $"($name | str upcase)_TOKEN"
"key" => $"($name | str upcase)_API_KEY"
"oauth" => $"($name | str upcase)_CLIENT_ID"
_ => $"($name | str upcase)_TOKEN"
}
let api_url = if ($api_base | is-empty) {
$"https://api.($name).com"
} else {
$api_base
}
let implementation = match $template {
"cloud" => generate_cloud_implementation $name $auth_var $api_url
"container" => generate_container_implementation $name
"baremetal" => generate_baremetal_implementation $name
_ => generate_generic_implementation $name $auth_var $api_url
}
$"# ($name_title) Provider Implementation
# Provider-specific functions for ($name_title)
use ../../core/nulib/lib_provisioning/utils/logging.nu *
# Authentication check
def check_auth []: nothing -> bool {
if (\\$env | get -o ($auth_var)) == null {
log-error \"($auth_var) environment variable required\" \"auth\"
return false
}
true
}
($implementation)
"
}
# Generate cloud provider implementation
def generate_cloud_implementation [name: string, auth_var: string, api_url: string]: nothing -> string {
$"# Cloud provider implementation for ($name)
export def ($name)_query_servers [find?: string, cols?: string]: nothing -> list {
if not (check_auth) { return [] }
let auth_header = { Authorization: \\$\"Bearer (\\$env.($auth_var))\" }
try {
let servers = (http get \\$\"($api_url)/servers\" --headers \\$auth_header)
# Filter results if find parameter provided
let filtered = if (\\$find != null) and (\\$find != \"\") {
\\$servers | where {|server| \\$server.name =~ \\$find}
} else {
\\$servers
}
# Select columns if specified
if (\\$cols != null) and (\\$cols != \"\") {
let field_list = (\\$cols | split row \",\")
\\$filtered | select ...(\\$field_list)
} else {
\\$filtered | select name status region instance_type public_ip
}
} catch {|err|
log-error \\$\"Failed to query servers: (\\$err.msg)\" \"($name)\"
[]
}
}
export def ($name)_create_server [settings: record, server: record, check: bool, wait: bool]: nothing -> bool {
if not (check_auth) { return false }
let payload = {
name: \\$server.hostname
region: (\\$server.zone? | default \"us-east-1\")
instance_type: (\\$server.plan? | default \"t3.micro\")
image: (\\$server.image? | default \"ubuntu-20.04\")
}
if \\$check {
log-info \\$\"Would create ($name) server: (\\$payload)\" \"($name)\"
return true
}
try {
let auth_header = { Authorization: \\$\"Bearer (\\$env.($auth_var))\" }
let result = (http post \\$\"($api_url)/servers\"
--headers \\$auth_header
--content-type application/json
\\$payload)
if \\$wait {
# Poll for server to be ready
mut attempts = 0
while \\$attempts < 60 { # 5 minutes max
let status = (($name)_server_exists \\$server false)
if \\$status {
log-success \\$\"Server (\\$server.hostname) is ready\" \"($name)\"
break
}
sleep 5sec
\\$attempts += 1
}
}
log-success \\$\"Created server: (\\$server.hostname)\" \"($name)\"
true
} catch {|err|
log-error \\$\"Failed to create server: (\\$err.msg)\" \"($name)\"
false
}
}
export def ($name)_server_exists [server: record, error_exit: bool]: nothing -> bool {
if not (check_auth) { return false }
try {
let auth_header = { Authorization: \\$\"Bearer (\\$env.($auth_var))\" }
let servers = (http get \\$\"($api_url)/servers\" --headers \\$auth_header)
let found = (\\$servers | where name == \\$server.hostname)
(\\$found | length) > 0
} catch {|err|
if \\$error_exit {
log-error \\$\"Error checking server existence: (\\$err.msg)\" \"($name)\"
exit 1
}
false
}
}
export def ($name)_get_ip [settings: record, server: record, ip_type: string, error_exit: bool]: nothing -> string {
if not (check_auth) { return \"\" }
try {
let auth_header = { Authorization: \\$\"Bearer (\\$env.($auth_var))\" }
let servers = (http get \\$\"($api_url)/servers\" --headers \\$auth_header)
let found = (\\$servers | where name == \\$server.hostname | get 0)
match \\$ip_type {
\"public\" => (\\$found.public_ip? | default \"\")
\"private\" => (\\$found.private_ip? | default \"\")
_ => (\\$found.public_ip? | default \"\")
}
} catch {|err|
if \\$error_exit {
log-error \\$\"Error getting IP: (\\$err.msg)\" \"($name)\"
exit 1
}
\"\"
}
}
export def ($name)_delete_server [settings: record, server: record, keep_storage: bool, error_exit: bool]: nothing -> bool {
if not (check_auth) { return false }
try {
let auth_header = { Authorization: \\$\"Bearer (\\$env.($auth_var))\" }
http delete \\$\"($api_url)/servers/(\\$server.hostname)\" --headers \\$auth_header
log-success \\$\"Deleted server: (\\$server.hostname)\" \"($name)\"
true
} catch {|err|
if \\$error_exit {
log-error \\$\"Error deleting server: (\\$err.msg)\" \"($name)\"
exit 1
}
false
}
}
# Implement other required functions...
export def ($name)_server_info [server: record, check: bool]: nothing -> record {
{ hostname: \\$server.hostname, provider: \"($name)\", status: \"unknown\" }
}
export def ($name)_server_is_running [server: record, error_exit: bool]: nothing -> bool {
# Implementation depends on your provider's API
true
}
export def ($name)_check_server_requirements [settings: record, server: record, check: bool]: nothing -> bool {
true
}
export def ($name)_delete_server_storage [settings: record, server: record, error_exit: bool]: nothing -> bool {
true
}
export def ($name)_post_create_server [settings: record, server: record, check: bool]: nothing -> bool {
true
}
export def ($name)_modify_server [settings: record, server: record, new_values: list, error_exit: bool]: nothing -> bool {
true
}
export def ($name)_server_state [server: record, new_state: string, error_exit: bool, wait: bool, settings: record]: nothing -> bool {
true
}
export def ($name)_servers_ips [settings: record, data: list, prov?: string, serverpos?: int]: nothing -> list {
[]
}
export def ($name)_load_infra_servers_info [settings: record, server: record, error_exit: bool]: nothing -> record {
{ provider: \"($name)\", server: \\$server.hostname }
}
export def ($name)_load_infra_storages_info [settings: record, server: record, error_exit: bool]: nothing -> record {
{ provider: \"($name)\", storage: \"managed\" }
}
export def ($name)_get_infra_storage [server: record, settings: record, cloud_data: record, error_exit: bool]: nothing -> list {
[]
}
export def ($name)_get_infra_item [server: record, settings: record, cloud_data: record, error_exit: bool]: nothing -> record {
{ provider: \"($name)\", hostname: \\$server.hostname }
}
export def ($name)_get_infra_price [server: record, data: record, key: string, error_exit: bool, price_col?: string]: nothing -> float {
0.0
}
export def ($name)_start_cache_info [settings: record, server: record]: nothing -> nothing {
# No-op
}
export def ($name)_create_cache [settings: record, server: record, error_exit: bool]: nothing -> nothing {
# No-op
}
export def ($name)_read_cache [settings: record, server: record, error_exit: bool]: nothing -> nothing {
# No-op
}
export def ($name)_clean_cache [settings: record, server: record, error_exit: bool]: nothing -> nothing {
# No-op
}
export def ($name)_ip_from_cache [settings: record, server: record, error_exit: bool]: nothing -> string {
\"\"
}
export def ($name)_on_prov_server [server: record]: nothing -> string {
\\$\"Using ($name) provider for (\\$server.hostname)\"
}
"
}
# Generate container provider implementation
def generate_container_implementation [name: string]: nothing -> string {
$"# Container provider implementation for ($name)
export def ($name)_query_servers [find?: string, cols?: string]: nothing -> list {
try {
let containers = (docker ps --format json | from json)
let filtered = if (\\$find != null) and (\\$find != \"\") {
\\$containers | where {|container| \\$container.Names =~ \\$find}
} else {
\\$containers
}
if (\\$cols != null) and (\\$cols != \"\") {
let field_list = (\\$cols | split row \",\")
\\$filtered | select ...(\\$field_list)
} else {
\\$filtered | select Names State Status Image
}
} catch {|err|
log-error \\$\"Failed to query containers: (\\$err.msg)\" \"($name)\"
[]
}
}
export def ($name)_create_server [settings: record, server: record, check: bool, wait: bool]: nothing -> bool {
let image = (\\$server.image? | default \"ubuntu:20.04\")
let command = [\"docker\", \"run\", \"-d\", \"--name\", \\$server.hostname, \\$image]
if \\$check {
log-info \\$\"Would create container: (\\$command | str join ' ')\" \"($name)\"
return true
}
try {
let result = (run-external \"docker\" \"run\" \"-d\" \"--name\" \\$server.hostname \\$image)
log-success \\$\"Created container: (\\$server.hostname)\" \"($name)\"
true
} catch {|err|
log-error \\$\"Failed to create container: (\\$err.msg)\" \"($name)\"
false
}
}
# Implement other required functions for container platform...
"
}
# Generate baremetal provider implementation
def generate_baremetal_implementation [name: string]: nothing -> string {
$"# Bare metal provider implementation for ($name)
export def ($name)_query_servers [find?: string, cols?: string]: nothing -> list {
try {
# Read from inventory file or configuration
let inventory_file = \\$\"inventory.(\\$name).yaml\"
if not (\\$inventory_file | path exists) {
log-warning \\$\"Inventory file not found: (\\$inventory_file)\" \"($name)\"
return []
}
let inventory = (open \\$inventory_file | from yaml)
let servers = \\$inventory.servers
let filtered = if (\\$find != null) and (\\$find != \"\") {
\\$servers | where {|server| \\$server.hostname =~ \\$find}
} else {
\\$servers
}
if (\\$cols != null) and (\\$cols != \"\") {
let field_list = (\\$cols | split row \",\")
\\$filtered | select ...(\\$field_list)
} else {
\\$filtered | select hostname ip_address status
}
} catch {|err|
log-error \\$\"Failed to query servers: (\\$err.msg)\" \"($name)\"
[]
}
}
export def ($name)_create_server [settings: record, server: record, check: bool, wait: bool]: nothing -> bool {
# For bare metal, \"creation\" means ensuring server is accessible
if \\$check {
log-info \\$\"Would validate bare metal server: (\\$server.hostname)\" \"($name)\"
return true
}
try {
# Test SSH connectivity
let test_result = (ssh -o ConnectTimeout=5 \\$server.hostname \"echo 'connection test'\")
if (\\$test_result | str contains \"connection test\") {
log-success \\$\"Bare metal server (\\$server.hostname) is accessible\" \"($name)\"
true
} else {
log-error \\$\"Cannot connect to bare metal server (\\$server.hostname)\" \"($name)\"
false
}
} catch {|err|
log-error \\$\"SSH connection failed: (\\$err.msg)\" \"($name)\"
false
}
}
# Implement other required functions for bare metal...
"
}
# Generate generic provider implementation
def generate_generic_implementation [name: string, auth_var: string, api_url: string]: nothing -> string {
$"# Generic provider implementation for ($name)
export def ($name)_query_servers [find?: string, cols?: string]: nothing -> list {
# TODO: Implement your provider's server listing logic
log-warning \"($name)_query_servers not yet implemented\" \"($name)\"
[]
}
export def ($name)_create_server [settings: record, server: record, check: bool, wait: bool]: nothing -> bool {
# TODO: Implement your provider's server creation logic
if \\$check {
log-info \\$\"Would create server: (\\$server.hostname)\" \"($name)\"
return true
}
log-warning \"($name)_create_server not yet implemented\" \"($name)\"
false
}
# TODO: Implement all other required functions...
"
}
# Generate provider README
def generate_provider_readme [name: string, description: string, auth_type: string]: nothing -> string {
let name_title = ($name | str title-case)
let auth_var = match $auth_type {
"token" => $"($name | str upcase)_TOKEN"
"key" => $"($name | str upcase)_API_KEY"
"oauth" => $"($name | str upcase)_CLIENT_ID"
_ => $"($name | str upcase)_TOKEN"
}
$"# ($name_title) Provider
($description)
## Authentication
Set the following environment variable:
```bash
export ($auth_var)=\"your-($auth_type)-here\"
```
## Configuration
Add servers using the ($name) provider in your KCL configuration:
```kcl
servers = [
{
hostname = \"my-server\"
provider = \"($name)\"
zone = \"region-1\"
plan = \"small\"
image = \"ubuntu-20.04\"
}
]
```
## Features
- ✅ Server management
- ✅ Basic operations
- ⚠️ Advanced features (in development)
## Usage
```bash
# Query servers
nu -c \"use provisioning/extensions/providers/($name)/provider.nu *; query_servers\"
# Test provider
./provisioning/tools/test-provider-agnostic.nu
```
## Development
This provider was generated using the provider creation tool. See `USAGE.md` for implementation examples.
## Status
🚧 **Work in Progress** - This provider needs implementation of core functions.
## Support
- Check the [Provider Development Guide](../../../docs/development/QUICK_PROVIDER_GUIDE.md)
- Review existing providers for implementation patterns
- Run tests with: `./provisioning/tools/test-provider-agnostic.nu`
"
}
# Generate usage examples
def generate_usage_examples [name: string]: nothing -> string {
let name_title = ($name | str title-case)
$"# ($name_title) Provider Usage Examples
## Basic Server Operations
### Query Servers
```bash
# List all servers
nu -c \"use provisioning/extensions/providers/($name)/provider.nu *; query_servers\"
# Find servers by name
nu -c \"use provisioning/extensions/providers/($name)/provider.nu *; query_servers 'web'\"
# Select specific columns
nu -c \"use provisioning/extensions/providers/($name)/provider.nu *; query_servers '' 'name,status,region'\"
```
### Server Information
```bash
# Get detailed server info
nu -c \"use provisioning/extensions/providers/($name)/provider.nu *; server_info {hostname: 'my-server'} false\"
# Check if server exists
nu -c \"use provisioning/extensions/providers/($name)/provider.nu *; server_exists {hostname: 'my-server'} false\"
```
## Infrastructure Configuration
### KCL Configuration Example
```kcl
# workspace/infra/example/servers.k
servers = [
{
hostname = \"web-01\"
provider = \"($name)\"
zone = \"us-east-1\"
plan = \"t3.micro\"
image = \"ubuntu-20.04\"
network_public_ip = \"auto\"
network_private_ip = \"auto\"
}
{
hostname = \"db-01\"
provider = \"($name)\"
zone = \"us-east-1\"
plan = \"t3.small\"
image = \"ubuntu-20.04\"
network_public_ip = \"auto\"
network_private_ip = \"auto\"
}
]
```
### Multi-Provider Configuration
```kcl
# Mix ($name) with other providers
servers = [
{
hostname = \"compute-01\"
provider = \"($name)\"
zone = \"us-east-1\"
plan = \"large\"
}
{
hostname = \"backup-01\"
provider = \"upcloud\"
zone = \"us-nyc1\"
plan = \"1xCPU-2GB\"
}
]
```
## Provider Testing
### Test Provider Loading
```bash
# Test provider registration
nu -c \"use provisioning/core/nulib/lib_provisioning/providers/registry.nu *; init-provider-registry; is-provider-available '($name)'\"
# Test provider loading
nu -c \"use provisioning/core/nulib/lib_provisioning/providers/loader.nu *; load-provider '($name)'\"
# Validate interface compliance
nu -c \"use provisioning/core/nulib/lib_provisioning/providers/interface.nu *; validate-provider-interface '($name)'\"
```
### Run Full Test Suite
```bash
# Run all provider tests
./provisioning/tools/test-provider-agnostic.nu run-all-tests
# Test specific provider
./provisioning/tools/test-provider-agnostic.nu test-provider-loading
```
## Integration with Provisioning System
### Using with Core CLI
```bash
# Create servers using the ($name) provider
./provisioning/core/cli/provisioning server create --infra example --check
# List servers from ($name) provider
./provisioning/core/cli/provisioning server list --provider ($name)
# Get server info
./provisioning/core/cli/provisioning server info web-01
```
### Provider-Agnostic Operations
```bash
# Query servers across all providers
nu -c \"use provisioning/extensions/providers/prov_lib/middleware_provider_agnostic.nu *; mw_query_servers {data: {servers: []}}\"
# Get provider status
nu -c \"use provisioning/extensions/providers/prov_lib/middleware_provider_agnostic.nu *; mw_provider_status\"
# Multi-provider deployment
nu -c \"use provisioning/extensions/providers/prov_lib/middleware_provider_agnostic.nu *; mw_deploy_multi_provider_infra \\$settings \\$plan\"
```
## Debugging
### Check Provider Status
```bash
# List all providers
nu -c \"use provisioning/core/nulib/lib_provisioning/providers/registry.nu *; list-providers\"
# Get provider capabilities
nu -c \"use provisioning/core/nulib/lib_provisioning/providers/registry.nu *; get-provider-capabilities-for '($name)'\"
# Check provider health
nu -c \"use provisioning/core/nulib/lib_provisioning/providers/loader.nu *; check-provider-health '($name)'\"
```
### Common Issues
1. **Provider not found**
```bash
# Refresh provider registry
nu -c \"use provisioning/core/nulib/lib_provisioning/providers/registry.nu *; init-provider-registry\"
```
2. **Authentication failed**
```bash
# Check environment variables
env | grep ($name | str upcase)
```
3. **Interface validation failed**
```bash
# Check missing functions
nu -c \"use provisioning/core/nulib/lib_provisioning/providers/interface.nu *; validate-provider-interface '($name)'\"
```
## Advanced Usage
### Custom Provider Functions
```bash
# Call provider-specific functions directly
nu -c \"use provisioning/extensions/providers/($name)/nulib/($name)/servers.nu *; ($name)_query_servers\"
```
### Provider Capabilities
```bash
# Check what the provider supports
nu -c \"use provisioning/extensions/providers/($name)/provider.nu *; get-provider-metadata\"
```
## Next Steps
1. Implement missing provider functions in `nulib/($name)/servers.nu`
2. Add authentication and API integration
3. Test with real infrastructure
4. Add provider-specific features and optimizations
5. Contribute improvements back to the project
"
}
# Show help
export def help []: nothing -> nothing {
let help_text = $"
Provider Creation Tool
USAGE:
./provisioning/tools/create-provider.nu <provider_name> [options]
ARGUMENTS:
provider_name Name of the new provider (e.g., 'digitalocean', 'linode')
OPTIONS:
--description Description of the provider (optional)
--api-base Base API URL for the provider (optional)
--auth-type Authentication type: token, key, oauth (default: token)
--template Template type: cloud, container, baremetal (default: cloud)
--dry-run Show what would be created without executing
EXAMPLES:
# Create a cloud provider with token auth
./provisioning/tools/create-provider.nu digitalocean --description \"DigitalOcean cloud provider\"
# Create a container platform provider
./provisioning/tools/create-provider.nu podman --template container
# Create a bare metal provider
./provisioning/tools/create-provider.nu datacenter --template baremetal
# Preview what would be created
./provisioning/tools/create-provider.nu newprovider --dry-run
TEMPLATES:
cloud - Full cloud provider with API integration (AWS, GCP, Azure style)
container - Container platform provider (Docker, Podman style)
baremetal - Bare metal or existing server provider (Local, SSH style)
NEXT STEPS:
1. Run the tool to create provider structure
2. Implement authentication in the generated files
3. Add provider-specific API calls
4. Test with: ./provisioning/tools/test-provider-agnostic.nu
5. Validate interface compliance
6. Add to infrastructure configurations
For more information, see docs/development/QUICK_PROVIDER_GUIDE.md
"
print $help_text
}