Skip to content
Open
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
17 changes: 11 additions & 6 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ import (
)

func assembleConfigForCommand(configBase *deployer.Config, configFromArgs deployer.Config, skipUserConfig bool) (deployer.Config, error) {
var config deployer.Config
if configBase == nil {
// Start with default configuration.
config = deployer.DefaultConfig()
} else {
config = *configBase
// Always start from the built-in defaults so that defaults (e.g.
// roxie.imageRegistry) are applied even when reusing an existing cluster
// manifest as the base configuration. Otherwise a manifest written by an
// older roxie (which predates such defaults) would leave those fields empty.
config := deployer.DefaultConfig()
if configBase != nil {
// Merge the provided base (e.g. an existing cluster manifest) on top of
// the defaults; non-empty values in the base take precedence.
if err := mergo.Merge(&config, configBase, mergo.WithOverride, mergo.WithoutDereference); err != nil {

@vladbologa vladbologa Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This seems ok for now, but could create problems in the future if we add e.g. an int field.
If the default value of that field is 1, and the user tries to set it to 0, it won't be posible.

An alternative would be to keep the direct copy and just add the missing field:

if config.Roxie.ImageRegistry == "" {
    config.Roxie.ImageRegistry = constants.DefaultRegistry
}

But then every new field with a non-zero default needs such a fix. Not sure which one's better. Your approach would always work fine if all config fields were pointers.

return deployer.Config{}, fmt.Errorf("merging base config over defaults: %w", err)
}
}

// Apply user config on top (overriding defaults).
Expand Down
36 changes: 36 additions & 0 deletions cmd/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,42 @@ func TestValidateImageRegistry(t *testing.T) {
}
}

func TestAssembleConfigForCommand(t *testing.T) {
// Isolate the user config lookup so it cannot pick up a real file on disk.
tmpDir := t.TempDir()
t.Setenv("XDG_CONFIG_HOME", tmpDir)
t.Setenv("HOME", tmpDir) // For non-Unix systems.

t.Run("applies defaults when no base config is provided", func(t *testing.T) {
config, err := assembleConfigForCommand(nil, deployer.Config{}, true)
require.NoError(t, err)
assert.Equal(t, constants.DefaultRegistry, config.Roxie.ImageRegistry)
})

t.Run("applies defaults even when base config (e.g. cluster manifest) omits them", func(t *testing.T) {
// Simulate a manifest stored by an older roxie that predates the
// imageRegistry field: ImageRegistry is empty. Defaults must still apply
// so an empty registry is not treated as a custom registry.
base := &deployer.Config{
Roxie: deployer.RoxieConfig{Version: "4.5.0"},
}
config, err := assembleConfigForCommand(base, deployer.Config{}, true)
require.NoError(t, err)
assert.Equal(t, constants.DefaultRegistry, config.Roxie.ImageRegistry)
// Values present in the base config are preserved.
assert.Equal(t, imagetag.MainTag("4.5.0"), config.Roxie.Version)
})

t.Run("base config overrides defaults", func(t *testing.T) {
base := &deployer.Config{
Roxie: deployer.RoxieConfig{ImageRegistry: "example.com/custom"},
}
config, err := assembleConfigForCommand(base, deployer.Config{}, true)
require.NoError(t, err)
assert.Equal(t, "example.com/custom", config.Roxie.ImageRegistry)
})
}

func TestApplyUserDefaults(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading