diff --git a/pkg/apis/k0s/v1beta1/clusterconfig_types.go b/pkg/apis/k0s/v1beta1/clusterconfig_types.go index d416358b8bd7..01e48ac03627 100644 --- a/pkg/apis/k0s/v1beta1/clusterconfig_types.go +++ b/pkg/apis/k0s/v1beta1/clusterconfig_types.go @@ -197,21 +197,24 @@ func (s *SchedulerSpec) IsZero() bool { return len(s.ExtraArgs) == 0 } -func ConfigFromBytes(bytes []byte, defaultStorage ...*StorageSpec) (*ClusterConfig, error) { - config := DefaultClusterConfig(defaultStorage...) - err := strictyaml.YamlUnmarshalStrictIgnoringFields(bytes, config, "interval", "podSecurityPolicy") +func ConfigFromBytes(bytes []byte) (*ClusterConfig, error) { + return DefaultClusterConfig().MergedWithYAML(bytes) +} + +func (c *ClusterConfig) MergedWithYAML(bytes []byte) (*ClusterConfig, error) { + merged := c.DeepCopy() + err := strictyaml.YamlUnmarshalStrictIgnoringFields(bytes, merged, "interval", "podSecurityPolicy") if err != nil { - return config, err + return nil, err } - if config.Spec == nil { - config.Spec = DefaultClusterSpec(defaultStorage...) + if merged.Spec == nil { + merged.Spec = c.Spec } - return config, nil + return merged, nil } // DefaultClusterConfig sets the default ClusterConfig values, when none are given -func DefaultClusterConfig(defaultStorage ...*StorageSpec) *ClusterConfig { - clusterSpec := DefaultClusterSpec(defaultStorage...) +func DefaultClusterConfig() *ClusterConfig { return &ClusterConfig{ TypeMeta: metav1.TypeMeta{ APIVersion: GroupVersion.String(), @@ -221,7 +224,7 @@ func DefaultClusterConfig(defaultStorage ...*StorageSpec) *ClusterConfig { Name: constant.ClusterConfigObjectName, Namespace: constant.ClusterConfigNamespace, }, - Spec: clusterSpec, + Spec: DefaultClusterSpec(), } } @@ -237,7 +240,10 @@ func (c *ClusterConfig) UnmarshalJSON(data []byte) error { if c.Spec != nil && c.Spec.Storage != nil { storage = c.Spec.Storage } - c.Spec = DefaultClusterSpec(storage) + c.Spec = DefaultClusterSpec() + if storage != nil { + c.Spec.Storage = storage + } type config ClusterConfig jc := (*config)(c) @@ -251,7 +257,10 @@ func (c *ClusterConfig) UnmarshalJSON(data []byte) error { } if jc.Spec == nil { - jc.Spec = DefaultClusterSpec(storage) + jc.Spec = DefaultClusterSpec() + if storage != nil { + jc.Spec.Storage = storage + } return nil } if jc.Spec.Storage == nil { @@ -291,17 +300,10 @@ func (c *ClusterConfig) UnmarshalJSON(data []byte) error { } // DefaultClusterSpec default settings -func DefaultClusterSpec(defaultStorage ...*StorageSpec) *ClusterSpec { - var storage *StorageSpec - if defaultStorage == nil || defaultStorage[0] == nil { - storage = DefaultStorageSpec() - } else { - storage = defaultStorage[0] - } - +func DefaultClusterSpec() *ClusterSpec { spec := &ClusterSpec{ Extensions: DefaultExtensions(), - Storage: storage, + Storage: DefaultStorageSpec(), Network: DefaultNetwork(), API: DefaultAPISpec(), ControllerManager: DefaultControllerManagerSpec(), diff --git a/pkg/component/controller/workerconfig/reconciler_test.go b/pkg/component/controller/workerconfig/reconciler_test.go index e49b92b2bc23..d77be4bcb002 100644 --- a/pkg/component/controller/workerconfig/reconciler_test.go +++ b/pkg/component/controller/workerconfig/reconciler_test.go @@ -96,7 +96,7 @@ func TestReconciler_Lifecycle(t *testing.T) { t.Run("reconcile_fails", func(t *testing.T) { underTest := createdReconciler(t, nil) - err := underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig(nil)) + err := underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig()) require.Error(t, err) assert.Equal(t, "cannot reconcile, not started: created", err.Error()) @@ -140,7 +140,7 @@ func TestReconciler_Lifecycle(t *testing.T) { t.Run("reconcile_fails", func(t *testing.T) { underTest := initializedReconciler(t, nil) - err := underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig(nil)) + err := underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig()) require.Error(t, err) assert.Equal(t, "cannot reconcile, not started: initialized", err.Error()) @@ -196,7 +196,7 @@ func TestReconciler_Lifecycle(t *testing.T) { underTest, mockApplier := startedReconciler(t) applied := mockApplier.expectApply(t, nil) - assert.NoError(t, underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig(nil))) + assert.NoError(t, underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig())) assert.NotEmpty(t, applied(), "Expected some resources to be applied") }) @@ -214,7 +214,7 @@ func TestReconciler_Lifecycle(t *testing.T) { t.Helper() underTest, mockApplier := startedReconciler(t) applied := mockApplier.expectApply(t, nil) - require.NoError(t, underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig(nil))) + require.NoError(t, underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig())) _ = applied() // wait until reconciliation happened return underTest, mockApplier @@ -242,7 +242,7 @@ func TestReconciler_Lifecycle(t *testing.T) { t.Run("another_reconcile_succeeds", func(t *testing.T) { underTest, mockApplier := reconciledReconciler(t) applied := mockApplier.expectApply(t, nil) - clusterConfig := v1beta1.DefaultClusterConfig(nil) + clusterConfig := v1beta1.DefaultClusterConfig() clusterConfig.Spec.WorkerProfiles = v1beta1.WorkerProfiles{ {Name: "foo", Config: &runtime.RawExtension{Raw: []byte("{}")}}, } @@ -291,7 +291,7 @@ func TestReconciler_Lifecycle(t *testing.T) { t.Run("reconcile_fails", func(t *testing.T) { underTest := stoppedReconciler(t) - err := underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig(nil)) + err := underTest.Reconcile(testContext(t), v1beta1.DefaultClusterConfig()) require.Error(t, err) assert.Equal(t, "cannot reconcile, not started: stopped", err.Error()) @@ -480,7 +480,7 @@ func TestReconciler_ResourceGeneration(t *testing.T) { } func TestReconciler_ReconcilesOnChangesOnly(t *testing.T) { - cluster := v1beta1.DefaultClusterConfig(nil) + cluster := v1beta1.DefaultClusterConfig() clients := testutil.NewFakeClientFactory() k0sVars, err := config.NewCfgVars(nil, t.TempDir()) require.NoError(t, err) @@ -630,7 +630,7 @@ func TestReconciler_runReconcileLoop(t *testing.T) { func TestReconciler_LeaderElection(t *testing.T) { var le mockLeaderElector - cluster := v1beta1.DefaultClusterConfig(nil) + cluster := v1beta1.DefaultClusterConfig() clients := testutil.NewFakeClientFactory() k0sVars, err := config.NewCfgVars(nil, t.TempDir()) require.NoError(t, err) diff --git a/pkg/config/cfgvars.go b/pkg/config/cfgvars.go index 60e2229e93fa..ae347c4c0e0e 100644 --- a/pkg/config/cfgvars.go +++ b/pkg/config/cfgvars.go @@ -200,17 +200,6 @@ func NewCfgVars(cobraCmd command, dirs ...string) (*CfgVars, error) { return vars, nil } -func (c *CfgVars) defaultStorageSpec() *v1beta1.StorageSpec { - if c.DefaultStorageType == v1beta1.KineStorageType { - return &v1beta1.StorageSpec{ - Type: v1beta1.KineStorageType, - Kine: v1beta1.DefaultKineConfig(c.DataDir), - } - } - - return v1beta1.DefaultStorageSpec() -} - var defaultConfigPath = constant.K0sConfigPathDefault func (c *CfgVars) NodeConfig() (*v1beta1.ClusterConfig, error) { @@ -218,7 +207,15 @@ func (c *CfgVars) NodeConfig() (*v1beta1.ClusterConfig, error) { return nil, errors.New("config path is not set") } - var nodeConfig *v1beta1.ClusterConfig + nodeConfig := v1beta1.DefaultClusterConfig() + + // Optionally override the default storage (used for single node clusters) + if c.DefaultStorageType == v1beta1.KineStorageType { + nodeConfig.Spec.Storage = &v1beta1.StorageSpec{ + Type: v1beta1.KineStorageType, + Kine: v1beta1.DefaultKineConfig(c.DataDir), + } + } if c.StartupConfigPath == "-" { configReader := c.stdin @@ -232,23 +229,22 @@ func (c *CfgVars) NodeConfig() (*v1beta1.ClusterConfig, error) { return nil, err } - nodeConfig, err = v1beta1.ConfigFromBytes(bytes, c.defaultStorageSpec()) + nodeConfig, err = nodeConfig.MergedWithYAML(bytes) if err != nil { return nil, err } } else { cfgContent, err := os.ReadFile(c.StartupConfigPath) - if errors.Is(err, os.ErrNotExist) && c.StartupConfigPath == defaultConfigPath { - nodeConfig = v1beta1.DefaultClusterConfig(c.defaultStorageSpec()) - } else if err == nil { - cfg, err := v1beta1.ConfigFromBytes(cfgContent, c.defaultStorageSpec()) - if err != nil { - return nil, err + if err != nil { + if c.StartupConfigPath == defaultConfigPath && errors.Is(err, os.ErrNotExist) { + // The default configuration file doesn't exist; continue with the defaults. + return nodeConfig, nil } - nodeConfig = cfg - } else { + return nil, err } + + return nodeConfig.MergedWithYAML(cfgContent) } if nodeConfig.Spec.Storage.Type == v1beta1.KineStorageType && nodeConfig.Spec.Storage.Kine == nil {