Add Configuration Validation
🔧 Priority: HIGH
Labels: enhancement, reliability, config, validation
Estimated Effort: 1 day
Assignee: @aaydin-tr
Problem Description
Currently, Divisor has minimal configuration validation, which can lead to runtime crashes or unexpected behavior when users provide invalid configurations. There's no comprehensive validation to catch common configuration mistakes before the service starts.
Current Issues:
- Missing port validation - Non-numeric ports cause runtime errors
- No URL validation - Malformed backend URLs aren't caught
- Incomplete required field checks - Missing required fields cause panics
- No range validation - Negative or zero values for timeouts/connections
- Poor error messages - Errors don't guide users to fix issues
Proposed Solution
Add comprehensive configuration validation with clear, helpful error messages that guide users to fix configuration issues.
Implementation Plan
1. Enhance Configuration Validation
File: pkg/config/config.go
Add detailed validation to the PrepareConfig() method:
import (
"errors"
"fmt"
"net"
"net/url"
"strconv"
"strings"
"time"
)
func (c *Config) PrepareConfig() error {
// Validate core server configuration
if err := c.validateServerConfig(); err != nil {
return fmt.Errorf("server configuration error: %w", err)
}
// Validate load balancer configuration
if err := c.validateLoadBalancerConfig(); err != nil {
return fmt.Errorf("load balancer configuration error: %w", err)
}
// Validate backend configuration
if err := c.validateBackends(); err != nil {
return fmt.Errorf("backend configuration error: %w", err)
}
// Validate monitoring configuration
if err := c.validateMonitoring(); err != nil {
return fmt.Errorf("monitoring configuration error: %w", err)
}
// Validate custom headers
if err := c.validateCustomHeaders(); err != nil {
return fmt.Errorf("custom headers configuration error: %w", err)
}
// Set defaults after validation
c.setDefaults()
return nil
}
func (c *Config) validateServerConfig() error {
// Validate host
if c.Host == "" {
c.Host = "localhost" // Set default
}
// Validate port
if c.Port == "" {
return errors.New("port is required")
}
port, err := strconv.Atoi(c.Port)
if err != nil {
return fmt.Errorf("port must be a valid number, got '%s'", c.Port)
}
if port < 1 || port > 65535 {
return fmt.Errorf("port must be between 1 and 65535, got %d", port)
}
// Check if port is available
if c.Host != "" {
addr := net.JoinHostPort(c.Host, c.Port)
listener, err := net.Listen("tcp", addr)
if err != nil {
return fmt.Errorf("cannot bind to %s: %w", addr, err)
}
listener.Close()
}
return nil
}
2. Add Backend URL Validation
func (c *Config) validateBackend(index int, backend *Backend) error {
// Validate URL
if backend.Url == "" {
return errors.New("url is required")
}
// Check if URL contains protocol and strip it
originalURL := backend.Url
backend.Url = protocolRegex.ReplaceAllString(backend.Url, "")
// Validate URL format (must contain port)
if !strings.Contains(backend.Url, ":") {
return fmt.Errorf("url '%s' must include port (e.g., example.com:80 or example.com:443)", originalURL)
}
// Parse and validate host:port
host, portStr, err := net.SplitHostPort(backend.Url)
if err != nil {
return fmt.Errorf("invalid url format '%s': %w", backend.Url, err)
}
if host == "" {
return fmt.Errorf("hostname cannot be empty in url '%s'", backend.Url)
}
port, err := strconv.Atoi(portStr)
if err != nil {
return fmt.Errorf("invalid port '%s' in url '%s'", portStr, backend.Url)
}
if port < 1 || port > 65535 {
return fmt.Errorf("port %d out of valid range (1-65535) in url '%s'", port, backend.Url)
}
return nil
}
Testing Plan
Unit Tests
Create pkg/config/config_test.go:
func TestConfigValidation(t *testing.T) {
tests := []struct {
name string
config Config
expectError bool
errorSubstr string
}{
{
name: "valid basic config",
config: Config{
Type: "round-robin",
Host: "localhost",
Port: "8080",
Backends: []Backend{
{Url: "backend1.local:8081"},
},
},
expectError: false,
},
{
name: "missing port",
config: Config{
Type: "round-robin",
Host: "localhost",
Backends: []Backend{
{Url: "backend1.local:8081"},
},
},
expectError: true,
errorSubstr: "port is required",
},
// ... more test cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.config.PrepareConfig()
if tt.expectError {
require.Error(t, err)
if tt.errorSubstr != "" {
assert.Contains(t, err.Error(), tt.errorSubstr)
}
} else {
require.NoError(t, err)
}
})
}
}
Configuration Examples
After implementation, provide clear error messages:
# Invalid configuration
port: "invalid"
backends: []
# Error output:
# server configuration error: port must be a valid number, got 'invalid'
# backend configuration error: at least one backend must be configured
Acceptance Criteria
Files to Modify
pkg/config/config.go - Add validation methods
pkg/config/config_test.go - Add comprehensive validation tests
- Examples/ - Add examples showing error handling
Dependencies: None
Follows: Issue #1 (HTTPS Backend Support)
Documentation: Update README with configuration validation examples
Add Configuration Validation
🔧 Priority: HIGH
Labels:
enhancement,reliability,config,validationEstimated Effort: 1 day
Assignee: @aaydin-tr
Problem Description
Currently, Divisor has minimal configuration validation, which can lead to runtime crashes or unexpected behavior when users provide invalid configurations. There's no comprehensive validation to catch common configuration mistakes before the service starts.
Current Issues:
Proposed Solution
Add comprehensive configuration validation with clear, helpful error messages that guide users to fix configuration issues.
Implementation Plan
1. Enhance Configuration Validation
File:
pkg/config/config.goAdd detailed validation to the
PrepareConfig()method:2. Add Backend URL Validation
Testing Plan
Unit Tests
Create
pkg/config/config_test.go:Configuration Examples
After implementation, provide clear error messages:
Acceptance Criteria
Files to Modify
pkg/config/config.go- Add validation methodspkg/config/config_test.go- Add comprehensive validation testsDependencies: None
Follows: Issue #1 (HTTPS Backend Support)
Documentation: Update README with configuration validation examples