# Extension Development Guide

This guide will help you create custom providers, task services, and cluster configurations to extend provisioning for your specific needs.

## What You'll Learn

- Extension architecture and concepts
- Creating custom cloud providers
- Developing task services
- Building cluster configurations
- Publishing and sharing extensions
- Best practices and patterns
- Testing and validation

## Extension Architecture

### Extension Types

| Extension Type | Purpose | Examples |
|----------------|---------|----------|
| **Providers** | Cloud platform integrations | Custom cloud, on-premises |
| **Task Services** | Software components | Custom databases, monitoring |
| **Clusters** | Service orchestration | Application stacks, platforms |
| **Templates** | Reusable configurations | Standard deployments |

### Extension Structure

```plaintext
my-extension/
├── schemas/                # Nickel schemas and models
│   ├── contracts.ncl      # Type contracts
│   ├── providers/         # Provider definitions
│   ├── taskservs/         # Task service definitions
│   └── clusters/          # Cluster definitions
├── nulib/                 # Nushell implementation
│   ├── providers/         # Provider logic
│   ├── taskservs/         # Task service logic
│   └── utils/             # Utility functions
├── templates/             # Configuration templates
├── tests/                 # Test files
├── docs/                  # Documentation
├── extension.toml         # Extension metadata
└── README.md              # Extension documentation
```plaintext

### Extension Metadata

`extension.toml`:

```toml
[extension]
name = "my-custom-provider"
version = "1.0.0"
description = "Custom cloud provider integration"
author = "Your Name <you@example.com>"
license = "MIT"

[compatibility]
provisioning_version = ">=1.0.0"
nickel_version = ">=1.15.0"

[provides]
providers = ["custom-cloud"]
taskservs = ["custom-database"]
clusters = ["custom-stack"]

[dependencies]
extensions = []
system_packages = ["curl", "jq"]

[configuration]
required_env = ["CUSTOM_CLOUD_API_KEY"]
optional_env = ["CUSTOM_CLOUD_REGION"]
```plaintext

## Creating Custom Providers

### Provider Architecture

A provider handles:

- Authentication with cloud APIs
- Resource lifecycle management (create, read, update, delete)
- Provider-specific configurations
- Cost estimation and billing integration

### Step 1: Define Provider Schema

`schemas/providers/custom_cloud.ncl`:

```nickel
# Custom cloud provider schema
{
  CustomCloudConfig = {
    # Configuration for Custom Cloud provider
    # Authentication
    api_key | String,
    api_secret | String = "",
    region | String = "us-west-1",

    # Provider-specific settings
    project_id | String = "",
    organization | String = "",

    # API configuration
    api_url | String = "https://api.custom-cloud.com/v1",
    timeout | Number = 30,

    # Cost configuration
    billing_account | String = "",
    cost_center | String = "",
  },

  CustomCloudServer = {
    # Server configuration for Custom Cloud
    # Instance configuration
    machine_type | String,
    zone | String,
    disk_size | Number = 20,
    disk_type | String = "ssd",

    # Network configuration
    vpc | String = "",
    subnet | String = "",
    external_ip | Bool = true,

    # Custom Cloud specific
    preemptible | Bool = false,
    labels | {String: String} = {},
  },

  # Provider capabilities
  provider_capabilities = {
    name = "custom-cloud",
    supports_auto_scaling = true,
    supports_load_balancing = true,
    supports_managed_databases = true,
    regions = [
      "us-west-1", "us-west-2", "us-east-1", "eu-west-1"
    ],
    machine_types = [
      "micro", "small", "medium", "large", "xlarge"
    ],
  },
}
```plaintext

### Step 2: Implement Provider Logic

`nulib/providers/custom_cloud.nu`:

