From a4be45186d49e20989b2415cf5572fc2379c31c3 Mon Sep 17 00:00:00 2001 From: Pavel Sviderski Date: Thu, 14 Nov 2024 09:12:25 +1000 Subject: [PATCH] find first available machine for running a service --- internal/cli/client/service.go | 42 ++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/internal/cli/client/service.go b/internal/cli/client/service.go index d3304b51..05b4863e 100644 --- a/internal/cli/client/service.go +++ b/internal/cli/client/service.go @@ -2,10 +2,13 @@ package client import ( "context" + "errors" "fmt" "github.com/docker/docker/api/types/container" "google.golang.org/grpc/metadata" "google.golang.org/protobuf/types/known/emptypb" + "slices" + "uncloud/internal/machine/api/pb" ) // ServiceOptions contains all the options for creating a service. @@ -30,18 +33,28 @@ func (c *Client) RunService(ctx context.Context, opts *ServiceOptions) (RunServi return resp, fmt.Errorf("list machines: %w", err) } - // TODO: find the first available machine (state UP). - machineIP, _ := listResp.Machines[0].Machine.Network.ManagementIp.ToAddr() - resp.MachineName = listResp.Machines[0].Machine.Name + var machine *pb.MachineMember if opts.Machine != "" { + // Check if the machine ID or name exists if it's explicitly specified. for _, m := range listResp.Machines { if m.Machine.Name == opts.Machine || m.Machine.Id == opts.Machine { - machineIP, _ = m.Machine.Network.ManagementIp.ToAddr() - resp.MachineName = m.Machine.Name + machine = m break } + return resp, fmt.Errorf("machine %q not found", opts.Machine) + } + } else { + machine, err = firstAvailableMachine(listResp.Machines) + if err != nil { + return resp, err } } + if machine == nil { // This should never happen. + return resp, errors.New("no available machine to run the service") + } + + machineIP, _ := machine.Machine.Network.ManagementIp.ToAddr() + resp.MachineName = machine.Machine.Name md := metadata.Pairs("machines", machineIP.String()) ctx = metadata.NewOutgoingContext(ctx, md) @@ -66,3 +79,22 @@ func (c *Client) RunService(ctx context.Context, opts *ServiceOptions) (RunServi return resp, nil } + +func firstAvailableMachine(machines []*pb.MachineMember) (*pb.MachineMember, error) { + // Find the first UP machine. + upIdx := slices.IndexFunc(machines, func(m *pb.MachineMember) bool { + return m.State == pb.MachineMember_UP + }) + if upIdx != -1 { + return machines[upIdx], nil + } + // There is no UP machine, try to find the first SUSPECT machine. + suspectIdx := slices.IndexFunc(machines, func(m *pb.MachineMember) bool { + return m.State == pb.MachineMember_SUSPECT + }) + if suspectIdx != -1 { + return machines[suspectIdx], nil + } + + return nil, errors.New("no available machine to run the service") +}