Files
tesseract/internal/workspace/workspace.go

127 lines
2.5 KiB
Go
Raw Normal View History

2024-11-12 00:31:10 +00:00
package workspace
import (
2024-11-17 18:10:35 +00:00
"context"
"database/sql"
"errors"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
2024-11-12 00:31:10 +00:00
"github.com/google/uuid"
"github.com/uptrace/bun"
"regexp"
2024-11-17 18:10:35 +00:00
"sync"
"tesseract/internal/docker"
"tesseract/internal/service"
2024-11-12 00:31:10 +00:00
)
type workspace struct {
bun.BaseModel `bun:"table:workspaces,alias:workspace"`
ID uuid.UUID `bun:",type:uuid,pk"`
Name string `json:"name"`
// containerId is the ID of the docker container
ContainerID string `json:"containerId"`
ImageTag string `json:"imageTag"`
CreatedAt string `json:"createdAt"`
2024-11-17 18:10:35 +00:00
SSHPort int `bun:"-" json:"sshPort,omitempty"`
Status status `bun:"-" json:"status"`
2024-11-12 00:31:10 +00:00
}
2024-11-17 18:10:35 +00:00
// status represents the status of a workspace.
type status string
const (
statusRunning status = "running"
statusStopped status = "stopped"
statusPaused status = "paused"
statusRestarting status = "restarting"
statusUnknown status = "unknown"
)
2024-11-12 00:31:10 +00:00
var workspaceNameRegex = regexp.MustCompile("^[\\w-]+$")
2024-11-17 18:10:35 +00:00
func SyncAll(ctx context.Context, services service.Services) error {
tx, err := services.Database.BeginTx(ctx, nil)
if err != nil {
return err
}
var workspaces []workspace
if err = tx.NewSelect().Model(&workspaces).
Column("id", "container_id").
Scan(ctx); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil
}
}
if len(workspaces) == 0 {
return nil
}
var wg sync.WaitGroup
var mu sync.Mutex
var errs []error
var deletedWorkspaces []workspace
for _, w := range workspaces {
w := w
wg.Add(1)
go func() {
var err error
defer wg.Done()
defer func() {
mu.Lock()
errs = append(errs, err)
mu.Unlock()
}()
if err = services.DockerClient.ContainerStart(ctx, w.ContainerID, container.StartOptions{}); err != nil {
if client.IsErrNotFound(err) {
err = nil
mu.Lock()
deletedWorkspaces = append(deletedWorkspaces, w)
mu.Unlock()
}
return
}
inspect, err := services.DockerClient.ContainerInspect(ctx, w.ContainerID)
if err != nil {
return
}
internalPort := docker.ContainerSSHHostPort(ctx, inspect)
if internalPort <= 0 {
return
}
err = services.SSHProxy.NewProxyEntryTo(internalPort)
}()
}
if err = errors.Join(errs...); err != nil {
_ = tx.Rollback()
return err
}
_, err = tx.NewDelete().Model(&deletedWorkspaces).WherePK().Exec(ctx)
if err != nil {
_ = tx.Rollback()
return err
}
if err = tx.Commit(); err != nil {
_ = tx.Rollback()
return err
}
return nil
}