```nushell
# Custom Cloud provider implementation

# Provider initialization
export def custom_cloud_init [] {
    # Validate environment variables
    if ($env.CUSTOM_CLOUD_API_KEY | is-empty) {
        error make {
            msg: "CUSTOM_CLOUD_API_KEY environment variable is required"
        }
    }

    # Set up provider context
    $env.CUSTOM_CLOUD_INITIALIZED = true
}

# Create server instance
export def custom_cloud_create_server [
    server_config: record
    --check: bool = false    # Dry run mode
] -> record {
    custom_cloud_init

    print $"Creating server: ($server_config.name)"

    if $check {
        return {
            action: "create"
            resource: "server"
            name: $server_config.name
            status: "planned"
            estimated_cost: (calculate_server_cost $server_config)
        }
    }

    # Make API call to create server
    let api_response = (custom_cloud_api_call "POST" "instances" $server_config)

    if ($api_response.status | str contains "error") {
        error make {
            msg: $"Failed to create server: ($api_response.message)"
        }
    }

    # Wait for server to be ready
    let server_id = $api_response.instance_id
    custom_cloud_wait_for_server $server_id "running"

    return {
        id: $server_id
        name: $server_config.name
        status: "running"
        ip_address: $api_response.ip_address
        created_at: (date now | format date "%Y-%m-%d %H:%M:%S")
    }
}

# Delete server instance
export def custom_cloud_delete_server [
    server_name: string
    --keep_storage: bool = false
] -> record {
    custom_cloud_init

    let server = (custom_cloud_get_server $server_name)

    if ($server | is-empty) {
        error make {
            msg: $"Server not found: ($server_name)"
        }
    }

    print $"Deleting server: ($server_name)"

    # Delete the instance
    let delete_response = (custom_cloud_api_call "DELETE" $"instances/($server.id)" {
        keep_storage: $keep_storage
    })

    return {
        action: "delete"
        resource: "server"
        name: $server_name
        status: "deleted"
    }
}

# List servers
export def custom_cloud_list_servers [] -> list<record> {
    custom_cloud_init

    let response = (custom_cloud_api_call "GET" "instances" {})

    return ($response.instances | each {|instance|
        {
            id: $instance.id
            name: $instance.name
            status: $instance.status
            machine_type: $instance.machine_type
            zone: $instance.zone
            ip_address: $instance.ip_address
            created_at: $instance.created_at
        }
    })
}

# Get server details
export def custom_cloud_get_server [server_name: string] -> record {
    let servers = (custom_cloud_list_servers)
    return ($servers | where name == $server_name | first)
}

# Calculate estimated costs
export def calculate_server_cost [server_config: record] -> float {
    # Cost calculation logic based on machine type
    let base_costs = {
        micro: 0.01
        small: 0.05
        medium: 0.10
        large: 0.20
        xlarge: 0.40
    }

    let machine_cost = ($base_costs | get $server_config.machine_type)
    let storage_cost = ($server_config.disk_size | default 20) * 0.001

    return ($machine_cost + $storage_cost)
}

# Make API call to Custom Cloud
def custom_cloud_api_call [
    method: string
    endpoint: string
    data: record
] -> record {
    let api_url = ($env.CUSTOM_CLOUD_API_URL | default "https://api.custom-cloud.com/v1")
    let api_key = $env.CUSTOM_CLOUD_API_KEY

    let headers = {
        "Authorization": $"Bearer ($api_key)"
        "Content-Type": "application/json"
    }

    let url = $"($api_url)/($endpoint)"

    match $method {
        "GET" => {
            http get $url --headers $headers
        }
        "POST" => {
            http post $url --headers $headers ($data | to json)
        }
        "PUT" => {
            http put $url --headers $headers ($data | to json)
        }
        "DELETE" => {
            http delete $url --headers $headers
        }
        _ => {
            error make {
                msg: $"Unsupported HTTP method: ($method)"
            }
        }
    }
}

# Wait for server to reach desired state
def custom_cloud_wait_for_server [
    server_id: string
    target_status: string
    --timeout: int = 300
] {
    let start_time = (date now)

    loop {
        let response = (custom_cloud_api_call "GET" $"instances/($server_id)" {})
        let current_status = $response.status

        if $current_status == $target_status {
            print $"Server ($server_id) reached status: ($target_status)"
            break
        }

        let elapsed = ((date now) - $start_time) / 1000000000  # Convert to seconds
        if $elapsed > $timeout {
            error make {
                msg: $"Timeout waiting for server ($server_id) to reach ($target_status)"
            }
        }

        sleep 10sec
        print $"Waiting for server status: ($current_status) -> ($target_status)"
    }
}
```plaintext

### Step 3: Provider Registration

`nulib/providers/mod.nu`:

