diff --git a/cmd/uncloud/service/root.go b/cmd/uncloud/service/root.go index df169f01..5329876b 100644 --- a/cmd/uncloud/service/root.go +++ b/cmd/uncloud/service/root.go @@ -10,6 +10,7 @@ func NewRootCommand() *cobra.Command { Short: "Manage services in an Uncloud cluster.", } cmd.AddCommand( + NewInspectCommand(), NewListCommand(), NewRmCommand(), NewRunCommand(), diff --git a/internal/cli/client/service.go b/internal/cli/client/service.go index 9a159316..295c5f29 100644 --- a/internal/cli/client/service.go +++ b/internal/cli/client/service.go @@ -36,6 +36,35 @@ type MachineContainerID struct { ContainerID string } +type Service struct { + ID string + Name string +} + +// NewService creates a new Service object with the specified name that can be used to run service containers. +// It doesn't create anything in the cluster yet. +func (cli *Client) NewService(ctx context.Context, name string) (*Service, error) { + // Optimistically check if a service with the specified name already exists. + // TODO: introduce a distributed lock to hold for all service related operations. + _, err := cli.InspectService(ctx, name) + if err == nil { + return nil, fmt.Errorf("service with name '%s' already exists", name) + } + if !errors.Is(err, ErrNotFound) { + return nil, fmt.Errorf("inspect service: %w", err) + } + + id, err := secret.NewID() + if err != nil { + return nil, fmt.Errorf("generate service ID: %w", err) + } + + return &Service{ + ID: id, + Name: name, + }, nil +} + func (cli *Client) RunService(ctx context.Context, spec api.ServiceSpec) (RunServiceResponse, error) { var resp RunServiceResponse diff --git a/test/e2e/service_test.go b/test/e2e/service_test.go index 15f7a185..0b4802af 100644 --- a/test/e2e/service_test.go +++ b/test/e2e/service_test.go @@ -11,6 +11,28 @@ import ( "uncloud/internal/ucind" ) +func TestService(t *testing.T) { + t.Parallel() + + clusterName := "ucind-test.service" + ctx := context.Background() + c, _ := createTestCluster(t, clusterName, ucind.CreateClusterOptions{Machines: 1}, true) + + cli, err := c.Machines[0].Connect(ctx) + require.NoError(t, err) + + t.Run("container lifecycle", func(t *testing.T) { + t.Parallel() + + name := "busybox-container-lifecycle" + svc, err := cli.NewService(ctx, name) + require.NoError(t, err) + + assert.NotEmpty(t, svc.ID) + assert.Equal(t, name, svc.Name) + }) +} + func TestRunService(t *testing.T) { t.Parallel()