Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quick Developer Guide: Adding New Providers

This guide shows how to quickly add a new provider to the provider-agnostic infrastructure system.

Prerequisites

5-Minute Provider Addition

Step 1: Create Provider Directory

mkdir -p provisioning/extensions/providers/{provider_name}
mkdir -p provisioning/extensions/providers/{provider_name}/nulib/{provider_name}

Step 2: Copy Template and Customize

# Copy the local provider as a template
cp provisioning/extensions/providers/local/provider.nu \
   provisioning/extensions/providers/{provider_name}/provider.nu

Step 3: Update Provider Metadata

Edit provisioning/extensions/providers/{provider_name}/provider.nu:

export def get-provider-metadata []: nothing -> 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
        }
    }
}

Step 4: Implement Core Functions

The provider interface requires these essential functions:

# Required: Server operations
export def query_servers [find?: string, cols?: string]: nothing -> 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 -> 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 -> 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 -> 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 -> 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 -> bool {
    your_provider_server_state $server $new_state $error_exit $wait $settings
}

Step 5: Create Provider-Specific Functions

Create provisioning/extensions/providers/{provider_name}/nulib/{provider_name}/servers.nu:

# Example: DigitalOcean provider functions
export def digitalocean_query_servers [find?: string, cols?: string]: nothing -> 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 -> 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
}

Step 6: Test Your Provider

# 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"

Step 7: Add Provider to Infrastructure

Add to your KCL configuration:

# workspace/infra/example/servers.k
servers = [
    {
        hostname = "test-server"
        provider = "your_provider_name"
        zone = "your-region-1"
        plan = "your-instance-type"
    }
]

Provider Templates

Cloud Provider Template

For cloud providers (AWS, GCP, Azure, etc.):

# Use HTTP calls to cloud APIs
export def cloud_query_servers [find?: string, cols?: string]: nothing -> 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
}

Container Platform Template

For container platforms (Docker, Podman, etc.):

# Use CLI commands for container platforms
export def container_query_servers [find?: string, cols?: string]: nothing -> list {
    let containers = (docker ps --format json | from json)

    $containers | select Names State Status Image
}

Bare Metal Provider Template

For bare metal or existing servers:

# Use SSH or local commands
export def baremetal_query_servers [find?: string, cols?: string]: nothing -> list {
    # Read from inventory file or ping servers
    let inventory = (open inventory.yaml | from yaml)

    $inventory.servers | select hostname ip_address status
}

Best Practices

1. Error Handling

export def provider_operation []: nothing -> any {
    try {
        # Your provider operation
        provider_api_call
    } catch {|err|
        log-error $"Provider operation failed: ($err.msg)" "provider"
        if $error_exit { exit 1 }
        null
    }
}

2. Authentication

# Check for required environment variables
def check_auth []: nothing -> bool {
    if ($env | get -o PROVIDER_TOKEN) == null {
        log-error "PROVIDER_TOKEN environment variable required" "auth"
        return false
    }
    true
}

3. Rate Limiting

# Add delays for API rate limits
def api_call_with_retry [url: string]: nothing -> any {
    mut attempts = 0
    mut max_attempts = 3

    while $attempts < $max_attempts {
        try {
            return (http get $url)
        } catch {
            $attempts += 1
            sleep 1sec
        }
    }

    error make { msg: "API call failed after retries" }
}

4. Provider Capabilities

Set capabilities accurately:

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
}

Testing Checklist

  • Provider discovered by registry
  • Provider loads without errors
  • All required interface functions implemented
  • Provider metadata correct
  • Authentication working
  • Can query existing resources
  • Can create new resources (in test mode)
  • Error handling working
  • Compatible with existing infrastructure configs

Common Issues

Provider Not Found

# 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

Interface Validation Failed

# Check which functions are missing
nu -c "use provisioning/core/nulib/lib_provisioning/providers/interface.nu *; validate-provider-interface 'your_provider_name'"

Authentication Errors

# Check environment variables
env | grep PROVIDER

# Test API access manually
curl -H "Authorization: Bearer $PROVIDER_TOKEN" https://api.provider.com/test

Next Steps

  1. Documentation: Add provider-specific documentation to docs/providers/
  2. Examples: Create example infrastructure using your provider
  3. Testing: Add integration tests for your provider
  4. Optimization: Implement caching and performance optimizations
  5. Features: Add provider-specific advanced features

Getting Help

  • Check existing providers for implementation patterns
  • Review the Provider Interface Documentation
  • Test with the provider test suite: ./provisioning/tools/test-provider-agnostic.nu
  • Run migration checks: ./provisioning/tools/migrate-to-provider-agnostic.nu status