```nushell
# Provider module exports
export use custom_cloud.nu *

# Provider registry
export def get_provider_info [] -> record {
    {
        name: "custom-cloud"
        version: "1.0.0"
        capabilities: {
            servers: true
            load_balancers: true
            databases: false
            storage: true
        }
        regions: ["us-west-1", "us-west-2", "us-east-1", "eu-west-1"]
        auth_methods: ["api_key", "oauth"]
    }
}
```plaintext

## Creating Custom Task Services

### Task Service Architecture

Task services handle:

- Software installation and configuration
- Service lifecycle management
- Health checking and monitoring
- Version management and updates

### Step 1: Define Service Schema

`schemas/taskservs/custom_database.ncl`:

```nickel
# Custom database task service
{
  CustomDatabaseConfig = {
    # Configuration for Custom Database service
    # Database configuration
    version | String = "14.0",
    port | Number = 5432,
    max_connections | Number = 100,
    memory_limit | String = "512 MB",

    # Data configuration
    data_directory | String = "/var/lib/customdb",
    log_directory | String = "/var/log/customdb",

    # Replication
    replication | {
      enabled | Bool = false,
      mode | String = "async",
      replicas | Number = 1,
    } = {},

    # Backup configuration
    backup | {
      enabled | Bool = true,
      schedule | String = "0 2 * * *",
      retention_days | Number = 7,
      storage_location | String = "local",
    } = {},

    # Security
    ssl | {
      enabled | Bool = true,
      cert_file | String = "/etc/ssl/certs/customdb.crt",
      key_file | String = "/etc/ssl/private/customdb.key",
    } = {},

    # Monitoring
    monitoring | {
      enabled | Bool = true,
      metrics_port | Number = 9187,
      log_level | String = "info",
    } = {},
  },

  # Service metadata
  service_metadata = {
    name = "custom-database",
    description = "Custom Database Server",
    version = "14.0",
    category = "database",
    dependencies = ["systemd"],
    supported_os = ["ubuntu", "debian", "centos", "rhel"],
    ports = [5432, 9187],
    data_directories = ["/var/lib/customdb"],
  },
}
```plaintext

### Step 2: Implement Service Logic

`nulib/taskservs/custom_database.nu`:

