Skip to content

feat(local): make StorageClass, MetalLB, and HTTPSPort configurable#201

Merged
viniciusdc merged 7 commits intomainfrom
feature/local-configurable-infra-settings
May 1, 2026
Merged

feat(local): make StorageClass, MetalLB, and HTTPSPort configurable#201
viniciusdc merged 7 commits intomainfrom
feature/local-configurable-infra-settings

Conversation

@dcmcand
Copy link
Copy Markdown
Contributor

@dcmcand dcmcand commented Mar 26, 2026

Summary

  • Add storage_class, https_port, and metallb (enabled + address_pool) as optional fields in the local provider config
  • InfraSettings() reads these from config with current hardcoded values as defaults
  • Fixes local provider only working for kind clusters - k3s users can now set storage_class: local-path and metallb.enabled: false

Closes

Closes #200

What changed

Config (pkg/provider/local/config.go)

  • Added StorageClass, HTTPSPort fields to Config
  • Added MetalLBConfig struct with Enabled (*bool to distinguish unset from false) and AddressPool

Implementation (pkg/provider/local/provider.go)

  • InfraSettings() now parses the local config block and overrides defaults for any set fields
  • Parse errors silently return defaults (same pattern as Hetzner provider)

Tests (pkg/provider/local/provider_test.go)

  • 8 table-driven test cases: no config, empty config, each field individually, full override, unmarshal error

Examples (examples/local-config.yaml)

  • Added commented examples showing k3s and custom MetalLB pool configurations

Example usage

k3s cluster:

local:
  storage_class: local-path
  https_port: 8443
  metallb:
    enabled: false

Custom MetalLB pool (kind on non-default Docker network):

local:
  metallb:
    address_pool: 172.18.255.100-172.18.255.110

Test plan

  • go test ./pkg/provider/local/ -v passes (8/8 test cases)
  • golangci-lint run ./pkg/provider/local/ passes
  • Full project test suite passes

@marcelovilla
Copy link
Copy Markdown
Member

@dcmcand I'll review this. Can you resolve the conflicts when you have a chance?

@viniciusdc
Copy link
Copy Markdown
Contributor

Hey @dcmcand — could you rebase this against main and resolve the conflicts when you get a chance? Three refactors landed after this was opened (#190, #208, #211) that changed the Provider.InfraSettings signature to take *config.ClusterConfig and moved provider config under a nested cluster: block. The diff itself is small (mostly mirroring the pattern already in Summary()), so it should be a fairly mechanical rebase. Happy to review once it's green.

@dcmcand dcmcand force-pushed the feature/local-configurable-infra-settings branch from 1f97d14 to a88139c Compare April 21, 2026 12:23
@dcmcand
Copy link
Copy Markdown
Contributor Author

dcmcand commented Apr 27, 2026

@viniciusdc it is rebased now

viniciusdc and others added 2 commits April 30, 2026 10:10
Six assertion lines were incorrectly pulled into the tests slice as
table cases during the merge conflict resolution. Removed them and
moved the net-new SupportsLocalGitOps check into the per-case
assertion block alongside the existing zero-value field checks.
Copy link
Copy Markdown
Contributor

@viniciusdc viniciusdc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked through the diff and nebari-dev/action-nebari-sandbox#15 — a few things worth discussing before merging.

Two questions:

pkg/provider/local/provider.go — the comment says "InfraSettings is called after Validate() has confirmed the config is parseable", but that ordering isn't enforced anywhere in the code. If someone calls InfraSettings without going through Validate (in a test, or a future codepath), they'll silently get defaults with no indication something went wrong. Is there a lifecycle guarantee that makes this safe? Or should we at least log the error instead of swallowing it?

Also on provider.go — why does UnmarshalProviderConfig take a context at all? For a pure config unmarshal context.Background() is fine, but if the function does any I/O the context matters and should be threaded through. If it's purely structural (the interface requires it), a quick comment there would save future readers the same question.

Two small things:

provider_test.go — the test covers metallb.enabled: false and the default (true implicitly), but not enabled: true explicitly. That's the case where the pointer is non-nil but *enabled == true — small gap in the pointer semantics coverage.

provider.gocfg.ProviderConfig() is called without guarding against a nil cfg. Current callers never pass nil (and the "no local config block" test case passes a non-nil cfg with Providers: nil, which the rawCfg == nil guard handles correctly), but the function signature allows it. Worth a nil check at the top for robustness.

What looks good:

Enabled *bool for MetalLB is the right design — cleanly distinguishes "not set" (default true) from "explicitly false" without overloading the zero value. omitempty on a pointer works exactly as expected here.

The 8 table-driven test cases are solid: defaults, each field in isolation, full override, and the unmarshal error path. I also noticed the assertions verify the fields this PR doesn't touch (LoadBalancerAnnotations, KeycloakBasePath, SupportsLocalGitOps) haven't regressed — good discipline.

The integration evidence from nebari-dev/action-nebari-sandbox#15 is the best part: it removes the standard StorageClass workaround from create-cluster.sh — the one with the TODO pointing directly at this PR. That deletion is the cleanest possible proof this solves the underlying problem. The workaround only existed because this config field didn't. Full platform profile runs end-to-end against this branch in CI.

examples/local-config.yaml is clear and matches the actual struct. The k3s and custom MetalLB pool examples cover exactly the two cases that motivated this.

Copy link
Copy Markdown
Contributor

@viniciusdc viniciusdc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full platform profile runs end-to-end against this branch in CI nebari-dev/action-nebari-sandbox#15

Comment thread pkg/provider/local/provider.go
Comment thread pkg/provider/local/provider.go
Comment thread pkg/provider/local/provider.go
Comment thread pkg/provider/local/provider_test.go
Comment thread pkg/provider/local/config.go
@viniciusdc viniciusdc merged commit 36fea32 into main May 1, 2026
2 checks passed
@viniciusdc viniciusdc deleted the feature/local-configurable-infra-settings branch May 1, 2026 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: in review 👀 This PR is currently being reviewed by the team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Local provider: make StorageClass and MetalLB configurable

3 participants