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

KCL Module Organization Guide

This guide explains how to organize KCL modules and create extensions for the provisioning system.

Module Structure Overview

provisioning/
├── kcl/                          # Core provisioning schemas
│   ├── settings.k                # Main Settings schema
│   ├── defaults.k                # Default configurations
│   └── main.k                    # Module entry point
├── extensions/
│   ├── kcl/                      # KCL expects modules here
│   │   └── provisioning/0.0.1/   # Auto-generated from provisioning/kcl/
│   ├── providers/                # Cloud providers
│   │   ├── upcloud/kcl/
│   │   ├── aws/kcl/
│   │   └── local/kcl/
│   ├── taskservs/                # Infrastructure services
│   │   ├── kubernetes/kcl/
│   │   ├── cilium/kcl/
│   │   ├── redis/kcl/            # Our example
│   │   └── {service}/kcl/
│   └── clusters/                 # Complete cluster definitions
└── config/                       # TOML configuration files

workspace/
└── infra/
    └── {your-infra}/             # Your infrastructure workspace
        ├── kcl.mod               # Module dependencies
        ├── settings.k            # Infrastructure settings
        ├── task-servs/           # Taskserver configurations
        └── clusters/             # Cluster configurations

Import Path Conventions

1. Core Provisioning Schemas

# Import main provisioning schemas
import provisioning

# Use Settings schema
_settings = provisioning.Settings {
    main_name = "my-infra"
    # ... other settings
}

2. Taskserver Schemas

# Import specific taskserver
import taskservs.{service}.kcl.{service} as {service}_schema

# Examples:
import taskservs.kubernetes.kcl.kubernetes as k8s_schema
import taskservs.cilium.kcl.cilium as cilium_schema
import taskservs.redis.kcl.redis as redis_schema

# Use the schema
_taskserv = redis_schema.Redis {
    version = "7.2.3"
    port = 6379
}

3. Provider Schemas

# Import cloud provider schemas
import {provider}_prov.{provider} as {provider}_schema

# Examples:
import upcloud_prov.upcloud as upcloud_schema
import aws_prov.aws as aws_schema

4. Cluster Schemas

# Import cluster definitions
import cluster.{cluster_name} as {cluster}_schema

KCL Module Resolution Issues & Solutions

Problem: Path Resolution

KCL ignores the actual path in kcl.mod and uses convention-based resolution.

What you write in kcl.mod:

[dependencies]
provisioning = { path = "../../../provisioning/kcl", version = "0.0.1" }

Where KCL actually looks:

/provisioning/extensions/kcl/provisioning/0.0.1/

Solutions:

Copy your KCL modules to where KCL expects them:

mkdir -p provisioning/extensions/kcl/provisioning/0.0.1
cp -r provisioning/kcl/* provisioning/extensions/kcl/provisioning/0.0.1/

Solution 2: Workspace-Local Copies

For development workspaces, copy modules locally:

cp -r ../../../provisioning/kcl workspace/infra/wuji/provisioning

Solution 3: Direct File Imports (Limited)

For simple cases, import files directly:

kcl run ../../../provisioning/kcl/settings.k

Creating New Taskservers

Directory Structure

provisioning/extensions/taskservs/{service}/
├── kcl/
│   ├── kcl.mod               # Module definition
│   ├── {service}.k           # KCL schema
│   └── dependencies.k        # Optional dependencies
├── default/
│   ├── install-{service}.sh  # Installation script
│   └── env-{service}.j2      # Environment template
└── README.md                 # Documentation

KCL Schema Template ({service}.k)

# Info: {Service} KCL schemas for provisioning
# Author: Your Name
# Release: 0.0.1

schema {Service}:
    """
    {Service} configuration schema for infrastructure provisioning
    """
    name: str = "{service}"
    version: str

    # Service-specific configuration
    port: int = {default_port}

    # Add your configuration options here

    # Validation
    check:
        port > 0 and port < 65536, "Port must be between 1 and 65535"
        len(version) > 0, "Version must be specified"

Module Configuration (kcl.mod)

[package]
name = "{service}"
edition = "v0.11.2"
version = "0.0.1"

[dependencies]
provisioning = { path = "../../../kcl", version = "0.0.1" }
taskservs = { path = "../..", version = "0.0.1" }

Usage in Workspace

# In workspace/infra/{your-infra}/task-servs/{service}.k
import taskservs.{service}.kcl.{service} as {service}_schema

_taskserv = {service}_schema.{Service} {
    version = "1.0.0"
    port = {port}
    # ... your configuration
}

_taskserv

Workspace Setup

1. Create Workspace Directory

mkdir -p workspace/infra/{your-infra}/{task-servs,clusters,defs}

2. Create kcl.mod

[package]
name = "{your-infra}"
edition = "v0.11.2"
version = "0.0.1"

[dependencies]
provisioning = { path = "../../../provisioning/kcl", version = "0.0.1" }
taskservs = { path = "../../../provisioning/extensions/taskservs", version = "0.0.1" }
cluster = { path = "../../../provisioning/extensions/cluster", version = "0.0.1" }
upcloud_prov = { path = "../../../provisioning/extensions/providers/upcloud/kcl", version = "0.0.1" }

3. Create settings.k

import provisioning

_settings = provisioning.Settings {
    main_name = "{your-infra}"
    main_title = "{Your Infrastructure Title}"
    # ... other settings
}

_settings

4. Test Configuration

cd workspace/infra/{your-infra}
kcl run settings.k

Common Patterns

Boolean Values

Use True and False (capitalized) in KCL:

enabled: bool = True
disabled: bool = False

Optional Fields

Use ? for optional fields:

optional_field?: str

Union Types

Use | for multiple allowed types:

log_level: "debug" | "info" | "warn" | "error" = "info"

Validation

Add validation rules:

check:
    port > 0 and port < 65536, "Port must be valid"
    len(name) > 0, "Name cannot be empty"

Testing Your Extensions

Test KCL Schema

cd workspace/infra/{your-infra}
kcl run task-servs/{service}.k

Test with Provisioning System

provisioning -c -i {your-infra} taskserv create {service}

Best Practices

  1. Use descriptive schema names: Redis, Kubernetes, not redis, k8s
  2. Add comprehensive validation: Check ports, required fields, etc.
  3. Provide sensible defaults: Make configuration easy to use
  4. Document all options: Use docstrings and comments
  5. Follow naming conventions: Use snake_case for fields, PascalCase for schemas
  6. Test thoroughly: Verify schemas work in workspaces
  7. Version properly: Use semantic versioning for modules
  8. Keep schemas focused: One service per schema file