```nushell
# Custom Database task service implementation

# Install custom database
export def install_custom_database [
    config: record
    --check: bool = false
] -> record {
    print "Installing Custom Database..."

    if $check {
        return {
            action: "install"
            service: "custom-database"
            version: ($config.version | default "14.0")
            status: "planned"
            changes: [
                "Install Custom Database packages"
                "Configure database server"
                "Start database service"
                "Set up monitoring"
            ]
        }
    }

    # Check prerequisites
    validate_prerequisites $config

    # Install packages
    install_packages $config

    # Configure service
    configure_service $config

    # Initialize database
    initialize_database $config

    # Set up monitoring
    if ($config.monitoring?.enabled | default true) {
        setup_monitoring $config
    }

    # Set up backups
    if ($config.backup?.enabled | default true) {
        setup_backups $config
    }

    # Start service
    start_service

    # Verify installation
    let status = (verify_installation $config)

    return {
        action: "install"
        service: "custom-database"
        version: ($config.version | default "14.0")
        status: $status.status
        endpoint: $"localhost:($config.port | default 5432)"
        data_directory: ($config.data_directory | default "/var/lib/customdb")
    }
}

# Configure custom database
export def configure_custom_database [
    config: record
] {
    print "Configuring Custom Database..."

    # Generate configuration file
    let db_config = generate_config $config
    $db_config | save "/etc/customdb/customdb.conf"

    # Set up SSL if enabled
    if ($config.ssl?.enabled | default true) {
        setup_ssl $config
    }

    # Configure replication if enabled
    if ($config.replication?.enabled | default false) {
        setup_replication $config
    }

    # Restart service to apply configuration
    restart_service
}

# Start service
export def start_custom_database [] {
    print "Starting Custom Database service..."
    ^systemctl start customdb
    ^systemctl enable customdb
}

# Stop service
export def stop_custom_database [] {
    print "Stopping Custom Database service..."
    ^systemctl stop customdb
}

# Check service status
export def status_custom_database [] -> record {
    let systemd_status = (^systemctl is-active customdb | str trim)
    let port_check = (check_port 5432)
    let version = (get_database_version)

    return {
        service: "custom-database"
        status: $systemd_status
        port_accessible: $port_check
        version: $version
        uptime: (get_service_uptime)
        connections: (get_active_connections)
    }
}

# Health check
export def health_custom_database [] -> record {
    let status = (status_custom_database)
    let health_checks = [
        {
            name: "Service Running"
            status: ($status.status == "active")
            message: $"Systemd status: ($status.status)"
        }
        {
            name: "Port Accessible"
            status: $status.port_accessible
            message: "Database port 5432 is accessible"
        }
        {
            name: "Database Responsive"
            status: (test_database_connection)
            message: "Database responds to queries"
        }
    ]

    let healthy = ($health_checks | all {|check| $check.status})

    return {
        service: "custom-database"
        healthy: $healthy
        checks: $health_checks
        last_check: (date now | format date "%Y-%m-%d %H:%M:%S")
    }
}

# Update service
export def update_custom_database [
    target_version: string
] -> record {
    print $"Updating Custom Database to version ($target_version)..."

    # Create backup before update
    backup_database "pre-update"

    # Stop service
    stop_custom_database

    # Update packages
    update_packages $target_version

    # Migrate database if needed
    migrate_database $target_version

    # Start service
    start_custom_database

    # Verify update
    let new_version = (get_database_version)

    return {
        action: "update"
        service: "custom-database"
        old_version: (get_previous_version)
        new_version: $new_version
        status: "completed"
    }
}

# Remove service
export def remove_custom_database [
    --keep_data: bool = false
] -> record {
    print "Removing Custom Database..."

    # Stop service
    stop_custom_database

    # Remove packages
    ^apt remove --purge -y customdb-server customdb-client

    # Remove configuration
    rm -rf "/etc/customdb"

    # Remove data (optional)
    if not $keep_data {
        print "Removing database data..."
        rm -rf "/var/lib/customdb"
        rm -rf "/var/log/customdb"
    }

    return {
        action: "remove"
        service: "custom-database"
        data_preserved: $keep_data
        status: "completed"
    }
}

# Helper functions

def validate_prerequisites [config: record] {
    # Check operating system
    let os_info = (^lsb_release -is | str trim | str downcase)
    let supported_os = ["ubuntu", "debian"]

    if not ($os_info in $supported_os) {
        error make {
            msg: $"Unsupported OS: ($os_info). Supported: ($supported_os | str join ', ')"
        }
    }

    # Check system resources
    let memory_mb = (^free -m | lines | get 1 | split row ' ' | get 1 | into int)
    if $memory_mb < 512 {
        error make {
            msg: $"Insufficient memory: ($memory_mb)MB. Minimum 512 MB required."
        }
    }
}

def install_packages [config: record] {
    let version = ($config.version | default "14.0")

    # Update package list
    ^apt update

    # Install packages
    ^apt install -y $"customdb-server-($version)" $"customdb-client-($version)"
}

def configure_service [config: record] {
    let config_content = generate_config $config
    $config_content | save "/etc/customdb/customdb.conf"

    # Set permissions
    ^chown -R customdb:customdb "/etc/customdb"
    ^chmod 600 "/etc/customdb/customdb.conf"
}

def generate_config [config: record] -> string {
    let port = ($config.port | default 5432)
    let max_connections = ($config.max_connections | default 100)
    let memory_limit = ($config.memory_limit | default "512 MB")

    return $"
# Custom Database Configuration
port = ($port)
max_connections = ($max_connections)
shared_buffers = ($memory_limit)
data_directory = '($config.data_directory | default "/var/lib/customdb")'
log_directory = '($config.log_directory | default "/var/log/customdb")'

# Logging
log_level = '($config.monitoring?.log_level | default "info")'

# SSL Configuration
ssl = ($config.ssl?.enabled | default true)
ssl_cert_file = '($config.ssl?.cert_file | default "/etc/ssl/certs/customdb.crt")'
ssl_key_file = '($config.ssl?.key_file | default "/etc/ssl/private/customdb.key")'
"
}

def initialize_database [config: record] {
    print "Initializing database..."

    # Create data directory
    let data_dir = ($config.data_directory | default "/var/lib/customdb")
    mkdir $data_dir
    ^chown -R customdb:customdb $data_dir

    # Initialize database
    ^su - customdb -c $"customdb-initdb -D ($data_dir)"
}

def setup_monitoring [config: record] {
    if ($config.monitoring?.enabled | default true) {
        print "Setting up monitoring..."

        # Install monitoring exporter
        ^apt install -y customdb-exporter

        # Configure exporter
        let exporter_config = $"
port: ($config.monitoring?.metrics_port | default 9187)
database_url: postgresql://localhost:($config.port | default 5432)/postgres
"
        $exporter_config | save "/etc/customdb-exporter/config.yaml"

        # Start exporter
        ^systemctl enable customdb-exporter
        ^systemctl start customdb-exporter
    }
}

def setup_backups [config: record] {
    if ($config.backup?.enabled | default true) {
        print "Setting up backups..."

        let schedule = ($config.backup?.schedule | default "0 2 * * *")
        let retention = ($config.backup?.retention_days | default 7)

        # Create backup script
        let backup_script = $"#!/bin/bash
customdb-dump --all-databases > /var/backups/customdb-$(date +%Y%m%d_%H%M%S).sql
find /var/backups -name 'customdb-*.sql' -mtime +($retention) -delete
"

        $backup_script | save "/usr/local/bin/customdb-backup.sh"
        ^chmod +x "/usr/local/bin/customdb-backup.sh"

        # Add to crontab
        $"($schedule) /usr/local/bin/customdb-backup.sh" | ^crontab -u customdb -
    }
}

def test_database_connection [] -> bool {
    let result = (^customdb-cli -h localhost -c "SELECT 1;" | complete)
    return ($result.exit_code == 0)
}

def get_database_version [] -> string {
    let result = (^customdb-cli -h localhost -c "SELECT version();" | complete)
    if ($result.exit_code == 0) {
        return ($result.stdout | lines | first | parse "Custom Database {version}" | get version.0)
    } else {
        return "unknown"
    }
}

def check_port [port: int] -> bool {
    let result = (^nc -z localhost $port | complete)
    return ($result.exit_code == 0)
}
```plaintext

