Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func parseConfig(configFile string) (*Config, error) {
slog.Info("Configuration file found", "path", "concierge.yaml")
}

fixNilSnapEntries(viper.GetViper())
fixNilYAMLEntries(viper.GetViper())

conf := &Config{}
err := viper.Unmarshal(conf)
Expand Down
46 changes: 46 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,52 @@ host:
}
}

func TestK8sFeaturesWithoutConfig(t *testing.T) {
yamlConfig := `
providers:
k8s:
enable: true
bootstrap: true
features:
dns:
ingress:
local-storage:
network:
load-balancer:
l2-mode: "true"
cidrs: 10.0.0.0/24
`

tmpFile, err := os.CreateTemp("", "concierge-test-*.yaml")
if err != nil {
t.Fatal(err)
}
defer func() { _ = os.Remove(tmpFile.Name()) }()

if _, err := tmpFile.Write([]byte(yamlConfig)); err != nil {
t.Fatal(err)
}
if err := tmpFile.Close(); err != nil {
t.Fatal(err)
}

cfg, err := parseConfig(tmpFile.Name())
if err != nil {
t.Fatalf("Failed to parse config: %v", err)
}

for _, name := range []string{"dns", "ingress", "local-storage", "network", "load-balancer"} {
if _, ok := cfg.Providers.K8s.Features[name]; !ok {
t.Fatalf("expected k8s feature %q to be present in map, got: %v", name, cfg.Providers.K8s.Features)
}
}

lb := cfg.Providers.K8s.Features["load-balancer"]
if lb["l2-mode"] != "true" || lb["cidrs"] != "10.0.0.0/24" {
t.Fatalf("expected load-balancer config preserved, got: %v", lb)
}
}

func TestExtraBootstrapArgsFromYAML(t *testing.T) {
yamlConfig := `
juju:
Expand Down
22 changes: 15 additions & 7 deletions internal/config/presets.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,26 @@ func Preset(preset string) (*Config, error) {
return loadPreset(data)
}

// fixNilSnapEntries replaces nil-valued snap entries with empty maps so that
// Viper's Unmarshal does not silently drop bare YAML keys like "charmcraft:".
func fixNilSnapEntries(v *viper.Viper) {
if snaps, ok := v.Get("host.snaps").(map[string]any); ok {
for name, val := range snaps {
// fixNilMapEntries replaces nil-valued entries within the map at the given
// Viper key with empty maps, so that Unmarshal does not silently drop bare
// YAML keys like "charmcraft:" or "ingress:".
func fixNilMapEntries(v *viper.Viper, key string) {
if entries, ok := v.Get(key).(map[string]any); ok {
for name, val := range entries {
if val == nil {
v.Set("host.snaps."+name, map[string]any{})
v.Set(key+"."+name, map[string]any{})
}
}
}
}

// fixNilYAMLEntries fixes bare YAML keys that Viper would otherwise silently
// drop when unmarshalling into typed maps.
func fixNilYAMLEntries(v *viper.Viper) {
fixNilMapEntries(v, "host.snaps")
fixNilMapEntries(v, "providers.k8s.features")
Comment thread
tonyandrewmeyer marked this conversation as resolved.
}

// loadPreset parses YAML data into a Config using a fresh Viper instance.
func loadPreset(data []byte) (*Config, error) {
v := viper.New()
Expand All @@ -63,7 +71,7 @@ func loadPreset(data []byte) (*Config, error) {
return nil, fmt.Errorf("failed to parse preset: %w", err)
}

fixNilSnapEntries(v)
fixNilYAMLEntries(v)

conf := &Config{}
if err := v.Unmarshal(conf); err != nil {
Expand Down
3 changes: 3 additions & 0 deletions tests/provider-k8s/concierge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ providers:
bootstrap: true
channel: 1.32-classic/stable
features:
dns:
ingress:
local-storage:
network:
load-balancer:
l2-mode: true
cidrs: 10.64.140.43/32
Expand Down
11 changes: 8 additions & 3 deletions tests/provider-k8s/task.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ execute: |
echo $list | MATCH juju
echo $list | MATCH kubectl

sudo k8s status --output-format yaml | yq '.dns.enabled' | MATCH true
sudo k8s status --output-format yaml | yq '.load-balancer.enabled' | MATCH true
sudo k8s status --output-format yaml | yq '.load-balancer.message' | MATCH "enabled, L2 mode"
status="$(sudo k8s status --output-format yaml)"
# Bare-key features (no inline config) must still be enabled — see issue #189.
echo "$status" | yq '.dns.enabled' | MATCH true
echo "$status" | yq '.ingress.enabled' | MATCH true
echo "$status" | yq '.["local-storage"].enabled' | MATCH true
echo "$status" | yq '.network.enabled' | MATCH true
echo "$status" | yq '.["load-balancer"].enabled' | MATCH true
echo "$status" | yq '.["load-balancer"].message' | MATCH "enabled, L2 mode"
sudo k8s get | yq '.load-balancer.cidrs' | MATCH "10.64.140.43/32"

kubectl config current-context | MATCH "k8s"
Expand Down
Loading