provisioning/docs/CONFIG_VALIDATION.md
2025-10-07 11:12:02 +01:00

11 KiB

Configuration Validation Guide

Overview

The new configuration system includes comprehensive schema validation to catch errors early and ensure configuration correctness.

Schema Validation Features

1. Required Fields Validation

Ensures all required fields are present:

# Schema definition
[required]
fields = ["name", "version", "enabled"]

# Valid config
name = "my-service"
version = "1.0.0"
enabled = true

# Invalid - missing 'enabled'
name = "my-service"
version = "1.0.0"
# Error: Required field missing: enabled

2. Type Validation

Validates field types:

# Schema
[fields.port]
type = "int"

[fields.name]
type = "string"

[fields.enabled]
type = "bool"

# Valid
port = 8080
name = "orchestrator"
enabled = true

# Invalid - wrong type
port = "8080"  # Error: Expected int, got string

3. Enum Validation

Restricts values to predefined set:

# Schema
[fields.environment]
type = "string"
enum = ["dev", "staging", "prod"]

# Valid
environment = "prod"

# Invalid
environment = "production"  # Error: Must be one of: dev, staging, prod

4. Range Validation

Validates numeric ranges:

# Schema
[fields.port]
type = "int"
min = 1024
max = 65535

# Valid
port = 8080

# Invalid - below minimum
port = 80  # Error: Must be >= 1024

# Invalid - above maximum
port = 70000  # Error: Must be <= 65535

5. Pattern Validation

Validates string patterns using regex:

# Schema
[fields.email]
type = "string"
pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"

# Valid
email = "admin@example.com"

# Invalid
email = "not-an-email"  # Error: Does not match pattern

6. Deprecated Fields

Warns about deprecated configuration:

# Schema
[deprecated]
fields = ["old_field"]

[deprecated_replacements]
old_field = "new_field"

# Config using deprecated field
old_field = "value"  # Warning: old_field is deprecated. Use new_field instead.

Using Schema Validator

Command Line

# Validate workspace config
provisioning workspace config validate

# Validate provider config
provisioning provider validate aws

# Validate platform service config
provisioning platform validate orchestrator

# Validate with detailed output
provisioning workspace config validate --verbose

Programmatic Usage

use provisioning/core/nulib/lib_provisioning/config/schema_validator.nu *

# Load config
let config = (open ~/workspaces/my-project/config/provisioning.yaml | from yaml)

# Validate against schema
let result = (validate-workspace-config $config)

# Check results
if $result.valid {
  print "✅ Configuration is valid"
} else {
  print "❌ Configuration has errors:"
  for error in $result.errors {
    print $"  • ($error.message)"
  }
}

# Display warnings
if ($result.warnings | length) > 0 {
  print "⚠️  Warnings:"
  for warning in $result.warnings {
    print $"  • ($warning.message)"
  }
}

Pretty Print Results

# Validate and print formatted results
let result = (validate-workspace-config $config)
print-validation-results $result

Schema Examples

Workspace Schema

File: /Users/Akasha/project-provisioning/provisioning/config/workspace.schema.toml

[required]
fields = ["workspace", "paths"]

[fields.workspace]
type = "record"

[fields.workspace.name]
type = "string"
pattern = "^[a-z][a-z0-9-]*$"

[fields.workspace.version]
type = "string"
pattern = "^\\d+\\.\\d+\\.\\d+$"

[fields.paths]
type = "record"

[fields.paths.base]
type = "string"

[fields.paths.infra]
type = "string"

[fields.debug]
type = "record"

[fields.debug.enabled]
type = "bool"

[fields.debug.log_level]
type = "string"
enum = ["debug", "info", "warn", "error"]

Provider Schema (AWS)

File: /Users/Akasha/project-provisioning/provisioning/extensions/providers/aws/config.schema.toml

[required]
fields = ["provider", "credentials"]

[fields.provider]
type = "record"

[fields.provider.name]
type = "string"
enum = ["aws"]

[fields.provider.region]
type = "string"
pattern = "^[a-z]{2}-[a-z]+-\\d+$"

[fields.provider.enabled]
type = "bool"

[fields.credentials]
type = "record"

[fields.credentials.type]
type = "string"
enum = ["environment", "file", "iam_role"]

[fields.compute]
type = "record"

[fields.compute.default_instance_type]
type = "string"

[fields.compute.default_ami]
type = "string"
pattern = "^ami-[a-f0-9]{8,17}$"

[fields.network]
type = "record"

[fields.network.vpc_id]
type = "string"
pattern = "^vpc-[a-f0-9]{8,17}$"

[fields.network.subnet_id]
type = "string"
pattern = "^subnet-[a-f0-9]{8,17}$"

[deprecated]
fields = ["old_region_field"]

[deprecated_replacements]
old_region_field = "provider.region"

Platform Service Schema (Orchestrator)

File: /Users/Akasha/project-provisioning/provisioning/platform/orchestrator/config.schema.toml

[required]
fields = ["service", "server"]

[fields.service]
type = "record"

[fields.service.name]
type = "string"
enum = ["orchestrator"]

[fields.service.enabled]
type = "bool"

[fields.server]
type = "record"

[fields.server.host]
type = "string"

[fields.server.port]
type = "int"
min = 1024
max = 65535

[fields.workers]
type = "int"
min = 1
max = 32

[fields.queue]
type = "record"

