Skip to content
14 changes: 14 additions & 0 deletions examples/local-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ cluster:
worker:
kubernetes.io/os: linux

# Storage class for persistent volumes (default: "standard")
# Use "local-path" for k3s clusters
# storage_class: local-path

# HTTPS port for Gateway listener (default: 443)
# Override if 443 is already in use or requires root
# https_port: 8443

# MetalLB configuration (default: enabled with 192.168.1.100-192.168.1.110 pool)
# Disable for k3s which ships with ServiceLB
# metallb:
# enabled: false
# address_pool: 172.18.255.100-172.18.255.110

# GitOps repository configuration (optional)
# Configures the repository that ArgoCD will use to manage cluster resources
#
Expand Down
16 changes: 15 additions & 1 deletion pkg/provider/local/config.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
package local

// Config represents local K3s configuration
// Config represents local provider configuration
type Config struct {
KubeContext string `yaml:"kube_context,omitempty"`
NodeSelectors map[string]map[string]string `yaml:"node_selectors,omitempty"`
StorageClass string `yaml:"storage_class,omitempty"`
HTTPSPort int `yaml:"https_port,omitempty"`
MetalLB *MetalLBConfig `yaml:"metallb,omitempty"`
AdditionalFields map[string]any `yaml:",inline"`
}

// MetalLBConfig holds MetalLB-specific settings for the local provider.
type MetalLBConfig struct {
// Enabled controls whether MetalLB is deployed. Default: true.
// Use a pointer to distinguish "not set" (default true) from "explicitly false".
Enabled *bool `yaml:"enabled,omitempty"`
Comment thread
viniciusdc marked this conversation as resolved.

// AddressPool is the IP range for MetalLB's IPAddressPool.
// Default: "192.168.1.100-192.168.1.110"
AddressPool string `yaml:"address_pool,omitempty"`
}
35 changes: 33 additions & 2 deletions pkg/provider/local/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,42 @@ func (p *Provider) Summary(clusterConfig *config.ClusterConfig) map[string]strin
}