## Creating Custom Clusters

### Cluster Architecture

Clusters orchestrate multiple services to work together as a cohesive application stack.

### Step 1: Define Cluster Schema

`schemas/clusters/custom_web_stack.ncl`:

```nickel
# Custom web application stack
{
  CustomWebStackConfig = {
    # Configuration for Custom Web Application Stack
    # Application configuration
    app_name | String,
    app_version | String = "latest",
    environment | String = "production",

    # Web tier configuration
    web_tier | {
      replicas | Number = 3,
      instance_type | String = "t3.medium",
      load_balancer | {
        enabled | Bool = true,
        ssl | Bool = true,
        health_check_path | String = "/health",
      } = {},
    },

    # Application tier configuration
    app_tier | {
      replicas | Number = 5,
      instance_type | String = "t3.large",
      auto_scaling | {
        enabled | Bool = true,
        min_replicas | Number = 2,
        max_replicas | Number = 10,
        cpu_threshold | Number = 70,
      } = {},
    },

    # Database tier configuration
    database_tier | {
      type | String = "postgresql",
      instance_type | String = "t3.xlarge",
      high_availability | Bool = true,
      backup_enabled | Bool = true,
    } = {},

    # Monitoring configuration
    monitoring | {
      enabled | Bool = true,
      metrics_retention | String = "30d",
      alerting | Bool = true,
    } = {},

    # Networking
    network | {
      vpc_cidr | String = "10.0.0.0/16",
      public_subnets | [String] = ["10.0.1.0/24", "10.0.2.0/24"],
      private_subnets | [String] = ["10.0.10.0/24", "10.0.20.0/24"],
      database_subnets | [String] = ["10.0.100.0/24", "10.0.200.0/24"],
    } = {},
  },

  # Cluster blueprint
  cluster_blueprint = {
    name = "custom-web-stack",
    description = "Custom web application stack with load balancer, app servers, and database",
    version = "1.0.0",
    components = [
      {
        name = "load-balancer",
        type = "taskserv",
        service = "haproxy",
        tier = "web",
      },
      {
        name = "web-servers",
        type = "server",
        tier = "web",
        scaling = "horizontal",
      },
      {
        name = "app-servers",
        type = "server",
        tier = "app",
        scaling = "horizontal",
      },
      {
        name = "database",
        type = "taskserv",
        service = "postgresql",
        tier = "database",
      },
      {
        name = "monitoring",
        type = "taskserv",
        service = "prometheus",
        tier = "monitoring",
      },
    ],
  },
}
```plaintext

### Step 2: Implement Cluster Logic

