From 4532b985d47847ae46d644d05115e56882429df6 Mon Sep 17 00:00:00 2001 From: Pavel Sviderski Date: Wed, 16 Apr 2025 22:01:33 +1000 Subject: [PATCH] chore: populate ucind cluster machines with IDs --- internal/ucind/cluster.go | 36 +++++++++++++++++++++++++++++++++--- internal/ucind/machine.go | 12 +++++++----- test/e2e/cluster_test.go | 18 +++++++++++++++++- 3 files changed, 57 insertions(+), 9 deletions(-) diff --git a/internal/ucind/cluster.go b/internal/ucind/cluster.go index 8b409ffb..a3dcc652 100644 --- a/internal/ucind/cluster.go +++ b/internal/ucind/cluster.go @@ -32,6 +32,29 @@ type CreateClusterOptions struct { Machines int } +func (c *Cluster) PopulateMachineIDs(ctx context.Context) error { + for i := range c.Machines { + if c.Machines[i].ID != "" { + continue + } + + cli, err := c.Machines[i].Connect(ctx) + if err != nil { + return fmt.Errorf("connect to machine '%s': %w", c.Machines[i].Name, err) + } + //goland:noinspection GoDeferInLoop + defer cli.Close() + + m, err := cli.Inspect(ctx, &emptypb.Empty{}) + if err != nil { + return fmt.Errorf("inspect machine '%s': %w", c.Machines[i].Name, err) + } + c.Machines[i].ID = m.Id + } + + return nil +} + func (p *Provisioner) CreateCluster(ctx context.Context, name string, opts CreateClusterOptions) (Cluster, error) { var c Cluster @@ -75,6 +98,9 @@ func (p *Provisioner) CreateCluster(ctx context.Context, name string, opts Creat if err = p.initCluster(ctx, c.Machines); err != nil { return c, err } + if err = c.PopulateMachineIDs(ctx); err != nil { + return c, fmt.Errorf("populate machine IDs: %w", err) + } if p.configUpdater != nil { if err = p.configUpdater.AddCluster(c); err != nil { @@ -90,7 +116,7 @@ func (p *Provisioner) initCluster(ctx context.Context, machines []Machine) error // Init a new cluster on the first machine. initMachine := machines[0] - if err := p.WaitMachineReady(ctx, initMachine, 30*time.Second); err != nil { + if err := WaitMachineReady(ctx, initMachine, 30*time.Second); err != nil { return fmt.Errorf("wait for machine %q to be ready: %w", initMachine.Name, err) } @@ -112,7 +138,7 @@ func (p *Provisioner) initCluster(ctx context.Context, machines []Machine) error // Join the rest of the machines to the cluster. for _, m := range machines[1:] { - if err = p.WaitMachineReady(ctx, m, 5*time.Second); err != nil { + if err = WaitMachineReady(ctx, m, 5*time.Second); err != nil { return fmt.Errorf("wait for machine %q to be ready: %w", m.Name, err) } @@ -216,13 +242,17 @@ func (p *Provisioner) InspectCluster(ctx context.Context, name string) (Cluster, c.Machines = append(c.Machines, m) } + if err = c.PopulateMachineIDs(ctx); err != nil { + return c, fmt.Errorf("populate machine IDs: %w", err) + } + return c, nil } // WaitClusterReady waits for all machines in the cluster to be ready and UP. func (p *Provisioner) WaitClusterReady(ctx context.Context, c Cluster, timeout time.Duration) error { firstMachine := c.Machines[0] - if err := p.WaitMachineReady(ctx, firstMachine, timeout); err != nil { + if err := WaitMachineReady(ctx, firstMachine, timeout); err != nil { return fmt.Errorf("wait for machine '%s' to be ready: %w", firstMachine.Name, err) } diff --git a/internal/ucind/machine.go b/internal/ucind/machine.go index eb7e7d70..07f4c730 100644 --- a/internal/ucind/machine.go +++ b/internal/ucind/machine.go @@ -4,6 +4,11 @@ import ( "context" "errors" "fmt" + "io" + "net" + "net/netip" + "time" + "github.com/cenkalti/backoff/v4" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/image" @@ -13,10 +18,6 @@ import ( "github.com/psviderski/uncloud/pkg/client" "github.com/psviderski/uncloud/pkg/client/connector" "google.golang.org/protobuf/types/known/emptypb" - "io" - "net" - "net/netip" - "time" ) const ( @@ -29,6 +30,7 @@ const ( type Machine struct { ClusterName string ContainerName string + ID string Name string APIAddress netip.AddrPort } @@ -185,7 +187,7 @@ func randomMachineName() (string, error) { } // WaitMachineReady waits for the machine API to respond. -func (p *Provisioner) WaitMachineReady(ctx context.Context, m Machine, timeout time.Duration) error { +func WaitMachineReady(ctx context.Context, m Machine, timeout time.Duration) error { cli, err := m.Connect(ctx) if err != nil { return fmt.Errorf("connect to machine over TCP '%s': %w", m.APIAddress, err) diff --git a/test/e2e/cluster_test.go b/test/e2e/cluster_test.go index 5bc11cc5..b667b90b 100644 --- a/test/e2e/cluster_test.go +++ b/test/e2e/cluster_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "os" + "strings" "testing" "time" @@ -45,6 +46,9 @@ func createTestCluster( require.NoError(t, err) assert.Equal(t, name, c.Name) assert.Len(t, c.Machines, opts.Machines) + for _, m := range c.Machines { + assert.NotEmpty(t, m.ID) + } t.Cleanup(func() { require.NoError(t, p.RemoveCluster(ctx, name)) @@ -69,7 +73,7 @@ func TestClusterLifecycle(t *testing.T) { // Create a client for each machine and wait for it to be ready. clients := make([]*client.Client, len(c.Machines)) for i, m := range c.Machines { - require.NoError(t, p.WaitMachineReady(ctx, m, 5*time.Second)) + require.NoError(t, ucind.WaitMachineReady(ctx, m, 5*time.Second)) clients[i], err = m.Connect(ctx) require.NoError(t, err) //goland:noinspection GoDeferInLoop @@ -106,6 +110,18 @@ func TestClusterLifecycle(t *testing.T) { } }) + t.Run("inspect", func(t *testing.T) { + cluster, err := p.InspectCluster(ctx, name) + require.NoError(t, err) + + assert.Equal(t, name, cluster.Name) + assert.Len(t, cluster.Machines, 3) + for _, m := range cluster.Machines { + assert.NotEmpty(t, m.ID) + assert.True(t, strings.HasPrefix(m.Name, "machine-")) + } + }) + t.Run("remove", func(t *testing.T) { err := p.RemoveCluster(ctx, name) require.NoError(t, err)