diff --git a/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go b/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go index 9728b550cf..75d5b7baae 100644 --- a/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go +++ b/internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go @@ -152,6 +152,16 @@ func (s *Server) Register(obj *grpc.Server) { timeapi.RegisterTimeServiceServer(obj, &TimeServer{ConfigProvider: s.Controller.Runtime()}) } +// modeWrapper overrides RequiresInstall() based on actual installed status. +type modeWrapper struct { + runtime.Mode + installed bool +} + +func (m modeWrapper) RequiresInstall() bool { + return m.Mode.RequiresInstall() && !m.installed +} + // ApplyConfiguration implements machine.MachineService. // //nolint:gocyclo,cyclop @@ -169,7 +179,12 @@ func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfig return nil, status.Error(codes.InvalidArgument, err.Error()) } - warnings, err := cfgProvider.Validate(s.Controller.Runtime().State().Platform().Mode()) + warnings, err := cfgProvider.Validate( + modeWrapper{ + Mode: s.Controller.Runtime().State().Platform().Mode(), + installed: s.Controller.Runtime().State().Machine().Installed(), + }, + ) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } diff --git a/internal/app/machined/pkg/controllers/config/acquire.go b/internal/app/machined/pkg/controllers/config/acquire.go index b245c8a733..be00731cc9 100644 --- a/internal/app/machined/pkg/controllers/config/acquire.go +++ b/internal/app/machined/pkg/controllers/config/acquire.go @@ -206,6 +206,19 @@ func (ctrl *AcquireController) stateDisk(ctx context.Context, r controller.Runti } } +// validationModeDiskConfig is a "fake" validation mode for config loaded from disk. +type validationModeDiskConfig struct{} + +// RequiresInstall implements validation.RuntimeMode interface. +func (validationModeDiskConfig) RequiresInstall() bool { + return false +} + +// String implements validation.RuntimeMode interface. +func (validationModeDiskConfig) String() string { + return "diskConfig" +} + // loadFromDisk is a helper function for stateDisk. func (ctrl *AcquireController) loadFromDisk(logger *zap.Logger) (config.Provider, error) { logger.Debug("loading config from STATE", zap.String("path", ctrl.ConfigPath)) @@ -232,7 +245,8 @@ func (ctrl *AcquireController) loadFromDisk(logger *zap.Logger) (config.Provider return nil, nil } - warnings, err := cfg.Validate(ctrl.ValidationMode) + // if the STATE partition is present & contains machine config, Talos is already installed + warnings, err := cfg.Validate(validationModeDiskConfig{}) if err != nil { return nil, fmt.Errorf("failed to validate on-disk config: %w", err) } diff --git a/internal/integration/api/apply-config.go b/internal/integration/api/apply-config.go index 983188aaa1..7f72cce671 100644 --- a/internal/integration/api/apply-config.go +++ b/internal/integration/api/apply-config.go @@ -383,7 +383,7 @@ func (suite *ApplyConfigSuite) TestApplyNoReboot() { nodeCtx := client.WithNodes(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) - suite.Assert().Nilf(err, "failed to read existing config from node %q: %w", node, err) + suite.Require().Nilf(err, "failed to read existing config from node %q: %s", node, err) cfg := provider.RawV1Alpha1() suite.Require().NotNil(cfg) @@ -395,7 +395,7 @@ func (suite *ApplyConfigSuite) TestApplyNoReboot() { suite.Require().NoError(err) cfgDataOut, err := provider.Bytes() - suite.Assert().Nilf(err, "failed to marshal updated machine config data (node %q): %w", node, err) + suite.Require().Nilf(err, "failed to marshal updated machine config data (node %q): %s", node, err) _, err = suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ @@ -429,7 +429,7 @@ func (suite *ApplyConfigSuite) TestApplyDryRun() { nodeCtx := client.WithNodes(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) - suite.Assert().Nilf(err, "failed to read existing config from node %q: %w", node, err) + suite.Require().Nilf(err, "failed to read existing config from node %q: %s", node, err) cfg := provider.RawV1Alpha1() suite.Require().NotNil(cfg) @@ -441,7 +441,7 @@ func (suite *ApplyConfigSuite) TestApplyDryRun() { suite.Require().NoError(err) cfgDataOut, err := provider.Bytes() - suite.Assert().Nilf(err, "failed to marshal updated machine config data (node %q): %w", node, err) + suite.Require().Nilf(err, "failed to marshal updated machine config data (node %q): %s", node, err) reply, err := suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ @@ -451,8 +451,8 @@ func (suite *ApplyConfigSuite) TestApplyDryRun() { }, ) - suite.Assert().Nilf(err, "failed to apply configuration (node %q): %w", node, err) - suite.Require().Contains(reply.Messages[0].ModeDetails, "Dry run summary") + suite.Require().Nilf(err, "failed to apply configuration (node %q): %s", node, err) + suite.Assert().Contains(reply.Messages[0].ModeDetails, "Dry run summary") } // TestApplyTry applies the config in try mode with a short timeout. @@ -496,7 +496,7 @@ func (suite *ApplyConfigSuite) TestApplyTry() { ) cfgDataOut, err := container.NewV1Alpha1(cfg).Bytes() - suite.Assert().Nilf(err, "failed to marshal updated machine config data (node %q): %s", node, err) + suite.Require().Nilf(err, "failed to marshal updated machine config data (node %q): %s", node, err) _, err = suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ @@ -508,7 +508,7 @@ func (suite *ApplyConfigSuite) TestApplyTry() { suite.Assert().Nilf(err, "failed to apply configuration (node %q): %s", node, err) provider, err = getMachineConfig(nodeCtx) - suite.Assert().Nilf(err, "failed to read existing config from node %q: %w", node, err) + suite.Require().Nilf(err, "failed to read existing config from node %q: %w", node, err) suite.Assert().NotNil(provider.Config().Machine().Network()) suite.Assert().NotNil(provider.Config().Machine().Network().Devices())