`nulib/clusters/custom_web_stack.nu`:

```nushell
# Custom Web Stack cluster implementation

# Deploy web stack cluster
export def deploy_custom_web_stack [
    config: record
    --check: bool = false
] -> record {
    print $"Deploying Custom Web Stack: ($config.app_name)"

    if $check {
        return {
            action: "deploy"
            cluster: "custom-web-stack"
            app_name: $config.app_name
            status: "planned"
            components: [
                "Network infrastructure"
                "Load balancer"
                "Web servers"
                "Application servers"
                "Database"
                "Monitoring"
            ]
            estimated_cost: (calculate_cluster_cost $config)
        }
    }

    # Deploy in order
    let network = (deploy_network $config)
    let database = (deploy_database $config)
    let app_servers = (deploy_app_tier $config)
    let web_servers = (deploy_web_tier $config)
    let load_balancer = (deploy_load_balancer $config)
    let monitoring = (deploy_monitoring $config)

    # Configure service discovery
    configure_service_discovery $config

    # Set up health checks
    setup_health_checks $config

    return {
        action: "deploy"
        cluster: "custom-web-stack"
        app_name: $config.app_name
        status: "deployed"
        components: {
            network: $network
            database: $database
            app_servers: $app_servers
            web_servers: $web_servers
            load_balancer: $load_balancer
            monitoring: $monitoring
        }
        endpoints: {
            web: $load_balancer.public_ip
            monitoring: $monitoring.grafana_url
        }
    }
}

# Scale cluster
export def scale_custom_web_stack [
    app_name: string
    tier: string
    replicas: int
] -> record {
    print $"Scaling ($tier) tier to ($replicas) replicas for ($app_name)"

    match $tier {
        "web" => {
            scale_web_tier $app_name $replicas
        }
        "app" => {
            scale_app_tier $app_name $replicas
        }
        _ => {
            error make {
                msg: $"Invalid tier: ($tier). Valid options: web, app"
            }
        }
    }

    return {
        action: "scale"
        cluster: "custom-web-stack"
        app_name: $app_name
        tier: $tier
        new_replicas: $replicas
        status: "completed"
    }
}

# Update cluster
export def update_custom_web_stack [
    app_name: string
    config: record
] -> record {
    print $"Updating Custom Web Stack: ($app_name)"

    # Rolling update strategy
    update_app_tier $app_name $config
    update_web_tier $app_name $config
    update_load_balancer $app_name $config

    return {
        action: "update"
        cluster: "custom-web-stack"
        app_name: $app_name
        status: "completed"
    }
}

# Delete cluster
export def delete_custom_web_stack [
    app_name: string
    --keep_data: bool = false
] -> record {
    print $"Deleting Custom Web Stack: ($app_name)"

    # Delete in reverse order
    delete_load_balancer $app_name
    delete_web_tier $app_name
    delete_app_tier $app_name

    if not $keep_data {
        delete_database $app_name
    }

    delete_monitoring $app_name
    delete_network $app_name

    return {
        action: "delete"
        cluster: "custom-web-stack"
        app_name: $app_name
        data_preserved: $keep_data
        status: "completed"
    }
}

# Cluster status
export def status_custom_web_stack [
    app_name: string
] -> record {
    let web_status = (get_web_tier_status $app_name)
    let app_status = (get_app_tier_status $app_name)
    let db_status = (get_database_status $app_name)
    let lb_status = (get_load_balancer_status $app_name)
    let monitoring_status = (get_monitoring_status $app_name)

    let overall_healthy = (
        $web_status.healthy and
        $app_status.healthy and
        $db_status.healthy and
        $lb_status.healthy and
        $monitoring_status.healthy
    )

    return {
        cluster: "custom-web-stack"
        app_name: $app_name
        healthy: $overall_healthy
        components: {
            web_tier: $web_status
            app_tier: $app_status
            database: $db_status
            load_balancer: $lb_status
            monitoring: $monitoring_status
        }
        last_check: (date now | format date "%Y-%m-%d %H:%M:%S")
    }
}

# Helper functions for deployment

def deploy_network [config: record] -> record {
    print "Deploying network infrastructure..."

    # Create VPC
    let vpc_config = {
        cidr: ($config.network.vpc_cidr | default "10.0.0.0/16")
        name: $"($config.app_name)-vpc"
    }

    # Create subnets
    let subnets = [
        {name: "public-1", cidr: ($config.network.public_subnets | get 0)}
        {name: "public-2", cidr: ($config.network.public_subnets | get 1)}
        {name: "private-1", cidr: ($config.network.private_subnets | get 0)}
        {name: "private-2", cidr: ($config.network.private_subnets | get 1)}
        {name: "database-1", cidr: ($config.network.database_subnets | get 0)}
        {name: "database-2", cidr: ($config.network.database_subnets | get 1)}
    ]

    return {
        vpc: $vpc_config
        subnets: $subnets
        status: "deployed"
    }
}

def deploy_database [config: record] -> record {
    print "Deploying database tier..."

    let db_config = {
        name: $"($config.app_name)-db"
        type: ($config.database_tier.type | default "postgresql")
        instance_type: ($config.database_tier.instance_type | default "t3.xlarge")
        high_availability: ($config.database_tier.high_availability | default true)
        backup_enabled: ($config.database_tier.backup_enabled | default true)
    }

    # Deploy database servers
    if $db_config.high_availability {
        deploy_ha_database $db_config
    } else {
        deploy_single_database $db_config
    }

    return {
        name: $db_config.name
        type: $db_config.type
        high_availability: $db_config.high_availability
        status: "deployed"
        endpoint: $"($config.app_name)-db.local:5432"
    }
}

def deploy_app_tier [config: record] -> record {
    print "Deploying application tier..."

    let replicas = ($config.app_tier.replicas | default 5)

    # Deploy app servers
    mut servers = []
    for i in 1..$replicas {
        let server_config = {
            name: $"($config.app_name)-app-($i | fill --width 2 --char '0')"
            instance_type: ($config.app_tier.instance_type | default "t3.large")
            subnet: "private"
        }

        let server = (deploy_app_server $server_config)
        $servers = ($servers | append $server)
    }

    return {
        tier: "application"
        servers: $servers
        replicas: $replicas
        status: "deployed"
    }
}

def calculate_cluster_cost [config: record] -> float {
    let web_cost = ($config.web_tier.replicas | default 3) * 0.10
    let app_cost = ($config.app_tier.replicas | default 5) * 0.20
    let db_cost = if ($config.database_tier.high_availability | default true) { 0.80 } else { 0.40 }
    let lb_cost = 0.05

    return ($web_cost + $app_cost + $db_cost + $lb_cost)
}
```plaintext

## Extension Testing

### Test Structure

```plaintext
tests/
├── unit/                   # Unit tests
│   ├── provider_test.nu   # Provider unit tests
│   ├── taskserv_test.nu   # Task service unit tests
│   └── cluster_test.nu    # Cluster unit tests
├── integration/            # Integration tests
│   ├── provider_integration_test.nu
│   ├── taskserv_integration_test.nu
│   └── cluster_integration_test.nu
├── e2e/                   # End-to-end tests
│   └── full_stack_test.nu
└── fixtures/              # Test data
    ├── configs/
    └── mocks/
```plaintext

### Example Unit Test

`tests/unit/provider_test.nu`:

```nushell
# Unit tests for custom cloud provider

use std testing

export def test_provider_validation [] {
    # Test valid configuration
    let valid_config = {
        api_key: "test-key"
        region: "us-west-1"
        project_id: "test-project"
    }

    let result = (validate_custom_cloud_config $valid_config)
    assert equal $result.valid true

    # Test invalid configuration
    let invalid_config = {
        region: "us-west-1"
        # Missing api_key
    }

    let result2 = (validate_custom_cloud_config $invalid_config)
    assert equal $result2.valid false
    assert str contains $result2.error "api_key"
}

export def test_cost_calculation [] {
    let server_config = {
        machine_type: "medium"
        disk_size: 50
    }

    let cost = (calculate_server_cost $server_config)
    assert equal $cost 0.15  # 0.10 (medium) + 0.05 (50 GB storage)
}

export def test_api_call_formatting [] {
    let config = {
        name: "test-server"
        machine_type: "small"
        zone: "us-west-1a"
    }

    let api_payload = (format_create_server_request $config)

    assert str contains ($api_payload | to json) "test-server"
    assert equal $api_payload.machine_type "small"
    assert equal $api_payload.zone "us-west-1a"
}
```plaintext

### Integration Test

`tests/integration/provider_integration_test.nu`:

```nushell
# Integration tests for custom cloud provider

use std testing

export def test_server_lifecycle [] {
    # Set up test environment
    $env.CUSTOM_CLOUD_API_KEY = "test-api-key"
    $env.CUSTOM_CLOUD_API_URL = "https://api.test.custom-cloud.com/v1"

    let server_config = {
        name: "test-integration-server"
        machine_type: "micro"
        zone: "us-west-1a"
    }

    # Test server creation
    let create_result = (custom_cloud_create_server $server_config --check true)
    assert equal $create_result.status "planned"

    # Note: Actual creation would require valid API credentials
    # In integration tests, you might use a test/sandbox environment
}

export def test_server_listing [] {
    # Mock API response for testing
    with-env [CUSTOM_CLOUD_API_KEY "test-key"] {
        # This would test against a real API in integration environment
        let servers = (custom_cloud_list_servers)
        assert ($servers | is-not-empty)
    }
}
```plaintext

## Publishing Extensions

### Extension Package Structure

```plaintext
my-extension-package/
├── extension.toml         # Extension metadata
├── README.md             # Documentation
├── LICENSE               # License file
├── CHANGELOG.md          # Version history
├── examples/             # Usage examples
├── src/                  # Source code
│   ├── kcl/
│   ├── nulib/
│   └── templates/
└── tests/               # Test files
```plaintext

### Publishing Configuration

`extension.toml`:

```toml
[extension]
name = "my-custom-provider"
version = "1.0.0"
description = "Custom cloud provider integration"
author = "Your Name <you@example.com>"
license = "MIT"
homepage = "https://github.com/username/my-custom-provider"
repository = "https://github.com/username/my-custom-provider"
keywords = ["cloud", "provider", "infrastructure"]
categories = ["providers"]

[compatibility]
provisioning_version = ">=1.0.0"
nickel_version = ">=1.15.0"

[provides]
providers = ["custom-cloud"]
taskservs = []
clusters = []

[dependencies]
system_packages = ["curl", "jq"]
extensions = []

[build]
include = ["src/**", "examples/**", "README.md", "LICENSE"]
exclude = ["tests/**", ".git/**", "*.tmp"]
```plaintext

### Publishing Process

```bash
# 1. Validate extension
provisioning extension validate .

# 2. Run tests
provisioning extension test .

# 3. Build package
provisioning extension build .

# 4. Publish to registry
provisioning extension publish ./dist/my-custom-provider-1.0.0.tar.gz
```plaintext

## Best Practices

### 1. Code Organization

```plaintext
# Follow standard structure
extension/
├── schemas/      # Nickel schemas and models
├── nulib/        # Nushell implementation
├── templates/    # Configuration templates
├── tests/        # Comprehensive tests
└── docs/         # Documentation
```plaintext

### 2. Error Handling

```nushell
# Always provide meaningful error messages
if ($api_response | get -o status | default "" | str contains "error") {
    error make {
        msg: $"API Error: ($api_response.message)"
        label: {
            text: "Custom Cloud API failure"
            span: (metadata $api_response | get span)
        }
        help: "Check your API key and network connectivity"
    }
}
```plaintext

### 3. Configuration Validation

```nickel
# Use Nickel's validation features with contracts
{
  CustomConfig = {
    # Configuration with validation
    name | String | doc "Name must not be empty",
    size | Number | doc "Size must be positive and at most 1000",
  },

  # Validation rules
  validate_config = fun config =>
    let valid_name = (std.string.length config.name) > 0 in
    let valid_size = config.size > 0 && config.size <= 1000 in
    if valid_name && valid_size then
      config
    else
      (std.fail "Configuration validation failed"),
}
```plaintext

### 4. Testing

- Write comprehensive unit tests
- Include integration tests
- Test error conditions
- Use fixtures for consistent test data
- Mock external dependencies

### 5. Documentation

- Include README with examples
- Document all configuration options
- Provide troubleshooting guide
- Include architecture diagrams
- Write API documentation

## Next Steps

Now that you understand extension development:

1. **Study existing extensions** in the `providers/` and `taskservs/` directories
2. **Practice with simple extensions** before building complex ones
3. **Join the community** to share and collaborate on extensions
4. **Contribute to the core system** by improving extension APIs
5. **Build a library** of reusable templates and patterns

You're now equipped to extend provisioning for any custom requirements!