// InfraSettings returns local provider Kubernetes infrastructure settings.
func (p *Provider) InfraSettings(_ *config.ClusterConfig) provider.InfraSettings {
return provider.InfraSettings{
// Values are read from the local provider config block, falling back to defaults.
// Parse errors are intentionally ignored: InfraSettings is called after Validate()
// has confirmed the config is parseable. If it somehow fails (e.g., nil config in
// tests), we return valid defaults.
Comment thread
viniciusdc marked this conversation as resolved.
func (p *Provider) InfraSettings(cfg *config.ClusterConfig) provider.InfraSettings {
settings := provider.InfraSettings{
StorageClass: "standard",
NeedsMetalLB: true,
MetalLBAddressPool: "192.168.1.100-192.168.1.110",
SupportsLocalGitOps: true,
}

rawCfg := cfg.ProviderConfig()
Comment thread
viniciusdc marked this conversation as resolved.
if rawCfg == nil {
return settings
}

var localCfg Config
if err := config.UnmarshalProviderConfig(context.Background(), rawCfg, &localCfg); err != nil {
Comment thread
viniciusdc marked this conversation as resolved.
return settings
}

if localCfg.StorageClass != "" {
settings.StorageClass = localCfg.StorageClass
}
if localCfg.HTTPSPort != 0 {
settings.HTTPSPort = localCfg.HTTPSPort
}
if localCfg.MetalLB != nil {
if localCfg.MetalLB.Enabled != nil {
settings.NeedsMetalLB = *localCfg.MetalLB.Enabled
}
if localCfg.MetalLB.AddressPool != "" {
settings.MetalLBAddressPool = localCfg.MetalLB.AddressPool
}
}

return settings
}
138 changes: 122 additions & 16 deletions pkg/provider/local/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,135 @@ var _ provider.Provider = (*Provider)(nil)

func TestInfraSettings(t *testing.T) {
p := NewProvider()
cfg := &config.ClusterConfig{
Providers: map[string]any{"local": map[string]any{}},
}

settings := p.InfraSettings(cfg)

tests := []struct {
name string
got any
want any
name string
providerConfig map[string]any
wantSC string
wantMetalLB bool
wantPool string
wantHTTPSPort int
}{
{"StorageClass", settings.StorageClass, "standard"},
{"NeedsMetalLB", settings.NeedsMetalLB, true},
{"MetalLBAddressPool", settings.MetalLBAddressPool, "192.168.1.100-192.168.1.110"},
{"LoadBalancerAnnotations is empty", len(settings.LoadBalancerAnnotations), 0},
{"KeycloakBasePath is empty", settings.KeycloakBasePath, ""},
{"SupportsLocalGitOps", settings.SupportsLocalGitOps, true},
{
name: "no local config block returns defaults",
providerConfig: nil,
wantSC: "standard",
wantMetalLB: true,
wantPool: "192.168.1.100-192.168.1.110",
wantHTTPSPort: 0,
},
{
name: "empty local config returns defaults",
providerConfig: map[string]any{"local": map[string]any{}},
wantSC: "standard",
wantMetalLB: true,
wantPool: "192.168.1.100-192.168.1.110",
wantHTTPSPort: 0,
},
{
name: "storage_class override",
providerConfig: map[string]any{
"local": map[string]any{"storage_class": "local-path"},
},
wantSC: "local-path",
wantMetalLB: true,
wantPool: "192.168.1.100-192.168.1.110",
wantHTTPSPort: 0,
},
{
name: "metallb disabled",
providerConfig: map[string]any{
"local": map[string]any{
"metallb": map[string]any{"enabled": false},
},
},
wantSC: "standard",
wantMetalLB: false,
wantPool: "192.168.1.100-192.168.1.110",
wantHTTPSPort: 0,
},
Comment thread
viniciusdc marked this conversation as resolved.
{
name: "metallb address_pool override",
providerConfig: map[string]any{
"local": map[string]any{
"metallb": map[string]any{
"address_pool": "172.18.255.100-172.18.255.110",
},
},
},
wantSC: "standard",
wantMetalLB: true,
wantPool: "172.18.255.100-172.18.255.110",
wantHTTPSPort: 0,
},
{
name: "https_port override",
providerConfig: map[string]any{
"local": map[string]any{"https_port": 8443},
},
wantSC: "standard",
wantMetalLB: true,
wantPool: "192.168.1.100-192.168.1.110",
wantHTTPSPort: 8443,
},
{
name: "full override",
providerConfig: map[string]any{
"local": map[string]any{
"storage_class": "local-path",
"https_port": 8443,
"metallb": map[string]any{
"enabled": false,
"address_pool": "10.0.0.100-10.0.0.110",
},
},
},
wantSC: "local-path",
wantMetalLB: false,
wantPool: "10.0.0.100-10.0.0.110",
wantHTTPSPort: 8443,
},
{
name: "unmarshal error returns defaults",
providerConfig: map[string]any{
"local": "not-a-map",
},
wantSC: "standard",
wantMetalLB: true,
wantPool: "192.168.1.100-192.168.1.110",
wantHTTPSPort: 0,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.got != tt.want {
t.Errorf("got %v, want %v", tt.got, tt.want)
cfg := &config.ClusterConfig{
Providers: tt.providerConfig,
}

settings := p.InfraSettings(cfg)

if settings.StorageClass != tt.wantSC {
t.Errorf("StorageClass = %q, want %q", settings.StorageClass, tt.wantSC)
}
if settings.NeedsMetalLB != tt.wantMetalLB {
t.Errorf("NeedsMetalLB = %v, want %v", settings.NeedsMetalLB, tt.wantMetalLB)
}
if settings.MetalLBAddressPool != tt.wantPool {
t.Errorf("MetalLBAddressPool = %q, want %q", settings.MetalLBAddressPool, tt.wantPool)
}
if settings.HTTPSPort != tt.wantHTTPSPort {
t.Errorf("HTTPSPort = %d, want %d", settings.HTTPSPort, tt.wantHTTPSPort)
}
// Fields not set by local provider should always be zero values
if len(settings.LoadBalancerAnnotations) != 0 {
t.Errorf("LoadBalancerAnnotations = %v, want empty", settings.LoadBalancerAnnotations)
}
if settings.KeycloakBasePath != "" {
t.Errorf("KeycloakBasePath = %q, want empty", settings.KeycloakBasePath)
}
if !settings.SupportsLocalGitOps {
t.Error("SupportsLocalGitOps = false, want true")
}
})
}
Expand Down
Loading