diff --git a/phase/gather_k0s_facts.go b/phase/gather_k0s_facts.go index 6ec12f0b..3ab9a60e 100644 --- a/phase/gather_k0s_facts.go +++ b/phase/gather_k0s_facts.go @@ -342,7 +342,9 @@ func (p *GatherK0sFacts) investigateK0s(ctx context.Context, h *cluster.Host) er } if status.Role != h.Role { - return fmt.Errorf("%s: is configured as k0s %s but is already running as %s - role change is not supported", h, h.Role, status.Role) + if err := p.handleRoleMismatch(h, status.Role); err != nil { + return err + } } h.Metadata.K0sRunningVersion = status.Version @@ -405,6 +407,20 @@ func (p *GatherK0sFacts) investigateK0s(ctx context.Context, h *cluster.Host) er return nil } +func (p *GatherK0sFacts) handleRoleMismatch(h *cluster.Host, detectedRole string) error { + if !h.Reset { + return fmt.Errorf("%s: is configured as k0s %s but is already running as %s - role change is not supported", h, h.Role, detectedRole) + } + + if !Force { + return fmt.Errorf("%s: is configured as k0s %s but is already running as %s - role change is not supported, use --force to ignore the mismatch during reset", h, h.Role, detectedRole) + } + + log.Warnf("%s: was configured as %s but is already running as %s - proceeding with reset using the discovered role because --force was given", h, h.Role, detectedRole) + h.Role = detectedRole + return nil +} + func (p *GatherK0sFacts) needsUpgrade(h *cluster.Host) bool { if h.Reset { return false diff --git a/phase/gather_k0s_facts_test.go b/phase/gather_k0s_facts_test.go index ce7caed3..ceccf6e4 100644 --- a/phase/gather_k0s_facts_test.go +++ b/phase/gather_k0s_facts_test.go @@ -70,3 +70,33 @@ func TestReportUseExistingHostsFailsWithoutBinary(t *testing.T) { p.SetManager(mgr) require.ErrorContains(t, p.reportUseExistingHosts(), "useExistingK0s=true but no k0s binary found on host") } + +func TestHandleRoleMismatch(t *testing.T) { + originalForce := Force + t.Cleanup(func() { Force = originalForce }) + + p := GatherK0sFacts{} + + t.Run("host not marked for reset", func(t *testing.T) { + Force = true + h := &cluster.Host{Role: "controller"} + err := p.handleRoleMismatch(h, "worker") + require.ErrorContains(t, err, "role change is not supported") + require.Equal(t, "controller", h.Role) + }) + + t.Run("reset host requires force", func(t *testing.T) { + Force = false + h := &cluster.Host{Role: "controller", Reset: true} + err := p.handleRoleMismatch(h, "single") + require.ErrorContains(t, err, "use --force") + require.Equal(t, "controller", h.Role) + }) + + t.Run("force allows role update", func(t *testing.T) { + Force = true + h := &cluster.Host{Role: "controller", Reset: true} + require.NoError(t, p.handleRoleMismatch(h, "worker")) + require.Equal(t, "worker", h.Role) + }) +}