[fields.queue.max_size]
type = "int"
min = 100
max = 10000

[fields.queue.storage_path]
type = "string"

KMS Service Schema

File: /Users/Akasha/project-provisioning/provisioning/core/services/kms/config.schema.toml

[required]
fields = ["kms", "encryption"]

[fields.kms]
type = "record"

[fields.kms.enabled]
type = "bool"

[fields.kms.provider]
type = "string"
enum = ["aws_kms", "gcp_kms", "azure_kv", "vault", "local"]

[fields.encryption]
type = "record"

[fields.encryption.algorithm]
type = "string"
enum = ["AES-256-GCM", "ChaCha20-Poly1305"]

[fields.encryption.key_rotation_days]
type = "int"
min = 30
max = 365

[fields.vault]
type = "record"

[fields.vault.address]
type = "string"
pattern = "^https?://.*$"

[fields.vault.token_path]
type = "string"

[deprecated]
fields = ["old_kms_type"]

[deprecated_replacements]
old_kms_type = "kms.provider"

Validation Workflow

1. Development

# Create new config
vim ~/workspaces/dev/config/provisioning.yaml

# Validate immediately
provisioning workspace config validate

# Fix errors and revalidate
vim ~/workspaces/dev/config/provisioning.yaml
provisioning workspace config validate

2. CI/CD Pipeline

# GitLab CI
validate-config:
  stage: validate
  script:
    - provisioning workspace config validate
    - provisioning provider validate aws
    - provisioning provider validate upcloud
    - provisioning platform validate orchestrator
  only:
    changes:
      - "*/config/**/*"

3. Pre-Deployment

# Validate all configurations before deployment
provisioning workspace config validate --verbose
provisioning provider validate --all
provisioning platform validate --all

# If valid, proceed with deployment
if [[ $? -eq 0 ]]; then
  provisioning deploy --workspace production
fi

Error Messages

Clear Error Format

❌ Validation failed

Errors:
  • Required field missing: workspace.name
  • Field port type mismatch: expected int, got string
  • Field environment must be one of: dev, staging, prod
  • Field port must be >= 1024
  • Field email does not match pattern: ^[a-zA-Z0-9._%+-]+@.*$

⚠️  Warnings:
  • Field old_field is deprecated. Use new_field instead.

Error Details

Each error includes:

  • field: Which field has the error
  • type: Error type (missing_required, type_mismatch, invalid_enum, etc.)
  • message: Human-readable description
  • Additional context: Expected values, patterns, ranges

Common Validation Patterns

Pattern 1: Hostname Validation

[fields.hostname]
type = "string"
pattern = "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"

Pattern 2: Email Validation

[fields.email]
type = "string"
pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"

Pattern 3: Semantic Version

[fields.version]
type = "string"
pattern = "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9]+)?$"

Pattern 4: URL Validation

[fields.url]
type = "string"
pattern = "^https?://[a-zA-Z0-9.-]+(:[0-9]+)?(/.*)?$"

Pattern 5: IPv4 Address

[fields.ip_address]
type = "string"
pattern = "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$"

Pattern 6: AWS Resource ID

[fields.instance_id]
type = "string"
pattern = "^i-[a-f0-9]{8,17}$"

[fields.ami_id]
type = "string"
pattern = "^ami-[a-f0-9]{8,17}$"

[fields.vpc_id]
type = "string"
pattern = "^vpc-[a-f0-9]{8,17}$"

Testing Validation

Unit Tests

# Run validation test suite
nu provisioning/tests/config_validation_tests.nu

Integration Tests

# Test with real configs
provisioning test validate --workspace dev
provisioning test validate --workspace staging
provisioning test validate --workspace prod

Custom Validation

# Create custom validation function
def validate-custom-config [config: record] {
  let result = (validate-workspace-config $config)

  # Add custom business logic validation
  if ($config.workspace.name | str starts-with "prod") {
    if not $config.debug.enabled == false {
      $result.errors = ($result.errors | append {
        field: "debug.enabled"
        type: "custom"
        message: "Debug must be disabled in production"
      })
    }
  }

  $result
}

Best Practices

1. Validate Early

# Validate during development
provisioning workspace config validate

# Don't wait for deployment

2. Use Strict Schemas

# Be explicit about types and constraints
[fields.port]
type = "int"
min = 1024
max = 65535

# Don't leave fields unvalidated

3. Document Patterns

# Include examples in schema
[fields.email]
type = "string"
pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
# Example: user@example.com

4. Handle Deprecation

# Always provide replacement guidance
[deprecated_replacements]
old_field = "new_field"  # Clear migration path

5. Test Schemas

# Include test cases in comments
# Valid: "admin@example.com"
# Invalid: "not-an-email"

Troubleshooting

Schema File Not Found

# Error: Schema file not found: /path/to/schema.toml

# Solution: Ensure schema exists
ls -la /Users/Akasha/project-provisioning/provisioning/config/*.schema.toml

Pattern Not Matching

# Error: Field hostname does not match pattern

# Debug: Test pattern separately
echo "my-hostname" | grep -E "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"

Type Mismatch

# Error: Expected int, got string

# Check config
cat ~/workspaces/dev/config/provisioning.yaml | yq '.server.port'
# Output: "8080" (string)

# Fix: Remove quotes
vim ~/workspaces/dev/config/provisioning.yaml
# Change: port: "8080"
# To:     port: 8080

Additional Resources