From b6dfc2175fcfa02d6fcc5a00928dd9118248cb35 Mon Sep 17 00:00:00 2001 From: Pasha Sviderski Date: Fri, 7 Nov 2025 11:18:53 +1000 Subject: [PATCH] fix: reconfigure WireGuard peers when listed >=1 machines in cluster store (fixes #155) --- internal/machine/cluster.go | 8 ++++++++ internal/machine/machine.go | 9 ++++++++- internal/machine/network/wireguard_linux.go | 2 +- website/docs/1-overview.md | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/machine/cluster.go b/internal/machine/cluster.go index ea14bdfe..adfe7c91 100644 --- a/internal/machine/cluster.go +++ b/internal/machine/cluster.go @@ -358,6 +358,14 @@ func (cc *clusterController) handleMachineChanges(ctx context.Context) error { slog.Error("Failed to list machines.", "err", err) continue } + // Skip reconfiguration if the machines list is empty. This can happen when joining the cluster. + // Corrosion can notifies about table changes before the data is fully replicated. + // Reconfiguring with an empty list would remove all peers and lock this machine out of the cluster. + // See https://github.com/psviderski/uncloud/issues/155. + if len(machines) == 0 { + slog.Debug("Skipping peer reconfiguration: machines list in store is empty.") + continue + } if err = cc.configurePeers(machines); err != nil { slog.Error("Failed to configure peers.", "err", err) } diff --git a/internal/machine/machine.go b/internal/machine/machine.go index e5d55f62..a2d1749b 100644 --- a/internal/machine/machine.go +++ b/internal/machine/machine.go @@ -821,7 +821,14 @@ func (m *Machine) JoinCluster(_ context.Context, req *pb.JoinClusterRequest) (*e if err := m.state.Save(); err != nil { return nil, status.Errorf(codes.Internal, "save machine state: %v", err) } - slog.Info("Machine configured to join the cluster.", "id", m.state.ID, "name", m.state.Name) + slog.Info( + "Machine configured to join the cluster.", + "id", m.state.ID, + "name", m.state.Name, + "subnet", m.state.Network.Subnet.String(), + "management_ip", m.state.Network.ManagementIP.String(), + "peers", len(m.state.Network.Peers), + ) // Signal that the machine is initialised as a member of a cluster. m.initialised <- struct{}{} diff --git a/internal/machine/network/wireguard_linux.go b/internal/machine/network/wireguard_linux.go index 75fe6107..99ea4bb9 100644 --- a/internal/machine/network/wireguard_linux.go +++ b/internal/machine/network/wireguard_linux.go @@ -79,7 +79,7 @@ func (n *WireGuardNetwork) Configure(config Config) error { if err := n.configureDevice(config); err != nil { return err } - slog.Info("Configured WireGuard interface.", "name", n.link.Attrs().Name) + slog.Info("Configured WireGuard interface.", "name", n.link.Attrs().Name, "peers", len(n.peers)) managementPrefix, err := addrToSingleIPPrefix(config.ManagementIP) if err != nil { diff --git a/website/docs/1-overview.md b/website/docs/1-overview.md index cc0338c7..be5025ec 100644 --- a/website/docs/1-overview.md +++ b/website/docs/1-overview.md @@ -42,7 +42,7 @@ Some of the common use cases Uncloud is a great fit for: - **Dev/staging environments**: Spin up additional environments for development and testing that mirror production reusing the same Compose configuration. -## What makes Uncloud different +## What makes Uncloud special Here are the design decisions that make Uncloud stand out: